Contents of /trunk/src/main.c

Parent Directory Parent Directory | Revision Log Revision Log


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