Contents of /trunk/src/project.c

Parent Directory Parent Directory | Revision Log Revision Log


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