Contents of /trunk/src/project.c

Parent Directory Parent Directory | Revision Log Revision Log


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