2 * viking -- GPS Data and Topo Analyzer, Explorer, and Manager
4 * Copyright (C) 2003-2005, Evan Battaglia <gtoevan@gmx.net>
5 * Copyright (C) 2005-2006, Alex Foobarian <foobarian@gmail.com>
6 * Copyright (C) 2012, Rob Norris <rw_norris@hotmail.com>
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
29 #include "background.h"
31 #include "datasources.h"
36 #include "preferences.h"
37 #include "viklayer_defaults.h"
38 #include "icons/icons.h"
39 #include "vikexttools.h"
40 #include "vikexttool_datasources.h"
41 #include "garminsymbols.h"
42 #include "vikmapslayer.h"
43 #include "geonamessearch.h"
56 #include <glib/gstdio.h>
57 #include <glib/gprintf.h>
58 #include <glib/gi18n.h>
60 #include <gdk/gdkkeysyms.h>
62 // This seems rather arbitary, quite large and pointless
63 // I mean, if you have a thousand windows open;
64 // why not be allowed to open a thousand more...
65 #define MAX_WINDOWS 1024
66 static guint window_count = 0;
67 static GSList *window_list = NULL;
69 #define VIKING_WINDOW_WIDTH 1000
70 #define VIKING_WINDOW_HEIGHT 800
71 #define DRAW_IMAGE_DEFAULT_WIDTH 1280
72 #define DRAW_IMAGE_DEFAULT_HEIGHT 1024
73 #define DRAW_IMAGE_DEFAULT_SAVE_AS_PNG TRUE
75 static void window_finalize ( GObject *gob );
76 static GObjectClass *parent_class;
78 static void window_set_filename ( VikWindow *vw, const gchar *filename );
79 static const gchar *window_get_filename ( VikWindow *vw );
81 static VikWindow *window_new ();
83 static void draw_update ( VikWindow *vw );
85 static void newwindow_cb ( GtkAction *a, VikWindow *vw );
88 static void open_window ( VikWindow *vw, GSList *files );
89 static void destroy_window ( GtkWidget *widget,
94 static gboolean delete_event( VikWindow *vw );
96 static gboolean key_press_event( VikWindow *vw, GdkEventKey *event, gpointer data );
98 static void window_configure_event ( VikWindow *vw );
99 static void draw_sync ( VikWindow *vw );
100 static void draw_redraw ( VikWindow *vw );
101 static void draw_scroll ( VikWindow *vw, GdkEventScroll *event );
102 static void draw_click ( VikWindow *vw, GdkEventButton *event );
103 static void draw_release ( VikWindow *vw, GdkEventButton *event );
104 static void draw_mouse_motion ( VikWindow *vw, GdkEventMotion *event );
105 static void draw_zoom_cb ( GtkAction *a, VikWindow *vw );
106 static void draw_goto_cb ( GtkAction *a, VikWindow *vw );
107 static void draw_refresh_cb ( GtkAction *a, VikWindow *vw );
109 static void draw_status ( VikWindow *vw );
111 /* End Drawing Functions */
113 static void menu_addlayer_cb ( GtkAction *a, VikWindow *vw );
114 static void menu_properties_cb ( GtkAction *a, VikWindow *vw );
115 static void menu_delete_layer_cb ( GtkAction *a, VikWindow *vw );
117 /* tool management */
123 #define TOOL_LAYER_TYPE_NONE -1
128 toolbox_tool_t *tools;
132 static void menu_tool_cb ( GtkAction *old, GtkAction *a, VikWindow *vw );
133 static toolbox_tools_t* toolbox_create(VikWindow *vw);
134 static void toolbox_add_tool(toolbox_tools_t *vt, VikToolInterface *vti, gint layer_type );
135 static int toolbox_get_tool(toolbox_tools_t *vt, const gchar *tool_name);
136 static void toolbox_activate(toolbox_tools_t *vt, const gchar *tool_name);
137 static const GdkCursor *toolbox_get_cursor(toolbox_tools_t *vt, const gchar *tool_name);
138 static void toolbox_click (toolbox_tools_t *vt, GdkEventButton *event);
139 static void toolbox_move (toolbox_tools_t *vt, GdkEventMotion *event);
140 static void toolbox_release (toolbox_tools_t *vt, GdkEventButton *event);
144 static void window_create_ui( VikWindow *window );
145 static void register_vik_icons (GtkIconFactory *icon_factory);
148 static void load_file ( GtkAction *a, VikWindow *vw );
149 static gboolean save_file_as ( GtkAction *a, VikWindow *vw );
150 static gboolean save_file ( GtkAction *a, VikWindow *vw );
151 static gboolean save_file_and_exit ( GtkAction *a, VikWindow *vw );
152 static gboolean window_save ( VikWindow *vw );
156 VikViewport *viking_vvp;
157 VikLayersPanel *viking_vlp;
158 VikStatusbar *viking_vs;
162 GdkCursor *busy_cursor;
163 GdkCursor *viewport_cursor; // only a reference
165 /* tool management state */
168 guint16 tool_layer_id;
169 guint16 tool_tool_id;
171 GtkActionGroup *action_group;
176 guint draw_image_width, draw_image_height;
177 gboolean draw_image_save_as_png;
182 GtkWidget *open_dia, *save_dia;
183 GtkWidget *save_img_dia, *save_img_dir_dia;
185 gboolean only_updating_coord_mode_ui; /* hack for a bug in GTK */
189 /* half-drawn update */
191 VikCoord trigger_center;
193 /* Store at this level for highlighted selection drawing since it applies to the viewport and the layers panel */
194 /* Only one of these items can be selected at the same time */
195 gpointer selected_vtl; /* notionally VikTrwLayer */
196 GHashTable *selected_tracks;
197 gpointer selected_track; /* notionally VikTrack */
198 GHashTable *selected_waypoints;
199 gpointer selected_waypoint; /* notionally VikWaypoint */
200 /* only use for individual track or waypoint */
201 /* For track(s) & waypoint(s) it is the layer they are in - this helps refering to the individual item easier */
202 gpointer containing_vtl; /* notionally VikTrwLayer */
216 VW_OPENWINDOW_SIGNAL,
220 static guint window_signals[VW_LAST_SIGNAL] = { 0 };
222 // TODO get rid of this as this is unnecessary duplication...
223 static gchar *tool_names[NUMBER_OF_TOOLS] = { N_("Pan"), N_("Zoom"), N_("Ruler"), N_("Select") };
225 G_DEFINE_TYPE (VikWindow, vik_window, GTK_TYPE_WINDOW)
227 VikViewport * vik_window_viewport(VikWindow *vw)
229 return(vw->viking_vvp);
232 VikLayersPanel * vik_window_layers_panel(VikWindow *vw)
234 return(vw->viking_vlp);
238 * Returns the statusbar for the window
240 VikStatusbar * vik_window_get_statusbar ( VikWindow *vw )
242 return vw->viking_vs;
247 vik_statusbar_type_t vs_type;
248 gchar* message; // Always make a copy of this data
249 } statusbar_idle_data;
252 * For the actual statusbar update!
254 static gboolean statusbar_idle_update ( statusbar_idle_data *sid )
256 vik_statusbar_set_message ( sid->vs, sid->vs_type, sid->message );
257 g_free ( sid->message );
263 * vik_window_statusbar_update:
264 * @vw: The main window in which the statusbar will be updated.
265 * @message: The string to be displayed. This is copied.
266 * @vs_type: The part of the statusbar to be updated.
268 * This updates any part of the statusbar with the new string.
269 * It handles calling from the main thread or any background thread
270 * ATM this mostly used from background threads - as from the main thread
271 * one may use the vik_statusbar_set_message() directly.
273 void vik_window_statusbar_update ( VikWindow *vw, const gchar* message, vik_statusbar_type_t vs_type )
275 statusbar_idle_data *sid = g_malloc ( sizeof (statusbar_idle_data) );
276 sid->vs = vw->viking_vs;
277 sid->vs_type = vs_type;
278 sid->message = g_strdup ( message );
280 if ( g_thread_self() == vik_window_get_thread ( vw ) ) {
281 g_idle_add ( (GSourceFunc) statusbar_idle_update, sid );
284 // From a background thread
285 gdk_threads_add_idle ( (GSourceFunc) statusbar_idle_update, sid );
289 // Actual signal handlers
290 static void destroy_window ( GtkWidget *widget,
293 if ( ! --window_count )
297 VikWindow *vik_window_new_window ()
299 if ( window_count < MAX_WINDOWS )
301 VikWindow *vw = window_new ();
303 g_signal_connect (G_OBJECT (vw), "destroy",
304 G_CALLBACK (destroy_window), NULL);
305 g_signal_connect (G_OBJECT (vw), "newwindow",
306 G_CALLBACK (vik_window_new_window), NULL);
307 g_signal_connect (G_OBJECT (vw), "openwindow",
308 G_CALLBACK (open_window), NULL);
310 gtk_widget_show_all ( GTK_WIDGET(vw) );
319 static void open_window ( VikWindow *vw, GSList *files )
321 gboolean change_fn = (g_slist_length(files) == 1); /* only change fn if one file */
322 GSList *cur_file = files;
324 // Only open a new window if a viking file
325 gchar *file_name = cur_file->data;
326 if (vw != NULL && check_file_magic_vik ( file_name ) ) {
327 VikWindow *newvw = vik_window_new_window ();
329 vik_window_open_file ( newvw, file_name, TRUE );
332 vik_window_open_file ( vw, file_name, change_fn );
335 cur_file = g_slist_next (cur_file);
337 g_slist_free (files);
341 void vik_window_selected_layer(VikWindow *vw, VikLayer *vl)
343 int i, j, tool_count;
344 VikLayerInterface *layer_interface;
346 if (!vw->action_group) return;
348 for (i=0; i<VIK_LAYER_NUM_TYPES; i++) {
350 layer_interface = vik_layer_get_interface(i);
351 tool_count = layer_interface->tools_count;
353 for (j = 0; j < tool_count; j++) {
354 action = gtk_action_group_get_action(vw->action_group,
355 layer_interface->tools[j].radioActionEntry.name);
356 g_object_set(action, "sensitive", i == vl->type, NULL);
361 static void window_finalize ( GObject *gob )
363 VikWindow *vw = VIK_WINDOW(gob);
364 g_return_if_fail ( vw != NULL );
366 a_background_remove_window ( vw );
368 window_list = g_slist_remove ( window_list, vw );
370 gdk_cursor_unref ( vw->busy_cursor );
372 G_OBJECT_CLASS(parent_class)->finalize(gob);
376 static void vik_window_class_init ( VikWindowClass *klass )
379 GObjectClass *object_class;
381 window_signals[VW_NEWWINDOW_SIGNAL] = g_signal_new ( "newwindow", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION, G_STRUCT_OFFSET (VikWindowClass, newwindow), NULL, NULL, g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0);
382 window_signals[VW_OPENWINDOW_SIGNAL] = g_signal_new ( "openwindow", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION, G_STRUCT_OFFSET (VikWindowClass, openwindow), NULL, NULL, g_cclosure_marshal_VOID__POINTER, G_TYPE_NONE, 1, G_TYPE_POINTER);
384 object_class = G_OBJECT_CLASS (klass);
386 object_class->finalize = window_finalize;
388 parent_class = g_type_class_peek_parent (klass);
392 static void zoom_changed (GtkMenuShell *menushell,
395 VikWindow *vw = VIK_WINDOW (user_data);
397 GtkWidget *aw = gtk_menu_get_active ( GTK_MENU (menushell) );
398 gint active = GPOINTER_TO_INT(g_object_get_data ( G_OBJECT (aw), "position" ));
400 gdouble zoom_request = pow (2, active-2 );
402 // But has it really changed?
403 gdouble current_zoom = vik_viewport_get_zoom ( vw->viking_vvp );
404 if ( current_zoom != 0.0 && zoom_request != current_zoom ) {
405 vik_viewport_set_zoom ( vw->viking_vvp, zoom_request );
406 // Force drawing update
412 * @mpp: The initial zoom level
414 static GtkWidget *create_zoom_menu_all_levels ( gdouble mpp )
416 GtkWidget *menu = gtk_menu_new ();
417 char *itemLabels[] = { "0.25", "0.5", "1", "2", "4", "8", "16", "32", "64", "128", "256", "512", "1024", "2048", "4096", "8192", "16384", "32768" };
420 for (i = 0 ; i < G_N_ELEMENTS(itemLabels) ; i++)
422 GtkWidget *item = gtk_menu_item_new_with_label (itemLabels[i]);
423 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
424 gtk_widget_show (item);
425 g_object_set_data (G_OBJECT (item), "position", GINT_TO_POINTER(i));
428 gint active = 2 + round ( log (mpp) / log (2) );
429 // Ensure value derived from mpp is in bounds of the menu
430 if ( active >= G_N_ELEMENTS(itemLabels) )
431 active = G_N_ELEMENTS(itemLabels) - 1;
434 gtk_menu_set_active ( GTK_MENU(menu), active );
439 static GtkWidget *create_zoom_combo_all_levels ()
441 GtkWidget *combo = vik_combo_box_text_new();
442 vik_combo_box_text_append ( combo, "0.25");
443 vik_combo_box_text_append ( combo, "0.5");
444 vik_combo_box_text_append ( combo, "1");
445 vik_combo_box_text_append ( combo, "2");
446 vik_combo_box_text_append ( combo, "4");
447 vik_combo_box_text_append ( combo, "8");
448 vik_combo_box_text_append ( combo, "16");
449 vik_combo_box_text_append ( combo, "32");
450 vik_combo_box_text_append ( combo, "64");
451 vik_combo_box_text_append ( combo, "128");
452 vik_combo_box_text_append ( combo, "256");
453 vik_combo_box_text_append ( combo, "512");
454 vik_combo_box_text_append ( combo, "1024");
455 vik_combo_box_text_append ( combo, "2048");
456 vik_combo_box_text_append ( combo, "4096");
457 vik_combo_box_text_append ( combo, "8192");
458 vik_combo_box_text_append ( combo, "16384");
459 vik_combo_box_text_append ( combo, "32768");
461 gtk_widget_set_tooltip_text (combo, _("Select zoom level"));
465 static gint zoom_popup_handler (GtkWidget *widget)
469 g_return_val_if_fail (widget != NULL, FALSE);
470 g_return_val_if_fail (GTK_IS_MENU (widget), FALSE);
472 /* The "widget" is the menu that was supplied when
473 * g_signal_connect_swapped() was called.
475 menu = GTK_MENU (widget);
477 gtk_menu_popup (menu, NULL, NULL, NULL, NULL,
478 1, gtk_get_current_event_time());
482 static void vik_window_init ( VikWindow *vw )
484 GtkWidget *main_vbox;
487 vw->action_group = NULL;
489 vw->viking_vvp = vik_viewport_new();
490 vw->viking_vlp = vik_layers_panel_new();
491 vik_layers_panel_set_viewport ( vw->viking_vlp, vw->viking_vvp );
492 vw->viking_vs = vik_statusbar_new();
494 vw->vt = toolbox_create(vw);
495 window_create_ui(vw);
496 window_set_filename (vw, NULL);
497 vw->toolbar = GTK_TOOLBAR(gtk_ui_manager_get_widget (vw->uim, "/MainToolbar"));
499 vw->busy_cursor = gdk_cursor_new ( GDK_WATCH );
501 // Set the default tool
502 gtk_action_activate ( gtk_action_group_get_action ( vw->action_group, "Pan" ) );
506 vw->modified = FALSE;
507 vw->only_updating_coord_mode_ui = FALSE;
509 vw->pan_move = FALSE;
510 vw->pan_x = vw->pan_y = -1;
511 vw->draw_image_width = DRAW_IMAGE_DEFAULT_WIDTH;
512 vw->draw_image_height = DRAW_IMAGE_DEFAULT_HEIGHT;
513 vw->draw_image_save_as_png = DRAW_IMAGE_DEFAULT_SAVE_AS_PNG;
515 main_vbox = gtk_vbox_new(FALSE, 1);
516 gtk_container_add (GTK_CONTAINER (vw), main_vbox);
518 gtk_box_pack_start (GTK_BOX(main_vbox), gtk_ui_manager_get_widget (vw->uim, "/MainMenu"), FALSE, TRUE, 0);
519 gtk_box_pack_start (GTK_BOX(main_vbox), GTK_WIDGET(vw->toolbar), FALSE, TRUE, 0);
520 gtk_toolbar_set_icon_size (vw->toolbar, GTK_ICON_SIZE_SMALL_TOOLBAR);
521 gtk_toolbar_set_style (vw->toolbar, GTK_TOOLBAR_ICONS);
523 vik_ext_tool_datasources_add_menu_items ( vw, vw->uim );
525 GtkWidget * zoom_levels = gtk_ui_manager_get_widget (vw->uim, "/MainMenu/View/SetZoom");
526 GtkWidget * zoom_levels_menu = create_zoom_menu_all_levels ( vik_viewport_get_zoom(vw->viking_vvp) );
527 gtk_menu_item_set_submenu (GTK_MENU_ITEM (zoom_levels), zoom_levels_menu);
528 g_signal_connect ( G_OBJECT(zoom_levels_menu), "selection-done", G_CALLBACK(zoom_changed), vw);
529 g_signal_connect_swapped ( G_OBJECT(vw->viking_vs), "clicked", G_CALLBACK(zoom_popup_handler), zoom_levels_menu );
531 g_signal_connect (G_OBJECT (vw), "delete_event", G_CALLBACK (delete_event), NULL);
533 g_signal_connect_swapped (G_OBJECT(vw->viking_vvp), "expose_event", G_CALLBACK(draw_sync), vw);
534 g_signal_connect_swapped (G_OBJECT(vw->viking_vvp), "configure_event", G_CALLBACK(window_configure_event), vw);
535 gtk_widget_add_events ( GTK_WIDGET(vw->viking_vvp), GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK | GDK_KEY_PRESS_MASK );
536 g_signal_connect_swapped (G_OBJECT(vw->viking_vvp), "scroll_event", G_CALLBACK(draw_scroll), vw);
537 g_signal_connect_swapped (G_OBJECT(vw->viking_vvp), "button_press_event", G_CALLBACK(draw_click), vw);
538 g_signal_connect_swapped (G_OBJECT(vw->viking_vvp), "button_release_event", G_CALLBACK(draw_release), vw);
539 g_signal_connect_swapped (G_OBJECT(vw->viking_vvp), "motion_notify_event", G_CALLBACK(draw_mouse_motion), vw);
540 g_signal_connect_swapped (G_OBJECT(vw->viking_vlp), "update", G_CALLBACK(draw_update), vw);
542 // Allow key presses to be processed anywhere
543 g_signal_connect_swapped (G_OBJECT (vw), "key_press_event", G_CALLBACK (key_press_event), vw);
545 gtk_window_set_default_size ( GTK_WINDOW(vw), VIKING_WINDOW_WIDTH, VIKING_WINDOW_HEIGHT);
547 hpaned = gtk_hpaned_new ();
548 gtk_paned_pack1 ( GTK_PANED(hpaned), GTK_WIDGET (vw->viking_vlp), FALSE, FALSE );
549 gtk_paned_pack2 ( GTK_PANED(hpaned), GTK_WIDGET (vw->viking_vvp), TRUE, TRUE );
551 /* This packs the button into the window (a gtk container). */
552 gtk_box_pack_start (GTK_BOX(main_vbox), hpaned, TRUE, TRUE, 0);
554 gtk_box_pack_end (GTK_BOX(main_vbox), GTK_WIDGET(vw->viking_vs), FALSE, TRUE, 0);
556 a_background_add_window ( vw );
558 window_list = g_slist_prepend ( window_list, vw);
562 vw->save_img_dia = NULL;
563 vw->save_img_dir_dia = NULL;
565 // Store the thread value so comparisons can be made to determine the gdk update method
566 // Hopefully we are storing the main thread value here :)
567 // [ATM any window initialization is always be performed by the main thread]
568 vw->thread = g_thread_self();
571 static VikWindow *window_new ()
573 return VIK_WINDOW ( g_object_new ( VIK_WINDOW_TYPE, NULL ) );
577 * Update the displayed map
578 * Only update the top most visible map layer
579 * ATM this assumes (as per defaults) the top most map has full alpha setting
580 * such that other other maps even though they may be active will not be seen
581 * It's more complicated to work out which maps are actually visible due to alpha settings
582 * and overkill for this simple refresh method.
584 static void simple_map_update ( VikWindow *vw, gboolean only_new )
586 // Find the most relevent single map layer to operate on
587 VikLayer *vl = vik_aggregate_layer_get_top_visible_layer_of_type (vik_layers_panel_get_top_layer(vw->viking_vlp), VIK_LAYER_MAPS);
589 vik_maps_layer_download ( VIK_MAPS_LAYER(vl), vw->viking_vvp, only_new );
593 * This is the global key press handler
594 * Global shortcuts are available at any time and hence are not restricted to when a certain tool is enabled
596 static gboolean key_press_event( VikWindow *vw, GdkEventKey *event, gpointer data )
598 // The keys handled here are not in the menuing system for a couple of reasons:
599 // . Keeps the menu size compact (alebit at expense of discoverably)
600 // . Allows differing key bindings to perform the same actions
602 // First decide if key events are related to the maps layer
603 gboolean map_download = FALSE;
604 gboolean map_download_only_new = TRUE; // Only new or reload
606 GdkModifierType modifiers = gtk_accelerator_get_default_mod_mask();
608 // Standard 'Refresh' keys: F5 or Ctrl+r
609 // Note 'F5' is actually handled via draw_refresh_cb() later on
610 // (not 'R' it's 'r' notice the case difference!!)
611 if ( event->keyval == GDK_r && (event->state & modifiers) == GDK_CONTROL_MASK ) {
613 map_download_only_new = TRUE;
615 // Full cache reload with Ctrl+F5 or Ctrl+Shift+r [This is not in the menu system]
616 // Note the use of uppercase R here since shift key has been pressed
617 else if ( (event->keyval == GDK_F5 && (event->state & modifiers) == GDK_CONTROL_MASK ) ||
618 ( event->keyval == GDK_R && (event->state & modifiers) == (GDK_CONTROL_MASK + GDK_SHIFT_MASK) ) ) {
620 map_download_only_new = FALSE;
623 if ( map_download ) {
624 simple_map_update ( vw, map_download_only_new );
627 VikLayer *vl = vik_layers_panel_get_selected ( vw->viking_vlp );
628 if (vl && vw->vt->active_tool != -1 && vw->vt->tools[vw->vt->active_tool].ti.key_press ) {
629 gint ltype = vw->vt->tools[vw->vt->active_tool].layer_type;
630 if ( vl && ltype == vl->type )
631 return vw->vt->tools[vw->vt->active_tool].ti.key_press(vl, event, vw->vt->tools[vw->vt->active_tool].state);
634 // Ensure called only on window tools (i.e. not on any of the Layer tools since the layer is NULL)
635 if ( vw->current_tool < TOOL_LAYER ) {
636 // No layer - but enable window tool keypress processing - these should be able to handle a NULL layer
637 if ( vw->vt->tools[vw->vt->active_tool].ti.key_press ) {
638 return vw->vt->tools[vw->vt->active_tool].ti.key_press ( vl, event, vw->vt->tools[vw->vt->active_tool].state );
642 /* Restore Main Menu via Escape key if the user has hidden it */
643 /* This key is more likely to be used as they may not remember the function key */
644 if ( event->keyval == GDK_Escape ) {
645 GtkWidget *check_box = gtk_ui_manager_get_widget ( vw->uim, "/ui/MainMenu/View/SetShow/ViewMainMenu" );
647 gboolean state = gtk_check_menu_item_get_active ( GTK_CHECK_MENU_ITEM(check_box) );
649 gtk_widget_show ( gtk_ui_manager_get_widget ( vw->uim, "/ui/MainMenu" ) );
650 gtk_check_menu_item_set_active ( GTK_CHECK_MENU_ITEM(check_box), TRUE );
651 return TRUE; /* handled keypress */
656 return FALSE; /* don't handle the keypress */
659 static gboolean delete_event( VikWindow *vw )
661 #ifdef VIKING_PROMPT_IF_MODIFIED
668 dia = GTK_DIALOG ( gtk_message_dialog_new ( GTK_WINDOW(vw), GTK_DIALOG_DESTROY_WITH_PARENT, GTK_MESSAGE_QUESTION, GTK_BUTTONS_NONE,
669 _("Do you want to save the changes you made to the document \"%s\"?\n"
671 "Your changes will be lost if you don't save them."),
672 window_get_filename ( vw ) ) );
673 gtk_dialog_add_buttons ( dia, _("Don't Save"), GTK_RESPONSE_NO, GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, GTK_STOCK_SAVE, GTK_RESPONSE_YES, NULL );
674 switch ( gtk_dialog_run ( dia ) )
676 case GTK_RESPONSE_NO: gtk_widget_destroy ( GTK_WIDGET(dia) ); return FALSE;
677 case GTK_RESPONSE_CANCEL: gtk_widget_destroy ( GTK_WIDGET(dia) ); return TRUE;
678 default: gtk_widget_destroy ( GTK_WIDGET(dia) ); return ! save_file(NULL, vw);
685 static void newwindow_cb ( GtkAction *a, VikWindow *vw )
687 g_signal_emit ( G_OBJECT(vw), window_signals[VW_NEWWINDOW_SIGNAL], 0 );
690 static void draw_update ( VikWindow *vw )
696 static void draw_sync ( VikWindow *vw )
698 vik_viewport_sync(vw->viking_vvp);
703 * Split the status update, as sometimes only need to update the tool part
704 * also on initialization the zoom related stuff is not ready to be used
706 static void draw_status_tool ( VikWindow *vw )
708 if ( vw->current_tool == TOOL_LAYER )
709 // Use tooltip rather than the internal name as the tooltip is i8n
710 vik_statusbar_set_message ( vw->viking_vs, VIK_STATUSBAR_TOOL, vik_layer_get_interface(vw->tool_layer_id)->tools[vw->tool_tool_id].radioActionEntry.tooltip );
712 vik_statusbar_set_message ( vw->viking_vs, VIK_STATUSBAR_TOOL, _(tool_names[vw->current_tool]) );
715 static void draw_status ( VikWindow *vw )
717 static gchar zoom_level[22];
718 gdouble xmpp = vik_viewport_get_xmpp (vw->viking_vvp);
719 gdouble ympp = vik_viewport_get_ympp(vw->viking_vvp);
720 gchar *unit = vik_viewport_get_coord_mode(vw->viking_vvp) == VIK_COORD_UTM ? _("mpp") : _("pixelfact");
722 g_snprintf ( zoom_level, 22, "%.3f/%.3f %s", xmpp, ympp, unit );
724 if ( (int)xmpp - xmpp < 0.0 )
725 g_snprintf ( zoom_level, 22, "%.3f %s", xmpp, unit );
727 /* xmpp should be a whole number so don't show useless .000 bit */
728 g_snprintf ( zoom_level, 22, "%d %s", (int)xmpp, unit );
730 vik_statusbar_set_message ( vw->viking_vs, VIK_STATUSBAR_ZOOM, zoom_level );
732 draw_status_tool ( vw );
735 void vik_window_set_redraw_trigger(VikLayer *vl)
737 VikWindow *vw = VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vl));
742 static void window_configure_event ( VikWindow *vw )
744 static int first = 1;
747 // This is a hack to set the cursor corresponding to the first tool
748 // FIXME find the correct way to initialize both tool and its cursor
750 vw->viewport_cursor = (GdkCursor *)toolbox_get_cursor(vw->vt, "Pan");
751 /* We set cursor, even if it is NULL: it resets to default */
752 gdk_window_set_cursor ( gtk_widget_get_window(GTK_WIDGET(vw->viking_vvp)), vw->viewport_cursor );
756 static void draw_redraw ( VikWindow *vw )
758 VikCoord old_center = vw->trigger_center;
759 vw->trigger_center = *(vik_viewport_get_center(vw->viking_vvp));
760 VikLayer *new_trigger = vw->trigger;
762 VikLayer *old_trigger = VIK_LAYER(vik_viewport_get_trigger(vw->viking_vvp));
765 ; /* do nothing -- have to redraw everything. */
766 else if ( (old_trigger != new_trigger) || !vik_coord_equals(&old_center, &vw->trigger_center) || (new_trigger->type == VIK_LAYER_AGGREGATE) )
767 vik_viewport_set_trigger ( vw->viking_vvp, new_trigger ); /* todo: set to half_drawn mode if new trigger is above old */
769 vik_viewport_set_half_drawn ( vw->viking_vvp, TRUE );
772 vik_viewport_clear ( vw->viking_vvp);
773 vik_layers_panel_draw_all ( vw->viking_vlp );
774 vik_viewport_draw_scale ( vw->viking_vvp );
775 vik_viewport_draw_copyright ( vw->viking_vvp );
776 vik_viewport_draw_centermark ( vw->viking_vvp );
777 vik_viewport_draw_logo ( vw->viking_vvp );
779 vik_viewport_set_half_drawn ( vw->viking_vvp, FALSE ); /* just in case. */
782 gboolean draw_buf_done = TRUE;
784 static gboolean draw_buf(gpointer data)
786 gpointer *pass_along = data;
788 gdk_draw_drawable (pass_along[0], pass_along[1],
789 pass_along[2], 0, 0, 0, 0, -1, -1);
790 draw_buf_done = TRUE;
796 /* Mouse event handlers ************************************************************************/
798 static void vik_window_pan_click (VikWindow *vw, GdkEventButton *event)
800 /* set panning origin */
801 vw->pan_move = FALSE;
802 vw->pan_x = (gint) event->x;
803 vw->pan_y = (gint) event->y;
806 static void draw_click (VikWindow *vw, GdkEventButton *event)
808 gtk_widget_grab_focus ( GTK_WIDGET(vw->viking_vvp) );
810 /* middle button pressed. we reserve all middle button and scroll events
811 * for panning and zooming; tools only get left/right/movement
813 if ( event->button == 2) {
814 if ( vw->vt->tools[vw->vt->active_tool].ti.pan_handler )
815 // Tool still may need to do something (such as disable something)
816 toolbox_click(vw->vt, event);
817 vik_window_pan_click ( vw, event );
820 toolbox_click(vw->vt, event);
824 static void vik_window_pan_move (VikWindow *vw, GdkEventMotion *event)
826 if ( vw->pan_x != -1 ) {
827 vik_viewport_set_center_screen ( vw->viking_vvp, vik_viewport_get_width(vw->viking_vvp)/2 - event->x + vw->pan_x,
828 vik_viewport_get_height(vw->viking_vvp)/2 - event->y + vw->pan_y );
830 vw->pan_x = event->x;
831 vw->pan_y = event->y;
836 static void draw_mouse_motion (VikWindow *vw, GdkEventMotion *event)
838 static VikCoord coord;
839 static struct UTM utm;
840 static struct LatLon ll;
841 #define BUFFER_SIZE 50
842 static char pointer_buf[BUFFER_SIZE];
843 gchar *lat = NULL, *lon = NULL;
846 VikDemInterpol interpol_method;
848 /* This is a hack, but work far the best, at least for single pointer systems.
849 * See http://bugzilla.gnome.org/show_bug.cgi?id=587714 for more. */
851 gdk_window_get_pointer (event->window, &x, &y, NULL);
855 toolbox_move(vw->vt, event);
857 vik_viewport_screen_to_coord ( vw->viking_vvp, event->x, event->y, &coord );
858 vik_coord_to_utm ( &coord, &utm );
860 if ( vik_viewport_get_drawmode ( vw->viking_vvp ) == VIK_VIEWPORT_DRAWMODE_UTM ) {
861 // Reuse lat for the first part (Zone + N or S, and lon for the second part (easting and northing) of a UTM format:
862 // ZONE[N|S] EASTING NORTHING
863 lat = g_malloc(4*sizeof(gchar));
864 // NB zone is stored in a char but is an actual number
865 g_snprintf (lat, 4, "%d%c", utm.zone, utm.letter);
866 lon = g_malloc(16*sizeof(gchar));
867 g_snprintf (lon, 16, "%d %d", (gint)utm.easting, (gint)utm.northing);
870 a_coords_utm_to_latlon ( &utm, &ll );
871 a_coords_latlon_to_string ( &ll, &lat, &lon );
874 /* Change interpolate method according to scale */
875 zoom = vik_viewport_get_zoom(vw->viking_vvp);
877 interpol_method = VIK_DEM_INTERPOL_NONE;
878 else if (zoom >= 1.0)
879 interpol_method = VIK_DEM_INTERPOL_SIMPLE;
881 interpol_method = VIK_DEM_INTERPOL_BEST;
882 if ((alt = a_dems_get_elev_by_coord(&coord, interpol_method)) != VIK_DEM_INVALID_ELEVATION) {
883 if ( a_vik_get_units_height () == VIK_UNITS_HEIGHT_METRES )
884 g_snprintf ( pointer_buf, BUFFER_SIZE, _("%s %s %dm"), lat, lon, alt );
886 g_snprintf ( pointer_buf, BUFFER_SIZE, _("%s %s %dft"), lat, lon, (int)VIK_METERS_TO_FEET(alt) );
889 g_snprintf ( pointer_buf, BUFFER_SIZE, _("%s %s"), lat, lon );
894 vik_statusbar_set_message ( vw->viking_vs, VIK_STATUSBAR_POSITION, pointer_buf );
896 vik_window_pan_move ( vw, event );
898 /* This is recommended by the GTK+ documentation, but does not work properly.
899 * Use deprecated way until GTK+ gets a solution for correct motion hint handling:
900 * http://bugzilla.gnome.org/show_bug.cgi?id=587714
902 /* gdk_event_request_motions ( event ); */
905 static void vik_window_pan_release ( VikWindow *vw, GdkEventButton *event )
907 if ( vw->pan_move == FALSE )
908 vik_viewport_set_center_screen ( vw->viking_vvp, vw->pan_x, vw->pan_y );
910 vik_viewport_set_center_screen ( vw->viking_vvp, vik_viewport_get_width(vw->viking_vvp)/2 - event->x + vw->pan_x,
911 vik_viewport_get_height(vw->viking_vvp)/2 - event->y + vw->pan_y );
912 vw->pan_move = FALSE;
913 vw->pan_x = vw->pan_y = -1;
917 static void draw_release ( VikWindow *vw, GdkEventButton *event )
919 gtk_widget_grab_focus ( GTK_WIDGET(vw->viking_vvp) );
921 if ( event->button == 2 ) { /* move / pan */
922 if ( vw->vt->tools[vw->vt->active_tool].ti.pan_handler )
923 // Tool still may need to do something (such as reenable something)
924 toolbox_release(vw->vt, event);
925 vik_window_pan_release ( vw, event );
928 toolbox_release(vw->vt, event);
932 static void draw_scroll (VikWindow *vw, GdkEventScroll *event)
934 guint modifiers = event->state & (GDK_SHIFT_MASK | GDK_CONTROL_MASK);
935 if ( modifiers == GDK_CONTROL_MASK ) {
936 /* control == pan up & down */
937 if ( event->direction == GDK_SCROLL_UP )
938 vik_viewport_set_center_screen ( vw->viking_vvp, vik_viewport_get_width(vw->viking_vvp)/2, vik_viewport_get_height(vw->viking_vvp)/3 );
940 vik_viewport_set_center_screen ( vw->viking_vvp, vik_viewport_get_width(vw->viking_vvp)/2, vik_viewport_get_height(vw->viking_vvp)*2/3 );
941 } else if ( modifiers == GDK_SHIFT_MASK ) {
942 /* shift == pan left & right */
943 if ( event->direction == GDK_SCROLL_UP )
944 vik_viewport_set_center_screen ( vw->viking_vvp, vik_viewport_get_width(vw->viking_vvp)/3, vik_viewport_get_height(vw->viking_vvp)/2 );
946 vik_viewport_set_center_screen ( vw->viking_vvp, vik_viewport_get_width(vw->viking_vvp)*2/3, vik_viewport_get_height(vw->viking_vvp)/2 );
947 } else if ( modifiers == (GDK_CONTROL_MASK | GDK_SHIFT_MASK) ) {
948 // This zoom is on the center position
949 if ( event->direction == GDK_SCROLL_UP )
950 vik_viewport_zoom_in (vw->viking_vvp);
952 vik_viewport_zoom_out (vw->viking_vvp);
954 /* make sure mouse is still over the same point on the map when we zoom */
957 gint center_x = vik_viewport_get_width ( vw->viking_vvp ) / 2;
958 gint center_y = vik_viewport_get_height ( vw->viking_vvp ) / 2;
959 vik_viewport_screen_to_coord ( vw->viking_vvp, event->x, event->y, &coord );
960 if ( event->direction == GDK_SCROLL_UP )
961 vik_viewport_zoom_in (vw->viking_vvp);
963 vik_viewport_zoom_out(vw->viking_vvp);
964 vik_viewport_coord_to_screen ( vw->viking_vvp, &coord, &x, &y );
965 vik_viewport_set_center_screen ( vw->viking_vvp, center_x + (x - event->x),
966 center_y + (y - event->y) );
974 /********************************************************************************
976 ********************************************************************************/
977 static void draw_ruler(VikViewport *vvp, GdkDrawable *d, GdkGC *gc, gint x1, gint y1, gint x2, gint y2, gdouble distance)
981 GdkGC *labgc = vik_viewport_new_gc ( vvp, "#cccccc", 1);
982 GdkGC *thickgc = gdk_gc_new(d);
984 gdouble len = sqrt((x1-x2)*(x1-x2) + (y1-y2)*(y1-y2));
985 gdouble dx = (x2-x1)/len*10;
986 gdouble dy = (y2-y1)/len*10;
987 gdouble c = cos(DEG2RAD(15.0));
988 gdouble s = sin(DEG2RAD(15.0));
990 gdouble baseangle = 0;
993 /* draw line with arrow ends */
995 gint tmp_x1=x1, tmp_y1=y1, tmp_x2=x2, tmp_y2=y2;
996 a_viewport_clip_line(&tmp_x1, &tmp_y1, &tmp_x2, &tmp_y2);
997 gdk_draw_line(d, gc, tmp_x1, tmp_y1, tmp_x2, tmp_y2);
1000 a_viewport_clip_line(&x1, &y1, &x2, &y2);
1001 gdk_draw_line(d, gc, x1, y1, x2, y2);
1003 gdk_draw_line(d, gc, x1 - dy, y1 + dx, x1 + dy, y1 - dx);
1004 gdk_draw_line(d, gc, x2 - dy, y2 + dx, x2 + dy, y2 - dx);
1005 gdk_draw_line(d, gc, x2, y2, x2 - (dx * c + dy * s), y2 - (dy * c - dx * s));
1006 gdk_draw_line(d, gc, x2, y2, x2 - (dx * c - dy * s), y2 - (dy * c + dx * s));
1007 gdk_draw_line(d, gc, x1, y1, x1 + (dx * c + dy * s), y1 + (dy * c - dx * s));
1008 gdk_draw_line(d, gc, x1, y1, x1 + (dx * c - dy * s), y1 + (dy * c + dx * s));
1014 vik_viewport_compute_bearing ( vvp, x1, y1, x2, y2, &angle, &baseangle );
1018 gdk_gc_copy(thickgc, gc);
1019 gdk_gc_set_line_attributes(thickgc, CW, GDK_LINE_SOLID, GDK_CAP_BUTT, GDK_JOIN_MITER);
1020 gdk_color_parse("#2255cc", &color);
1021 gdk_gc_set_rgb_fg_color(thickgc, &color);
1023 gdk_draw_arc (d, thickgc, FALSE, x1-CR+CW/2, y1-CR+CW/2, 2*CR-CW, 2*CR-CW, (90 - RAD2DEG(baseangle))*64, -RAD2DEG(angle)*64);
1026 gdk_gc_copy(thickgc, gc);
1027 gdk_gc_set_line_attributes(thickgc, 2, GDK_LINE_SOLID, GDK_CAP_BUTT, GDK_JOIN_MITER);
1028 for (i=0; i<180; i++) {
1029 c = cos(DEG2RAD(i)*2 + baseangle);
1030 s = sin(DEG2RAD(i)*2 + baseangle);
1033 gdk_draw_line (d, gc, x1 + CR*c, y1 + CR*s, x1 + (CR+CW)*c, y1 + (CR+CW)*s);
1035 gdouble ticksize = 2*CW;
1036 gdk_draw_line (d, thickgc, x1 + (CR-CW)*c, y1 + (CR-CW)*s, x1 + (CR+ticksize)*c, y1 + (CR+ticksize)*s);
1040 gdk_draw_arc (d, gc, FALSE, x1-CR, y1-CR, 2*CR, 2*CR, 0, 64*360);
1041 gdk_draw_arc (d, gc, FALSE, x1-CR-CW, y1-CR-CW, 2*(CR+CW), 2*(CR+CW), 0, 64*360);
1042 gdk_draw_arc (d, gc, FALSE, x1-CR+CW, y1-CR+CW, 2*(CR-CW), 2*(CR-CW), 0, 64*360);
1043 c = (CR+CW*2)*cos(baseangle);
1044 s = (CR+CW*2)*sin(baseangle);
1045 gdk_draw_line (d, gc, x1-c, y1-s, x1+c, y1+s);
1046 gdk_draw_line (d, gc, x1+s, y1-c, x1-s, y1+c);
1049 #define LABEL(x, y, w, h) { \
1050 gdk_draw_rectangle(d, labgc, TRUE, (x)-2, (y)-1, (w)+4, (h)+1); \
1051 gdk_draw_rectangle(d, gc, FALSE, (x)-2, (y)-1, (w)+4, (h)+1); \
1052 gdk_draw_layout(d, gc, (x), (y), pl); }
1054 gint wd, hd, xd, yd;
1055 gint wb, hb, xb, yb;
1057 pl = gtk_widget_create_pango_layout (GTK_WIDGET(vvp), NULL);
1058 pango_layout_set_font_description (pl, gtk_widget_get_style(GTK_WIDGET(vvp))->font_desc);
1059 pango_layout_set_text(pl, "N", -1);
1060 gdk_draw_layout(d, gc, x1-5, y1-CR-3*CW-8, pl);
1062 /* draw label with distance */
1063 vik_units_distance_t dist_units = a_vik_get_units_distance ();
1064 switch (dist_units) {
1065 case VIK_UNITS_DISTANCE_KILOMETRES:
1066 if (distance >= 1000 && distance < 100000) {
1067 g_sprintf(str, "%3.2f km", distance/1000.0);
1068 } else if (distance < 1000) {
1069 g_sprintf(str, "%d m", (int)distance);
1071 g_sprintf(str, "%d km", (int)distance/1000);
1074 case VIK_UNITS_DISTANCE_MILES:
1075 if (distance >= VIK_MILES_TO_METERS(1) && distance < VIK_MILES_TO_METERS(100)) {
1076 g_sprintf(str, "%3.2f miles", VIK_METERS_TO_MILES(distance));
1077 } else if (distance < VIK_MILES_TO_METERS(1)) {
1078 g_sprintf(str, "%d yards", (int)(distance*1.0936133));
1080 g_sprintf(str, "%d miles", (int)VIK_METERS_TO_MILES(distance));
1084 g_critical("Houston, we've had a problem. distance=%d", dist_units);
1087 pango_layout_set_text(pl, str, -1);
1089 pango_layout_get_pixel_size ( pl, &wd, &hd );
1091 xd = (x1+x2)/2 + dy;
1092 yd = (y1+y2)/2 - hd/2 - dx;
1094 xd = (x1+x2)/2 - dy;
1095 yd = (y1+y2)/2 - hd/2 + dx;
1098 if ( xd < -5 || yd < -5 || xd > vik_viewport_get_width(vvp)+5 || yd > vik_viewport_get_height(vvp)+5 ) {
1103 LABEL(xd, yd, wd, hd);
1105 /* draw label with bearing */
1106 g_sprintf(str, "%3.1f°", RAD2DEG(angle));
1107 pango_layout_set_text(pl, str, -1);
1108 pango_layout_get_pixel_size ( pl, &wb, &hb );
1109 xb = x1 + CR*cos(angle-M_PI_2);
1110 yb = y1 + CR*sin(angle-M_PI_2);
1112 if ( xb < -5 || yb < -5 || xb > vik_viewport_get_width(vvp)+5 || yb > vik_viewport_get_height(vvp)+5 ) {
1118 GdkRectangle r1 = {xd-2, yd-1, wd+4, hd+1}, r2 = {xb-2, yb-1, wb+4, hb+1};
1119 if (gdk_rectangle_intersect(&r1, &r2, &r2)) {
1123 LABEL(xb, yb, wb, hb);
1127 g_object_unref ( G_OBJECT ( pl ) );
1128 g_object_unref ( G_OBJECT ( labgc ) );
1129 g_object_unref ( G_OBJECT ( thickgc ) );
1135 gboolean has_oldcoord;
1137 } ruler_tool_state_t;
1139 static gpointer ruler_create (VikWindow *vw, VikViewport *vvp)
1141 ruler_tool_state_t *s = g_new(ruler_tool_state_t, 1);
1144 s->has_oldcoord = FALSE;
1148 static void ruler_destroy (ruler_tool_state_t *s)
1153 static VikLayerToolFuncStatus ruler_click (VikLayer *vl, GdkEventButton *event, ruler_tool_state_t *s)
1158 if ( event->button == 1 ) {
1159 gchar *lat=NULL, *lon=NULL;
1160 vik_viewport_screen_to_coord ( s->vvp, (gint) event->x, (gint) event->y, &coord );
1161 vik_coord_to_latlon ( &coord, &ll );
1162 a_coords_latlon_to_string ( &ll, &lat, &lon );
1163 if ( s->has_oldcoord ) {
1164 vik_units_distance_t dist_units = a_vik_get_units_distance ();
1165 switch (dist_units) {
1166 case VIK_UNITS_DISTANCE_KILOMETRES:
1167 temp = g_strdup_printf ( "%s %s DIFF %f meters", lat, lon, vik_coord_diff( &coord, &(s->oldcoord) ) );
1169 case VIK_UNITS_DISTANCE_MILES:
1170 temp = g_strdup_printf ( "%s %s DIFF %f miles", lat, lon, VIK_METERS_TO_MILES(vik_coord_diff( &coord, &(s->oldcoord) )) );
1173 temp = g_strdup_printf ("Just to keep the compiler happy");
1174 g_critical("Houston, we've had a problem. distance=%d", dist_units);
1177 s->has_oldcoord = FALSE;
1180 temp = g_strdup_printf ( "%s %s", lat, lon );
1181 s->has_oldcoord = TRUE;
1184 vik_statusbar_set_message ( s->vw->viking_vs, VIK_STATUSBAR_INFO, temp );
1187 s->oldcoord = coord;
1190 vik_viewport_set_center_screen ( s->vvp, (gint) event->x, (gint) event->y );
1191 draw_update ( s->vw );
1193 return VIK_LAYER_TOOL_ACK;
1196 static VikLayerToolFuncStatus ruler_move (VikLayer *vl, GdkEventMotion *event, ruler_tool_state_t *s)
1198 VikViewport *vvp = s->vvp;
1199 VikWindow *vw = s->vw;
1205 if ( s->has_oldcoord ) {
1206 int oldx, oldy, w1, h1, w2, h2;
1207 static GdkPixmap *buf = NULL;
1208 gchar *lat=NULL, *lon=NULL;
1209 w1 = vik_viewport_get_width(vvp);
1210 h1 = vik_viewport_get_height(vvp);
1212 buf = gdk_pixmap_new ( gtk_widget_get_window(GTK_WIDGET(vvp)), w1, h1, -1 );
1214 gdk_drawable_get_size(buf, &w2, &h2);
1215 if (w1 != w2 || h1 != h2) {
1216 g_object_unref ( G_OBJECT ( buf ) );
1217 buf = gdk_pixmap_new ( gtk_widget_get_window(GTK_WIDGET(vvp)), w1, h1, -1 );
1220 vik_viewport_screen_to_coord ( vvp, (gint) event->x, (gint) event->y, &coord );
1221 vik_coord_to_latlon ( &coord, &ll );
1222 vik_viewport_coord_to_screen ( vvp, &s->oldcoord, &oldx, &oldy );
1224 gdk_draw_drawable (buf, gtk_widget_get_style(GTK_WIDGET(vvp))->black_gc,
1225 vik_viewport_get_pixmap(vvp), 0, 0, 0, 0, -1, -1);
1226 draw_ruler(vvp, buf, gtk_widget_get_style(GTK_WIDGET(vvp))->black_gc, oldx, oldy, event->x, event->y, vik_coord_diff( &coord, &(s->oldcoord)) );
1227 if (draw_buf_done) {
1228 static gpointer pass_along[3];
1229 pass_along[0] = gtk_widget_get_window(GTK_WIDGET(vvp));
1230 pass_along[1] = gtk_widget_get_style(GTK_WIDGET(vvp))->black_gc;
1231 pass_along[2] = buf;
1232 g_idle_add_full (G_PRIORITY_HIGH_IDLE + 10, draw_buf, pass_along, NULL);
1233 draw_buf_done = FALSE;
1235 a_coords_latlon_to_string(&ll, &lat, &lon);
1236 vik_units_distance_t dist_units = a_vik_get_units_distance ();
1237 switch (dist_units) {
1238 case VIK_UNITS_DISTANCE_KILOMETRES:
1239 temp = g_strdup_printf ( "%s %s DIFF %f meters", lat, lon, vik_coord_diff( &coord, &(s->oldcoord) ) );
1241 case VIK_UNITS_DISTANCE_MILES:
1242 temp = g_strdup_printf ( "%s %s DIFF %f miles", lat, lon, VIK_METERS_TO_MILES (vik_coord_diff( &coord, &(s->oldcoord) )) );
1245 temp = g_strdup_printf ("Just to keep the compiler happy");
1246 g_critical("Houston, we've had a problem. distance=%d", dist_units);
1248 vik_statusbar_set_message ( vw->viking_vs, VIK_STATUSBAR_INFO, temp );
1251 return VIK_LAYER_TOOL_ACK;
1254 static VikLayerToolFuncStatus ruler_release (VikLayer *vl, GdkEventButton *event, ruler_tool_state_t *s)
1256 return VIK_LAYER_TOOL_ACK;
1259 static void ruler_deactivate (VikLayer *vl, ruler_tool_state_t *s)
1261 draw_update ( s->vw );
1264 static gboolean ruler_key_press (VikLayer *vl, GdkEventKey *event, ruler_tool_state_t *s)
1266 if (event->keyval == GDK_Escape) {
1267 s->has_oldcoord = FALSE;
1268 ruler_deactivate ( vl, s );
1271 // Regardless of whether we used it, return false so other GTK things may use it
1275 static VikToolInterface ruler_tool =
1276 // NB Ctrl+Shift+R is used for Refresh (deemed more important), so use 'U' instead
1277 { { "Ruler", "vik-icon-ruler", N_("_Ruler"), "<control><shift>U", N_("Ruler Tool"), 2 },
1278 (VikToolConstructorFunc) ruler_create,
1279 (VikToolDestructorFunc) ruler_destroy,
1280 (VikToolActivationFunc) NULL,
1281 (VikToolActivationFunc) ruler_deactivate,
1282 (VikToolMouseFunc) ruler_click,
1283 (VikToolMouseMoveFunc) ruler_move,
1284 (VikToolMouseFunc) ruler_release,
1285 (VikToolKeyFunc) ruler_key_press,
1287 GDK_CURSOR_IS_PIXMAP,
1288 &cursor_ruler_pixbuf };
1289 /*** end ruler code ********************************************************/
1293 /********************************************************************************
1295 ********************************************************************************/
1300 // Track zoom bounds for zoom tool with shift modifier:
1301 gboolean bounds_active;
1304 } zoom_tool_state_t;
1307 * In case the screen size has changed
1309 static void zoomtool_resize_pixmap (zoom_tool_state_t *zts)
1313 // Allocate a drawing area the size of the viewport
1314 w1 = vik_viewport_get_width ( zts->vw->viking_vvp );
1315 h1 = vik_viewport_get_height ( zts->vw->viking_vvp );
1317 if ( !zts->pixmap ) {
1319 zts->pixmap = gdk_pixmap_new ( gtk_widget_get_window(GTK_WIDGET(zts->vw->viking_vvp)), w1, h1, -1 );
1322 gdk_drawable_get_size ( zts->pixmap, &w2, &h2 );
1324 if ( w1 != w2 || h1 != h2 ) {
1325 // Has changed - delete and recreate with new values
1326 g_object_unref ( G_OBJECT ( zts->pixmap ) );
1327 zts->pixmap = gdk_pixmap_new ( gtk_widget_get_window(GTK_WIDGET(zts->vw->viking_vvp)), w1, h1, -1 );
1331 static gpointer zoomtool_create (VikWindow *vw, VikViewport *vvp)
1333 zoom_tool_state_t *zts = g_new(zoom_tool_state_t, 1);
1338 zts->bounds_active = FALSE;
1342 static void zoomtool_destroy ( zoom_tool_state_t *zts)
1345 g_object_unref ( G_OBJECT ( zts->pixmap ) );
1349 static VikLayerToolFuncStatus zoomtool_click (VikLayer *vl, GdkEventButton *event, zoom_tool_state_t *zts)
1351 zts->vw->modified = TRUE;
1352 guint modifiers = event->state & (GDK_SHIFT_MASK | GDK_CONTROL_MASK);
1356 gint center_x = vik_viewport_get_width ( zts->vw->viking_vvp ) / 2;
1357 gint center_y = vik_viewport_get_height ( zts->vw->viking_vvp ) / 2;
1359 gboolean skip_update = FALSE;
1361 zts->bounds_active = FALSE;
1363 if ( modifiers == (GDK_CONTROL_MASK | GDK_SHIFT_MASK) ) {
1364 // This zoom is on the center position
1365 vik_viewport_set_center_screen ( zts->vw->viking_vvp, center_x, center_y );
1366 if ( event->button == 1 )
1367 vik_viewport_zoom_in (zts->vw->viking_vvp);
1368 else if ( event->button == 3 )
1369 vik_viewport_zoom_out (zts->vw->viking_vvp);
1371 else if ( modifiers == GDK_CONTROL_MASK ) {
1372 // This zoom is to recenter on the mouse position
1373 vik_viewport_set_center_screen ( zts->vw->viking_vvp, (gint) event->x, (gint) event->y );
1374 if ( event->button == 1 )
1375 vik_viewport_zoom_in (zts->vw->viking_vvp);
1376 else if ( event->button == 3 )
1377 vik_viewport_zoom_out (zts->vw->viking_vvp);
1379 else if ( modifiers == GDK_SHIFT_MASK ) {
1380 // Get start of new zoom bounds
1381 if ( event->button == 1 ) {
1382 zts->bounds_active = TRUE;
1383 zts->start_x = (gint) event->x;
1384 zts->start_y = (gint) event->y;
1389 /* make sure mouse is still over the same point on the map when we zoom */
1390 vik_viewport_screen_to_coord ( zts->vw->viking_vvp, event->x, event->y, &coord );
1391 if ( event->button == 1 )
1392 vik_viewport_zoom_in (zts->vw->viking_vvp);
1393 else if ( event->button == 3 )
1394 vik_viewport_zoom_out(zts->vw->viking_vvp);
1395 vik_viewport_coord_to_screen ( zts->vw->viking_vvp, &coord, &x, &y );
1396 vik_viewport_set_center_screen ( zts->vw->viking_vvp,
1397 center_x + (x - event->x),
1398 center_y + (y - event->y) );
1402 draw_update ( zts->vw );
1404 return VIK_LAYER_TOOL_ACK;
1407 static VikLayerToolFuncStatus zoomtool_move (VikLayer *vl, GdkEventMotion *event, zoom_tool_state_t *zts)
1409 guint modifiers = event->state & (GDK_SHIFT_MASK | GDK_CONTROL_MASK);
1411 if ( zts->bounds_active && modifiers == GDK_SHIFT_MASK ) {
1412 zoomtool_resize_pixmap ( zts );
1414 // Blank out currently drawn area
1415 gdk_draw_drawable ( zts->pixmap,
1416 gtk_widget_get_style(GTK_WIDGET(zts->vw->viking_vvp))->black_gc,
1417 vik_viewport_get_pixmap(zts->vw->viking_vvp),
1418 0, 0, 0, 0, -1, -1);
1420 // Calculate new box starting point & size in pixels
1421 int xx, yy, width, height;
1422 if ( event->y > zts->start_y ) {
1424 height = event->y-zts->start_y;
1428 height = zts->start_y-event->y;
1430 if ( event->x > zts->start_x ) {
1432 width = event->x-zts->start_x;
1436 width = zts->start_x-event->x;
1440 gdk_draw_rectangle (zts->pixmap, gtk_widget_get_style(GTK_WIDGET(zts->vw->viking_vvp))->black_gc, FALSE, xx, yy, width, height);
1442 // Only actually draw when there's time to do so
1443 if (draw_buf_done) {
1444 static gpointer pass_along[3];
1445 pass_along[0] = gtk_widget_get_window(GTK_WIDGET(zts->vw->viking_vvp));
1446 pass_along[1] = gtk_widget_get_style(GTK_WIDGET(zts->vw->viking_vvp))->black_gc;
1447 pass_along[2] = zts->pixmap;
1448 g_idle_add_full (G_PRIORITY_HIGH_IDLE + 10, draw_buf, pass_along, NULL);
1449 draw_buf_done = FALSE;
1452 return VIK_LAYER_TOOL_ACK;
1455 static VikLayerToolFuncStatus zoomtool_release (VikLayer *vl, GdkEventButton *event, zoom_tool_state_t *zts)
1457 guint modifiers = event->state & (GDK_SHIFT_MASK | GDK_CONTROL_MASK);
1459 zts->bounds_active = FALSE;
1461 // Ensure haven't just released on the exact same position
1462 // i.e. probably haven't moved the mouse at all
1463 if ( modifiers == GDK_SHIFT_MASK && !( ( event->x == zts->start_x ) && ( event->y == zts->start_y )) ) {
1465 VikCoord coord1, coord2;
1466 vik_viewport_screen_to_coord ( zts->vw->viking_vvp, zts->start_x, zts->start_y, &coord1);
1467 vik_viewport_screen_to_coord ( zts->vw->viking_vvp, event->x, event->y, &coord2);
1469 // From the extend of the bounds pick the best zoom level
1470 // c.f. trw_layer_zoom_to_show_latlons()
1471 // Maybe refactor...
1472 struct LatLon ll1, ll2;
1473 vik_coord_to_latlon(&coord1, &ll1);
1474 vik_coord_to_latlon(&coord2, &ll2);
1475 struct LatLon average = { (ll1.lat+ll2.lat)/2,
1476 (ll1.lon+ll2.lon)/2 };
1478 VikCoord new_center;
1479 vik_coord_load_from_latlon ( &new_center, vik_viewport_get_coord_mode ( zts->vw->viking_vvp ), &average );
1480 vik_viewport_set_center_coord ( zts->vw->viking_vvp, &new_center );
1482 /* Convert into definite 'smallest' and 'largest' positions */
1483 struct LatLon minmin;
1484 if ( ll1.lat < ll2.lat )
1485 minmin.lat = ll1.lat;
1487 minmin.lat = ll2.lat;
1489 struct LatLon maxmax;
1490 if ( ll1.lon > ll2.lon )
1491 maxmax.lon = ll1.lon;
1493 maxmax.lon = ll2.lon;
1495 /* Always recalculate the 'best' zoom level */
1496 gdouble zoom = VIK_VIEWPORT_MIN_ZOOM;
1497 vik_viewport_set_zoom ( zts->vw->viking_vvp, zoom );
1499 gdouble min_lat, max_lat, min_lon, max_lon;
1500 /* Should only be a maximum of about 18 iterations from min to max zoom levels */
1501 while ( zoom <= VIK_VIEWPORT_MAX_ZOOM ) {
1502 vik_viewport_get_min_max_lat_lon ( zts->vw->viking_vvp, &min_lat, &max_lat, &min_lon, &max_lon );
1503 /* NB I think the logic used in this test to determine if the bounds is within view
1504 fails if track goes across 180 degrees longitude.
1505 Hopefully that situation is not too common...
1506 Mind you viking doesn't really do edge locations to well anyway */
1507 if ( min_lat < minmin.lat &&
1508 max_lat > minmin.lat &&
1509 min_lon < maxmax.lon &&
1510 max_lon > maxmax.lon )
1511 /* Found within zoom level */
1516 vik_viewport_set_zoom ( zts->vw->viking_vvp, zoom );
1519 draw_update ( zts->vw );
1521 return VIK_LAYER_TOOL_ACK;
1524 static VikToolInterface zoom_tool =
1525 { { "Zoom", "vik-icon-zoom", N_("_Zoom"), "<control><shift>Z", N_("Zoom Tool"), 1 },
1526 (VikToolConstructorFunc) zoomtool_create,
1527 (VikToolDestructorFunc) zoomtool_destroy,
1528 (VikToolActivationFunc) NULL,
1529 (VikToolActivationFunc) NULL,
1530 (VikToolMouseFunc) zoomtool_click,
1531 (VikToolMouseMoveFunc) zoomtool_move,
1532 (VikToolMouseFunc) zoomtool_release,
1535 GDK_CURSOR_IS_PIXMAP,
1536 &cursor_zoom_pixbuf };
1537 /*** end zoom code ********************************************************/
1539 /********************************************************************************
1541 ********************************************************************************/
1542 static gpointer pantool_create (VikWindow *vw, VikViewport *vvp)
1547 static VikLayerToolFuncStatus pantool_click (VikLayer *vl, GdkEventButton *event, VikWindow *vw)
1549 vw->modified = TRUE;
1550 if ( event->button == 1 )
1551 vik_window_pan_click ( vw, event );
1553 return VIK_LAYER_TOOL_ACK;
1556 static VikLayerToolFuncStatus pantool_move (VikLayer *vl, GdkEventMotion *event, VikWindow *vw)
1558 vik_window_pan_move ( vw, event );
1559 return VIK_LAYER_TOOL_ACK;
1562 static VikLayerToolFuncStatus pantool_release (VikLayer *vl, GdkEventButton *event, VikWindow *vw)
1564 if ( event->button == 1 )
1565 vik_window_pan_release ( vw, event );
1566 return VIK_LAYER_TOOL_ACK;
1569 static VikToolInterface pan_tool =
1570 { { "Pan", "vik-icon-pan", N_("_Pan"), "<control><shift>P", N_("Pan Tool"), 0 },
1571 (VikToolConstructorFunc) pantool_create,
1572 (VikToolDestructorFunc) NULL,
1573 (VikToolActivationFunc) NULL,
1574 (VikToolActivationFunc) NULL,
1575 (VikToolMouseFunc) pantool_click,
1576 (VikToolMouseMoveFunc) pantool_move,
1577 (VikToolMouseFunc) pantool_release,
1581 /*** end pan code ********************************************************/
1583 /********************************************************************************
1585 ********************************************************************************/
1586 static gpointer selecttool_create (VikWindow *vw, VikViewport *vvp)
1588 tool_ed_t *t = g_new(tool_ed_t, 1);
1592 t->is_waypoint = FALSE;
1596 static void selecttool_destroy (tool_ed_t *t)
1604 GdkEventButton *event;
1605 tool_ed_t *tool_edit;
1608 static void click_layer_selected (VikLayer *vl, clicker *ck)
1610 /* Do nothing when function call returns true; */
1611 /* i.e. stop on first found item */
1614 if ( vik_layer_get_interface(vl->type)->select_click )
1615 ck->cont = !vik_layer_get_interface(vl->type)->select_click ( vl, ck->event, ck->vvp, ck->tool_edit );
1618 static VikLayerToolFuncStatus selecttool_click (VikLayer *vl, GdkEventButton *event, tool_ed_t *t)
1620 /* Only allow selection on primary button */
1621 if ( event->button == 1 ) {
1622 /* Enable click to apply callback to potentially all track/waypoint layers */
1623 /* Useful as we can find things that aren't necessarily in the currently selected layer */
1624 GList* gl = vik_layers_panel_get_all_layers_of_type ( t->vw->viking_vlp, VIK_LAYER_TRW, FALSE ); // Don't get invisible layers
1627 ck.vvp = t->vw->viking_vvp;
1630 g_list_foreach ( gl, (GFunc) click_layer_selected, &ck );
1633 // If nothing found then deselect & redraw screen if necessary to remove the highlight
1636 VikTreeview *vtv = vik_layers_panel_get_treeview ( t->vw->viking_vlp );
1638 if ( vik_treeview_get_selected_iter ( vtv, &iter ) ) {
1639 // Only clear if selected thing is a TrackWaypoint layer or a sublayer
1640 gint type = vik_treeview_item_get_type ( vtv, &iter );
1641 if ( type == VIK_TREEVIEW_TYPE_SUBLAYER ||
1642 VIK_LAYER(vik_treeview_item_get_pointer ( vtv, &iter ))->type == VIK_LAYER_TRW ) {
1644 vik_treeview_item_unselect ( vtv, &iter );
1645 if ( vik_window_clear_highlight ( t->vw ) )
1646 draw_update ( t->vw );
1651 else if ( ( event->button == 3 ) && ( vl && ( vl->type == VIK_LAYER_TRW ) ) ) {
1653 /* Act on currently selected item to show menu */
1654 if ( t->vw->selected_track || t->vw->selected_waypoint )
1655 if ( vik_layer_get_interface(vl->type)->show_viewport_menu )
1656 vik_layer_get_interface(vl->type)->show_viewport_menu ( vl, event, t->vw->viking_vvp );
1659 return VIK_LAYER_TOOL_ACK;
1662 static VikLayerToolFuncStatus selecttool_move (VikLayer *vl, GdkEventButton *event, tool_ed_t *t)
1664 /* Only allow selection on primary button */
1665 if ( event->button == 1 ) {
1666 // Don't care about vl here
1668 if ( vik_layer_get_interface(VIK_LAYER_TRW)->select_move )
1669 vik_layer_get_interface(VIK_LAYER_TRW)->select_move ( vl, event, t->vvp, t );
1671 return VIK_LAYER_TOOL_ACK;
1674 static VikLayerToolFuncStatus selecttool_release (VikLayer *vl, GdkEventButton *event, tool_ed_t *t)
1676 /* Only allow selection on primary button */
1677 if ( event->button == 1 ) {
1678 // Don't care about vl here
1680 if ( vik_layer_get_interface(VIK_LAYER_TRW)->select_release )
1681 vik_layer_get_interface(VIK_LAYER_TRW)->select_release ( (VikLayer*)t->vtl, event, t->vvp, t );
1683 return VIK_LAYER_TOOL_ACK;
1686 static VikToolInterface select_tool =
1687 { { "Select", "vik-icon-select", N_("_Select"), "<control><shift>S", N_("Select Tool"), 3 },
1688 (VikToolConstructorFunc) selecttool_create,
1689 (VikToolDestructorFunc) selecttool_destroy,
1690 (VikToolActivationFunc) NULL,
1691 (VikToolActivationFunc) NULL,
1692 (VikToolMouseFunc) selecttool_click,
1693 (VikToolMouseMoveFunc) selecttool_move,
1694 (VikToolMouseFunc) selecttool_release,
1695 (VikToolKeyFunc) NULL,
1700 /*** end select tool code ********************************************************/
1702 static void draw_pan_cb ( GtkAction *a, VikWindow *vw )
1704 // Since the treeview cell editting intercepts standard keyboard handlers, it means we can receive events here
1705 // Thus if currently editting, ensure we don't move the viewport when Ctrl+<arrow> is received
1706 VikLayer *sel = vik_layers_panel_get_selected ( vw->viking_vlp );
1707 if ( sel && vik_treeview_get_editing ( sel->vt ) )
1710 if (!strcmp(gtk_action_get_name(a), "PanNorth")) {
1711 vik_viewport_set_center_screen ( vw->viking_vvp, vik_viewport_get_width(vw->viking_vvp)/2, 0 );
1712 } else if (!strcmp(gtk_action_get_name(a), "PanEast")) {
1713 vik_viewport_set_center_screen ( vw->viking_vvp, vik_viewport_get_width(vw->viking_vvp), vik_viewport_get_height(vw->viking_vvp)/2 );
1714 } else if (!strcmp(gtk_action_get_name(a), "PanSouth")) {
1715 vik_viewport_set_center_screen ( vw->viking_vvp, vik_viewport_get_width(vw->viking_vvp)/2, vik_viewport_get_height(vw->viking_vvp) );
1716 } else if (!strcmp(gtk_action_get_name(a), "PanWest")) {
1717 vik_viewport_set_center_screen ( vw->viking_vvp, 0, vik_viewport_get_height(vw->viking_vvp)/2 );
1722 static void full_screen_cb ( GtkAction *a, VikWindow *vw )
1724 GtkWidget *check_box = gtk_ui_manager_get_widget ( vw->uim, "/ui/MainMenu/View/FullScreen" );
1725 g_assert(check_box);
1726 gboolean state = gtk_check_menu_item_get_active ( GTK_CHECK_MENU_ITEM(check_box));
1728 gtk_window_fullscreen ( GTK_WINDOW(vw) );
1730 gtk_window_unfullscreen ( GTK_WINDOW(vw) );
1733 static void draw_zoom_cb ( GtkAction *a, VikWindow *vw )
1737 if (!strcmp(gtk_action_get_name(a), "ZoomIn")) {
1740 else if (!strcmp(gtk_action_get_name(a), "ZoomOut")) {
1743 else if (!strcmp(gtk_action_get_name(a), "Zoom0.25")) {
1746 else if (!strcmp(gtk_action_get_name(a), "Zoom0.5")) {
1750 gchar *s = (gchar *)gtk_action_get_name(a);
1756 case -3: vik_viewport_zoom_in ( vw->viking_vvp ); break;
1757 case -4: vik_viewport_zoom_out ( vw->viking_vvp ); break;
1758 case -1: vik_viewport_set_zoom ( vw->viking_vvp, 0.5 ); break;
1759 case -2: vik_viewport_set_zoom ( vw->viking_vvp, 0.25 ); break;
1760 default: vik_viewport_set_zoom ( vw->viking_vvp, what );
1765 static void draw_goto_cb ( GtkAction *a, VikWindow *vw )
1767 VikCoord new_center;
1769 if (!strcmp(gtk_action_get_name(a), "GotoLL")) {
1770 struct LatLon ll, llold;
1771 vik_coord_to_latlon ( vik_viewport_get_center ( vw->viking_vvp ), &llold );
1772 if ( a_dialog_goto_latlon ( GTK_WINDOW(vw), &ll, &llold ) )
1773 vik_coord_load_from_latlon ( &new_center, vik_viewport_get_coord_mode(vw->viking_vvp), &ll );
1777 else if (!strcmp(gtk_action_get_name(a), "GotoUTM")) {
1778 struct UTM utm, utmold;
1779 vik_coord_to_utm ( vik_viewport_get_center ( vw->viking_vvp ), &utmold );
1780 if ( a_dialog_goto_utm ( GTK_WINDOW(vw), &utm, &utmold ) )
1781 vik_coord_load_from_utm ( &new_center, vik_viewport_get_coord_mode(vw->viking_vvp), &utm );
1786 g_critical("Houston, we've had a problem.");
1790 vik_viewport_set_center_coord ( vw->viking_vvp, &new_center );
1795 * Refresh maps displayed
1797 static void draw_refresh_cb ( GtkAction *a, VikWindow *vw )
1799 // Only get 'new' maps
1800 simple_map_update ( vw, TRUE );
1803 static void menu_addlayer_cb ( GtkAction *a, VikWindow *vw )
1805 VikLayerTypeEnum type;
1806 for ( type = 0; type < VIK_LAYER_NUM_TYPES; type++ ) {
1807 if (!strcmp(vik_layer_get_interface(type)->name, gtk_action_get_name(a))) {
1808 if ( vik_layers_panel_new_layer ( vw->viking_vlp, type ) ) {
1810 vw->modified = TRUE;
1816 static void menu_copy_layer_cb ( GtkAction *a, VikWindow *vw )
1818 a_clipboard_copy_selected ( vw->viking_vlp );
1821 static void menu_cut_layer_cb ( GtkAction *a, VikWindow *vw )
1823 vik_layers_panel_cut_selected ( vw->viking_vlp );
1824 vw->modified = TRUE;
1827 static void menu_paste_layer_cb ( GtkAction *a, VikWindow *vw )
1829 if ( vik_layers_panel_paste_selected ( vw->viking_vlp ) )
1831 vw->modified = TRUE;
1835 static void menu_properties_cb ( GtkAction *a, VikWindow *vw )
1837 if ( ! vik_layers_panel_properties ( vw->viking_vlp ) )
1838 a_dialog_info_msg ( GTK_WINDOW(vw), _("You must select a layer to show its properties.") );
1841 static void help_help_cb ( GtkAction *a, VikWindow *vw )
1844 ShellExecute(NULL, "open", ""PACKAGE".pdf", NULL, NULL, SW_SHOWNORMAL);
1847 uri = g_strdup_printf("ghelp:%s", PACKAGE);
1848 GError *error = NULL;
1849 gboolean show = gtk_show_uri (NULL, uri, GDK_CURRENT_TIME, &error);
1850 if ( !show && !error )
1851 // No error to show, so unlikely this will get called
1852 a_dialog_error_msg ( GTK_WINDOW(vw), _("The help system is not available.") );
1855 a_dialog_error_msg_extra ( GTK_WINDOW(vw), _("Help is not available because: %s.\nEnsure a Mime Type ghelp handler program is installed (e.g. yelp)."), error->message );
1856 g_error_free ( error );
1859 #endif /* WINDOWS */
1862 static void help_about_cb ( GtkAction *a, VikWindow *vw )
1864 a_dialog_about(GTK_WINDOW(vw));
1867 static void menu_delete_layer_cb ( GtkAction *a, VikWindow *vw )
1869 if ( vik_layers_panel_get_selected ( vw->viking_vlp ) )
1871 vik_layers_panel_delete_selected ( vw->viking_vlp );
1872 vw->modified = TRUE;
1875 a_dialog_info_msg ( GTK_WINDOW(vw), _("You must select a layer to delete.") );
1878 static void view_side_panel_cb ( GtkAction *a, VikWindow *vw )
1880 GtkWidget *check_box = gtk_ui_manager_get_widget ( vw->uim, "/ui/MainMenu/View/SetShow/ViewSidePanel" );
1881 g_assert(check_box);
1882 gboolean state = gtk_check_menu_item_get_active ( GTK_CHECK_MENU_ITEM(check_box));
1884 gtk_widget_show(GTK_WIDGET(vw->viking_vlp));
1886 gtk_widget_hide(GTK_WIDGET(vw->viking_vlp));
1889 static void view_statusbar_cb ( GtkAction *a, VikWindow *vw )
1891 GtkWidget *check_box = gtk_ui_manager_get_widget ( vw->uim, "/ui/MainMenu/View/SetShow/ViewStatusBar" );
1894 gboolean state = gtk_check_menu_item_get_active ( GTK_CHECK_MENU_ITEM(check_box) );
1896 gtk_widget_show ( GTK_WIDGET(vw->viking_vs) );
1898 gtk_widget_hide ( GTK_WIDGET(vw->viking_vs) );
1901 static void view_toolbar_cb ( GtkAction *a, VikWindow *vw )
1903 GtkWidget *check_box = gtk_ui_manager_get_widget ( vw->uim, "/ui/MainMenu/View/SetShow/ViewToolbar" );
1906 gboolean state = gtk_check_menu_item_get_active ( GTK_CHECK_MENU_ITEM(check_box) );
1908 gtk_widget_show ( GTK_WIDGET(vw->toolbar) );
1910 gtk_widget_hide ( GTK_WIDGET(vw->toolbar) );
1913 static void view_main_menu_cb ( GtkAction *a, VikWindow *vw )
1915 GtkWidget *check_box = gtk_ui_manager_get_widget ( vw->uim, "/ui/MainMenu/View/SetShow/ViewMainMenu" );
1918 gboolean state = gtk_check_menu_item_get_active ( GTK_CHECK_MENU_ITEM(check_box) );
1920 gtk_widget_hide ( gtk_ui_manager_get_widget ( vw->uim, "/ui/MainMenu" ) );
1922 gtk_widget_show ( gtk_ui_manager_get_widget ( vw->uim, "/ui/MainMenu" ) );
1925 /***************************************
1926 ** tool management routines
1928 ***************************************/
1930 static toolbox_tools_t* toolbox_create(VikWindow *vw)
1932 toolbox_tools_t *vt = g_new(toolbox_tools_t, 1);
1935 vt->active_tool = -1;
1940 static void toolbox_add_tool(toolbox_tools_t *vt, VikToolInterface *vti, gint layer_type )
1942 vt->tools = g_renew(toolbox_tool_t, vt->tools, vt->n_tools+1);
1943 vt->tools[vt->n_tools].ti = *vti;
1944 vt->tools[vt->n_tools].layer_type = layer_type;
1946 vt->tools[vt->n_tools].state = vti->create(vt->vw, vt->vw->viking_vvp);
1949 vt->tools[vt->n_tools].state = NULL;
1954 static int toolbox_get_tool(toolbox_tools_t *vt, const gchar *tool_name)
1957 for (i=0; i<vt->n_tools; i++) {
1958 if (!strcmp(tool_name, vt->tools[i].ti.radioActionEntry.name)) {
1965 static void toolbox_activate(toolbox_tools_t *vt, const gchar *tool_name)
1967 int tool = toolbox_get_tool(vt, tool_name);
1968 toolbox_tool_t *t = &vt->tools[tool];
1969 VikLayer *vl = vik_layers_panel_get_selected ( vt->vw->viking_vlp );
1971 if (tool == vt->n_tools) {
1972 g_critical("trying to activate a non-existent tool...");
1975 /* is the tool already active? */
1976 if (vt->active_tool == tool) {
1980 if (vt->active_tool != -1) {
1981 if (vt->tools[vt->active_tool].ti.deactivate) {
1982 vt->tools[vt->active_tool].ti.deactivate(NULL, vt->tools[vt->active_tool].state);
1985 if (t->ti.activate) {
1986 t->ti.activate(vl, t->state);
1988 vt->active_tool = tool;
1991 static const GdkCursor *toolbox_get_cursor(toolbox_tools_t *vt, const gchar *tool_name)
1993 int tool = toolbox_get_tool(vt, tool_name);
1994 toolbox_tool_t *t = &vt->tools[tool];
1995 if (t->ti.cursor == NULL) {
1996 if (t->ti.cursor_type == GDK_CURSOR_IS_PIXMAP && t->ti.cursor_data != NULL) {
1997 GError *cursor_load_err = NULL;
1998 GdkPixbuf *cursor_pixbuf = gdk_pixbuf_from_pixdata (t->ti.cursor_data, FALSE, &cursor_load_err);
1999 /* TODO: settable offeset */
2000 t->ti.cursor = gdk_cursor_new_from_pixbuf ( gdk_display_get_default(), cursor_pixbuf, 3, 3 );
2001 g_object_unref ( G_OBJECT(cursor_pixbuf) );
2003 t->ti.cursor = gdk_cursor_new ( t->ti.cursor_type );
2006 return t->ti.cursor;
2009 static void toolbox_click (toolbox_tools_t *vt, GdkEventButton *event)
2011 VikLayer *vl = vik_layers_panel_get_selected ( vt->vw->viking_vlp );
2012 if (vt->active_tool != -1 && vt->tools[vt->active_tool].ti.click) {
2013 gint ltype = vt->tools[vt->active_tool].layer_type;
2014 if ( ltype == TOOL_LAYER_TYPE_NONE || (vl && ltype == vl->type) )
2015 vt->tools[vt->active_tool].ti.click(vl, event, vt->tools[vt->active_tool].state);
2019 static void toolbox_move (toolbox_tools_t *vt, GdkEventMotion *event)
2021 VikLayer *vl = vik_layers_panel_get_selected ( vt->vw->viking_vlp );
2022 if (vt->active_tool != -1 && vt->tools[vt->active_tool].ti.move) {
2023 gint ltype = vt->tools[vt->active_tool].layer_type;
2024 if ( ltype == TOOL_LAYER_TYPE_NONE || (vl && ltype == vl->type) )
2025 if ( VIK_LAYER_TOOL_ACK_GRAB_FOCUS == vt->tools[vt->active_tool].ti.move(vl, event, vt->tools[vt->active_tool].state) )
2026 gtk_widget_grab_focus ( GTK_WIDGET(vt->vw->viking_vvp) );
2030 static void toolbox_release (toolbox_tools_t *vt, GdkEventButton *event)
2032 VikLayer *vl = vik_layers_panel_get_selected ( vt->vw->viking_vlp );
2033 if (vt->active_tool != -1 && vt->tools[vt->active_tool].ti.release ) {
2034 gint ltype = vt->tools[vt->active_tool].layer_type;
2035 if ( ltype == TOOL_LAYER_TYPE_NONE || (vl && ltype == vl->type) )
2036 vt->tools[vt->active_tool].ti.release(vl, event, vt->tools[vt->active_tool].state);
2039 /** End tool management ************************************/
2041 void vik_window_enable_layer_tool ( VikWindow *vw, gint layer_id, gint tool_id )
2043 gtk_action_activate ( gtk_action_group_get_action ( vw->action_group, vik_layer_get_interface(layer_id)->tools[tool_id].radioActionEntry.name ) );
2046 /* this function gets called whenever a toolbar tool is clicked */
2047 static void menu_tool_cb ( GtkAction *old, GtkAction *a, VikWindow *vw )
2049 /* White Magic, my friends ... White Magic... */
2051 toolbox_activate(vw->vt, gtk_action_get_name(a));
2053 vw->viewport_cursor = (GdkCursor *)toolbox_get_cursor(vw->vt, gtk_action_get_name(a));
2055 if ( gtk_widget_get_window(GTK_WIDGET(vw->viking_vvp)) )
2056 /* We set cursor, even if it is NULL: it resets to default */
2057 gdk_window_set_cursor ( gtk_widget_get_window(GTK_WIDGET(vw->viking_vvp)), vw->viewport_cursor );
2059 if (!strcmp(gtk_action_get_name(a), "Pan")) {
2060 vw->current_tool = TOOL_PAN;
2062 else if (!strcmp(gtk_action_get_name(a), "Zoom")) {
2063 vw->current_tool = TOOL_ZOOM;
2065 else if (!strcmp(gtk_action_get_name(a), "Ruler")) {
2066 vw->current_tool = TOOL_RULER;
2068 else if (!strcmp(gtk_action_get_name(a), "Select")) {
2069 vw->current_tool = TOOL_SELECT;
2072 /* TODO: only enable tools from active layer */
2073 VikLayerTypeEnum layer_id;
2074 for (layer_id=0; layer_id<VIK_LAYER_NUM_TYPES; layer_id++) {
2075 for ( tool_id = 0; tool_id < vik_layer_get_interface(layer_id)->tools_count; tool_id++ ) {
2076 if (!strcmp(vik_layer_get_interface(layer_id)->tools[tool_id].radioActionEntry.name, gtk_action_get_name(a))) {
2077 vw->current_tool = TOOL_LAYER;
2078 vw->tool_layer_id = layer_id;
2079 vw->tool_tool_id = tool_id;
2084 draw_status_tool ( vw );
2087 static void window_set_filename ( VikWindow *vw, const gchar *filename )
2092 g_free ( vw->filename );
2093 if ( filename == NULL )
2095 vw->filename = NULL;
2099 vw->filename = g_strdup(filename);
2102 /* Refresh window's title */
2103 file = window_get_filename ( vw );
2104 title = g_strdup_printf( "%s - Viking", file );
2105 gtk_window_set_title ( GTK_WINDOW(vw), title );
2109 static const gchar *window_get_filename ( VikWindow *vw )
2111 return vw->filename ? a_file_basename ( vw->filename ) : _("Untitled");
2114 GtkWidget *vik_window_get_drawmode_button ( VikWindow *vw, VikViewportDrawMode mode )
2116 GtkWidget *mode_button;
2119 #ifdef VIK_CONFIG_EXPEDIA
2120 case VIK_VIEWPORT_DRAWMODE_EXPEDIA: buttonname = "/ui/MainMenu/View/ModeExpedia"; break;
2122 case VIK_VIEWPORT_DRAWMODE_MERCATOR: buttonname = "/ui/MainMenu/View/ModeMercator"; break;
2123 case VIK_VIEWPORT_DRAWMODE_LATLON: buttonname = "/ui/MainMenu/View/ModeLatLon"; break;
2124 default: buttonname = "/ui/MainMenu/View/ModeUTM";
2126 mode_button = gtk_ui_manager_get_widget ( vw->uim, buttonname );
2127 g_assert ( mode_button );
2132 * vik_window_get_pan_move:
2133 * @vw: some VikWindow
2135 * Retrieves @vw's pan_move.
2137 * Should be removed as soon as possible.
2139 * Returns: @vw's pan_move
2143 gboolean vik_window_get_pan_move ( VikWindow *vw )
2145 return vw->pan_move;
2148 static void on_activate_recent_item (GtkRecentChooser *chooser,
2153 filename = gtk_recent_chooser_get_current_uri (chooser);
2154 if (filename != NULL)
2156 GFile *file = g_file_new_for_uri ( filename );
2157 gchar *path = g_file_get_path ( file );
2158 g_object_unref ( file );
2159 if ( self->filename )
2161 GSList *filenames = NULL;
2162 filenames = g_slist_append ( filenames, path );
2163 g_signal_emit ( G_OBJECT(self), window_signals[VW_OPENWINDOW_SIGNAL], 0, filenames );
2164 // NB: GSList & contents are freed by main.open_window
2167 vik_window_open_file ( self, path, TRUE );
2175 static void setup_recent_files (VikWindow *self)
2177 GtkRecentManager *manager;
2178 GtkRecentFilter *filter;
2179 GtkWidget *menu, *menu_item;
2181 filter = gtk_recent_filter_new ();
2182 /* gtk_recent_filter_add_application (filter, g_get_application_name()); */
2183 gtk_recent_filter_add_group(filter, "viking");
2185 manager = gtk_recent_manager_get_default ();
2186 menu = gtk_recent_chooser_menu_new_for_manager (manager);
2187 gtk_recent_chooser_set_sort_type (GTK_RECENT_CHOOSER (menu), GTK_RECENT_SORT_MRU);
2188 gtk_recent_chooser_add_filter (GTK_RECENT_CHOOSER (menu), filter);
2190 menu_item = gtk_ui_manager_get_widget (self->uim, "/ui/MainMenu/File/OpenRecentFile");
2191 gtk_menu_item_set_submenu (GTK_MENU_ITEM (menu_item), menu);
2193 g_signal_connect (G_OBJECT (menu), "item-activated",
2194 G_CALLBACK (on_activate_recent_item), (gpointer) self);
2197 static void update_recently_used_document(const gchar *filename)
2199 /* Update Recently Used Document framework */
2200 GtkRecentManager *manager = gtk_recent_manager_get_default();
2201 GtkRecentData *recent_data = g_slice_new (GtkRecentData);
2202 gchar *groups[] = {"viking", NULL};
2203 GFile *file = g_file_new_for_commandline_arg(filename);
2204 gchar *uri = g_file_get_uri(file);
2205 gchar *basename = g_path_get_basename(filename);
2206 g_object_unref(file);
2209 recent_data->display_name = basename;
2210 recent_data->description = NULL;
2211 recent_data->mime_type = "text/x-gps-data";
2212 recent_data->app_name = (gchar *) g_get_application_name ();
2213 recent_data->app_exec = g_strjoin (" ", g_get_prgname (), "%f", NULL);
2214 recent_data->groups = groups;
2215 recent_data->is_private = FALSE;
2216 if (!gtk_recent_manager_add_full (manager, uri, recent_data))
2218 g_warning (_("Unable to add '%s' to the list of recently used documents"), uri);
2223 g_free (recent_data->app_exec);
2224 g_slice_free (GtkRecentData, recent_data);
2228 * Call this before doing things that may take a long time and otherwise not show any other feedback
2229 * such as loading and saving files
2231 void vik_window_set_busy_cursor ( VikWindow *vw )
2233 gdk_window_set_cursor ( gtk_widget_get_window(GTK_WIDGET(vw)), vw->busy_cursor );
2234 // Viewport has a separate cursor
2235 gdk_window_set_cursor ( GTK_WIDGET(vw->viking_vvp)->window, vw->busy_cursor );
2236 // Ensure cursor updated before doing stuff
2237 while( gtk_events_pending() )
2238 gtk_main_iteration();
2241 void vik_window_clear_busy_cursor ( VikWindow *vw )
2243 gdk_window_set_cursor ( gtk_widget_get_window(GTK_WIDGET(vw)), NULL );
2244 // Restore viewport cursor
2245 gdk_window_set_cursor ( GTK_WIDGET(vw->viking_vvp)->window, vw->viewport_cursor );
2248 void vik_window_open_file ( VikWindow *vw, const gchar *filename, gboolean change_filename )
2250 vik_window_set_busy_cursor ( vw );
2252 switch ( a_file_load ( vik_layers_panel_get_top_layer(vw->viking_vlp), vw->viking_vvp, filename ) )
2254 case LOAD_TYPE_READ_FAILURE:
2255 a_dialog_error_msg ( GTK_WINDOW(vw), _("The file you requested could not be opened.") );
2257 case LOAD_TYPE_GPSBABEL_FAILURE:
2258 a_dialog_error_msg ( GTK_WINDOW(vw), _("GPSBabel is required to load files of this type or GPSBabel encountered problems.") );
2260 case LOAD_TYPE_GPX_FAILURE:
2261 a_dialog_error_msg_extra ( GTK_WINDOW(vw), _("Unable to load malformed GPX file %s"), filename );
2263 case LOAD_TYPE_UNSUPPORTED_FAILURE:
2264 a_dialog_error_msg_extra ( GTK_WINDOW(vw), _("Unsupported file type for %s"), filename );
2266 case LOAD_TYPE_VIK_FAILURE_NON_FATAL:
2268 // Since we can process .vik files with issues just show a warning in the status bar
2269 // Not that a user can do much about it... or tells them what this issue is yet...
2270 gchar *msg = g_strdup_printf (_("WARNING: issues encountered loading %s"), a_file_basename (filename) );
2271 vik_statusbar_set_message ( vw->viking_vs, VIK_STATUSBAR_INFO, msg );
2274 // No break, carry on to show any data
2275 case LOAD_TYPE_VIK_SUCCESS:
2277 GtkWidget *mode_button;
2279 if ( change_filename )
2280 window_set_filename ( vw, filename );
2281 mode_button = vik_window_get_drawmode_button ( vw, vik_viewport_get_drawmode ( vw->viking_vvp ) );
2282 vw->only_updating_coord_mode_ui = TRUE; /* if we don't set this, it will change the coord to UTM if we click Lat/Lon. I don't know why. */
2283 gtk_check_menu_item_set_active ( GTK_CHECK_MENU_ITEM(mode_button), TRUE );
2284 vw->only_updating_coord_mode_ui = FALSE;
2286 vik_layers_panel_change_coord_mode ( vw->viking_vlp, vik_viewport_get_coord_mode ( vw->viking_vvp ) );
2288 mode_button = gtk_ui_manager_get_widget ( vw->uim, "/ui/MainMenu/View/SetShow/ShowScale" );
2289 g_assert ( mode_button );
2290 gtk_check_menu_item_set_active ( GTK_CHECK_MENU_ITEM(mode_button),vik_viewport_get_draw_scale(vw->viking_vvp) );
2292 mode_button = gtk_ui_manager_get_widget ( vw->uim, "/ui/MainMenu/View/SetShow/ShowCenterMark" );
2293 g_assert ( mode_button );
2294 gtk_check_menu_item_set_active ( GTK_CHECK_MENU_ITEM(mode_button),vik_viewport_get_draw_centermark(vw->viking_vvp) );
2296 mode_button = gtk_ui_manager_get_widget ( vw->uim, "/ui/MainMenu/View/SetShow/ShowHighlight" );
2297 g_assert ( mode_button );
2298 gtk_check_menu_item_set_active ( GTK_CHECK_MENU_ITEM(mode_button),vik_viewport_get_draw_highlight (vw->viking_vvp) );
2300 //case LOAD_TYPE_OTHER_SUCCESS:
2302 update_recently_used_document(filename);
2307 vik_window_clear_busy_cursor ( vw );
2310 static void load_file ( GtkAction *a, VikWindow *vw )
2312 GSList *files = NULL;
2313 GSList *cur_file = NULL;
2315 if (!strcmp(gtk_action_get_name(a), "Open")) {
2318 else if (!strcmp(gtk_action_get_name(a), "Append")) {
2322 g_critical("Houston, we've had a problem.");
2326 if ( ! vw->open_dia )
2328 vw->open_dia = gtk_file_chooser_dialog_new (_("Please select a GPS data file to open. "),
2330 GTK_FILE_CHOOSER_ACTION_OPEN,
2331 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
2332 GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT,
2334 gchar *cwd = g_get_current_dir();
2336 gtk_file_chooser_set_current_folder ( GTK_FILE_CHOOSER(vw->open_dia), cwd );
2340 GtkFileFilter *filter;
2341 // NB file filters are listed this way for alphabetical ordering
2342 #ifdef VIK_CONFIG_GEOCACHES
2343 filter = gtk_file_filter_new ();
2344 gtk_file_filter_set_name( filter, _("Geocaching") );
2345 gtk_file_filter_add_pattern ( filter, "*.loc" ); // No MIME type available
2346 gtk_file_chooser_add_filter (GTK_FILE_CHOOSER(vw->open_dia), filter);
2349 filter = gtk_file_filter_new ();
2350 gtk_file_filter_set_name( filter, _("Google Earth") );
2351 gtk_file_filter_add_mime_type ( filter, "application/vnd.google-earth.kml+xml");
2352 gtk_file_chooser_add_filter (GTK_FILE_CHOOSER(vw->open_dia), filter);
2354 filter = gtk_file_filter_new ();
2355 gtk_file_filter_set_name( filter, _("GPX") );
2356 gtk_file_filter_add_pattern ( filter, "*.gpx" ); // No MIME type available
2357 gtk_file_chooser_add_filter (GTK_FILE_CHOOSER(vw->open_dia), filter);
2359 filter = gtk_file_filter_new ();
2360 gtk_file_filter_set_name( filter, _("Viking") );
2361 gtk_file_filter_add_pattern ( filter, "*.vik" );
2362 gtk_file_filter_add_pattern ( filter, "*.viking" );
2363 gtk_file_chooser_add_filter (GTK_FILE_CHOOSER(vw->open_dia), filter);
2365 // NB could have filters for gpspoint (*.gps,*.gpsoint?) + gpsmapper (*.gsm,*.gpsmapper?)
2366 // However assume this are barely used and thus not worthy of inclusion
2367 // as they'll just make the options too many and have no clear file pattern
2368 // one can always use the all option
2369 filter = gtk_file_filter_new ();
2370 gtk_file_filter_set_name( filter, _("All") );
2371 gtk_file_filter_add_pattern ( filter, "*" );
2372 gtk_file_chooser_add_filter (GTK_FILE_CHOOSER(vw->open_dia), filter);
2373 // Default to any file - same as before open filters were added
2374 gtk_file_chooser_set_filter (GTK_FILE_CHOOSER(vw->open_dia), filter);
2376 gtk_file_chooser_set_select_multiple ( GTK_FILE_CHOOSER(vw->open_dia), TRUE );
2377 gtk_window_set_transient_for ( GTK_WINDOW(vw->open_dia), GTK_WINDOW(vw) );
2378 gtk_window_set_destroy_with_parent ( GTK_WINDOW(vw->open_dia), TRUE );
2380 if ( gtk_dialog_run ( GTK_DIALOG(vw->open_dia) ) == GTK_RESPONSE_ACCEPT )
2382 gtk_widget_hide ( vw->open_dia );
2383 #ifdef VIKING_PROMPT_IF_MODIFIED
2384 if ( (vw->modified || vw->filename) && newwindow )
2386 if ( vw->filename && newwindow )
2388 g_signal_emit ( G_OBJECT(vw), window_signals[VW_OPENWINDOW_SIGNAL], 0, gtk_file_chooser_get_filenames (GTK_FILE_CHOOSER(vw->open_dia) ) );
2390 files = gtk_file_chooser_get_filenames (GTK_FILE_CHOOSER(vw->open_dia) );
2391 gboolean change_fn = newwindow && (g_slist_length(files)==1); /* only change fn if one file */
2392 gboolean first_vik_file = TRUE;
2394 while ( cur_file ) {
2396 gchar *file_name = cur_file->data;
2397 if ( newwindow && check_file_magic_vik ( file_name ) ) {
2398 // Load first of many .vik files in current window
2399 if ( first_vik_file ) {
2400 vik_window_open_file ( vw, file_name, TRUE );
2401 first_vik_file = FALSE;
2404 // Load each subsequent .vik file in a separate window
2405 VikWindow *newvw = vik_window_new_window ();
2407 vik_window_open_file ( newvw, file_name, TRUE );
2412 vik_window_open_file ( vw, file_name, change_fn );
2415 cur_file = g_slist_next (cur_file);
2417 g_slist_free (files);
2421 gtk_widget_hide ( vw->open_dia );
2424 static gboolean save_file_as ( GtkAction *a, VikWindow *vw )
2426 gboolean rv = FALSE;
2428 if ( ! vw->save_dia )
2430 vw->save_dia = gtk_file_chooser_dialog_new (_("Save as Viking File."),
2432 GTK_FILE_CHOOSER_ACTION_SAVE,
2433 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
2434 GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT,
2436 gchar *cwd = g_get_current_dir();
2438 gtk_file_chooser_set_current_folder ( GTK_FILE_CHOOSER(vw->save_dia), cwd );
2442 GtkFileFilter *filter;
2443 filter = gtk_file_filter_new ();
2444 gtk_file_filter_set_name( filter, _("All") );
2445 gtk_file_filter_add_pattern ( filter, "*" );
2446 gtk_file_chooser_add_filter (GTK_FILE_CHOOSER(vw->save_dia), filter);
2448 filter = gtk_file_filter_new ();
2449 gtk_file_filter_set_name( filter, _("Viking") );
2450 gtk_file_filter_add_pattern ( filter, "*.vik" );
2451 gtk_file_filter_add_pattern ( filter, "*.viking" );
2452 gtk_file_chooser_add_filter (GTK_FILE_CHOOSER(vw->save_dia), filter);
2453 // Default to a Viking file
2454 gtk_file_chooser_set_filter (GTK_FILE_CHOOSER(vw->save_dia), filter);
2456 gtk_window_set_transient_for ( GTK_WINDOW(vw->save_dia), GTK_WINDOW(vw) );
2457 gtk_window_set_destroy_with_parent ( GTK_WINDOW(vw->save_dia), TRUE );
2459 // Auto append / replace extension with '.vik' to the suggested file name as it's going to be a Viking File
2460 gchar* auto_save_name = g_strdup ( window_get_filename ( vw ) );
2461 if ( ! check_file_ext ( auto_save_name, ".vik" ) )
2462 auto_save_name = g_strconcat ( auto_save_name, ".vik", NULL );
2464 gtk_file_chooser_set_current_name (GTK_FILE_CHOOSER(vw->save_dia), auto_save_name);
2466 while ( gtk_dialog_run ( GTK_DIALOG(vw->save_dia) ) == GTK_RESPONSE_ACCEPT )
2468 fn = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER(vw->save_dia) );
2469 if ( g_file_test ( fn, G_FILE_TEST_EXISTS ) == FALSE || a_dialog_yes_or_no ( GTK_WINDOW(vw->save_dia), _("The file \"%s\" exists, do you wish to overwrite it?"), a_file_basename ( fn ) ) )
2471 window_set_filename ( vw, fn );
2472 rv = window_save ( vw );
2473 vw->modified = FALSE;
2477 g_free ( auto_save_name );
2478 gtk_widget_hide ( vw->save_dia );
2482 static gboolean window_save ( VikWindow *vw )
2484 vik_window_set_busy_cursor ( vw );
2485 gboolean success = TRUE;
2487 if ( a_file_save ( vik_layers_panel_get_top_layer ( vw->viking_vlp ), vw->viking_vvp, vw->filename ) )
2489 update_recently_used_document ( vw->filename );
2493 a_dialog_error_msg ( GTK_WINDOW(vw), _("The filename you requested could not be opened for writing.") );
2496 vik_window_clear_busy_cursor ( vw );
2500 static gboolean save_file ( GtkAction *a, VikWindow *vw )
2502 if ( ! vw->filename )
2503 return save_file_as ( NULL, vw );
2506 vw->modified = FALSE;
2507 return window_save ( vw );
2514 * Export all TRW Layers in the list to individual files in the specified directory
2516 * Returns: %TRUE on success
2518 static gboolean export_to ( VikWindow *vw, GList *gl, VikFileType_t vft, const gchar *dir, const gchar *extension )
2520 gboolean success = TRUE;
2522 gint export_count = 0;
2524 vik_window_set_busy_cursor ( vw );
2528 gchar *fn = g_strconcat ( dir, G_DIR_SEPARATOR_S, VIK_LAYER(gl->data)->name, extension, NULL );
2530 // Some protection in attempting to write too many same named files
2531 // As this will get horribly slow...
2532 gboolean safe = FALSE;
2534 while ( ii < 5000 ) {
2535 if ( g_file_test ( fn, G_FILE_TEST_EXISTS ) ) {
2538 fn = g_strdup_printf ( "%s%s%s#%03d%s", dir, G_DIR_SEPARATOR_S, VIK_LAYER(gl->data)->name, ii, extension );
2549 // NB: We allow exporting empty layers
2551 gboolean this_success = a_file_export ( VIK_TRW_LAYER(gl->data), fn, vft, NULL, TRUE );
2553 // Show some progress
2554 if ( this_success ) {
2556 gchar *message = g_strconcat ( _("Exporting to file: "), fn, NULL );
2557 vik_statusbar_set_message ( vw->viking_vs, VIK_STATUSBAR_INFO, message );
2558 while ( gtk_events_pending() )
2559 gtk_main_iteration ();
2563 success = success && this_success;
2567 gl = g_list_next ( gl );
2570 vik_window_clear_busy_cursor ( vw );
2572 // Confirm what happened.
2573 gchar *message = g_strdup_printf ( _("Exported files: %d"), export_count );
2574 vik_statusbar_set_message ( vw->viking_vs, VIK_STATUSBAR_INFO, message );
2580 static void export_to_common ( VikWindow *vw, VikFileType_t vft, const gchar *extension )
2582 GList *gl = vik_layers_panel_get_all_layers_of_type ( vw->viking_vlp, VIK_LAYER_TRW, TRUE );
2585 a_dialog_info_msg ( GTK_WINDOW(vw), _("Nothing to Export!") );
2589 GtkWidget *dialog = gtk_dialog_new_with_buttons ( _("Export to directory"),
2591 GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
2593 GTK_RESPONSE_REJECT,
2595 GTK_RESPONSE_ACCEPT,
2598 GtkWidget *gw = gtk_file_chooser_widget_new ( GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER );
2599 gtk_box_pack_start ( GTK_BOX(GTK_DIALOG(dialog)->vbox), gw, TRUE, TRUE, 0 );
2601 // try to make it a nice size - otherwise seems to default to something impractically small
2602 gtk_window_set_default_size ( GTK_WINDOW(dialog), 600, 300 );
2604 gtk_widget_show_all ( dialog );
2606 if ( gtk_dialog_run ( GTK_DIALOG(dialog) ) == GTK_RESPONSE_ACCEPT ) {
2607 gchar *dir = gtk_file_chooser_get_filename ( GTK_FILE_CHOOSER(gw) );
2608 gtk_widget_destroy ( dialog );
2610 if ( !export_to ( vw, gl, vft, dir, extension ) )
2611 a_dialog_error_msg ( GTK_WINDOW(vw),_("Could not convert all files") );
2616 gtk_widget_destroy ( dialog );
2621 static void export_to_gpx ( GtkAction *a, VikWindow *vw )
2623 export_to_common ( vw, FILE_TYPE_GPX, ".gpx" );
2626 static void export_to_kml ( GtkAction *a, VikWindow *vw )
2628 export_to_common ( vw, FILE_TYPE_KML, ".kml" );
2631 static void acquire_from_gps ( GtkAction *a, VikWindow *vw )
2633 // Via the file menu, acquiring from a GPS makes a new layer
2634 // this has always been the way (not entirely sure if this was the real intention!)
2635 // thus maintain the behaviour ATM.
2636 // Hence explicit setting here (as the value may be changed elsewhere)
2637 vik_datasource_gps_interface.mode = VIK_DATASOURCE_CREATENEWLAYER;
2638 a_acquire(vw, vw->viking_vlp, vw->viking_vvp, &vik_datasource_gps_interface, NULL, NULL );
2641 static void acquire_from_file ( GtkAction *a, VikWindow *vw )
2643 a_acquire(vw, vw->viking_vlp, vw->viking_vvp, &vik_datasource_file_interface, NULL, NULL );
2646 #ifdef VIK_CONFIG_GOOGLE
2647 static void acquire_from_google ( GtkAction *a, VikWindow *vw )
2649 a_acquire(vw, vw->viking_vlp, vw->viking_vvp, &vik_datasource_google_interface, NULL, NULL );
2653 #ifdef VIK_CONFIG_OPENSTREETMAP
2654 static void acquire_from_osm ( GtkAction *a, VikWindow *vw )
2656 a_acquire(vw, vw->viking_vlp, vw->viking_vvp, &vik_datasource_osm_interface, NULL, NULL );
2659 static void acquire_from_my_osm ( GtkAction *a, VikWindow *vw )
2661 a_acquire(vw, vw->viking_vlp, vw->viking_vvp, &vik_datasource_osm_my_traces_interface, NULL, NULL );
2665 #ifdef VIK_CONFIG_GEOCACHES
2666 static void acquire_from_gc ( GtkAction *a, VikWindow *vw )
2668 a_acquire(vw, vw->viking_vlp, vw->viking_vvp, &vik_datasource_gc_interface, NULL, NULL );
2672 #ifdef VIK_CONFIG_GEOTAG
2673 static void acquire_from_geotag ( GtkAction *a, VikWindow *vw )
2675 vik_datasource_geotag_interface.mode = VIK_DATASOURCE_CREATENEWLAYER;
2676 a_acquire(vw, vw->viking_vlp, vw->viking_vvp, &vik_datasource_geotag_interface, NULL, NULL );
2680 #ifdef VIK_CONFIG_GEONAMES
2681 static void acquire_from_wikipedia ( GtkAction *a, VikWindow *vw )
2683 a_acquire(vw, vw->viking_vlp, vw->viking_vvp, &vik_datasource_wikipedia_interface, NULL, NULL );
2687 static void goto_default_location( GtkAction *a, VikWindow *vw)
2690 ll.lat = a_vik_get_default_lat();
2691 ll.lon = a_vik_get_default_long();
2692 vik_viewport_set_center_latlon(vw->viking_vvp, &ll);
2693 vik_layers_panel_emit_update(vw->viking_vlp);
2697 static void goto_address( GtkAction *a, VikWindow *vw)
2699 a_vik_goto ( vw, vw->viking_vvp );
2700 vik_layers_panel_emit_update ( vw->viking_vlp );
2703 static void mapcache_flush_cb ( GtkAction *a, VikWindow *vw )
2708 static void layer_defaults_cb ( GtkAction *a, VikWindow *vw )
2710 gchar **texts = g_strsplit ( gtk_action_get_name(a), "Layer", 0 );
2713 return; // Internally broken :(
2715 if ( ! a_layer_defaults_show_window ( GTK_WINDOW(vw), texts[1] ) )
2716 a_dialog_info_msg ( GTK_WINDOW(vw), _("This layer has no configurable properties.") );
2717 // NB no update needed
2719 g_strfreev ( texts );
2722 static void preferences_change_update ( VikWindow *vw, gpointer data )
2724 // Want to update all TrackWaypoint layers
2725 GList *layers = vik_layers_panel_get_all_layers_of_type ( vw->viking_vlp, VIK_LAYER_TRW, TRUE );
2727 GList *iter = g_list_first ( layers );
2729 // Reset the individual waypoints themselves due to the preferences change
2730 VikTrwLayer *vtl = VIK_TRW_LAYER(VIK_LAYER(layers->data));
2731 vik_trw_layer_reset_waypoints ( vtl );
2732 iter = g_list_next ( iter );
2735 g_list_free ( layers );
2740 static void preferences_cb ( GtkAction *a, VikWindow *vw )
2742 gboolean wp_icon_size = a_vik_get_use_large_waypoint_icons();
2744 a_preferences_show_window ( GTK_WINDOW(vw) );
2746 // Has the waypoint size setting changed?
2747 if (wp_icon_size != a_vik_get_use_large_waypoint_icons()) {
2748 // Delete icon indexing 'cache' and so automatically regenerates with the new setting when changed
2749 clear_garmin_icon_syms ();
2751 // Update all windows
2752 g_slist_foreach ( window_list, (GFunc) preferences_change_update, NULL );
2756 static void default_location_cb ( GtkAction *a, VikWindow *vw )
2758 /* Simplistic repeat of preference setting
2759 Only the name & type are important for setting the preference via this 'external' way */
2760 VikLayerParam pref_lat[] = {
2761 { VIK_LAYER_NUM_TYPES,
2762 VIKING_PREFERENCES_NAMESPACE "default_latitude",
2763 VIK_LAYER_PARAM_DOUBLE,
2766 VIK_LAYER_WIDGET_SPINBUTTON,
2771 VikLayerParam pref_lon[] = {
2772 { VIK_LAYER_NUM_TYPES,
2773 VIKING_PREFERENCES_NAMESPACE "default_longitude",
2774 VIK_LAYER_PARAM_DOUBLE,
2777 VIK_LAYER_WIDGET_SPINBUTTON,
2783 /* Get current center */
2785 vik_coord_to_latlon ( vik_viewport_get_center ( vw->viking_vvp ), &ll );
2787 /* Apply to preferences */
2788 VikLayerParamData vlp_data;
2789 vlp_data.d = ll.lat;
2790 a_preferences_run_setparam (vlp_data, pref_lat);
2791 vlp_data.d = ll.lon;
2792 a_preferences_run_setparam (vlp_data, pref_lon);
2793 /* Remember to save */
2794 a_preferences_save_to_file();
2797 static void clear_cb ( GtkAction *a, VikWindow *vw )
2799 vik_layers_panel_clear ( vw->viking_vlp );
2800 window_set_filename ( vw, NULL );
2804 static void window_close ( GtkAction *a, VikWindow *vw )
2806 if ( ! delete_event ( vw ) )
2807 gtk_widget_destroy ( GTK_WIDGET(vw) );
2810 static gboolean save_file_and_exit ( GtkAction *a, VikWindow *vw )
2812 if (save_file( NULL, vw)) {
2813 window_close( NULL, vw);
2820 static void zoom_to_cb ( GtkAction *a, VikWindow *vw )
2822 gdouble xmpp = vik_viewport_get_xmpp ( vw->viking_vvp ), ympp = vik_viewport_get_ympp ( vw->viking_vvp );
2823 if ( a_dialog_custom_zoom ( GTK_WINDOW(vw), &xmpp, &ympp ) )
2825 vik_viewport_set_xmpp ( vw->viking_vvp, xmpp );
2826 vik_viewport_set_ympp ( vw->viking_vvp, ympp );
2831 static void save_image_file ( VikWindow *vw, const gchar *fn, guint w, guint h, gdouble zoom, gboolean save_as_png )
2833 /* more efficient way: stuff draws directly to pixbuf (fork viewport) */
2834 GdkPixbuf *pixbuf_to_save;
2835 gdouble old_xmpp, old_ympp;
2836 GError *error = NULL;
2838 GtkWidget *msgbox = gtk_message_dialog_new ( GTK_WINDOW(vw),
2839 GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
2842 _("Generating image file...") );
2844 g_signal_connect_swapped (msgbox, "response", G_CALLBACK (gtk_widget_destroy), msgbox);
2845 // Ensure dialog shown
2846 gtk_widget_show_all ( msgbox );
2848 vik_statusbar_set_message ( vw->viking_vs, VIK_STATUSBAR_INFO, _("Generating image file...") );
2849 while ( gtk_events_pending() )
2850 gtk_main_iteration ();
2851 // Despite many efforts & variations, GTK on my Linux system doesn't show the actual msgbox contents :(
2852 // At least the empty box can give a clue something's going on + the statusbar msg...
2853 // Windows version under Wine OK!
2855 /* backup old zoom & set new */
2856 old_xmpp = vik_viewport_get_xmpp ( vw->viking_vvp );
2857 old_ympp = vik_viewport_get_ympp ( vw->viking_vvp );
2858 vik_viewport_set_zoom ( vw->viking_vvp, zoom );
2860 /* reset width and height: */
2861 vik_viewport_configure_manually ( vw->viking_vvp, w, h );
2863 /* draw all layers */
2866 /* save buffer as file. */
2867 pixbuf_to_save = gdk_pixbuf_get_from_drawable ( NULL, GDK_DRAWABLE(vik_viewport_get_pixmap ( vw->viking_vvp )), NULL, 0, 0, 0, 0, w, h);
2868 if ( !pixbuf_to_save ) {
2869 g_warning("Failed to generate internal pixmap size: %d x %d", w, h);
2870 gtk_message_dialog_set_markup ( GTK_MESSAGE_DIALOG(msgbox), _("Failed to generate internal image.\n\nTry creating a smaller image.") );
2874 gdk_pixbuf_save ( pixbuf_to_save, fn, save_as_png ? "png" : "jpeg", &error, NULL );
2877 g_warning("Unable to write to file %s: %s", fn, error->message );
2878 gtk_message_dialog_set_markup ( GTK_MESSAGE_DIALOG(msgbox), _("Failed to generate image file.") );
2879 g_error_free (error);
2883 gtk_message_dialog_set_markup ( GTK_MESSAGE_DIALOG(msgbox), _("Image file generated.") );
2885 g_object_unref ( G_OBJECT(pixbuf_to_save) );
2888 vik_statusbar_set_message ( vw->viking_vs, VIK_STATUSBAR_INFO, "" );
2889 gtk_dialog_add_button ( GTK_DIALOG(msgbox), GTK_STOCK_OK, GTK_RESPONSE_OK );
2890 gtk_dialog_run ( GTK_DIALOG(msgbox) ); // Don't care about the result
2892 /* pretend like nothing happened ;) */
2893 vik_viewport_set_xmpp ( vw->viking_vvp, old_xmpp );
2894 vik_viewport_set_ympp ( vw->viking_vvp, old_ympp );
2895 vik_viewport_configure ( vw->viking_vvp );
2899 static void save_image_dir ( VikWindow *vw, const gchar *fn, guint w, guint h, gdouble zoom, gboolean save_as_png, guint tiles_w, guint tiles_h )
2901 gulong size = sizeof(gchar) * (strlen(fn) + 15);
2902 gchar *name_of_file = g_malloc ( size );
2904 struct UTM utm_orig, utm;
2906 /* *** copied from above *** */
2907 GdkPixbuf *pixbuf_to_save;
2908 gdouble old_xmpp, old_ympp;
2909 GError *error = NULL;
2911 /* backup old zoom & set new */
2912 old_xmpp = vik_viewport_get_xmpp ( vw->viking_vvp );
2913 old_ympp = vik_viewport_get_ympp ( vw->viking_vvp );
2914 vik_viewport_set_zoom ( vw->viking_vvp, zoom );
2916 /* reset width and height: do this only once for all images (same size) */
2917 vik_viewport_configure_manually ( vw->viking_vvp, w, h );
2918 /* *** end copy from above *** */
2920 g_assert ( vik_viewport_get_coord_mode ( vw->viking_vvp ) == VIK_COORD_UTM );
2924 utm_orig = *((const struct UTM *)vik_viewport_get_center ( vw->viking_vvp ));
2926 for ( y = 1; y <= tiles_h; y++ )
2928 for ( x = 1; x <= tiles_w; x++ )
2930 g_snprintf ( name_of_file, size, "%s%cy%d-x%d.%s", fn, G_DIR_SEPARATOR, y, x, save_as_png ? "png" : "jpg" );
2932 if ( tiles_w & 0x1 )
2933 utm.easting += ((gdouble)x - ceil(((gdouble)tiles_w)/2)) * (w*zoom);
2935 utm.easting += ((gdouble)x - (((gdouble)tiles_w)+1)/2) * (w*zoom);
2936 if ( tiles_h & 0x1 ) /* odd */
2937 utm.northing -= ((gdouble)y - ceil(((gdouble)tiles_h)/2)) * (h*zoom);
2939 utm.northing -= ((gdouble)y - (((gdouble)tiles_h)+1)/2) * (h*zoom);
2941 /* move to correct place. */
2942 vik_viewport_set_center_utm ( vw->viking_vvp, &utm );
2946 /* save buffer as file. */
2947 pixbuf_to_save = gdk_pixbuf_get_from_drawable ( NULL, GDK_DRAWABLE(vik_viewport_get_pixmap ( vw->viking_vvp )), NULL, 0, 0, 0, 0, w, h);
2948 gdk_pixbuf_save ( pixbuf_to_save, name_of_file, save_as_png ? "png" : "jpeg", &error, NULL );
2951 g_warning("Unable to write to file %s: %s", name_of_file, error->message );
2952 g_error_free (error);
2955 g_object_unref ( G_OBJECT(pixbuf_to_save) );
2959 vik_viewport_set_center_utm ( vw->viking_vvp, &utm_orig );
2960 vik_viewport_set_xmpp ( vw->viking_vvp, old_xmpp );
2961 vik_viewport_set_ympp ( vw->viking_vvp, old_ympp );
2962 vik_viewport_configure ( vw->viking_vvp );
2965 g_free ( name_of_file );
2968 static void draw_to_image_file_current_window_cb(GtkWidget* widget,GdkEventButton *event,gpointer *pass_along)
2970 VikWindow *vw = VIK_WINDOW(pass_along[0]);
2971 GtkSpinButton *width_spin = GTK_SPIN_BUTTON(pass_along[1]), *height_spin = GTK_SPIN_BUTTON(pass_along[2]);
2973 gint active = gtk_combo_box_get_active ( GTK_COMBO_BOX(pass_along[3]) );
2974 gdouble zoom = pow (2, active-2 );
2976 gdouble width_min, width_max, height_min, height_max;
2979 gtk_spin_button_get_range ( width_spin, &width_min, &width_max );
2980 gtk_spin_button_get_range ( height_spin, &height_min, &height_max );
2982 /* TODO: support for xzoom and yzoom values */
2983 width = vik_viewport_get_width ( vw->viking_vvp ) * vik_viewport_get_xmpp ( vw->viking_vvp ) / zoom;
2984 height = vik_viewport_get_height ( vw->viking_vvp ) * vik_viewport_get_xmpp ( vw->viking_vvp ) / zoom;
2986 if ( width > width_max || width < width_min || height > height_max || height < height_min )
2987 a_dialog_info_msg ( GTK_WINDOW(vw), _("Viewable region outside allowable pixel size bounds for image. Clipping width/height values.") );
2989 gtk_spin_button_set_value ( width_spin, width );
2990 gtk_spin_button_set_value ( height_spin, height );
2993 static void draw_to_image_file_total_area_cb (GtkSpinButton *spinbutton, gpointer *pass_along)
2995 GtkSpinButton *width_spin = GTK_SPIN_BUTTON(pass_along[1]), *height_spin = GTK_SPIN_BUTTON(pass_along[2]);
2997 gint active = gtk_combo_box_get_active ( GTK_COMBO_BOX(pass_along[3]) );
2998 gdouble zoom = pow (2, active-2 );
3002 w = gtk_spin_button_get_value(width_spin) * zoom;
3003 h = gtk_spin_button_get_value(height_spin) * zoom;
3004 if (pass_along[4]) /* save many images; find TOTAL area covered */
3006 w *= gtk_spin_button_get_value(GTK_SPIN_BUTTON(pass_along[4]));
3007 h *= gtk_spin_button_get_value(GTK_SPIN_BUTTON(pass_along[5]));
3009 vik_units_distance_t dist_units = a_vik_get_units_distance ();
3010 switch (dist_units) {
3011 case VIK_UNITS_DISTANCE_KILOMETRES:
3012 label_text = g_strdup_printf ( _("Total area: %ldm x %ldm (%.3f sq. km)"), (glong)w, (glong)h, (w*h/1000000));
3014 case VIK_UNITS_DISTANCE_MILES:
3015 label_text = g_strdup_printf ( _("Total area: %ldm x %ldm (%.3f sq. miles)"), (glong)w, (glong)h, (w*h/2589988.11));
3018 label_text = g_strdup_printf ("Just to keep the compiler happy");
3019 g_critical("Houston, we've had a problem. distance=%d", dist_units);
3022 gtk_label_set_text(GTK_LABEL(pass_along[6]), label_text);
3023 g_free ( label_text );
3027 * Get an allocated filename (or directory as specified)
3029 static gchar* draw_image_filename ( VikWindow *vw, gboolean one_image_only )
3032 if ( one_image_only )
3035 if (!vw->save_img_dia) {
3036 vw->save_img_dia = gtk_file_chooser_dialog_new (_("Save Image"),
3038 GTK_FILE_CHOOSER_ACTION_SAVE,
3039 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
3040 GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT,
3043 gchar *cwd = g_get_current_dir();
3045 gtk_file_chooser_set_current_folder ( GTK_FILE_CHOOSER(vw->save_img_dia), cwd );
3049 GtkFileChooser *chooser = GTK_FILE_CHOOSER ( vw->save_img_dia );
3051 GtkFileFilter *filter;
3052 filter = gtk_file_filter_new ();
3053 gtk_file_filter_set_name ( filter, _("All") );
3054 gtk_file_filter_add_pattern ( filter, "*" );
3055 gtk_file_chooser_add_filter ( chooser, filter );
3057 filter = gtk_file_filter_new ();
3058 gtk_file_filter_set_name ( filter, _("JPG") );
3059 gtk_file_filter_add_mime_type ( filter, "image/jpeg");
3060 gtk_file_chooser_add_filter ( chooser, filter );
3062 if ( !vw->draw_image_save_as_png )
3063 gtk_file_chooser_set_filter ( chooser, filter );
3065 filter = gtk_file_filter_new ();
3066 gtk_file_filter_set_name ( filter, _("PNG") );
3067 gtk_file_filter_add_mime_type ( filter, "image/png");
3068 gtk_file_chooser_add_filter ( chooser, filter );
3070 if ( vw->draw_image_save_as_png )
3071 gtk_file_chooser_set_filter ( chooser, filter );
3073 gtk_window_set_transient_for ( GTK_WINDOW(vw->save_img_dia), GTK_WINDOW(vw) );
3074 gtk_window_set_destroy_with_parent ( GTK_WINDOW(vw->save_img_dia), TRUE );
3077 if ( gtk_dialog_run ( GTK_DIALOG(vw->save_img_dia) ) == GTK_RESPONSE_ACCEPT ) {
3078 fn = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER(vw->save_img_dia) );
3079 if ( g_file_test ( fn, G_FILE_TEST_EXISTS ) )
3080 if ( ! a_dialog_yes_or_no ( GTK_WINDOW(vw->save_img_dia), _("The file \"%s\" exists, do you wish to overwrite it?"), a_file_basename ( fn ) ) )
3083 gtk_widget_hide ( vw->save_img_dia );
3087 // For some reason this method is only written to work in UTM...
3088 if ( vik_viewport_get_coord_mode(vw->viking_vvp) != VIK_COORD_UTM ) {
3089 a_dialog_error_msg ( GTK_WINDOW(vw), _("You must be in UTM mode to use this feature") );
3093 if (!vw->save_img_dir_dia) {
3094 vw->save_img_dir_dia = gtk_file_chooser_dialog_new (_("Choose a directory to hold images"),
3096 GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER,
3097 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
3098 GTK_STOCK_OK, GTK_RESPONSE_ACCEPT,
3100 gtk_window_set_transient_for ( GTK_WINDOW(vw->save_img_dir_dia), GTK_WINDOW(vw) );
3101 gtk_window_set_destroy_with_parent ( GTK_WINDOW(vw->save_img_dir_dia), TRUE );
3104 if ( gtk_dialog_run ( GTK_DIALOG(vw->save_img_dir_dia) ) == GTK_RESPONSE_ACCEPT ) {
3105 fn = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER(vw->save_img_dir_dia) );
3107 gtk_widget_hide ( vw->save_img_dir_dia );
3112 static void draw_to_image_file ( VikWindow *vw, gboolean one_image_only )
3114 /* todo: default for answers inside VikWindow or static (thruout instance) */
3115 GtkWidget *dialog = gtk_dialog_new_with_buttons ( _("Save to Image File"), GTK_WINDOW(vw),
3116 GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
3118 GTK_RESPONSE_REJECT,
3120 GTK_RESPONSE_ACCEPT,
3122 GtkWidget *width_label, *width_spin, *height_label, *height_spin;
3123 GtkWidget *png_radio, *jpeg_radio;
3124 GtkWidget *current_window_button;
3125 gpointer current_window_pass_along[7];
3126 GtkWidget *zoom_label, *zoom_combo;
3127 GtkWidget *total_size_label;
3129 /* only used if (!one_image_only) */
3130 GtkWidget *tiles_width_spin = NULL, *tiles_height_spin = NULL;
3132 width_label = gtk_label_new ( _("Width (pixels):") );
3133 width_spin = gtk_spin_button_new ( GTK_ADJUSTMENT(gtk_adjustment_new ( vw->draw_image_width, 10, 50000, 10, 100, 0 )), 10, 0 );
3134 height_label = gtk_label_new ( _("Height (pixels):") );
3135 height_spin = gtk_spin_button_new ( GTK_ADJUSTMENT(gtk_adjustment_new ( vw->draw_image_height, 10, 50000, 10, 100, 0 )), 10, 0 );
3137 GtkWidget *win_warning_label = gtk_label_new ( _("WARNING: USING LARGE IMAGES OVER 10000x10000\nMAY CRASH THE PROGRAM!") );
3139 zoom_label = gtk_label_new ( _("Zoom (meters per pixel):") );
3140 /* TODO: separate xzoom and yzoom factors */
3141 zoom_combo = create_zoom_combo_all_levels();
3143 gdouble mpp = vik_viewport_get_xmpp(vw->viking_vvp);
3144 gint active = 2 + round ( log (mpp) / log (2) );
3146 // Can we not hard code size here?
3151 gtk_combo_box_set_active ( GTK_COMBO_BOX(zoom_combo), active );
3153 total_size_label = gtk_label_new ( NULL );
3155 current_window_button = gtk_button_new_with_label ( _("Area in current viewable window") );
3156 current_window_pass_along [0] = vw;
3157 current_window_pass_along [1] = width_spin;
3158 current_window_pass_along [2] = height_spin;
3159 current_window_pass_along [3] = zoom_combo;
3160 current_window_pass_along [4] = NULL; /* used for one_image_only != 1 */
3161 current_window_pass_along [5] = NULL;
3162 current_window_pass_along [6] = total_size_label;
3163 g_signal_connect ( G_OBJECT(current_window_button), "button_press_event", G_CALLBACK(draw_to_image_file_current_window_cb), current_window_pass_along );
3165 png_radio = gtk_radio_button_new_with_label ( NULL, _("Save as PNG") );
3166 jpeg_radio = gtk_radio_button_new_with_label_from_widget ( GTK_RADIO_BUTTON(png_radio), _("Save as JPEG") );
3168 if ( ! vw->draw_image_save_as_png )
3169 gtk_toggle_button_set_active ( GTK_TOGGLE_BUTTON(jpeg_radio), TRUE );
3171 gtk_box_pack_start (GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), width_label, FALSE, FALSE, 0);
3172 gtk_box_pack_start (GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), width_spin, FALSE, FALSE, 0);
3173 gtk_box_pack_start (GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), height_label, FALSE, FALSE, 0);
3174 gtk_box_pack_start (GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), height_spin, FALSE, FALSE, 0);
3176 gtk_box_pack_start (GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), win_warning_label, FALSE, FALSE, 0);
3178 gtk_box_pack_start (GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), current_window_button, FALSE, FALSE, 0);
3179 gtk_box_pack_start (GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), png_radio, FALSE, FALSE, 0);
3180 gtk_box_pack_start (GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), jpeg_radio, FALSE, FALSE, 0);
3181 gtk_box_pack_start (GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), zoom_label, FALSE, FALSE, 0);
3182 gtk_box_pack_start (GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), zoom_combo, FALSE, FALSE, 0);
3184 if ( ! one_image_only )
3186 GtkWidget *tiles_width_label, *tiles_height_label;
3188 tiles_width_label = gtk_label_new ( _("East-west image tiles:") );
3189 tiles_width_spin = gtk_spin_button_new ( GTK_ADJUSTMENT(gtk_adjustment_new ( 5, 1, 10, 1, 100, 0 )), 1, 0 );
3190 tiles_height_label = gtk_label_new ( _("North-south image tiles:") );
3191 tiles_height_spin = gtk_spin_button_new ( GTK_ADJUSTMENT(gtk_adjustment_new ( 5, 1, 10, 1, 100, 0 )), 1, 0 );
3192 gtk_box_pack_start (GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), tiles_width_label, FALSE, FALSE, 0);
3193 gtk_box_pack_start (GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), tiles_width_spin, FALSE, FALSE, 0);
3194 gtk_box_pack_start (GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), tiles_height_label, FALSE, FALSE, 0);
3195 gtk_box_pack_start (GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), tiles_height_spin, FALSE, FALSE, 0);
3197 current_window_pass_along [4] = tiles_width_spin;
3198 current_window_pass_along [5] = tiles_height_spin;
3199 g_signal_connect ( G_OBJECT(tiles_width_spin), "value-changed", G_CALLBACK(draw_to_image_file_total_area_cb), current_window_pass_along );
3200 g_signal_connect ( G_OBJECT(tiles_height_spin), "value-changed", G_CALLBACK(draw_to_image_file_total_area_cb), current_window_pass_along );
3202 gtk_box_pack_start (GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), total_size_label, FALSE, FALSE, 0);
3203 g_signal_connect ( G_OBJECT(width_spin), "value-changed", G_CALLBACK(draw_to_image_file_total_area_cb), current_window_pass_along );
3204 g_signal_connect ( G_OBJECT(height_spin), "value-changed", G_CALLBACK(draw_to_image_file_total_area_cb), current_window_pass_along );
3205 g_signal_connect ( G_OBJECT(zoom_combo), "changed", G_CALLBACK(draw_to_image_file_total_area_cb), current_window_pass_along );
3207 draw_to_image_file_total_area_cb ( NULL, current_window_pass_along ); /* set correct size info now */
3209 gtk_dialog_set_default_response ( GTK_DIALOG(dialog), GTK_RESPONSE_ACCEPT );
3211 gtk_widget_show_all ( gtk_dialog_get_content_area(GTK_DIALOG(dialog)) );
3213 if ( gtk_dialog_run ( GTK_DIALOG(dialog) ) == GTK_RESPONSE_ACCEPT )
3215 gtk_widget_hide ( GTK_WIDGET(dialog) );
3217 gchar *fn = draw_image_filename ( vw, one_image_only );
3221 gint active = gtk_combo_box_get_active ( GTK_COMBO_BOX(zoom_combo) );
3222 gdouble zoom = pow (2, active-2 );
3224 if ( one_image_only )
3225 save_image_file ( vw, fn,
3226 vw->draw_image_width = gtk_spin_button_get_value_as_int ( GTK_SPIN_BUTTON(width_spin) ),
3227 vw->draw_image_height = gtk_spin_button_get_value_as_int ( GTK_SPIN_BUTTON(height_spin) ),
3229 vw->draw_image_save_as_png = gtk_toggle_button_get_active ( GTK_TOGGLE_BUTTON(png_radio) ) );
3231 // NB is in UTM mode ATM
3232 save_image_dir ( vw, fn,
3233 vw->draw_image_width = gtk_spin_button_get_value_as_int ( GTK_SPIN_BUTTON(width_spin) ),
3234 vw->draw_image_height = gtk_spin_button_get_value_as_int ( GTK_SPIN_BUTTON(height_spin) ),
3236 vw->draw_image_save_as_png = gtk_toggle_button_get_active ( GTK_TOGGLE_BUTTON(png_radio) ),
3237 gtk_spin_button_get_value ( GTK_SPIN_BUTTON(tiles_width_spin) ),
3238 gtk_spin_button_get_value ( GTK_SPIN_BUTTON(tiles_height_spin) ) );
3243 gtk_widget_destroy ( GTK_WIDGET(dialog) );
3247 static void draw_to_image_file_cb ( GtkAction *a, VikWindow *vw )
3249 draw_to_image_file ( vw, TRUE );
3252 static void draw_to_image_dir_cb ( GtkAction *a, VikWindow *vw )
3254 draw_to_image_file ( vw, FALSE );
3257 static void print_cb ( GtkAction *a, VikWindow *vw )
3259 a_print(vw, vw->viking_vvp);
3262 /* really a misnomer: changes coord mode (actual coordinates) AND/OR draw mode (viewport only) */
3263 static void window_change_coord_mode_cb ( GtkAction *old_a, GtkAction *a, VikWindow *vw )
3265 VikViewportDrawMode drawmode;
3266 if (!strcmp(gtk_action_get_name(a), "ModeUTM")) {
3267 drawmode = VIK_VIEWPORT_DRAWMODE_UTM;
3269 else if (!strcmp(gtk_action_get_name(a), "ModeLatLon")) {
3270 drawmode = VIK_VIEWPORT_DRAWMODE_LATLON;
3272 else if (!strcmp(gtk_action_get_name(a), "ModeExpedia")) {
3273 drawmode = VIK_VIEWPORT_DRAWMODE_EXPEDIA;
3275 else if (!strcmp(gtk_action_get_name(a), "ModeMercator")) {
3276 drawmode = VIK_VIEWPORT_DRAWMODE_MERCATOR;
3279 g_critical("Houston, we've had a problem.");
3283 if ( !vw->only_updating_coord_mode_ui )
3285 VikViewportDrawMode olddrawmode = vik_viewport_get_drawmode ( vw->viking_vvp );
3286 if ( olddrawmode != drawmode )
3288 /* this takes care of coord mode too */
3289 vik_viewport_set_drawmode ( vw->viking_vvp, drawmode );
3290 if ( drawmode == VIK_VIEWPORT_DRAWMODE_UTM ) {
3291 vik_layers_panel_change_coord_mode ( vw->viking_vlp, VIK_COORD_UTM );
3292 } else if ( olddrawmode == VIK_VIEWPORT_DRAWMODE_UTM ) {
3293 vik_layers_panel_change_coord_mode ( vw->viking_vlp, VIK_COORD_LATLON );
3300 static void set_draw_scale ( GtkAction *a, VikWindow *vw )
3302 GtkWidget *check_box = gtk_ui_manager_get_widget ( vw->uim, "/ui/MainMenu/View/SetShow/ShowScale" );
3303 g_assert(check_box);
3304 gboolean state = gtk_check_menu_item_get_active ( GTK_CHECK_MENU_ITEM(check_box));
3305 vik_viewport_set_draw_scale ( vw->viking_vvp, state );
3309 static void set_draw_centermark ( GtkAction *a, VikWindow *vw )
3311 GtkWidget *check_box = gtk_ui_manager_get_widget ( vw->uim, "/ui/MainMenu/View/SetShow/ShowCenterMark" );
3312 g_assert(check_box);
3313 gboolean state = gtk_check_menu_item_get_active ( GTK_CHECK_MENU_ITEM(check_box));
3314 vik_viewport_set_draw_centermark ( vw->viking_vvp, state );
3318 static void set_draw_highlight ( GtkAction *a, VikWindow *vw )
3320 GtkWidget *check_box = gtk_ui_manager_get_widget ( vw->uim, "/ui/MainMenu/View/SetShow/ShowHighlight" );
3321 g_assert(check_box);
3322 gboolean state = gtk_check_menu_item_get_active ( GTK_CHECK_MENU_ITEM(check_box));
3323 vik_viewport_set_draw_highlight ( vw->viking_vvp, state );
3327 static void set_bg_color ( GtkAction *a, VikWindow *vw )
3329 GtkWidget *colorsd = gtk_color_selection_dialog_new ( _("Choose a background color") );
3330 GdkColor *color = vik_viewport_get_background_gdkcolor ( vw->viking_vvp );
3331 gtk_color_selection_set_previous_color ( GTK_COLOR_SELECTION(gtk_color_selection_dialog_get_color_selection(GTK_COLOR_SELECTION_DIALOG(colorsd))), color );
3332 gtk_color_selection_set_current_color ( GTK_COLOR_SELECTION(gtk_color_selection_dialog_get_color_selection(GTK_COLOR_SELECTION_DIALOG(colorsd))), color );
3333 if ( gtk_dialog_run ( GTK_DIALOG(colorsd) ) == GTK_RESPONSE_OK )
3335 gtk_color_selection_get_current_color ( GTK_COLOR_SELECTION(gtk_color_selection_dialog_get_color_selection(GTK_COLOR_SELECTION_DIALOG(colorsd))), color );
3336 vik_viewport_set_background_gdkcolor ( vw->viking_vvp, color );
3340 gtk_widget_destroy ( colorsd );
3343 static void set_highlight_color ( GtkAction *a, VikWindow *vw )
3345 GtkWidget *colorsd = gtk_color_selection_dialog_new ( _("Choose a track highlight color") );
3346 GdkColor *color = vik_viewport_get_highlight_gdkcolor ( vw->viking_vvp );
3347 gtk_color_selection_set_previous_color ( GTK_COLOR_SELECTION(gtk_color_selection_dialog_get_color_selection(GTK_COLOR_SELECTION_DIALOG(colorsd))), color );
3348 gtk_color_selection_set_current_color ( GTK_COLOR_SELECTION(gtk_color_selection_dialog_get_color_selection(GTK_COLOR_SELECTION_DIALOG(colorsd))), color );
3349 if ( gtk_dialog_run ( GTK_DIALOG(colorsd) ) == GTK_RESPONSE_OK )
3351 gtk_color_selection_get_current_color ( GTK_COLOR_SELECTION(gtk_color_selection_dialog_get_color_selection(GTK_COLOR_SELECTION_DIALOG(colorsd))), color );
3352 vik_viewport_set_highlight_gdkcolor ( vw->viking_vvp, color );
3356 gtk_widget_destroy ( colorsd );
3361 /***********************************************************************************************
3363 ***********************************************************************************************/
3365 static GtkActionEntry entries[] = {
3366 { "File", NULL, N_("_File"), 0, 0, 0 },
3367 { "Edit", NULL, N_("_Edit"), 0, 0, 0 },
3368 { "View", NULL, N_("_View"), 0, 0, 0 },
3369 { "SetShow", NULL, N_("_Show"), 0, 0, 0 },
3370 { "SetZoom", NULL, N_("_Zoom"), 0, 0, 0 },
3371 { "SetPan", NULL, N_("_Pan"), 0, 0, 0 },
3372 { "Layers", NULL, N_("_Layers"), 0, 0, 0 },
3373 { "Tools", NULL, N_("_Tools"), 0, 0, 0 },
3374 { "Exttools", NULL, N_("_Webtools"), 0, 0, 0 },
3375 { "Help", NULL, N_("_Help"), 0, 0, 0 },
3377 { "New", GTK_STOCK_NEW, N_("_New"), "<control>N", N_("New file"), (GCallback)newwindow_cb },
3378 { "Open", GTK_STOCK_OPEN, N_("_Open..."), "<control>O", N_("Open a file"), (GCallback)load_file },
3379 { "OpenRecentFile", NULL, N_("Open _Recent File"), NULL, NULL, (GCallback)NULL },
3380 { "Append", GTK_STOCK_ADD, N_("Append _File..."), NULL, N_("Append data from a different file"), (GCallback)load_file },
3381 { "Export", GTK_STOCK_CONVERT, N_("_Export All"), NULL, N_("Export All TrackWaypoint Layers"), (GCallback)NULL },
3382 { "ExportGPX", NULL, N_("_GPX..."), NULL, N_("Export as GPX"), (GCallback)export_to_gpx },
3383 { "Acquire", GTK_STOCK_GO_DOWN, N_("A_cquire"), NULL, NULL, (GCallback)NULL },
3384 { "AcquireGPS", NULL, N_("From _GPS..."), NULL, N_("Transfer data from a GPS device"), (GCallback)acquire_from_gps },
3385 { "AcquireGPSBabel", NULL, N_("Import File With GPS_Babel..."), NULL, N_("Import file via GPSBabel converter"), (GCallback)acquire_from_file },
3386 #ifdef VIK_CONFIG_GOOGLE
3387 { "AcquireGoogle", NULL, N_("Google _Directions..."), NULL, N_("Get driving directions from Google"), (GCallback)acquire_from_google },
3389 #ifdef VIK_CONFIG_OPENSTREETMAP
3390 { "AcquireOSM", NULL, N_("_OSM Traces..."), NULL, N_("Get traces from OpenStreetMap"), (GCallback)acquire_from_osm },
3391 { "AcquireMyOSM", NULL, N_("_My OSM Traces..."), NULL, N_("Get Your Own Traces from OpenStreetMap"), (GCallback)acquire_from_my_osm },
3393 #ifdef VIK_CONFIG_GEOCACHES
3394 { "AcquireGC", NULL, N_("Geo_caches..."), NULL, N_("Get Geocaches from geocaching.com"), (GCallback)acquire_from_gc },
3396 #ifdef VIK_CONFIG_GEOTAG
3397 { "AcquireGeotag", NULL, N_("From Geotagged _Images..."), NULL, N_("Create waypoints from geotagged images"), (GCallback)acquire_from_geotag },
3399 #ifdef VIK_CONFIG_GEONAMES
3400 { "AcquireWikipedia", NULL, N_("From _Wikipedia Waypoints"), NULL, N_("Create waypoints from Wikipedia items in the current view"), (GCallback)acquire_from_wikipedia },
3402 { "Save", GTK_STOCK_SAVE, N_("_Save"), "<control>S", N_("Save the file"), (GCallback)save_file },
3403 { "SaveAs", GTK_STOCK_SAVE_AS, N_("Save _As..."), NULL, N_("Save the file under different name"), (GCallback)save_file_as },
3404 { "GenImg", GTK_STOCK_CLEAR, N_("_Generate Image File..."), NULL, N_("Save a snapshot of the workspace into a file"), (GCallback)draw_to_image_file_cb },
3405 { "GenImgDir", GTK_STOCK_DND_MULTIPLE, N_("Generate _Directory of Images..."), NULL, N_("FIXME:IMGDIR"), (GCallback)draw_to_image_dir_cb },
3406 { "Print", GTK_STOCK_PRINT, N_("_Print..."), NULL, N_("Print maps"), (GCallback)print_cb },
3407 { "Exit", GTK_STOCK_QUIT, N_("E_xit"), "<control>W", N_("Exit the program"), (GCallback)window_close },
3408 { "SaveExit", GTK_STOCK_QUIT, N_("Save and Exit"), NULL, N_("Save and Exit the program"), (GCallback)save_file_and_exit },
3410 { "GotoDefaultLocation", GTK_STOCK_HOME, N_("Go to the _Default Location"), NULL, N_("Go to the default location"), (GCallback)goto_default_location },
3411 { "GotoSearch", GTK_STOCK_JUMP_TO, N_("Go to _Location..."), NULL, N_("Go to address/place using text search"), (GCallback)goto_address },
3412 { "GotoLL", GTK_STOCK_JUMP_TO, N_("_Go to Lat/Lon..."), NULL, N_("Go to arbitrary lat/lon coordinate"), (GCallback)draw_goto_cb },
3413 { "GotoUTM", GTK_STOCK_JUMP_TO, N_("Go to UTM..."), NULL, N_("Go to arbitrary UTM coordinate"), (GCallback)draw_goto_cb },
3414 { "Refresh", GTK_STOCK_REFRESH, N_("_Refresh"), "F5", N_("Refresh any maps displayed"), (GCallback)draw_refresh_cb },
3415 { "SetHLColor",GTK_STOCK_SELECT_COLOR, N_("Set _Highlight Color..."), NULL, NULL, (GCallback)set_highlight_color },
3416 { "SetBGColor",GTK_STOCK_SELECT_COLOR, N_("Set Bac_kground Color..."), NULL, NULL, (GCallback)set_bg_color },
3417 { "ZoomIn", GTK_STOCK_ZOOM_IN, N_("Zoom _In"), "<control>plus", NULL, (GCallback)draw_zoom_cb },
3418 { "ZoomOut", GTK_STOCK_ZOOM_OUT, N_("Zoom _Out"), "<control>minus", NULL, (GCallback)draw_zoom_cb },
3419 { "ZoomTo", GTK_STOCK_ZOOM_FIT, N_("Zoom _To..."), "<control>Z", NULL, (GCallback)zoom_to_cb },
3420 { "PanNorth", NULL, N_("Pan _North"), "<control>Up", NULL, (GCallback)draw_pan_cb },
3421 { "PanEast", NULL, N_("Pan _East"), "<control>Right", NULL, (GCallback)draw_pan_cb },
3422 { "PanSouth", NULL, N_("Pan _South"), "<control>Down", NULL, (GCallback)draw_pan_cb },
3423 { "PanWest", NULL, N_("Pan _West"), "<control>Left", NULL, (GCallback)draw_pan_cb },
3424 { "BGJobs", GTK_STOCK_EXECUTE, N_("Background _Jobs"), NULL, NULL, (GCallback)a_background_show_window },
3426 { "Cut", GTK_STOCK_CUT, N_("Cu_t"), NULL, NULL, (GCallback)menu_cut_layer_cb },
3427 { "Copy", GTK_STOCK_COPY, N_("_Copy"), NULL, NULL, (GCallback)menu_copy_layer_cb },
3428 { "Paste", GTK_STOCK_PASTE, N_("_Paste"), NULL, NULL, (GCallback)menu_paste_layer_cb },
3429 { "Delete", GTK_STOCK_DELETE, N_("_Delete"), NULL, NULL, (GCallback)menu_delete_layer_cb },
3430 { "DeleteAll", NULL, N_("Delete All"), NULL, NULL, (GCallback)clear_cb },
3431 { "MapCacheFlush",NULL, N_("_Flush Map Cache"), NULL, NULL, (GCallback)mapcache_flush_cb },
3432 { "SetDefaultLocation", GTK_STOCK_GO_FORWARD, N_("_Set the Default Location"), NULL, N_("Set the Default Location to the current position"),(GCallback)default_location_cb },
3433 { "Preferences",GTK_STOCK_PREFERENCES, N_("_Preferences"), NULL, NULL, (GCallback)preferences_cb },
3434 { "LayerDefaults",GTK_STOCK_PROPERTIES, N_("_Layer Defaults"), NULL, NULL, NULL },
3435 { "Properties",GTK_STOCK_PROPERTIES, N_("_Properties"), NULL, NULL, (GCallback)menu_properties_cb },
3437 { "HelpEntry", GTK_STOCK_HELP, N_("_Help"), "F1", NULL, (GCallback)help_help_cb },
3438 { "About", GTK_STOCK_ABOUT, N_("_About"), NULL, NULL, (GCallback)help_about_cb },
3441 static GtkActionEntry entries_gpsbabel[] = {
3442 { "ExportKML", NULL, N_("_KML..."), NULL, N_("Export as KML"), (GCallback)export_to_kml },
3446 /* FIXME use VIEWPORT_DRAWMODE values */
3447 static GtkRadioActionEntry mode_entries[] = {
3448 { "ModeUTM", NULL, N_("_UTM Mode"), "<control>u", NULL, 0 },
3449 { "ModeExpedia", NULL, N_("_Expedia Mode"), "<control>e", NULL, 1 },
3450 { "ModeMercator", NULL, N_("_Mercator Mode"), "<control>m", NULL, 4 },
3451 { "ModeLatLon", NULL, N_("Lat_/Lon Mode"), "<control>l", NULL, 5 },
3454 static GtkToggleActionEntry toggle_entries[] = {
3455 { "ShowScale", NULL, N_("Show _Scale"), "<shift>F5", N_("Show Scale"), (GCallback)set_draw_scale, TRUE },
3456 { "ShowCenterMark", NULL, N_("Show _Center Mark"), "F6", N_("Show Center Mark"), (GCallback)set_draw_centermark, TRUE },
3457 { "ShowHighlight", GTK_STOCK_UNDERLINE, N_("Show _Highlight"), "F7", N_("Show Highlight"), (GCallback)set_draw_highlight, TRUE },
3458 { "FullScreen", GTK_STOCK_FULLSCREEN, N_("_Full Screen"), "F11", N_("Activate full screen mode"), (GCallback)full_screen_cb, FALSE },
3459 { "ViewSidePanel", GTK_STOCK_INDEX, N_("Show Side _Panel"), "F9", N_("Show Side Panel"), (GCallback)view_side_panel_cb, TRUE },
3460 { "ViewStatusBar", NULL, N_("Show Status_bar"), "F12", N_("Show Statusbar"), (GCallback)view_statusbar_cb, TRUE },
3461 { "ViewToolbar", NULL, N_("Show _Toolbar"), "F3", N_("Show Toolbar"), (GCallback)view_toolbar_cb, TRUE },
3462 { "ViewMainMenu", NULL, N_("Show _Menu"), "F4", N_("Show Menu"), (GCallback)view_main_menu_cb, TRUE },
3465 #include "menu.xml.h"
3466 static void window_create_ui( VikWindow *window )
3469 GtkActionGroup *action_group;
3470 GtkAccelGroup *accel_group;
3473 GtkIconFactory *icon_factory;
3474 GtkIconSet *icon_set;
3475 GtkRadioActionEntry *tools = NULL, *radio;
3478 uim = gtk_ui_manager_new ();
3481 toolbox_add_tool(window->vt, &ruler_tool, TOOL_LAYER_TYPE_NONE);
3482 toolbox_add_tool(window->vt, &zoom_tool, TOOL_LAYER_TYPE_NONE);
3483 toolbox_add_tool(window->vt, &pan_tool, TOOL_LAYER_TYPE_NONE);
3484 toolbox_add_tool(window->vt, &select_tool, TOOL_LAYER_TYPE_NONE);
3487 if (!(mid = gtk_ui_manager_add_ui_from_string (uim, menu_xml, -1, &error))) {
3488 g_error_free (error);
3492 action_group = gtk_action_group_new ("MenuActions");
3493 gtk_action_group_set_translation_domain(action_group, PACKAGE_NAME);
3494 gtk_action_group_add_actions (action_group, entries, G_N_ELEMENTS (entries), window);
3495 gtk_action_group_add_toggle_actions (action_group, toggle_entries, G_N_ELEMENTS (toggle_entries), window);
3496 gtk_action_group_add_radio_actions (action_group, mode_entries, G_N_ELEMENTS (mode_entries), 4, (GCallback)window_change_coord_mode_cb, window);
3498 // Use this to see if GPSBabel is available:
3499 if ( a_babel_device_list ) {
3500 // If going to add more entries then might be worth creating a menu_gpsbabel.xml.h file
3501 if ( gtk_ui_manager_add_ui_from_string ( uim,
3502 "<ui><menubar name='MainMenu'><menu action='File'><menu action='Export'><menuitem action='ExportKML'/></menu></menu></menubar></ui>",
3504 gtk_action_group_add_actions ( action_group, entries_gpsbabel, G_N_ELEMENTS (entries_gpsbabel), window );
3507 icon_factory = gtk_icon_factory_new ();
3508 gtk_icon_factory_add_default (icon_factory);
3510 register_vik_icons(icon_factory);
3512 // Copy the tool RadioActionEntries out of the main Window structure into an extending array 'tools'
3513 // so that it can be applied to the UI in one action group add function call below
3515 for (i=0; i<window->vt->n_tools; i++) {
3516 tools = g_renew(GtkRadioActionEntry, tools, ntools+1);
3517 radio = &tools[ntools];
3519 *radio = window->vt->tools[i].ti.radioActionEntry;
3520 radio->value = ntools;
3523 for (i=0; i<VIK_LAYER_NUM_TYPES; i++) {
3524 GtkActionEntry action;
3525 gtk_ui_manager_add_ui(uim, mid, "/ui/MainMenu/Layers/",
3526 vik_layer_get_interface(i)->name,
3527 vik_layer_get_interface(i)->name,
3528 GTK_UI_MANAGER_MENUITEM, FALSE);
3530 icon_set = gtk_icon_set_new_from_pixbuf (gdk_pixbuf_from_pixdata (vik_layer_get_interface(i)->icon, FALSE, NULL ));
3531 gtk_icon_factory_add (icon_factory, vik_layer_get_interface(i)->name, icon_set);
3532 gtk_icon_set_unref (icon_set);
3534 action.name = vik_layer_get_interface(i)->name;
3535 action.stock_id = vik_layer_get_interface(i)->name;
3536 action.label = g_strdup_printf( _("New _%s Layer"), vik_layer_get_interface(i)->name);
3537 action.accelerator = vik_layer_get_interface(i)->accelerator;
3538 action.tooltip = NULL;
3539 action.callback = (GCallback)menu_addlayer_cb;
3540 gtk_action_group_add_actions(action_group, &action, 1, window);
3542 if ( vik_layer_get_interface(i)->tools_count ) {
3543 gtk_ui_manager_add_ui(uim, mid, "/ui/MainMenu/Tools/", vik_layer_get_interface(i)->name, NULL, GTK_UI_MANAGER_SEPARATOR, FALSE);
3544 gtk_ui_manager_add_ui(uim, mid, "/ui/MainToolbar/ToolItems/", vik_layer_get_interface(i)->name, NULL, GTK_UI_MANAGER_SEPARATOR, FALSE);
3547 // Further tool copying for to apply to the UI, also apply menu UI setup
3548 for ( j = 0; j < vik_layer_get_interface(i)->tools_count; j++ ) {
3549 tools = g_renew(GtkRadioActionEntry, tools, ntools+1);
3550 radio = &tools[ntools];
3553 gtk_ui_manager_add_ui(uim, mid, "/ui/MainMenu/Tools",
3554 vik_layer_get_interface(i)->tools[j].radioActionEntry.label,
3555 vik_layer_get_interface(i)->tools[j].radioActionEntry.name,
3556 GTK_UI_MANAGER_MENUITEM, FALSE);
3557 gtk_ui_manager_add_ui(uim, mid, "/ui/MainToolbar/ToolItems",
3558 vik_layer_get_interface(i)->tools[j].radioActionEntry.label,
3559 vik_layer_get_interface(i)->tools[j].radioActionEntry.name,
3560 GTK_UI_MANAGER_TOOLITEM, FALSE);
3562 toolbox_add_tool(window->vt, &(vik_layer_get_interface(i)->tools[j]), i);
3564 *radio = vik_layer_get_interface(i)->tools[j].radioActionEntry;
3565 // Overwrite with actual number to use
3566 radio->value = ntools;
3569 GtkActionEntry action_dl;
3570 gtk_ui_manager_add_ui(uim, mid, "/ui/MainMenu/Edit/LayerDefaults",
3571 vik_layer_get_interface(i)->name,
3572 g_strdup_printf("Layer%s", vik_layer_get_interface(i)->fixed_layer_name),
3573 GTK_UI_MANAGER_MENUITEM, FALSE);
3575 // For default layers use action names of the form 'Layer<LayerName>'
3576 // This is to avoid clashing with just the layer name used above for the tool actions
3577 action_dl.name = g_strconcat("Layer", vik_layer_get_interface(i)->fixed_layer_name, NULL);
3578 action_dl.stock_id = NULL;
3579 action_dl.label = g_strconcat("_", vik_layer_get_interface(i)->name, "...", NULL); // Prepend marker for keyboard accelerator
3580 action_dl.accelerator = NULL;
3581 action_dl.tooltip = NULL;
3582 action_dl.callback = (GCallback)layer_defaults_cb;
3583 gtk_action_group_add_actions(action_group, &action_dl, 1, window);
3585 g_object_unref (icon_factory);
3587 gtk_action_group_add_radio_actions(action_group, tools, ntools, 0, (GCallback)menu_tool_cb, window);
3590 gtk_ui_manager_insert_action_group (uim, action_group, 0);
3592 for (i=0; i<VIK_LAYER_NUM_TYPES; i++) {
3593 for ( j = 0; j < vik_layer_get_interface(i)->tools_count; j++ ) {
3594 GtkAction *action = gtk_action_group_get_action(action_group,
3595 vik_layer_get_interface(i)->tools[j].radioActionEntry.name);
3596 g_object_set(action, "sensitive", FALSE, NULL);
3600 // This is done last so we don't need to track the value of mid anymore
3601 vik_ext_tools_add_action_items ( window, window->uim, action_group, mid );
3603 window->action_group = action_group;
3605 accel_group = gtk_ui_manager_get_accel_group (uim);
3606 gtk_window_add_accel_group (GTK_WINDOW (window), accel_group);
3607 gtk_ui_manager_ensure_update (uim);
3609 setup_recent_files(window);
3613 // TODO - add method to add tool icons defined from outside this file
3614 // and remove the reverse dependency on icon definition from this file
3616 const GdkPixdata *data;
3619 { &mover_22_pixbuf, "vik-icon-pan" },
3620 { &zoom_18_pixbuf, "vik-icon-zoom" },
3621 { &ruler_18_pixbuf, "vik-icon-ruler" },
3622 { &select_18_pixbuf, "vik-icon-select" },
3623 { &vik_new_route_18_pixbuf, "vik-icon-Create Route" },
3624 { &route_finder_18_pixbuf, "vik-icon-Route Finder" },
3625 { &demdl_18_pixbuf, "vik-icon-DEM Download" },
3626 { &showpic_18_pixbuf, "vik-icon-Show Picture" },
3627 { &addtr_18_pixbuf, "vik-icon-Create Track" },
3628 { &edtr_18_pixbuf, "vik-icon-Edit Trackpoint" },
3629 { &addwp_18_pixbuf, "vik-icon-Create Waypoint" },
3630 { &edwp_18_pixbuf, "vik-icon-Edit Waypoint" },
3631 { &geozoom_18_pixbuf, "vik-icon-Georef Zoom Tool" },
3632 { &geomove_18_pixbuf, "vik-icon-Georef Move Map" },
3633 { &mapdl_18_pixbuf, "vik-icon-Maps Download" },
3636 static gint n_stock_icons = G_N_ELEMENTS (stock_icons);
3639 register_vik_icons (GtkIconFactory *icon_factory)
3641 GtkIconSet *icon_set;
3644 for (i = 0; i < n_stock_icons; i++) {
3645 icon_set = gtk_icon_set_new_from_pixbuf (gdk_pixbuf_from_pixdata (
3646 stock_icons[i].data, FALSE, NULL ));
3647 gtk_icon_factory_add (icon_factory, stock_icons[i].stock_id, icon_set);
3648 gtk_icon_set_unref (icon_set);
3652 gpointer vik_window_get_selected_trw_layer ( VikWindow *vw )
3654 return vw->selected_vtl;
3657 void vik_window_set_selected_trw_layer ( VikWindow *vw, gpointer vtl )
3659 vw->selected_vtl = vtl;
3660 vw->containing_vtl = vtl;
3662 vw->selected_track = NULL;
3663 vw->selected_tracks = NULL;
3664 vw->selected_waypoint = NULL;
3665 vw->selected_waypoints = NULL;
3666 // Set highlight thickness
3667 vik_viewport_set_highlight_thickness ( vw->viking_vvp, vik_trw_layer_get_property_tracks_line_thickness (vw->containing_vtl) );
3670 GHashTable *vik_window_get_selected_tracks ( VikWindow *vw )
3672 return vw->selected_tracks;
3675 void vik_window_set_selected_tracks ( VikWindow *vw, GHashTable *ght, gpointer vtl )
3677 vw->selected_tracks = ght;
3678 vw->containing_vtl = vtl;
3680 vw->selected_vtl = NULL;
3681 vw->selected_track = NULL;
3682 vw->selected_waypoint = NULL;
3683 vw->selected_waypoints = NULL;
3684 // Set highlight thickness
3685 vik_viewport_set_highlight_thickness ( vw->viking_vvp, vik_trw_layer_get_property_tracks_line_thickness (vw->containing_vtl) );
3688 gpointer vik_window_get_selected_track ( VikWindow *vw )
3690 return vw->selected_track;
3693 void vik_window_set_selected_track ( VikWindow *vw, gpointer *vt, gpointer vtl )
3695 vw->selected_track = vt;
3696 vw->containing_vtl = vtl;
3698 vw->selected_vtl = NULL;
3699 vw->selected_tracks = NULL;
3700 vw->selected_waypoint = NULL;
3701 vw->selected_waypoints = NULL;
3702 // Set highlight thickness
3703 vik_viewport_set_highlight_thickness ( vw->viking_vvp, vik_trw_layer_get_property_tracks_line_thickness (vw->containing_vtl) );
3706 GHashTable *vik_window_get_selected_waypoints ( VikWindow *vw )
3708 return vw->selected_waypoints;
3711 void vik_window_set_selected_waypoints ( VikWindow *vw, GHashTable *ght, gpointer vtl )
3713 vw->selected_waypoints = ght;
3714 vw->containing_vtl = vtl;
3716 vw->selected_vtl = NULL;
3717 vw->selected_track = NULL;
3718 vw->selected_tracks = NULL;
3719 vw->selected_waypoint = NULL;
3722 gpointer vik_window_get_selected_waypoint ( VikWindow *vw )
3724 return vw->selected_waypoint;
3727 void vik_window_set_selected_waypoint ( VikWindow *vw, gpointer *vwp, gpointer vtl )
3729 vw->selected_waypoint = vwp;
3730 vw->containing_vtl = vtl;
3732 vw->selected_vtl = NULL;
3733 vw->selected_track = NULL;
3734 vw->selected_tracks = NULL;
3735 vw->selected_waypoints = NULL;
3738 gboolean vik_window_clear_highlight ( VikWindow *vw )
3740 gboolean need_redraw = FALSE;
3741 if ( vw->selected_vtl != NULL ) {
3742 vw->selected_vtl = NULL;
3745 if ( vw->selected_track != NULL ) {
3746 vw->selected_track = NULL;
3749 if ( vw->selected_tracks != NULL ) {
3750 vw->selected_tracks = NULL;
3753 if ( vw->selected_waypoint != NULL ) {
3754 vw->selected_waypoint = NULL;
3757 if ( vw->selected_waypoints != NULL ) {
3758 vw->selected_waypoints = NULL;
3764 GThread *vik_window_get_thread ( VikWindow *vw )