Contents of /trunk/src/project.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 186 - (hide annotations)
Fri Jul 3 14:07:27 2009 UTC (14 years, 11 months ago) by harbaum
File MIME type: text/plain
File size: 43661 byte(s)
Wizard tests
1 harbaum 1 /*
2     * Copyright (C) 2008 Till Harbaum <till@harbaum.org>.
3     *
4     * This file is part of OSM2Go.
5     *
6     * OSM2Go is free software: you can redistribute it and/or modify
7     * it under the terms of the GNU General Public License as published by
8     * the Free Software Foundation, either version 3 of the License, or
9     * (at your option) any later version.
10     *
11     * OSM2Go is distributed in the hope that it will be useful,
12     * but WITHOUT ANY WARRANTY; without even the implied warranty of
13     * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14     * GNU General Public License for more details.
15     *
16     * You should have received a copy of the GNU General Public License
17     * along with OSM2Go. If not, see <http://www.gnu.org/licenses/>.
18     */
19    
20     #include "appdata.h"
21 achadwick 28 #include "banner.h"
22 harbaum 1
23     #include <sys/stat.h>
24    
25     #include <libxml/parser.h>
26     #include <libxml/tree.h>
27    
28     #if !defined(LIBXML_TREE_ENABLED) || !defined(LIBXML_OUTPUT_ENABLED)
29     #error "libxml doesn't support required tree or output"
30     #endif
31    
32     typedef struct {
33     project_t *project;
34 harbaum 169 settings_t *settings;
35 harbaum 1 GtkWidget *dialog, *fsize, *diff_stat, *diff_remove;
36     GtkWidget *desc, *server;
37     GtkWidget *minlat, *minlon, *maxlat, *maxlon;
38     area_edit_t area_edit;
39     } project_context_t;
40    
41     /* ------------ project file io ------------- */
42    
43     static gboolean project_read(appdata_t *appdata,
44     char *project_file, project_t *project) {
45    
46     LIBXML_TEST_VERSION;
47    
48     xmlDoc *doc = NULL;
49     xmlNode *root_element = NULL;
50    
51     /* parse the file and get the DOM */
52     if((doc = xmlReadFile(project_file, NULL, 0)) == NULL) {
53     printf("error: could not parse file %s\n", project_file);
54     return FALSE;
55     }
56    
57     /* Get the root element node */
58     root_element = xmlDocGetRootElement(doc);
59    
60     xmlNode *cur_node = NULL;
61     for (cur_node = root_element; cur_node; cur_node = cur_node->next) {
62     if (cur_node->type == XML_ELEMENT_NODE) {
63     if(strcasecmp((char*)cur_node->name, "proj") == 0) {
64     char *str;
65    
66     if((str = (char*)xmlGetProp(cur_node, BAD_CAST "dirty"))) {
67     project->data_dirty = (strcasecmp(str, "true") == 0);
68     xmlFree(str);
69     } else
70     project->data_dirty = FALSE;
71    
72     xmlNode *node = cur_node->children;
73    
74     while(node != NULL) {
75     if(node->type == XML_ELEMENT_NODE) {
76    
77     if(strcasecmp((char*)node->name, "desc") == 0) {
78     str = (char*)xmlNodeListGetString(doc, node->children, 1);
79     project->desc = g_strdup(str);
80     printf("desc = %s\n", project->desc);
81     xmlFree(str);
82    
83     } else if(strcasecmp((char*)node->name, "server") == 0) {
84     str = (char*)xmlNodeListGetString(doc, node->children, 1);
85     project->server = g_strdup(str);
86     printf("server = %s\n", project->server);
87     xmlFree(str);
88    
89     } else if(project->map_state &&
90     strcasecmp((char*)node->name, "map") == 0) {
91     if((str = (char*)xmlGetProp(node, BAD_CAST "zoom"))) {
92     project->map_state->zoom = g_ascii_strtod(str, NULL);
93     xmlFree(str);
94     }
95 harbaum 162 if((str = (char*)xmlGetProp(node, BAD_CAST "detail"))) {
96     project->map_state->detail = g_ascii_strtod(str, NULL);
97     xmlFree(str);
98     }
99 harbaum 1 if((str = (char*)xmlGetProp(node, BAD_CAST "scroll-offset-x"))) {
100     project->map_state->scroll_offset.x = strtoul(str, NULL, 10);
101     xmlFree(str);
102     }
103     if((str = (char*)xmlGetProp(node, BAD_CAST "scroll-offset-y"))) {
104     project->map_state->scroll_offset.y = strtoul(str, NULL, 10);
105     xmlFree(str);
106     }
107    
108     } else if(strcasecmp((char*)node->name, "wms") == 0) {
109    
110     if((str = (char*)xmlGetProp(node, BAD_CAST "server"))) {
111     project->wms_server = g_strdup(str);
112     xmlFree(str);
113     }
114     if((str = (char*)xmlGetProp(node, BAD_CAST "path"))) {
115     project->wms_path = g_strdup(str);
116     xmlFree(str);
117     }
118     if((str = (char*)xmlGetProp(node, BAD_CAST "x-offset"))) {
119     project->wms_offset.x = strtoul(str, NULL, 10);
120     xmlFree(str);
121     }
122     if((str = (char*)xmlGetProp(node, BAD_CAST "y-offset"))) {
123     project->wms_offset.y = strtoul(str, NULL, 10);
124     xmlFree(str);
125     }
126    
127     } else if(strcasecmp((char*)node->name, "osm") == 0) {
128     str = (char*)xmlNodeListGetString(doc, node->children, 1);
129 harbaum 175 printf("osm = %s\n", str);
130    
131     /* make this a relative path if possible */
132     /* if the project path actually is a prefix of this, */
133     /* then just remove this prefix */
134     if((str[0] == '/') &&
135     (strlen(str) > strlen(project->path)) &&
136     !strncmp(str, project->path, strlen(project->path))) {
137    
138     project->osm = g_strdup(str + strlen(project->path));
139     printf("osm name converted to relative %s\n", project->osm);
140     } else
141     project->osm = g_strdup(str);
142    
143 harbaum 1 xmlFree(str);
144 harbaum 175
145 harbaum 1 } else if(strcasecmp((char*)node->name, "min") == 0) {
146     if((str = (char*)xmlGetProp(node, BAD_CAST "lat"))) {
147     project->min.lat = g_ascii_strtod(str, NULL);
148     xmlFree(str);
149     }
150     if((str = (char*)xmlGetProp(node, BAD_CAST "lon"))) {
151     project->min.lon = g_ascii_strtod(str, NULL);
152     xmlFree(str);
153     }
154    
155     } else if(strcasecmp((char*)node->name, "max") == 0) {
156     if((str = (char*)xmlGetProp(node, BAD_CAST "lat"))) {
157     project->max.lat = g_ascii_strtod(str, NULL);
158     xmlFree(str);
159     }
160     if((str = (char*)xmlGetProp(node, BAD_CAST "lon"))) {
161     project->max.lon = g_ascii_strtod(str, NULL);
162     xmlFree(str);
163     }
164     }
165     }
166     node = node->next;
167     }
168     }
169     }
170     }
171    
172     xmlFreeDoc(doc);
173     xmlCleanupParser();
174    
175     return TRUE;
176     }
177    
178     gboolean project_save(GtkWidget *parent, project_t *project) {
179     char str[32];
180     char *project_file = g_strdup_printf("%s%s.proj",
181     project->path, project->name);
182    
183     printf("saving project to %s\n", project_file);
184    
185     /* check if project path exists */
186     if(!g_file_test(project->path, G_FILE_TEST_IS_DIR)) {
187     /* make sure project base path exists */
188     if(g_mkdir_with_parents(project->path, S_IRWXU) != 0) {
189     errorf(GTK_WIDGET(parent),
190     _("Unable to create project path %s"), project->path);
191     return FALSE;
192     }
193     }
194    
195     LIBXML_TEST_VERSION;
196    
197     xmlDocPtr doc = xmlNewDoc(BAD_CAST "1.0");
198     xmlNodePtr node, root_node = xmlNewNode(NULL, BAD_CAST "proj");
199     xmlNewProp(root_node, BAD_CAST "name", BAD_CAST project->name);
200     if(project->data_dirty)
201     xmlNewProp(root_node, BAD_CAST "dirty", BAD_CAST "true");
202    
203     xmlDocSetRootElement(doc, root_node);
204    
205 harbaum 178 if(project->server)
206     node = xmlNewChild(root_node, NULL, BAD_CAST "server",
207     BAD_CAST project->server);
208 harbaum 1
209     xmlNewChild(root_node, NULL, BAD_CAST "desc", BAD_CAST project->desc);
210     xmlNewChild(root_node, NULL, BAD_CAST "osm", BAD_CAST project->osm);
211    
212     node = xmlNewChild(root_node, NULL, BAD_CAST "min", NULL);
213 harbaum 156 g_ascii_formatd(str, sizeof(str), LL_FORMAT, project->min.lat);
214 harbaum 1 xmlNewProp(node, BAD_CAST "lat", BAD_CAST str);
215 harbaum 156 g_ascii_formatd(str, sizeof(str), LL_FORMAT, project->min.lon);
216 harbaum 1 xmlNewProp(node, BAD_CAST "lon", BAD_CAST str);
217    
218     node = xmlNewChild(root_node, NULL, BAD_CAST "max", NULL);
219 harbaum 156 g_ascii_formatd(str, sizeof(str), LL_FORMAT, project->max.lat);
220 harbaum 1 xmlNewProp(node, BAD_CAST "lat", BAD_CAST str);
221 harbaum 156 g_ascii_formatd(str, sizeof(str), LL_FORMAT, project->max.lon);
222 harbaum 1 xmlNewProp(node, BAD_CAST "lon", BAD_CAST str);
223    
224     if(project->map_state) {
225     node = xmlNewChild(root_node, NULL, BAD_CAST "map", BAD_CAST NULL);
226 harbaum 162 g_ascii_formatd(str, sizeof(str), "%.04f", project->map_state->zoom);
227 harbaum 1 xmlNewProp(node, BAD_CAST "zoom", BAD_CAST str);
228 harbaum 162 g_ascii_formatd(str, sizeof(str), "%.04f", project->map_state->detail);
229     xmlNewProp(node, BAD_CAST "detail", BAD_CAST str);
230 harbaum 1 snprintf(str, sizeof(str), "%d", project->map_state->scroll_offset.x);
231     xmlNewProp(node, BAD_CAST "scroll-offset-x", BAD_CAST str);
232     snprintf(str, sizeof(str), "%d", project->map_state->scroll_offset.y);
233     xmlNewProp(node, BAD_CAST "scroll-offset-y", BAD_CAST str);
234     }
235    
236     node = xmlNewChild(root_node, NULL, BAD_CAST "wms", NULL);
237 harbaum 14 if(project->wms_server)
238     xmlNewProp(node, BAD_CAST "server", BAD_CAST project->wms_server);
239     if(project->wms_path)
240     xmlNewProp(node, BAD_CAST "path", BAD_CAST project->wms_path);
241 harbaum 1 snprintf(str, sizeof(str), "%d", project->wms_offset.x);
242     xmlNewProp(node, BAD_CAST "x-offset", BAD_CAST str);
243     snprintf(str, sizeof(str), "%d", project->wms_offset.y);
244     xmlNewProp(node, BAD_CAST "y-offset", BAD_CAST str);
245    
246     xmlSaveFormatFileEnc(project_file, doc, "UTF-8", 1);
247     xmlFreeDoc(doc);
248     xmlCleanupParser();
249    
250     g_free(project_file);
251    
252     return TRUE;
253     }
254    
255     /* ------------ freeing projects --------------------- */
256    
257     void project_free(project_t *project) {
258     if(!project) return;
259    
260     if(project->name) g_free(project->name);
261     if(project->desc) g_free(project->desc);
262     if(project->server) g_free(project->server);
263    
264     if(project->wms_server) g_free(project->wms_server);
265     if(project->wms_path) g_free(project->wms_path);
266    
267     if(project->path) g_free(project->path);
268     if(project->osm) g_free(project->osm);
269    
270     map_state_free(project->map_state);
271    
272     g_free(project);
273     }
274    
275     /* ------------ project selection dialog ------------- */
276    
277     static char *project_fullname(settings_t *settings, const char *name) {
278     return g_strdup_printf("%s%s/%s.proj", settings->base_path, name, name);
279     }
280    
281 harbaum 179 gboolean project_exists(settings_t *settings, const char *name) {
282 harbaum 1 gboolean ok = FALSE;
283     char *fulldir = g_strdup_printf("%s%s", settings->base_path, name);
284    
285     if(g_file_test(fulldir, G_FILE_TEST_IS_DIR)) {
286    
287     /* check for project file */
288     char *fullname = project_fullname(settings, name);
289    
290     if(g_file_test(fullname, G_FILE_TEST_IS_REGULAR))
291     ok = TRUE;
292    
293     g_free(fullname);
294     }
295     g_free(fulldir);
296    
297     return ok;
298     }
299    
300     static project_t *project_scan(appdata_t *appdata) {
301     project_t *projects = NULL, **current = &projects;
302    
303     /* scan for projects */
304     GDir *dir = g_dir_open(appdata->settings->base_path, 0, NULL);
305     const char *name = NULL;
306     do {
307     if((name = g_dir_read_name(dir))) {
308     if(project_exists(appdata->settings, name)) {
309     printf("found project %s\n", name);
310    
311     /* try to read project and append it to chain */
312     *current = g_new0(project_t, 1);
313     (*current)->name = g_strdup(name);
314     (*current)->path = g_strdup_printf("%s%s/",
315     appdata->settings->base_path, name);
316    
317     char *fullname = project_fullname(appdata->settings, name);
318     if(project_read(appdata, fullname, *current))
319     current = &((*current)->next);
320     else {
321     g_free(*current);
322     *current = NULL;
323     }
324     g_free(fullname);
325     }
326     }
327     } while(name);
328    
329     g_dir_close(dir);
330    
331     return projects;
332     }
333    
334     typedef struct {
335     project_t *project;
336 harbaum 146 GtkWidget *dialog, *list;
337 harbaum 1 settings_t *settings;
338     #ifdef USE_HILDON
339     dbus_mm_pos_t *mmpos;
340     osso_context_t *osso_context;
341     #endif
342     } select_context_t;
343    
344     enum {
345     PROJECT_COL_NAME = 0,
346 achadwick 45 PROJECT_COL_STATUS,
347 harbaum 1 PROJECT_COL_DESCRIPTION,
348     PROJECT_COL_DATA,
349     PROJECT_NUM_COLS
350     };
351    
352 harbaum 175 static gboolean osm_file_exists(char *path, char *name) {
353     gboolean exists = FALSE;
354    
355     if(name[0] == '/')
356     exists = g_file_test(name, G_FILE_TEST_IS_REGULAR);
357     else {
358     char *full = g_strjoin(NULL, path, name, NULL);
359     exists = g_file_test(full, G_FILE_TEST_IS_REGULAR);
360     g_free(full);
361     }
362     return exists;
363     }
364    
365 harbaum 1 static void view_selected(select_context_t *context, project_t *project) {
366 harbaum 146 list_button_enable(context->list, LIST_BUTTON_REMOVE, project != NULL);
367     list_button_enable(context->list, LIST_BUTTON_EDIT, project != NULL);
368 harbaum 1
369     /* check if the selected project also has a valid osm file */
370     gtk_dialog_set_response_sensitive(GTK_DIALOG(context->dialog),
371     GTK_RESPONSE_ACCEPT,
372 harbaum 175 project && osm_file_exists(project->path, project->osm));
373 harbaum 1 }
374    
375     static gboolean
376     view_selection_func(GtkTreeSelection *selection, GtkTreeModel *model,
377     GtkTreePath *path, gboolean path_currently_selected,
378     gpointer userdata) {
379     select_context_t *context = (select_context_t*)userdata;
380     GtkTreeIter iter;
381    
382     if(gtk_tree_model_get_iter(model, &iter, path)) {
383     project_t *project = NULL;
384     gtk_tree_model_get(model, &iter, PROJECT_COL_DATA, &project, -1);
385     g_assert(gtk_tree_path_get_depth(path) == 1);
386    
387     view_selected(context, project);
388     }
389    
390     return TRUE; /* allow selection state to change */
391     }
392    
393     /* get the currently selected project in the list, NULL if none */
394 harbaum 146 static project_t *project_get_selected(GtkWidget *list) {
395 harbaum 1 project_t *project = NULL;
396     GtkTreeModel *model;
397     GtkTreeIter iter;
398    
399 harbaum 146 GtkTreeSelection *selection = list_get_selection(list);
400 harbaum 1 g_assert(gtk_tree_selection_get_selected(selection, &model, &iter));
401     gtk_tree_model_get(model, &iter, PROJECT_COL_DATA, &project, -1);
402    
403     return project;
404     }
405    
406     /* ------------------------- create a new project ---------------------- */
407    
408     /* returns true of str contains one of the characters in chars */
409     static gboolean strchrs(char *str, char *chars) {
410     while(*chars) {
411     char *p = str;
412     while(*p) {
413     if(*p == *chars)
414     return TRUE;
415    
416     p++;
417     }
418     chars++;
419     }
420     return FALSE;
421     }
422    
423     typedef struct {
424     GtkWidget *dialog;
425     settings_t *settings;
426     } name_callback_context_t;
427    
428     static void callback_modified_name(GtkWidget *widget, gpointer data) {
429     name_callback_context_t *context = (name_callback_context_t*)data;
430    
431     char *name = (char*)gtk_entry_get_text(GTK_ENTRY(widget));
432    
433     /* name must not contain some special chars */
434     gboolean ok = FALSE;
435    
436     /* check if there's a name */
437     if(name && strlen(name) > 0) {
438     /* check if it consists of valid characters */
439     if(!strchrs(name, "\\*?()\n\t\r")) {
440     /* check if such a project already exists */
441     if(!project_exists(context->settings, name))
442     ok = TRUE;
443     }
444     }
445    
446     gtk_dialog_set_response_sensitive(GTK_DIALOG(context->dialog),
447     GTK_RESPONSE_ACCEPT, ok);
448     }
449    
450    
451     gboolean project_delete(select_context_t *context, project_t *project) {
452    
453     /* remove entire directory from disk */
454     GDir *dir = g_dir_open(project->path, 0, NULL);
455     const char *name = NULL;
456     do {
457     if((name = g_dir_read_name(dir))) {
458     char *fullname = g_strdup_printf("%s/%s", project->path, name);
459     g_remove(fullname);
460     g_free(fullname);
461     }
462     } while(name);
463    
464     /* remove the projects directory */
465     g_remove(project->path);
466    
467     /* remove from view */
468     GtkTreeIter iter;
469 harbaum 146 GtkTreeModel *model = list_get_model(context->list);
470 harbaum 1 gboolean deleted = FALSE;
471     if(gtk_tree_model_get_iter_first(model, &iter)) {
472     do {
473     project_t *prj = NULL;
474     gtk_tree_model_get(model, &iter, PROJECT_COL_DATA, &prj, -1);
475     if(prj && (prj == project)) {
476     printf("found %s to remove\n", prj->name);
477     /* and remove from store */
478     gtk_list_store_remove(GTK_LIST_STORE(model), &iter);
479     deleted = TRUE;
480     }
481     } while(!deleted && gtk_tree_model_iter_next(model, &iter));
482     }
483    
484     /* de-chain entry from project list */
485     project_t **project_list = &context->project;
486     while(*project_list) {
487     if(*project_list == project)
488     *project_list = (*project_list)->next;
489     else
490     project_list = &((*project_list)->next);
491     }
492    
493     /* free project structure */
494     project_free(project);
495    
496     /* disable edit/remove buttons */
497     view_selected(context, NULL);
498    
499     return TRUE;
500     }
501    
502     project_t *project_new(select_context_t *context) {
503     printf("creating project with default values\n");
504    
505     /* -------------- first choose a name for the project --------------- */
506 harbaum 167 GtkWidget *dialog =
507     misc_dialog_new(MISC_DIALOG_NOSIZE, _("Project name"),
508     GTK_WINDOW(context->dialog),
509     GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT,
510     GTK_STOCK_OK, GTK_RESPONSE_ACCEPT,
511     NULL);
512 harbaum 1
513     GtkWidget *hbox = gtk_hbox_new(FALSE, 8);
514     gtk_box_pack_start_defaults(GTK_BOX(hbox), gtk_label_new(_("Name:")));
515    
516     name_callback_context_t name_context = { dialog, context->settings };
517     GtkWidget *entry = gtk_entry_new();
518     // gtk_entry_set_text(GTK_ENTRY(entry), "<enter name>");
519     gtk_box_pack_start_defaults(GTK_BOX(hbox), entry);
520     g_signal_connect(G_OBJECT(entry), "changed",
521     G_CALLBACK(callback_modified_name), &name_context);
522    
523     gtk_box_pack_start_defaults(GTK_BOX(GTK_DIALOG(dialog)->vbox), hbox);
524    
525     /* don't all user to click ok until something useful has been entered */
526     gtk_dialog_set_response_sensitive(GTK_DIALOG(dialog),
527     GTK_RESPONSE_ACCEPT, FALSE);
528    
529     gtk_widget_show_all(dialog);
530     if(GTK_RESPONSE_ACCEPT != gtk_dialog_run(GTK_DIALOG(dialog))) {
531     gtk_widget_destroy(dialog);
532     return NULL;
533     }
534    
535     project_t *project = g_new0(project_t, 1);
536     project->name = g_strdup(gtk_entry_get_text(GTK_ENTRY(entry)));
537     gtk_widget_destroy(dialog);
538    
539    
540     project->path = g_strdup_printf("%s%s/",
541     context->settings->base_path, project->name);
542     project->desc = g_strdup(_("<project description>"));
543    
544     /* no data downloaded yet */
545     project->data_dirty = TRUE;
546    
547 harbaum 167 /* adjust default server stored in settings if required */
548     if(strstr(context->settings->server, "0.5") != NULL) {
549     strstr(context->settings->server, "0.5")[2] = '6';
550     printf("adjusting server path in settings to 0.6\n");
551     }
552    
553 harbaum 1 /* use global server/access settings */
554     project->server = g_strdup(context->settings->server);
555 harbaum 167
556 harbaum 1 /* build project osm file name */
557 harbaum 175 project->osm = g_strdup_printf("%s.osm", project->name);
558 harbaum 1
559     /* around the castle in karlsruhe, germany ... */
560     project->min.lat = 49.005; project->min.lon = 8.3911;
561     project->max.lat = 49.023; project->max.lon = 8.4185;
562    
563     /* create project file on disk */
564     project_save(context->dialog, project);
565    
566     #ifdef USE_HILDON
567 harbaum 169 if(!project_edit(context->dialog, context->settings,
568     project, context->mmpos, context->osso_context))
569 harbaum 1 #else
570 harbaum 169 if(!project_edit(context->dialog, context->settings, project))
571 harbaum 1 #endif
572     {
573     printf("edit cancelled!!\n");
574    
575     project_delete(context, project);
576    
577     project = NULL;
578     }
579    
580 harbaum 14 /* enable/disable edit/remove buttons */
581     view_selected(context, project);
582    
583 harbaum 1 return project;
584     }
585    
586 achadwick 45 // predecs
587     void project_get_status_icon_stock_id(project_t *project, gchar **stock_id);
588    
589 harbaum 1 static void on_project_new(GtkButton *button, gpointer data) {
590     select_context_t *context = (select_context_t*)data;
591     project_t **project = &context->project;
592     *project = project_new(context);
593     if(*project) {
594    
595 harbaum 146 GtkTreeModel *model = list_get_model(context->list);
596 harbaum 1
597     GtkTreeIter iter;
598 achadwick 45 gchar *status_stock_id = NULL;
599     project_get_status_icon_stock_id(*project, &status_stock_id);
600 harbaum 1 gtk_list_store_append(GTK_LIST_STORE(model), &iter);
601     gtk_list_store_set(GTK_LIST_STORE(model), &iter,
602     PROJECT_COL_NAME, (*project)->name,
603 achadwick 45 PROJECT_COL_STATUS, status_stock_id,
604 harbaum 1 PROJECT_COL_DESCRIPTION, (*project)->desc,
605     PROJECT_COL_DATA, *project,
606     -1);
607    
608 harbaum 146 GtkTreeSelection *selection = list_get_selection(context->list);
609 harbaum 1 gtk_tree_selection_select_iter(selection, &iter);
610     }
611     }
612    
613     static void on_project_delete(GtkButton *button, gpointer data) {
614     select_context_t *context = (select_context_t*)data;
615 harbaum 146 project_t *project = project_get_selected(context->list);
616 harbaum 1
617     char *str = g_strdup_printf(_("Do you really want to delete the "
618     "project \"%s\"?"), project->name);
619     GtkWidget *dialog = gtk_message_dialog_new(
620     GTK_WINDOW(context->dialog),
621     GTK_DIALOG_DESTROY_WITH_PARENT,
622     GTK_MESSAGE_QUESTION, GTK_BUTTONS_YES_NO, str);
623     g_free(str);
624    
625     gtk_window_set_title(GTK_WINDOW(dialog), _("Delete project?"));
626    
627     /* set the active flag again if the user answered "no" */
628     if(GTK_RESPONSE_NO == gtk_dialog_run(GTK_DIALOG(dialog))) {
629     gtk_widget_destroy(dialog);
630     return;
631     }
632    
633     gtk_widget_destroy(dialog);
634    
635     if(!project_delete(context, project))
636     printf("unable to delete project\n");
637     }
638    
639     static void on_project_edit(GtkButton *button, gpointer data) {
640     select_context_t *context = (select_context_t*)data;
641 harbaum 146 project_t *project = project_get_selected(context->list);
642 harbaum 1 g_assert(project);
643     #ifdef USE_HILDON
644 harbaum 169 if(project_edit(context->dialog, context->settings, project,
645 harbaum 1 context->mmpos, context->osso_context))
646     #else
647 harbaum 169 if(project_edit(context->dialog, context->settings, project))
648 harbaum 1 #endif
649     {
650     GtkTreeModel *model;
651     GtkTreeIter iter;
652    
653 achadwick 45 /* description etc. may have changed, so update list */
654 harbaum 146 GtkTreeSelection *selection = list_get_selection(context->list);
655 harbaum 1 g_assert(gtk_tree_selection_get_selected(selection, &model, &iter));
656    
657     // gtk_tree_model_get(model, &iter, PROJECT_COL_DATA, &project, -1);
658 achadwick 45 gchar *status_stock_id = NULL;
659     project_get_status_icon_stock_id(project, &status_stock_id);
660 harbaum 1 gtk_list_store_set(GTK_LIST_STORE(model), &iter,
661     PROJECT_COL_NAME, project->name,
662 achadwick 45 PROJECT_COL_STATUS, status_stock_id,
663 harbaum 1 PROJECT_COL_DESCRIPTION, project->desc,
664     -1);
665    
666    
667     }
668 harbaum 14
669     /* enable/disable edit/remove buttons */
670     view_selected(context, project);
671 harbaum 1 }
672    
673 achadwick 45
674     gboolean project_osm_present(project_t *project) {
675     char *osm_name = g_strdup_printf("%s/%s.osm", project->path, project->name);
676     gboolean is_present = g_file_test(osm_name, G_FILE_TEST_EXISTS);
677     g_free(osm_name);
678     return is_present;
679     }
680    
681     void project_get_status_icon_stock_id(project_t *project, gchar **stock_id) {
682     *stock_id = (! project_osm_present(project)) ? GTK_STOCK_DIALOG_WARNING
683     : diff_present(project) ? GTK_STOCK_PROPERTIES
684     : GTK_STOCK_FILE;
685     // TODO: check for outdatedness too. Which icon to use?
686     }
687    
688 harbaum 1 static GtkWidget *project_list_widget(select_context_t *context) {
689 harbaum 148 context->list = list_new(LIST_HILDON_WITHOUT_HEADERS);
690 harbaum 1
691 harbaum 146 list_set_selection_function(context->list, view_selection_func, context);
692 harbaum 1
693 harbaum 146 list_set_columns(context->list,
694     _("Name"), PROJECT_COL_NAME, 0,
695     _("State"), PROJECT_COL_STATUS, LIST_FLAG_STOCK_ICON,
696 harbaum 148 _("Description"), PROJECT_COL_DESCRIPTION, LIST_FLAG_ELLIPSIZE,
697 harbaum 146 NULL);
698    
699 harbaum 1
700     /* build the store */
701     GtkListStore *store = gtk_list_store_new(PROJECT_NUM_COLS,
702 achadwick 45 G_TYPE_STRING, // name
703     G_TYPE_STRING, // status
704     G_TYPE_STRING, // desc
705     G_TYPE_POINTER); // data
706 harbaum 1
707     GtkTreeIter iter;
708     project_t *project = context->project;
709     while(project) {
710 achadwick 45 gchar *status_stock_id = NULL;
711     project_get_status_icon_stock_id(project, &status_stock_id);
712 harbaum 1 /* Append a row and fill in some data */
713     gtk_list_store_append(store, &iter);
714     gtk_list_store_set(store, &iter,
715     PROJECT_COL_NAME, project->name,
716 achadwick 45 PROJECT_COL_STATUS, status_stock_id,
717 harbaum 1 PROJECT_COL_DESCRIPTION, project->desc,
718     PROJECT_COL_DATA, project,
719     -1);
720     project = project->next;
721     }
722    
723 harbaum 146 list_set_store(context->list, store);
724 harbaum 1 g_object_unref(store);
725    
726 harbaum 146 list_set_static_buttons(context->list, G_CALLBACK(on_project_new),
727     G_CALLBACK(on_project_edit), G_CALLBACK(on_project_delete), context);
728 harbaum 1
729 harbaum 146 return context->list;
730 harbaum 1 }
731    
732     char *project_select(appdata_t *appdata) {
733     char *name = NULL;
734    
735     select_context_t *context = g_new0(select_context_t, 1);
736     #ifdef USE_HILDON
737     context->mmpos = &appdata->mmpos;
738     context->osso_context = appdata->osso_context;
739     #endif
740     context->settings = appdata->settings;
741     context->project = project_scan(appdata);
742    
743     /* create project selection dialog */
744 harbaum 167 context->dialog =
745     misc_dialog_new(MISC_DIALOG_MEDIUM,_("Project selection"),
746     GTK_WINDOW(appdata->window),
747 harbaum 1 GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT,
748     GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT,
749     NULL);
750    
751     gtk_box_pack_start_defaults(GTK_BOX(GTK_DIALOG(context->dialog)->vbox),
752     project_list_widget(context));
753    
754     /* don't all user to click ok until something is selected */
755     gtk_dialog_set_response_sensitive(GTK_DIALOG(context->dialog),
756     GTK_RESPONSE_ACCEPT, FALSE);
757    
758     gtk_widget_show_all(context->dialog);
759     if(GTK_RESPONSE_ACCEPT == gtk_dialog_run(GTK_DIALOG(context->dialog)))
760 harbaum 146 name = g_strdup(project_get_selected(context->list)->name);
761 harbaum 1
762     gtk_widget_destroy(context->dialog);
763    
764     /* free all entries */
765     project_t *project = context->project;
766     while(project) {
767     project_t *next = project->next;
768     project_free(project);
769     project = next;
770     }
771    
772     g_free(context);
773    
774     return name;
775     }
776    
777     /* ---------------------------------------------------- */
778    
779     /* return file length or -1 on error */
780 harbaum 175 static gsize file_length(char *path, char *name) {
781     char *str = NULL;
782    
783     if(name[0] == '/') str = g_strdup(name);
784     else str = g_strjoin(NULL, path, name, NULL);
785    
786     GMappedFile *gmap = g_mapped_file_new(str, FALSE, NULL);
787     g_free(str);
788    
789 harbaum 1 if(!gmap) return -1;
790     gsize size = g_mapped_file_get_length(gmap);
791     g_mapped_file_free(gmap);
792     return size;
793     }
794    
795     void project_filesize(project_context_t *context) {
796     char *str = NULL;
797    
798     printf("Checking size of %s\n", context->project->osm);
799    
800 harbaum 175 if(!osm_file_exists(context->project->path, context->project->osm)) {
801 harbaum 1 GdkColor color;
802     gdk_color_parse("red", &color);
803     gtk_widget_modify_fg(context->fsize, GTK_STATE_NORMAL, &color);
804    
805     str = g_strdup(_("Not downloaded!"));
806     gtk_dialog_set_response_sensitive(GTK_DIALOG(context->dialog),
807     GTK_RESPONSE_ACCEPT, 0);
808     } else {
809     gtk_widget_modify_fg(context->fsize, GTK_STATE_NORMAL, NULL);
810    
811     if(!context->project->data_dirty)
812     str = g_strdup_printf(_("%d bytes present"),
813 harbaum 175 file_length(context->project->path,
814     context->project->osm));
815 harbaum 1 else
816     str = g_strdup_printf(_("Outdated, please download!"));
817    
818     /* project also must not be dirty to proceed */
819     gtk_dialog_set_response_sensitive(GTK_DIALOG(context->dialog),
820     GTK_RESPONSE_ACCEPT, !context->project->data_dirty);
821     }
822    
823     if(str) {
824     gtk_label_set_text(GTK_LABEL(context->fsize), str);
825     g_free(str);
826     }
827     }
828    
829     void project_diffstat(project_context_t *context) {
830     char *str = NULL;
831    
832     if(diff_present(context->project))
833     str = g_strdup(_("present"));
834     else
835     str = g_strdup(_("not present"));
836    
837     gtk_label_set_text(GTK_LABEL(context->diff_stat), str);
838     g_free(str);
839     }
840    
841     static void project_update(project_context_t *context) {
842    
843     /* fetch values from dialog */
844     if(context->project->desc) g_free(context->project->desc);
845     context->project->desc = g_strdup(gtk_entry_get_text(
846     GTK_ENTRY(context->desc)));
847    
848     if(context->project->server) g_free(context->project->server);
849     context->project->server = g_strdup(gtk_entry_get_text(
850     GTK_ENTRY(context->server)));
851     }
852    
853     static void on_edit_clicked(GtkButton *button, gpointer data) {
854     project_context_t *context = (project_context_t*)data;
855    
856     if(area_edit(&context->area_edit)) {
857     printf("coordinates changed!!\n");
858    
859     pos_lon_label_set(context->minlat, context->project->min.lat);
860     pos_lon_label_set(context->minlon, context->project->min.lon);
861     pos_lon_label_set(context->maxlat, context->project->max.lat);
862     pos_lon_label_set(context->maxlon, context->project->max.lon);
863     }
864     }
865    
866     static void on_download_clicked(GtkButton *button, gpointer data) {
867     project_context_t *context = (project_context_t*)data;
868    
869     project_update(context);
870    
871     printf("download %s\n", context->project->osm);
872    
873 harbaum 169 if(osm_download(context->dialog, context->settings, context->project)) {
874 harbaum 1 context->project->data_dirty = FALSE;
875     project_filesize(context);
876     } else
877     printf("download failed\n");
878     }
879    
880     static void on_diff_remove_clicked(GtkButton *button, gpointer data) {
881     project_context_t *context = (project_context_t*)data;
882    
883     printf("clicked diff remove\n");
884    
885     GtkWidget *dialog = gtk_message_dialog_new(
886     GTK_WINDOW(context->dialog),
887     GTK_DIALOG_DESTROY_WITH_PARENT,
888     GTK_MESSAGE_QUESTION, GTK_BUTTONS_YES_NO,
889     _("Do you really want to remove the diff file? This "
890     "will delete all changes you've made so far and which "
891     "you didn't upload yet."));
892    
893     gtk_window_set_title(GTK_WINDOW(dialog), _("Remove diff?"));
894    
895     /* set the active flag again if the user answered "no" */
896     if(GTK_RESPONSE_YES == gtk_dialog_run(GTK_DIALOG(dialog))) {
897     diff_remove(context->project);
898     project_diffstat(context);
899     gtk_widget_set_sensitive(context->diff_remove, FALSE);
900     }
901    
902     gtk_widget_destroy(dialog);
903     }
904    
905 harbaum 178 gboolean project_check_demo(GtkWidget *parent, project_t *project) {
906     if(!project->server)
907     messagef(parent, "Demo project",
908     "This is a preinstalled demo project. This means that the "
909     "basic project parameters cannot be changed and no data can "
910     "be up- or downloaded via the OSM servers.\n\n"
911     "Please setup a new project to do these things.");
912    
913     return !project->server;
914     }
915    
916    
917 harbaum 169 gboolean project_edit(GtkWidget *parent, settings_t *settings,
918     project_t *project POS_PARM) {
919 harbaum 1 gboolean ok = FALSE;
920    
921 harbaum 178 if(project_check_demo(parent, project))
922     return ok;
923    
924 harbaum 1 /* ------------ project edit dialog ------------- */
925    
926     project_context_t *context = g_new0(project_context_t, 1);
927     context->project = project;
928 harbaum 169 context->settings = settings;
929 harbaum 1
930     context->area_edit.min = &project->min;
931     context->area_edit.max = &project->max;
932     #ifdef USE_HILDON
933     context->area_edit.mmpos = mmpos;
934     context->area_edit.osso_context = osso_context;
935     #endif
936    
937     char *str = g_strdup_printf(_("Project - %s"), project->name);
938     context->area_edit.parent =
939 harbaum 167 context->dialog = misc_dialog_new(MISC_DIALOG_WIDE, str,
940     GTK_WINDOW(parent),
941 harbaum 1 GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT,
942     GTK_STOCK_OK, GTK_RESPONSE_ACCEPT,
943     NULL);
944     g_free(str);
945    
946     GtkWidget *download, *label;
947     GtkWidget *table = gtk_table_new(4, 6, FALSE); // x, y
948    
949     label = gtk_label_new(_("Description:"));
950     gtk_misc_set_alignment(GTK_MISC(label), 1.f, 0.5f);
951     gtk_table_attach_defaults(GTK_TABLE(table), label, 0, 1, 0, 1);
952     context->desc = gtk_entry_new();
953     gtk_entry_set_text(GTK_ENTRY(context->desc), project->desc);
954     gtk_table_attach_defaults(GTK_TABLE(table), context->desc, 1, 4, 0, 1);
955    
956     gtk_table_set_row_spacing(GTK_TABLE(table), 0, 4);
957    
958     label = gtk_label_new(_("Latitude"));
959     gtk_table_attach_defaults(GTK_TABLE(table), label, 1, 2, 1, 2);
960     label = gtk_label_new(_("Longitude"));
961     gtk_table_attach_defaults(GTK_TABLE(table), label, 2, 3, 1, 2);
962    
963     label = gtk_label_new(_("Min:"));
964     gtk_misc_set_alignment(GTK_MISC(label), 1.f, 0.5f);
965     gtk_table_attach_defaults(GTK_TABLE(table), label, 0, 1, 2, 3);
966     context->minlat = pos_lat_label_new(project->min.lat);
967     gtk_table_attach_defaults(GTK_TABLE(table), context->minlat, 1, 2, 2, 3);
968     context->minlon = pos_lon_label_new(project->min.lon);
969     gtk_table_attach_defaults(GTK_TABLE(table), context->minlon, 2, 3, 2, 3);
970    
971     label = gtk_label_new(_("Max:"));
972     gtk_misc_set_alignment(GTK_MISC(label), 1.f, 0.5f);
973     gtk_table_attach_defaults(GTK_TABLE(table), label, 0, 1, 3, 4);
974     context->maxlat = pos_lat_label_new(project->max.lat);
975     gtk_table_attach_defaults(GTK_TABLE(table), context->maxlat, 1, 2, 3, 4);
976     context->maxlon = pos_lon_label_new(project->max.lon);
977     gtk_table_attach_defaults(GTK_TABLE(table), context->maxlon, 2, 3, 3, 4);
978    
979 harbaum 177 GtkWidget *edit = gtk_button_new_with_label(_("Edit"));
980 harbaum 1 gtk_signal_connect(GTK_OBJECT(edit), "clicked",
981     (GtkSignalFunc)on_edit_clicked, context);
982     gtk_table_attach(GTK_TABLE(table), edit, 3, 4, 2, 4,
983     GTK_EXPAND | GTK_FILL,0,0,0);
984    
985     gtk_table_set_col_spacing(GTK_TABLE(table), 0, 4);
986     gtk_table_set_row_spacing(GTK_TABLE(table), 3, 4);
987    
988     label = gtk_label_new(_("Server:"));
989     gtk_misc_set_alignment(GTK_MISC(label), 1.f, 0.5f);
990     gtk_table_attach_defaults(GTK_TABLE(table), label, 0, 1, 4, 5);
991     context->server = gtk_entry_new();
992     HILDON_ENTRY_NO_AUTOCAP(context->server);
993     gtk_entry_set_text(GTK_ENTRY(context->server), project->server);
994     gtk_table_attach_defaults(GTK_TABLE(table), context->server, 1, 4, 4, 5);
995    
996     label = gtk_label_new(_("OSM file:"));
997     gtk_misc_set_alignment(GTK_MISC(label), 1.f, 0.5f);
998     gtk_table_attach_defaults(GTK_TABLE(table), label, 0, 1, 5, 6);
999     GtkWidget *hbox = gtk_hbox_new(FALSE, 0);
1000     context->fsize = gtk_label_new(_(""));
1001     gtk_misc_set_alignment(GTK_MISC(context->fsize), 0.f, 0.5f);
1002     project_filesize(context);
1003     gtk_box_pack_start_defaults(GTK_BOX(hbox), context->fsize);
1004 harbaum 177 download = gtk_button_new_with_label(_("Download"));
1005 harbaum 1 gtk_signal_connect(GTK_OBJECT(download), "clicked",
1006     (GtkSignalFunc)on_download_clicked, context);
1007     gtk_box_pack_start(GTK_BOX(hbox), download, FALSE, FALSE, 0);
1008     gtk_table_attach_defaults(GTK_TABLE(table), hbox, 1, 4, 5, 6);
1009    
1010     label = gtk_label_new(_("Diff file:"));
1011     gtk_misc_set_alignment(GTK_MISC(label), 1.f, 0.5f);
1012     gtk_table_attach_defaults(GTK_TABLE(table), label, 0, 1, 6, 7);
1013     hbox = gtk_hbox_new(FALSE, 0);
1014     context->diff_stat = gtk_label_new(_(""));
1015     gtk_misc_set_alignment(GTK_MISC(context->diff_stat), 0.f, 0.5f);
1016     project_diffstat(context);
1017     gtk_box_pack_start_defaults(GTK_BOX(hbox), context->diff_stat);
1018 harbaum 177 context->diff_remove = gtk_button_new_with_label(_("Remove"));
1019 harbaum 1 if(!diff_present(project))
1020     gtk_widget_set_sensitive(context->diff_remove, FALSE);
1021     gtk_signal_connect(GTK_OBJECT(context->diff_remove), "clicked",
1022     (GtkSignalFunc)on_diff_remove_clicked, context);
1023     gtk_box_pack_start(GTK_BOX(hbox), context->diff_remove, FALSE, FALSE, 0);
1024     gtk_table_attach_defaults(GTK_TABLE(table), hbox, 1, 4, 6, 7);
1025    
1026    
1027     gtk_box_pack_start_defaults(GTK_BOX(GTK_DIALOG(context->dialog)->vbox),
1028     table);
1029     gtk_widget_show_all(context->dialog);
1030    
1031     if(GTK_RESPONSE_ACCEPT == gtk_dialog_run(GTK_DIALOG(context->dialog))) {
1032     ok = TRUE;
1033    
1034     /* transfer values from edit dialog into project structure */
1035     project_update(context);
1036    
1037     /* save project */
1038     project_save(context->dialog, project);
1039     }
1040    
1041     gtk_widget_destroy(context->dialog);
1042     g_free(context);
1043    
1044     return ok;
1045     }
1046    
1047     gboolean project_open(appdata_t *appdata, char *name) {
1048     project_t *project = g_new0(project_t, 1);
1049    
1050     /* link to map state if a map already exists */
1051     if(appdata->map) {
1052     printf("Project: Using map state\n");
1053     project->map_state = appdata->map->state;
1054     } else {
1055     printf("Project: Creating new map_state\n");
1056 harbaum 173 project->map_state = map_state_new();
1057 harbaum 1 }
1058 harbaum 173
1059     map_state_reset(project->map_state);
1060 harbaum 1 project->map_state->refcount++;
1061    
1062     /* build project path */
1063     project->path = g_strdup_printf("%s%s/",
1064     appdata->settings->base_path, name);
1065     project->name = g_strdup(name);
1066    
1067     char *project_file = g_strdup_printf("%s%s.proj", project->path, name);
1068    
1069     printf("project file = %s\n", project_file);
1070     if(!g_file_test(project_file, G_FILE_TEST_IS_REGULAR)) {
1071     printf("requested project file doesn't exist\n");
1072     project_free(project);
1073     g_free(project_file);
1074     return FALSE;
1075     }
1076    
1077     if(!project_read(appdata, project_file, project)) {
1078     printf("error reading project file\n");
1079     project_free(project);
1080     g_free(project_file);
1081     return FALSE;
1082     }
1083    
1084     g_free(project_file);
1085    
1086     /* --------- project structure ok: load its OSM file --------- */
1087     appdata->project = project;
1088    
1089 harbaum 173 printf("project_open: loading osm %s\n", project->osm);
1090 harbaum 175 appdata->osm = osm_parse(project->path, project->osm);
1091 harbaum 173 if(!appdata->osm) {
1092     printf("OSM parsing failed\n");
1093     return FALSE;
1094     }
1095 harbaum 1
1096     printf("parsing ok\n");
1097    
1098     return TRUE;
1099     }
1100    
1101     gboolean project_close(appdata_t *appdata) {
1102     if(!appdata->project) return FALSE;
1103    
1104     printf("closing current project\n");
1105    
1106     /* redraw the entire map by destroying all map items and redrawing them */
1107     if(appdata->osm)
1108     diff_save(appdata->project, appdata->osm);
1109    
1110 achadwick 26 /* Save track and turn off the handler callback */
1111     track_save(appdata->project, appdata->track.track);
1112 harbaum 156 track_clear(appdata, appdata->track.track);
1113     appdata->track.track = NULL;
1114 achadwick 26
1115 harbaum 1 map_clear(appdata, MAP_LAYER_ALL);
1116    
1117     if(appdata->osm) {
1118     osm_free(&appdata->icon, appdata->osm);
1119     appdata->osm = NULL;
1120     }
1121    
1122 harbaum 174 /* update project file on disk */
1123     project_save(GTK_WIDGET(appdata->window), appdata->project);
1124    
1125 harbaum 1 project_free(appdata->project);
1126     appdata->project = NULL;
1127    
1128     return TRUE;
1129     }
1130    
1131 achadwick 28 #define _PROJECT_LOAD_BUF_SIZ 64
1132    
1133 harbaum 1 gboolean project_load(appdata_t *appdata, char *name) {
1134     char *proj_name = NULL;
1135    
1136     if(!name) {
1137     /* make user select a project */
1138     proj_name = project_select(appdata);
1139     if(!proj_name) {
1140     printf("no project selected\n");
1141     return FALSE;
1142     }
1143 achadwick 28 }
1144     else {
1145 harbaum 1 proj_name = g_strdup(name);
1146 achadwick 28 }
1147 harbaum 1
1148 achadwick 28 char banner_txt[_PROJECT_LOAD_BUF_SIZ];
1149     memset(banner_txt, 0, _PROJECT_LOAD_BUF_SIZ);
1150    
1151 harbaum 29 snprintf(banner_txt, _PROJECT_LOAD_BUF_SIZ, _("Loading %s"), proj_name);
1152 achadwick 28 banner_busy_start(appdata, TRUE, banner_txt);
1153    
1154 harbaum 1 /* close current project */
1155 achadwick 28 banner_busy_tick();
1156 harbaum 1 if(appdata->project)
1157     project_close(appdata);
1158    
1159     /* open project itself */
1160 achadwick 28 banner_busy_tick();
1161 harbaum 1 if(!project_open(appdata, proj_name)) {
1162     printf("error opening requested project\n");
1163 harbaum 159
1164     if(appdata->project) {
1165     project_free(appdata->project);
1166     appdata->project = NULL;
1167     }
1168    
1169     if(appdata->osm) {
1170     osm_free(&appdata->icon, appdata->osm);
1171     appdata->osm = NULL;
1172     }
1173    
1174     snprintf(banner_txt, _PROJECT_LOAD_BUF_SIZ,
1175     _("Error opening %s"), proj_name);
1176 achadwick 28 banner_busy_stop(appdata);
1177     banner_show_info(appdata, banner_txt);
1178 harbaum 159
1179 harbaum 1 g_free(proj_name);
1180     return FALSE;
1181     }
1182    
1183     /* check if OSM data is valid */
1184 achadwick 28 banner_busy_tick();
1185 harbaum 1 if(!osm_sanity_check(GTK_WIDGET(appdata->window), appdata->osm)) {
1186     printf("project/osm sanity checks failed, unloading project\n");
1187 harbaum 159
1188     if(appdata->project) {
1189     project_free(appdata->project);
1190     appdata->project = NULL;
1191     }
1192    
1193     if(appdata->osm) {
1194     osm_free(&appdata->icon, appdata->osm);
1195     appdata->osm = NULL;
1196     }
1197    
1198     snprintf(banner_txt, _PROJECT_LOAD_BUF_SIZ,
1199     _("Error opening %s"), proj_name);
1200 achadwick 28 banner_busy_stop(appdata);
1201     banner_show_info(appdata, banner_txt);
1202 harbaum 159
1203 achadwick 28 g_free(proj_name);
1204 harbaum 1 return FALSE;
1205     }
1206    
1207     /* load diff possibly preset */
1208 achadwick 28 banner_busy_tick();
1209 harbaum 1 diff_restore(appdata, appdata->project, appdata->osm);
1210    
1211     /* prepare colors etc, draw data and adjust scroll/zoom settings */
1212 achadwick 28 banner_busy_tick();
1213 harbaum 1 map_init(appdata);
1214    
1215     /* restore a track */
1216 achadwick 28 banner_busy_tick();
1217 harbaum 1 appdata->track.track = track_restore(appdata, appdata->project);
1218     if(appdata->track.track)
1219     map_track_draw(appdata->map, appdata->track.track);
1220    
1221     /* finally load a background if present */
1222 achadwick 28 banner_busy_tick();
1223 harbaum 1 wms_load(appdata);
1224    
1225     /* save the name of the project for the perferences */
1226     if(appdata->settings->project)
1227     g_free(appdata->settings->project);
1228     appdata->settings->project = g_strdup(appdata->project->name);
1229    
1230 harbaum 179 snprintf(banner_txt, _PROJECT_LOAD_BUF_SIZ, _("Loaded %s"), proj_name);
1231 harbaum 178
1232 achadwick 28 banner_busy_stop(appdata);
1233     banner_show_info(appdata, banner_txt);
1234     statusbar_set(appdata, NULL, 0);
1235    
1236     g_free(proj_name);
1237 harbaum 1 return TRUE;
1238     }
1239 harbaum 185
1240     /* ------------------- project setup wizard ----------------- */
1241    
1242 harbaum 186 typedef struct wizard_page_s {
1243 harbaum 185 const gchar *title;
1244 harbaum 186 GtkWidget* (*setup)(struct wizard_page_s *page);
1245 harbaum 185 GtkAssistantPageType type;
1246     gboolean complete;
1247 harbaum 186 GtkWidget *widget;
1248     gint index;
1249 harbaum 185 } wizard_page_t;
1250    
1251 harbaum 186 typedef struct {
1252     gboolean running;
1253    
1254     int page_num;
1255     wizard_page_t *page;
1256     } wizard_t;
1257    
1258    
1259     static gint on_assistant_destroy(GtkWidget *widget, wizard_t *wizard) {
1260     printf("destroy callback\n");
1261     wizard->running = FALSE;
1262 harbaum 185 return FALSE;
1263     }
1264    
1265 harbaum 186 static void on_assistant_cancel(GtkWidget *widget, wizard_t *wizard) {
1266     printf("cancel callback\n");
1267     wizard->running = FALSE;
1268     }
1269    
1270     static void on_assistant_close(GtkWidget *widget, wizard_t *wizard) {
1271     printf("close callback\n");
1272     wizard->running = FALSE;
1273     }
1274    
1275     static GtkWidget *wizard_text(const char *text) {
1276     GtkTextBuffer *buffer = gtk_text_buffer_new(NULL);
1277     gtk_text_buffer_set_text(buffer, text, -1);
1278    
1279     #ifndef USE_HILDON_TEXT_VIEW
1280     GtkWidget *view = gtk_text_view_new_with_buffer(buffer);
1281     #else
1282     GtkWidget *view = hildon_text_view_new();
1283     hildon_text_view_set_buffer(HILDON_TEXT_VIEW(view), buffer);
1284     #endif
1285    
1286     gtk_text_view_set_wrap_mode(GTK_TEXT_VIEW(view), GTK_WRAP_WORD);
1287     gtk_text_view_set_editable(GTK_TEXT_VIEW(view), FALSE);
1288     gtk_text_view_set_left_margin(GTK_TEXT_VIEW(view), 2 );
1289     gtk_text_view_set_right_margin(GTK_TEXT_VIEW(view), 2 );
1290     gtk_text_view_set_cursor_visible(GTK_TEXT_VIEW(view), FALSE );
1291    
1292     return view;
1293     }
1294    
1295     static GtkWidget *wizard_create_intro_page(wizard_page_t *page) {
1296     static const char *text =
1297     "This wizard will guide you through the setup of a new project.\n\n"
1298     "An osm2go project covers a certain area of the world as seen "
1299     "by openstreetmap.org. The wizard will help you downloading "
1300     "the data describing that area and will enable you to make changes "
1301     "to it using osm2go.";
1302    
1303     return wizard_text(text);
1304     }
1305    
1306     static GtkWidget *wizard_create_source_selection_page(wizard_page_t *page) {
1307     GtkWidget *vbox = gtk_vbox_new(FALSE, 0);
1308    
1309     gtk_box_pack_start_defaults(GTK_BOX(vbox),
1310     wizard_text("Please choose how to determine the area you "
1311     "are planning to work on."));
1312    
1313     /* add selection buttons */
1314     GtkWidget *choice1 =
1315     gtk_radio_button_new_with_label(NULL,
1316     _("Use current GPS position"));
1317     gtk_box_pack_start(GTK_BOX(vbox), choice1, TRUE, TRUE, 2);
1318    
1319     GtkWidget *choice2 =
1320     gtk_radio_button_new_with_label_from_widget(GTK_RADIO_BUTTON(choice1),
1321     _("Get from Maemo Mapper"));
1322     gtk_box_pack_start(GTK_BOX(vbox), choice2, TRUE, TRUE, 2);
1323    
1324     GtkWidget *choice3 =
1325     gtk_radio_button_new_with_label_from_widget(GTK_RADIO_BUTTON(choice1),
1326     _("Specify area manually"));
1327     gtk_box_pack_start(GTK_BOX(vbox), choice3, TRUE, TRUE, 2);
1328    
1329    
1330    
1331     return vbox;
1332     }
1333    
1334 harbaum 185 void project_wizard(appdata_t *appdata) {
1335 harbaum 186 wizard_page_t page[] = {
1336     { "Introduction", wizard_create_intro_page,
1337     GTK_ASSISTANT_PAGE_INTRO, TRUE},
1338     { "Area source selection", wizard_create_source_selection_page,
1339     GTK_ASSISTANT_PAGE_CONTENT, FALSE},
1340     { "Click the Check Button", NULL,
1341     GTK_ASSISTANT_PAGE_CONTENT, FALSE},
1342     { "Click the Button", NULL,
1343     GTK_ASSISTANT_PAGE_PROGRESS, FALSE},
1344     { "Confirmation", NULL,
1345     GTK_ASSISTANT_PAGE_CONFIRM, TRUE},
1346 harbaum 185 };
1347    
1348 harbaum 186 wizard_t wizard = {
1349     TRUE,
1350    
1351     /* the pages themselves */
1352     sizeof(page) / sizeof(wizard_page_t), page
1353     };
1354    
1355 harbaum 185 GtkWidget *assistant = gtk_assistant_new();
1356     gtk_widget_set_size_request (assistant, 450, 300);
1357    
1358     /* Add five pages to the GtkAssistant dialog. */
1359     int i;
1360 harbaum 186 for (i = 0; i < wizard.page_num; i++) {
1361     if(wizard.page[i].setup)
1362     wizard.page[i].widget =
1363     wizard.page[i].setup(&wizard.page[i]);
1364     else {
1365     char *str = g_strdup_printf("Page %d", i);
1366     wizard.page[i].widget = gtk_label_new(str);
1367     g_free(str);
1368     }
1369 harbaum 185
1370     page[i].index = gtk_assistant_append_page(GTK_ASSISTANT (assistant),
1371 harbaum 186 wizard.page[i].widget);
1372 harbaum 185
1373     gtk_assistant_set_page_title (GTK_ASSISTANT (assistant),
1374 harbaum 186 wizard.page[i].widget, wizard.page[i].title);
1375 harbaum 185 gtk_assistant_set_page_type (GTK_ASSISTANT (assistant),
1376 harbaum 186 wizard.page[i].widget, wizard.page[i].type);
1377 harbaum 185
1378     /* Set the introduction and conclusion pages as complete so they can be
1379     * incremented or closed. */
1380     gtk_assistant_set_page_complete (GTK_ASSISTANT (assistant),
1381 harbaum 186 wizard.page[i].widget, wizard.page[i].complete);
1382 harbaum 185 }
1383    
1384     /* make it a modal subdialog of the main window */
1385     gtk_window_set_modal(GTK_WINDOW(assistant), TRUE);
1386     gtk_window_set_transient_for(GTK_WINDOW(assistant),
1387     GTK_WINDOW(appdata->window));
1388    
1389     gtk_widget_show_all(assistant);
1390    
1391     g_signal_connect(G_OBJECT(assistant), "destroy",
1392 harbaum 186 G_CALLBACK(on_assistant_destroy), &wizard);
1393 harbaum 185
1394 harbaum 186 g_signal_connect(G_OBJECT(assistant), "cancel",
1395     G_CALLBACK(on_assistant_cancel), &wizard);
1396    
1397     g_signal_connect(G_OBJECT(assistant), "close",
1398     G_CALLBACK(on_assistant_close), &wizard);
1399    
1400 harbaum 185 do {
1401     if(gtk_events_pending())
1402     gtk_main_iteration();
1403     else
1404     usleep(100000);
1405    
1406 harbaum 186 } while(wizard.running);
1407 harbaum 185
1408     gtk_widget_destroy(assistant);
1409     }
1410    
1411    
1412 achadwick 28 // vim:et:ts=8:sw=2:sts=2:ai