Contents of /trunk/src/project.c

Parent Directory Parent Directory | Revision Log Revision Log


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