Contents of /trunk/src/main.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 301 - (show annotations)
Sat Sep 26 08:36:04 2009 UTC (14 years, 7 months ago) by harbaum
File MIME type: text/plain
File size: 52658 byte(s)
Consistent undo_all behaviour
1 /*
2 * Copyright (C) 2008-2009 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 <stdio.h>
21 #include <string.h>
22
23 #include <curl/curl.h>
24 #include <gdk/gdkkeysyms.h>
25
26 #if defined(USE_HILDON) && (MAEMO_VERSION_MAJOR == 5)
27 #define FREMANTLE
28 #include <hildon/hildon-button.h>
29 #include <hildon/hildon-check-button.h>
30 #include <hildon/hildon-window-stack.h>
31 #include <gdk/gdkx.h>
32 #include <X11/Xatom.h>
33 #endif
34
35 #include "appdata.h"
36 #include "banner.h"
37
38 /* disable/enable main screen control dependant on presence of open project */
39 void main_ui_enable(appdata_t *appdata) {
40 gboolean project_valid = (appdata->project != NULL);
41 gboolean osm_valid = (appdata->osm != NULL);
42
43 /* cancel any action in progress */
44 g_assert(appdata->iconbar->cancel);
45 if(GTK_WIDGET_FLAGS(appdata->iconbar->cancel) & GTK_SENSITIVE)
46 map_action_cancel(appdata);
47
48 /* ---- set project name as window title ----- */
49 #if defined(USE_HILDON) && MAEMO_VERSION_MAJOR < 5
50 if(project_valid)
51 gtk_window_set_title(GTK_WINDOW(appdata->window), appdata->project->name);
52 else
53 gtk_window_set_title(GTK_WINDOW(appdata->window), "");
54 #else
55 char *str = NULL;
56 #ifdef USE_HILDON
57 if(project_valid)
58 str = g_markup_printf_escaped("<b>%s</b> - OSM2Go",
59 appdata->project->name);
60 else
61 str = g_markup_printf_escaped("OSM2Go");
62
63 hildon_window_set_markup(HILDON_WINDOW(appdata->window), str);
64 #else
65 if(project_valid)
66 str = g_strdup_printf("%s - OSM2Go", appdata->project->name);
67 else
68 str = g_strdup_printf("OSM2Go");
69
70 gtk_window_set_title(GTK_WINDOW(appdata->window), str);
71 #endif
72 g_free(str);
73 #endif
74
75 if(appdata->iconbar && appdata->iconbar->toolbar)
76 gtk_widget_set_sensitive(appdata->iconbar->toolbar, osm_valid);
77 /* disable all menu entries related to map */
78 gtk_widget_set_sensitive(appdata->submenu_map, project_valid);
79 gtk_widget_set_sensitive(appdata->menu_item_map_upload, osm_valid);
80 if(appdata->menu_item_map_undo)
81 gtk_widget_set_sensitive(appdata->menu_item_map_undo, osm_valid);
82 if(appdata->menu_item_map_save_changes)
83 gtk_widget_set_sensitive(appdata->menu_item_map_save_changes, osm_valid);
84 gtk_widget_set_sensitive(appdata->menu_item_map_undo_changes, osm_valid);
85 gtk_widget_set_sensitive(appdata->menu_item_map_relations, osm_valid);
86 gtk_widget_set_sensitive(appdata->track.submenu_track, osm_valid);
87 gtk_widget_set_sensitive(appdata->submenu_view, osm_valid);
88 gtk_widget_set_sensitive(appdata->submenu_wms, osm_valid);
89
90 #ifdef ZOOM_BUTTONS
91 gtk_widget_set_sensitive(appdata->btn_zoom_in, osm_valid);
92 gtk_widget_set_sensitive(appdata->btn_zoom_out, osm_valid);
93 #endif
94
95 if(!project_valid)
96 statusbar_set(appdata, _("Please load or create a project"), FALSE);
97 }
98
99 /******************** begin of menu *********************/
100
101 static void
102 cb_menu_project_open(GtkMenuItem *item, gpointer data) {
103 appdata_t *appdata = (appdata_t*)data;
104 project_load(appdata, NULL);
105 main_ui_enable(appdata);
106 }
107
108 static void
109 cb_menu_project_wizard(GtkMenuItem *item, gpointer data) {
110 appdata_t *appdata = (appdata_t*)data;
111 project_wizard(appdata);
112 }
113
114 typedef struct {
115 appdata_t *appdata;
116 GtkWidget *dialog;
117 } about_context_t;
118
119 #ifdef ENABLE_BROWSER_INTERFACE
120 #ifndef USE_HILDON
121 #include <libgnome/gnome-url.h>
122 #else
123 #include <tablet-browser-interface.h>
124 #endif
125
126 void on_paypal_button_clicked(GtkButton *button, about_context_t *context) {
127 const char *url =
128 "https://www.paypal.com/cgi-bin/webscr"
129 "?cmd=_s-xclick&hosted_button_id=7439286";
130
131 #ifndef USE_HILDON
132 /* taken from gnome-open, part of libgnome */
133 GError *err = NULL;
134 gnome_url_show(url, &err);
135 #else
136 /* close the dialog on hildon as it doesn't like open dialogs */
137 gtk_dialog_response(GTK_DIALOG(context->dialog), GTK_RESPONSE_ACCEPT);
138
139 osso_rpc_run_with_defaults(context->appdata->osso_context, "osso_browser",
140 OSSO_BROWSER_OPEN_NEW_WINDOW_REQ, NULL,
141 DBUS_TYPE_STRING, url,
142 DBUS_TYPE_BOOLEAN, FALSE, DBUS_TYPE_INVALID);
143 #endif
144 }
145 #endif
146
147 static void
148 cb_menu_about(GtkMenuItem *item, gpointer data) {
149 about_context_t context;
150 context.appdata = (appdata_t *)data;
151
152 const gchar *authors[] = {
153 "Till Harbaum <till@harbaum.org>",
154 "Andrew Chadwick <andrewc-osm2go@piffle.org>",
155 "Marcus Bauer <marcus.bauer@gmail.com>",
156 "John Stowers <john.stowers@gmail.com>",
157 "Rolf Bode-Meyer <robome@gmail.com>",
158 NULL };
159
160 const gchar *artists[] = {
161 "Andrew Zhilin <drew.zhilin@gmail.com>",
162 NULL };
163
164 context.dialog = g_object_new(GTK_TYPE_ABOUT_DIALOG,
165 "name", "OSM2Go",
166 "version", VERSION,
167 "copyright", _("Copyright 2008-2009"),
168 "authors", authors,
169 "artists", artists,
170 "website", _("http://www.harbaum.org/till/maemo"),
171 "comments", _("Mobile OSM Editor"),
172 NULL);
173
174 #ifdef ENABLE_BROWSER_INTERFACE
175 /* add a way to donate to the project */
176 GtkWidget *alignment = gtk_alignment_new(0.5, 0, 0, 0);
177
178 GtkWidget *hbox = gtk_hbox_new(FALSE, 8);
179 gtk_box_pack_start(GTK_BOX(hbox),
180 gtk_label_new(_("Do you ĺike OSM2Go?")),
181 FALSE, FALSE, 0);
182
183 GtkWidget *button = gtk_button_new();
184 gtk_button_set_image(GTK_BUTTON(button),
185 icon_widget_load(&context.appdata->icon, "paypal"));
186 gtk_button_set_relief(GTK_BUTTON(button), GTK_RELIEF_NONE);
187 g_signal_connect(button, "clicked",
188 G_CALLBACK(on_paypal_button_clicked), &context);
189 gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, FALSE, 0);
190
191 gtk_container_add(GTK_CONTAINER(alignment), hbox);
192 gtk_box_pack_start_defaults(GTK_BOX((GTK_DIALOG(context.dialog))->vbox),
193 alignment);
194
195 gtk_widget_show_all(alignment);
196 #endif
197
198 gtk_dialog_run(GTK_DIALOG(context.dialog));
199 gtk_widget_destroy(context.dialog);
200 }
201
202 void on_window_destroy (GtkWidget *widget, gpointer data);
203
204 #ifndef USE_HILDON
205 static void
206 cb_menu_quit(GtkMenuItem *item, gpointer data) {
207 appdata_t *appdata = (appdata_t*)data;
208 gtk_widget_destroy(GTK_WIDGET(appdata->window));
209 }
210 #endif
211
212 static void
213 cb_menu_upload(GtkMenuItem *item, gpointer data) {
214 appdata_t *appdata = (appdata_t*)data;
215 if(!appdata->osm || !appdata->project) return;
216
217 if(project_check_demo(GTK_WIDGET(appdata->window), appdata->project))
218 return;
219
220 osm_upload(appdata, appdata->osm, appdata->project);
221 }
222
223 static void
224 cb_menu_download(GtkMenuItem *item, gpointer data) {
225 appdata_t *appdata = (appdata_t*)data;
226 if(!appdata->project) return;
227
228 if(project_check_demo(GTK_WIDGET(appdata->window), appdata->project))
229 return;
230
231 /* if we have valid osm data loaded: save state first */
232 if(appdata->osm) {
233 /* redraw the entire map by destroying all map items and redrawing them */
234 diff_save(appdata->project, appdata->osm);
235 map_clear(appdata, MAP_LAYER_OBJECTS_ONLY);
236
237 osm_free(&appdata->icon, appdata->osm);
238 appdata->osm = NULL;
239 }
240
241 // download
242 if(osm_download(GTK_WIDGET(appdata->window), appdata->settings,
243 appdata->project)) {
244 banner_busy_start(appdata, 1, "Redrawing");
245 appdata->osm = osm_parse(appdata->project->path, appdata->project->osm);
246 diff_restore(appdata, appdata->project, appdata->osm);
247 map_paint(appdata);
248 banner_busy_stop(appdata); //"Redrawing"
249 }
250
251 main_ui_enable(appdata);
252 }
253
254 static void
255 cb_menu_wms_import(GtkMenuItem *item, gpointer data) {
256 appdata_t *appdata = (appdata_t*)data;
257 wms_import(appdata);
258 }
259
260 static void
261 cb_menu_wms_clear(GtkMenuItem *item, gpointer data) {
262 appdata_t *appdata = (appdata_t*)data;
263 wms_remove(appdata);
264 }
265
266 static void
267 cb_menu_wms_adjust(GtkMenuItem *item, gpointer data) {
268 appdata_t *appdata = (appdata_t*)data;
269 map_action_set(appdata, MAP_ACTION_BG_ADJUST);
270 }
271
272 /* ----------- hide objects for performance reasons ----------- */
273
274 static void
275 cb_menu_map_hide_sel(GtkMenuItem *item, gpointer data) {
276 appdata_t *appdata = (appdata_t*)data;
277 map_hide_selected(appdata);
278 }
279
280 static void
281 cb_menu_map_show_all(GtkMenuItem *item, gpointer data) {
282 appdata_t *appdata = (appdata_t*)data;
283 map_show_all(appdata);
284 }
285
286 /* ---------------------------------------------------------- */
287
288 #if defined(USE_HILDON) && (MAEMO_VERSION_MAJOR == 5)
289 #define MENU_CHECK_ITEM HildonCheckButton
290 #define MENU_CHECK_ITEM_ACTIVE(a) hildon_check_button_get_active(a)
291 #else
292 #define MENU_CHECK_ITEM GtkCheckMenuItem
293 #define MENU_CHECK_ITEM_ACTIVE(a) gtk_check_menu_item_get_active(a)
294 #endif
295
296 static void
297 cb_menu_style(GtkMenuItem *item, gpointer data) {
298 appdata_t *appdata = (appdata_t*)data;
299
300 style_select(GTK_WIDGET(appdata->window), appdata);
301 }
302
303 static void
304 cb_menu_undo(GtkMenuItem *item, gpointer data) {
305 appdata_t *appdata = (appdata_t*)data;
306
307 undo(appdata);
308
309 // the banner will be displayed from within undo with more details
310 }
311
312 static void
313 cb_menu_save_changes(GtkMenuItem *item, gpointer data) {
314 appdata_t *appdata = (appdata_t*)data;
315
316 diff_save(appdata->project, appdata->osm);
317 banner_show_info(appdata, _("Saved local changes"));
318 }
319
320 static void
321 cb_menu_undo_changes(GtkMenuItem *item, gpointer data) {
322 appdata_t *appdata = (appdata_t*)data;
323
324 if(!yes_no_f(GTK_WIDGET(appdata->window), NULL, 0, 0,
325 _("Undo all changes?"),
326 _("Throw away all the changes you've not "
327 "uploaded yet? This can't be undone.")))
328 return;
329
330 map_clear(appdata, MAP_LAYER_OBJECTS_ONLY);
331
332 osm_free(&appdata->icon, appdata->osm);
333 appdata->osm = NULL;
334
335 diff_remove(appdata->project);
336 appdata->osm = osm_parse(appdata->project->path, appdata->project->osm);
337 map_paint(appdata);
338
339 banner_show_info(appdata, _("Undo all changes"));
340 }
341
342 static void
343 cb_menu_osm_relations(GtkMenuItem *item, appdata_t *appdata) {
344 /* list relations of all objects */
345 relation_list(GTK_WIDGET(appdata->window), appdata, NULL);
346 }
347
348 #if !defined(USE_HILDON) || (MAEMO_VERSION_MAJOR < 5)
349 static void
350 cb_menu_fullscreen(MENU_CHECK_ITEM *item, gpointer data) {
351 appdata_t *appdata = (appdata_t *)data;
352
353 if(MENU_CHECK_ITEM_ACTIVE(item))
354 gtk_window_fullscreen(GTK_WINDOW(appdata->window));
355 else
356 gtk_window_unfullscreen(GTK_WINDOW(appdata->window));
357 }
358 #endif
359
360 static void
361 cb_menu_zoomin(GtkMenuItem *item, appdata_t *appdata) {
362 if(!appdata || !appdata->map) return;
363
364 map_set_zoom(appdata->map, appdata->map->state->zoom*ZOOM_FACTOR_MENU, TRUE);
365 printf("zoom is now %f\n", appdata->map->state->zoom);
366 }
367
368 static void
369 cb_menu_zoomout(GtkMenuItem *item, appdata_t *appdata) {
370 if(!appdata || !appdata->map) return;
371
372 map_set_zoom(appdata->map, appdata->map->state->zoom/ZOOM_FACTOR_MENU, TRUE);
373 printf("zoom is now %f\n", appdata->map->state->zoom);
374 }
375
376 #if defined(FREMANTLE) || (MAEMO_VERSION_MAJOR != 5) || !defined(DETAIL_POPUP)
377 static void
378 cb_menu_view_detail_inc(GtkMenuItem *item, gpointer data) {
379 appdata_t *appdata = (appdata_t*)data;
380
381 printf("detail level increase\n");
382 map_detail_increase(appdata->map);
383 }
384
385 static void
386 cb_menu_view_detail_normal(GtkMenuItem *item, gpointer data) {
387 appdata_t *appdata = (appdata_t*)data;
388
389 printf("detail level normal\n");
390 map_detail_normal(appdata->map);
391 }
392
393 static void
394 cb_menu_view_detail_dec(GtkMenuItem *item, gpointer data) {
395 appdata_t *appdata = (appdata_t*)data;
396
397 printf("detail level decrease\n");
398 map_detail_decrease(appdata->map);
399 }
400 #endif
401
402 static void
403 cb_menu_track_import(GtkMenuItem *item, appdata_t *appdata) {
404 g_assert(appdata->settings);
405
406 /* open a file selector */
407 GtkWidget *dialog;
408
409 #ifdef USE_HILDON
410 dialog = hildon_file_chooser_dialog_new(GTK_WINDOW(appdata->window),
411 GTK_FILE_CHOOSER_ACTION_OPEN);
412 #else
413 dialog = gtk_file_chooser_dialog_new (_("Import track file"),
414 GTK_WINDOW(appdata->window),
415 GTK_FILE_CHOOSER_ACTION_OPEN,
416 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
417 GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT,
418 NULL);
419 #endif
420
421 if(appdata->settings->track_path) {
422 if(!g_file_test(appdata->settings->track_path, G_FILE_TEST_EXISTS)) {
423 char *last_sep = strrchr(appdata->settings->track_path, '/');
424 if(last_sep) {
425 *last_sep = 0; // seperate path from file
426
427 /* the user just created a new document */
428 gtk_file_chooser_set_current_folder(GTK_FILE_CHOOSER(dialog),
429 appdata->settings->track_path);
430 gtk_file_chooser_set_current_name(GTK_FILE_CHOOSER(dialog),
431 last_sep+1);
432
433 /* restore full filename */
434 *last_sep = '/';
435 }
436 } else
437 gtk_file_chooser_set_filename(GTK_FILE_CHOOSER(dialog),
438 appdata->settings->track_path);
439 }
440
441 gtk_widget_show_all (GTK_WIDGET(dialog));
442 if (gtk_dialog_run (GTK_DIALOG(dialog)) == GTK_FM_OK) {
443 char *filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dialog));
444
445 /* load a track */
446 appdata->track.track = track_import(appdata, filename);
447 if(appdata->track.track) {
448 if(appdata->settings->track_path) g_free(appdata->settings->track_path);
449 appdata->settings->track_path = g_strdup(filename);
450 }
451 g_free (filename);
452 }
453
454 gtk_widget_destroy (dialog);
455 }
456
457 static void
458 cb_menu_track_enable_gps(MENU_CHECK_ITEM *item, appdata_t *appdata) {
459 track_enable_gps(appdata, MENU_CHECK_ITEM_ACTIVE(item));
460 }
461
462
463 static void
464 cb_menu_track_follow_gps(MENU_CHECK_ITEM *item, appdata_t *appdata) {
465 appdata->settings->follow_gps = MENU_CHECK_ITEM_ACTIVE(item);
466 }
467
468
469 static void
470 cb_menu_track_export(GtkMenuItem *item, appdata_t *appdata) {
471 g_assert(appdata->settings);
472
473 /* open a file selector */
474 GtkWidget *dialog;
475
476 #ifdef USE_HILDON
477 dialog = hildon_file_chooser_dialog_new(GTK_WINDOW(appdata->window),
478 GTK_FILE_CHOOSER_ACTION_SAVE);
479 #else
480 dialog = gtk_file_chooser_dialog_new(_("Export track file"),
481 GTK_WINDOW(appdata->window),
482 GTK_FILE_CHOOSER_ACTION_SAVE,
483 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
484 GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT,
485 NULL);
486 #endif
487
488 printf("set filename <%s>\n", appdata->settings->track_path);
489
490 if(appdata->settings->track_path) {
491 if(!g_file_test(appdata->settings->track_path, G_FILE_TEST_EXISTS)) {
492 char *last_sep = strrchr(appdata->settings->track_path, '/');
493 if(last_sep) {
494 *last_sep = 0; // seperate path from file
495
496 /* the user just created a new document */
497 gtk_file_chooser_set_current_folder(GTK_FILE_CHOOSER(dialog),
498 appdata->settings->track_path);
499 gtk_file_chooser_set_current_name(GTK_FILE_CHOOSER(dialog),
500 last_sep+1);
501
502 /* restore full filename */
503 *last_sep = '/';
504 }
505 } else
506 gtk_file_chooser_set_filename(GTK_FILE_CHOOSER(dialog),
507 appdata->settings->track_path);
508 }
509
510 if(gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_FM_OK) {
511 gchar *filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dialog));
512 if(filename) {
513 printf("export to %s\n", filename);
514
515 if(!g_file_test(filename, G_FILE_TEST_EXISTS) ||
516 yes_no_f(dialog, appdata, MISC_AGAIN_ID_EXPORT_OVERWRITE,
517 MISC_AGAIN_FLAG_DONT_SAVE_NO,
518 "Overwrite existing file",
519 "The file already exists. "
520 "Do you really want to replace it?")) {
521 if(appdata->settings->track_path)
522 g_free(appdata->settings->track_path);
523 appdata->settings->track_path = g_strdup(filename);
524
525 track_export(appdata, filename);
526 }
527 }
528 }
529
530 gtk_widget_destroy (dialog);
531 }
532
533
534 static void
535 cb_menu_track_clear(GtkMenuItem *item, gpointer data) {
536 appdata_t *appdata = (appdata_t*)data;
537 track_clear(appdata, appdata->track.track);
538 appdata->track.track = NULL;
539 }
540
541
542
543
544 /*
545 * Platform-specific UI tweaks.
546 */
547
548
549 #ifndef USE_HILDON
550 #ifdef PORTRAIT
551
552 // Portrait mode, for openmoko-like systems
553 #define uispecific_main_menu_new gtk_menu_new
554
555 #else
556
557 // Regular desktop builds
558 #define uispecific_main_menu_new gtk_menu_bar_new
559 #define UISPECIFIC_MAIN_MENU_IS_MENU_BAR
560 #define UISPECIFIC_MENU_HAS_ICONS
561 #define UISPECIFIC_MENU_HAS_ACCELS
562
563 #endif //PORTRAIT
564 #else//USE_HILDON
565
566 // Maemo/Hildon builds
567 #define uispecific_main_menu_new gtk_menu_new
568
569 #endif
570
571
572 #if !defined(USE_HILDON) || (MAEMO_VERSION_MAJOR < 5)
573 // Half-arsed slapdash common menu item constructor. Let's use GtkBuilder
574 // instead so we have some flexibility.
575
576 static GtkWidget *
577 menu_append_new_item(appdata_t *appdata,
578 GtkWidget *menu_shell,
579 GtkSignalFunc activate_cb,
580 char *label,
581 const gchar *icon_name, // stock id or name for icon_load
582 // overridden by label, accels, icon_name
583 const gchar *accel_path,
584 guint accel_key, // from gdk/gdkkeysyms.h
585 GdkModifierType accel_mods, // e.g. GDK_CONTROL_MASK
586 gboolean enabled,
587 gboolean is_check, gboolean check_status)
588 {
589 GtkWidget *item = NULL;
590 GtkWidget *image = NULL;
591
592 gboolean stock_item_known = FALSE;
593 GtkStockItem stock_item;
594 if (icon_name != NULL) {
595 stock_item_known = gtk_stock_lookup(icon_name, &stock_item);
596 }
597
598 // Icons
599 #ifndef UISPECIFIC_MENU_HAS_ICONS
600 item = is_check ? gtk_check_menu_item_new_with_mnemonic (label)
601 : gtk_menu_item_new_with_mnemonic (label);
602 #else
603 if (is_check) {
604 item = gtk_check_menu_item_new_with_mnemonic (label);
605 }
606 else if (!stock_item_known) {
607 GdkPixbuf *pbuf = icon_load(&appdata->icon, icon_name);
608 if (pbuf) {
609 image = gtk_image_new_from_pixbuf(pbuf);
610 }
611 if (image) {
612 item = gtk_image_menu_item_new_with_mnemonic(label);
613 gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(item), image);
614 }
615 else {
616 item = gtk_menu_item_new_with_mnemonic(label);
617 }
618 }
619 else {
620 item = gtk_image_menu_item_new_with_mnemonic(label);
621 image = gtk_image_new_from_stock(icon_name, GTK_ICON_SIZE_MENU);
622 gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(item), image);
623 }
624 #endif
625
626 #ifdef UISPECIFIC_MENU_HAS_ACCELS
627 // Accelerators
628 // Default
629 if (accel_path != NULL) {
630 gtk_menu_item_set_accel_path(GTK_MENU_ITEM(item), accel_path);
631 if (accel_key != 0) {
632 gtk_accel_map_add_entry( accel_path, accel_key, accel_mods );
633 }
634 else if (stock_item_known) {
635 gtk_accel_map_add_entry( accel_path, stock_item.keyval,
636 stock_item.modifier );
637 }
638 }
639 #endif
640
641 gtk_menu_shell_append(GTK_MENU_SHELL(menu_shell), GTK_WIDGET(item));
642 gtk_widget_set_sensitive(GTK_WIDGET(item), enabled);
643 if (is_check)
644 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(item), check_status);
645
646 g_signal_connect(item, "activate", GTK_SIGNAL_FUNC(activate_cb),
647 appdata);
648 return item;
649 }
650
651 void menu_create(appdata_t *appdata) {
652 GtkWidget *menu, *item, *submenu;
653 GtkWidget *about_quit_items_menu;
654
655 if (g_module_supported()) {
656 printf("*** can use GModule: consider using GtkUIManager / GtkBuilder\n");
657 }
658
659 menu = uispecific_main_menu_new();
660 about_quit_items_menu = menu;
661
662 /* -------------------- Project submenu -------------------- */
663
664 GtkAccelGroup *accel_grp = gtk_accel_group_new();
665
666 #ifndef USE_HILDON
667 item = gtk_menu_item_new_with_mnemonic( _("_Project") );
668 gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
669 submenu = gtk_menu_new();
670 gtk_menu_set_accel_group(GTK_MENU(submenu), accel_grp);
671 gtk_menu_item_set_submenu(GTK_MENU_ITEM(item), submenu);
672 #ifdef UISPECIFIC_MAIN_MENU_IS_MENU_BAR
673 about_quit_items_menu = submenu;
674 #endif
675
676 menu_append_new_item(
677 appdata, submenu, GTK_SIGNAL_FUNC(cb_menu_project_open), _("_Open"),
678 GTK_STOCK_OPEN, "<OSM2Go-Main>/Project/Open",
679 0, 0, TRUE, FALSE, FALSE
680 );
681
682 menu_append_new_item(
683 appdata, submenu, GTK_SIGNAL_FUNC(cb_menu_project_wizard), _("_Wizard"),
684 GTK_STOCK_NEW, "<OSM2Go-Main>/Project/Wizard",
685 0, 0, TRUE, FALSE, FALSE
686 );
687 #else
688 menu_append_new_item(
689 appdata, menu, GTK_SIGNAL_FUNC(cb_menu_project_open), _("_Project"),
690 GTK_STOCK_OPEN, "<OSM2Go-Main>/Project",
691 0, 0, TRUE, FALSE, FALSE
692 );
693 #endif
694
695 /* --------------- view menu ------------------- */
696
697 #ifndef UISPECIFIC_MAIN_MENU_IS_MENU_BAR
698 gtk_menu_shell_append(GTK_MENU_SHELL(menu), gtk_separator_menu_item_new());
699 #endif
700
701 appdata->submenu_view = item = gtk_menu_item_new_with_mnemonic( _("_View") );
702 gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
703 submenu = gtk_menu_new();
704 gtk_menu_set_accel_group(GTK_MENU(submenu), accel_grp);
705 gtk_menu_item_set_submenu(GTK_MENU_ITEM(item), submenu);
706
707 #if !defined(USE_HILDON) || (MAEMO_VERSION_MAJOR < 5)
708 appdata->menu_item_view_fullscreen = menu_append_new_item(
709 appdata, submenu, GTK_SIGNAL_FUNC(cb_menu_fullscreen), _("_Fullscreen"),
710 GTK_STOCK_FULLSCREEN, "<OSM2Go-Main>/View/Fullscreen",
711 0, 0, TRUE, TRUE, FALSE
712 );
713 #endif
714
715 #if !defined(ZOOM_BUTTONS) || !defined(USE_HILDON)
716 menu_append_new_item(
717 appdata, submenu, GTK_SIGNAL_FUNC(cb_menu_zoomin), _("Zoom _in"),
718 GTK_STOCK_ZOOM_IN, "<OSM2Go-Main>/View/ZoomIn",
719 GDK_comma, GDK_CONTROL_MASK, TRUE, FALSE, FALSE
720 );
721
722 menu_append_new_item(
723 appdata, submenu, GTK_SIGNAL_FUNC(cb_menu_zoomout), _("Zoom _out"),
724 GTK_STOCK_ZOOM_OUT, "<OSM2Go-Main>/View/ZoomOut",
725 GDK_period, GDK_CONTROL_MASK, TRUE, FALSE, FALSE
726 );
727 #endif
728
729 gtk_menu_shell_append(GTK_MENU_SHELL(submenu), gtk_separator_menu_item_new());
730
731 menu_append_new_item(
732 appdata, submenu, GTK_SIGNAL_FUNC(cb_menu_view_detail_inc), _("More details"),
733 NULL, "<OSM2Go-Main>/View/DetailInc",
734 GDK_period, GDK_MOD1_MASK, TRUE, FALSE, FALSE
735 );
736
737 menu_append_new_item(
738 appdata, submenu, GTK_SIGNAL_FUNC(cb_menu_view_detail_normal), _("Normal details"),
739 NULL, "<OSM2Go-Main>/View/DetailNormal",
740 0, 0, TRUE, FALSE, FALSE
741 );
742
743 menu_append_new_item(
744 appdata, submenu, GTK_SIGNAL_FUNC(cb_menu_view_detail_dec), _("Less details"),
745 NULL, "<OSM2Go-Main>/View/DetailDec",
746 GDK_comma, GDK_MOD1_MASK, TRUE, FALSE, FALSE
747 );
748
749 gtk_menu_shell_append(GTK_MENU_SHELL(submenu), gtk_separator_menu_item_new());
750
751 appdata->menu_item_map_hide_sel = menu_append_new_item(
752 appdata, submenu, GTK_SIGNAL_FUNC(cb_menu_map_hide_sel), _("_Hide selected"),
753 GTK_STOCK_REMOVE, "<OSM2Go-Main>/View/HideSelected",
754 0, 0, TRUE, FALSE, FALSE
755 );
756 gtk_widget_set_sensitive(appdata->menu_item_map_hide_sel, FALSE);
757
758 appdata->menu_item_map_show_all = menu_append_new_item(
759 appdata, submenu, GTK_SIGNAL_FUNC(cb_menu_map_show_all), _("_Show all"),
760 GTK_STOCK_ADD, "<OSM2Go-Main>/View/ShowAll",
761 0, 0, TRUE, FALSE, FALSE
762 );
763 gtk_widget_set_sensitive(appdata->menu_item_map_show_all, FALSE);
764
765 gtk_menu_shell_append(GTK_MENU_SHELL(submenu), gtk_separator_menu_item_new());
766
767 menu_append_new_item(
768 appdata, submenu, GTK_SIGNAL_FUNC(cb_menu_style), _("St_yle"),
769 GTK_STOCK_SELECT_COLOR, "<OSM2Go-Main>/View/Style",
770 0, 0, TRUE, FALSE, FALSE
771 );
772
773 /* -------------------- map submenu -------------------- */
774
775 appdata->submenu_map = item = gtk_menu_item_new_with_mnemonic( _("_Map") );
776 gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
777 submenu = gtk_menu_new();
778 gtk_menu_set_accel_group(GTK_MENU(submenu), accel_grp);
779 gtk_menu_item_set_submenu(GTK_MENU_ITEM(item), submenu);
780
781 appdata->menu_item_map_upload = menu_append_new_item(
782 appdata, submenu, GTK_SIGNAL_FUNC(cb_menu_upload), _("_Upload"),
783 "upload.16", "<OSM2Go-Main>/Map/Upload",
784 GDK_u, GDK_SHIFT_MASK|GDK_CONTROL_MASK, TRUE, FALSE, FALSE
785 );
786
787 menu_append_new_item(
788 appdata, submenu, GTK_SIGNAL_FUNC(cb_menu_download), _("_Download"),
789 "download.16", "<OSM2Go-Main>/Map/Download",
790 GDK_d, GDK_SHIFT_MASK|GDK_CONTROL_MASK, TRUE, FALSE, FALSE
791 );
792
793 gtk_menu_shell_append(GTK_MENU_SHELL(submenu), gtk_separator_menu_item_new());
794
795 if(getenv("OSM2GO_UNDO_TEST")) {
796 appdata->menu_item_map_undo = menu_append_new_item(
797 appdata, submenu, GTK_SIGNAL_FUNC(cb_menu_undo), _("_Undo"),
798 GTK_STOCK_UNDO, "<OSM2Go-Main>/Map/Undo",
799 GDK_z, GDK_CONTROL_MASK, TRUE, FALSE, FALSE
800 );
801 } else
802 printf("set environment variable OSM2GO_UNDO_TEST to enable undo framework tests\n");
803
804 #ifndef USE_HILDON
805 appdata->menu_item_map_save_changes = menu_append_new_item(
806 appdata, submenu, GTK_SIGNAL_FUNC(cb_menu_save_changes), _("_Save local changes"),
807 GTK_STOCK_SAVE, "<OSM2Go-Main>/Map/SaveChanges",
808 GDK_s, GDK_SHIFT_MASK|GDK_CONTROL_MASK, TRUE, FALSE, FALSE
809 );
810 #endif
811
812 appdata->menu_item_map_undo_changes = menu_append_new_item(
813 appdata, submenu, GTK_SIGNAL_FUNC(cb_menu_undo_changes), _("Undo _all"),
814 GTK_STOCK_DELETE, "<OSM2Go-Main>/Map/UndoAll",
815 0, 0, TRUE, FALSE, FALSE
816 );
817
818 gtk_menu_shell_append(GTK_MENU_SHELL(submenu), gtk_separator_menu_item_new());
819 appdata->menu_item_map_relations = menu_append_new_item(
820 appdata, submenu, GTK_SIGNAL_FUNC(cb_menu_osm_relations), _("_Relations"),
821 NULL, "<OSM2Go-Main>/Map/Relations",
822 GDK_r, GDK_SHIFT_MASK|GDK_CONTROL_MASK, TRUE, FALSE, FALSE
823 );
824
825 /* -------------------- wms submenu -------------------- */
826
827 appdata->submenu_wms = item = gtk_menu_item_new_with_mnemonic( _("_WMS") );
828 gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
829 submenu = gtk_menu_new();
830 gtk_menu_set_accel_group(GTK_MENU(submenu), accel_grp);
831 gtk_menu_item_set_submenu(GTK_MENU_ITEM(item), submenu);
832
833 menu_append_new_item(
834 appdata, submenu, GTK_SIGNAL_FUNC(cb_menu_wms_import), _("_Import"),
835 GTK_STOCK_INDEX, "<OSM2Go-Main>/WMS/Import",
836 0, 0, TRUE, FALSE, FALSE
837 );
838
839 appdata->menu_item_wms_clear = menu_append_new_item(
840 appdata, submenu, GTK_SIGNAL_FUNC(cb_menu_wms_clear), _("_Clear"),
841 GTK_STOCK_CLEAR, "<OSM2Go-Main>/WMS/Clear",
842 0, 0, TRUE, FALSE, FALSE
843 );
844 gtk_widget_set_sensitive(appdata->menu_item_wms_clear, FALSE);
845
846 appdata->menu_item_wms_adjust = menu_append_new_item(
847 appdata, submenu, GTK_SIGNAL_FUNC(cb_menu_wms_adjust), _("_Adjust"),
848 NULL, "<OSM2Go-Main>/WMS/Adjust",
849 0, 0, TRUE, FALSE, FALSE
850 );
851 gtk_widget_set_sensitive(appdata->menu_item_wms_adjust, FALSE);
852
853 /* -------------------- track submenu -------------------- */
854
855 appdata->track.submenu_track = item =
856 gtk_menu_item_new_with_mnemonic(_("_Track"));
857 gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
858 submenu = gtk_menu_new();
859 gtk_menu_set_accel_group(GTK_MENU(submenu), accel_grp);
860 gtk_menu_item_set_submenu(GTK_MENU_ITEM(item), submenu);
861
862 appdata->track.menu_item_track_import = menu_append_new_item(
863 appdata, submenu, GTK_SIGNAL_FUNC(cb_menu_track_import), _("_Import"),
864 NULL, "<OSM2Go-Main>/Track/Import",
865 0, 0, TRUE, FALSE, FALSE
866 );
867
868 appdata->track.menu_item_track_export = menu_append_new_item(
869 appdata, submenu, GTK_SIGNAL_FUNC(cb_menu_track_export), _("_Export"),
870 NULL, "<OSM2Go-Main>/Track/Export",
871 0, 0, FALSE, FALSE, FALSE
872 );
873
874 appdata->track.menu_item_track_clear = menu_append_new_item(
875 appdata, submenu, GTK_SIGNAL_FUNC(cb_menu_track_clear), _("_Clear"),
876 GTK_STOCK_CLEAR, "<OSM2Go-Main>/Track/Clear",
877 0, 0, FALSE, FALSE, FALSE
878 );
879
880
881 appdata->track.menu_item_track_enable_gps = menu_append_new_item(
882 appdata, submenu, GTK_SIGNAL_FUNC(cb_menu_track_enable_gps),_("_GPS enable"),
883 NULL, "<OSM2Go-Main>/Track/GPS",
884 GDK_g, GDK_CONTROL_MASK|GDK_SHIFT_MASK, TRUE, TRUE,
885 appdata->settings->enable_gps
886 );
887
888 appdata->track.menu_item_track_follow_gps = menu_append_new_item(
889 appdata, submenu, GTK_SIGNAL_FUNC(cb_menu_track_follow_gps), _("GPS follow"),
890 NULL, "<OSM2Go-Main>/Track/Follow",
891 0, 0, appdata->settings->enable_gps, TRUE,
892 appdata->settings->follow_gps
893 );
894
895 /* ------------------------------------------------------- */
896
897 gtk_menu_shell_append(GTK_MENU_SHELL(about_quit_items_menu),
898 gtk_separator_menu_item_new());
899
900 menu_append_new_item(
901 appdata, about_quit_items_menu, GTK_SIGNAL_FUNC(cb_menu_about), _("_About"),
902 GTK_STOCK_ABOUT, "<OSM2Go-Main>/About",
903 0, 0, TRUE, FALSE, FALSE
904 );
905
906 #ifndef USE_HILDON
907 menu_append_new_item(
908 appdata, about_quit_items_menu, GTK_SIGNAL_FUNC(cb_menu_quit), _("_Quit"),
909 GTK_STOCK_QUIT, "<OSM2Go-Main>/Quit",
910 0, 0, TRUE, FALSE, FALSE
911 );
912 #endif
913
914 gtk_window_add_accel_group(GTK_WINDOW(appdata->window), accel_grp);
915
916 #ifdef USE_HILDON
917 hildon_window_set_menu(appdata->window, GTK_MENU(menu));
918 #else
919 GtkWidget *menu_bar = menu;
920
921 #ifndef UISPECIFIC_MAIN_MENU_IS_MENU_BAR
922 // we need to make one first
923 menu_bar = gtk_menu_bar_new();
924
925 GtkWidget *root_menu = gtk_menu_item_new_with_label (_("Menu"));
926 gtk_widget_show(root_menu);
927
928 gtk_menu_bar_append(menu_bar, root_menu);
929 gtk_menu_item_set_submenu(GTK_MENU_ITEM (root_menu), menu);
930
931 gtk_widget_show(menu_bar);
932 #endif //UISPECIFIC_MAIN_MENU_IS_MENU_BAR
933
934 gtk_box_pack_start(GTK_BOX(appdata->vbox), menu_bar, 0, 0, 0);
935
936 #endif //USE_HILDON
937 }
938
939 void menu_cleanup(appdata_t *appdata) { }
940
941 #else // !defined(USE_HILDON) || (MAEMO_VERSION_MAJOR < 5)
942
943 void submenu_entry(appdata_t *appdata, HildonAppMenu *menu,
944 const char *label, const char *value,
945 GtkSignalFunc activate_cb) {
946
947 }
948
949 typedef struct {
950 const char *label, *value;
951 gboolean enabled;
952 gboolean (*toggle)(appdata_t *appdata);
953 gulong offset;
954 GtkSignalFunc activate_cb;
955 } menu_entry_t;
956
957 typedef struct {
958 const char *title;
959 const menu_entry_t *menu;
960 int len;
961 } submenu_t;
962
963 static gboolean enable_gps_get_toggle(appdata_t *appdata) {
964 if(!appdata) return FALSE;
965 if(!appdata->settings) return FALSE;
966 return appdata->settings->enable_gps;
967 }
968
969 static gboolean follow_gps_get_toggle(appdata_t *appdata) {
970 if(!appdata) return FALSE;
971 if(!appdata->settings) return FALSE;
972 return appdata->settings->follow_gps;
973 }
974
975 /* create a HildonAppMenu */
976 static GtkWidget *app_menu_create(appdata_t *appdata,
977 const menu_entry_t *menu_entries) {
978 HildonAppMenu *menu = HILDON_APP_MENU(hildon_app_menu_new());
979
980 while(menu_entries->label) {
981 GtkWidget *button = NULL;
982
983 if(!menu_entries->toggle) {
984 button = hildon_button_new_with_text(
985 HILDON_SIZE_FINGER_HEIGHT | HILDON_SIZE_AUTO_WIDTH,
986 HILDON_BUTTON_ARRANGEMENT_VERTICAL,
987 _(menu_entries->label), _(menu_entries->value));
988 g_signal_connect_after(button, "clicked",
989 menu_entries->activate_cb, appdata);
990 } else {
991 button = hildon_check_button_new(HILDON_SIZE_AUTO);
992 gtk_button_set_label(GTK_BUTTON(button), _(menu_entries->label));
993 printf("requesting check for %s: %p\n", menu_entries->label,
994 menu_entries->toggle);
995 hildon_check_button_set_active(HILDON_CHECK_BUTTON(button),
996 menu_entries->toggle(appdata));
997 g_signal_connect_after(button, "toggled",
998 menu_entries->activate_cb, appdata);
999 }
1000
1001 hildon_button_set_title_alignment(HILDON_BUTTON(button), 0.5, 0.5);
1002 hildon_button_set_value_alignment(HILDON_BUTTON(button), 0.5, 0.5);
1003
1004 /* offset to GtkWidget pointer was given -> store pointer */
1005 if(menu_entries->offset)
1006 *(GtkWidget**)(((void*)appdata)+menu_entries->offset) = button;
1007
1008 gtk_widget_set_sensitive(button, menu_entries->enabled);
1009
1010 hildon_app_menu_append(menu, GTK_BUTTON(button));
1011 menu_entries++;
1012 }
1013
1014 gtk_widget_show_all(GTK_WIDGET(menu));
1015 return GTK_WIDGET(menu);
1016 }
1017
1018 #ifdef MAEMO5_SUBMENU_POPUP
1019 static GtkWidget *app_submenu_create(appdata_t *appdata,
1020 const submenu_t *submenu) {
1021 return app_menu_create(appdata, submenu->menu);
1022 }
1023
1024 static void submenu_popup(GtkWidget *menu) {
1025 GtkWidget *top = hildon_window_stack_peek(hildon_window_stack_get_default());
1026
1027 #if 0 // nasty workaround for race condition in hildonappmenu
1028 int start, end;
1029 GTimeVal tv;
1030 g_get_current_time(&tv);
1031 start = tv.tv_sec * 1000 + tv.tv_usec / 1000;
1032 do {
1033 if(gtk_events_pending())
1034 while(gtk_events_pending()) {
1035 putchar('.'); fflush(stdout);
1036 gtk_main_iteration();
1037 }
1038 else
1039 usleep(100);
1040
1041 g_get_current_time(&tv);
1042 end = tv.tv_sec * 1000 + tv.tv_usec / 1000;
1043 } while(end-start < 500);
1044 #endif
1045
1046 hildon_app_menu_popup(HILDON_APP_MENU(menu), GTK_WINDOW(top));
1047 }
1048
1049 static void submenu_cleanup(GtkWidget *menu) {
1050 g_object_unref(menu);
1051 }
1052 #else
1053 #define COLUMNS 2
1054
1055 void on_submenu_entry_clicked(GtkButton *button, GtkWidget *menu) {
1056
1057 /* force closing of submenu dialog */
1058 gtk_dialog_response(GTK_DIALOG(menu), GTK_RESPONSE_NONE);
1059 gtk_widget_hide(menu);
1060
1061 /* let gtk clean up */
1062 while(gtk_events_pending())
1063 gtk_main_iteration();
1064 }
1065
1066 /* use standard dialog boxes for fremantle submenues */
1067 static GtkWidget *app_submenu_create(appdata_t *appdata,
1068 const submenu_t *submenu) {
1069
1070 /* create a oridinary dialog box */
1071 GtkWidget *dialog = misc_dialog_new(MISC_DIALOG_SMALL, _(submenu->title),
1072 GTK_WINDOW(appdata->window), NULL);
1073
1074 gtk_dialog_set_has_separator(GTK_DIALOG(dialog), FALSE);
1075
1076 GtkWidget *table = gtk_table_new(submenu->len/COLUMNS, COLUMNS, TRUE);
1077 int x = 0, y = 0;
1078
1079 const menu_entry_t *menu_entries = submenu->menu;
1080 while(menu_entries->label) {
1081 GtkWidget *button = NULL;
1082
1083 if(!menu_entries->toggle) {
1084 button = hildon_button_new_with_text(
1085 HILDON_SIZE_FINGER_HEIGHT | HILDON_SIZE_AUTO_WIDTH,
1086 HILDON_BUTTON_ARRANGEMENT_VERTICAL,
1087 _(menu_entries->label), _(menu_entries->value));
1088
1089 g_signal_connect(button, "clicked",
1090 G_CALLBACK(on_submenu_entry_clicked), dialog);
1091
1092 g_signal_connect(button, "clicked",
1093 menu_entries->activate_cb, appdata);
1094
1095 hildon_button_set_title_alignment(HILDON_BUTTON(button), 0.5, 0.5);
1096 hildon_button_set_value_alignment(HILDON_BUTTON(button), 0.5, 0.5);
1097 } else {
1098 button = hildon_check_button_new(HILDON_SIZE_AUTO);
1099 gtk_button_set_label(GTK_BUTTON(button), _(menu_entries->label));
1100 printf("requesting check for %s: %p\n", menu_entries->label,
1101 menu_entries->toggle);
1102 hildon_check_button_set_active(HILDON_CHECK_BUTTON(button),
1103 menu_entries->toggle(appdata));
1104
1105 g_signal_connect(button, "clicked",
1106 G_CALLBACK(on_submenu_entry_clicked), dialog);
1107
1108 g_signal_connect(button, "toggled",
1109 menu_entries->activate_cb, appdata);
1110
1111 gtk_button_set_alignment(GTK_BUTTON(button), 0.5, 0.5);
1112 }
1113
1114 /* offset to GtkWidget pointer was given -> store pointer */
1115 if(menu_entries->offset)
1116 *(GtkWidget**)(((void*)appdata)+menu_entries->offset) = button;
1117
1118 gtk_widget_set_sensitive(button, menu_entries->enabled);
1119
1120 gtk_table_attach_defaults(GTK_TABLE(table), button, x, x+1, y, y+1);
1121
1122 x++;
1123 if(x == COLUMNS) { x = 0; y++; }
1124
1125 menu_entries++;
1126 }
1127
1128
1129 gtk_box_pack_start_defaults(GTK_BOX(GTK_DIALOG(dialog)->vbox), table);
1130
1131 return dialog;
1132 }
1133
1134 /* popup the dialog shaped submenu */
1135 static void submenu_popup(GtkWidget *menu) {
1136 gtk_widget_show_all(menu);
1137 gtk_dialog_run(GTK_DIALOG(menu));
1138 gtk_widget_hide(menu);
1139 }
1140
1141 static void submenu_cleanup(GtkWidget *menu) {
1142 gtk_widget_destroy(menu);
1143 }
1144 #endif
1145
1146 /* the view submenu */
1147 void on_submenu_view_clicked(GtkButton *button, appdata_t *appdata) {
1148 submenu_popup(appdata->app_menu_view);
1149 }
1150
1151 void on_submenu_map_clicked(GtkButton *button, appdata_t *appdata) {
1152 submenu_popup(appdata->app_menu_map);
1153 }
1154
1155 void on_submenu_wms_clicked(GtkButton *button, appdata_t *appdata) {
1156 submenu_popup(appdata->app_menu_wms);
1157 }
1158
1159 void on_submenu_track_clicked(GtkButton *button, appdata_t *appdata) {
1160 submenu_popup(appdata->app_menu_track);
1161 }
1162
1163 #define APP_OFFSET(a) offsetof(appdata_t, a)
1164 #define SIMPLE_ENTRY(a,b) { a, NULL, TRUE, NULL, 0, G_CALLBACK(b) }
1165 #define ENABLED_ENTRY(a,b,c) { a, NULL, TRUE, NULL, APP_OFFSET(c), G_CALLBACK(b) }
1166 #define DISABLED_ENTRY(a,b,c) { a, NULL, FALSE, NULL, APP_OFFSET(c), G_CALLBACK(b) }
1167 #define TOGGLE_ENTRY(a,b,c) { a, NULL, TRUE, c, 0, G_CALLBACK(b) }
1168 #define DISABLED_TOGGLE_ENTRY(a,b,c,d) \
1169 { a, NULL, FALSE, c, APP_OFFSET(d), G_CALLBACK(b) }
1170 #define ENABLED_TOGGLE_ENTRY(a,b,c,d) \
1171 { a, NULL, TRUE, c, APP_OFFSET(d), G_CALLBACK(b) }
1172 #define LAST_ENTRY { NULL, NULL, FALSE, NULL, 0, NULL }
1173
1174 /* -- the view submenu -- */
1175 static const menu_entry_t submenu_view_entries[] = {
1176 #ifndef ZOOM_BUTTONS
1177 SIMPLE_ENTRY("Zoom in", cb_menu_zoomin),
1178 SIMPLE_ENTRY("Zoom out", cb_menu_zoomout),
1179 #endif
1180 /* --- */
1181 SIMPLE_ENTRY("Style", cb_menu_style),
1182 /* --- */
1183 #ifndef DETAIL_POPUP
1184 SIMPLE_ENTRY("Normal details", cb_menu_view_detail_normal),
1185 SIMPLE_ENTRY("More details", cb_menu_view_detail_inc),
1186 SIMPLE_ENTRY("Less details", cb_menu_view_detail_dec),
1187 #endif
1188 /* --- */
1189 DISABLED_ENTRY("Hide selected", cb_menu_map_hide_sel, menu_item_map_hide_sel),
1190 DISABLED_ENTRY("Show all", cb_menu_map_show_all, menu_item_map_show_all),
1191
1192 LAST_ENTRY
1193 };
1194
1195 static const submenu_t submenu_view = {
1196 "View", submenu_view_entries,
1197 sizeof(submenu_view_entries)/sizeof(menu_entry_t)-1
1198 };
1199
1200 /* -- the map submenu -- */
1201 static const menu_entry_t submenu_map_entries[] = {
1202 ENABLED_ENTRY("Upload", cb_menu_upload, menu_item_map_upload),
1203 SIMPLE_ENTRY("Download", cb_menu_download),
1204 ENABLED_ENTRY("Undo all", cb_menu_undo_changes,
1205 menu_item_map_undo_changes),
1206 ENABLED_ENTRY("Relations", cb_menu_osm_relations,
1207 menu_item_map_relations),
1208
1209 LAST_ENTRY
1210 };
1211
1212 static const submenu_t submenu_map = {
1213 "Map", submenu_map_entries,
1214 sizeof(submenu_map_entries)/sizeof(menu_entry_t)-1
1215 };
1216
1217 /* -- the wms submenu -- */
1218 static const menu_entry_t submenu_wms_entries[] = {
1219 SIMPLE_ENTRY("Import", cb_menu_wms_import),
1220 DISABLED_ENTRY("Clear", cb_menu_wms_clear, menu_item_wms_clear),
1221 DISABLED_ENTRY("Adjust", cb_menu_wms_adjust, menu_item_wms_adjust),
1222
1223 LAST_ENTRY
1224 };
1225
1226 static const submenu_t submenu_wms = {
1227 "WMS", submenu_wms_entries,
1228 sizeof(submenu_wms_entries)/sizeof(menu_entry_t)-1
1229 };
1230
1231 /* -- the track submenu -- */
1232 static const menu_entry_t submenu_track_entries[] = {
1233 ENABLED_ENTRY("Import", cb_menu_track_import, track.menu_item_track_import),
1234 DISABLED_ENTRY("Export", cb_menu_track_export, track.menu_item_track_export),
1235 DISABLED_ENTRY("Clear", cb_menu_track_clear, track.menu_item_track_clear),
1236 ENABLED_TOGGLE_ENTRY("GPS enable", cb_menu_track_enable_gps,
1237 enable_gps_get_toggle, track.menu_item_track_enable_gps),
1238 DISABLED_TOGGLE_ENTRY("GPS follow", cb_menu_track_follow_gps,
1239 follow_gps_get_toggle, track.menu_item_track_follow_gps),
1240
1241 LAST_ENTRY
1242 };
1243
1244 static const submenu_t submenu_track = {
1245 "Track", submenu_track_entries,
1246 sizeof(submenu_track_entries)/sizeof(menu_entry_t)-1
1247 };
1248
1249
1250 /* -- the applications main menu -- */
1251 static const menu_entry_t main_menu[] = {
1252 SIMPLE_ENTRY("About", cb_menu_about),
1253 SIMPLE_ENTRY("Project", cb_menu_project_open),
1254 ENABLED_ENTRY("View", on_submenu_view_clicked, submenu_view),
1255 ENABLED_ENTRY("Map", on_submenu_map_clicked, submenu_map),
1256 ENABLED_ENTRY("WMS", on_submenu_wms_clicked, submenu_wms),
1257 ENABLED_ENTRY("Track", on_submenu_track_clicked, track.submenu_track),
1258
1259 LAST_ENTRY
1260 };
1261
1262 void menu_create(appdata_t *appdata) {
1263 HildonAppMenu *menu = HILDON_APP_MENU(hildon_app_menu_new());
1264
1265 /* build menu/submenus */
1266 menu = HILDON_APP_MENU(app_menu_create(appdata, main_menu));
1267 appdata->app_menu_wms = app_submenu_create(appdata, &submenu_wms);
1268 g_object_ref(appdata->app_menu_wms);
1269 appdata->app_menu_map = app_submenu_create(appdata, &submenu_map);
1270 g_object_ref(appdata->app_menu_map);
1271 appdata->app_menu_view = app_submenu_create(appdata, &submenu_view);
1272 g_object_ref(appdata->app_menu_view);
1273 appdata->app_menu_track = app_submenu_create(appdata, &submenu_track);
1274 g_object_ref(appdata->app_menu_track);
1275
1276 /* enable/disable some entries according to settings */
1277 if(appdata && appdata->settings)
1278 gtk_widget_set_sensitive(appdata->track.menu_item_track_follow_gps,
1279 appdata->settings->enable_gps);
1280
1281 hildon_window_set_app_menu(HILDON_WINDOW(appdata->window), menu);
1282 }
1283
1284 void menu_cleanup(appdata_t *appdata) {
1285 submenu_cleanup(appdata->app_menu_view);
1286 submenu_cleanup(appdata->app_menu_map);
1287 submenu_cleanup(appdata->app_menu_wms);
1288 submenu_cleanup(appdata->app_menu_track);
1289 }
1290 #endif
1291
1292 /********************* end of menu **********************/
1293
1294 #ifdef UISPECIFIC_MENU_HAS_ACCELS
1295 #define ACCELS_FILE "accels"
1296
1297 static void menu_accels_load(appdata_t *appdata) {
1298 char *accels_file = g_strdup_printf("%s/" ACCELS_FILE,
1299 appdata->settings->base_path);
1300 gtk_accel_map_load(accels_file);
1301 g_free(accels_file);
1302 }
1303
1304 static void menu_accels_save(appdata_t *appdata) {
1305 char *accels_file = g_strdup_printf("%s" ACCELS_FILE,
1306 appdata->settings->base_path);
1307 gtk_accel_map_save(accels_file);
1308 g_free(accels_file);
1309 }
1310
1311 #endif
1312
1313
1314 void cleanup(appdata_t *appdata) {
1315 #ifdef UISPECIFIC_MENU_HAS_ACCELS
1316 menu_accels_save(appdata);
1317 #endif
1318
1319 settings_save(appdata->settings);
1320
1321 #ifdef USE_HILDON
1322 if(appdata->osso_context)
1323 osso_deinitialize(appdata->osso_context);
1324
1325 appdata->program = NULL;
1326 #endif
1327
1328 printf("waiting for gtk to shut down ");
1329
1330 /* let gtk clean up first */
1331 while(gtk_events_pending()) {
1332 putchar('.');
1333 gtk_main_iteration();
1334 }
1335
1336 printf(" ok\n");
1337
1338 /* save project file */
1339 if(appdata->project)
1340 project_save(GTK_WIDGET(appdata->window), appdata->project);
1341
1342 map_remove_bg_image(appdata->map);
1343
1344 osm_free(&appdata->icon, appdata->osm);
1345 appdata->osm = NULL;
1346
1347 curl_global_cleanup();
1348
1349 josm_presets_free(appdata->presets);
1350
1351 icon_free_all(&appdata->icon);
1352
1353 gps_release(appdata);
1354
1355 settings_free(appdata->settings);
1356
1357 statusbar_free(appdata->statusbar);
1358
1359 iconbar_free(appdata->iconbar);
1360
1361 project_free(appdata->project);
1362
1363 if(appdata->menu_item_map_undo)
1364 undo_free(appdata->osm, appdata->undo.state);
1365
1366 menu_cleanup(appdata);
1367
1368 puts("everything is gone");
1369 }
1370
1371 void on_window_destroy (GtkWidget *widget, gpointer data) {
1372 appdata_t *appdata = (appdata_t*)data;
1373
1374 gtk_main_quit();
1375 appdata->window = NULL;
1376 }
1377
1378 gboolean on_window_key_press(GtkWidget *widget,
1379 GdkEventKey *event, gpointer data) {
1380 appdata_t *appdata = (appdata_t*)data;
1381 int handled = FALSE;
1382
1383 // printf("key event with keyval %x\n", event->keyval);
1384
1385 // the map handles some keys on its own ...
1386 switch(event->keyval) {
1387
1388 #if !defined(USE_HILDON) || (MAEMO_VERSION_MAJOR < 5)
1389 #ifdef USE_HILDON
1390 /* this is in fact a mapping to GDK_F6 */
1391 case HILDON_HARDKEY_FULLSCREEN:
1392 #else
1393 case GDK_F11:
1394 #endif
1395 if(!gtk_check_menu_item_get_active(
1396 GTK_CHECK_MENU_ITEM(appdata->menu_item_view_fullscreen))) {
1397 gtk_window_fullscreen(GTK_WINDOW(appdata->window));
1398 gtk_check_menu_item_set_active(
1399 GTK_CHECK_MENU_ITEM(appdata->menu_item_view_fullscreen), TRUE);
1400 } else {
1401 gtk_window_unfullscreen(GTK_WINDOW(appdata->window));
1402 gtk_check_menu_item_set_active(
1403 GTK_CHECK_MENU_ITEM(appdata->menu_item_view_fullscreen), FALSE);
1404 }
1405
1406 handled = TRUE;
1407 break;
1408 #endif
1409 }
1410
1411 /* forward unprocessed key presses to map */
1412 if(!handled && appdata->project && appdata->osm)
1413 handled = map_key_press_event(appdata, event);
1414
1415 return handled;
1416 }
1417
1418 #if (MAEMO_VERSION_MAJOR == 5) && !defined(__i386__)
1419 /* get access to zoom buttons */
1420 static void
1421 on_window_realize(GtkWidget *widget, appdata_t *appdata) {
1422 if (widget->window) {
1423 unsigned char value = 1;
1424 Atom hildon_zoom_key_atom =
1425 gdk_x11_get_xatom_by_name("_HILDON_ZOOM_KEY_ATOM"),
1426 integer_atom = gdk_x11_get_xatom_by_name("INTEGER");
1427 Display *dpy =
1428 GDK_DISPLAY_XDISPLAY(gdk_drawable_get_display(widget->window));
1429 Window w = GDK_WINDOW_XID(widget->window);
1430
1431 XChangeProperty(dpy, w, hildon_zoom_key_atom,
1432 integer_atom, 8, PropModeReplace, &value, 1);
1433 }
1434 }
1435 #endif
1436
1437 #ifdef FREMANTLE
1438 static GtkWidget *icon_button(appdata_t *appdata, char *icon, GCallback cb,
1439 GtkWidget *box) {
1440 /* add zoom-in button */
1441 GtkWidget *but = gtk_button_new();
1442 gtk_button_set_image(GTK_BUTTON(but),
1443 icon_widget_load(&appdata->icon, icon));
1444 // gtk_button_set_relief(GTK_BUTTON(but), GTK_RELIEF_NONE);
1445 hildon_gtk_widget_set_theme_size(but,
1446 (HILDON_SIZE_FINGER_HEIGHT | HILDON_SIZE_AUTO_WIDTH));
1447
1448 if(cb)
1449 g_signal_connect(but, "clicked", G_CALLBACK(cb), appdata);
1450
1451 gtk_box_pack_start(GTK_BOX(box), but, FALSE, FALSE, 0);
1452 return but;
1453 }
1454 #endif
1455
1456 int main(int argc, char *argv[]) {
1457 appdata_t appdata;
1458
1459 /* init appdata */
1460 memset(&appdata, 0, sizeof(appdata));
1461
1462 printf("Using locale for %s in %s\n", PACKAGE, LOCALEDIR);
1463
1464 setlocale(LC_ALL, "");
1465 bindtextdomain(PACKAGE, LOCALEDIR);
1466 bind_textdomain_codeset(PACKAGE, "UTF-8");
1467 textdomain(PACKAGE);
1468
1469 /* Must initialize libcurl before any threads are started */
1470 curl_global_init(CURL_GLOBAL_ALL);
1471
1472 g_thread_init(NULL);
1473
1474 gtk_init (&argc, &argv);
1475
1476 gps_init(&appdata);
1477
1478 #ifdef USE_HILDON
1479 printf("Installing osso context for \"org.harbaum." PACKAGE "\"\n");
1480 appdata.osso_context = osso_initialize("org.harbaum."PACKAGE,
1481 VERSION, TRUE, NULL);
1482 if(appdata.osso_context == NULL)
1483 fprintf(stderr, "error initiating osso context\n");
1484
1485 dbus_register(&appdata);
1486 #endif
1487
1488 #ifdef USE_HILDON
1489 /* Create the hildon program and setup the title */
1490 appdata.program = HILDON_PROGRAM(hildon_program_get_instance());
1491 g_set_application_name("OSM2Go");
1492
1493 /* Create HildonWindow and set it to HildonProgram */
1494 #if MAEMO_VERSION_MAJOR < 5
1495 appdata.window = HILDON_WINDOW(hildon_window_new());
1496 #else
1497 appdata.window = HILDON_WINDOW(hildon_stackable_window_new());
1498 #endif
1499 hildon_program_add_window(appdata.program, appdata.window);
1500
1501 /* try to enable the zoom buttons. don't do this on x86 as it breaks */
1502 /* at runtime with cygwin x */
1503 #if (MAEMO_VERSION_MAJOR == 5) && !defined(__i386__)
1504 g_signal_connect(G_OBJECT(appdata.window), "realize",
1505 G_CALLBACK(on_window_realize), &appdata);
1506 #endif // MAEMO_VERSION
1507
1508 #else
1509 /* Create a Window. */
1510 appdata.window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
1511 gtk_window_set_title(GTK_WINDOW(appdata.window), "OSM2Go");
1512 /* Set a decent default size for the window. */
1513 gtk_window_set_default_size(GTK_WINDOW(appdata.window),
1514 DEFAULT_WIDTH, DEFAULT_HEIGHT);
1515 gtk_window_set_icon(GTK_WINDOW(appdata.window),
1516 icon_load(&appdata.icon, PACKAGE));
1517 #endif
1518
1519 g_signal_connect(G_OBJECT(appdata.window), "destroy",
1520 G_CALLBACK(on_window_destroy), &appdata);
1521
1522 g_signal_connect(G_OBJECT(appdata.window), "key_press_event",
1523 G_CALLBACK(on_window_key_press), &appdata);
1524
1525 /* user specific init */
1526 appdata.settings = settings_load();
1527
1528 appdata.vbox = gtk_vbox_new(FALSE,0);
1529 menu_create(&appdata);
1530 #ifdef UISPECIFIC_MENU_HAS_ACCELS
1531 menu_accels_load(&appdata);
1532 #endif
1533
1534 /* ----------------------- setup main window ---------------- */
1535
1536 GtkWidget *hbox = gtk_hbox_new(FALSE,0);
1537 GtkWidget *vbox = gtk_vbox_new(FALSE,0);
1538
1539 #ifdef PORTRAIT
1540 gtk_box_pack_start(GTK_BOX(vbox), iconbar_new(&appdata), FALSE, FALSE, 0);
1541 #endif
1542
1543 /* generate main map view */
1544 GtkWidget *map = map_new(&appdata);
1545 if(!map) {
1546 cleanup(&appdata);
1547 return -1;
1548 }
1549
1550 gtk_box_pack_start(GTK_BOX(vbox), map, TRUE, TRUE, 0);
1551
1552 /* fremantle has seperate zoom/details buttons on the right screen side */
1553 #if !defined(FREMANTLE) && (defined(ZOOM_BUTTONS) || defined(DETAIL_POPUP))
1554 GtkWidget *zhbox = gtk_hbox_new(FALSE, 0);
1555 gtk_box_pack_start_defaults(GTK_BOX(zhbox), statusbar_new(&appdata));
1556 #endif
1557
1558 #if !defined(FREMANTLE) && defined(DETAIL_POPUP)
1559 /* ---- detail popup ---- */
1560 appdata.btn_detail_popup = gtk_button_new();
1561 gtk_button_set_image(GTK_BUTTON(appdata.btn_detail_popup),
1562 gtk_image_new_from_stock(GTK_STOCK_EDIT, GTK_ICON_SIZE_MENU));
1563 g_signal_connect(appdata.btn_detail_popup, "clicked",
1564 G_CALLBACK(scale_popup), &appdata);
1565 gtk_box_pack_start(GTK_BOX(zhbox), appdata.btn_detail_popup, FALSE, FALSE, 0);
1566 #endif
1567
1568 #if !defined(FREMANTLE) && defined(ZOOM_BUTTONS)
1569 /* ---- add zoom out button right of statusbar ---- */
1570 appdata.btn_zoom_out = gtk_button_new();
1571 gtk_button_set_image(GTK_BUTTON(appdata.btn_zoom_out),
1572 gtk_image_new_from_stock(GTK_STOCK_ZOOM_OUT, GTK_ICON_SIZE_MENU));
1573 g_signal_connect(appdata.btn_zoom_out, "clicked",
1574 G_CALLBACK(cb_menu_zoomout), &appdata);
1575 gtk_box_pack_start(GTK_BOX(zhbox), appdata.btn_zoom_out, FALSE, FALSE, 0);
1576
1577 /* ---- add zoom in button right of statusbar ---- */
1578 appdata.btn_zoom_in = gtk_button_new();
1579 gtk_button_set_image(GTK_BUTTON(appdata.btn_zoom_in),
1580 gtk_image_new_from_stock(GTK_STOCK_ZOOM_IN, GTK_ICON_SIZE_MENU));
1581 g_signal_connect(appdata.btn_zoom_in, "clicked",
1582 G_CALLBACK(cb_menu_zoomin), &appdata);
1583 gtk_box_pack_start(GTK_BOX(zhbox), appdata.btn_zoom_in, FALSE, FALSE, 0);
1584 #endif
1585
1586 #if !defined(FREMANTLE) && (defined(ZOOM_BUTTONS) || defined(DETAIL_POPUP))
1587 gtk_box_pack_start(GTK_BOX(vbox), zhbox, FALSE, FALSE, 0);
1588 #else
1589 gtk_box_pack_start(GTK_BOX(vbox), statusbar_new(&appdata), FALSE, FALSE, 0);
1590 #endif
1591
1592 #ifndef PORTRAIT
1593 gtk_box_pack_start(GTK_BOX(hbox), iconbar_new(&appdata), FALSE, FALSE, 0);
1594 #endif
1595 gtk_box_pack_start(GTK_BOX(hbox), vbox, TRUE, TRUE, 0);
1596
1597 #ifdef FREMANTLE
1598 /* fremantle has a set of buttons on the right screen side as well */
1599 vbox = gtk_vbox_new(FALSE, 0);
1600
1601 GtkWidget *ivbox = gtk_vbox_new(FALSE, 0);
1602 appdata.btn_zoom_in =
1603 icon_button(&appdata, "zoomin_thumb", G_CALLBACK(cb_menu_zoomin), ivbox);
1604 appdata.btn_zoom_out =
1605 icon_button(&appdata, "zoomout_thumb", G_CALLBACK(cb_menu_zoomout), ivbox);
1606 gtk_box_pack_start(GTK_BOX(vbox), ivbox, FALSE, FALSE, 0);
1607
1608 ivbox = gtk_vbox_new(FALSE, 0);
1609 icon_button(&appdata, "detailup_thumb", G_CALLBACK(cb_menu_view_detail_inc), ivbox);
1610 icon_button(&appdata, "detaildown_thumb", G_CALLBACK(cb_menu_view_detail_dec), ivbox);
1611 gtk_box_pack_start(GTK_BOX(vbox), ivbox, TRUE, FALSE, 0);
1612
1613 ivbox = gtk_vbox_new(FALSE, 0);
1614 GtkWidget *ok = icon_button(&appdata, "ok_thumb", NULL, ivbox);
1615 GtkWidget *cancel = icon_button(&appdata, "cancel_thumb", NULL, ivbox);
1616 iconbar_register_buttons(&appdata, ok, cancel);
1617 gtk_box_pack_start(GTK_BOX(vbox), ivbox, FALSE, FALSE, 0);
1618
1619 gtk_box_pack_start(GTK_BOX(hbox), vbox, FALSE, FALSE, 0);
1620 #endif // FREMANTLE
1621
1622 gtk_box_pack_start(GTK_BOX(appdata.vbox), hbox, TRUE, TRUE, 0);
1623
1624 gtk_container_add(GTK_CONTAINER(appdata.window), appdata.vbox);
1625
1626 gtk_widget_show_all(GTK_WIDGET(appdata.window));
1627
1628 appdata.presets = josm_presets_load();
1629
1630 /* let gtk do its thing before loading the data, */
1631 /* so the user sees something */
1632 while(gtk_events_pending())
1633 gtk_main_iteration();
1634
1635 /* load project if one is specified in the settings */
1636 if(appdata.settings->project)
1637 project_load(&appdata, appdata.settings->project);
1638
1639 main_ui_enable(&appdata);
1640
1641 /* start GPS if enabled by config */
1642 if(appdata.settings && appdata.settings->enable_gps)
1643 track_enable_gps(&appdata, TRUE);
1644
1645 /* again let the ui do its thing */
1646 while(gtk_events_pending())
1647 gtk_main_iteration();
1648
1649 /* start to interact with the user now that the gui is running */
1650 if(appdata.settings->first_run_demo) {
1651 messagef(GTK_WIDGET(appdata.window), _("Welcome to OSM2Go"),
1652 _("This is the first time you run OSM2Go. "
1653 "A demo project has been loaded to get you "
1654 "started. You can play around with this demo as much "
1655 "as you like. However, you cannot upload or download "
1656 "the demo project.\n\n"
1657 "In order to start working on real data you'll have "
1658 "to setup a new project and enter your OSM user name "
1659 "and password. You'll then be able to download the "
1660 "latest data from OSM and upload your changes into "
1661 "the OSM main database."
1662 ));
1663 }
1664
1665 /* ------------ jump into main loop ---------------- */
1666 gtk_main();
1667
1668 puts("gtk_main() left");
1669
1670 track_save(appdata.project, appdata.track.track);
1671 track_clear(&appdata, appdata.track.track);
1672 appdata.track.track = NULL;
1673
1674 /* save a diff if there are dirty entries */
1675 diff_save(appdata.project, appdata.osm);
1676
1677 cleanup(&appdata);
1678
1679 return 0;
1680 }
1681
1682 // vim:et:ts=8:sw=2:sts=2:ai