Contents of /trunk/src/project.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 192 - (show annotations)
Tue Jul 7 11:07:11 2009 UTC (14 years, 10 months ago) by harbaum
File MIME type: text/plain
File size: 43673 byte(s)
Fremantle widget sensitivity cleanups
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 project_t *project;
34 settings_t *settings;
35 GtkWidget *dialog, *fsize, *diff_stat, *diff_remove;
36 GtkWidget *desc, *server;
37 GtkWidget *minlat, *minlon, *maxlat, *maxlon;
38 area_edit_t area_edit;
39 } project_context_t;
40
41 /* ------------ project file io ------------- */
42
43 static gboolean project_read(appdata_t *appdata,
44 char *project_file, project_t *project) {
45
46 LIBXML_TEST_VERSION;
47
48 xmlDoc *doc = NULL;
49 xmlNode *root_element = NULL;
50
51 /* parse the file and get the DOM */
52 if((doc = xmlReadFile(project_file, NULL, 0)) == NULL) {
53 printf("error: could not parse file %s\n", project_file);
54 return FALSE;
55 }
56
57 /* Get the root element node */
58 root_element = xmlDocGetRootElement(doc);
59
60 xmlNode *cur_node = NULL;
61 for (cur_node = root_element; cur_node; cur_node = cur_node->next) {
62 if (cur_node->type == XML_ELEMENT_NODE) {
63 if(strcasecmp((char*)cur_node->name, "proj") == 0) {
64 char *str;
65
66 if((str = (char*)xmlGetProp(cur_node, BAD_CAST "dirty"))) {
67 project->data_dirty = (strcasecmp(str, "true") == 0);
68 xmlFree(str);
69 } else
70 project->data_dirty = FALSE;
71
72 xmlNode *node = cur_node->children;
73
74 while(node != NULL) {
75 if(node->type == XML_ELEMENT_NODE) {
76
77 if(strcasecmp((char*)node->name, "desc") == 0) {
78 str = (char*)xmlNodeListGetString(doc, node->children, 1);
79 project->desc = g_strdup(str);
80 printf("desc = %s\n", project->desc);
81 xmlFree(str);
82
83 } else if(strcasecmp((char*)node->name, "server") == 0) {
84 str = (char*)xmlNodeListGetString(doc, node->children, 1);
85 project->server = g_strdup(str);
86 printf("server = %s\n", project->server);
87 xmlFree(str);
88
89 } else if(project->map_state &&
90 strcasecmp((char*)node->name, "map") == 0) {
91 if((str = (char*)xmlGetProp(node, BAD_CAST "zoom"))) {
92 project->map_state->zoom = g_ascii_strtod(str, NULL);
93 xmlFree(str);
94 }
95 if((str = (char*)xmlGetProp(node, BAD_CAST "detail"))) {
96 project->map_state->detail = g_ascii_strtod(str, NULL);
97 xmlFree(str);
98 }
99 if((str = (char*)xmlGetProp(node, BAD_CAST "scroll-offset-x"))) {
100 project->map_state->scroll_offset.x = strtoul(str, NULL, 10);
101 xmlFree(str);
102 }
103 if((str = (char*)xmlGetProp(node, BAD_CAST "scroll-offset-y"))) {
104 project->map_state->scroll_offset.y = strtoul(str, NULL, 10);
105 xmlFree(str);
106 }
107
108 } else if(strcasecmp((char*)node->name, "wms") == 0) {
109
110 if((str = (char*)xmlGetProp(node, BAD_CAST "server"))) {
111 project->wms_server = g_strdup(str);
112 xmlFree(str);
113 }
114 if((str = (char*)xmlGetProp(node, BAD_CAST "path"))) {
115 project->wms_path = g_strdup(str);
116 xmlFree(str);
117 }
118 if((str = (char*)xmlGetProp(node, BAD_CAST "x-offset"))) {
119 project->wms_offset.x = strtoul(str, NULL, 10);
120 xmlFree(str);
121 }
122 if((str = (char*)xmlGetProp(node, BAD_CAST "y-offset"))) {
123 project->wms_offset.y = strtoul(str, NULL, 10);
124 xmlFree(str);
125 }
126
127 } else if(strcasecmp((char*)node->name, "osm") == 0) {
128 str = (char*)xmlNodeListGetString(doc, node->children, 1);
129 printf("osm = %s\n", str);
130
131 /* make this a relative path if possible */
132 /* if the project path actually is a prefix of this, */
133 /* then just remove this prefix */
134 if((str[0] == '/') &&
135 (strlen(str) > strlen(project->path)) &&
136 !strncmp(str, project->path, strlen(project->path))) {
137
138 project->osm = g_strdup(str + strlen(project->path));
139 printf("osm name converted to relative %s\n", project->osm);
140 } else
141 project->osm = g_strdup(str);
142
143 xmlFree(str);
144
145 } else if(strcasecmp((char*)node->name, "min") == 0) {
146 if((str = (char*)xmlGetProp(node, BAD_CAST "lat"))) {
147 project->min.lat = g_ascii_strtod(str, NULL);
148 xmlFree(str);
149 }
150 if((str = (char*)xmlGetProp(node, BAD_CAST "lon"))) {
151 project->min.lon = g_ascii_strtod(str, NULL);
152 xmlFree(str);
153 }
154
155 } else if(strcasecmp((char*)node->name, "max") == 0) {
156 if((str = (char*)xmlGetProp(node, BAD_CAST "lat"))) {
157 project->max.lat = g_ascii_strtod(str, NULL);
158 xmlFree(str);
159 }
160 if((str = (char*)xmlGetProp(node, BAD_CAST "lon"))) {
161 project->max.lon = g_ascii_strtod(str, NULL);
162 xmlFree(str);
163 }
164 }
165 }
166 node = node->next;
167 }
168 }
169 }
170 }
171
172 xmlFreeDoc(doc);
173 xmlCleanupParser();
174
175 return TRUE;
176 }
177
178 gboolean project_save(GtkWidget *parent, project_t *project) {
179 char str[32];
180 char *project_file = g_strdup_printf("%s%s.proj",
181 project->path, project->name);
182
183 printf("saving project to %s\n", project_file);
184
185 /* check if project path exists */
186 if(!g_file_test(project->path, G_FILE_TEST_IS_DIR)) {
187 /* make sure project base path exists */
188 if(g_mkdir_with_parents(project->path, S_IRWXU) != 0) {
189 errorf(GTK_WIDGET(parent),
190 _("Unable to create project path %s"), project->path);
191 return FALSE;
192 }
193 }
194
195 LIBXML_TEST_VERSION;
196
197 xmlDocPtr doc = xmlNewDoc(BAD_CAST "1.0");
198 xmlNodePtr node, root_node = xmlNewNode(NULL, BAD_CAST "proj");
199 xmlNewProp(root_node, BAD_CAST "name", BAD_CAST project->name);
200 if(project->data_dirty)
201 xmlNewProp(root_node, BAD_CAST "dirty", BAD_CAST "true");
202
203 xmlDocSetRootElement(doc, root_node);
204
205 if(project->server)
206 node = xmlNewChild(root_node, NULL, BAD_CAST "server",
207 BAD_CAST project->server);
208
209 xmlNewChild(root_node, NULL, BAD_CAST "desc", BAD_CAST project->desc);
210 xmlNewChild(root_node, NULL, BAD_CAST "osm", BAD_CAST project->osm);
211
212 node = xmlNewChild(root_node, NULL, BAD_CAST "min", NULL);
213 g_ascii_formatd(str, sizeof(str), LL_FORMAT, project->min.lat);
214 xmlNewProp(node, BAD_CAST "lat", BAD_CAST str);
215 g_ascii_formatd(str, sizeof(str), LL_FORMAT, project->min.lon);
216 xmlNewProp(node, BAD_CAST "lon", BAD_CAST str);
217
218 node = xmlNewChild(root_node, NULL, BAD_CAST "max", NULL);
219 g_ascii_formatd(str, sizeof(str), LL_FORMAT, project->max.lat);
220 xmlNewProp(node, BAD_CAST "lat", BAD_CAST str);
221 g_ascii_formatd(str, sizeof(str), LL_FORMAT, project->max.lon);
222 xmlNewProp(node, BAD_CAST "lon", BAD_CAST str);
223
224 if(project->map_state) {
225 node = xmlNewChild(root_node, NULL, BAD_CAST "map", BAD_CAST NULL);
226 g_ascii_formatd(str, sizeof(str), "%.04f", project->map_state->zoom);
227 xmlNewProp(node, BAD_CAST "zoom", BAD_CAST str);
228 g_ascii_formatd(str, sizeof(str), "%.04f", project->map_state->detail);
229 xmlNewProp(node, BAD_CAST "detail", BAD_CAST str);
230 snprintf(str, sizeof(str), "%d", project->map_state->scroll_offset.x);
231 xmlNewProp(node, BAD_CAST "scroll-offset-x", BAD_CAST str);
232 snprintf(str, sizeof(str), "%d", project->map_state->scroll_offset.y);
233 xmlNewProp(node, BAD_CAST "scroll-offset-y", BAD_CAST str);
234 }
235
236 node = xmlNewChild(root_node, NULL, BAD_CAST "wms", NULL);
237 if(project->wms_server)
238 xmlNewProp(node, BAD_CAST "server", BAD_CAST project->wms_server);
239 if(project->wms_path)
240 xmlNewProp(node, BAD_CAST "path", BAD_CAST project->wms_path);
241 snprintf(str, sizeof(str), "%d", project->wms_offset.x);
242 xmlNewProp(node, BAD_CAST "x-offset", BAD_CAST str);
243 snprintf(str, sizeof(str), "%d", project->wms_offset.y);
244 xmlNewProp(node, BAD_CAST "y-offset", BAD_CAST str);
245
246 xmlSaveFormatFileEnc(project_file, doc, "UTF-8", 1);
247 xmlFreeDoc(doc);
248 xmlCleanupParser();
249
250 g_free(project_file);
251
252 return TRUE;
253 }
254
255 /* ------------ freeing projects --------------------- */
256
257 void project_free(project_t *project) {
258 if(!project) return;
259
260 if(project->name) g_free(project->name);
261 if(project->desc) g_free(project->desc);
262 if(project->server) g_free(project->server);
263
264 if(project->wms_server) g_free(project->wms_server);
265 if(project->wms_path) g_free(project->wms_path);
266
267 if(project->path) g_free(project->path);
268 if(project->osm) g_free(project->osm);
269
270 map_state_free(project->map_state);
271
272 g_free(project);
273 }
274
275 /* ------------ project selection dialog ------------- */
276
277 static char *project_fullname(settings_t *settings, const char *name) {
278 return g_strdup_printf("%s%s/%s.proj", settings->base_path, name, name);
279 }
280
281 gboolean project_exists(settings_t *settings, const char *name) {
282 gboolean ok = FALSE;
283 char *fulldir = g_strdup_printf("%s%s", settings->base_path, name);
284
285 if(g_file_test(fulldir, G_FILE_TEST_IS_DIR)) {
286
287 /* check for project file */
288 char *fullname = project_fullname(settings, name);
289
290 if(g_file_test(fullname, G_FILE_TEST_IS_REGULAR))
291 ok = TRUE;
292
293 g_free(fullname);
294 }
295 g_free(fulldir);
296
297 return ok;
298 }
299
300 static project_t *project_scan(appdata_t *appdata) {
301 project_t *projects = NULL, **current = &projects;
302
303 /* scan for projects */
304 GDir *dir = g_dir_open(appdata->settings->base_path, 0, NULL);
305 const char *name = NULL;
306 do {
307 if((name = g_dir_read_name(dir))) {
308 if(project_exists(appdata->settings, name)) {
309 printf("found project %s\n", name);
310
311 /* try to read project and append it to chain */
312 *current = g_new0(project_t, 1);
313 (*current)->name = g_strdup(name);
314 (*current)->path = g_strdup_printf("%s%s/",
315 appdata->settings->base_path, name);
316
317 char *fullname = project_fullname(appdata->settings, name);
318 if(project_read(appdata, fullname, *current))
319 current = &((*current)->next);
320 else {
321 g_free(*current);
322 *current = NULL;
323 }
324 g_free(fullname);
325 }
326 }
327 } while(name);
328
329 g_dir_close(dir);
330
331 return projects;
332 }
333
334 typedef struct {
335 project_t *project;
336 GtkWidget *dialog, *list;
337 settings_t *settings;
338 #ifdef USE_HILDON
339 dbus_mm_pos_t *mmpos;
340 osso_context_t *osso_context;
341 #endif
342 } select_context_t;
343
344 enum {
345 PROJECT_COL_NAME = 0,
346 PROJECT_COL_STATUS,
347 PROJECT_COL_DESCRIPTION,
348 PROJECT_COL_DATA,
349 PROJECT_NUM_COLS
350 };
351
352 static gboolean osm_file_exists(char *path, char *name) {
353 gboolean exists = FALSE;
354
355 if(name[0] == '/')
356 exists = g_file_test(name, G_FILE_TEST_IS_REGULAR);
357 else {
358 char *full = g_strjoin(NULL, path, name, NULL);
359 exists = g_file_test(full, G_FILE_TEST_IS_REGULAR);
360 g_free(full);
361 }
362 return exists;
363 }
364
365 static void view_selected(select_context_t *context, project_t *project) {
366 list_button_enable(context->list, LIST_BUTTON_REMOVE, project != NULL);
367 list_button_enable(context->list, LIST_BUTTON_EDIT, project != NULL);
368
369 /* check if the selected project also has a valid osm file */
370 gtk_dialog_set_response_sensitive(GTK_DIALOG(context->dialog),
371 GTK_RESPONSE_ACCEPT,
372 project && osm_file_exists(project->path, project->osm));
373 }
374
375 static gboolean
376 view_selection_func(GtkTreeSelection *selection, GtkTreeModel *model,
377 GtkTreePath *path, gboolean path_currently_selected,
378 gpointer userdata) {
379 select_context_t *context = (select_context_t*)userdata;
380 GtkTreeIter iter;
381
382 if(gtk_tree_model_get_iter(model, &iter, path)) {
383 project_t *project = NULL;
384 gtk_tree_model_get(model, &iter, PROJECT_COL_DATA, &project, -1);
385 g_assert(gtk_tree_path_get_depth(path) == 1);
386
387 view_selected(context, project);
388 }
389
390 return TRUE; /* allow selection state to change */
391 }
392
393 /* get the currently selected project in the list, NULL if none */
394 static project_t *project_get_selected(GtkWidget *list) {
395 project_t *project = NULL;
396 GtkTreeModel *model;
397 GtkTreeIter iter;
398
399 GtkTreeSelection *selection = list_get_selection(list);
400 g_assert(gtk_tree_selection_get_selected(selection, &model, &iter));
401 gtk_tree_model_get(model, &iter, PROJECT_COL_DATA, &project, -1);
402
403 return project;
404 }
405
406 /* ------------------------- create a new project ---------------------- */
407
408 /* returns true of str contains one of the characters in chars */
409 static gboolean strchrs(char *str, char *chars) {
410 while(*chars) {
411 char *p = str;
412 while(*p) {
413 if(*p == *chars)
414 return TRUE;
415
416 p++;
417 }
418 chars++;
419 }
420 return FALSE;
421 }
422
423 typedef struct {
424 GtkWidget *dialog;
425 settings_t *settings;
426 } name_callback_context_t;
427
428 static void callback_modified_name(GtkWidget *widget, gpointer data) {
429 name_callback_context_t *context = (name_callback_context_t*)data;
430
431 char *name = (char*)gtk_entry_get_text(GTK_ENTRY(widget));
432
433 /* name must not contain some special chars */
434 gboolean ok = FALSE;
435
436 /* check if there's a name */
437 if(name && strlen(name) > 0) {
438 /* check if it consists of valid characters */
439 if(!strchrs(name, "\\*?()\n\t\r")) {
440 /* check if such a project already exists */
441 if(!project_exists(context->settings, name))
442 ok = TRUE;
443 }
444 }
445
446 gtk_dialog_set_response_sensitive(GTK_DIALOG(context->dialog),
447 GTK_RESPONSE_ACCEPT, ok);
448 }
449
450
451 gboolean project_delete(select_context_t *context, project_t *project) {
452
453 /* remove entire directory from disk */
454 GDir *dir = g_dir_open(project->path, 0, NULL);
455 const char *name = NULL;
456 do {
457 if((name = g_dir_read_name(dir))) {
458 char *fullname = g_strdup_printf("%s/%s", project->path, name);
459 g_remove(fullname);
460 g_free(fullname);
461 }
462 } while(name);
463
464 /* remove the projects directory */
465 g_remove(project->path);
466
467 /* remove from view */
468 GtkTreeIter iter;
469 GtkTreeModel *model = list_get_model(context->list);
470 gboolean deleted = FALSE;
471 if(gtk_tree_model_get_iter_first(model, &iter)) {
472 do {
473 project_t *prj = NULL;
474 gtk_tree_model_get(model, &iter, PROJECT_COL_DATA, &prj, -1);
475 if(prj && (prj == project)) {
476 printf("found %s to remove\n", prj->name);
477 /* and remove from store */
478 gtk_list_store_remove(GTK_LIST_STORE(model), &iter);
479 deleted = TRUE;
480 }
481 } while(!deleted && gtk_tree_model_iter_next(model, &iter));
482 }
483
484 /* de-chain entry from project list */
485 project_t **project_list = &context->project;
486 while(*project_list) {
487 if(*project_list == project)
488 *project_list = (*project_list)->next;
489 else
490 project_list = &((*project_list)->next);
491 }
492
493 /* free project structure */
494 project_free(project);
495
496 /* disable edit/remove buttons */
497 view_selected(context, NULL);
498
499 return TRUE;
500 }
501
502 project_t *project_new(select_context_t *context) {
503 printf("creating project with default values\n");
504
505 /* -------------- first choose a name for the project --------------- */
506 GtkWidget *dialog =
507 misc_dialog_new(MISC_DIALOG_NOSIZE, _("Project name"),
508 GTK_WINDOW(context->dialog),
509 GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT,
510 GTK_STOCK_OK, GTK_RESPONSE_ACCEPT,
511 NULL);
512
513 GtkWidget *hbox = gtk_hbox_new(FALSE, 8);
514 gtk_box_pack_start_defaults(GTK_BOX(hbox), gtk_label_new(_("Name:")));
515
516 name_callback_context_t name_context = { dialog, context->settings };
517 GtkWidget *entry = gtk_entry_new();
518 // gtk_entry_set_text(GTK_ENTRY(entry), "<enter name>");
519 gtk_box_pack_start_defaults(GTK_BOX(hbox), entry);
520 g_signal_connect(G_OBJECT(entry), "changed",
521 G_CALLBACK(callback_modified_name), &name_context);
522
523 gtk_box_pack_start_defaults(GTK_BOX(GTK_DIALOG(dialog)->vbox), hbox);
524
525 /* don't all user to click ok until something useful has been entered */
526 gtk_dialog_set_response_sensitive(GTK_DIALOG(dialog),
527 GTK_RESPONSE_ACCEPT, FALSE);
528
529 gtk_widget_show_all(dialog);
530 if(GTK_RESPONSE_ACCEPT != gtk_dialog_run(GTK_DIALOG(dialog))) {
531 gtk_widget_destroy(dialog);
532 return NULL;
533 }
534
535 project_t *project = g_new0(project_t, 1);
536 project->name = g_strdup(gtk_entry_get_text(GTK_ENTRY(entry)));
537 gtk_widget_destroy(dialog);
538
539
540 project->path = g_strdup_printf("%s%s/",
541 context->settings->base_path, project->name);
542 project->desc = g_strdup(_("<project description>"));
543
544 /* no data downloaded yet */
545 project->data_dirty = TRUE;
546
547 /* adjust default server stored in settings if required */
548 if(strstr(context->settings->server, "0.5") != NULL) {
549 strstr(context->settings->server, "0.5")[2] = '6';
550 printf("adjusting server path in settings to 0.6\n");
551 }
552
553 /* use global server/access settings */
554 project->server = g_strdup(context->settings->server);
555
556 /* build project osm file name */
557 project->osm = g_strdup_printf("%s.osm", project->name);
558
559 /* around the castle in karlsruhe, germany ... */
560 project->min.lat = 49.005; project->min.lon = 8.3911;
561 project->max.lat = 49.023; project->max.lon = 8.4185;
562
563 /* create project file on disk */
564 project_save(context->dialog, project);
565
566 #ifdef USE_HILDON
567 if(!project_edit(context->dialog, context->settings,
568 project, context->mmpos, context->osso_context))
569 #else
570 if(!project_edit(context->dialog, context->settings, project))
571 #endif
572 {
573 printf("edit cancelled!!\n");
574
575 project_delete(context, project);
576
577 project = NULL;
578 }
579
580 /* enable/disable edit/remove buttons */
581 view_selected(context, project);
582
583 return project;
584 }
585
586 // predecs
587 void project_get_status_icon_stock_id(project_t *project, gchar **stock_id);
588
589 static void on_project_new(GtkButton *button, gpointer data) {
590 select_context_t *context = (select_context_t*)data;
591 project_t **project = &context->project;
592 *project = project_new(context);
593 if(*project) {
594
595 GtkTreeModel *model = list_get_model(context->list);
596
597 GtkTreeIter iter;
598 gchar *status_stock_id = NULL;
599 project_get_status_icon_stock_id(*project, &status_stock_id);
600 gtk_list_store_append(GTK_LIST_STORE(model), &iter);
601 gtk_list_store_set(GTK_LIST_STORE(model), &iter,
602 PROJECT_COL_NAME, (*project)->name,
603 PROJECT_COL_STATUS, status_stock_id,
604 PROJECT_COL_DESCRIPTION, (*project)->desc,
605 PROJECT_COL_DATA, *project,
606 -1);
607
608 GtkTreeSelection *selection = list_get_selection(context->list);
609 gtk_tree_selection_select_iter(selection, &iter);
610 }
611 }
612
613 static void on_project_delete(GtkButton *button, gpointer data) {
614 select_context_t *context = (select_context_t*)data;
615 project_t *project = project_get_selected(context->list);
616
617 char *str = g_strdup_printf(_("Do you really want to delete the "
618 "project \"%s\"?"), project->name);
619 GtkWidget *dialog = gtk_message_dialog_new(
620 GTK_WINDOW(context->dialog),
621 GTK_DIALOG_DESTROY_WITH_PARENT,
622 GTK_MESSAGE_QUESTION, GTK_BUTTONS_YES_NO, str);
623 g_free(str);
624
625 gtk_window_set_title(GTK_WINDOW(dialog), _("Delete project?"));
626
627 /* set the active flag again if the user answered "no" */
628 if(GTK_RESPONSE_NO == gtk_dialog_run(GTK_DIALOG(dialog))) {
629 gtk_widget_destroy(dialog);
630 return;
631 }
632
633 gtk_widget_destroy(dialog);
634
635 if(!project_delete(context, project))
636 printf("unable to delete project\n");
637 }
638
639 static void on_project_edit(GtkButton *button, gpointer data) {
640 select_context_t *context = (select_context_t*)data;
641 project_t *project = project_get_selected(context->list);
642 g_assert(project);
643 #ifdef USE_HILDON
644 if(project_edit(context->dialog, context->settings, project,
645 context->mmpos, context->osso_context))
646 #else
647 if(project_edit(context->dialog, context->settings, project))
648 #endif
649 {
650 GtkTreeModel *model;
651 GtkTreeIter iter;
652
653 /* description etc. may have changed, so update list */
654 GtkTreeSelection *selection = list_get_selection(context->list);
655 g_assert(gtk_tree_selection_get_selected(selection, &model, &iter));
656
657 // gtk_tree_model_get(model, &iter, PROJECT_COL_DATA, &project, -1);
658 gchar *status_stock_id = NULL;
659 project_get_status_icon_stock_id(project, &status_stock_id);
660 gtk_list_store_set(GTK_LIST_STORE(model), &iter,
661 PROJECT_COL_NAME, project->name,
662 PROJECT_COL_STATUS, status_stock_id,
663 PROJECT_COL_DESCRIPTION, project->desc,
664 -1);
665
666
667 }
668
669 /* enable/disable edit/remove buttons */
670 view_selected(context, project);
671 }
672
673
674 gboolean project_osm_present(project_t *project) {
675 char *osm_name = g_strdup_printf("%s/%s.osm", project->path, project->name);
676 gboolean is_present = g_file_test(osm_name, G_FILE_TEST_EXISTS);
677 g_free(osm_name);
678 return is_present;
679 }
680
681 void project_get_status_icon_stock_id(project_t *project, gchar **stock_id) {
682 *stock_id = (! project_osm_present(project)) ? GTK_STOCK_DIALOG_WARNING
683 : diff_present(project) ? GTK_STOCK_PROPERTIES
684 : GTK_STOCK_FILE;
685 // TODO: check for outdatedness too. Which icon to use?
686 }
687
688 static GtkWidget *project_list_widget(select_context_t *context) {
689 context->list = list_new(LIST_HILDON_WITHOUT_HEADERS);
690
691 list_set_selection_function(context->list, view_selection_func, context);
692
693 list_set_columns(context->list,
694 _("Name"), PROJECT_COL_NAME, 0,
695 _("State"), PROJECT_COL_STATUS, LIST_FLAG_STOCK_ICON,
696 _("Description"), PROJECT_COL_DESCRIPTION, LIST_FLAG_ELLIPSIZE,
697 NULL);
698
699
700 /* build the store */
701 GtkListStore *store = gtk_list_store_new(PROJECT_NUM_COLS,
702 G_TYPE_STRING, // name
703 G_TYPE_STRING, // status
704 G_TYPE_STRING, // desc
705 G_TYPE_POINTER); // data
706
707 GtkTreeIter iter;
708 project_t *project = context->project;
709 while(project) {
710 gchar *status_stock_id = NULL;
711 project_get_status_icon_stock_id(project, &status_stock_id);
712 /* Append a row and fill in some data */
713 gtk_list_store_append(store, &iter);
714 gtk_list_store_set(store, &iter,
715 PROJECT_COL_NAME, project->name,
716 PROJECT_COL_STATUS, status_stock_id,
717 PROJECT_COL_DESCRIPTION, project->desc,
718 PROJECT_COL_DATA, project,
719 -1);
720 project = project->next;
721 }
722
723 list_set_store(context->list, store);
724 g_object_unref(store);
725
726 list_set_static_buttons(context->list, G_CALLBACK(on_project_new),
727 G_CALLBACK(on_project_edit), G_CALLBACK(on_project_delete), context);
728
729 return context->list;
730 }
731
732 char *project_select(appdata_t *appdata) {
733 char *name = NULL;
734
735 select_context_t *context = g_new0(select_context_t, 1);
736 #ifdef USE_HILDON
737 context->mmpos = &appdata->mmpos;
738 context->osso_context = appdata->osso_context;
739 #endif
740 context->settings = appdata->settings;
741 context->project = project_scan(appdata);
742
743 /* create project selection dialog */
744 context->dialog =
745 misc_dialog_new(MISC_DIALOG_MEDIUM,_("Project selection"),
746 GTK_WINDOW(appdata->window),
747 GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT,
748 GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT,
749 NULL);
750
751 gtk_box_pack_start_defaults(GTK_BOX(GTK_DIALOG(context->dialog)->vbox),
752 project_list_widget(context));
753
754 /* don't all user to click ok until something is selected */
755 gtk_dialog_set_response_sensitive(GTK_DIALOG(context->dialog),
756 GTK_RESPONSE_ACCEPT, FALSE);
757
758 gtk_widget_show_all(context->dialog);
759 if(GTK_RESPONSE_ACCEPT == gtk_dialog_run(GTK_DIALOG(context->dialog)))
760 name = g_strdup(project_get_selected(context->list)->name);
761
762 gtk_widget_destroy(context->dialog);
763
764 /* free all entries */
765 project_t *project = context->project;
766 while(project) {
767 project_t *next = project->next;
768 project_free(project);
769 project = next;
770 }
771
772 g_free(context);
773
774 return name;
775 }
776
777 /* ---------------------------------------------------- */
778
779 /* return file length or -1 on error */
780 static gsize file_length(char *path, char *name) {
781 char *str = NULL;
782
783 if(name[0] == '/') str = g_strdup(name);
784 else str = g_strjoin(NULL, path, name, NULL);
785
786 GMappedFile *gmap = g_mapped_file_new(str, FALSE, NULL);
787 g_free(str);
788
789 if(!gmap) return -1;
790 gsize size = g_mapped_file_get_length(gmap);
791 g_mapped_file_free(gmap);
792 return size;
793 }
794
795 void project_filesize(project_context_t *context) {
796 char *str = NULL;
797
798 printf("Checking size of %s\n", context->project->osm);
799
800 if(!osm_file_exists(context->project->path, context->project->osm)) {
801 GdkColor color;
802 gdk_color_parse("red", &color);
803 gtk_widget_modify_fg(context->fsize, GTK_STATE_NORMAL, &color);
804
805 str = g_strdup(_("Not downloaded!"));
806 gtk_dialog_set_response_sensitive(GTK_DIALOG(context->dialog),
807 GTK_RESPONSE_ACCEPT, 0);
808 } else {
809 gtk_widget_modify_fg(context->fsize, GTK_STATE_NORMAL, NULL);
810
811 if(!context->project->data_dirty)
812 str = g_strdup_printf(_("%d bytes present"),
813 file_length(context->project->path,
814 context->project->osm));
815 else
816 str = g_strdup_printf(_("Outdated, please download!"));
817
818 /* project also must not be dirty to proceed */
819 gtk_dialog_set_response_sensitive(GTK_DIALOG(context->dialog),
820 GTK_RESPONSE_ACCEPT, !context->project->data_dirty);
821 }
822
823 if(str) {
824 gtk_label_set_text(GTK_LABEL(context->fsize), str);
825 g_free(str);
826 }
827 }
828
829 void project_diffstat(project_context_t *context) {
830 char *str = NULL;
831
832 if(diff_present(context->project))
833 str = g_strdup(_("present"));
834 else
835 str = g_strdup(_("not present"));
836
837 gtk_label_set_text(GTK_LABEL(context->diff_stat), str);
838 g_free(str);
839 }
840
841 static void project_update(project_context_t *context) {
842
843 /* fetch values from dialog */
844 if(context->project->desc) g_free(context->project->desc);
845 context->project->desc = g_strdup(gtk_entry_get_text(
846 GTK_ENTRY(context->desc)));
847
848 if(context->project->server) g_free(context->project->server);
849 context->project->server = g_strdup(gtk_entry_get_text(
850 GTK_ENTRY(context->server)));
851 }
852
853 static void on_edit_clicked(GtkButton *button, gpointer data) {
854 project_context_t *context = (project_context_t*)data;
855
856 if(area_edit(&context->area_edit)) {
857 printf("coordinates changed!!\n");
858
859 pos_lon_label_set(context->minlat, context->project->min.lat);
860 pos_lon_label_set(context->minlon, context->project->min.lon);
861 pos_lon_label_set(context->maxlat, context->project->max.lat);
862 pos_lon_label_set(context->maxlon, context->project->max.lon);
863 }
864 }
865
866 static void on_download_clicked(GtkButton *button, gpointer data) {
867 project_context_t *context = (project_context_t*)data;
868
869 project_update(context);
870
871 printf("download %s\n", context->project->osm);
872
873 if(osm_download(context->dialog, context->settings, context->project)) {
874 context->project->data_dirty = FALSE;
875 project_filesize(context);
876 } else
877 printf("download failed\n");
878 }
879
880 static void on_diff_remove_clicked(GtkButton *button, gpointer data) {
881 project_context_t *context = (project_context_t*)data;
882
883 printf("clicked diff remove\n");
884
885 GtkWidget *dialog = gtk_message_dialog_new(
886 GTK_WINDOW(context->dialog),
887 GTK_DIALOG_DESTROY_WITH_PARENT,
888 GTK_MESSAGE_QUESTION, GTK_BUTTONS_YES_NO,
889 _("Do you really want to remove the diff file? This "
890 "will delete all changes you've made so far and which "
891 "you didn't upload yet."));
892
893 gtk_window_set_title(GTK_WINDOW(dialog), _("Remove diff?"));
894
895 /* set the active flag again if the user answered "no" */
896 if(GTK_RESPONSE_YES == gtk_dialog_run(GTK_DIALOG(dialog))) {
897 diff_remove(context->project);
898 project_diffstat(context);
899 gtk_widget_set_sensitive(context->diff_remove, FALSE);
900 }
901
902 gtk_widget_destroy(dialog);
903 }
904
905 gboolean project_check_demo(GtkWidget *parent, project_t *project) {
906 if(!project->server)
907 messagef(parent, "Demo project",
908 "This is a preinstalled demo project. This means that the "
909 "basic project parameters cannot be changed and no data can "
910 "be up- or downloaded via the OSM servers.\n\n"
911 "Please setup a new project to do these things.");
912
913 return !project->server;
914 }
915
916
917 gboolean project_edit(GtkWidget *parent, settings_t *settings,
918 project_t *project POS_PARM) {
919 gboolean ok = FALSE;
920
921 if(project_check_demo(parent, project))
922 return ok;
923
924 /* ------------ project edit dialog ------------- */
925
926 project_context_t *context = g_new0(project_context_t, 1);
927 context->project = project;
928 context->settings = settings;
929
930 context->area_edit.min = &project->min;
931 context->area_edit.max = &project->max;
932 #ifdef USE_HILDON
933 context->area_edit.mmpos = mmpos;
934 context->area_edit.osso_context = osso_context;
935 #endif
936
937 char *str = g_strdup_printf(_("Project - %s"), project->name);
938 context->area_edit.parent =
939 context->dialog = misc_dialog_new(MISC_DIALOG_WIDE, str,
940 GTK_WINDOW(parent),
941 GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT,
942 GTK_STOCK_OK, GTK_RESPONSE_ACCEPT,
943 NULL);
944 g_free(str);
945
946 GtkWidget *download, *label;
947 GtkWidget *table = gtk_table_new(4, 6, FALSE); // x, y
948
949 label = gtk_label_new(_("Description:"));
950 gtk_misc_set_alignment(GTK_MISC(label), 1.f, 0.5f);
951 gtk_table_attach_defaults(GTK_TABLE(table), label, 0, 1, 0, 1);
952 context->desc = gtk_entry_new();
953 gtk_entry_set_text(GTK_ENTRY(context->desc), project->desc);
954 gtk_table_attach_defaults(GTK_TABLE(table), context->desc, 1, 4, 0, 1);
955
956 gtk_table_set_row_spacing(GTK_TABLE(table), 0, 4);
957
958 label = gtk_label_new(_("Latitude"));
959 gtk_table_attach_defaults(GTK_TABLE(table), label, 1, 2, 1, 2);
960 label = gtk_label_new(_("Longitude"));
961 gtk_table_attach_defaults(GTK_TABLE(table), label, 2, 3, 1, 2);
962
963 label = gtk_label_new(_("Min:"));
964 gtk_misc_set_alignment(GTK_MISC(label), 1.f, 0.5f);
965 gtk_table_attach_defaults(GTK_TABLE(table), label, 0, 1, 2, 3);
966 context->minlat = pos_lat_label_new(project->min.lat);
967 gtk_table_attach_defaults(GTK_TABLE(table), context->minlat, 1, 2, 2, 3);
968 context->minlon = pos_lon_label_new(project->min.lon);
969 gtk_table_attach_defaults(GTK_TABLE(table), context->minlon, 2, 3, 2, 3);
970
971 label = gtk_label_new(_("Max:"));
972 gtk_misc_set_alignment(GTK_MISC(label), 1.f, 0.5f);
973 gtk_table_attach_defaults(GTK_TABLE(table), label, 0, 1, 3, 4);
974 context->maxlat = pos_lat_label_new(project->max.lat);
975 gtk_table_attach_defaults(GTK_TABLE(table), context->maxlat, 1, 2, 3, 4);
976 context->maxlon = pos_lon_label_new(project->max.lon);
977 gtk_table_attach_defaults(GTK_TABLE(table), context->maxlon, 2, 3, 3, 4);
978
979 GtkWidget *edit = gtk_button_new_with_label(_("Edit"));
980 gtk_signal_connect(GTK_OBJECT(edit), "clicked",
981 (GtkSignalFunc)on_edit_clicked, context);
982 gtk_table_attach(GTK_TABLE(table), edit, 3, 4, 2, 4,
983 GTK_EXPAND | GTK_FILL,0,0,0);
984
985 gtk_table_set_col_spacing(GTK_TABLE(table), 0, 4);
986 gtk_table_set_row_spacing(GTK_TABLE(table), 3, 4);
987
988 label = gtk_label_new(_("Server:"));
989 gtk_misc_set_alignment(GTK_MISC(label), 1.f, 0.5f);
990 gtk_table_attach_defaults(GTK_TABLE(table), label, 0, 1, 4, 5);
991 context->server = gtk_entry_new();
992 HILDON_ENTRY_NO_AUTOCAP(context->server);
993 gtk_entry_set_text(GTK_ENTRY(context->server), project->server);
994 gtk_table_attach_defaults(GTK_TABLE(table), context->server, 1, 4, 4, 5);
995
996 label = gtk_label_new(_("OSM file:"));
997 gtk_misc_set_alignment(GTK_MISC(label), 1.f, 0.5f);
998 gtk_table_attach_defaults(GTK_TABLE(table), label, 0, 1, 5, 6);
999 GtkWidget *hbox = gtk_hbox_new(FALSE, 0);
1000 context->fsize = gtk_label_new(_(""));
1001 gtk_misc_set_alignment(GTK_MISC(context->fsize), 0.f, 0.5f);
1002 project_filesize(context);
1003 gtk_box_pack_start_defaults(GTK_BOX(hbox), context->fsize);
1004 download = gtk_button_new_with_label(_("Download"));
1005 gtk_signal_connect(GTK_OBJECT(download), "clicked",
1006 (GtkSignalFunc)on_download_clicked, context);
1007 gtk_box_pack_start(GTK_BOX(hbox), download, FALSE, FALSE, 0);
1008 gtk_table_attach_defaults(GTK_TABLE(table), hbox, 1, 4, 5, 6);
1009
1010 label = gtk_label_new(_("Diff file:"));
1011 gtk_misc_set_alignment(GTK_MISC(label), 1.f, 0.5f);
1012 gtk_table_attach_defaults(GTK_TABLE(table), label, 0, 1, 6, 7);
1013 hbox = gtk_hbox_new(FALSE, 0);
1014 context->diff_stat = gtk_label_new(_(""));
1015 gtk_misc_set_alignment(GTK_MISC(context->diff_stat), 0.f, 0.5f);
1016 project_diffstat(context);
1017 gtk_box_pack_start_defaults(GTK_BOX(hbox), context->diff_stat);
1018 context->diff_remove = gtk_button_new_with_label(_("Remove"));
1019 if(!diff_present(project))
1020 gtk_widget_set_sensitive(context->diff_remove, FALSE);
1021 gtk_signal_connect(GTK_OBJECT(context->diff_remove), "clicked",
1022 (GtkSignalFunc)on_diff_remove_clicked, context);
1023 gtk_box_pack_start(GTK_BOX(hbox), context->diff_remove, FALSE, FALSE, 0);
1024 gtk_table_attach_defaults(GTK_TABLE(table), hbox, 1, 4, 6, 7);
1025
1026
1027 gtk_box_pack_start_defaults(GTK_BOX(GTK_DIALOG(context->dialog)->vbox),
1028 table);
1029 gtk_widget_show_all(context->dialog);
1030
1031 if(GTK_RESPONSE_ACCEPT == gtk_dialog_run(GTK_DIALOG(context->dialog))) {
1032 ok = TRUE;
1033
1034 /* transfer values from edit dialog into project structure */
1035 project_update(context);
1036
1037 /* save project */
1038 project_save(context->dialog, project);
1039 }
1040
1041 gtk_widget_destroy(context->dialog);
1042 g_free(context);
1043
1044 return ok;
1045 }
1046
1047 gboolean project_open(appdata_t *appdata, char *name) {
1048 project_t *project = g_new0(project_t, 1);
1049
1050 /* link to map state if a map already exists */
1051 if(appdata->map) {
1052 printf("Project: Using map state\n");
1053 project->map_state = appdata->map->state;
1054 } else {
1055 printf("Project: Creating new map_state\n");
1056 project->map_state = map_state_new();
1057 }
1058
1059 map_state_reset(project->map_state);
1060 project->map_state->refcount++;
1061
1062 /* build project path */
1063 project->path = g_strdup_printf("%s%s/",
1064 appdata->settings->base_path, name);
1065 project->name = g_strdup(name);
1066
1067 char *project_file = g_strdup_printf("%s%s.proj", project->path, name);
1068
1069 printf("project file = %s\n", project_file);
1070 if(!g_file_test(project_file, G_FILE_TEST_IS_REGULAR)) {
1071 printf("requested project file doesn't exist\n");
1072 project_free(project);
1073 g_free(project_file);
1074 return FALSE;
1075 }
1076
1077 if(!project_read(appdata, project_file, project)) {
1078 printf("error reading project file\n");
1079 project_free(project);
1080 g_free(project_file);
1081 return FALSE;
1082 }
1083
1084 g_free(project_file);
1085
1086 /* --------- project structure ok: load its OSM file --------- */
1087 appdata->project = project;
1088
1089 printf("project_open: loading osm %s\n", project->osm);
1090 appdata->osm = osm_parse(project->path, project->osm);
1091 if(!appdata->osm) {
1092 printf("OSM parsing failed\n");
1093 return FALSE;
1094 }
1095
1096 printf("parsing ok\n");
1097
1098 return TRUE;
1099 }
1100
1101 gboolean project_close(appdata_t *appdata) {
1102 if(!appdata->project) return FALSE;
1103
1104 printf("closing current project\n");
1105
1106 /* redraw the entire map by destroying all map items and redrawing them */
1107 if(appdata->osm)
1108 diff_save(appdata->project, appdata->osm);
1109
1110 /* Save track and turn off the handler callback */
1111 track_save(appdata->project, appdata->track.track);
1112 track_clear(appdata, appdata->track.track);
1113 appdata->track.track = NULL;
1114
1115 map_clear(appdata, MAP_LAYER_ALL);
1116
1117 if(appdata->osm) {
1118 osm_free(&appdata->icon, appdata->osm);
1119 appdata->osm = NULL;
1120 }
1121
1122 /* update project file on disk */
1123 project_save(GTK_WIDGET(appdata->window), appdata->project);
1124
1125 project_free(appdata->project);
1126 appdata->project = NULL;
1127
1128 return TRUE;
1129 }
1130
1131 #define _PROJECT_LOAD_BUF_SIZ 64
1132
1133 gboolean project_load(appdata_t *appdata, char *name) {
1134 char *proj_name = NULL;
1135
1136 if(!name) {
1137 /* make user select a project */
1138 proj_name = project_select(appdata);
1139 if(!proj_name) {
1140 printf("no project selected\n");
1141 return FALSE;
1142 }
1143 }
1144 else {
1145 proj_name = g_strdup(name);
1146 }
1147
1148 char banner_txt[_PROJECT_LOAD_BUF_SIZ];
1149 memset(banner_txt, 0, _PROJECT_LOAD_BUF_SIZ);
1150
1151 snprintf(banner_txt, _PROJECT_LOAD_BUF_SIZ, _("Loading %s"), proj_name);
1152 banner_busy_start(appdata, TRUE, banner_txt);
1153
1154 /* close current project */
1155 banner_busy_tick();
1156 if(appdata->project)
1157 project_close(appdata);
1158
1159 /* open project itself */
1160 banner_busy_tick();
1161 if(!project_open(appdata, proj_name)) {
1162 printf("error opening requested project\n");
1163
1164 if(appdata->project) {
1165 project_free(appdata->project);
1166 appdata->project = NULL;
1167 }
1168
1169 if(appdata->osm) {
1170 osm_free(&appdata->icon, appdata->osm);
1171 appdata->osm = NULL;
1172 }
1173
1174 snprintf(banner_txt, _PROJECT_LOAD_BUF_SIZ,
1175 _("Error opening %s"), proj_name);
1176 banner_busy_stop(appdata);
1177 banner_show_info(appdata, banner_txt);
1178
1179 g_free(proj_name);
1180 return FALSE;
1181 }
1182
1183 /* check if OSM data is valid */
1184 banner_busy_tick();
1185 if(!osm_sanity_check(GTK_WIDGET(appdata->window), appdata->osm)) {
1186 printf("project/osm sanity checks failed, unloading project\n");
1187
1188 if(appdata->project) {
1189 project_free(appdata->project);
1190 appdata->project = NULL;
1191 }
1192
1193 if(appdata->osm) {
1194 osm_free(&appdata->icon, appdata->osm);
1195 appdata->osm = NULL;
1196 }
1197
1198 snprintf(banner_txt, _PROJECT_LOAD_BUF_SIZ,
1199 _("Error opening %s"), proj_name);
1200 banner_busy_stop(appdata);
1201 banner_show_info(appdata, banner_txt);
1202
1203 g_free(proj_name);
1204 return FALSE;
1205 }
1206
1207 /* load diff possibly preset */
1208 banner_busy_tick();
1209 diff_restore(appdata, appdata->project, appdata->osm);
1210
1211 /* prepare colors etc, draw data and adjust scroll/zoom settings */
1212 banner_busy_tick();
1213 map_init(appdata);
1214
1215 /* restore a track */
1216 banner_busy_tick();
1217 appdata->track.track = track_restore(appdata, appdata->project);
1218 if(appdata->track.track)
1219 map_track_draw(appdata->map, appdata->track.track);
1220
1221 /* finally load a background if present */
1222 banner_busy_tick();
1223 wms_load(appdata);
1224
1225 /* save the name of the project for the perferences */
1226 if(appdata->settings->project)
1227 g_free(appdata->settings->project);
1228 appdata->settings->project = g_strdup(appdata->project->name);
1229
1230 banner_busy_stop(appdata);
1231
1232 #if 0
1233 snprintf(banner_txt, _PROJECT_LOAD_BUF_SIZ, _("Loaded %s"), proj_name);
1234 banner_show_info(appdata, banner_txt);
1235 #endif
1236
1237 statusbar_set(appdata, NULL, 0);
1238
1239 g_free(proj_name);
1240 return TRUE;
1241 }
1242
1243 /* ------------------- project setup wizard ----------------- */
1244
1245 typedef struct wizard_page_s {
1246 const gchar *title;
1247 GtkWidget* (*setup)(struct wizard_page_s *page);
1248 GtkAssistantPageType type;
1249 gboolean complete;
1250 GtkWidget *widget;
1251 gint index;
1252 } wizard_page_t;
1253
1254 typedef struct {
1255 gboolean running;
1256
1257 int page_num;
1258 wizard_page_t *page;
1259 } wizard_t;
1260
1261
1262 static gint on_assistant_destroy(GtkWidget *widget, wizard_t *wizard) {
1263 printf("destroy callback\n");
1264 wizard->running = FALSE;
1265 return FALSE;
1266 }
1267
1268 static void on_assistant_cancel(GtkWidget *widget, wizard_t *wizard) {
1269 printf("cancel callback\n");
1270 wizard->running = FALSE;
1271 }
1272
1273 static void on_assistant_close(GtkWidget *widget, wizard_t *wizard) {
1274 printf("close callback\n");
1275 wizard->running = FALSE;
1276 }
1277
1278 static GtkWidget *wizard_text(const char *text) {
1279 GtkTextBuffer *buffer = gtk_text_buffer_new(NULL);
1280 gtk_text_buffer_set_text(buffer, text, -1);
1281
1282 #ifndef USE_HILDON_TEXT_VIEW
1283 GtkWidget *view = gtk_text_view_new_with_buffer(buffer);
1284 #else
1285 GtkWidget *view = hildon_text_view_new();
1286 hildon_text_view_set_buffer(HILDON_TEXT_VIEW(view), buffer);
1287 #endif
1288
1289 gtk_text_view_set_wrap_mode(GTK_TEXT_VIEW(view), GTK_WRAP_WORD);
1290 gtk_text_view_set_editable(GTK_TEXT_VIEW(view), FALSE);
1291 gtk_text_view_set_left_margin(GTK_TEXT_VIEW(view), 2 );
1292 gtk_text_view_set_right_margin(GTK_TEXT_VIEW(view), 2 );
1293 gtk_text_view_set_cursor_visible(GTK_TEXT_VIEW(view), FALSE );
1294
1295 return view;
1296 }
1297
1298 static GtkWidget *wizard_create_intro_page(wizard_page_t *page) {
1299 static const char *text =
1300 "This wizard will guide you through the setup of a new project.\n\n"
1301 "An osm2go project covers a certain area of the world as seen "
1302 "by openstreetmap.org. The wizard will help you downloading "
1303 "the data describing that area and will enable you to make changes "
1304 "to it using osm2go.";
1305
1306 return wizard_text(text);
1307 }
1308
1309 static GtkWidget *wizard_create_source_selection_page(wizard_page_t *page) {
1310 GtkWidget *vbox = gtk_vbox_new(FALSE, 0);
1311
1312 gtk_box_pack_start_defaults(GTK_BOX(vbox),
1313 wizard_text("Please choose how to determine the area you "
1314 "are planning to work on."));
1315
1316 /* add selection buttons */
1317 GtkWidget *choice1 =
1318 gtk_radio_button_new_with_label(NULL,
1319 _("Use current GPS position"));
1320 gtk_box_pack_start(GTK_BOX(vbox), choice1, TRUE, TRUE, 2);
1321
1322 GtkWidget *choice2 =
1323 gtk_radio_button_new_with_label_from_widget(GTK_RADIO_BUTTON(choice1),
1324 _("Get from Maemo Mapper"));
1325 gtk_box_pack_start(GTK_BOX(vbox), choice2, TRUE, TRUE, 2);
1326
1327 GtkWidget *choice3 =
1328 gtk_radio_button_new_with_label_from_widget(GTK_RADIO_BUTTON(choice1),
1329 _("Specify area manually"));
1330 gtk_box_pack_start(GTK_BOX(vbox), choice3, TRUE, TRUE, 2);
1331
1332
1333
1334 return vbox;
1335 }
1336
1337 void project_wizard(appdata_t *appdata) {
1338 wizard_page_t page[] = {
1339 { "Introduction", wizard_create_intro_page,
1340 GTK_ASSISTANT_PAGE_INTRO, TRUE},
1341 { "Area source selection", wizard_create_source_selection_page,
1342 GTK_ASSISTANT_PAGE_CONTENT, FALSE},
1343 { "Click the Check Button", NULL,
1344 GTK_ASSISTANT_PAGE_CONTENT, FALSE},
1345 { "Click the Button", NULL,
1346 GTK_ASSISTANT_PAGE_PROGRESS, FALSE},
1347 { "Confirmation", NULL,
1348 GTK_ASSISTANT_PAGE_CONFIRM, TRUE},
1349 };
1350
1351 wizard_t wizard = {
1352 TRUE,
1353
1354 /* the pages themselves */
1355 sizeof(page) / sizeof(wizard_page_t), page
1356 };
1357
1358 GtkWidget *assistant = gtk_assistant_new();
1359 gtk_widget_set_size_request (assistant, 450, 300);
1360
1361 /* Add five pages to the GtkAssistant dialog. */
1362 int i;
1363 for (i = 0; i < wizard.page_num; i++) {
1364 if(wizard.page[i].setup)
1365 wizard.page[i].widget =
1366 wizard.page[i].setup(&wizard.page[i]);
1367 else {
1368 char *str = g_strdup_printf("Page %d", i);
1369 wizard.page[i].widget = gtk_label_new(str);
1370 g_free(str);
1371 }
1372
1373 page[i].index = gtk_assistant_append_page(GTK_ASSISTANT (assistant),
1374 wizard.page[i].widget);
1375
1376 gtk_assistant_set_page_title (GTK_ASSISTANT (assistant),
1377 wizard.page[i].widget, wizard.page[i].title);
1378 gtk_assistant_set_page_type (GTK_ASSISTANT (assistant),
1379 wizard.page[i].widget, wizard.page[i].type);
1380
1381 /* Set the introduction and conclusion pages as complete so they can be
1382 * incremented or closed. */
1383 gtk_assistant_set_page_complete (GTK_ASSISTANT (assistant),
1384 wizard.page[i].widget, wizard.page[i].complete);
1385 }
1386
1387 /* make it a modal subdialog of the main window */
1388 gtk_window_set_modal(GTK_WINDOW(assistant), TRUE);
1389 gtk_window_set_transient_for(GTK_WINDOW(assistant),
1390 GTK_WINDOW(appdata->window));
1391
1392 gtk_widget_show_all(assistant);
1393
1394 g_signal_connect(G_OBJECT(assistant), "destroy",
1395 G_CALLBACK(on_assistant_destroy), &wizard);
1396
1397 g_signal_connect(G_OBJECT(assistant), "cancel",
1398 G_CALLBACK(on_assistant_cancel), &wizard);
1399
1400 g_signal_connect(G_OBJECT(assistant), "close",
1401 G_CALLBACK(on_assistant_close), &wizard);
1402
1403 do {
1404 if(gtk_events_pending())
1405 gtk_main_iteration();
1406 else
1407 usleep(100000);
1408
1409 } while(wizard.running);
1410
1411 gtk_widget_destroy(assistant);
1412 }
1413
1414
1415 // vim:et:ts=8:sw=2:sts=2:ai