Contents of /trunk/src/project.c

Parent Directory Parent Directory | Revision Log Revision Log


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