Contents of /trunk/src/project.c

Parent Directory Parent Directory | Revision Log Revision Log


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