Contents of /trunk/src/project.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 173 - (hide annotations)
Sat May 2 21:36:51 2009 UTC (15 years, 1 month ago) by harbaum
File MIME type: text/plain
File size: 36945 byte(s)
Fixed crash when switching projects
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     project->osm = g_strdup(str);
130     printf("osm = %s\n", project->osm);
131     xmlFree(str);
132     } else if(strcasecmp((char*)node->name, "min") == 0) {
133     if((str = (char*)xmlGetProp(node, BAD_CAST "lat"))) {
134     project->min.lat = g_ascii_strtod(str, NULL);
135     xmlFree(str);
136     }
137     if((str = (char*)xmlGetProp(node, BAD_CAST "lon"))) {
138     project->min.lon = g_ascii_strtod(str, NULL);
139     xmlFree(str);
140     }
141    
142     } else if(strcasecmp((char*)node->name, "max") == 0) {
143     if((str = (char*)xmlGetProp(node, BAD_CAST "lat"))) {
144     project->max.lat = g_ascii_strtod(str, NULL);
145     xmlFree(str);
146     }
147     if((str = (char*)xmlGetProp(node, BAD_CAST "lon"))) {
148     project->max.lon = g_ascii_strtod(str, NULL);
149     xmlFree(str);
150     }
151     }
152     }
153     node = node->next;
154     }
155     }
156     }
157     }
158    
159     xmlFreeDoc(doc);
160     xmlCleanupParser();
161    
162     return TRUE;
163     }
164    
165     gboolean project_save(GtkWidget *parent, project_t *project) {
166     char str[32];
167     char *project_file = g_strdup_printf("%s%s.proj",
168     project->path, project->name);
169    
170     printf("saving project to %s\n", project_file);
171    
172     /* check if project path exists */
173     if(!g_file_test(project->path, G_FILE_TEST_IS_DIR)) {
174     /* make sure project base path exists */
175     if(g_mkdir_with_parents(project->path, S_IRWXU) != 0) {
176     errorf(GTK_WIDGET(parent),
177     _("Unable to create project path %s"), project->path);
178     return FALSE;
179     }
180     }
181    
182     LIBXML_TEST_VERSION;
183    
184     xmlDocPtr doc = xmlNewDoc(BAD_CAST "1.0");
185     xmlNodePtr node, root_node = xmlNewNode(NULL, BAD_CAST "proj");
186     xmlNewProp(root_node, BAD_CAST "name", BAD_CAST project->name);
187     if(project->data_dirty)
188     xmlNewProp(root_node, BAD_CAST "dirty", BAD_CAST "true");
189    
190     xmlDocSetRootElement(doc, root_node);
191    
192     node = xmlNewChild(root_node, NULL, BAD_CAST "server",
193     BAD_CAST project->server);
194    
195     xmlNewChild(root_node, NULL, BAD_CAST "desc", BAD_CAST project->desc);
196     xmlNewChild(root_node, NULL, BAD_CAST "osm", BAD_CAST project->osm);
197    
198     node = xmlNewChild(root_node, NULL, BAD_CAST "min", NULL);
199 harbaum 156 g_ascii_formatd(str, sizeof(str), LL_FORMAT, project->min.lat);
200 harbaum 1 xmlNewProp(node, BAD_CAST "lat", BAD_CAST str);
201 harbaum 156 g_ascii_formatd(str, sizeof(str), LL_FORMAT, project->min.lon);
202 harbaum 1 xmlNewProp(node, BAD_CAST "lon", BAD_CAST str);
203    
204     node = xmlNewChild(root_node, NULL, BAD_CAST "max", NULL);
205 harbaum 156 g_ascii_formatd(str, sizeof(str), LL_FORMAT, project->max.lat);
206 harbaum 1 xmlNewProp(node, BAD_CAST "lat", BAD_CAST str);
207 harbaum 156 g_ascii_formatd(str, sizeof(str), LL_FORMAT, project->max.lon);
208 harbaum 1 xmlNewProp(node, BAD_CAST "lon", BAD_CAST str);
209    
210     if(project->map_state) {
211     node = xmlNewChild(root_node, NULL, BAD_CAST "map", BAD_CAST NULL);
212 harbaum 162 g_ascii_formatd(str, sizeof(str), "%.04f", project->map_state->zoom);
213 harbaum 1 xmlNewProp(node, BAD_CAST "zoom", BAD_CAST str);
214 harbaum 162 g_ascii_formatd(str, sizeof(str), "%.04f", project->map_state->detail);
215     xmlNewProp(node, BAD_CAST "detail", BAD_CAST str);
216 harbaum 1 snprintf(str, sizeof(str), "%d", project->map_state->scroll_offset.x);
217     xmlNewProp(node, BAD_CAST "scroll-offset-x", BAD_CAST str);
218     snprintf(str, sizeof(str), "%d", project->map_state->scroll_offset.y);
219     xmlNewProp(node, BAD_CAST "scroll-offset-y", BAD_CAST str);
220     }
221    
222     node = xmlNewChild(root_node, NULL, BAD_CAST "wms", NULL);
223 harbaum 14 if(project->wms_server)
224     xmlNewProp(node, BAD_CAST "server", BAD_CAST project->wms_server);
225     if(project->wms_path)
226     xmlNewProp(node, BAD_CAST "path", BAD_CAST project->wms_path);
227 harbaum 1 snprintf(str, sizeof(str), "%d", project->wms_offset.x);
228     xmlNewProp(node, BAD_CAST "x-offset", BAD_CAST str);
229     snprintf(str, sizeof(str), "%d", project->wms_offset.y);
230     xmlNewProp(node, BAD_CAST "y-offset", BAD_CAST str);
231    
232     xmlSaveFormatFileEnc(project_file, doc, "UTF-8", 1);
233     xmlFreeDoc(doc);
234     xmlCleanupParser();
235    
236     g_free(project_file);
237    
238     return TRUE;
239     }
240    
241     /* ------------ freeing projects --------------------- */
242    
243     void project_free(project_t *project) {
244     if(!project) return;
245    
246     if(project->name) g_free(project->name);
247     if(project->desc) g_free(project->desc);
248     if(project->server) g_free(project->server);
249    
250     if(project->wms_server) g_free(project->wms_server);
251     if(project->wms_path) g_free(project->wms_path);
252    
253     if(project->path) g_free(project->path);
254     if(project->osm) g_free(project->osm);
255    
256     map_state_free(project->map_state);
257    
258     g_free(project);
259     }
260    
261     /* ------------ project selection dialog ------------- */
262    
263     static char *project_fullname(settings_t *settings, const char *name) {
264     return g_strdup_printf("%s%s/%s.proj", settings->base_path, name, name);
265     }
266    
267     static gboolean project_exists(settings_t *settings, const char *name) {
268     gboolean ok = FALSE;
269     char *fulldir = g_strdup_printf("%s%s", settings->base_path, name);
270    
271     if(g_file_test(fulldir, G_FILE_TEST_IS_DIR)) {
272    
273     /* check for project file */
274     char *fullname = project_fullname(settings, name);
275    
276     if(g_file_test(fullname, G_FILE_TEST_IS_REGULAR))
277     ok = TRUE;
278    
279     g_free(fullname);
280     }
281     g_free(fulldir);
282    
283     return ok;
284     }
285    
286     static project_t *project_scan(appdata_t *appdata) {
287     project_t *projects = NULL, **current = &projects;
288    
289     /* scan for projects */
290     GDir *dir = g_dir_open(appdata->settings->base_path, 0, NULL);
291     const char *name = NULL;
292     do {
293     if((name = g_dir_read_name(dir))) {
294     if(project_exists(appdata->settings, name)) {
295     printf("found project %s\n", name);
296    
297     /* try to read project and append it to chain */
298     *current = g_new0(project_t, 1);
299     (*current)->name = g_strdup(name);
300     (*current)->path = g_strdup_printf("%s%s/",
301     appdata->settings->base_path, name);
302    
303     char *fullname = project_fullname(appdata->settings, name);
304     if(project_read(appdata, fullname, *current))
305     current = &((*current)->next);
306     else {
307     g_free(*current);
308     *current = NULL;
309     }
310     g_free(fullname);
311     }
312     }
313     } while(name);
314    
315     g_dir_close(dir);
316    
317     return projects;
318     }
319    
320     typedef struct {
321     project_t *project;
322 harbaum 146 GtkWidget *dialog, *list;
323 harbaum 1 settings_t *settings;
324     #ifdef USE_HILDON
325     dbus_mm_pos_t *mmpos;
326     osso_context_t *osso_context;
327     #endif
328     } select_context_t;
329    
330     enum {
331     PROJECT_COL_NAME = 0,
332 achadwick 45 PROJECT_COL_STATUS,
333 harbaum 1 PROJECT_COL_DESCRIPTION,
334     PROJECT_COL_DATA,
335     PROJECT_NUM_COLS
336     };
337    
338     static void view_selected(select_context_t *context, project_t *project) {
339 harbaum 146 list_button_enable(context->list, LIST_BUTTON_REMOVE, project != NULL);
340     list_button_enable(context->list, LIST_BUTTON_EDIT, project != NULL);
341 harbaum 1
342     /* check if the selected project also has a valid osm file */
343     gtk_dialog_set_response_sensitive(GTK_DIALOG(context->dialog),
344     GTK_RESPONSE_ACCEPT,
345     project && g_file_test(project->osm, G_FILE_TEST_IS_REGULAR));
346     }
347    
348     static gboolean
349     view_selection_func(GtkTreeSelection *selection, GtkTreeModel *model,
350     GtkTreePath *path, gboolean path_currently_selected,
351     gpointer userdata) {
352     select_context_t *context = (select_context_t*)userdata;
353     GtkTreeIter iter;
354    
355     if(gtk_tree_model_get_iter(model, &iter, path)) {
356     project_t *project = NULL;
357     gtk_tree_model_get(model, &iter, PROJECT_COL_DATA, &project, -1);
358     g_assert(gtk_tree_path_get_depth(path) == 1);
359    
360     view_selected(context, project);
361     }
362    
363     return TRUE; /* allow selection state to change */
364     }
365    
366     /* get the currently selected project in the list, NULL if none */
367 harbaum 146 static project_t *project_get_selected(GtkWidget *list) {
368 harbaum 1 project_t *project = NULL;
369     GtkTreeModel *model;
370     GtkTreeIter iter;
371    
372 harbaum 146 GtkTreeSelection *selection = list_get_selection(list);
373 harbaum 1 g_assert(gtk_tree_selection_get_selected(selection, &model, &iter));
374     gtk_tree_model_get(model, &iter, PROJECT_COL_DATA, &project, -1);
375    
376     return project;
377     }
378    
379     /* ------------------------- create a new project ---------------------- */
380    
381     /* returns true of str contains one of the characters in chars */
382     static gboolean strchrs(char *str, char *chars) {
383     while(*chars) {
384     char *p = str;
385     while(*p) {
386     if(*p == *chars)
387     return TRUE;
388    
389     p++;
390     }
391     chars++;
392     }
393     return FALSE;
394     }
395    
396     typedef struct {
397     GtkWidget *dialog;
398     settings_t *settings;
399     } name_callback_context_t;
400    
401     static void callback_modified_name(GtkWidget *widget, gpointer data) {
402     name_callback_context_t *context = (name_callback_context_t*)data;
403    
404     char *name = (char*)gtk_entry_get_text(GTK_ENTRY(widget));
405    
406     /* name must not contain some special chars */
407     gboolean ok = FALSE;
408    
409     /* check if there's a name */
410     if(name && strlen(name) > 0) {
411     /* check if it consists of valid characters */
412     if(!strchrs(name, "\\*?()\n\t\r")) {
413     /* check if such a project already exists */
414     if(!project_exists(context->settings, name))
415     ok = TRUE;
416     }
417     }
418    
419     gtk_dialog_set_response_sensitive(GTK_DIALOG(context->dialog),
420     GTK_RESPONSE_ACCEPT, ok);
421     }
422    
423    
424     gboolean project_delete(select_context_t *context, project_t *project) {
425    
426     /* remove entire directory from disk */
427     GDir *dir = g_dir_open(project->path, 0, NULL);
428     const char *name = NULL;
429     do {
430     if((name = g_dir_read_name(dir))) {
431     char *fullname = g_strdup_printf("%s/%s", project->path, name);
432     g_remove(fullname);
433     g_free(fullname);
434     }
435     } while(name);
436    
437     /* remove the projects directory */
438     g_remove(project->path);
439    
440     /* remove from view */
441     GtkTreeIter iter;
442 harbaum 146 GtkTreeModel *model = list_get_model(context->list);
443 harbaum 1 gboolean deleted = FALSE;
444     if(gtk_tree_model_get_iter_first(model, &iter)) {
445     do {
446     project_t *prj = NULL;
447     gtk_tree_model_get(model, &iter, PROJECT_COL_DATA, &prj, -1);
448     if(prj && (prj == project)) {
449     printf("found %s to remove\n", prj->name);
450     /* and remove from store */
451     gtk_list_store_remove(GTK_LIST_STORE(model), &iter);
452     deleted = TRUE;
453     }
454     } while(!deleted && gtk_tree_model_iter_next(model, &iter));
455     }
456    
457     /* de-chain entry from project list */
458     project_t **project_list = &context->project;
459     while(*project_list) {
460     if(*project_list == project)
461     *project_list = (*project_list)->next;
462     else
463     project_list = &((*project_list)->next);
464     }
465    
466     /* free project structure */
467     project_free(project);
468    
469     /* disable edit/remove buttons */
470     view_selected(context, NULL);
471    
472     return TRUE;
473     }
474    
475     project_t *project_new(select_context_t *context) {
476     printf("creating project with default values\n");
477    
478     /* -------------- first choose a name for the project --------------- */
479 harbaum 167 GtkWidget *dialog =
480     misc_dialog_new(MISC_DIALOG_NOSIZE, _("Project name"),
481     GTK_WINDOW(context->dialog),
482     GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT,
483     GTK_STOCK_OK, GTK_RESPONSE_ACCEPT,
484     NULL);
485 harbaum 1
486     GtkWidget *hbox = gtk_hbox_new(FALSE, 8);
487     gtk_box_pack_start_defaults(GTK_BOX(hbox), gtk_label_new(_("Name:")));
488    
489     name_callback_context_t name_context = { dialog, context->settings };
490     GtkWidget *entry = gtk_entry_new();
491     // gtk_entry_set_text(GTK_ENTRY(entry), "<enter name>");
492     gtk_box_pack_start_defaults(GTK_BOX(hbox), entry);
493     g_signal_connect(G_OBJECT(entry), "changed",
494     G_CALLBACK(callback_modified_name), &name_context);
495    
496     gtk_box_pack_start_defaults(GTK_BOX(GTK_DIALOG(dialog)->vbox), hbox);
497    
498     /* don't all user to click ok until something useful has been entered */
499     gtk_dialog_set_response_sensitive(GTK_DIALOG(dialog),
500     GTK_RESPONSE_ACCEPT, FALSE);
501    
502     gtk_widget_show_all(dialog);
503     if(GTK_RESPONSE_ACCEPT != gtk_dialog_run(GTK_DIALOG(dialog))) {
504     gtk_widget_destroy(dialog);
505     return NULL;
506     }
507    
508     project_t *project = g_new0(project_t, 1);
509     project->name = g_strdup(gtk_entry_get_text(GTK_ENTRY(entry)));
510     gtk_widget_destroy(dialog);
511    
512    
513     project->path = g_strdup_printf("%s%s/",
514     context->settings->base_path, project->name);
515     project->desc = g_strdup(_("<project description>"));
516    
517     /* no data downloaded yet */
518     project->data_dirty = TRUE;
519    
520 harbaum 167 /* adjust default server stored in settings if required */
521     if(strstr(context->settings->server, "0.5") != NULL) {
522     strstr(context->settings->server, "0.5")[2] = '6';
523     printf("adjusting server path in settings to 0.6\n");
524     }
525    
526 harbaum 1 /* use global server/access settings */
527     project->server = g_strdup(context->settings->server);
528 harbaum 167
529 harbaum 1 /* build project osm file name */
530     project->osm = g_strdup_printf("%s%s.osm", project->path, project->name);
531    
532     /* around the castle in karlsruhe, germany ... */
533     project->min.lat = 49.005; project->min.lon = 8.3911;
534     project->max.lat = 49.023; project->max.lon = 8.4185;
535    
536     /* create project file on disk */
537     project_save(context->dialog, project);
538    
539     #ifdef USE_HILDON
540 harbaum 169 if(!project_edit(context->dialog, context->settings,
541     project, context->mmpos, context->osso_context))
542 harbaum 1 #else
543 harbaum 169 if(!project_edit(context->dialog, context->settings, project))
544 harbaum 1 #endif
545     {
546     printf("edit cancelled!!\n");
547    
548     project_delete(context, project);
549    
550     project = NULL;
551     }
552    
553 harbaum 14 /* enable/disable edit/remove buttons */
554     view_selected(context, project);
555    
556 harbaum 1 return project;
557     }
558    
559 achadwick 45 // predecs
560     void project_get_status_icon_stock_id(project_t *project, gchar **stock_id);
561    
562 harbaum 1 static void on_project_new(GtkButton *button, gpointer data) {
563     select_context_t *context = (select_context_t*)data;
564     project_t **project = &context->project;
565     *project = project_new(context);
566     if(*project) {
567    
568 harbaum 146 GtkTreeModel *model = list_get_model(context->list);
569 harbaum 1
570     GtkTreeIter iter;
571 achadwick 45 gchar *status_stock_id = NULL;
572     project_get_status_icon_stock_id(*project, &status_stock_id);
573 harbaum 1 gtk_list_store_append(GTK_LIST_STORE(model), &iter);
574     gtk_list_store_set(GTK_LIST_STORE(model), &iter,
575     PROJECT_COL_NAME, (*project)->name,
576 achadwick 45 PROJECT_COL_STATUS, status_stock_id,
577 harbaum 1 PROJECT_COL_DESCRIPTION, (*project)->desc,
578     PROJECT_COL_DATA, *project,
579     -1);
580    
581 harbaum 146 GtkTreeSelection *selection = list_get_selection(context->list);
582 harbaum 1 gtk_tree_selection_select_iter(selection, &iter);
583     }
584     }
585    
586     static void on_project_delete(GtkButton *button, gpointer data) {
587     select_context_t *context = (select_context_t*)data;
588 harbaum 146 project_t *project = project_get_selected(context->list);
589 harbaum 1
590     char *str = g_strdup_printf(_("Do you really want to delete the "
591     "project \"%s\"?"), project->name);
592     GtkWidget *dialog = gtk_message_dialog_new(
593     GTK_WINDOW(context->dialog),
594     GTK_DIALOG_DESTROY_WITH_PARENT,
595     GTK_MESSAGE_QUESTION, GTK_BUTTONS_YES_NO, str);
596     g_free(str);
597    
598     gtk_window_set_title(GTK_WINDOW(dialog), _("Delete project?"));
599    
600     /* set the active flag again if the user answered "no" */
601     if(GTK_RESPONSE_NO == gtk_dialog_run(GTK_DIALOG(dialog))) {
602     gtk_widget_destroy(dialog);
603     return;
604     }
605    
606     gtk_widget_destroy(dialog);
607    
608     if(!project_delete(context, project))
609     printf("unable to delete project\n");
610     }
611    
612     static void on_project_edit(GtkButton *button, gpointer data) {
613     select_context_t *context = (select_context_t*)data;
614 harbaum 146 project_t *project = project_get_selected(context->list);
615 harbaum 1 g_assert(project);
616     #ifdef USE_HILDON
617 harbaum 169 if(project_edit(context->dialog, context->settings, project,
618 harbaum 1 context->mmpos, context->osso_context))
619     #else
620 harbaum 169 if(project_edit(context->dialog, context->settings, project))
621 harbaum 1 #endif
622     {
623     GtkTreeModel *model;
624     GtkTreeIter iter;
625    
626 achadwick 45 /* description etc. may have changed, so update list */
627 harbaum 146 GtkTreeSelection *selection = list_get_selection(context->list);
628 harbaum 1 g_assert(gtk_tree_selection_get_selected(selection, &model, &iter));
629    
630     // gtk_tree_model_get(model, &iter, PROJECT_COL_DATA, &project, -1);
631 achadwick 45 gchar *status_stock_id = NULL;
632     project_get_status_icon_stock_id(project, &status_stock_id);
633 harbaum 1 gtk_list_store_set(GTK_LIST_STORE(model), &iter,
634     PROJECT_COL_NAME, project->name,
635 achadwick 45 PROJECT_COL_STATUS, status_stock_id,
636 harbaum 1 PROJECT_COL_DESCRIPTION, project->desc,
637     -1);
638    
639    
640     }
641 harbaum 14
642     /* enable/disable edit/remove buttons */
643     view_selected(context, project);
644 harbaum 1 }
645    
646 achadwick 45
647     gboolean project_osm_present(project_t *project) {
648     char *osm_name = g_strdup_printf("%s/%s.osm", project->path, project->name);
649     gboolean is_present = g_file_test(osm_name, G_FILE_TEST_EXISTS);
650     g_free(osm_name);
651     return is_present;
652     }
653    
654     void project_get_status_icon_stock_id(project_t *project, gchar **stock_id) {
655     *stock_id = (! project_osm_present(project)) ? GTK_STOCK_DIALOG_WARNING
656     : diff_present(project) ? GTK_STOCK_PROPERTIES
657     : GTK_STOCK_FILE;
658     // TODO: check for outdatedness too. Which icon to use?
659     }
660    
661 harbaum 1 static GtkWidget *project_list_widget(select_context_t *context) {
662 harbaum 148 context->list = list_new(LIST_HILDON_WITHOUT_HEADERS);
663 harbaum 1
664 harbaum 146 list_set_selection_function(context->list, view_selection_func, context);
665 harbaum 1
666 harbaum 146 list_set_columns(context->list,
667     _("Name"), PROJECT_COL_NAME, 0,
668     _("State"), PROJECT_COL_STATUS, LIST_FLAG_STOCK_ICON,
669 harbaum 148 _("Description"), PROJECT_COL_DESCRIPTION, LIST_FLAG_ELLIPSIZE,
670 harbaum 146 NULL);
671    
672 harbaum 1
673     /* build the store */
674     GtkListStore *store = gtk_list_store_new(PROJECT_NUM_COLS,
675 achadwick 45 G_TYPE_STRING, // name
676     G_TYPE_STRING, // status
677     G_TYPE_STRING, // desc
678     G_TYPE_POINTER); // data
679 harbaum 1
680     GtkTreeIter iter;
681     project_t *project = context->project;
682     while(project) {
683 achadwick 45 gchar *status_stock_id = NULL;
684     project_get_status_icon_stock_id(project, &status_stock_id);
685 harbaum 1 /* Append a row and fill in some data */
686     gtk_list_store_append(store, &iter);
687     gtk_list_store_set(store, &iter,
688     PROJECT_COL_NAME, project->name,
689 achadwick 45 PROJECT_COL_STATUS, status_stock_id,
690 harbaum 1 PROJECT_COL_DESCRIPTION, project->desc,
691     PROJECT_COL_DATA, project,
692     -1);
693     project = project->next;
694     }
695    
696 harbaum 146 list_set_store(context->list, store);
697 harbaum 1 g_object_unref(store);
698    
699 harbaum 146 list_set_static_buttons(context->list, G_CALLBACK(on_project_new),
700     G_CALLBACK(on_project_edit), G_CALLBACK(on_project_delete), context);
701 harbaum 1
702 harbaum 146 return context->list;
703 harbaum 1 }
704    
705     char *project_select(appdata_t *appdata) {
706     char *name = NULL;
707    
708     select_context_t *context = g_new0(select_context_t, 1);
709     #ifdef USE_HILDON
710     context->mmpos = &appdata->mmpos;
711     context->osso_context = appdata->osso_context;
712     #endif
713     context->settings = appdata->settings;
714     context->project = project_scan(appdata);
715    
716     /* create project selection dialog */
717 harbaum 167 context->dialog =
718     misc_dialog_new(MISC_DIALOG_MEDIUM,_("Project selection"),
719     GTK_WINDOW(appdata->window),
720 harbaum 1 GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT,
721     GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT,
722     NULL);
723    
724     gtk_box_pack_start_defaults(GTK_BOX(GTK_DIALOG(context->dialog)->vbox),
725     project_list_widget(context));
726    
727     /* don't all user to click ok until something is selected */
728     gtk_dialog_set_response_sensitive(GTK_DIALOG(context->dialog),
729     GTK_RESPONSE_ACCEPT, FALSE);
730    
731     gtk_widget_show_all(context->dialog);
732     if(GTK_RESPONSE_ACCEPT == gtk_dialog_run(GTK_DIALOG(context->dialog)))
733 harbaum 146 name = g_strdup(project_get_selected(context->list)->name);
734 harbaum 1
735     gtk_widget_destroy(context->dialog);
736    
737     /* free all entries */
738     project_t *project = context->project;
739     while(project) {
740     project_t *next = project->next;
741     project_free(project);
742     project = next;
743     }
744    
745     g_free(context);
746    
747     return name;
748     }
749    
750     /* ---------------------------------------------------- */
751    
752     /* return file length or -1 on error */
753     static gsize file_length(char *name) {
754     GMappedFile *gmap = g_mapped_file_new(name, FALSE, NULL);
755     if(!gmap) return -1;
756     gsize size = g_mapped_file_get_length(gmap);
757     g_mapped_file_free(gmap);
758     return size;
759     }
760    
761     void project_filesize(project_context_t *context) {
762     char *str = NULL;
763    
764     printf("Checking size of %s\n", context->project->osm);
765    
766     if(!g_file_test(context->project->osm, G_FILE_TEST_IS_REGULAR)) {
767     GdkColor color;
768     gdk_color_parse("red", &color);
769     gtk_widget_modify_fg(context->fsize, GTK_STATE_NORMAL, &color);
770    
771     str = g_strdup(_("Not downloaded!"));
772     gtk_dialog_set_response_sensitive(GTK_DIALOG(context->dialog),
773     GTK_RESPONSE_ACCEPT, 0);
774     } else {
775     gtk_widget_modify_fg(context->fsize, GTK_STATE_NORMAL, NULL);
776    
777     if(!context->project->data_dirty)
778     str = g_strdup_printf(_("%d bytes present"),
779     file_length(context->project->osm));
780     else
781     str = g_strdup_printf(_("Outdated, please download!"));
782    
783     /* project also must not be dirty to proceed */
784     gtk_dialog_set_response_sensitive(GTK_DIALOG(context->dialog),
785     GTK_RESPONSE_ACCEPT, !context->project->data_dirty);
786     }
787    
788     if(str) {
789     gtk_label_set_text(GTK_LABEL(context->fsize), str);
790     g_free(str);
791     }
792     }
793    
794     void project_diffstat(project_context_t *context) {
795     char *str = NULL;
796    
797     if(diff_present(context->project))
798     str = g_strdup(_("present"));
799     else
800     str = g_strdup(_("not present"));
801    
802     gtk_label_set_text(GTK_LABEL(context->diff_stat), str);
803     g_free(str);
804     }
805    
806     static void project_update(project_context_t *context) {
807    
808     /* fetch values from dialog */
809     if(context->project->desc) g_free(context->project->desc);
810     context->project->desc = g_strdup(gtk_entry_get_text(
811     GTK_ENTRY(context->desc)));
812    
813     if(context->project->server) g_free(context->project->server);
814     context->project->server = g_strdup(gtk_entry_get_text(
815     GTK_ENTRY(context->server)));
816     }
817    
818     static void on_edit_clicked(GtkButton *button, gpointer data) {
819     project_context_t *context = (project_context_t*)data;
820    
821     if(area_edit(&context->area_edit)) {
822     printf("coordinates changed!!\n");
823    
824     pos_lon_label_set(context->minlat, context->project->min.lat);
825     pos_lon_label_set(context->minlon, context->project->min.lon);
826     pos_lon_label_set(context->maxlat, context->project->max.lat);
827     pos_lon_label_set(context->maxlon, context->project->max.lon);
828     }
829     }
830    
831     static void on_download_clicked(GtkButton *button, gpointer data) {
832     project_context_t *context = (project_context_t*)data;
833    
834     project_update(context);
835    
836     printf("download %s\n", context->project->osm);
837    
838 harbaum 169 if(osm_download(context->dialog, context->settings, context->project)) {
839 harbaum 1 context->project->data_dirty = FALSE;
840     project_filesize(context);
841     } else
842     printf("download failed\n");
843     }
844    
845     static void on_diff_remove_clicked(GtkButton *button, gpointer data) {
846     project_context_t *context = (project_context_t*)data;
847    
848     printf("clicked diff remove\n");
849    
850     GtkWidget *dialog = gtk_message_dialog_new(
851     GTK_WINDOW(context->dialog),
852     GTK_DIALOG_DESTROY_WITH_PARENT,
853     GTK_MESSAGE_QUESTION, GTK_BUTTONS_YES_NO,
854     _("Do you really want to remove the diff file? This "
855     "will delete all changes you've made so far and which "
856     "you didn't upload yet."));
857    
858     gtk_window_set_title(GTK_WINDOW(dialog), _("Remove diff?"));
859    
860     /* set the active flag again if the user answered "no" */
861     if(GTK_RESPONSE_YES == gtk_dialog_run(GTK_DIALOG(dialog))) {
862     diff_remove(context->project);
863     project_diffstat(context);
864     gtk_widget_set_sensitive(context->diff_remove, FALSE);
865     }
866    
867     gtk_widget_destroy(dialog);
868     }
869    
870 harbaum 169 gboolean project_edit(GtkWidget *parent, settings_t *settings,
871     project_t *project POS_PARM) {
872 harbaum 1 gboolean ok = FALSE;
873    
874     /* ------------ project edit dialog ------------- */
875    
876     project_context_t *context = g_new0(project_context_t, 1);
877     context->project = project;
878 harbaum 169 context->settings = settings;
879 harbaum 1
880     context->area_edit.min = &project->min;
881     context->area_edit.max = &project->max;
882     #ifdef USE_HILDON
883     context->area_edit.mmpos = mmpos;
884     context->area_edit.osso_context = osso_context;
885     #endif
886    
887     char *str = g_strdup_printf(_("Project - %s"), project->name);
888     context->area_edit.parent =
889 harbaum 167 context->dialog = misc_dialog_new(MISC_DIALOG_WIDE, str,
890     GTK_WINDOW(parent),
891 harbaum 1 GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT,
892     GTK_STOCK_OK, GTK_RESPONSE_ACCEPT,
893     NULL);
894     g_free(str);
895    
896     GtkWidget *download, *label;
897     GtkWidget *table = gtk_table_new(4, 6, FALSE); // x, y
898    
899     label = gtk_label_new(_("Description:"));
900     gtk_misc_set_alignment(GTK_MISC(label), 1.f, 0.5f);
901     gtk_table_attach_defaults(GTK_TABLE(table), label, 0, 1, 0, 1);
902     context->desc = gtk_entry_new();
903     gtk_entry_set_text(GTK_ENTRY(context->desc), project->desc);
904     gtk_table_attach_defaults(GTK_TABLE(table), context->desc, 1, 4, 0, 1);
905    
906     gtk_table_set_row_spacing(GTK_TABLE(table), 0, 4);
907    
908     label = gtk_label_new(_("Latitude"));
909     gtk_table_attach_defaults(GTK_TABLE(table), label, 1, 2, 1, 2);
910     label = gtk_label_new(_("Longitude"));
911     gtk_table_attach_defaults(GTK_TABLE(table), label, 2, 3, 1, 2);
912    
913     label = gtk_label_new(_("Min:"));
914     gtk_misc_set_alignment(GTK_MISC(label), 1.f, 0.5f);
915     gtk_table_attach_defaults(GTK_TABLE(table), label, 0, 1, 2, 3);
916     context->minlat = pos_lat_label_new(project->min.lat);
917     gtk_table_attach_defaults(GTK_TABLE(table), context->minlat, 1, 2, 2, 3);
918     context->minlon = pos_lon_label_new(project->min.lon);
919     gtk_table_attach_defaults(GTK_TABLE(table), context->minlon, 2, 3, 2, 3);
920    
921     label = gtk_label_new(_("Max:"));
922     gtk_misc_set_alignment(GTK_MISC(label), 1.f, 0.5f);
923     gtk_table_attach_defaults(GTK_TABLE(table), label, 0, 1, 3, 4);
924     context->maxlat = pos_lat_label_new(project->max.lat);
925     gtk_table_attach_defaults(GTK_TABLE(table), context->maxlat, 1, 2, 3, 4);
926     context->maxlon = pos_lon_label_new(project->max.lon);
927     gtk_table_attach_defaults(GTK_TABLE(table), context->maxlon, 2, 3, 3, 4);
928    
929     GtkWidget *edit = gtk_button_new_with_label(_("Edit..."));
930     gtk_signal_connect(GTK_OBJECT(edit), "clicked",
931     (GtkSignalFunc)on_edit_clicked, context);
932     gtk_table_attach(GTK_TABLE(table), edit, 3, 4, 2, 4,
933     GTK_EXPAND | GTK_FILL,0,0,0);
934    
935     gtk_table_set_col_spacing(GTK_TABLE(table), 0, 4);
936     gtk_table_set_row_spacing(GTK_TABLE(table), 3, 4);
937    
938     label = gtk_label_new(_("Server:"));
939     gtk_misc_set_alignment(GTK_MISC(label), 1.f, 0.5f);
940     gtk_table_attach_defaults(GTK_TABLE(table), label, 0, 1, 4, 5);
941     context->server = gtk_entry_new();
942     HILDON_ENTRY_NO_AUTOCAP(context->server);
943     gtk_entry_set_text(GTK_ENTRY(context->server), project->server);
944     gtk_table_attach_defaults(GTK_TABLE(table), context->server, 1, 4, 4, 5);
945    
946     label = gtk_label_new(_("OSM file:"));
947     gtk_misc_set_alignment(GTK_MISC(label), 1.f, 0.5f);
948     gtk_table_attach_defaults(GTK_TABLE(table), label, 0, 1, 5, 6);
949     GtkWidget *hbox = gtk_hbox_new(FALSE, 0);
950     context->fsize = gtk_label_new(_(""));
951     gtk_misc_set_alignment(GTK_MISC(context->fsize), 0.f, 0.5f);
952     project_filesize(context);
953     gtk_box_pack_start_defaults(GTK_BOX(hbox), context->fsize);
954     download = gtk_button_new_with_label(_("Download..."));
955     gtk_signal_connect(GTK_OBJECT(download), "clicked",
956     (GtkSignalFunc)on_download_clicked, context);
957     gtk_box_pack_start(GTK_BOX(hbox), download, FALSE, FALSE, 0);
958     gtk_table_attach_defaults(GTK_TABLE(table), hbox, 1, 4, 5, 6);
959    
960     label = gtk_label_new(_("Diff file:"));
961     gtk_misc_set_alignment(GTK_MISC(label), 1.f, 0.5f);
962     gtk_table_attach_defaults(GTK_TABLE(table), label, 0, 1, 6, 7);
963     hbox = gtk_hbox_new(FALSE, 0);
964     context->diff_stat = gtk_label_new(_(""));
965     gtk_misc_set_alignment(GTK_MISC(context->diff_stat), 0.f, 0.5f);
966     project_diffstat(context);
967     gtk_box_pack_start_defaults(GTK_BOX(hbox), context->diff_stat);
968     context->diff_remove = gtk_button_new_with_label(_("Remove..."));
969     if(!diff_present(project))
970     gtk_widget_set_sensitive(context->diff_remove, FALSE);
971     gtk_signal_connect(GTK_OBJECT(context->diff_remove), "clicked",
972     (GtkSignalFunc)on_diff_remove_clicked, context);
973     gtk_box_pack_start(GTK_BOX(hbox), context->diff_remove, FALSE, FALSE, 0);
974     gtk_table_attach_defaults(GTK_TABLE(table), hbox, 1, 4, 6, 7);
975    
976    
977     gtk_box_pack_start_defaults(GTK_BOX(GTK_DIALOG(context->dialog)->vbox),
978     table);
979     gtk_widget_show_all(context->dialog);
980    
981     if(GTK_RESPONSE_ACCEPT == gtk_dialog_run(GTK_DIALOG(context->dialog))) {
982     ok = TRUE;
983    
984     /* transfer values from edit dialog into project structure */
985     project_update(context);
986    
987     /* save project */
988     project_save(context->dialog, project);
989     }
990    
991     gtk_widget_destroy(context->dialog);
992     g_free(context);
993    
994     return ok;
995     }
996    
997     gboolean project_open(appdata_t *appdata, char *name) {
998     project_t *project = g_new0(project_t, 1);
999    
1000     /* link to map state if a map already exists */
1001     if(appdata->map) {
1002     printf("Project: Using map state\n");
1003     project->map_state = appdata->map->state;
1004     } else {
1005     printf("Project: Creating new map_state\n");
1006 harbaum 173 project->map_state = map_state_new();
1007 harbaum 1 }
1008 harbaum 173
1009     map_state_reset(project->map_state);
1010 harbaum 1 project->map_state->refcount++;
1011    
1012     /* build project path */
1013     project->path = g_strdup_printf("%s%s/",
1014     appdata->settings->base_path, name);
1015     project->name = g_strdup(name);
1016    
1017     char *project_file = g_strdup_printf("%s%s.proj", project->path, name);
1018    
1019     printf("project file = %s\n", project_file);
1020     if(!g_file_test(project_file, G_FILE_TEST_IS_REGULAR)) {
1021     printf("requested project file doesn't exist\n");
1022     project_free(project);
1023     g_free(project_file);
1024     return FALSE;
1025     }
1026    
1027     if(!project_read(appdata, project_file, project)) {
1028     printf("error reading project file\n");
1029     project_free(project);
1030     g_free(project_file);
1031     return FALSE;
1032     }
1033    
1034     g_free(project_file);
1035    
1036     /* --------- project structure ok: load its OSM file --------- */
1037     appdata->project = project;
1038    
1039 harbaum 173 printf("project_open: loading osm %s\n", project->osm);
1040 harbaum 1 appdata->osm = osm_parse(project->osm);
1041 harbaum 173 if(!appdata->osm) {
1042     printf("OSM parsing failed\n");
1043     return FALSE;
1044     }
1045 harbaum 1
1046     printf("parsing ok\n");
1047    
1048     return TRUE;
1049     }
1050    
1051     gboolean project_close(appdata_t *appdata) {
1052     if(!appdata->project) return FALSE;
1053    
1054     printf("closing current project\n");
1055    
1056     /* redraw the entire map by destroying all map items and redrawing them */
1057     if(appdata->osm)
1058     diff_save(appdata->project, appdata->osm);
1059    
1060 achadwick 26 /* Save track and turn off the handler callback */
1061     track_save(appdata->project, appdata->track.track);
1062 harbaum 156 track_clear(appdata, appdata->track.track);
1063     appdata->track.track = NULL;
1064 achadwick 26
1065 harbaum 1 map_clear(appdata, MAP_LAYER_ALL);
1066    
1067     if(appdata->osm) {
1068     osm_free(&appdata->icon, appdata->osm);
1069     appdata->osm = NULL;
1070     }
1071    
1072     project_free(appdata->project);
1073     appdata->project = NULL;
1074    
1075     return TRUE;
1076     }
1077    
1078 achadwick 28 #define _PROJECT_LOAD_BUF_SIZ 64
1079    
1080 harbaum 1 gboolean project_load(appdata_t *appdata, char *name) {
1081     char *proj_name = NULL;
1082    
1083     if(!name) {
1084     /* make user select a project */
1085     proj_name = project_select(appdata);
1086     if(!proj_name) {
1087     printf("no project selected\n");
1088     return FALSE;
1089     }
1090 achadwick 28 }
1091     else {
1092 harbaum 1 proj_name = g_strdup(name);
1093 achadwick 28 }
1094 harbaum 1
1095 achadwick 28 char banner_txt[_PROJECT_LOAD_BUF_SIZ];
1096     memset(banner_txt, 0, _PROJECT_LOAD_BUF_SIZ);
1097    
1098 harbaum 29 snprintf(banner_txt, _PROJECT_LOAD_BUF_SIZ, _("Loading %s"), proj_name);
1099 achadwick 28 banner_busy_start(appdata, TRUE, banner_txt);
1100    
1101 harbaum 1 /* close current project */
1102 achadwick 28 banner_busy_tick();
1103 harbaum 1 if(appdata->project)
1104     project_close(appdata);
1105    
1106     /* open project itself */
1107 achadwick 28 banner_busy_tick();
1108 harbaum 1 if(!project_open(appdata, proj_name)) {
1109     printf("error opening requested project\n");
1110 harbaum 159
1111     if(appdata->project) {
1112     project_free(appdata->project);
1113     appdata->project = NULL;
1114     }
1115    
1116     if(appdata->osm) {
1117     osm_free(&appdata->icon, appdata->osm);
1118     appdata->osm = NULL;
1119     }
1120    
1121     snprintf(banner_txt, _PROJECT_LOAD_BUF_SIZ,
1122     _("Error opening %s"), proj_name);
1123 achadwick 28 banner_busy_stop(appdata);
1124     banner_show_info(appdata, banner_txt);
1125 harbaum 159
1126 harbaum 1 g_free(proj_name);
1127     return FALSE;
1128     }
1129    
1130     /* check if OSM data is valid */
1131 achadwick 28 banner_busy_tick();
1132 harbaum 1 if(!osm_sanity_check(GTK_WIDGET(appdata->window), appdata->osm)) {
1133     printf("project/osm sanity checks failed, unloading project\n");
1134 harbaum 159
1135     if(appdata->project) {
1136     project_free(appdata->project);
1137     appdata->project = NULL;
1138     }
1139    
1140     if(appdata->osm) {
1141     osm_free(&appdata->icon, appdata->osm);
1142     appdata->osm = NULL;
1143     }
1144    
1145     snprintf(banner_txt, _PROJECT_LOAD_BUF_SIZ,
1146     _("Error opening %s"), proj_name);
1147 achadwick 28 banner_busy_stop(appdata);
1148     banner_show_info(appdata, banner_txt);
1149 harbaum 159
1150 achadwick 28 g_free(proj_name);
1151 harbaum 1 return FALSE;
1152     }
1153    
1154     /* load diff possibly preset */
1155 achadwick 28 banner_busy_tick();
1156 harbaum 1 diff_restore(appdata, appdata->project, appdata->osm);
1157    
1158     /* prepare colors etc, draw data and adjust scroll/zoom settings */
1159 achadwick 28 banner_busy_tick();
1160 harbaum 1 map_init(appdata);
1161    
1162     /* restore a track */
1163 achadwick 28 banner_busy_tick();
1164 harbaum 1 appdata->track.track = track_restore(appdata, appdata->project);
1165     if(appdata->track.track)
1166     map_track_draw(appdata->map, appdata->track.track);
1167    
1168     /* finally load a background if present */
1169 achadwick 28 banner_busy_tick();
1170 harbaum 1 wms_load(appdata);
1171    
1172     /* save the name of the project for the perferences */
1173     if(appdata->settings->project)
1174     g_free(appdata->settings->project);
1175     appdata->settings->project = g_strdup(appdata->project->name);
1176    
1177 harbaum 29 snprintf(banner_txt, _PROJECT_LOAD_BUF_SIZ, _("Loaded %s"), proj_name);
1178 achadwick 28 banner_busy_stop(appdata);
1179     banner_show_info(appdata, banner_txt);
1180     statusbar_set(appdata, NULL, 0);
1181    
1182     g_free(proj_name);
1183 harbaum 1 return TRUE;
1184     }
1185 achadwick 28 // vim:et:ts=8:sw=2:sts=2:ai