Contents of /trunk/src/project.c

Parent Directory Parent Directory | Revision Log Revision Log


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