Contents of /trunk/src/project.c

Parent Directory Parent Directory | Revision Log Revision Log


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