Contents of /trunk/src/project.c

Parent Directory Parent Directory | Revision Log Revision Log


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