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-2015, 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"
37 #include "preferences.h"
39 #include "viklayer_defaults.h"
40 #include "icons/icons.h"
41 #include "vikexttools.h"
42 #include "vikexttool_datasources.h"
43 #include "garminsymbols.h"
44 #include "vikmapslayer.h"
45 #include "vikrouting.h"
46 #include "geonamessearch.h"
61 #include <glib/gstdio.h>
62 #include <glib/gprintf.h>
63 #include <glib/gi18n.h>
65 #include <gdk/gdkkeysyms.h>
67 // This seems rather arbitary, quite large and pointless
68 // I mean, if you have a thousand windows open;
69 // why not be allowed to open a thousand more...
70 #define MAX_WINDOWS 1024
71 static guint window_count = 0;
72 static GSList *window_list = NULL;
74 #define VIKING_WINDOW_WIDTH 1000
75 #define VIKING_WINDOW_HEIGHT 800
76 #define DRAW_IMAGE_DEFAULT_WIDTH 1280
77 #define DRAW_IMAGE_DEFAULT_HEIGHT 1024
78 #define DRAW_IMAGE_DEFAULT_SAVE_AS_PNG TRUE
80 // The last used directories
81 static gchar *last_folder_files_uri = NULL;
82 static gchar *last_folder_images_uri = NULL;
84 static void window_finalize ( GObject *gob );
85 static GObjectClass *parent_class;
87 static void window_set_filename ( VikWindow *vw, const gchar *filename );
88 static const gchar *window_get_filename ( VikWindow *vw );
90 static VikWindow *window_new ();
92 static void draw_update ( VikWindow *vw );
94 static void newwindow_cb ( GtkAction *a, VikWindow *vw );
97 static void open_window ( VikWindow *vw, GSList *files );
98 static void destroy_window ( GtkWidget *widget,
101 /* Drawing & stuff */
103 static gboolean delete_event( VikWindow *vw );
105 static gboolean key_press_event( VikWindow *vw, GdkEventKey *event, gpointer data );
107 static void center_changed_cb ( VikWindow *vw );
108 static void window_configure_event ( VikWindow *vw );
109 static void draw_sync ( VikWindow *vw );
110 static void draw_redraw ( VikWindow *vw );
111 static void draw_scroll ( VikWindow *vw, GdkEventScroll *event );
112 static void draw_click ( VikWindow *vw, GdkEventButton *event );
113 static void draw_release ( VikWindow *vw, GdkEventButton *event );
114 static void draw_mouse_motion ( VikWindow *vw, GdkEventMotion *event );
115 static void draw_zoom_cb ( GtkAction *a, VikWindow *vw );
116 static void draw_goto_cb ( GtkAction *a, VikWindow *vw );
117 static void draw_refresh_cb ( GtkAction *a, VikWindow *vw );
119 static void draw_status ( VikWindow *vw );
121 /* End Drawing Functions */
123 static void toggle_draw_scale ( GtkAction *a, VikWindow *vw );
124 static void toggle_draw_centermark ( GtkAction *a, VikWindow *vw );
125 static void toggle_draw_highlight ( GtkAction *a, VikWindow *vw );
127 static void menu_addlayer_cb ( GtkAction *a, VikWindow *vw );
128 static void menu_properties_cb ( GtkAction *a, VikWindow *vw );
129 static void menu_delete_layer_cb ( GtkAction *a, VikWindow *vw );
131 /* tool management */
137 #define TOOL_LAYER_TYPE_NONE -1
142 toolbox_tool_t *tools;
146 static void menu_cb ( GtkAction *old, GtkAction *a, VikWindow *vw );
147 static void window_change_coord_mode_cb ( GtkAction *old, GtkAction *a, VikWindow *vw );
148 static toolbox_tools_t* toolbox_create(VikWindow *vw);
149 static void toolbox_add_tool(toolbox_tools_t *vt, VikToolInterface *vti, gint layer_type );
150 static int toolbox_get_tool(toolbox_tools_t *vt, const gchar *tool_name);
151 static void toolbox_activate(toolbox_tools_t *vt, const gchar *tool_name);
152 static const GdkCursor *toolbox_get_cursor(toolbox_tools_t *vt, const gchar *tool_name);
153 static void toolbox_click (toolbox_tools_t *vt, GdkEventButton *event);
154 static void toolbox_move (toolbox_tools_t *vt, GdkEventMotion *event);
155 static void toolbox_release (toolbox_tools_t *vt, GdkEventButton *event);
159 static void window_create_ui( VikWindow *window );
160 static void register_vik_icons (GtkIconFactory *icon_factory);
163 static void load_file ( GtkAction *a, VikWindow *vw );
164 static gboolean save_file_as ( GtkAction *a, VikWindow *vw );
165 static gboolean save_file ( GtkAction *a, VikWindow *vw );
166 static gboolean save_file_and_exit ( GtkAction *a, VikWindow *vw );
167 static gboolean window_save ( VikWindow *vw );
172 VikViewport *viking_vvp;
173 VikLayersPanel *viking_vlp;
174 VikStatusbar *viking_vs;
175 VikToolbar *viking_vtb;
177 GtkWidget *main_vbox;
178 GtkWidget *menu_hbox;
180 GdkCursor *busy_cursor;
181 GdkCursor *viewport_cursor; // only a reference
183 /* tool management state */
186 guint16 tool_layer_id;
187 guint16 tool_tool_id;
189 GtkActionGroup *action_group;
192 // NB scale, centermark and highlight are in viewport.
193 gboolean show_full_screen;
194 gboolean show_side_panel;
195 gboolean show_statusbar;
196 gboolean show_toolbar;
197 gboolean show_main_menu;
199 gboolean select_move;
202 gint delayed_pan_x, delayed_pan_y; // Temporary storage
203 gboolean single_click_pending;
205 guint draw_image_width, draw_image_height;
206 gboolean draw_image_save_as_png;
210 VikLoadType_t loaded_type;
212 gboolean only_updating_coord_mode_ui; /* hack for a bug in GTK */
216 /* half-drawn update */
218 VikCoord trigger_center;
220 /* Store at this level for highlighted selection drawing since it applies to the viewport and the layers panel */
221 /* Only one of these items can be selected at the same time */
222 gpointer selected_vtl; /* notionally VikTrwLayer */
223 GHashTable *selected_tracks;
224 gpointer selected_track; /* notionally VikTrack */
225 GHashTable *selected_waypoints;
226 gpointer selected_waypoint; /* notionally VikWaypoint */
227 /* only use for individual track or waypoint */
228 /* For track(s) & waypoint(s) it is the layer they are in - this helps refering to the individual item easier */
229 gpointer containing_vtl; /* notionally VikTrwLayer */
243 VW_OPENWINDOW_SIGNAL,
247 static guint window_signals[VW_LAST_SIGNAL] = { 0 };
249 // TODO get rid of this as this is unnecessary duplication...
250 static gchar *tool_names[NUMBER_OF_TOOLS] = { N_("Pan"), N_("Zoom"), N_("Ruler"), N_("Select") };
252 G_DEFINE_TYPE (VikWindow, vik_window, GTK_TYPE_WINDOW)
254 VikViewport * vik_window_viewport(VikWindow *vw)
256 return(vw->viking_vvp);
259 VikLayersPanel * vik_window_layers_panel(VikWindow *vw)
261 return(vw->viking_vlp);
265 * Returns the statusbar for the window
267 VikStatusbar * vik_window_get_statusbar ( VikWindow *vw )
269 return vw->viking_vs;
273 * Returns the 'project' filename
275 const gchar *vik_window_get_filename (VikWindow *vw)
282 vik_statusbar_type_t vs_type;
283 gchar* message; // Always make a copy of this data
284 } statusbar_idle_data;
287 * For the actual statusbar update!
289 static gboolean statusbar_idle_update ( statusbar_idle_data *sid )
291 vik_statusbar_set_message ( sid->vs, sid->vs_type, sid->message );
292 g_free ( sid->message );
298 * vik_window_statusbar_update:
299 * @vw: The main window in which the statusbar will be updated.
300 * @message: The string to be displayed. This is copied.
301 * @vs_type: The part of the statusbar to be updated.
303 * This updates any part of the statusbar with the new string.
304 * It handles calling from the main thread or any background thread
305 * ATM this mostly used from background threads - as from the main thread
306 * one may use the vik_statusbar_set_message() directly.
308 void vik_window_statusbar_update ( VikWindow *vw, const gchar* message, vik_statusbar_type_t vs_type )
310 GThread *thread = vik_window_get_thread ( vw );
315 statusbar_idle_data *sid = g_malloc ( sizeof (statusbar_idle_data) );
316 sid->vs = vw->viking_vs;
317 sid->vs_type = vs_type;
318 sid->message = g_strdup ( message );
320 if ( g_thread_self() == thread ) {
321 g_idle_add ( (GSourceFunc) statusbar_idle_update, sid );
324 // From a background thread
325 gdk_threads_add_idle ( (GSourceFunc) statusbar_idle_update, sid );
329 // Actual signal handlers
330 static void destroy_window ( GtkWidget *widget,
333 g_debug ( "%s", __FUNCTION__ );
334 if ( ! --window_count ) {
335 g_free ( last_folder_files_uri );
336 g_free ( last_folder_images_uri );
341 #define VIK_SETTINGS_WIN_SIDEPANEL "window_sidepanel"
342 #define VIK_SETTINGS_WIN_STATUSBAR "window_statusbar"
343 #define VIK_SETTINGS_WIN_TOOLBAR "window_toolbar"
344 // Menubar setting to off is never auto saved in case it's accidentally turned off
345 // It's not so obvious so to recover the menu visibility.
346 // Thus this value is for setting manually via editting the settings file directly
347 #define VIK_SETTINGS_WIN_MENUBAR "window_menubar"
349 VikWindow *vik_window_new_window ()
351 if ( window_count < MAX_WINDOWS )
353 VikWindow *vw = window_new ();
355 if ( window_count == 0 ) {
356 vik_window_statusbar_update ( vw, _("This is Viking "VIKING_VERSION), VIK_STATUSBAR_INFO );
359 g_signal_connect (G_OBJECT (vw), "destroy",
360 G_CALLBACK (destroy_window), NULL);
361 g_signal_connect (G_OBJECT (vw), "newwindow",
362 G_CALLBACK (vik_window_new_window), NULL);
363 g_signal_connect (G_OBJECT (vw), "openwindow",
364 G_CALLBACK (open_window), NULL);
366 gtk_widget_show_all ( GTK_WIDGET(vw) );
368 if ( a_vik_get_restore_window_state() ) {
369 // These settings are applied after the show all as these options hide widgets
371 if ( a_settings_get_boolean ( VIK_SETTINGS_WIN_SIDEPANEL, &sidepanel ) )
373 gtk_widget_hide ( GTK_WIDGET(vw->viking_vlp) );
374 GtkWidget *check_box = gtk_ui_manager_get_widget ( vw->uim, "/ui/MainMenu/View/SetShow/ViewSidePanel" );
375 gtk_check_menu_item_set_active ( GTK_CHECK_MENU_ITEM(check_box), FALSE );
379 if ( a_settings_get_boolean ( VIK_SETTINGS_WIN_STATUSBAR, &statusbar ) )
381 gtk_widget_hide ( GTK_WIDGET(vw->viking_vs) );
382 GtkWidget *check_box = gtk_ui_manager_get_widget ( vw->uim, "/ui/MainMenu/View/SetShow/ViewStatusBar" );
383 gtk_check_menu_item_set_active ( GTK_CHECK_MENU_ITEM(check_box), FALSE );
387 if ( a_settings_get_boolean ( VIK_SETTINGS_WIN_TOOLBAR, &toolbar ) )
389 gtk_widget_hide ( toolbar_get_widget (vw->viking_vtb) );
390 GtkWidget *check_box = gtk_ui_manager_get_widget ( vw->uim, "/ui/MainMenu/View/SetShow/ViewToolBar" );
391 gtk_check_menu_item_set_active ( GTK_CHECK_MENU_ITEM(check_box), FALSE );
395 if ( a_settings_get_boolean ( VIK_SETTINGS_WIN_MENUBAR, &menubar ) )
397 gtk_widget_hide ( gtk_ui_manager_get_widget ( vw->uim, "/ui/MainMenu" ) );
398 GtkWidget *check_box = gtk_ui_manager_get_widget ( vw->uim, "/ui/MainMenu/View/SetShow/ViewMainMenu" );
399 gtk_check_menu_item_set_active ( GTK_CHECK_MENU_ITEM(check_box), FALSE );
410 * determine_location_thread:
411 * @vw: The window that will get updated
412 * @threaddata: Data used by our background thread mechanism
414 * Use the features in vikgoto to determine where we are
415 * Then set up the viewport:
416 * 1. To goto the location
417 * 2. Set an appropriate level zoom for the location type
418 * 3. Some statusbar message feedback
420 static int determine_location_thread ( VikWindow *vw, gpointer threaddata )
424 gint ans = a_vik_goto_where_am_i ( vw->viking_vvp, &ll, &name );
426 int result = a_background_thread_progress ( threaddata, 1.0 );
428 vik_window_statusbar_update ( vw, _("Location lookup aborted"), VIK_STATUSBAR_INFO );
429 return -1; /* Abort thread */
437 // Position found with city precision - so zoom out more
440 else if ( ans == 3 ) {
441 // Position found via country name search - so zoom wayyyy out
445 vik_viewport_set_zoom ( vw->viking_vvp, zoom );
446 vik_viewport_set_center_latlon ( vw->viking_vvp, &ll, FALSE );
448 gchar *message = g_strdup_printf ( _("Location found: %s"), name );
449 vik_window_statusbar_update ( vw, message, VIK_STATUSBAR_INFO );
453 // Signal to redraw from the background
454 vik_layers_panel_emit_update ( vw->viking_vlp );
457 vik_window_statusbar_update ( vw, _("Unable to determine location"), VIK_STATUSBAR_INFO );
463 * Steps to be taken once initial loading has completed
465 void vik_window_new_window_finish ( VikWindow *vw )
467 // Don't add a map if we've loaded a Viking file already
471 // Maybe add a default map layer
472 if ( a_vik_get_add_default_map_layer () ) {
473 VikMapsLayer *vml = VIK_MAPS_LAYER ( vik_layer_create(VIK_LAYER_MAPS, vw->viking_vvp, FALSE) );
474 vik_layer_rename ( VIK_LAYER(vml), _("Default Map") );
475 vik_aggregate_layer_add_layer ( vik_layers_panel_get_top_layer(vw->viking_vlp), VIK_LAYER(vml), TRUE );
480 // If not loaded any file, maybe try the location lookup
481 if ( vw->loaded_type == LOAD_TYPE_READ_FAILURE ) {
482 if ( a_vik_get_startup_method ( ) == VIK_STARTUP_METHOD_AUTO_LOCATION ) {
484 vik_statusbar_set_message ( vw->viking_vs, VIK_STATUSBAR_INFO, _("Trying to determine location...") );
486 a_background_thread ( BACKGROUND_POOL_REMOTE,
488 _("Determining location"),
489 (vik_thr_func) determine_location_thread,
498 static void open_window ( VikWindow *vw, GSList *files )
502 gboolean change_fn = (g_slist_length(files) == 1); /* only change fn if one file */
503 GSList *cur_file = files;
505 // Only open a new window if a viking file
506 gchar *file_name = cur_file->data;
507 if (vw->filename && check_file_magic_vik ( file_name ) ) {
508 VikWindow *newvw = vik_window_new_window ();
510 vik_window_open_file ( newvw, file_name, TRUE );
513 vik_window_open_file ( vw, file_name, change_fn );
516 cur_file = g_slist_next (cur_file);
518 g_slist_free (files);
522 void vik_window_selected_layer(VikWindow *vw, VikLayer *vl)
524 int i, j, tool_count;
525 VikLayerInterface *layer_interface;
527 if (!vw->action_group) return;
529 for (i=0; i<VIK_LAYER_NUM_TYPES; i++) {
531 layer_interface = vik_layer_get_interface(i);
532 tool_count = layer_interface->tools_count;
534 for (j = 0; j < tool_count; j++) {
535 action = gtk_action_group_get_action(vw->action_group,
536 layer_interface->tools[j].radioActionEntry.name);
537 g_object_set(action, "sensitive", i == vl->type, NULL);
538 toolbar_action_set_sensitive ( vw->viking_vtb, vik_layer_get_interface(i)->tools[j].radioActionEntry.name, i == vl->type );
543 static void window_finalize ( GObject *gob )
545 VikWindow *vw = VIK_WINDOW(gob);
546 g_return_if_fail ( vw != NULL );
548 a_background_remove_window ( vw );
550 window_list = g_slist_remove ( window_list, vw );
552 gdk_cursor_unref ( vw->busy_cursor );
554 for (tt = 0; tt < vw->vt->n_tools; tt++ )
555 if ( vw->vt->tools[tt].ti.destroy )
556 vw->vt->tools[tt].ti.destroy ( vw->vt->tools[tt].state );
557 g_free ( vw->vt->tools );
560 vik_toolbar_finalize ( vw->viking_vtb );
562 G_OBJECT_CLASS(parent_class)->finalize(gob);
566 static void vik_window_class_init ( VikWindowClass *klass )
569 GObjectClass *object_class;
571 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);
572 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);
574 object_class = G_OBJECT_CLASS (klass);
576 object_class->finalize = window_finalize;
578 parent_class = g_type_class_peek_parent (klass);
582 static void zoom_changed (GtkMenuShell *menushell,
585 VikWindow *vw = VIK_WINDOW (user_data);
587 GtkWidget *aw = gtk_menu_get_active ( GTK_MENU (menushell) );
588 gint active = GPOINTER_TO_INT(g_object_get_data ( G_OBJECT (aw), "position" ));
590 gdouble zoom_request = pow (2, active-5 );
592 // But has it really changed?
593 gdouble current_zoom = vik_viewport_get_zoom ( vw->viking_vvp );
594 if ( current_zoom != 0.0 && zoom_request != current_zoom ) {
595 vik_viewport_set_zoom ( vw->viking_vvp, zoom_request );
596 // Force drawing update
602 * @mpp: The initial zoom level
604 static GtkWidget *create_zoom_menu_all_levels ( gdouble mpp )
606 GtkWidget *menu = gtk_menu_new ();
607 char *itemLabels[] = { "0.031", "0.063", "0.125", "0.25", "0.5", "1", "2", "4", "8", "16", "32", "64", "128", "256", "512", "1024", "2048", "4096", "8192", "16384", "32768" };
610 for (i = 0 ; i < G_N_ELEMENTS(itemLabels) ; i++)
612 GtkWidget *item = gtk_menu_item_new_with_label (itemLabels[i]);
613 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
614 gtk_widget_show (item);
615 g_object_set_data (G_OBJECT (item), "position", GINT_TO_POINTER(i));
618 gint active = 5 + round ( log (mpp) / log (2) );
619 // Ensure value derived from mpp is in bounds of the menu
620 if ( active >= G_N_ELEMENTS(itemLabels) )
621 active = G_N_ELEMENTS(itemLabels) - 1;
624 gtk_menu_set_active ( GTK_MENU(menu), active );
629 static GtkWidget *create_zoom_combo_all_levels ()
631 GtkWidget *combo = vik_combo_box_text_new();
632 vik_combo_box_text_append ( combo, "0.25");
633 vik_combo_box_text_append ( combo, "0.5");
634 vik_combo_box_text_append ( combo, "1");
635 vik_combo_box_text_append ( combo, "2");
636 vik_combo_box_text_append ( combo, "4");
637 vik_combo_box_text_append ( combo, "8");
638 vik_combo_box_text_append ( combo, "16");
639 vik_combo_box_text_append ( combo, "32");
640 vik_combo_box_text_append ( combo, "64");
641 vik_combo_box_text_append ( combo, "128");
642 vik_combo_box_text_append ( combo, "256");
643 vik_combo_box_text_append ( combo, "512");
644 vik_combo_box_text_append ( combo, "1024");
645 vik_combo_box_text_append ( combo, "2048");
646 vik_combo_box_text_append ( combo, "4096");
647 vik_combo_box_text_append ( combo, "8192");
648 vik_combo_box_text_append ( combo, "16384");
649 vik_combo_box_text_append ( combo, "32768");
651 gtk_widget_set_tooltip_text (combo, _("Select zoom level"));
655 static gint zoom_popup_handler (GtkWidget *widget)
659 g_return_val_if_fail (widget != NULL, FALSE);
660 g_return_val_if_fail (GTK_IS_MENU (widget), FALSE);
662 /* The "widget" is the menu that was supplied when
663 * g_signal_connect_swapped() was called.
665 menu = GTK_MENU (widget);
667 gtk_menu_popup (menu, NULL, NULL, NULL, NULL,
668 1, gtk_get_current_event_time());
676 static void drag_data_received_cb ( GtkWidget *widget,
677 GdkDragContext *context,
680 GtkSelectionData *selection_data,
685 gboolean success = FALSE;
687 if ( (selection_data != NULL) && (gtk_selection_data_get_length(selection_data) > 0) ) {
688 switch (target_type) {
690 gchar *str = (gchar*)gtk_selection_data_get_data(selection_data);
691 g_debug ("drag received string:%s \n", str);
693 // Convert string into GSList of individual entries for use with our open signal
694 gchar **entries = g_strsplit(str, "\r\n", 0);
695 GSList *filenames = NULL;
696 gint entry_runner = 0;
697 gchar *entry = entries[entry_runner];
699 if ( g_strcmp0 ( entry, "" ) ) {
700 // Drag+Drop gives URIs. And so in particular, %20 in place of spaces in filenames
701 // thus need to convert the text into a plain string
702 gchar *filename = g_filename_from_uri ( entry, NULL, NULL );
704 filenames = g_slist_append ( filenames, filename );
707 entry = entries[entry_runner];
711 g_signal_emit ( G_OBJECT(VIK_WINDOW_FROM_WIDGET(widget)), window_signals[VW_OPENWINDOW_SIGNAL], 0, filenames );
712 // NB: GSList & contents are freed by main.open_window
721 gtk_drag_finish ( context, success, FALSE, time );
724 static void toolbar_tool_cb ( GtkAction *old, GtkAction *current, gpointer gp )
726 VikWindow *vw = (VikWindow*)gp;
727 GtkAction *action = gtk_action_group_get_action ( vw->action_group, gtk_action_get_name(current) );
729 gtk_action_activate ( action );
732 static void toolbar_reload_cb ( GtkActionGroup *grp, gpointer gp )
734 VikWindow *vw = (VikWindow*)gp;
735 center_changed_cb ( vw );
738 #define VIK_SETTINGS_WIN_MAX "window_maximized"
739 #define VIK_SETTINGS_WIN_FULLSCREEN "window_fullscreen"
740 #define VIK_SETTINGS_WIN_WIDTH "window_width"
741 #define VIK_SETTINGS_WIN_HEIGHT "window_height"
742 #define VIK_SETTINGS_WIN_PANE_POSITION "window_horizontal_pane_position"
743 #define VIK_SETTINGS_WIN_SAVE_IMAGE_WIDTH "window_save_image_width"
744 #define VIK_SETTINGS_WIN_SAVE_IMAGE_HEIGHT "window_save_image_height"
745 #define VIK_SETTINGS_WIN_SAVE_IMAGE_PNG "window_save_image_as_png"
746 #define VIK_SETTINGS_WIN_COPY_CENTRE_FULL_FORMAT "window_copy_centre_full_format"
748 #define VIKING_ACCELERATOR_KEY_FILE "keys.rc"
750 static void vik_window_init ( VikWindow *vw )
752 vw->action_group = NULL;
754 vw->viking_vvp = vik_viewport_new();
755 vw->viking_vlp = vik_layers_panel_new();
756 vik_layers_panel_set_viewport ( vw->viking_vlp, vw->viking_vvp );
757 vw->viking_vs = vik_statusbar_new();
759 vw->vt = toolbox_create(vw);
760 vw->viking_vtb = vik_toolbar_new ();
761 window_create_ui(vw);
762 window_set_filename (vw, NULL);
764 vw->busy_cursor = gdk_cursor_new ( GDK_WATCH );
767 vw->loaded_type = LOAD_TYPE_READ_FAILURE; //AKA none
768 vw->modified = FALSE;
769 vw->only_updating_coord_mode_ui = FALSE;
771 vw->select_move = FALSE;
772 vw->pan_move = FALSE;
773 vw->pan_x = vw->pan_y = -1;
774 vw->single_click_pending = FALSE;
776 gint draw_image_width;
777 if ( a_settings_get_integer ( VIK_SETTINGS_WIN_SAVE_IMAGE_WIDTH, &draw_image_width ) )
778 vw->draw_image_width = draw_image_width;
780 vw->draw_image_width = DRAW_IMAGE_DEFAULT_WIDTH;
781 gint draw_image_height;
782 if ( a_settings_get_integer ( VIK_SETTINGS_WIN_SAVE_IMAGE_HEIGHT, &draw_image_height ) )
783 vw->draw_image_height = draw_image_height;
785 vw->draw_image_height = DRAW_IMAGE_DEFAULT_HEIGHT;
786 gboolean draw_image_save_as_png;
787 if ( a_settings_get_boolean ( VIK_SETTINGS_WIN_SAVE_IMAGE_PNG, &draw_image_save_as_png ) )
788 vw->draw_image_save_as_png = draw_image_save_as_png;
790 vw->draw_image_save_as_png = DRAW_IMAGE_DEFAULT_SAVE_AS_PNG;
792 vw->main_vbox = gtk_vbox_new(FALSE, 1);
793 gtk_container_add (GTK_CONTAINER (vw), vw->main_vbox);
794 vw->menu_hbox = gtk_hbox_new(FALSE, 1);
795 GtkWidget *menu_bar = gtk_ui_manager_get_widget (vw->uim, "/MainMenu");
796 gtk_box_pack_start (GTK_BOX(vw->menu_hbox), menu_bar, FALSE, TRUE, 0);
797 gtk_box_pack_start (GTK_BOX(vw->main_vbox), vw->menu_hbox, FALSE, TRUE, 0);
799 toolbar_init(vw->viking_vtb,
805 (gpointer)vw); // This auto packs toolbar into the vbox
806 // Must be performed post toolbar init
808 for (i=0; i<VIK_LAYER_NUM_TYPES; i++) {
809 for ( j = 0; j < vik_layer_get_interface(i)->tools_count; j++ ) {
810 toolbar_action_set_sensitive ( vw->viking_vtb, vik_layer_get_interface(i)->tools[j].radioActionEntry.name, FALSE );
814 vik_ext_tool_datasources_add_menu_items ( vw, vw->uim );
816 GtkWidget * zoom_levels = gtk_ui_manager_get_widget (vw->uim, "/MainMenu/View/SetZoom");
817 GtkWidget * zoom_levels_menu = create_zoom_menu_all_levels ( vik_viewport_get_zoom(vw->viking_vvp) );
818 gtk_menu_item_set_submenu (GTK_MENU_ITEM (zoom_levels), zoom_levels_menu);
819 g_signal_connect ( G_OBJECT(zoom_levels_menu), "selection-done", G_CALLBACK(zoom_changed), vw);
820 g_signal_connect_swapped ( G_OBJECT(vw->viking_vs), "clicked", G_CALLBACK(zoom_popup_handler), zoom_levels_menu );
822 g_signal_connect (G_OBJECT (vw), "delete_event", G_CALLBACK (delete_event), NULL);
825 g_signal_connect_swapped (G_OBJECT(vw->viking_vvp), "updated_center", G_CALLBACK(center_changed_cb), vw);
827 g_signal_connect_swapped (G_OBJECT(vw->viking_vvp), "expose_event", G_CALLBACK(draw_sync), vw);
828 g_signal_connect_swapped (G_OBJECT(vw->viking_vvp), "configure_event", G_CALLBACK(window_configure_event), vw);
829 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 );
830 g_signal_connect_swapped (G_OBJECT(vw->viking_vvp), "scroll_event", G_CALLBACK(draw_scroll), vw);
831 g_signal_connect_swapped (G_OBJECT(vw->viking_vvp), "button_press_event", G_CALLBACK(draw_click), vw);
832 g_signal_connect_swapped (G_OBJECT(vw->viking_vvp), "button_release_event", G_CALLBACK(draw_release), vw);
833 g_signal_connect_swapped (G_OBJECT(vw->viking_vvp), "motion_notify_event", G_CALLBACK(draw_mouse_motion), vw);
835 g_signal_connect_swapped (G_OBJECT(vw->viking_vlp), "update", G_CALLBACK(draw_update), vw);
836 g_signal_connect_swapped (G_OBJECT(vw->viking_vlp), "delete_layer", G_CALLBACK(vik_window_clear_highlight), vw);
838 // Allow key presses to be processed anywhere
839 g_signal_connect_swapped (G_OBJECT (vw), "key_press_event", G_CALLBACK (key_press_event), vw);
841 // Set initial button sensitivity
842 center_changed_cb ( vw );
844 vw->hpaned = gtk_hpaned_new ();
845 gtk_paned_pack1 ( GTK_PANED(vw->hpaned), GTK_WIDGET (vw->viking_vlp), FALSE, TRUE );
846 gtk_paned_pack2 ( GTK_PANED(vw->hpaned), GTK_WIDGET (vw->viking_vvp), TRUE, TRUE );
848 /* This packs the button into the window (a gtk container). */
849 gtk_box_pack_start (GTK_BOX(vw->main_vbox), vw->hpaned, TRUE, TRUE, 0);
851 gtk_box_pack_end (GTK_BOX(vw->main_vbox), GTK_WIDGET(vw->viking_vs), FALSE, TRUE, 0);
853 a_background_add_window ( vw );
855 window_list = g_slist_prepend ( window_list, vw);
857 gint height = VIKING_WINDOW_HEIGHT;
858 gint width = VIKING_WINDOW_WIDTH;
860 if ( a_vik_get_restore_window_state() ) {
861 if ( a_settings_get_integer ( VIK_SETTINGS_WIN_HEIGHT, &height ) ) {
862 // Enforce a basic minimum size
867 // No setting - so use default
868 height = VIKING_WINDOW_HEIGHT;
870 if ( a_settings_get_integer ( VIK_SETTINGS_WIN_WIDTH, &width ) ) {
871 // Enforce a basic minimum size
876 // No setting - so use default
877 width = VIKING_WINDOW_WIDTH;
880 if ( a_settings_get_boolean ( VIK_SETTINGS_WIN_MAX, &maxed ) )
882 gtk_window_maximize ( GTK_WINDOW(vw) );
885 if ( a_settings_get_boolean ( VIK_SETTINGS_WIN_FULLSCREEN, &full ) ) {
887 vw->show_full_screen = TRUE;
888 gtk_window_fullscreen ( GTK_WINDOW(vw) );
889 GtkWidget *check_box = gtk_ui_manager_get_widget ( vw->uim, "/ui/MainMenu/View/FullScreen" );
891 gtk_check_menu_item_set_active ( GTK_CHECK_MENU_ITEM(check_box), TRUE );
895 gint position = -1; // Let GTK determine default positioning
896 if ( !a_settings_get_integer ( VIK_SETTINGS_WIN_PANE_POSITION, &position ) ) {
899 gtk_paned_set_position ( GTK_PANED(vw->hpaned), position );
902 gtk_window_set_default_size ( GTK_WINDOW(vw), width, height );
904 vw->show_side_panel = TRUE;
905 vw->show_statusbar = TRUE;
906 vw->show_toolbar = TRUE;
907 vw->show_main_menu = TRUE;
909 // Only accept Drag and Drop of files onto the viewport
910 gtk_drag_dest_set ( GTK_WIDGET(vw->viking_vvp), GTK_DEST_DEFAULT_ALL, NULL, 0, GDK_ACTION_COPY );
911 gtk_drag_dest_add_uri_targets ( GTK_WIDGET(vw->viking_vvp) );
912 g_signal_connect ( GTK_WIDGET(vw->viking_vvp), "drag-data-received", G_CALLBACK(drag_data_received_cb), NULL );
914 // Store the thread value so comparisons can be made to determine the gdk update method
915 // Hopefully we are storing the main thread value here :)
916 // [ATM any window initialization is always be performed by the main thread]
917 vw->thread = g_thread_self();
919 // Set the default tool + mode
920 gtk_action_activate ( gtk_action_group_get_action ( vw->action_group, "Pan" ) );
921 gtk_action_activate ( gtk_action_group_get_action ( vw->action_group, "ModeMercator" ) );
923 gchar *accel_file_name = g_build_filename ( a_get_viking_dir(), VIKING_ACCELERATOR_KEY_FILE, NULL );
924 gtk_accel_map_load ( accel_file_name );
925 g_free ( accel_file_name );
928 static VikWindow *window_new ()
930 return VIK_WINDOW ( g_object_new ( VIK_WINDOW_TYPE, NULL ) );
934 * Update the displayed map
935 * Only update the top most visible map layer
936 * ATM this assumes (as per defaults) the top most map has full alpha setting
937 * such that other other maps even though they may be active will not be seen
938 * It's more complicated to work out which maps are actually visible due to alpha settings
939 * and overkill for this simple refresh method.
941 static void simple_map_update ( VikWindow *vw, gboolean only_new )
943 // Find the most relevent single map layer to operate on
944 VikLayer *vl = vik_aggregate_layer_get_top_visible_layer_of_type (vik_layers_panel_get_top_layer(vw->viking_vlp), VIK_LAYER_MAPS);
946 vik_maps_layer_download ( VIK_MAPS_LAYER(vl), vw->viking_vvp, only_new );
950 * This is the global key press handler
951 * Global shortcuts are available at any time and hence are not restricted to when a certain tool is enabled
953 static gboolean key_press_event( VikWindow *vw, GdkEventKey *event, gpointer data )
955 // The keys handled here are not in the menuing system for a couple of reasons:
956 // . Keeps the menu size compact (alebit at expense of discoverably)
957 // . Allows differing key bindings to perform the same actions
959 // First decide if key events are related to the maps layer
960 gboolean map_download = FALSE;
961 gboolean map_download_only_new = TRUE; // Only new or reload
963 GdkModifierType modifiers = gtk_accelerator_get_default_mod_mask();
965 // Standard 'Refresh' keys: F5 or Ctrl+r
966 // Note 'F5' is actually handled via draw_refresh_cb() later on
967 // (not 'R' it's 'r' notice the case difference!!)
968 if ( event->keyval == GDK_r && (event->state & modifiers) == GDK_CONTROL_MASK ) {
970 map_download_only_new = TRUE;
972 // Full cache reload with Ctrl+F5 or Ctrl+Shift+r [This is not in the menu system]
973 // Note the use of uppercase R here since shift key has been pressed
974 else if ( (event->keyval == GDK_F5 && (event->state & modifiers) == GDK_CONTROL_MASK ) ||
975 ( event->keyval == GDK_R && (event->state & modifiers) == (GDK_CONTROL_MASK + GDK_SHIFT_MASK) ) ) {
977 map_download_only_new = FALSE;
979 // Standard Ctrl+KP+ / Ctrl+KP- to zoom in/out respectively
980 else if ( event->keyval == GDK_KEY_KP_Add && (event->state & modifiers) == GDK_CONTROL_MASK ) {
981 vik_viewport_zoom_in ( vw->viking_vvp );
983 return TRUE; // handled keypress
985 else if ( event->keyval == GDK_KEY_KP_Subtract && (event->state & modifiers) == GDK_CONTROL_MASK ) {
986 vik_viewport_zoom_out ( vw->viking_vvp );
988 return TRUE; // handled keypress
991 if ( map_download ) {
992 simple_map_update ( vw, map_download_only_new );
993 return TRUE; // handled keypress
996 VikLayer *vl = vik_layers_panel_get_selected ( vw->viking_vlp );
997 if (vl && vw->vt->active_tool != -1 && vw->vt->tools[vw->vt->active_tool].ti.key_press ) {
998 gint ltype = vw->vt->tools[vw->vt->active_tool].layer_type;
999 if ( vl && ltype == vl->type )
1000 return vw->vt->tools[vw->vt->active_tool].ti.key_press(vl, event, vw->vt->tools[vw->vt->active_tool].state);
1003 // Ensure called only on window tools (i.e. not on any of the Layer tools since the layer is NULL)
1004 if ( vw->current_tool < TOOL_LAYER ) {
1005 // No layer - but enable window tool keypress processing - these should be able to handle a NULL layer
1006 if ( vw->vt->tools[vw->vt->active_tool].ti.key_press ) {
1007 return vw->vt->tools[vw->vt->active_tool].ti.key_press ( vl, event, vw->vt->tools[vw->vt->active_tool].state );
1011 /* Restore Main Menu via Escape key if the user has hidden it */
1012 /* This key is more likely to be used as they may not remember the function key */
1013 if ( event->keyval == GDK_Escape ) {
1014 GtkWidget *check_box = gtk_ui_manager_get_widget ( vw->uim, "/ui/MainMenu/View/SetShow/ViewMainMenu" );
1016 gboolean state = gtk_check_menu_item_get_active ( GTK_CHECK_MENU_ITEM(check_box) );
1018 gtk_widget_show ( gtk_ui_manager_get_widget ( vw->uim, "/ui/MainMenu" ) );
1019 gtk_check_menu_item_set_active ( GTK_CHECK_MENU_ITEM(check_box), TRUE );
1020 return TRUE; /* handled keypress */
1025 return FALSE; /* don't handle the keypress */
1028 static gboolean delete_event( VikWindow *vw )
1030 #ifdef VIKING_PROMPT_IF_MODIFIED
1037 dia = GTK_DIALOG ( gtk_message_dialog_new ( GTK_WINDOW(vw), GTK_DIALOG_DESTROY_WITH_PARENT, GTK_MESSAGE_QUESTION, GTK_BUTTONS_NONE,
1038 _("Do you want to save the changes you made to the document \"%s\"?\n"
1040 "Your changes will be lost if you don't save them."),
1041 window_get_filename ( vw ) ) );
1042 gtk_dialog_add_buttons ( dia, _("Don't Save"), GTK_RESPONSE_NO, GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, GTK_STOCK_SAVE, GTK_RESPONSE_YES, NULL );
1043 switch ( gtk_dialog_run ( dia ) )
1045 case GTK_RESPONSE_NO: gtk_widget_destroy ( GTK_WIDGET(dia) ); return FALSE;
1046 case GTK_RESPONSE_CANCEL: gtk_widget_destroy ( GTK_WIDGET(dia) ); return TRUE;
1047 default: gtk_widget_destroy ( GTK_WIDGET(dia) ); return ! save_file(NULL, vw);
1051 if ( window_count == 1 ) {
1052 // On the final window close - save latest state - if it's wanted...
1053 if ( a_vik_get_restore_window_state() ) {
1054 gint state = gdk_window_get_state ( GTK_WIDGET(vw)->window );
1055 gboolean state_max = state & GDK_WINDOW_STATE_MAXIMIZED;
1056 a_settings_set_boolean ( VIK_SETTINGS_WIN_MAX, state_max );
1058 gboolean state_fullscreen = state & GDK_WINDOW_STATE_FULLSCREEN;
1059 a_settings_set_boolean ( VIK_SETTINGS_WIN_FULLSCREEN, state_fullscreen );
1061 a_settings_set_boolean ( VIK_SETTINGS_WIN_SIDEPANEL, GTK_WIDGET_VISIBLE (GTK_WIDGET(vw->viking_vlp)) );
1063 a_settings_set_boolean ( VIK_SETTINGS_WIN_STATUSBAR, GTK_WIDGET_VISIBLE (GTK_WIDGET(vw->viking_vs)) );
1065 a_settings_set_boolean ( VIK_SETTINGS_WIN_TOOLBAR, GTK_WIDGET_VISIBLE (toolbar_get_widget(vw->viking_vtb)) );
1067 // If supersized - no need to save the enlarged width+height values
1068 if ( ! (state_fullscreen || state_max) ) {
1070 gtk_window_get_size ( GTK_WINDOW (vw), &width, &height );
1071 a_settings_set_integer ( VIK_SETTINGS_WIN_WIDTH, width );
1072 a_settings_set_integer ( VIK_SETTINGS_WIN_HEIGHT, height );
1075 a_settings_set_integer ( VIK_SETTINGS_WIN_PANE_POSITION, gtk_paned_get_position (GTK_PANED(vw->hpaned)) );
1078 a_settings_set_integer ( VIK_SETTINGS_WIN_SAVE_IMAGE_WIDTH, vw->draw_image_width );
1079 a_settings_set_integer ( VIK_SETTINGS_WIN_SAVE_IMAGE_HEIGHT, vw->draw_image_height );
1080 a_settings_set_boolean ( VIK_SETTINGS_WIN_SAVE_IMAGE_PNG, vw->draw_image_save_as_png );
1082 gchar *accel_file_name = g_build_filename ( a_get_viking_dir(), VIKING_ACCELERATOR_KEY_FILE, NULL );
1083 gtk_accel_map_save ( accel_file_name );
1084 g_free ( accel_file_name );
1091 static void newwindow_cb ( GtkAction *a, VikWindow *vw )
1093 g_signal_emit ( G_OBJECT(vw), window_signals[VW_NEWWINDOW_SIGNAL], 0 );
1096 static void draw_update ( VikWindow *vw )
1102 static void draw_sync ( VikWindow *vw )
1104 vik_viewport_sync(vw->viking_vvp);
1109 * Split the status update, as sometimes only need to update the tool part
1110 * also on initialization the zoom related stuff is not ready to be used
1112 static void draw_status_tool ( VikWindow *vw )
1114 if ( vw->current_tool == TOOL_LAYER )
1115 // Use tooltip rather than the internal name as the tooltip is i8n
1116 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 );
1118 vik_statusbar_set_message ( vw->viking_vs, VIK_STATUSBAR_TOOL, _(tool_names[vw->current_tool]) );
1121 static void draw_status ( VikWindow *vw )
1123 static gchar zoom_level[22];
1124 gdouble xmpp = vik_viewport_get_xmpp (vw->viking_vvp);
1125 gdouble ympp = vik_viewport_get_ympp(vw->viking_vvp);
1126 gchar *unit = vik_viewport_get_coord_mode(vw->viking_vvp) == VIK_COORD_UTM ? _("mpp") : _("pixelfact");
1128 g_snprintf ( zoom_level, 22, "%.3f/%.3f %s", xmpp, ympp, unit );
1130 if ( (int)xmpp - xmpp < 0.0 )
1131 g_snprintf ( zoom_level, 22, "%.3f %s", xmpp, unit );
1133 /* xmpp should be a whole number so don't show useless .000 bit */
1134 g_snprintf ( zoom_level, 22, "%d %s", (int)xmpp, unit );
1136 vik_statusbar_set_message ( vw->viking_vs, VIK_STATUSBAR_ZOOM, zoom_level );
1138 draw_status_tool ( vw );
1141 void vik_window_set_redraw_trigger(VikLayer *vl)
1143 VikWindow *vw = VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vl));
1148 static void window_configure_event ( VikWindow *vw )
1150 static int first = 1;
1153 // This is a hack to set the cursor corresponding to the first tool
1154 // FIXME find the correct way to initialize both tool and its cursor
1156 vw->viewport_cursor = (GdkCursor *)toolbox_get_cursor(vw->vt, "Pan");
1157 /* We set cursor, even if it is NULL: it resets to default */
1158 gdk_window_set_cursor ( gtk_widget_get_window(GTK_WIDGET(vw->viking_vvp)), vw->viewport_cursor );
1162 static void draw_redraw ( VikWindow *vw )
1164 VikCoord old_center = vw->trigger_center;
1165 vw->trigger_center = *(vik_viewport_get_center(vw->viking_vvp));
1166 VikLayer *new_trigger = vw->trigger;
1168 VikLayer *old_trigger = VIK_LAYER(vik_viewport_get_trigger(vw->viking_vvp));
1170 if ( ! new_trigger )
1171 ; /* do nothing -- have to redraw everything. */
1172 else if ( (old_trigger != new_trigger) || !vik_coord_equals(&old_center, &vw->trigger_center) || (new_trigger->type == VIK_LAYER_AGGREGATE) )
1173 vik_viewport_set_trigger ( vw->viking_vvp, new_trigger ); /* todo: set to half_drawn mode if new trigger is above old */
1175 vik_viewport_set_half_drawn ( vw->viking_vvp, TRUE );
1178 vik_viewport_clear ( vw->viking_vvp);
1179 // Main layer drawing
1180 vik_layers_panel_draw_all ( vw->viking_vlp );
1181 // Draw highlight (possibly again but ensures it is on top - especially for when tracks overlap)
1182 if ( vik_viewport_get_draw_highlight (vw->viking_vvp) ) {
1183 if ( vw->containing_vtl && (vw->selected_tracks || vw->selected_waypoints ) ) {
1184 vik_trw_layer_draw_highlight_items ( vw->containing_vtl, vw->selected_tracks, vw->selected_waypoints, vw->viking_vvp );
1186 else if ( vw->containing_vtl && (vw->selected_track || vw->selected_waypoint) ) {
1187 vik_trw_layer_draw_highlight_item ( vw->containing_vtl, vw->selected_track, vw->selected_waypoint, vw->viking_vvp );
1189 else if ( vw->selected_vtl ) {
1190 vik_trw_layer_draw_highlight ( vw->selected_vtl, vw->viking_vvp );
1193 // Other viewport decoration items on top if they are enabled/in use
1194 vik_viewport_draw_scale ( vw->viking_vvp );
1195 vik_viewport_draw_copyright ( vw->viking_vvp );
1196 vik_viewport_draw_centermark ( vw->viking_vvp );
1197 vik_viewport_draw_logo ( vw->viking_vvp );
1199 vik_viewport_set_half_drawn ( vw->viking_vvp, FALSE ); /* just in case. */
1202 gboolean draw_buf_done = TRUE;
1204 static gboolean draw_buf(gpointer data)
1206 gpointer *pass_along = data;
1207 gdk_draw_drawable (pass_along[0], pass_along[1],
1208 pass_along[2], 0, 0, 0, 0, -1, -1);
1209 draw_buf_done = TRUE;
1214 /* Mouse event handlers ************************************************************************/
1216 static void vik_window_pan_click (VikWindow *vw, GdkEventButton *event)
1218 /* set panning origin */
1219 vw->pan_move = FALSE;
1220 vw->pan_x = (gint) event->x;
1221 vw->pan_y = (gint) event->y;
1224 static void draw_click (VikWindow *vw, GdkEventButton *event)
1226 gtk_widget_grab_focus ( GTK_WIDGET(vw->viking_vvp) );
1228 /* middle button pressed. we reserve all middle button and scroll events
1229 * for panning and zooming; tools only get left/right/movement
1231 if ( event->button == 2) {
1232 if ( vw->vt->tools[vw->vt->active_tool].ti.pan_handler )
1233 // Tool still may need to do something (such as disable something)
1234 toolbox_click(vw->vt, event);
1235 vik_window_pan_click ( vw, event );
1238 toolbox_click(vw->vt, event);
1242 static void vik_window_pan_move (VikWindow *vw, GdkEventMotion *event)
1244 if ( vw->pan_x != -1 ) {
1245 vik_viewport_set_center_screen ( vw->viking_vvp, vik_viewport_get_width(vw->viking_vvp)/2 - event->x + vw->pan_x,
1246 vik_viewport_get_height(vw->viking_vvp)/2 - event->y + vw->pan_y );
1247 vw->pan_move = TRUE;
1248 vw->pan_x = event->x;
1249 vw->pan_y = event->y;
1255 * get_location_strings:
1257 * Utility function to get positional strings for the given location
1258 * lat and lon strings will get allocated and so need to be freed after use
1260 static void get_location_strings ( VikWindow *vw, struct UTM utm, gchar **lat, gchar **lon )
1262 if ( vik_viewport_get_drawmode ( vw->viking_vvp ) == VIK_VIEWPORT_DRAWMODE_UTM ) {
1263 // Reuse lat for the first part (Zone + N or S, and lon for the second part (easting and northing) of a UTM format:
1264 // ZONE[N|S] EASTING NORTHING
1265 *lat = g_malloc(4*sizeof(gchar));
1266 // NB zone is stored in a char but is an actual number
1267 g_snprintf (*lat, 4, "%d%c", utm.zone, utm.letter);
1268 *lon = g_malloc(16*sizeof(gchar));
1269 g_snprintf (*lon, 16, "%d %d", (gint)utm.easting, (gint)utm.northing);
1273 a_coords_utm_to_latlon ( &utm, &ll );
1274 a_coords_latlon_to_string ( &ll, lat, lon );
1278 static void draw_mouse_motion (VikWindow *vw, GdkEventMotion *event)
1280 static VikCoord coord;
1281 static struct UTM utm;
1282 #define BUFFER_SIZE 50
1283 static char pointer_buf[BUFFER_SIZE];
1284 gchar *lat = NULL, *lon = NULL;
1287 VikDemInterpol interpol_method;
1289 /* This is a hack, but work far the best, at least for single pointer systems.
1290 * See http://bugzilla.gnome.org/show_bug.cgi?id=587714 for more. */
1292 gdk_window_get_pointer (event->window, &x, &y, NULL);
1296 toolbox_move(vw->vt, event);
1298 vik_viewport_screen_to_coord ( vw->viking_vvp, event->x, event->y, &coord );
1299 vik_coord_to_utm ( &coord, &utm );
1301 get_location_strings ( vw, utm, &lat, &lon );
1303 /* Change interpolate method according to scale */
1304 zoom = vik_viewport_get_zoom(vw->viking_vvp);
1306 interpol_method = VIK_DEM_INTERPOL_NONE;
1307 else if (zoom >= 1.0)
1308 interpol_method = VIK_DEM_INTERPOL_SIMPLE;
1310 interpol_method = VIK_DEM_INTERPOL_BEST;
1311 if ((alt = a_dems_get_elev_by_coord(&coord, interpol_method)) != VIK_DEM_INVALID_ELEVATION) {
1312 if ( a_vik_get_units_height () == VIK_UNITS_HEIGHT_METRES )
1313 g_snprintf ( pointer_buf, BUFFER_SIZE, _("%s %s %dm"), lat, lon, alt );
1315 g_snprintf ( pointer_buf, BUFFER_SIZE, _("%s %s %dft"), lat, lon, (int)VIK_METERS_TO_FEET(alt) );
1318 g_snprintf ( pointer_buf, BUFFER_SIZE, _("%s %s"), lat, lon );
1323 vik_statusbar_set_message ( vw->viking_vs, VIK_STATUSBAR_POSITION, pointer_buf );
1325 vik_window_pan_move ( vw, event );
1327 /* This is recommended by the GTK+ documentation, but does not work properly.
1328 * Use deprecated way until GTK+ gets a solution for correct motion hint handling:
1329 * http://bugzilla.gnome.org/show_bug.cgi?id=587714
1331 /* gdk_event_request_motions ( event ); */
1335 * Action the single click after a small timeout
1336 * If a double click has occurred then this will do nothing
1338 static gboolean vik_window_pan_timeout (VikWindow *vw)
1340 if ( ! vw->single_click_pending ) {
1341 // Double click happened, so don't do anything
1345 /* set panning origin */
1346 vw->pan_move = FALSE;
1347 vw->single_click_pending = FALSE;
1348 vik_viewport_set_center_screen ( vw->viking_vvp, vw->delayed_pan_x, vw->delayed_pan_y );
1351 // Really turn off the pan moving!!
1352 vw->pan_x = vw->pan_y = -1;
1356 static void vik_window_pan_release ( VikWindow *vw, GdkEventButton *event )
1358 gboolean do_draw = TRUE;
1360 if ( vw->pan_move == FALSE ) {
1361 vw->single_click_pending = !vw->single_click_pending;
1363 if ( vw->single_click_pending ) {
1364 // Store offset to use
1365 vw->delayed_pan_x = vw->pan_x;
1366 vw->delayed_pan_y = vw->pan_y;
1367 // Get double click time
1368 GtkSettings *gs = gtk_widget_get_settings ( GTK_WIDGET(vw) );
1369 GValue dct = { 0 }; // = G_VALUE_INIT; // GLIB 2.30+ only
1370 g_value_init ( &dct, G_TYPE_INT );
1371 g_object_get_property ( G_OBJECT(gs), "gtk-double-click-time", &dct );
1372 // Give chance for a double click to occur
1373 gint timer = g_value_get_int ( &dct ) + 50;
1374 g_timeout_add ( timer, (GSourceFunc)vik_window_pan_timeout, vw );
1378 vik_viewport_set_center_screen ( vw->viking_vvp, vw->pan_x, vw->pan_y );
1382 vik_viewport_set_center_screen ( vw->viking_vvp, vik_viewport_get_width(vw->viking_vvp)/2 - event->x + vw->pan_x,
1383 vik_viewport_get_height(vw->viking_vvp)/2 - event->y + vw->pan_y );
1386 vw->pan_move = FALSE;
1387 vw->pan_x = vw->pan_y = -1;
1392 static void draw_release ( VikWindow *vw, GdkEventButton *event )
1394 gtk_widget_grab_focus ( GTK_WIDGET(vw->viking_vvp) );
1396 if ( event->button == 2 ) { /* move / pan */
1397 if ( vw->vt->tools[vw->vt->active_tool].ti.pan_handler )
1398 // Tool still may need to do something (such as reenable something)
1399 toolbox_release(vw->vt, event);
1400 vik_window_pan_release ( vw, event );
1403 toolbox_release(vw->vt, event);
1407 static void draw_scroll (VikWindow *vw, GdkEventScroll *event)
1409 guint modifiers = event->state & (GDK_SHIFT_MASK | GDK_CONTROL_MASK);
1410 if ( modifiers == GDK_CONTROL_MASK ) {
1411 /* control == pan up & down */
1412 if ( event->direction == GDK_SCROLL_UP )
1413 vik_viewport_set_center_screen ( vw->viking_vvp, vik_viewport_get_width(vw->viking_vvp)/2, vik_viewport_get_height(vw->viking_vvp)/3 );
1415 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 );
1416 } else if ( modifiers == GDK_SHIFT_MASK ) {
1417 /* shift == pan left & right */
1418 if ( event->direction == GDK_SCROLL_UP )
1419 vik_viewport_set_center_screen ( vw->viking_vvp, vik_viewport_get_width(vw->viking_vvp)/3, vik_viewport_get_height(vw->viking_vvp)/2 );
1421 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 );
1422 } else if ( modifiers == (GDK_CONTROL_MASK | GDK_SHIFT_MASK) ) {
1423 // This zoom is on the center position
1424 if ( event->direction == GDK_SCROLL_UP )
1425 vik_viewport_zoom_in (vw->viking_vvp);
1427 vik_viewport_zoom_out (vw->viking_vvp);
1429 /* make sure mouse is still over the same point on the map when we zoom */
1432 gint center_x = vik_viewport_get_width ( vw->viking_vvp ) / 2;
1433 gint center_y = vik_viewport_get_height ( vw->viking_vvp ) / 2;
1434 vik_viewport_screen_to_coord ( vw->viking_vvp, event->x, event->y, &coord );
1435 if ( event->direction == GDK_SCROLL_UP )
1436 vik_viewport_zoom_in (vw->viking_vvp);
1438 vik_viewport_zoom_out(vw->viking_vvp);
1439 vik_viewport_coord_to_screen ( vw->viking_vvp, &coord, &x, &y );
1440 vik_viewport_set_center_screen ( vw->viking_vvp, center_x + (x - event->x),
1441 center_y + (y - event->y) );
1449 /********************************************************************************
1451 ********************************************************************************/
1452 static void draw_ruler(VikViewport *vvp, GdkDrawable *d, GdkGC *gc, gint x1, gint y1, gint x2, gint y2, gdouble distance)
1456 GdkGC *labgc = vik_viewport_new_gc ( vvp, "#cccccc", 1);
1457 GdkGC *thickgc = gdk_gc_new(d);
1459 gdouble len = sqrt((x1-x2)*(x1-x2) + (y1-y2)*(y1-y2));
1460 gdouble dx = (x2-x1)/len*10;
1461 gdouble dy = (y2-y1)/len*10;
1462 gdouble c = cos(DEG2RAD(15.0));
1463 gdouble s = sin(DEG2RAD(15.0));
1465 gdouble baseangle = 0;
1468 /* draw line with arrow ends */
1470 gint tmp_x1=x1, tmp_y1=y1, tmp_x2=x2, tmp_y2=y2;
1471 a_viewport_clip_line(&tmp_x1, &tmp_y1, &tmp_x2, &tmp_y2);
1472 gdk_draw_line(d, gc, tmp_x1, tmp_y1, tmp_x2, tmp_y2);
1475 a_viewport_clip_line(&x1, &y1, &x2, &y2);
1476 gdk_draw_line(d, gc, x1, y1, x2, y2);
1478 gdk_draw_line(d, gc, x1 - dy, y1 + dx, x1 + dy, y1 - dx);
1479 gdk_draw_line(d, gc, x2 - dy, y2 + dx, x2 + dy, y2 - dx);
1480 gdk_draw_line(d, gc, x2, y2, x2 - (dx * c + dy * s), y2 - (dy * c - dx * s));
1481 gdk_draw_line(d, gc, x2, y2, x2 - (dx * c - dy * s), y2 - (dy * c + dx * s));
1482 gdk_draw_line(d, gc, x1, y1, x1 + (dx * c + dy * s), y1 + (dy * c - dx * s));
1483 gdk_draw_line(d, gc, x1, y1, x1 + (dx * c - dy * s), y1 + (dy * c + dx * s));
1489 vik_viewport_compute_bearing ( vvp, x1, y1, x2, y2, &angle, &baseangle );
1493 gdk_gc_copy(thickgc, gc);
1494 gdk_gc_set_line_attributes(thickgc, CW, GDK_LINE_SOLID, GDK_CAP_BUTT, GDK_JOIN_MITER);
1495 gdk_color_parse("#2255cc", &color);
1496 gdk_gc_set_rgb_fg_color(thickgc, &color);
1498 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);
1501 gdk_gc_copy(thickgc, gc);
1502 gdk_gc_set_line_attributes(thickgc, 2, GDK_LINE_SOLID, GDK_CAP_BUTT, GDK_JOIN_MITER);
1503 for (i=0; i<180; i++) {
1504 c = cos(DEG2RAD(i)*2 + baseangle);
1505 s = sin(DEG2RAD(i)*2 + baseangle);
1508 gdk_draw_line (d, gc, x1 + CR*c, y1 + CR*s, x1 + (CR+CW)*c, y1 + (CR+CW)*s);
1510 gdouble ticksize = 2*CW;
1511 gdk_draw_line (d, thickgc, x1 + (CR-CW)*c, y1 + (CR-CW)*s, x1 + (CR+ticksize)*c, y1 + (CR+ticksize)*s);
1515 gdk_draw_arc (d, gc, FALSE, x1-CR, y1-CR, 2*CR, 2*CR, 0, 64*360);
1516 gdk_draw_arc (d, gc, FALSE, x1-CR-CW, y1-CR-CW, 2*(CR+CW), 2*(CR+CW), 0, 64*360);
1517 gdk_draw_arc (d, gc, FALSE, x1-CR+CW, y1-CR+CW, 2*(CR-CW), 2*(CR-CW), 0, 64*360);
1518 c = (CR+CW*2)*cos(baseangle);
1519 s = (CR+CW*2)*sin(baseangle);
1520 gdk_draw_line (d, gc, x1-c, y1-s, x1+c, y1+s);
1521 gdk_draw_line (d, gc, x1+s, y1-c, x1-s, y1+c);
1524 #define LABEL(x, y, w, h) { \
1525 gdk_draw_rectangle(d, labgc, TRUE, (x)-2, (y)-1, (w)+4, (h)+1); \
1526 gdk_draw_rectangle(d, gc, FALSE, (x)-2, (y)-1, (w)+4, (h)+1); \
1527 gdk_draw_layout(d, gc, (x), (y), pl); }
1529 gint wd, hd, xd, yd;
1530 gint wb, hb, xb, yb;
1532 pl = gtk_widget_create_pango_layout (GTK_WIDGET(vvp), NULL);
1533 pango_layout_set_font_description (pl, gtk_widget_get_style(GTK_WIDGET(vvp))->font_desc);
1534 pango_layout_set_text(pl, "N", -1);
1535 gdk_draw_layout(d, gc, x1-5, y1-CR-3*CW-8, pl);
1537 /* draw label with distance */
1538 vik_units_distance_t dist_units = a_vik_get_units_distance ();
1539 switch (dist_units) {
1540 case VIK_UNITS_DISTANCE_KILOMETRES:
1541 if (distance >= 1000 && distance < 100000) {
1542 g_sprintf(str, "%3.2f km", distance/1000.0);
1543 } else if (distance < 1000) {
1544 g_sprintf(str, "%d m", (int)distance);
1546 g_sprintf(str, "%d km", (int)distance/1000);
1549 case VIK_UNITS_DISTANCE_MILES:
1550 if (distance >= VIK_MILES_TO_METERS(1) && distance < VIK_MILES_TO_METERS(100)) {
1551 g_sprintf(str, "%3.2f miles", VIK_METERS_TO_MILES(distance));
1552 } else if (distance < VIK_MILES_TO_METERS(1)) {
1553 g_sprintf(str, "%d yards", (int)(distance*1.0936133));
1555 g_sprintf(str, "%d miles", (int)VIK_METERS_TO_MILES(distance));
1558 case VIK_UNITS_DISTANCE_NAUTICAL_MILES:
1559 if (distance >= VIK_NAUTICAL_MILES_TO_METERS(1) && distance < VIK_NAUTICAL_MILES_TO_METERS(100)) {
1560 g_sprintf(str, "%3.2f NM", VIK_METERS_TO_NAUTICAL_MILES(distance));
1561 } else if (distance < VIK_NAUTICAL_MILES_TO_METERS(1)) {
1562 g_sprintf(str, "%d yards", (int)(distance*1.0936133));
1564 g_sprintf(str, "%d NM", (int)VIK_METERS_TO_NAUTICAL_MILES(distance));
1568 g_critical("Houston, we've had a problem. distance=%d", dist_units);
1571 pango_layout_set_text(pl, str, -1);
1573 pango_layout_get_pixel_size ( pl, &wd, &hd );
1575 xd = (x1+x2)/2 + dy;
1576 yd = (y1+y2)/2 - hd/2 - dx;
1578 xd = (x1+x2)/2 - dy;
1579 yd = (y1+y2)/2 - hd/2 + dx;
1582 if ( xd < -5 || yd < -5 || xd > vik_viewport_get_width(vvp)+5 || yd > vik_viewport_get_height(vvp)+5 ) {
1587 LABEL(xd, yd, wd, hd);
1589 /* draw label with bearing */
1590 g_sprintf(str, "%3.1f°", RAD2DEG(angle));
1591 pango_layout_set_text(pl, str, -1);
1592 pango_layout_get_pixel_size ( pl, &wb, &hb );
1593 xb = x1 + CR*cos(angle-M_PI_2);
1594 yb = y1 + CR*sin(angle-M_PI_2);
1596 if ( xb < -5 || yb < -5 || xb > vik_viewport_get_width(vvp)+5 || yb > vik_viewport_get_height(vvp)+5 ) {
1602 GdkRectangle r1 = {xd-2, yd-1, wd+4, hd+1}, r2 = {xb-2, yb-1, wb+4, hb+1};
1603 if (gdk_rectangle_intersect(&r1, &r2, &r2)) {
1607 LABEL(xb, yb, wb, hb);
1611 g_object_unref ( G_OBJECT ( pl ) );
1612 g_object_unref ( G_OBJECT ( labgc ) );
1613 g_object_unref ( G_OBJECT ( thickgc ) );
1619 gboolean has_oldcoord;
1621 } ruler_tool_state_t;
1623 static gpointer ruler_create (VikWindow *vw, VikViewport *vvp)
1625 ruler_tool_state_t *s = g_new(ruler_tool_state_t, 1);
1628 s->has_oldcoord = FALSE;
1632 static void ruler_destroy (ruler_tool_state_t *s)
1637 static VikLayerToolFuncStatus ruler_click (VikLayer *vl, GdkEventButton *event, ruler_tool_state_t *s)
1642 if ( event->button == 1 ) {
1643 gchar *lat=NULL, *lon=NULL;
1644 vik_viewport_screen_to_coord ( s->vvp, (gint) event->x, (gint) event->y, &coord );
1645 vik_coord_to_latlon ( &coord, &ll );
1646 a_coords_latlon_to_string ( &ll, &lat, &lon );
1647 if ( s->has_oldcoord ) {
1648 vik_units_distance_t dist_units = a_vik_get_units_distance ();
1649 switch (dist_units) {
1650 case VIK_UNITS_DISTANCE_KILOMETRES:
1651 temp = g_strdup_printf ( "%s %s DIFF %f meters", lat, lon, vik_coord_diff( &coord, &(s->oldcoord) ) );
1653 case VIK_UNITS_DISTANCE_MILES:
1654 temp = g_strdup_printf ( "%s %s DIFF %f miles", lat, lon, VIK_METERS_TO_MILES(vik_coord_diff( &coord, &(s->oldcoord) )) );
1656 case VIK_UNITS_DISTANCE_NAUTICAL_MILES:
1657 temp = g_strdup_printf ( "%s %s DIFF %f NM", lat, lon, VIK_METERS_TO_NAUTICAL_MILES(vik_coord_diff( &coord, &(s->oldcoord) )) );
1660 temp = g_strdup_printf ("Just to keep the compiler happy");
1661 g_critical("Houston, we've had a problem. distance=%d", dist_units);
1664 s->has_oldcoord = FALSE;
1667 temp = g_strdup_printf ( "%s %s", lat, lon );
1668 s->has_oldcoord = TRUE;
1671 vik_statusbar_set_message ( s->vw->viking_vs, VIK_STATUSBAR_INFO, temp );
1674 s->oldcoord = coord;
1677 vik_viewport_set_center_screen ( s->vvp, (gint) event->x, (gint) event->y );
1678 draw_update ( s->vw );
1680 return VIK_LAYER_TOOL_ACK;
1683 static VikLayerToolFuncStatus ruler_move (VikLayer *vl, GdkEventMotion *event, ruler_tool_state_t *s)
1685 VikViewport *vvp = s->vvp;
1686 VikWindow *vw = s->vw;
1692 if ( s->has_oldcoord ) {
1693 int oldx, oldy, w1, h1, w2, h2;
1694 static GdkPixmap *buf = NULL;
1695 gchar *lat=NULL, *lon=NULL;
1696 w1 = vik_viewport_get_width(vvp);
1697 h1 = vik_viewport_get_height(vvp);
1699 buf = gdk_pixmap_new ( gtk_widget_get_window(GTK_WIDGET(vvp)), w1, h1, -1 );
1701 gdk_drawable_get_size(buf, &w2, &h2);
1702 if (w1 != w2 || h1 != h2) {
1703 g_object_unref ( G_OBJECT ( buf ) );
1704 buf = gdk_pixmap_new ( gtk_widget_get_window(GTK_WIDGET(vvp)), w1, h1, -1 );
1707 vik_viewport_screen_to_coord ( vvp, (gint) event->x, (gint) event->y, &coord );
1708 vik_coord_to_latlon ( &coord, &ll );
1709 vik_viewport_coord_to_screen ( vvp, &s->oldcoord, &oldx, &oldy );
1711 gdk_draw_drawable (buf, gtk_widget_get_style(GTK_WIDGET(vvp))->black_gc,
1712 vik_viewport_get_pixmap(vvp), 0, 0, 0, 0, -1, -1);
1713 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)) );
1714 if (draw_buf_done) {
1715 static gpointer pass_along[3];
1716 pass_along[0] = gtk_widget_get_window(GTK_WIDGET(vvp));
1717 pass_along[1] = gtk_widget_get_style(GTK_WIDGET(vvp))->black_gc;
1718 pass_along[2] = buf;
1719 g_idle_add_full (G_PRIORITY_HIGH_IDLE + 10, draw_buf, pass_along, NULL);
1720 draw_buf_done = FALSE;
1722 a_coords_latlon_to_string(&ll, &lat, &lon);
1723 vik_units_distance_t dist_units = a_vik_get_units_distance ();
1724 switch (dist_units) {
1725 case VIK_UNITS_DISTANCE_KILOMETRES:
1726 temp = g_strdup_printf ( "%s %s DIFF %f meters", lat, lon, vik_coord_diff( &coord, &(s->oldcoord) ) );
1728 case VIK_UNITS_DISTANCE_MILES:
1729 temp = g_strdup_printf ( "%s %s DIFF %f miles", lat, lon, VIK_METERS_TO_MILES (vik_coord_diff( &coord, &(s->oldcoord) )) );
1731 case VIK_UNITS_DISTANCE_NAUTICAL_MILES:
1732 temp = g_strdup_printf ( "%s %s DIFF %f NM", lat, lon, VIK_METERS_TO_NAUTICAL_MILES (vik_coord_diff( &coord, &(s->oldcoord) )) );
1735 temp = g_strdup_printf ("Just to keep the compiler happy");
1736 g_critical("Houston, we've had a problem. distance=%d", dist_units);
1738 vik_statusbar_set_message ( vw->viking_vs, VIK_STATUSBAR_INFO, temp );
1741 return VIK_LAYER_TOOL_ACK;
1744 static VikLayerToolFuncStatus ruler_release (VikLayer *vl, GdkEventButton *event, ruler_tool_state_t *s)
1746 return VIK_LAYER_TOOL_ACK;
1749 static void ruler_deactivate (VikLayer *vl, ruler_tool_state_t *s)
1751 draw_update ( s->vw );
1754 static gboolean ruler_key_press (VikLayer *vl, GdkEventKey *event, ruler_tool_state_t *s)
1756 if (event->keyval == GDK_Escape) {
1757 s->has_oldcoord = FALSE;
1758 ruler_deactivate ( vl, s );
1761 // Regardless of whether we used it, return false so other GTK things may use it
1765 static VikToolInterface ruler_tool =
1766 // NB Ctrl+Shift+R is used for Refresh (deemed more important), so use 'U' instead
1768 { "Ruler", "vik-icon-ruler", N_("_Ruler"), "<control><shift>U", N_("Ruler Tool"), 2 },
1769 (VikToolConstructorFunc) ruler_create,
1770 (VikToolDestructorFunc) ruler_destroy,
1771 (VikToolActivationFunc) NULL,
1772 (VikToolActivationFunc) ruler_deactivate,
1773 (VikToolMouseFunc) ruler_click,
1774 (VikToolMouseMoveFunc) ruler_move,
1775 (VikToolMouseFunc) ruler_release,
1776 (VikToolKeyFunc) ruler_key_press,
1778 GDK_CURSOR_IS_PIXMAP,
1779 &cursor_ruler_pixbuf,
1781 /*** end ruler code ********************************************************/
1785 /********************************************************************************
1787 ********************************************************************************/
1792 // Track zoom bounds for zoom tool with shift modifier:
1793 gboolean bounds_active;
1796 } zoom_tool_state_t;
1799 * In case the screen size has changed
1801 static void zoomtool_resize_pixmap (zoom_tool_state_t *zts)
1805 // Allocate a drawing area the size of the viewport
1806 w1 = vik_viewport_get_width ( zts->vw->viking_vvp );
1807 h1 = vik_viewport_get_height ( zts->vw->viking_vvp );
1809 if ( !zts->pixmap ) {
1811 zts->pixmap = gdk_pixmap_new ( gtk_widget_get_window(GTK_WIDGET(zts->vw->viking_vvp)), w1, h1, -1 );
1814 gdk_drawable_get_size ( zts->pixmap, &w2, &h2 );
1816 if ( w1 != w2 || h1 != h2 ) {
1817 // Has changed - delete and recreate with new values
1818 g_object_unref ( G_OBJECT ( zts->pixmap ) );
1819 zts->pixmap = gdk_pixmap_new ( gtk_widget_get_window(GTK_WIDGET(zts->vw->viking_vvp)), w1, h1, -1 );
1823 static gpointer zoomtool_create (VikWindow *vw, VikViewport *vvp)
1825 zoom_tool_state_t *zts = g_new(zoom_tool_state_t, 1);
1830 zts->bounds_active = FALSE;
1834 static void zoomtool_destroy ( zoom_tool_state_t *zts)
1837 g_object_unref ( G_OBJECT ( zts->pixmap ) );
1841 static VikLayerToolFuncStatus zoomtool_click (VikLayer *vl, GdkEventButton *event, zoom_tool_state_t *zts)
1843 zts->vw->modified = TRUE;
1844 guint modifiers = event->state & (GDK_SHIFT_MASK | GDK_CONTROL_MASK);
1848 gint center_x = vik_viewport_get_width ( zts->vw->viking_vvp ) / 2;
1849 gint center_y = vik_viewport_get_height ( zts->vw->viking_vvp ) / 2;
1851 gboolean skip_update = FALSE;
1853 zts->bounds_active = FALSE;
1855 if ( modifiers == (GDK_CONTROL_MASK | GDK_SHIFT_MASK) ) {
1856 // This zoom is on the center position
1857 vik_viewport_set_center_screen ( zts->vw->viking_vvp, center_x, center_y );
1858 if ( event->button == 1 )
1859 vik_viewport_zoom_in (zts->vw->viking_vvp);
1860 else if ( event->button == 3 )
1861 vik_viewport_zoom_out (zts->vw->viking_vvp);
1863 else if ( modifiers == GDK_CONTROL_MASK ) {
1864 // This zoom is to recenter on the mouse position
1865 vik_viewport_set_center_screen ( zts->vw->viking_vvp, (gint) event->x, (gint) event->y );
1866 if ( event->button == 1 )
1867 vik_viewport_zoom_in (zts->vw->viking_vvp);
1868 else if ( event->button == 3 )
1869 vik_viewport_zoom_out (zts->vw->viking_vvp);
1871 else if ( modifiers == GDK_SHIFT_MASK ) {
1872 // Get start of new zoom bounds
1873 if ( event->button == 1 ) {
1874 zts->bounds_active = TRUE;
1875 zts->start_x = (gint) event->x;
1876 zts->start_y = (gint) event->y;
1881 /* make sure mouse is still over the same point on the map when we zoom */
1882 vik_viewport_screen_to_coord ( zts->vw->viking_vvp, event->x, event->y, &coord );
1883 if ( event->button == 1 )
1884 vik_viewport_zoom_in (zts->vw->viking_vvp);
1885 else if ( event->button == 3 )
1886 vik_viewport_zoom_out(zts->vw->viking_vvp);
1887 vik_viewport_coord_to_screen ( zts->vw->viking_vvp, &coord, &x, &y );
1888 vik_viewport_set_center_screen ( zts->vw->viking_vvp,
1889 center_x + (x - event->x),
1890 center_y + (y - event->y) );
1894 draw_update ( zts->vw );
1896 return VIK_LAYER_TOOL_ACK;
1899 static VikLayerToolFuncStatus zoomtool_move (VikLayer *vl, GdkEventMotion *event, zoom_tool_state_t *zts)
1901 guint modifiers = event->state & (GDK_SHIFT_MASK | GDK_CONTROL_MASK);
1903 if ( zts->bounds_active && modifiers == GDK_SHIFT_MASK ) {
1904 zoomtool_resize_pixmap ( zts );
1906 // Blank out currently drawn area
1907 gdk_draw_drawable ( zts->pixmap,
1908 gtk_widget_get_style(GTK_WIDGET(zts->vw->viking_vvp))->black_gc,
1909 vik_viewport_get_pixmap(zts->vw->viking_vvp),
1910 0, 0, 0, 0, -1, -1);
1912 // Calculate new box starting point & size in pixels
1913 int xx, yy, width, height;
1914 if ( event->y > zts->start_y ) {
1916 height = event->y-zts->start_y;
1920 height = zts->start_y-event->y;
1922 if ( event->x > zts->start_x ) {
1924 width = event->x-zts->start_x;
1928 width = zts->start_x-event->x;
1932 gdk_draw_rectangle (zts->pixmap, gtk_widget_get_style(GTK_WIDGET(zts->vw->viking_vvp))->black_gc, FALSE, xx, yy, width, height);
1934 // Only actually draw when there's time to do so
1935 if (draw_buf_done) {
1936 static gpointer pass_along[3];
1937 pass_along[0] = gtk_widget_get_window(GTK_WIDGET(zts->vw->viking_vvp));
1938 pass_along[1] = gtk_widget_get_style(GTK_WIDGET(zts->vw->viking_vvp))->black_gc;
1939 pass_along[2] = zts->pixmap;
1940 g_idle_add_full (G_PRIORITY_HIGH_IDLE + 10, draw_buf, pass_along, NULL);
1941 draw_buf_done = FALSE;
1945 zts->bounds_active = FALSE;
1947 return VIK_LAYER_TOOL_ACK;
1950 static VikLayerToolFuncStatus zoomtool_release (VikLayer *vl, GdkEventButton *event, zoom_tool_state_t *zts)
1952 guint modifiers = event->state & (GDK_SHIFT_MASK | GDK_CONTROL_MASK);
1954 // Ensure haven't just released on the exact same position
1955 // i.e. probably haven't moved the mouse at all
1956 if ( zts->bounds_active && modifiers == GDK_SHIFT_MASK &&
1957 ( event->x < zts->start_x-5 || event->x > zts->start_x+5 ) &&
1958 ( event->y < zts->start_y-5 || event->y > zts->start_y+5 ) ) {
1960 VikCoord coord1, coord2;
1961 vik_viewport_screen_to_coord ( zts->vw->viking_vvp, zts->start_x, zts->start_y, &coord1);
1962 vik_viewport_screen_to_coord ( zts->vw->viking_vvp, event->x, event->y, &coord2);
1964 // From the extend of the bounds pick the best zoom level
1965 // c.f. trw_layer_zoom_to_show_latlons()
1966 // Maybe refactor...
1967 struct LatLon ll1, ll2;
1968 vik_coord_to_latlon(&coord1, &ll1);
1969 vik_coord_to_latlon(&coord2, &ll2);
1970 struct LatLon average = { (ll1.lat+ll2.lat)/2,
1971 (ll1.lon+ll2.lon)/2 };
1973 VikCoord new_center;
1974 vik_coord_load_from_latlon ( &new_center, vik_viewport_get_coord_mode ( zts->vw->viking_vvp ), &average );
1975 vik_viewport_set_center_coord ( zts->vw->viking_vvp, &new_center, FALSE );
1977 /* Convert into definite 'smallest' and 'largest' positions */
1978 struct LatLon minmin;
1979 if ( ll1.lat < ll2.lat )
1980 minmin.lat = ll1.lat;
1982 minmin.lat = ll2.lat;
1984 struct LatLon maxmax;
1985 if ( ll1.lon > ll2.lon )
1986 maxmax.lon = ll1.lon;
1988 maxmax.lon = ll2.lon;
1990 /* Always recalculate the 'best' zoom level */
1991 gdouble zoom = VIK_VIEWPORT_MIN_ZOOM;
1992 vik_viewport_set_zoom ( zts->vw->viking_vvp, zoom );
1994 gdouble min_lat, max_lat, min_lon, max_lon;
1995 /* Should only be a maximum of about 18 iterations from min to max zoom levels */
1996 while ( zoom <= VIK_VIEWPORT_MAX_ZOOM ) {
1997 vik_viewport_get_min_max_lat_lon ( zts->vw->viking_vvp, &min_lat, &max_lat, &min_lon, &max_lon );
1998 /* NB I think the logic used in this test to determine if the bounds is within view
1999 fails if track goes across 180 degrees longitude.
2000 Hopefully that situation is not too common...
2001 Mind you viking doesn't really do edge locations to well anyway */
2002 if ( min_lat < minmin.lat &&
2003 max_lat > minmin.lat &&
2004 min_lon < maxmax.lon &&
2005 max_lon > maxmax.lon )
2006 /* Found within zoom level */
2011 vik_viewport_set_zoom ( zts->vw->viking_vvp, zoom );
2015 // When pressing shift and clicking for zoom, then jump three levels
2016 if ( modifiers == GDK_SHIFT_MASK ) {
2017 // Zoom in/out by three if possible
2018 vik_viewport_set_center_screen ( zts->vw->viking_vvp, event->x, event->y );
2019 if ( event->button == 1 ) {
2020 vik_viewport_zoom_in ( zts->vw->viking_vvp );
2021 vik_viewport_zoom_in ( zts->vw->viking_vvp );
2022 vik_viewport_zoom_in ( zts->vw->viking_vvp );
2024 else if ( event->button == 3 ) {
2025 vik_viewport_zoom_out ( zts->vw->viking_vvp );
2026 vik_viewport_zoom_out ( zts->vw->viking_vvp );
2027 vik_viewport_zoom_out ( zts->vw->viking_vvp );
2032 draw_update ( zts->vw );
2035 zts->bounds_active = FALSE;
2037 return VIK_LAYER_TOOL_ACK;
2040 static VikToolInterface zoom_tool =
2042 { "Zoom", "vik-icon-zoom", N_("_Zoom"), "<control><shift>Z", N_("Zoom Tool"), 1 },
2043 (VikToolConstructorFunc) zoomtool_create,
2044 (VikToolDestructorFunc) zoomtool_destroy,
2045 (VikToolActivationFunc) NULL,
2046 (VikToolActivationFunc) NULL,
2047 (VikToolMouseFunc) zoomtool_click,
2048 (VikToolMouseMoveFunc) zoomtool_move,
2049 (VikToolMouseFunc) zoomtool_release,
2052 GDK_CURSOR_IS_PIXMAP,
2053 &cursor_zoom_pixbuf,
2055 /*** end zoom code ********************************************************/
2057 /********************************************************************************
2059 ********************************************************************************/
2060 static gpointer pantool_create (VikWindow *vw, VikViewport *vvp)
2065 // NB Double clicking means this gets called THREE times!!!
2066 static VikLayerToolFuncStatus pantool_click (VikLayer *vl, GdkEventButton *event, VikWindow *vw)
2068 vw->modified = TRUE;
2070 if ( event->type == GDK_2BUTTON_PRESS ) {
2071 // Zoom in / out on double click
2072 // No need to change the center as that has already occurred in the first click of a double click occurrence
2073 if ( event->button == 1 ) {
2074 guint modifier = event->state & GDK_SHIFT_MASK;
2076 vik_viewport_zoom_out ( vw->viking_vvp );
2078 vik_viewport_zoom_in ( vw->viking_vvp );
2080 else if ( event->button == 3 )
2081 vik_viewport_zoom_out ( vw->viking_vvp );
2086 // Standard pan click
2087 if ( event->button == 1 )
2088 vik_window_pan_click ( vw, event );
2090 return VIK_LAYER_TOOL_ACK;
2093 static VikLayerToolFuncStatus pantool_move (VikLayer *vl, GdkEventMotion *event, VikWindow *vw)
2095 vik_window_pan_move ( vw, event );
2096 return VIK_LAYER_TOOL_ACK;
2099 static VikLayerToolFuncStatus pantool_release (VikLayer *vl, GdkEventButton *event, VikWindow *vw)
2101 if ( event->button == 1 )
2102 vik_window_pan_release ( vw, event );
2103 return VIK_LAYER_TOOL_ACK;
2106 static VikToolInterface pan_tool =
2108 { "Pan", "vik-icon-pan", N_("_Pan"), "<control><shift>P", N_("Pan Tool"), 0 },
2109 (VikToolConstructorFunc) pantool_create,
2110 (VikToolDestructorFunc) NULL,
2111 (VikToolActivationFunc) NULL,
2112 (VikToolActivationFunc) NULL,
2113 (VikToolMouseFunc) pantool_click,
2114 (VikToolMouseMoveFunc) pantool_move,
2115 (VikToolMouseFunc) pantool_release,
2121 /*** end pan code ********************************************************/
2123 /********************************************************************************
2125 ********************************************************************************/
2126 static gpointer selecttool_create (VikWindow *vw, VikViewport *vvp)
2128 tool_ed_t *t = g_new(tool_ed_t, 1);
2132 t->is_waypoint = FALSE;
2136 static void selecttool_destroy (tool_ed_t *t)
2144 GdkEventButton *event;
2145 tool_ed_t *tool_edit;
2148 static void click_layer_selected (VikLayer *vl, clicker *ck)
2150 /* Do nothing when function call returns true; */
2151 /* i.e. stop on first found item */
2154 if ( vik_layer_get_interface(vl->type)->select_click )
2155 ck->cont = !vik_layer_get_interface(vl->type)->select_click ( vl, ck->event, ck->vvp, ck->tool_edit );
2159 // Hopefully Alt keys by default
2160 #define VIK_MOVE_MODIFIER GDK_MOD1_MASK
2162 // Alt+mouse on Linux desktops tend to be used by the desktop manager
2163 // Thus use an alternate modifier - you may need to set something into this group
2164 #define VIK_MOVE_MODIFIER GDK_MOD5_MASK
2167 static VikLayerToolFuncStatus selecttool_click (VikLayer *vl, GdkEventButton *event, tool_ed_t *t)
2169 t->vw->select_move = FALSE;
2170 /* Only allow selection on primary button */
2171 if ( event->button == 1 ) {
2173 if ( event->state & VIK_MOVE_MODIFIER )
2174 vik_window_pan_click ( t->vw, event );
2176 /* Enable click to apply callback to potentially all track/waypoint layers */
2177 /* Useful as we can find things that aren't necessarily in the currently selected layer */
2178 GList* gl = vik_layers_panel_get_all_layers_of_type ( t->vw->viking_vlp, VIK_LAYER_TRW, FALSE ); // Don't get invisible layers
2181 ck.vvp = t->vw->viking_vvp;
2184 g_list_foreach ( gl, (GFunc) click_layer_selected, &ck );
2187 // If nothing found then deselect & redraw screen if necessary to remove the highlight
2190 VikTreeview *vtv = vik_layers_panel_get_treeview ( t->vw->viking_vlp );
2192 if ( vik_treeview_get_selected_iter ( vtv, &iter ) ) {
2193 // Only clear if selected thing is a TrackWaypoint layer or a sublayer
2194 gint type = vik_treeview_item_get_type ( vtv, &iter );
2195 if ( type == VIK_TREEVIEW_TYPE_SUBLAYER ||
2196 VIK_LAYER(vik_treeview_item_get_pointer ( vtv, &iter ))->type == VIK_LAYER_TRW ) {
2198 vik_treeview_item_unselect ( vtv, &iter );
2199 if ( vik_window_clear_highlight ( t->vw ) )
2200 draw_update ( t->vw );
2205 // Something found - so enable movement
2206 t->vw->select_move = TRUE;
2210 else if ( ( event->button == 3 ) && ( vl && ( vl->type == VIK_LAYER_TRW ) ) ) {
2212 /* Act on currently selected item to show menu */
2213 if ( t->vw->selected_track || t->vw->selected_waypoint )
2214 if ( vik_layer_get_interface(vl->type)->show_viewport_menu )
2215 vik_layer_get_interface(vl->type)->show_viewport_menu ( vl, event, t->vw->viking_vvp );
2218 return VIK_LAYER_TOOL_ACK;
2221 static VikLayerToolFuncStatus selecttool_move (VikLayer *vl, GdkEventMotion *event, tool_ed_t *t)
2223 if ( t->vw->select_move ) {
2224 // Don't care about vl here
2226 if ( vik_layer_get_interface(VIK_LAYER_TRW)->select_move )
2227 vik_layer_get_interface(VIK_LAYER_TRW)->select_move ( vl, event, t->vvp, t );
2231 if ( event->state & VIK_MOVE_MODIFIER )
2232 vik_window_pan_move ( t->vw, event );
2234 return VIK_LAYER_TOOL_ACK;
2237 static VikLayerToolFuncStatus selecttool_release (VikLayer *vl, GdkEventButton *event, tool_ed_t *t)
2239 if ( t->vw->select_move ) {
2240 // Don't care about vl here
2242 if ( vik_layer_get_interface(VIK_LAYER_TRW)->select_release )
2243 vik_layer_get_interface(VIK_LAYER_TRW)->select_release ( (VikLayer*)t->vtl, event, t->vvp, t );
2246 if ( event->button == 1 && (event->state & VIK_MOVE_MODIFIER) )
2247 vik_window_pan_release ( t->vw, event );
2249 // Force pan off incase it was on
2250 t->vw->pan_move = FALSE;
2251 t->vw->pan_x = t->vw->pan_y = -1;
2253 // End of this select movement
2254 t->vw->select_move = FALSE;
2256 return VIK_LAYER_TOOL_ACK;
2259 static VikToolInterface select_tool =
2260 { &select_18_pixbuf,
2261 { "Select", "vik-icon-select", N_("_Select"), "<control><shift>S", N_("Select Tool"), 3 },
2262 (VikToolConstructorFunc) selecttool_create,
2263 (VikToolDestructorFunc) selecttool_destroy,
2264 (VikToolActivationFunc) NULL,
2265 (VikToolActivationFunc) NULL,
2266 (VikToolMouseFunc) selecttool_click,
2267 (VikToolMouseMoveFunc) selecttool_move,
2268 (VikToolMouseFunc) selecttool_release,
2269 (VikToolKeyFunc) NULL,
2274 /*** end select tool code ********************************************************/
2276 static void draw_pan_cb ( GtkAction *a, VikWindow *vw )
2278 // Since the treeview cell editting intercepts standard keyboard handlers, it means we can receive events here
2279 // Thus if currently editting, ensure we don't move the viewport when Ctrl+<arrow> is received
2280 VikLayer *sel = vik_layers_panel_get_selected ( vw->viking_vlp );
2281 if ( sel && vik_treeview_get_editing ( sel->vt ) )
2284 if (!strcmp(gtk_action_get_name(a), "PanNorth")) {
2285 vik_viewport_set_center_screen ( vw->viking_vvp, vik_viewport_get_width(vw->viking_vvp)/2, 0 );
2286 } else if (!strcmp(gtk_action_get_name(a), "PanEast")) {
2287 vik_viewport_set_center_screen ( vw->viking_vvp, vik_viewport_get_width(vw->viking_vvp), vik_viewport_get_height(vw->viking_vvp)/2 );
2288 } else if (!strcmp(gtk_action_get_name(a), "PanSouth")) {
2289 vik_viewport_set_center_screen ( vw->viking_vvp, vik_viewport_get_width(vw->viking_vvp)/2, vik_viewport_get_height(vw->viking_vvp) );
2290 } else if (!strcmp(gtk_action_get_name(a), "PanWest")) {
2291 vik_viewport_set_center_screen ( vw->viking_vvp, 0, vik_viewport_get_height(vw->viking_vvp)/2 );
2296 static void draw_zoom_cb ( GtkAction *a, VikWindow *vw )
2300 if (!strcmp(gtk_action_get_name(a), "ZoomIn")) {
2303 else if (!strcmp(gtk_action_get_name(a), "ZoomOut")) {
2306 else if (!strcmp(gtk_action_get_name(a), "Zoom0.25")) {
2309 else if (!strcmp(gtk_action_get_name(a), "Zoom0.5")) {
2313 gchar *s = (gchar *)gtk_action_get_name(a);
2319 case -3: vik_viewport_zoom_in ( vw->viking_vvp ); break;
2320 case -4: vik_viewport_zoom_out ( vw->viking_vvp ); break;
2321 case -1: vik_viewport_set_zoom ( vw->viking_vvp, 0.5 ); break;
2322 case -2: vik_viewport_set_zoom ( vw->viking_vvp, 0.25 ); break;
2323 default: vik_viewport_set_zoom ( vw->viking_vvp, what );
2328 static void draw_goto_cb ( GtkAction *a, VikWindow *vw )
2330 VikCoord new_center;
2332 if (!strcmp(gtk_action_get_name(a), "GotoLL")) {
2333 struct LatLon ll, llold;
2334 vik_coord_to_latlon ( vik_viewport_get_center ( vw->viking_vvp ), &llold );
2335 if ( a_dialog_goto_latlon ( GTK_WINDOW(vw), &ll, &llold ) )
2336 vik_coord_load_from_latlon ( &new_center, vik_viewport_get_coord_mode(vw->viking_vvp), &ll );
2340 else if (!strcmp(gtk_action_get_name(a), "GotoUTM")) {
2341 struct UTM utm, utmold;
2342 vik_coord_to_utm ( vik_viewport_get_center ( vw->viking_vvp ), &utmold );
2343 if ( a_dialog_goto_utm ( GTK_WINDOW(vw), &utm, &utmold ) )
2344 vik_coord_load_from_utm ( &new_center, vik_viewport_get_coord_mode(vw->viking_vvp), &utm );
2349 g_critical("Houston, we've had a problem.");
2353 vik_viewport_set_center_coord ( vw->viking_vvp, &new_center, TRUE );
2358 * center_changed_cb:
2360 static void center_changed_cb ( VikWindow *vw )
2362 // ATM Keep back always available, so when we pan - we can jump to the last requested position
2364 GtkAction* action_back = gtk_action_group_get_action ( vw->action_group, "GoBack" );
2365 if ( action_back ) {
2366 gtk_action_set_sensitive ( action_back, vik_viewport_back_available(vw->viking_vvp) );
2369 GtkAction* action_forward = gtk_action_group_get_action ( vw->action_group, "GoForward" );
2370 if ( action_forward ) {
2371 gtk_action_set_sensitive ( action_forward, vik_viewport_forward_available(vw->viking_vvp) );
2374 toolbar_action_set_sensitive ( vw->viking_vtb, "GoForward", vik_viewport_forward_available(vw->viking_vvp) );
2378 * draw_goto_back_and_forth:
2380 static void draw_goto_back_and_forth ( GtkAction *a, VikWindow *vw )
2382 gboolean changed = FALSE;
2383 if (!strcmp(gtk_action_get_name(a), "GoBack")) {
2384 changed = vik_viewport_go_back ( vw->viking_vvp );
2386 else if (!strcmp(gtk_action_get_name(a), "GoForward")) {
2387 changed = vik_viewport_go_forward ( vw->viking_vvp );
2393 // Recheck buttons sensitivities, as the center changed signal is not sent on back/forward changes
2394 // (otherwise we would get stuck in an infinite loop!)
2395 center_changed_cb ( vw );
2402 * Refresh maps displayed
2404 static void draw_refresh_cb ( GtkAction *a, VikWindow *vw )
2406 // Only get 'new' maps
2407 simple_map_update ( vw, TRUE );
2410 static void menu_addlayer_cb ( GtkAction *a, VikWindow *vw )
2412 VikLayerTypeEnum type;
2413 for ( type = 0; type < VIK_LAYER_NUM_TYPES; type++ ) {
2414 if (!strcmp(vik_layer_get_interface(type)->fixed_layer_name, gtk_action_get_name(a))) {
2415 if ( vik_layers_panel_new_layer ( vw->viking_vlp, type ) ) {
2417 vw->modified = TRUE;
2423 static void menu_copy_layer_cb ( GtkAction *a, VikWindow *vw )
2425 a_clipboard_copy_selected ( vw->viking_vlp );
2428 static void menu_cut_layer_cb ( GtkAction *a, VikWindow *vw )
2430 vik_layers_panel_cut_selected ( vw->viking_vlp );
2431 vw->modified = TRUE;
2434 static void menu_paste_layer_cb ( GtkAction *a, VikWindow *vw )
2436 if ( vik_layers_panel_paste_selected ( vw->viking_vlp ) )
2438 vw->modified = TRUE;
2442 static void menu_properties_cb ( GtkAction *a, VikWindow *vw )
2444 if ( ! vik_layers_panel_properties ( vw->viking_vlp ) )
2445 a_dialog_info_msg ( GTK_WINDOW(vw), _("You must select a layer to show its properties.") );
2448 static void help_help_cb ( GtkAction *a, VikWindow *vw )
2451 ShellExecute(NULL, "open", ""PACKAGE".pdf", NULL, NULL, SW_SHOWNORMAL);
2454 uri = g_strdup_printf("ghelp:%s", PACKAGE);
2455 GError *error = NULL;
2456 gboolean show = gtk_show_uri (NULL, uri, GDK_CURRENT_TIME, &error);
2457 if ( !show && !error )
2458 // No error to show, so unlikely this will get called
2459 a_dialog_error_msg ( GTK_WINDOW(vw), _("The help system is not available.") );
2462 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 );
2463 g_error_free ( error );
2466 #endif /* WINDOWS */
2469 static void toggle_side_panel ( VikWindow *vw )
2471 vw->show_side_panel = !vw->show_side_panel;
2472 if ( vw->show_side_panel )
2473 gtk_widget_show(GTK_WIDGET(vw->viking_vlp));
2475 gtk_widget_hide(GTK_WIDGET(vw->viking_vlp));
2478 static void toggle_full_screen ( VikWindow *vw )
2480 vw->show_full_screen = !vw->show_full_screen;
2481 if ( vw->show_full_screen )
2482 gtk_window_fullscreen ( GTK_WINDOW(vw) );
2484 gtk_window_unfullscreen ( GTK_WINDOW(vw) );
2487 static void toggle_statusbar ( VikWindow *vw )
2489 vw->show_statusbar = !vw->show_statusbar;
2490 if ( vw->show_statusbar )
2491 gtk_widget_show ( GTK_WIDGET(vw->viking_vs) );
2493 gtk_widget_hide ( GTK_WIDGET(vw->viking_vs) );
2496 static void toggle_toolbar ( VikWindow *vw )
2498 vw->show_toolbar = !vw->show_toolbar;
2499 if ( vw->show_toolbar )
2500 gtk_widget_show ( toolbar_get_widget (vw->viking_vtb) );
2502 gtk_widget_hide ( toolbar_get_widget (vw->viking_vtb) );
2505 static void toggle_main_menu ( VikWindow *vw )
2507 vw->show_main_menu = !vw->show_main_menu;
2508 if ( vw->show_main_menu )
2509 gtk_widget_show ( gtk_ui_manager_get_widget ( vw->uim, "/ui/MainMenu" ) );
2511 gtk_widget_hide ( gtk_ui_manager_get_widget ( vw->uim, "/ui/MainMenu" ) );
2514 // Only for 'view' toggle menu widgets ATM.
2515 GtkWidget *get_show_widget_by_name(VikWindow *vw, const gchar *name)
2517 g_return_val_if_fail(name != NULL, NULL);
2519 // ATM only FullScreen is *not* in SetShow path
2521 if ( g_strcmp0 ("FullScreen", name ) )
2522 path = g_strconcat("/ui/MainMenu/View/SetShow/", name, NULL);
2524 path = g_strconcat("/ui/MainMenu/View/", name, NULL);
2526 GtkWidget *widget = gtk_ui_manager_get_widget(vw->uim, path);
2532 static void tb_view_side_panel_cb ( GtkAction *a, VikWindow *vw )
2534 gboolean next_state = !vw->show_side_panel;
2535 GtkWidget *check_box = get_show_widget_by_name ( vw, gtk_action_get_name(a) );
2536 gboolean menu_state = gtk_check_menu_item_get_active ( GTK_CHECK_MENU_ITEM(check_box) );
2537 if ( next_state != menu_state )
2538 gtk_check_menu_item_set_active ( GTK_CHECK_MENU_ITEM(check_box), next_state );
2540 toggle_side_panel ( vw );
2543 static void tb_full_screen_cb ( GtkAction *a, VikWindow *vw )
2545 gboolean next_state = !vw->show_full_screen;
2546 GtkWidget *check_box = get_show_widget_by_name ( vw, gtk_action_get_name(a) );
2547 gboolean menu_state = gtk_check_menu_item_get_active ( GTK_CHECK_MENU_ITEM(check_box) );
2548 if ( next_state != menu_state )
2549 gtk_check_menu_item_set_active ( GTK_CHECK_MENU_ITEM(check_box), next_state );
2551 toggle_full_screen ( vw );
2554 static void tb_view_statusbar_cb ( GtkAction *a, VikWindow *vw )
2556 gboolean next_state = !vw->show_statusbar;
2557 GtkWidget *check_box = get_show_widget_by_name ( vw, gtk_action_get_name(a) );
2558 gboolean menu_state = gtk_check_menu_item_get_active ( GTK_CHECK_MENU_ITEM(check_box) );
2559 if ( next_state != menu_state )
2560 gtk_check_menu_item_set_active ( GTK_CHECK_MENU_ITEM(check_box), next_state );
2562 toggle_statusbar ( vw );
2565 static void tb_view_toolbar_cb ( GtkAction *a, VikWindow *vw )
2567 gboolean next_state = !vw->show_toolbar;
2568 GtkWidget *check_box = get_show_widget_by_name ( vw, gtk_action_get_name(a) );
2569 gboolean menu_state = gtk_check_menu_item_get_active ( GTK_CHECK_MENU_ITEM(check_box) );
2570 if ( next_state != menu_state )
2571 gtk_check_menu_item_set_active ( GTK_CHECK_MENU_ITEM(check_box), next_state );
2573 toggle_toolbar ( vw );
2576 static void tb_view_main_menu_cb ( GtkAction *a, VikWindow *vw )
2578 gboolean next_state = !vw->show_main_menu;
2579 GtkWidget *check_box = get_show_widget_by_name ( vw, gtk_action_get_name(a) );
2580 gboolean menu_state = gtk_check_menu_item_get_active ( GTK_CHECK_MENU_ITEM(check_box) );
2581 if ( next_state != menu_state )
2582 gtk_check_menu_item_set_active ( GTK_CHECK_MENU_ITEM(check_box), next_state );
2584 toggle_main_menu ( vw );
2587 static void tb_set_draw_scale ( GtkAction *a, VikWindow *vw )
2589 gboolean next_state = !vik_viewport_get_draw_scale ( vw->viking_vvp );
2590 GtkWidget *check_box = get_show_widget_by_name ( vw, gtk_action_get_name(a) );
2591 gboolean menu_state = gtk_check_menu_item_get_active ( GTK_CHECK_MENU_ITEM(check_box) );
2592 if ( next_state != menu_state )
2593 gtk_check_menu_item_set_active ( GTK_CHECK_MENU_ITEM(check_box), next_state );
2595 vik_viewport_set_draw_scale ( vw->viking_vvp, next_state );
2600 static void tb_set_draw_centermark ( GtkAction *a, VikWindow *vw )
2602 gboolean next_state = !vik_viewport_get_draw_centermark ( vw->viking_vvp );
2603 GtkWidget *check_box = get_show_widget_by_name ( vw, gtk_action_get_name(a) );
2604 gboolean menu_state = gtk_check_menu_item_get_active ( GTK_CHECK_MENU_ITEM(check_box) );
2605 if ( next_state != menu_state )
2606 gtk_check_menu_item_set_active ( GTK_CHECK_MENU_ITEM(check_box), next_state );
2608 vik_viewport_set_draw_centermark ( vw->viking_vvp, next_state );
2613 static void tb_set_draw_highlight ( GtkAction *a, VikWindow *vw )
2615 gboolean next_state = !vik_viewport_get_draw_highlight ( vw->viking_vvp );
2616 GtkWidget *check_box = get_show_widget_by_name ( vw, gtk_action_get_name(a) );
2617 gboolean menu_state = gtk_check_menu_item_get_active ( GTK_CHECK_MENU_ITEM(check_box) );
2618 if ( next_state != menu_state )
2619 gtk_check_menu_item_set_active ( GTK_CHECK_MENU_ITEM(check_box), next_state );
2621 vik_viewport_set_draw_highlight ( vw->viking_vvp, next_state );
2626 static void help_about_cb ( GtkAction *a, VikWindow *vw )
2628 a_dialog_about(GTK_WINDOW(vw));
2631 static void help_cache_info_cb ( GtkAction *a, VikWindow *vw )
2633 // NB: No i18n as this is just for debug
2634 gint byte_size = a_mapcache_get_size();
2635 gchar *msg_sz = NULL;
2637 #if GLIB_CHECK_VERSION(2,30,0)
2638 msg_sz = g_format_size_full ( byte_size, G_FORMAT_SIZE_LONG_FORMAT );
2640 msg_sz = g_format_size_for_display ( byte_size );
2642 msg = g_strdup_printf ( "Map Cache size is %s with %d items", msg_sz, a_mapcache_get_count());
2643 a_dialog_info_msg_extra ( GTK_WINDOW(vw), "%s", msg );
2648 static void back_forward_info_cb ( GtkAction *a, VikWindow *vw )
2650 vik_viewport_show_centers ( vw->viking_vvp, GTK_WINDOW(vw) );
2653 static void menu_delete_layer_cb ( GtkAction *a, VikWindow *vw )
2655 if ( vik_layers_panel_get_selected ( vw->viking_vlp ) )
2657 vik_layers_panel_delete_selected ( vw->viking_vlp );
2658 vw->modified = TRUE;
2661 a_dialog_info_msg ( GTK_WINDOW(vw), _("You must select a layer to delete.") );
2664 static void full_screen_cb ( GtkAction *a, VikWindow *vw )
2666 gboolean next_state = !vw->show_full_screen;
2667 GtkToggleToolButton *tbutton = (GtkToggleToolButton *)toolbar_get_widget_by_name ( vw->viking_vtb, gtk_action_get_name(a) );
2669 gboolean tb_state = gtk_toggle_tool_button_get_active ( tbutton );
2670 if ( next_state != tb_state )
2671 gtk_toggle_tool_button_set_active ( tbutton, next_state );
2673 toggle_full_screen ( vw );
2676 toggle_full_screen ( vw );
2679 static void view_side_panel_cb ( GtkAction *a, VikWindow *vw )
2681 gboolean next_state = !vw->show_side_panel;
2682 GtkToggleToolButton *tbutton = (GtkToggleToolButton *)toolbar_get_widget_by_name ( vw->viking_vtb, gtk_action_get_name(a) );
2684 gboolean tb_state = gtk_toggle_tool_button_get_active ( tbutton );
2685 if ( next_state != tb_state )
2686 gtk_toggle_tool_button_set_active ( tbutton, next_state );
2688 toggle_side_panel ( vw );
2691 toggle_side_panel ( vw );
2694 static void view_statusbar_cb ( GtkAction *a, VikWindow *vw )
2696 gboolean next_state = !vw->show_statusbar;
2697 GtkToggleToolButton *tbutton = (GtkToggleToolButton *)toolbar_get_widget_by_name ( vw->viking_vtb, gtk_action_get_name(a) );
2699 gboolean tb_state = gtk_toggle_tool_button_get_active ( tbutton );
2700 if ( next_state != tb_state )
2701 gtk_toggle_tool_button_set_active ( tbutton, next_state );
2703 toggle_statusbar ( vw );
2706 toggle_statusbar ( vw );
2709 static void view_toolbar_cb ( GtkAction *a, VikWindow *vw )
2711 gboolean next_state = !vw->show_toolbar;
2712 GtkToggleToolButton *tbutton = (GtkToggleToolButton *)toolbar_get_widget_by_name ( vw->viking_vtb, gtk_action_get_name(a) );
2714 gboolean tb_state = gtk_toggle_tool_button_get_active ( tbutton );
2715 if ( next_state != tb_state )
2716 gtk_toggle_tool_button_set_active ( tbutton, next_state );
2718 toggle_toolbar ( vw );
2721 toggle_toolbar ( vw );
2724 static void view_main_menu_cb ( GtkAction *a, VikWindow *vw )
2726 gboolean next_state = !vw->show_main_menu;
2727 GtkToggleToolButton *tbutton = (GtkToggleToolButton *)toolbar_get_widget_by_name ( vw->viking_vtb, gtk_action_get_name(a) );
2729 gboolean tb_state = gtk_toggle_tool_button_get_active ( tbutton );
2730 if ( next_state != tb_state )
2731 gtk_toggle_tool_button_set_active ( tbutton, next_state );
2733 toggle_main_menu ( vw );
2736 toggle_main_menu ( vw );
2739 /***************************************
2740 ** tool management routines
2742 ***************************************/
2744 static toolbox_tools_t* toolbox_create(VikWindow *vw)
2746 toolbox_tools_t *vt = g_new(toolbox_tools_t, 1);
2749 vt->active_tool = -1;
2754 static void toolbox_add_tool(toolbox_tools_t *vt, VikToolInterface *vti, gint layer_type )
2756 vt->tools = g_renew(toolbox_tool_t, vt->tools, vt->n_tools+1);
2757 vt->tools[vt->n_tools].ti = *vti;
2758 vt->tools[vt->n_tools].layer_type = layer_type;
2760 vt->tools[vt->n_tools].state = vti->create(vt->vw, vt->vw->viking_vvp);
2763 vt->tools[vt->n_tools].state = NULL;
2768 static int toolbox_get_tool(toolbox_tools_t *vt, const gchar *tool_name)
2771 for (i=0; i<vt->n_tools; i++) {
2772 if (!strcmp(tool_name, vt->tools[i].ti.radioActionEntry.name)) {
2779 static void toolbox_activate(toolbox_tools_t *vt, const gchar *tool_name)
2781 int tool = toolbox_get_tool(vt, tool_name);
2782 toolbox_tool_t *t = &vt->tools[tool];
2783 VikLayer *vl = vik_layers_panel_get_selected ( vt->vw->viking_vlp );
2785 if (tool == vt->n_tools) {
2786 g_critical("trying to activate a non-existent tool...");
2789 /* is the tool already active? */
2790 if (vt->active_tool == tool) {
2794 if (vt->active_tool != -1) {
2795 if (vt->tools[vt->active_tool].ti.deactivate) {
2796 vt->tools[vt->active_tool].ti.deactivate(NULL, vt->tools[vt->active_tool].state);
2799 if (t->ti.activate) {
2800 t->ti.activate(vl, t->state);
2802 vt->active_tool = tool;
2805 static const GdkCursor *toolbox_get_cursor(toolbox_tools_t *vt, const gchar *tool_name)
2807 int tool = toolbox_get_tool(vt, tool_name);
2808 toolbox_tool_t *t = &vt->tools[tool];
2809 if (t->ti.cursor == NULL) {
2810 if (t->ti.cursor_type == GDK_CURSOR_IS_PIXMAP && t->ti.cursor_data != NULL) {
2811 GdkPixbuf *cursor_pixbuf = gdk_pixbuf_from_pixdata (t->ti.cursor_data, FALSE, NULL);
2812 /* TODO: settable offeset */
2813 t->ti.cursor = gdk_cursor_new_from_pixbuf ( gdk_display_get_default(), cursor_pixbuf, 3, 3 );
2814 g_object_unref ( G_OBJECT(cursor_pixbuf) );
2816 t->ti.cursor = gdk_cursor_new ( t->ti.cursor_type );
2819 return t->ti.cursor;
2822 static void toolbox_click (toolbox_tools_t *vt, GdkEventButton *event)
2824 VikLayer *vl = vik_layers_panel_get_selected ( vt->vw->viking_vlp );
2825 if (vt->active_tool != -1 && vt->tools[vt->active_tool].ti.click) {
2826 gint ltype = vt->tools[vt->active_tool].layer_type;
2827 if ( ltype == TOOL_LAYER_TYPE_NONE || (vl && ltype == vl->type) )
2828 vt->tools[vt->active_tool].ti.click(vl, event, vt->tools[vt->active_tool].state);
2832 static void toolbox_move (toolbox_tools_t *vt, GdkEventMotion *event)
2834 VikLayer *vl = vik_layers_panel_get_selected ( vt->vw->viking_vlp );
2835 if (vt->active_tool != -1 && vt->tools[vt->active_tool].ti.move) {
2836 gint ltype = vt->tools[vt->active_tool].layer_type;
2837 if ( ltype == TOOL_LAYER_TYPE_NONE || (vl && ltype == vl->type) )
2838 if ( VIK_LAYER_TOOL_ACK_GRAB_FOCUS == vt->tools[vt->active_tool].ti.move(vl, event, vt->tools[vt->active_tool].state) )
2839 gtk_widget_grab_focus ( GTK_WIDGET(vt->vw->viking_vvp) );
2843 static void toolbox_release (toolbox_tools_t *vt, GdkEventButton *event)
2845 VikLayer *vl = vik_layers_panel_get_selected ( vt->vw->viking_vlp );
2846 if (vt->active_tool != -1 && vt->tools[vt->active_tool].ti.release ) {
2847 gint ltype = vt->tools[vt->active_tool].layer_type;
2848 if ( ltype == TOOL_LAYER_TYPE_NONE || (vl && ltype == vl->type) )
2849 vt->tools[vt->active_tool].ti.release(vl, event, vt->tools[vt->active_tool].state);
2852 /** End tool management ************************************/
2854 void vik_window_enable_layer_tool ( VikWindow *vw, gint layer_id, gint tool_id )
2856 gtk_action_activate ( gtk_action_group_get_action ( vw->action_group, vik_layer_get_interface(layer_id)->tools[tool_id].radioActionEntry.name ) );
2859 // Be careful with usage - as it may trigger actions being continually alternately by the menu and toolbar items
2860 // DON'T Use this from menu callback with toggle toolbar items!!
2861 static void toolbar_sync ( VikWindow *vw, const gchar *name, gboolean state )
2863 GtkToggleToolButton *tbutton = (GtkToggleToolButton *)toolbar_get_widget_by_name ( vw->viking_vtb, name );
2865 // Causes toggle signal action to be raised.
2866 gtk_toggle_tool_button_set_active ( tbutton, state );
2870 /* this function gets called whenever a menu is clicked */
2871 // Note old is not used
2872 static void menu_cb ( GtkAction *old, GtkAction *a, VikWindow *vw )
2874 // Ensure Toolbar kept in sync
2875 const gchar *name = gtk_action_get_name(a);
2876 toolbar_sync ( vw, name, TRUE );
2878 /* White Magic, my friends ... White Magic... */
2880 toolbox_activate(vw->vt, name);
2882 vw->viewport_cursor = (GdkCursor *)toolbox_get_cursor(vw->vt, name);
2884 if ( gtk_widget_get_window(GTK_WIDGET(vw->viking_vvp)) )
2885 /* We set cursor, even if it is NULL: it resets to default */
2886 gdk_window_set_cursor ( gtk_widget_get_window(GTK_WIDGET(vw->viking_vvp)), vw->viewport_cursor );
2888 if (!g_strcmp0(name, "Pan")) {
2889 vw->current_tool = TOOL_PAN;
2891 else if (!g_strcmp0(name, "Zoom")) {
2892 vw->current_tool = TOOL_ZOOM;
2894 else if (!g_strcmp0(name, "Ruler")) {
2895 vw->current_tool = TOOL_RULER;
2897 else if (!g_strcmp0(name, "Select")) {
2898 vw->current_tool = TOOL_SELECT;
2901 VikLayerTypeEnum layer_id;
2902 for (layer_id=0; layer_id<VIK_LAYER_NUM_TYPES; layer_id++) {
2903 for ( tool_id = 0; tool_id < vik_layer_get_interface(layer_id)->tools_count; tool_id++ ) {
2904 if (!g_strcmp0(vik_layer_get_interface(layer_id)->tools[tool_id].radioActionEntry.name, name)) {
2905 vw->current_tool = TOOL_LAYER;
2906 vw->tool_layer_id = layer_id;
2907 vw->tool_tool_id = tool_id;
2912 draw_status_tool ( vw );
2915 static void window_set_filename ( VikWindow *vw, const gchar *filename )
2920 g_free ( vw->filename );
2921 if ( filename == NULL )
2923 vw->filename = NULL;
2927 vw->filename = g_strdup(filename);
2930 /* Refresh window's title */
2931 file = window_get_filename ( vw );
2932 title = g_strdup_printf( "%s - Viking", file );
2933 gtk_window_set_title ( GTK_WINDOW(vw), title );
2937 static const gchar *window_get_filename ( VikWindow *vw )
2939 return vw->filename ? a_file_basename ( vw->filename ) : _("Untitled");
2942 GtkWidget *vik_window_get_drawmode_button ( VikWindow *vw, VikViewportDrawMode mode )
2944 GtkWidget *mode_button;
2947 #ifdef VIK_CONFIG_EXPEDIA
2948 case VIK_VIEWPORT_DRAWMODE_EXPEDIA: buttonname = "/ui/MainMenu/View/ModeExpedia"; break;
2950 case VIK_VIEWPORT_DRAWMODE_MERCATOR: buttonname = "/ui/MainMenu/View/ModeMercator"; break;
2951 case VIK_VIEWPORT_DRAWMODE_LATLON: buttonname = "/ui/MainMenu/View/ModeLatLon"; break;
2952 default: buttonname = "/ui/MainMenu/View/ModeUTM";
2954 mode_button = gtk_ui_manager_get_widget ( vw->uim, buttonname );
2955 g_assert ( mode_button );
2960 * vik_window_get_pan_move:
2961 * @vw: some VikWindow
2963 * Retrieves @vw's pan_move.
2965 * Should be removed as soon as possible.
2967 * Returns: @vw's pan_move
2971 gboolean vik_window_get_pan_move ( VikWindow *vw )
2973 return vw->pan_move;
2977 * Remove the potentially auto added map
2979 static void remove_default_map_layer ( VikWindow *vw )
2981 if ( a_vik_get_add_default_map_layer () ) {
2982 VikAggregateLayer *top = vik_layers_panel_get_top_layer(vw->viking_vlp);
2983 if ( vik_aggregate_layer_count(top) == 1 ) {
2984 VikLayer *vl = vik_aggregate_layer_get_top_visible_layer_of_type (top, VIK_LAYER_MAPS);
2985 // Could try to compare name vs _("Default Map") but this might have i8n issues if not translated
2987 vik_aggregate_layer_delete_layer ( top, vl );
2992 static void on_activate_recent_item (GtkRecentChooser *chooser,
2997 filename = gtk_recent_chooser_get_current_uri (chooser);
2998 if (filename != NULL)
3000 GFile *file = g_file_new_for_uri ( filename );
3001 gchar *path = g_file_get_path ( file );
3002 g_object_unref ( file );
3003 if ( self->filename )
3005 GSList *filenames = NULL;
3006 filenames = g_slist_append ( filenames, path );
3007 g_signal_emit ( G_OBJECT(self), window_signals[VW_OPENWINDOW_SIGNAL], 0, filenames );
3008 // NB: GSList & contents are freed by main.open_window
3011 remove_default_map_layer ( self );
3012 vik_window_open_file ( self, path, TRUE );
3020 static void setup_recent_files (VikWindow *self)
3022 GtkRecentManager *manager;
3023 GtkRecentFilter *filter;
3024 GtkWidget *menu, *menu_item;
3026 filter = gtk_recent_filter_new ();
3027 /* gtk_recent_filter_add_application (filter, g_get_application_name()); */
3028 gtk_recent_filter_add_group(filter, "viking");
3030 manager = gtk_recent_manager_get_default ();
3031 menu = gtk_recent_chooser_menu_new_for_manager (manager);
3032 gtk_recent_chooser_set_sort_type (GTK_RECENT_CHOOSER (menu), GTK_RECENT_SORT_MRU);
3033 gtk_recent_chooser_add_filter (GTK_RECENT_CHOOSER (menu), filter);
3034 gtk_recent_chooser_set_limit (GTK_RECENT_CHOOSER (menu), a_vik_get_recent_number_files() );
3036 menu_item = gtk_ui_manager_get_widget (self->uim, "/ui/MainMenu/File/OpenRecentFile");
3037 gtk_menu_item_set_submenu (GTK_MENU_ITEM (menu_item), menu);
3039 g_signal_connect (G_OBJECT (menu), "item-activated",
3040 G_CALLBACK (on_activate_recent_item), (gpointer) self);
3046 static void update_recently_used_document (VikWindow *vw, const gchar *filename)
3048 /* Update Recently Used Document framework */
3049 GtkRecentManager *manager = gtk_recent_manager_get_default();
3050 GtkRecentData *recent_data = g_slice_new (GtkRecentData);
3051 gchar *groups[] = {"viking", NULL};
3052 GFile *file = g_file_new_for_commandline_arg(filename);
3053 gchar *uri = g_file_get_uri(file);
3054 gchar *basename = g_path_get_basename(filename);
3055 g_object_unref(file);
3058 recent_data->display_name = basename;
3059 recent_data->description = NULL;
3060 recent_data->mime_type = "text/x-gps-data";
3061 recent_data->app_name = (gchar *) g_get_application_name ();
3062 recent_data->app_exec = g_strjoin (" ", g_get_prgname (), "%f", NULL);
3063 recent_data->groups = groups;
3064 recent_data->is_private = FALSE;
3065 if (!gtk_recent_manager_add_full (manager, uri, recent_data))
3067 gchar *msg = g_strdup_printf (_("Unable to add '%s' to the list of recently used documents"), uri);
3068 vik_statusbar_set_message ( vw->viking_vs, VIK_STATUSBAR_INFO, msg );
3074 g_free (recent_data->app_exec);
3075 g_slice_free (GtkRecentData, recent_data);
3079 * Call this before doing things that may take a long time and otherwise not show any other feedback
3080 * such as loading and saving files
3082 void vik_window_set_busy_cursor ( VikWindow *vw )
3084 gdk_window_set_cursor ( gtk_widget_get_window(GTK_WIDGET(vw)), vw->busy_cursor );
3085 // Viewport has a separate cursor
3086 gdk_window_set_cursor ( gtk_widget_get_window(GTK_WIDGET(vw->viking_vvp)), vw->busy_cursor );
3087 // Ensure cursor updated before doing stuff
3088 while( gtk_events_pending() )
3089 gtk_main_iteration();
3092 void vik_window_clear_busy_cursor ( VikWindow *vw )
3094 gdk_window_set_cursor ( gtk_widget_get_window(GTK_WIDGET(vw)), NULL );
3095 // Restore viewport cursor
3096 gdk_window_set_cursor ( gtk_widget_get_window(GTK_WIDGET(vw->viking_vvp)), vw->viewport_cursor );
3099 void vik_window_open_file ( VikWindow *vw, const gchar *filename, gboolean change_filename )
3101 vik_window_set_busy_cursor ( vw );
3103 // Enable the *new* filename to be accessible by the Layers codez
3104 gchar *original_filename = g_strdup ( vw->filename );
3105 g_free ( vw->filename );
3106 vw->filename = g_strdup ( filename );
3107 gboolean success = FALSE;
3108 gboolean restore_original_filename = FALSE;
3109 vw->loaded_type = a_file_load ( vik_layers_panel_get_top_layer(vw->viking_vlp), vw->viking_vvp, vw->containing_vtl, filename );
3110 switch ( vw->loaded_type )
3112 case LOAD_TYPE_READ_FAILURE:
3113 a_dialog_error_msg ( GTK_WINDOW(vw), _("The file you requested could not be opened.") );
3115 case LOAD_TYPE_GPSBABEL_FAILURE:
3116 a_dialog_error_msg ( GTK_WINDOW(vw), _("GPSBabel is required to load files of this type or GPSBabel encountered problems.") );
3118 case LOAD_TYPE_GPX_FAILURE:
3119 a_dialog_error_msg_extra ( GTK_WINDOW(vw), _("Unable to load malformed GPX file %s"), filename );
3121 case LOAD_TYPE_UNSUPPORTED_FAILURE:
3122 a_dialog_error_msg_extra ( GTK_WINDOW(vw), _("Unsupported file type for %s"), filename );
3124 case LOAD_TYPE_VIK_FAILURE_NON_FATAL:
3126 // Since we can process .vik files with issues just show a warning in the status bar
3127 // Not that a user can do much about it... or tells them what this issue is yet...
3128 gchar *msg = g_strdup_printf (_("WARNING: issues encountered loading %s"), a_file_basename (filename) );
3129 vik_statusbar_set_message ( vw->viking_vs, VIK_STATUSBAR_INFO, msg );
3132 // No break, carry on to show any data
3133 case LOAD_TYPE_VIK_SUCCESS:
3135 restore_original_filename = TRUE; // NB Will actually get inverted by the 'success' component below
3136 GtkWidget *mode_button;
3138 if ( change_filename )
3139 window_set_filename ( vw, filename );
3140 mode_button = vik_window_get_drawmode_button ( vw, vik_viewport_get_drawmode ( vw->viking_vvp ) );
3141 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. */
3142 gtk_check_menu_item_set_active ( GTK_CHECK_MENU_ITEM(mode_button), TRUE );
3143 vw->only_updating_coord_mode_ui = FALSE;
3145 vik_layers_panel_change_coord_mode ( vw->viking_vlp, vik_viewport_get_coord_mode ( vw->viking_vvp ) );
3147 // Slightly long winded methods to align loaded viewport settings with the UI
3148 // Since the rewrite for toolbar + menu actions
3149 // there no longer exists a simple way to directly change the UI to a value for toggle settings
3150 // it only supports toggling the existing setting (otherwise get infinite loops in trying to align tb+menu elements)
3151 // Thus get state, compare them, if different then invert viewport setting and (re)sync the setting (via toggling)
3152 gboolean vp_state_scale = vik_viewport_get_draw_scale ( vw->viking_vvp );
3153 gboolean ui_state_scale = gtk_check_menu_item_get_active ( GTK_CHECK_MENU_ITEM(get_show_widget_by_name(vw, "ShowScale")) );
3154 if ( vp_state_scale != ui_state_scale ) {
3155 vik_viewport_set_draw_scale ( vw->viking_vvp, !vp_state_scale );
3156 toggle_draw_scale ( NULL, vw );
3158 gboolean vp_state_centermark = vik_viewport_get_draw_centermark ( vw->viking_vvp );
3159 gboolean ui_state_centermark = gtk_check_menu_item_get_active ( GTK_CHECK_MENU_ITEM(get_show_widget_by_name(vw, "ShowCenterMark")) );
3160 if ( vp_state_centermark != ui_state_centermark ) {
3161 vik_viewport_set_draw_centermark ( vw->viking_vvp, !vp_state_centermark );
3162 toggle_draw_centermark ( NULL, vw );
3164 gboolean vp_state_highlight = vik_viewport_get_draw_highlight ( vw->viking_vvp );
3165 gboolean ui_state_highlight = gtk_check_menu_item_get_active ( GTK_CHECK_MENU_ITEM(get_show_widget_by_name(vw, "ShowHighlight")) );
3166 if ( vp_state_highlight != ui_state_highlight ) {
3167 vik_viewport_set_draw_highlight ( vw->viking_vvp, !vp_state_highlight );
3168 toggle_draw_highlight ( NULL, vw );
3171 // NB No break, carry on to redraw
3172 //case LOAD_TYPE_OTHER_SUCCESS:
3175 // When LOAD_TYPE_OTHER_SUCCESS *only*, this will maintain the existing Viking project
3176 restore_original_filename = ! restore_original_filename;
3177 update_recently_used_document (vw, filename);
3182 if ( ! success || restore_original_filename )
3183 // Load didn't work or want to keep as the existing Viking project, keep using the original name
3184 window_set_filename ( vw, original_filename );
3185 g_free ( original_filename );
3187 vik_window_clear_busy_cursor ( vw );
3190 static void load_file ( GtkAction *a, VikWindow *vw )
3192 GSList *files = NULL;
3193 GSList *cur_file = NULL;
3195 if (!strcmp(gtk_action_get_name(a), "Open")) {
3198 else if (!strcmp(gtk_action_get_name(a), "Append")) {
3202 g_critical("Houston, we've had a problem.");
3206 GtkWidget *dialog = gtk_file_chooser_dialog_new (_("Please select a GPS data file to open. "),
3208 GTK_FILE_CHOOSER_ACTION_OPEN,
3209 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
3210 GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT,
3212 if ( last_folder_files_uri )
3213 gtk_file_chooser_set_current_folder_uri ( GTK_FILE_CHOOSER(dialog), last_folder_files_uri );
3215 GtkFileFilter *filter;
3216 // NB file filters are listed this way for alphabetical ordering
3217 #ifdef VIK_CONFIG_GEOCACHES
3218 filter = gtk_file_filter_new ();
3219 gtk_file_filter_set_name( filter, _("Geocaching") );
3220 gtk_file_filter_add_pattern ( filter, "*.loc" ); // No MIME type available
3221 gtk_file_chooser_add_filter (GTK_FILE_CHOOSER(dialog), filter);
3224 filter = gtk_file_filter_new ();
3225 gtk_file_filter_set_name( filter, _("Google Earth") );
3226 gtk_file_filter_add_mime_type ( filter, "application/vnd.google-earth.kml+xml");
3227 gtk_file_chooser_add_filter (GTK_FILE_CHOOSER(dialog), filter);
3229 filter = gtk_file_filter_new ();
3230 gtk_file_filter_set_name( filter, _("GPX") );
3231 gtk_file_filter_add_pattern ( filter, "*.gpx" ); // No MIME type available
3232 gtk_file_chooser_add_filter (GTK_FILE_CHOOSER(dialog), filter);
3234 filter = gtk_file_filter_new ();
3235 gtk_file_filter_set_name ( filter, _("JPG") );
3236 gtk_file_filter_add_mime_type ( filter, "image/jpeg");
3237 gtk_file_chooser_add_filter (GTK_FILE_CHOOSER(dialog), filter);
3239 filter = gtk_file_filter_new ();
3240 gtk_file_filter_set_name( filter, _("Viking") );
3241 gtk_file_filter_add_pattern ( filter, "*.vik" );
3242 gtk_file_filter_add_pattern ( filter, "*.viking" );
3243 gtk_file_chooser_add_filter (GTK_FILE_CHOOSER(dialog), filter);
3245 // NB could have filters for gpspoint (*.gps,*.gpsoint?) + gpsmapper (*.gsm,*.gpsmapper?)
3246 // However assume this are barely used and thus not worthy of inclusion
3247 // as they'll just make the options too many and have no clear file pattern
3248 // one can always use the all option
3249 filter = gtk_file_filter_new ();
3250 gtk_file_filter_set_name( filter, _("All") );
3251 gtk_file_filter_add_pattern ( filter, "*" );
3252 gtk_file_chooser_add_filter (GTK_FILE_CHOOSER(dialog), filter);
3253 // Default to any file - same as before open filters were added
3254 gtk_file_chooser_set_filter (GTK_FILE_CHOOSER(dialog), filter);
3256 gtk_file_chooser_set_select_multiple ( GTK_FILE_CHOOSER(dialog), TRUE );
3257 gtk_window_set_transient_for ( GTK_WINDOW(dialog), GTK_WINDOW(vw) );
3258 gtk_window_set_destroy_with_parent ( GTK_WINDOW(dialog), TRUE );
3260 if ( gtk_dialog_run ( GTK_DIALOG(dialog) ) == GTK_RESPONSE_ACCEPT )
3262 g_free ( last_folder_files_uri );
3263 last_folder_files_uri = gtk_file_chooser_get_current_folder_uri ( GTK_FILE_CHOOSER(dialog) );
3265 #ifdef VIKING_PROMPT_IF_MODIFIED
3266 if ( (vw->modified || vw->filename) && newwindow )
3268 if ( vw->filename && newwindow )
3270 g_signal_emit ( G_OBJECT(vw), window_signals[VW_OPENWINDOW_SIGNAL], 0, gtk_file_chooser_get_filenames (GTK_FILE_CHOOSER(dialog) ) );
3273 files = gtk_file_chooser_get_filenames (GTK_FILE_CHOOSER(dialog) );
3274 gboolean change_fn = newwindow && (g_slist_length(files)==1); /* only change fn if one file */
3275 gboolean first_vik_file = TRUE;
3277 while ( cur_file ) {
3279 gchar *file_name = cur_file->data;
3280 if ( newwindow && check_file_magic_vik ( file_name ) ) {
3281 // Load first of many .vik files in current window
3282 if ( first_vik_file ) {
3283 remove_default_map_layer ( vw );
3284 vik_window_open_file ( vw, file_name, TRUE );
3285 first_vik_file = FALSE;
3288 // Load each subsequent .vik file in a separate window
3289 VikWindow *newvw = vik_window_new_window ();
3291 vik_window_open_file ( newvw, file_name, TRUE );
3296 vik_window_open_file ( vw, file_name, change_fn );
3299 cur_file = g_slist_next (cur_file);
3301 g_slist_free (files);
3304 gtk_widget_destroy ( dialog );
3307 static gboolean save_file_as ( GtkAction *a, VikWindow *vw )
3309 gboolean rv = FALSE;
3312 GtkWidget *dialog = gtk_file_chooser_dialog_new (_("Save as Viking File."),
3314 GTK_FILE_CHOOSER_ACTION_SAVE,
3315 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
3316 GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT,
3318 if ( last_folder_files_uri )
3319 gtk_file_chooser_set_current_folder_uri ( GTK_FILE_CHOOSER(dialog), last_folder_files_uri );
3321 GtkFileFilter *filter;
3322 filter = gtk_file_filter_new ();
3323 gtk_file_filter_set_name( filter, _("All") );
3324 gtk_file_filter_add_pattern ( filter, "*" );
3325 gtk_file_chooser_add_filter (GTK_FILE_CHOOSER(dialog), filter);
3327 filter = gtk_file_filter_new ();
3328 gtk_file_filter_set_name( filter, _("Viking") );
3329 gtk_file_filter_add_pattern ( filter, "*.vik" );
3330 gtk_file_filter_add_pattern ( filter, "*.viking" );
3331 gtk_file_chooser_add_filter (GTK_FILE_CHOOSER(dialog), filter);
3332 // Default to a Viking file
3333 gtk_file_chooser_set_filter (GTK_FILE_CHOOSER(dialog), filter);
3335 gtk_window_set_transient_for ( GTK_WINDOW(dialog), GTK_WINDOW(vw) );
3336 gtk_window_set_destroy_with_parent ( GTK_WINDOW(dialog), TRUE );
3338 // Auto append / replace extension with '.vik' to the suggested file name as it's going to be a Viking File
3339 gchar* auto_save_name = g_strdup ( window_get_filename ( vw ) );
3340 if ( ! a_file_check_ext ( auto_save_name, ".vik" ) )
3341 auto_save_name = g_strconcat ( auto_save_name, ".vik", NULL );
3343 gtk_file_chooser_set_current_name (GTK_FILE_CHOOSER(dialog), auto_save_name);
3345 while ( gtk_dialog_run ( GTK_DIALOG(dialog) ) == GTK_RESPONSE_ACCEPT )
3347 fn = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER(dialog) );
3348 if ( g_file_test ( fn, G_FILE_TEST_EXISTS ) == FALSE || a_dialog_yes_or_no ( GTK_WINDOW(dialog), _("The file \"%s\" exists, do you wish to overwrite it?"), a_file_basename ( fn ) ) )
3350 window_set_filename ( vw, fn );
3351 rv = window_save ( vw );
3353 vw->modified = FALSE;
3354 g_free ( last_folder_files_uri );
3355 last_folder_files_uri = gtk_file_chooser_get_current_folder_uri ( GTK_FILE_CHOOSER(dialog) );
3360 g_free ( auto_save_name );
3361 gtk_widget_destroy ( dialog );
3365 static gboolean window_save ( VikWindow *vw )
3367 vik_window_set_busy_cursor ( vw );
3368 gboolean success = TRUE;
3370 if ( a_file_save ( vik_layers_panel_get_top_layer ( vw->viking_vlp ), vw->viking_vvp, vw->filename ) )
3372 update_recently_used_document ( vw, vw->filename );
3376 a_dialog_error_msg ( GTK_WINDOW(vw), _("The filename you requested could not be opened for writing.") );
3379 vik_window_clear_busy_cursor ( vw );
3383 static gboolean save_file ( GtkAction *a, VikWindow *vw )
3385 if ( ! vw->filename )
3386 return save_file_as ( NULL, vw );
3389 vw->modified = FALSE;
3390 return window_save ( vw );
3397 * Export all TRW Layers in the list to individual files in the specified directory
3399 * Returns: %TRUE on success
3401 static gboolean export_to ( VikWindow *vw, GList *gl, VikFileType_t vft, const gchar *dir, const gchar *extension )
3403 gboolean success = TRUE;
3405 gint export_count = 0;
3407 vik_window_set_busy_cursor ( vw );
3411 gchar *fn = g_strconcat ( dir, G_DIR_SEPARATOR_S, VIK_LAYER(gl->data)->name, extension, NULL );
3413 // Some protection in attempting to write too many same named files
3414 // As this will get horribly slow...
3415 gboolean safe = FALSE;
3417 while ( ii < 5000 ) {
3418 if ( g_file_test ( fn, G_FILE_TEST_EXISTS ) ) {
3421 fn = g_strdup_printf ( "%s%s%s#%03d%s", dir, G_DIR_SEPARATOR_S, VIK_LAYER(gl->data)->name, ii, extension );
3432 // NB: We allow exporting empty layers
3434 gboolean this_success = a_file_export ( VIK_TRW_LAYER(gl->data), fn, vft, NULL, TRUE );
3436 // Show some progress
3437 if ( this_success ) {
3439 gchar *message = g_strdup_printf ( _("Exporting to file: %s"), fn );
3440 vik_statusbar_set_message ( vw->viking_vs, VIK_STATUSBAR_INFO, message );
3441 while ( gtk_events_pending() )
3442 gtk_main_iteration ();
3446 success = success && this_success;
3450 gl = g_list_next ( gl );
3453 vik_window_clear_busy_cursor ( vw );
3455 // Confirm what happened.
3456 gchar *message = g_strdup_printf ( _("Exported files: %d"), export_count );
3457 vik_statusbar_set_message ( vw->viking_vs, VIK_STATUSBAR_INFO, message );
3463 static void export_to_common ( VikWindow *vw, VikFileType_t vft, const gchar *extension )
3465 GList *gl = vik_layers_panel_get_all_layers_of_type ( vw->viking_vlp, VIK_LAYER_TRW, TRUE );
3468 a_dialog_info_msg ( GTK_WINDOW(vw), _("Nothing to Export!") );
3472 GtkWidget *dialog = gtk_file_chooser_dialog_new ( _("Export to directory"),
3474 GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER,
3476 GTK_RESPONSE_REJECT,
3478 GTK_RESPONSE_ACCEPT,
3480 gtk_window_set_transient_for ( GTK_WINDOW(dialog), GTK_WINDOW(vw) );
3481 gtk_window_set_destroy_with_parent ( GTK_WINDOW(dialog), TRUE );
3482 gtk_window_set_modal ( GTK_WINDOW(dialog), TRUE );
3484 gtk_widget_show_all ( dialog );
3486 if ( gtk_dialog_run ( GTK_DIALOG(dialog) ) == GTK_RESPONSE_ACCEPT ) {
3487 gchar *dir = gtk_file_chooser_get_filename ( GTK_FILE_CHOOSER(dialog) );
3488 gtk_widget_destroy ( dialog );
3490 if ( !export_to ( vw, gl, vft, dir, extension ) )
3491 a_dialog_error_msg ( GTK_WINDOW(vw),_("Could not convert all files") );
3496 gtk_widget_destroy ( dialog );
3501 static void export_to_gpx ( GtkAction *a, VikWindow *vw )
3503 export_to_common ( vw, FILE_TYPE_GPX, ".gpx" );
3506 static void export_to_kml ( GtkAction *a, VikWindow *vw )
3508 export_to_common ( vw, FILE_TYPE_KML, ".kml" );
3511 static void file_properties_cb ( GtkAction *a, VikWindow *vw )
3513 gchar *message = NULL;
3514 if ( vw->filename ) {
3515 if ( g_file_test ( vw->filename, G_FILE_TEST_EXISTS ) ) {
3516 // Get some timestamp information of the file
3518 if ( g_stat ( vw->filename, &stat_buf ) == 0 ) {
3520 strftime ( time_buf, sizeof(time_buf), "%c", gmtime((const time_t *)&stat_buf.st_mtime) );
3522 gint byte_size = stat_buf.st_size;
3523 #if GLIB_CHECK_VERSION(2,30,0)
3524 size = g_format_size_full ( byte_size, G_FORMAT_SIZE_DEFAULT );
3526 size = g_format_size_for_display ( byte_size );
3528 message = g_strdup_printf ( "%s\n\n%s\n\n%s", vw->filename, time_buf, size );
3533 message = g_strdup ( _("File not accessible") );
3536 message = g_strdup ( _("No Viking File") );
3539 a_dialog_info_msg ( GTK_WINDOW(vw), message );
3543 static void my_acquire ( VikWindow *vw, VikDataSourceInterface *datasource )
3545 vik_datasource_mode_t mode = datasource->mode;
3546 if ( mode == VIK_DATASOURCE_AUTO_LAYER_MANAGEMENT )
3547 mode = VIK_DATASOURCE_CREATENEWLAYER;
3548 a_acquire ( vw, vw->viking_vlp, vw->viking_vvp, mode, datasource, NULL, NULL );
3551 static void acquire_from_gps ( GtkAction *a, VikWindow *vw )
3553 my_acquire ( vw, &vik_datasource_gps_interface );
3556 static void acquire_from_file ( GtkAction *a, VikWindow *vw )
3558 my_acquire ( vw, &vik_datasource_file_interface );
3561 static void acquire_from_geojson ( GtkAction *a, VikWindow *vw )
3563 my_acquire ( vw, &vik_datasource_geojson_interface );
3566 static void acquire_from_routing ( GtkAction *a, VikWindow *vw )
3568 my_acquire ( vw, &vik_datasource_routing_interface );
3571 #ifdef VIK_CONFIG_OPENSTREETMAP
3572 static void acquire_from_osm ( GtkAction *a, VikWindow *vw )
3574 my_acquire ( vw, &vik_datasource_osm_interface );
3577 static void acquire_from_my_osm ( GtkAction *a, VikWindow *vw )
3579 my_acquire ( vw, &vik_datasource_osm_my_traces_interface );
3583 #ifdef VIK_CONFIG_GEOCACHES
3584 static void acquire_from_gc ( GtkAction *a, VikWindow *vw )
3586 my_acquire ( vw, &vik_datasource_gc_interface );
3590 #ifdef VIK_CONFIG_GEOTAG
3591 static void acquire_from_geotag ( GtkAction *a, VikWindow *vw )
3593 my_acquire ( vw, &vik_datasource_geotag_interface );
3597 #ifdef VIK_CONFIG_GEONAMES
3598 static void acquire_from_wikipedia ( GtkAction *a, VikWindow *vw )
3600 my_acquire ( vw, &vik_datasource_wikipedia_interface );
3604 static void acquire_from_url ( GtkAction *a, VikWindow *vw )
3606 my_acquire ( vw, &vik_datasource_url_interface );
3609 static void goto_default_location( GtkAction *a, VikWindow *vw)
3612 ll.lat = a_vik_get_default_lat();
3613 ll.lon = a_vik_get_default_long();
3614 vik_viewport_set_center_latlon(vw->viking_vvp, &ll, TRUE);
3615 vik_layers_panel_emit_update(vw->viking_vlp);
3619 static void goto_address( GtkAction *a, VikWindow *vw)
3621 a_vik_goto ( vw, vw->viking_vvp );
3622 vik_layers_panel_emit_update ( vw->viking_vlp );
3625 static void mapcache_flush_cb ( GtkAction *a, VikWindow *vw )
3630 static void menu_copy_centre_cb ( GtkAction *a, VikWindow *vw )
3632 const VikCoord* coord;
3634 gchar *lat = NULL, *lon = NULL;
3636 coord = vik_viewport_get_center ( vw->viking_vvp );
3637 vik_coord_to_utm ( coord, &utm );
3639 gboolean full_format = FALSE;
3640 (void)a_settings_get_boolean ( VIK_SETTINGS_WIN_COPY_CENTRE_FULL_FORMAT, &full_format );
3643 // Bells & Whistles - may include degrees, minutes and second symbols
3644 get_location_strings ( vw, utm, &lat, &lon );
3646 // Simple x.xx y.yy format
3648 a_coords_utm_to_latlon ( &utm, &ll );
3649 lat = g_strdup_printf ( "%.6f", ll.lat );
3650 lon = g_strdup_printf ( "%.6f", ll.lon );
3653 gchar *msg = g_strdup_printf ( "%s %s", lat, lon );
3657 a_clipboard_copy ( VIK_CLIPBOARD_DATA_TEXT, 0, 0, 0, msg, NULL );
3662 static void layer_defaults_cb ( GtkAction *a, VikWindow *vw )
3664 gchar **texts = g_strsplit ( gtk_action_get_name(a), "Layer", 0 );
3667 return; // Internally broken :(
3669 if ( ! a_layer_defaults_show_window ( GTK_WINDOW(vw), texts[1] ) )
3670 a_dialog_info_msg ( GTK_WINDOW(vw), _("This layer has no configurable properties.") );
3671 // NB no update needed
3673 g_strfreev ( texts );
3676 static void preferences_change_update ( VikWindow *vw, gpointer data )
3678 // Want to update all TrackWaypoint layers
3679 GList *layers = vik_layers_panel_get_all_layers_of_type ( vw->viking_vlp, VIK_LAYER_TRW, TRUE );
3685 // Reset the individual waypoints themselves due to the preferences change
3686 VikTrwLayer *vtl = VIK_TRW_LAYER(layers->data);
3687 vik_trw_layer_reset_waypoints ( vtl );
3688 layers = g_list_next ( layers );
3691 g_list_free ( layers );
3696 static void preferences_cb ( GtkAction *a, VikWindow *vw )
3698 gboolean wp_icon_size = a_vik_get_use_large_waypoint_icons();
3700 a_preferences_show_window ( GTK_WINDOW(vw) );
3702 // Has the waypoint size setting changed?
3703 if (wp_icon_size != a_vik_get_use_large_waypoint_icons()) {
3704 // Delete icon indexing 'cache' and so automatically regenerates with the new setting when changed
3705 clear_garmin_icon_syms ();
3707 // Update all windows
3708 g_slist_foreach ( window_list, (GFunc) preferences_change_update, NULL );
3711 // Ensure TZ Lookup initialized
3712 if ( a_vik_get_time_ref_frame() == VIK_TIME_REF_WORLD )
3713 vu_setup_lat_lon_tz_lookup();
3715 toolbar_apply_settings ( vw->viking_vtb, vw->main_vbox, vw->menu_hbox, TRUE );
3718 static void default_location_cb ( GtkAction *a, VikWindow *vw )
3720 /* Simplistic repeat of preference setting
3721 Only the name & type are important for setting the preference via this 'external' way */
3722 VikLayerParam pref_lat[] = {
3723 { VIK_LAYER_NUM_TYPES,
3724 VIKING_PREFERENCES_NAMESPACE "default_latitude",
3725 VIK_LAYER_PARAM_DOUBLE,
3726 VIK_LAYER_GROUP_NONE,
3728 VIK_LAYER_WIDGET_SPINBUTTON,
3737 VikLayerParam pref_lon[] = {
3738 { VIK_LAYER_NUM_TYPES,
3739 VIKING_PREFERENCES_NAMESPACE "default_longitude",
3740 VIK_LAYER_PARAM_DOUBLE,
3741 VIK_LAYER_GROUP_NONE,
3743 VIK_LAYER_WIDGET_SPINBUTTON,
3753 /* Get current center */
3755 vik_coord_to_latlon ( vik_viewport_get_center ( vw->viking_vvp ), &ll );
3757 /* Apply to preferences */
3758 VikLayerParamData vlp_data;
3759 vlp_data.d = ll.lat;
3760 a_preferences_run_setparam (vlp_data, pref_lat);
3761 vlp_data.d = ll.lon;
3762 a_preferences_run_setparam (vlp_data, pref_lon);
3763 /* Remember to save */
3764 a_preferences_save_to_file();
3770 static void clear_cb ( GtkAction *a, VikWindow *vw )
3772 // Do nothing if empty
3773 VikAggregateLayer *top = vik_layers_panel_get_top_layer(vw->viking_vlp);
3774 if ( ! vik_aggregate_layer_is_empty(top) ) {
3775 if ( a_dialog_yes_or_no ( GTK_WINDOW(vw), _("Are you sure you wish to delete all layers?"), NULL ) ) {
3776 vik_layers_panel_clear ( vw->viking_vlp );
3777 window_set_filename ( vw, NULL );
3783 static void window_close ( GtkAction *a, VikWindow *vw )
3785 if ( ! delete_event ( vw ) )
3786 gtk_widget_destroy ( GTK_WIDGET(vw) );
3789 static gboolean save_file_and_exit ( GtkAction *a, VikWindow *vw )
3791 if (save_file( NULL, vw)) {
3792 window_close( NULL, vw);
3799 static void zoom_to_cb ( GtkAction *a, VikWindow *vw )
3801 gdouble xmpp = vik_viewport_get_xmpp ( vw->viking_vvp ), ympp = vik_viewport_get_ympp ( vw->viking_vvp );
3802 if ( a_dialog_custom_zoom ( GTK_WINDOW(vw), &xmpp, &ympp ) )
3804 vik_viewport_set_xmpp ( vw->viking_vvp, xmpp );
3805 vik_viewport_set_ympp ( vw->viking_vvp, ympp );
3810 static void save_image_file ( VikWindow *vw, const gchar *fn, guint w, guint h, gdouble zoom, gboolean save_as_png, gboolean save_kmz )
3812 /* more efficient way: stuff draws directly to pixbuf (fork viewport) */
3813 GdkPixbuf *pixbuf_to_save;
3814 gdouble old_xmpp, old_ympp;
3815 GError *error = NULL;
3817 GtkWidget *msgbox = gtk_message_dialog_new ( GTK_WINDOW(vw),
3818 GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
3821 _("Generating image file...") );
3823 g_signal_connect_swapped (msgbox, "response", G_CALLBACK (gtk_widget_destroy), msgbox);
3824 // Ensure dialog shown
3825 gtk_widget_show_all ( msgbox );
3827 vik_statusbar_set_message ( vw->viking_vs, VIK_STATUSBAR_INFO, _("Generating image file...") );
3828 while ( gtk_events_pending() )
3829 gtk_main_iteration ();
3830 // Despite many efforts & variations, GTK on my Linux system doesn't show the actual msgbox contents :(
3831 // At least the empty box can give a clue something's going on + the statusbar msg...
3832 // Windows version under Wine OK!
3834 /* backup old zoom & set new */
3835 old_xmpp = vik_viewport_get_xmpp ( vw->viking_vvp );
3836 old_ympp = vik_viewport_get_ympp ( vw->viking_vvp );
3837 vik_viewport_set_zoom ( vw->viking_vvp, zoom );
3839 /* reset width and height: */
3840 vik_viewport_configure_manually ( vw->viking_vvp, w, h );
3842 /* draw all layers */
3845 /* save buffer as file. */
3846 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);
3847 if ( !pixbuf_to_save ) {
3848 g_warning("Failed to generate internal pixmap size: %d x %d", w, h);
3849 gtk_message_dialog_set_markup ( GTK_MESSAGE_DIALOG(msgbox), _("Failed to generate internal image.\n\nTry creating a smaller image.") );
3853 int ans = 0; // Default to success
3856 gdouble north, east, south, west;
3857 vik_viewport_get_min_max_lat_lon ( vw->viking_vvp, &south, &north, &west, &east );
3858 ans = kmz_save_file ( pixbuf_to_save, fn, north, east, south, west );
3861 gdk_pixbuf_save ( pixbuf_to_save, fn, save_as_png ? "png" : "jpeg", &error, NULL );
3863 g_warning("Unable to write to file %s: %s", fn, error->message );
3864 g_error_free (error);
3870 gtk_message_dialog_set_markup ( GTK_MESSAGE_DIALOG(msgbox), _("Image file generated.") );
3872 gtk_message_dialog_set_markup ( GTK_MESSAGE_DIALOG(msgbox), _("Failed to generate image file.") );
3874 g_object_unref ( G_OBJECT(pixbuf_to_save) );
3877 vik_statusbar_set_message ( vw->viking_vs, VIK_STATUSBAR_INFO, "" );
3878 gtk_dialog_add_button ( GTK_DIALOG(msgbox), GTK_STOCK_OK, GTK_RESPONSE_OK );
3879 gtk_dialog_run ( GTK_DIALOG(msgbox) ); // Don't care about the result
3881 /* pretend like nothing happened ;) */
3882 vik_viewport_set_xmpp ( vw->viking_vvp, old_xmpp );
3883 vik_viewport_set_ympp ( vw->viking_vvp, old_ympp );
3884 vik_viewport_configure ( vw->viking_vvp );
3888 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 )
3890 gulong size = sizeof(gchar) * (strlen(fn) + 15);
3891 gchar *name_of_file = g_malloc ( size );
3893 struct UTM utm_orig, utm;
3895 /* *** copied from above *** */
3896 GdkPixbuf *pixbuf_to_save;
3897 gdouble old_xmpp, old_ympp;
3898 GError *error = NULL;
3900 /* backup old zoom & set new */
3901 old_xmpp = vik_viewport_get_xmpp ( vw->viking_vvp );
3902 old_ympp = vik_viewport_get_ympp ( vw->viking_vvp );
3903 vik_viewport_set_zoom ( vw->viking_vvp, zoom );
3905 /* reset width and height: do this only once for all images (same size) */
3906 vik_viewport_configure_manually ( vw->viking_vvp, w, h );
3907 /* *** end copy from above *** */
3909 g_assert ( vik_viewport_get_coord_mode ( vw->viking_vvp ) == VIK_COORD_UTM );
3911 if ( g_mkdir(fn,0777) != 0 )
3912 g_warning ( "%s: Failed to create directory %s", __FUNCTION__, fn );
3914 utm_orig = *((const struct UTM *)vik_viewport_get_center ( vw->viking_vvp ));
3916 for ( y = 1; y <= tiles_h; y++ )
3918 for ( x = 1; x <= tiles_w; x++ )
3920 g_snprintf ( name_of_file, size, "%s%cy%d-x%d.%s", fn, G_DIR_SEPARATOR, y, x, save_as_png ? "png" : "jpg" );
3922 if ( tiles_w & 0x1 )
3923 utm.easting += ((gdouble)x - ceil(((gdouble)tiles_w)/2)) * (w*zoom);
3925 utm.easting += ((gdouble)x - (((gdouble)tiles_w)+1)/2) * (w*zoom);
3926 if ( tiles_h & 0x1 ) /* odd */
3927 utm.northing -= ((gdouble)y - ceil(((gdouble)tiles_h)/2)) * (h*zoom);
3929 utm.northing -= ((gdouble)y - (((gdouble)tiles_h)+1)/2) * (h*zoom);
3931 /* move to correct place. */
3932 vik_viewport_set_center_utm ( vw->viking_vvp, &utm, FALSE );
3936 /* save buffer as file. */
3937 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);
3938 gdk_pixbuf_save ( pixbuf_to_save, name_of_file, save_as_png ? "png" : "jpeg", &error, NULL );
3941 gchar *msg = g_strdup_printf (_("Unable to write to file %s: %s"), name_of_file, error->message );
3942 vik_statusbar_set_message ( vw->viking_vs, VIK_STATUSBAR_INFO, msg );
3944 g_error_free (error);
3947 g_object_unref ( G_OBJECT(pixbuf_to_save) );
3951 vik_viewport_set_center_utm ( vw->viking_vvp, &utm_orig, FALSE );
3952 vik_viewport_set_xmpp ( vw->viking_vvp, old_xmpp );
3953 vik_viewport_set_ympp ( vw->viking_vvp, old_ympp );
3954 vik_viewport_configure ( vw->viking_vvp );
3957 g_free ( name_of_file );
3960 static void draw_to_image_file_current_window_cb(GtkWidget* widget,GdkEventButton *event,gpointer *pass_along)
3962 VikWindow *vw = VIK_WINDOW(pass_along[0]);
3963 GtkSpinButton *width_spin = GTK_SPIN_BUTTON(pass_along[1]), *height_spin = GTK_SPIN_BUTTON(pass_along[2]);
3965 gint active = gtk_combo_box_get_active ( GTK_COMBO_BOX(pass_along[3]) );
3966 gdouble zoom = pow (2, active-2 );
3968 gdouble width_min, width_max, height_min, height_max;
3971 gtk_spin_button_get_range ( width_spin, &width_min, &width_max );
3972 gtk_spin_button_get_range ( height_spin, &height_min, &height_max );
3974 /* TODO: support for xzoom and yzoom values */
3975 width = vik_viewport_get_width ( vw->viking_vvp ) * vik_viewport_get_xmpp ( vw->viking_vvp ) / zoom;
3976 height = vik_viewport_get_height ( vw->viking_vvp ) * vik_viewport_get_xmpp ( vw->viking_vvp ) / zoom;
3978 if ( width > width_max || width < width_min || height > height_max || height < height_min )
3979 a_dialog_info_msg ( GTK_WINDOW(vw), _("Viewable region outside allowable pixel size bounds for image. Clipping width/height values.") );
3981 gtk_spin_button_set_value ( width_spin, width );
3982 gtk_spin_button_set_value ( height_spin, height );
3985 static void draw_to_image_file_total_area_cb (GtkSpinButton *spinbutton, gpointer *pass_along)
3987 GtkSpinButton *width_spin = GTK_SPIN_BUTTON(pass_along[1]), *height_spin = GTK_SPIN_BUTTON(pass_along[2]);
3989 gint active = gtk_combo_box_get_active ( GTK_COMBO_BOX(pass_along[3]) );
3990 gdouble zoom = pow (2, active-2 );
3994 w = gtk_spin_button_get_value(width_spin) * zoom;
3995 h = gtk_spin_button_get_value(height_spin) * zoom;
3996 if (pass_along[4]) /* save many images; find TOTAL area covered */
3998 w *= gtk_spin_button_get_value(GTK_SPIN_BUTTON(pass_along[4]));
3999 h *= gtk_spin_button_get_value(GTK_SPIN_BUTTON(pass_along[5]));
4001 vik_units_distance_t dist_units = a_vik_get_units_distance ();
4002 switch (dist_units) {
4003 case VIK_UNITS_DISTANCE_KILOMETRES:
4004 label_text = g_strdup_printf ( _("Total area: %ldm x %ldm (%.3f sq. km)"), (glong)w, (glong)h, (w*h/1000000));
4006 case VIK_UNITS_DISTANCE_MILES:
4007 label_text = g_strdup_printf ( _("Total area: %ldm x %ldm (%.3f sq. miles)"), (glong)w, (glong)h, (w*h/2589988.11));
4009 case VIK_UNITS_DISTANCE_NAUTICAL_MILES:
4010 label_text = g_strdup_printf ( _("Total area: %ldm x %ldm (%.3f sq. NM)"), (glong)w, (glong)h, (w*h/(1852.0*1852.0)));
4013 label_text = g_strdup_printf ("Just to keep the compiler happy");
4014 g_critical("Houston, we've had a problem. distance=%d", dist_units);
4017 gtk_label_set_text(GTK_LABEL(pass_along[6]), label_text);
4018 g_free ( label_text );
4022 VW_GEN_SINGLE_IMAGE,
4023 VW_GEN_DIRECTORY_OF_IMAGES,
4028 * Get an allocated filename (or directory as specified)
4030 static gchar* draw_image_filename ( VikWindow *vw, img_generation_t img_gen )
4033 if ( img_gen != VW_GEN_DIRECTORY_OF_IMAGES )
4036 GtkWidget *dialog = gtk_file_chooser_dialog_new (_("Save Image"),
4038 GTK_FILE_CHOOSER_ACTION_SAVE,
4039 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
4040 GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT,
4042 if ( last_folder_images_uri )
4043 gtk_file_chooser_set_current_folder_uri ( GTK_FILE_CHOOSER(dialog), last_folder_images_uri );
4045 GtkFileChooser *chooser = GTK_FILE_CHOOSER ( dialog );
4047 GtkFileFilter *filter;
4048 filter = gtk_file_filter_new ();
4049 gtk_file_filter_set_name ( filter, _("All") );
4050 gtk_file_filter_add_pattern ( filter, "*" );
4051 gtk_file_chooser_add_filter ( chooser, filter );
4053 if ( img_gen == VW_GEN_KMZ_FILE ) {
4054 filter = gtk_file_filter_new ();
4055 gtk_file_filter_set_name ( filter, _("KMZ") );
4056 gtk_file_filter_add_mime_type ( filter, "vnd.google-earth.kmz");
4057 gtk_file_filter_add_pattern ( filter, "*.kmz" );
4058 gtk_file_chooser_add_filter ( chooser, filter );
4059 gtk_file_chooser_set_filter ( chooser, filter );
4062 filter = gtk_file_filter_new ();
4063 gtk_file_filter_set_name ( filter, _("JPG") );
4064 gtk_file_filter_add_mime_type ( filter, "image/jpeg");
4065 gtk_file_chooser_add_filter ( chooser, filter );
4067 if ( !vw->draw_image_save_as_png )
4068 gtk_file_chooser_set_filter ( chooser, filter );
4070 filter = gtk_file_filter_new ();
4071 gtk_file_filter_set_name ( filter, _("PNG") );
4072 gtk_file_filter_add_mime_type ( filter, "image/png");
4073 gtk_file_chooser_add_filter ( chooser, filter );
4075 if ( vw->draw_image_save_as_png )
4076 gtk_file_chooser_set_filter ( chooser, filter );
4079 gtk_window_set_transient_for ( GTK_WINDOW(dialog), GTK_WINDOW(vw) );
4080 gtk_window_set_destroy_with_parent ( GTK_WINDOW(dialog), TRUE );
4082 if ( gtk_dialog_run ( GTK_DIALOG(dialog) ) == GTK_RESPONSE_ACCEPT ) {
4083 g_free ( last_folder_images_uri );
4084 last_folder_images_uri = gtk_file_chooser_get_current_folder_uri ( GTK_FILE_CHOOSER(dialog) );
4086 fn = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER(dialog) );
4087 if ( g_file_test ( fn, G_FILE_TEST_EXISTS ) )
4088 if ( ! a_dialog_yes_or_no ( GTK_WINDOW(dialog), _("The file \"%s\" exists, do you wish to overwrite it?"), a_file_basename ( fn ) ) )
4091 gtk_widget_destroy ( dialog );
4095 // For some reason this method is only written to work in UTM...
4096 if ( vik_viewport_get_coord_mode(vw->viking_vvp) != VIK_COORD_UTM ) {
4097 a_dialog_error_msg ( GTK_WINDOW(vw), _("You must be in UTM mode to use this feature") );
4101 GtkWidget *dialog = gtk_file_chooser_dialog_new (_("Choose a directory to hold images"),
4103 GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER,
4104 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
4105 GTK_STOCK_OK, GTK_RESPONSE_ACCEPT,
4107 gtk_window_set_transient_for ( GTK_WINDOW(dialog), GTK_WINDOW(vw) );
4108 gtk_window_set_destroy_with_parent ( GTK_WINDOW(dialog), TRUE );
4110 if ( gtk_dialog_run ( GTK_DIALOG(dialog) ) == GTK_RESPONSE_ACCEPT ) {
4111 fn = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER(dialog) );
4113 gtk_widget_destroy ( dialog );
4118 static void draw_to_image_file ( VikWindow *vw, img_generation_t img_gen )
4120 /* todo: default for answers inside VikWindow or static (thruout instance) */
4121 GtkWidget *dialog = gtk_dialog_new_with_buttons ( _("Save to Image File"), GTK_WINDOW(vw),
4122 GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
4124 GTK_RESPONSE_REJECT,
4126 GTK_RESPONSE_ACCEPT,
4128 GtkWidget *width_label, *width_spin, *height_label, *height_spin;
4129 GtkWidget *current_window_button;
4130 gpointer current_window_pass_along[7];
4131 GtkWidget *zoom_label, *zoom_combo;
4132 GtkWidget *total_size_label;
4134 // only used for VW_GEN_DIRECTORY_OF_IMAGES
4135 GtkWidget *tiles_width_spin = NULL, *tiles_height_spin = NULL;
4137 width_label = gtk_label_new ( _("Width (pixels):") );
4138 width_spin = gtk_spin_button_new ( GTK_ADJUSTMENT(gtk_adjustment_new ( vw->draw_image_width, 10, 50000, 10, 100, 0 )), 10, 0 );
4139 height_label = gtk_label_new ( _("Height (pixels):") );
4140 height_spin = gtk_spin_button_new ( GTK_ADJUSTMENT(gtk_adjustment_new ( vw->draw_image_height, 10, 50000, 10, 100, 0 )), 10, 0 );
4142 GtkWidget *win_warning_label = gtk_label_new ( _("WARNING: USING LARGE IMAGES OVER 10000x10000\nMAY CRASH THE PROGRAM!") );
4144 zoom_label = gtk_label_new ( _("Zoom (meters per pixel):") );
4145 /* TODO: separate xzoom and yzoom factors */
4146 zoom_combo = create_zoom_combo_all_levels();
4148 gdouble mpp = vik_viewport_get_xmpp(vw->viking_vvp);
4149 gint active = 2 + round ( log (mpp) / log (2) );
4151 // Can we not hard code size here?
4156 gtk_combo_box_set_active ( GTK_COMBO_BOX(zoom_combo), active );
4158 total_size_label = gtk_label_new ( NULL );
4160 current_window_button = gtk_button_new_with_label ( _("Area in current viewable window") );
4161 current_window_pass_along [0] = vw;
4162 current_window_pass_along [1] = width_spin;
4163 current_window_pass_along [2] = height_spin;
4164 current_window_pass_along [3] = zoom_combo;
4165 current_window_pass_along [4] = NULL; // Only for directory of tiles: width
4166 current_window_pass_along [5] = NULL; // Only for directory of tiles: height
4167 current_window_pass_along [6] = total_size_label;
4168 g_signal_connect ( G_OBJECT(current_window_button), "button_press_event", G_CALLBACK(draw_to_image_file_current_window_cb), current_window_pass_along );
4170 GtkWidget *png_radio = gtk_radio_button_new_with_label ( NULL, _("Save as PNG") );
4171 GtkWidget *jpeg_radio = gtk_radio_button_new_with_label_from_widget ( GTK_RADIO_BUTTON(png_radio), _("Save as JPEG") );
4173 if ( img_gen == VW_GEN_KMZ_FILE ) {
4174 // Don't show image type selection if creating a KMZ (always JPG internally)
4175 // Start with viewable area by default
4176 draw_to_image_file_current_window_cb ( current_window_button, NULL, current_window_pass_along );
4178 gtk_box_pack_start (GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), jpeg_radio, FALSE, FALSE, 0);
4179 gtk_box_pack_start (GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), png_radio, FALSE, FALSE, 0);
4182 if ( ! vw->draw_image_save_as_png )
4183 gtk_toggle_button_set_active ( GTK_TOGGLE_BUTTON(jpeg_radio), TRUE );
4185 gtk_box_pack_start (GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), width_label, FALSE, FALSE, 0);
4186 gtk_box_pack_start (GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), width_spin, FALSE, FALSE, 0);
4187 gtk_box_pack_start (GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), height_label, FALSE, FALSE, 0);
4188 gtk_box_pack_start (GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), height_spin, FALSE, FALSE, 0);
4190 gtk_box_pack_start (GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), win_warning_label, FALSE, FALSE, 0);
4192 gtk_box_pack_start (GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), current_window_button, FALSE, FALSE, 0);
4193 gtk_box_pack_start (GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), zoom_label, FALSE, FALSE, 0);
4194 gtk_box_pack_start (GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), zoom_combo, FALSE, FALSE, 0);
4196 if ( img_gen == VW_GEN_DIRECTORY_OF_IMAGES )
4198 GtkWidget *tiles_width_label, *tiles_height_label;
4200 tiles_width_label = gtk_label_new ( _("East-west image tiles:") );
4201 tiles_width_spin = gtk_spin_button_new ( GTK_ADJUSTMENT(gtk_adjustment_new ( 5, 1, 10, 1, 100, 0 )), 1, 0 );
4202 tiles_height_label = gtk_label_new ( _("North-south image tiles:") );
4203 tiles_height_spin = gtk_spin_button_new ( GTK_ADJUSTMENT(gtk_adjustment_new ( 5, 1, 10, 1, 100, 0 )), 1, 0 );
4204 gtk_box_pack_start (GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), tiles_width_label, FALSE, FALSE, 0);
4205 gtk_box_pack_start (GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), tiles_width_spin, FALSE, FALSE, 0);
4206 gtk_box_pack_start (GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), tiles_height_label, FALSE, FALSE, 0);
4207 gtk_box_pack_start (GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), tiles_height_spin, FALSE, FALSE, 0);
4209 current_window_pass_along [4] = tiles_width_spin;
4210 current_window_pass_along [5] = tiles_height_spin;
4211 g_signal_connect ( G_OBJECT(tiles_width_spin), "value-changed", G_CALLBACK(draw_to_image_file_total_area_cb), current_window_pass_along );
4212 g_signal_connect ( G_OBJECT(tiles_height_spin), "value-changed", G_CALLBACK(draw_to_image_file_total_area_cb), current_window_pass_along );
4214 gtk_box_pack_start (GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), total_size_label, FALSE, FALSE, 0);
4215 g_signal_connect ( G_OBJECT(width_spin), "value-changed", G_CALLBACK(draw_to_image_file_total_area_cb), current_window_pass_along );
4216 g_signal_connect ( G_OBJECT(height_spin), "value-changed", G_CALLBACK(draw_to_image_file_total_area_cb), current_window_pass_along );
4217 g_signal_connect ( G_OBJECT(zoom_combo), "changed", G_CALLBACK(draw_to_image_file_total_area_cb), current_window_pass_along );
4219 draw_to_image_file_total_area_cb ( NULL, current_window_pass_along ); /* set correct size info now */
4221 gtk_dialog_set_default_response ( GTK_DIALOG(dialog), GTK_RESPONSE_ACCEPT );
4223 gtk_widget_show_all ( gtk_dialog_get_content_area(GTK_DIALOG(dialog)) );
4225 if ( gtk_dialog_run ( GTK_DIALOG(dialog) ) == GTK_RESPONSE_ACCEPT )
4227 gtk_widget_hide ( GTK_WIDGET(dialog) );
4229 gchar *fn = draw_image_filename ( vw, img_gen );
4233 gint active_z = gtk_combo_box_get_active ( GTK_COMBO_BOX(zoom_combo) );
4234 gdouble zoom = pow (2, active_z-2 );
4236 if ( img_gen == VW_GEN_SINGLE_IMAGE )
4237 save_image_file ( vw, fn,
4238 vw->draw_image_width = gtk_spin_button_get_value_as_int ( GTK_SPIN_BUTTON(width_spin) ),
4239 vw->draw_image_height = gtk_spin_button_get_value_as_int ( GTK_SPIN_BUTTON(height_spin) ),
4241 vw->draw_image_save_as_png = gtk_toggle_button_get_active ( GTK_TOGGLE_BUTTON(png_radio) ),
4243 else if ( img_gen == VW_GEN_KMZ_FILE ) {
4244 // Remove some viewport overlays as these aren't useful in KMZ file.
4245 gboolean restore_xhair = vik_viewport_get_draw_centermark ( vw->viking_vvp );
4246 if ( restore_xhair )
4247 vik_viewport_set_draw_centermark ( vw->viking_vvp, FALSE );
4248 gboolean restore_scale = vik_viewport_get_draw_scale ( vw->viking_vvp );
4249 if ( restore_scale )
4250 vik_viewport_set_draw_scale ( vw->viking_vvp, FALSE );
4252 save_image_file ( vw,
4254 gtk_spin_button_get_value_as_int ( GTK_SPIN_BUTTON(width_spin) ),
4255 gtk_spin_button_get_value_as_int ( GTK_SPIN_BUTTON(height_spin) ),
4260 if ( restore_xhair )
4261 vik_viewport_set_draw_centermark ( vw->viking_vvp, TRUE );
4262 if ( restore_scale )
4263 vik_viewport_set_draw_scale ( vw->viking_vvp, TRUE );
4264 if ( restore_xhair || restore_scale )
4268 // NB is in UTM mode ATM
4269 save_image_dir ( vw, fn,
4270 vw->draw_image_width = gtk_spin_button_get_value_as_int ( GTK_SPIN_BUTTON(width_spin) ),
4271 vw->draw_image_height = gtk_spin_button_get_value_as_int ( GTK_SPIN_BUTTON(height_spin) ),
4273 vw->draw_image_save_as_png = gtk_toggle_button_get_active ( GTK_TOGGLE_BUTTON(png_radio) ),
4274 gtk_spin_button_get_value ( GTK_SPIN_BUTTON(tiles_width_spin) ),
4275 gtk_spin_button_get_value ( GTK_SPIN_BUTTON(tiles_height_spin) ) );
4280 gtk_widget_destroy ( GTK_WIDGET(dialog) );
4283 static void draw_to_kmz_file_cb ( GtkAction *a, VikWindow *vw )
4285 if ( vik_viewport_get_coord_mode(vw->viking_vvp) == VIK_COORD_UTM ) {
4286 a_dialog_error_msg ( GTK_WINDOW(vw), _("This feature is not available in UTM mode") );
4289 // NB ATM This only generates a KMZ file with the current viewport image - intended mostly for map images [but will include any lines/icons from track & waypoints that are drawn]
4290 // (it does *not* include a full KML dump of every track, waypoint etc...)
4291 draw_to_image_file ( vw, VW_GEN_KMZ_FILE );
4294 static void draw_to_image_file_cb ( GtkAction *a, VikWindow *vw )
4296 draw_to_image_file ( vw, VW_GEN_SINGLE_IMAGE );
4299 static void draw_to_image_dir_cb ( GtkAction *a, VikWindow *vw )
4301 draw_to_image_file ( vw, VW_GEN_DIRECTORY_OF_IMAGES );
4307 static void import_kmz_file_cb ( GtkAction *a, VikWindow *vw )
4309 GtkWidget *dialog = gtk_file_chooser_dialog_new (_("Open File"),
4311 GTK_FILE_CHOOSER_ACTION_OPEN,
4312 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
4313 GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT,
4316 GtkFileFilter *filter;
4317 filter = gtk_file_filter_new ();
4318 gtk_file_filter_set_name ( filter, _("KMZ") );
4319 gtk_file_filter_add_mime_type ( filter, "vnd.google-earth.kmz");
4320 gtk_file_filter_add_pattern ( filter, "*.kmz" );
4321 gtk_file_chooser_add_filter ( GTK_FILE_CHOOSER(dialog), filter );
4322 gtk_file_chooser_set_filter ( GTK_FILE_CHOOSER(dialog), filter );
4324 filter = gtk_file_filter_new ();
4325 gtk_file_filter_set_name( filter, _("All") );
4326 gtk_file_filter_add_pattern ( filter, "*" );
4327 gtk_file_chooser_add_filter ( GTK_FILE_CHOOSER(dialog), filter );
4328 // Default to any file - same as before open filters were added
4329 gtk_file_chooser_set_filter ( GTK_FILE_CHOOSER(dialog), filter );
4331 if ( gtk_dialog_run ( GTK_DIALOG(dialog) ) == GTK_RESPONSE_ACCEPT ) {
4332 gchar *fn = gtk_file_chooser_get_filename ( GTK_FILE_CHOOSER(dialog) );
4333 // TODO convert ans value into readable explaination of failure...
4334 int ans = kmz_open_file ( fn, vw->viking_vvp, vw->viking_vlp );
4336 a_dialog_error_msg_extra ( GTK_WINDOW(vw), _("Unable to import %s."), fn );
4340 gtk_widget_destroy ( dialog );
4343 static void print_cb ( GtkAction *a, VikWindow *vw )
4345 a_print(vw, vw->viking_vvp);
4348 /* really a misnomer: changes coord mode (actual coordinates) AND/OR draw mode (viewport only) */
4349 static void window_change_coord_mode_cb ( GtkAction *old_a, GtkAction *a, VikWindow *vw )
4351 const gchar *name = gtk_action_get_name(a);
4352 GtkToggleToolButton *tbutton = (GtkToggleToolButton *)toolbar_get_widget_by_name ( vw->viking_vtb, name );
4354 gtk_toggle_tool_button_set_active ( tbutton, TRUE );
4356 VikViewportDrawMode drawmode;
4357 if (!g_strcmp0(name, "ModeUTM")) {
4358 drawmode = VIK_VIEWPORT_DRAWMODE_UTM;
4360 else if (!g_strcmp0(name, "ModeLatLon")) {
4361 drawmode = VIK_VIEWPORT_DRAWMODE_LATLON;
4363 else if (!g_strcmp0(name, "ModeExpedia")) {
4364 drawmode = VIK_VIEWPORT_DRAWMODE_EXPEDIA;
4366 else if (!g_strcmp0(name, "ModeMercator")) {
4367 drawmode = VIK_VIEWPORT_DRAWMODE_MERCATOR;
4370 g_critical("Houston, we've had a problem.");
4374 if ( !vw->only_updating_coord_mode_ui )
4376 VikViewportDrawMode olddrawmode = vik_viewport_get_drawmode ( vw->viking_vvp );
4377 if ( olddrawmode != drawmode )
4379 /* this takes care of coord mode too */
4380 vik_viewport_set_drawmode ( vw->viking_vvp, drawmode );
4381 if ( drawmode == VIK_VIEWPORT_DRAWMODE_UTM ) {
4382 vik_layers_panel_change_coord_mode ( vw->viking_vlp, VIK_COORD_UTM );
4383 } else if ( olddrawmode == VIK_VIEWPORT_DRAWMODE_UTM ) {
4384 vik_layers_panel_change_coord_mode ( vw->viking_vlp, VIK_COORD_LATLON );
4391 static void toggle_draw_scale ( GtkAction *a, VikWindow *vw )
4393 gboolean state = !vik_viewport_get_draw_scale ( vw->viking_vvp );
4394 GtkWidget *check_box = gtk_ui_manager_get_widget ( vw->uim, "/ui/MainMenu/View/SetShow/ShowScale" );
4397 gtk_check_menu_item_set_active ( GTK_CHECK_MENU_ITEM(check_box), state );
4398 vik_viewport_set_draw_scale ( vw->viking_vvp, state );
4402 static void toggle_draw_centermark ( GtkAction *a, VikWindow *vw )
4404 gboolean state = !vik_viewport_get_draw_centermark ( vw->viking_vvp );
4405 GtkWidget *check_box = gtk_ui_manager_get_widget ( vw->uim, "/ui/MainMenu/View/SetShow/ShowCenterMark" );
4408 gtk_check_menu_item_set_active ( GTK_CHECK_MENU_ITEM(check_box), state );
4409 vik_viewport_set_draw_centermark ( vw->viking_vvp, state );
4413 static void toggle_draw_highlight ( GtkAction *a, VikWindow *vw )
4415 gboolean state = !vik_viewport_get_draw_highlight ( vw->viking_vvp );
4416 GtkWidget *check_box = gtk_ui_manager_get_widget ( vw->uim, "/ui/MainMenu/View/SetShow/ShowHighlight" );
4419 gtk_check_menu_item_set_active ( GTK_CHECK_MENU_ITEM(check_box), state );
4420 vik_viewport_set_draw_highlight ( vw->viking_vvp, state );
4424 static void set_bg_color ( GtkAction *a, VikWindow *vw )
4426 GtkWidget *colorsd = gtk_color_selection_dialog_new ( _("Choose a background color") );
4427 GdkColor *color = vik_viewport_get_background_gdkcolor ( vw->viking_vvp );
4428 gtk_color_selection_set_previous_color ( GTK_COLOR_SELECTION(gtk_color_selection_dialog_get_color_selection(GTK_COLOR_SELECTION_DIALOG(colorsd))), color );
4429 gtk_color_selection_set_current_color ( GTK_COLOR_SELECTION(gtk_color_selection_dialog_get_color_selection(GTK_COLOR_SELECTION_DIALOG(colorsd))), color );
4430 if ( gtk_dialog_run ( GTK_DIALOG(colorsd) ) == GTK_RESPONSE_OK )
4432 gtk_color_selection_get_current_color ( GTK_COLOR_SELECTION(gtk_color_selection_dialog_get_color_selection(GTK_COLOR_SELECTION_DIALOG(colorsd))), color );
4433 vik_viewport_set_background_gdkcolor ( vw->viking_vvp, color );
4437 gtk_widget_destroy ( colorsd );
4440 static void set_highlight_color ( GtkAction *a, VikWindow *vw )
4442 GtkWidget *colorsd = gtk_color_selection_dialog_new ( _("Choose a track highlight color") );
4443 GdkColor *color = vik_viewport_get_highlight_gdkcolor ( vw->viking_vvp );
4444 gtk_color_selection_set_previous_color ( GTK_COLOR_SELECTION(gtk_color_selection_dialog_get_color_selection(GTK_COLOR_SELECTION_DIALOG(colorsd))), color );
4445 gtk_color_selection_set_current_color ( GTK_COLOR_SELECTION(gtk_color_selection_dialog_get_color_selection(GTK_COLOR_SELECTION_DIALOG(colorsd))), color );
4446 if ( gtk_dialog_run ( GTK_DIALOG(colorsd) ) == GTK_RESPONSE_OK )
4448 gtk_color_selection_get_current_color ( GTK_COLOR_SELECTION(gtk_color_selection_dialog_get_color_selection(GTK_COLOR_SELECTION_DIALOG(colorsd))), color );
4449 vik_viewport_set_highlight_gdkcolor ( vw->viking_vvp, color );
4453 gtk_widget_destroy ( colorsd );
4457 /***********************************************************************************************
4459 ***********************************************************************************************/
4461 const GdkPixdata *data;
4463 } icon_definition_t;
4466 a_register_icon ( GtkIconFactory *icon_factory, icon_definition_t icon )
4468 if ( !icon.data ) return;
4469 GtkIconSet *icon_set = gtk_icon_set_new_from_pixbuf (gdk_pixbuf_from_pixdata ( icon.data, FALSE, NULL ));
4470 gtk_icon_factory_add ( icon_factory, icon.name, icon_set );
4471 gtk_icon_set_unref ( icon_set );
4475 static GtkActionEntry entries[] = {
4476 { "File", NULL, N_("_File"), 0, 0, 0 },
4477 { "Edit", NULL, N_("_Edit"), 0, 0, 0 },
4478 { "View", NULL, N_("_View"), 0, 0, 0 },
4479 { "SetShow", NULL, N_("_Show"), 0, 0, 0 },
4480 { "SetZoom", NULL, N_("_Zoom"), 0, 0, 0 },
4481 { "SetPan", NULL, N_("_Pan"), 0, 0, 0 },
4482 { "Layers", NULL, N_("_Layers"), 0, 0, 0 },
4483 { "Tools", NULL, N_("_Tools"), 0, 0, 0 },
4484 { "Exttools", NULL, N_("_Webtools"), 0, 0, 0 },
4485 { "Help", NULL, N_("_Help"), 0, 0, 0 },
4487 { "New", GTK_STOCK_NEW, N_("_New"), "<control>N", N_("New file"), (GCallback)newwindow_cb },
4488 { "Open", GTK_STOCK_OPEN, N_("_Open..."), "<control>O", N_("Open a file"), (GCallback)load_file },
4489 { "OpenRecentFile", NULL, N_("Open _Recent File"), NULL, NULL, (GCallback)NULL },
4490 { "Append", GTK_STOCK_ADD, N_("Append _File..."), NULL, N_("Append data from a different file"), (GCallback)load_file },
4491 { "Export", GTK_STOCK_CONVERT, N_("_Export All"), NULL, N_("Export All TrackWaypoint Layers"), (GCallback)NULL },
4492 { "ExportGPX", NULL, N_("_GPX..."), NULL, N_("Export as GPX"), (GCallback)export_to_gpx },
4493 { "Acquire", GTK_STOCK_GO_DOWN, N_("A_cquire"), NULL, NULL, (GCallback)NULL },
4494 { "AcquireRouting", NULL, N_("_Directions..."), NULL, N_("Get driving directions"), (GCallback)acquire_from_routing },
4495 #ifdef VIK_CONFIG_OPENSTREETMAP
4496 { "AcquireOSM", NULL, N_("_OSM Traces..."), NULL, N_("Get traces from OpenStreetMap"), (GCallback)acquire_from_osm },
4497 { "AcquireMyOSM", NULL, N_("_My OSM Traces..."), NULL, N_("Get Your Own Traces from OpenStreetMap"), (GCallback)acquire_from_my_osm },
4499 #ifdef VIK_CONFIG_GEOCACHES
4500 { "AcquireGC", NULL, N_("Geo_caches..."), NULL, N_("Get Geocaches from geocaching.com"), (GCallback)acquire_from_gc },
4502 #ifdef VIK_CONFIG_GEOTAG
4503 { "AcquireGeotag", NULL, N_("From Geotagged _Images..."), NULL, N_("Create waypoints from geotagged images"), (GCallback)acquire_from_geotag },
4505 { "AcquireURL", NULL, N_("From _URL..."), NULL, N_("Get a file from a URL"), (GCallback)acquire_from_url },
4506 #ifdef VIK_CONFIG_GEONAMES
4507 { "AcquireWikipedia", NULL, N_("From _Wikipedia Waypoints"), NULL, N_("Create waypoints from Wikipedia items in the current view"), (GCallback)acquire_from_wikipedia },
4509 { "Save", GTK_STOCK_SAVE, N_("_Save"), "<control>S", N_("Save the file"), (GCallback)save_file },
4510 { "SaveAs", GTK_STOCK_SAVE_AS, N_("Save _As..."), NULL, N_("Save the file under different name"), (GCallback)save_file_as },
4511 { "FileProperties", NULL, N_("Properties..."), NULL, N_("File Properties"), (GCallback)file_properties_cb },
4513 { "ImportKMZ", GTK_STOCK_CONVERT, N_("Import KMZ _Map File..."), NULL, N_("Import a KMZ file"), (GCallback)import_kmz_file_cb },
4514 { "GenKMZ", GTK_STOCK_DND, N_("Generate _KMZ Map File..."), NULL, N_("Generate a KMZ file with an overlay of the current view"), (GCallback)draw_to_kmz_file_cb },
4516 { "GenImg", GTK_STOCK_FILE, N_("_Generate Image File..."), NULL, N_("Save a snapshot of the workspace into a file"), (GCallback)draw_to_image_file_cb },
4517 { "GenImgDir", GTK_STOCK_DND_MULTIPLE, N_("Generate _Directory of Images..."), NULL, N_("Generate _Directory of Images"), (GCallback)draw_to_image_dir_cb },
4518 { "Print", GTK_STOCK_PRINT, N_("_Print..."), NULL, N_("Print maps"), (GCallback)print_cb },
4519 { "Exit", GTK_STOCK_QUIT, N_("E_xit"), "<control>W", N_("Exit the program"), (GCallback)window_close },
4520 { "SaveExit", GTK_STOCK_QUIT, N_("Save and Exit"), NULL, N_("Save and Exit the program"), (GCallback)save_file_and_exit },
4522 { "GoBack", GTK_STOCK_GO_BACK, N_("Go to the Pre_vious Location"), NULL, N_("Go to the previous location"), (GCallback)draw_goto_back_and_forth },
4523 { "GoForward", GTK_STOCK_GO_FORWARD, N_("Go to the _Next Location"), NULL, N_("Go to the next location"), (GCallback)draw_goto_back_and_forth },
4524 { "GotoDefaultLocation", GTK_STOCK_HOME, N_("Go to the _Default Location"), NULL, N_("Go to the default location"), (GCallback)goto_default_location },
4525 { "GotoSearch", GTK_STOCK_JUMP_TO, N_("Go to _Location..."), NULL, N_("Go to address/place using text search"), (GCallback)goto_address },
4526 { "GotoLL", GTK_STOCK_JUMP_TO, N_("_Go to Lat/Lon..."), NULL, N_("Go to arbitrary lat/lon coordinate"), (GCallback)draw_goto_cb },
4527 { "GotoUTM", GTK_STOCK_JUMP_TO, N_("Go to UTM..."), NULL, N_("Go to arbitrary UTM coordinate"), (GCallback)draw_goto_cb },
4528 { "Refresh", GTK_STOCK_REFRESH, N_("_Refresh"), "F5", N_("Refresh any maps displayed"), (GCallback)draw_refresh_cb },
4529 { "SetHLColor",GTK_STOCK_SELECT_COLOR, N_("Set _Highlight Color..."), NULL, N_("Set Highlight Color"), (GCallback)set_highlight_color },
4530 { "SetBGColor",GTK_STOCK_SELECT_COLOR, N_("Set Bac_kground Color..."), NULL, N_("Set Background Color"), (GCallback)set_bg_color },
4531 { "ZoomIn", GTK_STOCK_ZOOM_IN, N_("Zoom _In"), "<control>plus", N_("Zoom In"), (GCallback)draw_zoom_cb },
4532 { "ZoomOut", GTK_STOCK_ZOOM_OUT, N_("Zoom _Out"), "<control>minus", N_("Zoom Out"), (GCallback)draw_zoom_cb },
4533 { "ZoomTo", GTK_STOCK_ZOOM_FIT, N_("Zoom _To..."), "<control>Z", N_("Zoom To"), (GCallback)zoom_to_cb },
4534 { "PanNorth", NULL, N_("Pan _North"), "<control>Up", NULL, (GCallback)draw_pan_cb },
4535 { "PanEast", NULL, N_("Pan _East"), "<control>Right", NULL, (GCallback)draw_pan_cb },
4536 { "PanSouth", NULL, N_("Pan _South"), "<control>Down", NULL, (GCallback)draw_pan_cb },
4537 { "PanWest", NULL, N_("Pan _West"), "<control>Left", NULL, (GCallback)draw_pan_cb },
4538 { "BGJobs", GTK_STOCK_EXECUTE, N_("Background _Jobs"), NULL, N_("Background Jobs"), (GCallback)a_background_show_window },
4540 { "Cut", GTK_STOCK_CUT, N_("Cu_t"), NULL, N_("Cut selected layer"), (GCallback)menu_cut_layer_cb },
4541 { "Copy", GTK_STOCK_COPY, N_("_Copy"), NULL, N_("Copy selected layer"), (GCallback)menu_copy_layer_cb },
4542 { "Paste", GTK_STOCK_PASTE, N_("_Paste"), NULL, N_("Paste layer into selected container layer or otherwise above selected layer"), (GCallback)menu_paste_layer_cb },
4543 { "Delete", GTK_STOCK_DELETE, N_("_Delete"), NULL, N_("Remove selected layer"), (GCallback)menu_delete_layer_cb },
4544 { "DeleteAll", NULL, N_("Delete All"), NULL, NULL, (GCallback)clear_cb },
4545 { "CopyCentre",NULL, N_("Copy Centre _Location"), "<control>h", NULL, (GCallback)menu_copy_centre_cb },
4546 { "MapCacheFlush",NULL, N_("_Flush Map Cache"), NULL, NULL, (GCallback)mapcache_flush_cb },
4547 { "SetDefaultLocation", GTK_STOCK_GO_FORWARD, N_("_Set the Default Location"), NULL, N_("Set the Default Location to the current position"),(GCallback)default_location_cb },
4548 { "Preferences",GTK_STOCK_PREFERENCES, N_("_Preferences"), NULL, N_("Program Preferences"), (GCallback)preferences_cb },
4549 { "LayerDefaults",GTK_STOCK_PROPERTIES, N_("_Layer Defaults"), NULL, NULL, NULL },
4550 { "Properties",GTK_STOCK_PROPERTIES, N_("_Properties"), NULL, N_("Layer Properties"), (GCallback)menu_properties_cb },
4552 { "HelpEntry", GTK_STOCK_HELP, N_("_Help"), "F1", N_("Help"), (GCallback)help_help_cb },
4553 { "About", GTK_STOCK_ABOUT, N_("_About"), NULL, N_("About"), (GCallback)help_about_cb },
4556 static GtkActionEntry debug_entries[] = {
4557 { "MapCacheInfo", NULL, "_Map Cache Info", NULL, NULL, (GCallback)help_cache_info_cb },
4558 { "BackForwardInfo", NULL, "_Back/Forward Info", NULL, NULL, (GCallback)back_forward_info_cb },
4561 static GtkActionEntry entries_gpsbabel[] = {
4562 { "ExportKML", NULL, N_("_KML..."), NULL, N_("Export as KML"), (GCallback)export_to_kml },
4563 { "AcquireGPS", NULL, N_("From _GPS..."), NULL, N_("Transfer data from a GPS device"), (GCallback)acquire_from_gps },
4564 { "AcquireGPSBabel", NULL, N_("Import File With GPS_Babel..."), NULL, N_("Import file via GPSBabel converter"), (GCallback)acquire_from_file },
4567 static GtkActionEntry entries_geojson[] = {
4568 { "AcquireGeoJSON", NULL, N_("Import Geo_JSON File..."), NULL, N_("Import GeoJSON file"), (GCallback)acquire_from_geojson },
4572 static GtkRadioActionEntry mode_entries[] = {
4573 { "ModeUTM", NULL, N_("_UTM Mode"), "<control>u", NULL, VIK_VIEWPORT_DRAWMODE_UTM },
4574 { "ModeExpedia", NULL, N_("_Expedia Mode"), "<control>e", NULL, VIK_VIEWPORT_DRAWMODE_EXPEDIA },
4575 { "ModeMercator", NULL, N_("_Mercator Mode"), "<control>m", NULL, VIK_VIEWPORT_DRAWMODE_MERCATOR },
4576 { "ModeLatLon", NULL, N_("Lat_/Lon Mode"), "<control>l", NULL, VIK_VIEWPORT_DRAWMODE_LATLON },
4579 static GtkToggleActionEntry toggle_entries[] = {
4580 { "ShowScale", NULL, N_("Show _Scale"), "<shift>F5", N_("Show Scale"), (GCallback)toggle_draw_scale, TRUE },
4581 { "ShowCenterMark", NULL, N_("Show _Center Mark"), "F6", N_("Show Center Mark"), (GCallback)toggle_draw_centermark, TRUE },
4582 { "ShowHighlight", GTK_STOCK_UNDERLINE, N_("Show _Highlight"), "F7", N_("Show Highlight"), (GCallback)toggle_draw_highlight, TRUE },
4583 { "FullScreen", GTK_STOCK_FULLSCREEN, N_("_Full Screen"), "F11", N_("Activate full screen mode"), (GCallback)full_screen_cb, FALSE },
4584 { "ViewSidePanel", GTK_STOCK_INDEX, N_("Show Side _Panel"), "F9", N_("Show Side Panel"), (GCallback)view_side_panel_cb, TRUE },
4585 { "ViewStatusBar", NULL, N_("Show Status_bar"), "F12", N_("Show Statusbar"), (GCallback)view_statusbar_cb, TRUE },
4586 { "ViewToolbar", NULL, N_("Show _Toolbar"), "F3", N_("Show Toolbar"), (GCallback)view_toolbar_cb, TRUE },
4587 { "ViewMainMenu", NULL, N_("Show _Menu"), "F4", N_("Show Menu"), (GCallback)view_main_menu_cb, TRUE },
4590 // This must match the toggle entries order above
4591 static gpointer toggle_entries_toolbar_cb[] = {
4592 (GCallback)tb_set_draw_scale,
4593 (GCallback)tb_set_draw_centermark,
4594 (GCallback)tb_set_draw_highlight,
4595 (GCallback)tb_full_screen_cb,
4596 (GCallback)tb_view_side_panel_cb,
4597 (GCallback)tb_view_statusbar_cb,
4598 (GCallback)tb_view_toolbar_cb,
4599 (GCallback)tb_view_main_menu_cb,
4602 #include "menu.xml.h"
4603 static void window_create_ui( VikWindow *window )
4606 GtkActionGroup *action_group;
4607 GtkAccelGroup *accel_group;
4610 GtkIconFactory *icon_factory;
4611 GtkRadioActionEntry *tools = NULL, *radio;
4614 uim = gtk_ui_manager_new ();
4617 toolbox_add_tool(window->vt, &ruler_tool, TOOL_LAYER_TYPE_NONE);
4618 toolbox_add_tool(window->vt, &zoom_tool, TOOL_LAYER_TYPE_NONE);
4619 toolbox_add_tool(window->vt, &pan_tool, TOOL_LAYER_TYPE_NONE);
4620 toolbox_add_tool(window->vt, &select_tool, TOOL_LAYER_TYPE_NONE);
4622 toolbar_action_tool_entry_register ( window->viking_vtb, &pan_tool.radioActionEntry );
4623 toolbar_action_tool_entry_register ( window->viking_vtb, &zoom_tool.radioActionEntry );
4624 toolbar_action_tool_entry_register ( window->viking_vtb, &ruler_tool.radioActionEntry );
4625 toolbar_action_tool_entry_register ( window->viking_vtb, &select_tool.radioActionEntry );
4628 if (!(mid = gtk_ui_manager_add_ui_from_string (uim, menu_xml, -1, &error))) {
4629 g_error_free (error);
4633 action_group = gtk_action_group_new ("MenuActions");
4634 gtk_action_group_set_translation_domain(action_group, PACKAGE_NAME);
4635 gtk_action_group_add_actions (action_group, entries, G_N_ELEMENTS (entries), window);
4636 gtk_action_group_add_toggle_actions (action_group, toggle_entries, G_N_ELEMENTS (toggle_entries), window);
4637 gtk_action_group_add_radio_actions (action_group, mode_entries, G_N_ELEMENTS (mode_entries), 4, (GCallback)window_change_coord_mode_cb, window);
4639 if ( gtk_ui_manager_add_ui_from_string ( uim,
4640 "<ui><menubar name='MainMenu'><menu action='Help'>"
4641 "<menuitem action='MapCacheInfo'/>"
4642 "<menuitem action='BackForwardInfo'/>"
4643 "</menu></menubar></ui>",
4645 gtk_action_group_add_actions (action_group, debug_entries, G_N_ELEMENTS (debug_entries), window);
4649 for ( i=0; i < G_N_ELEMENTS (entries); i++ ) {
4650 if ( entries[i].callback )
4651 toolbar_action_entry_register ( window->viking_vtb, &entries[i] );
4654 if ( G_N_ELEMENTS (toggle_entries) != G_N_ELEMENTS (toggle_entries_toolbar_cb) ) {
4655 g_print ( "Broken entries definitions\n" );
4658 for ( i=0; i < G_N_ELEMENTS (toggle_entries); i++ ) {
4659 if ( toggle_entries_toolbar_cb[i] )
4660 toolbar_action_toggle_entry_register ( window->viking_vtb, &toggle_entries[i], toggle_entries_toolbar_cb[i] );
4663 for ( i=0; i < G_N_ELEMENTS (mode_entries); i++ ) {
4664 toolbar_action_mode_entry_register ( window->viking_vtb, &mode_entries[i] );
4667 // Use this to see if GPSBabel is available:
4668 if ( a_babel_available () ) {
4669 // If going to add more entries then might be worth creating a menu_gpsbabel.xml.h file
4670 if ( gtk_ui_manager_add_ui_from_string ( uim,
4672 "<menubar name='MainMenu'>" \
4673 "<menu action='File'><menu action='Export'><menuitem action='ExportKML'/></menu></menu>" \
4674 "<menu action='File'><menu action='Acquire'><menuitem action='AcquireGPS'/></menu></menu>" \
4675 "<menu action='File'><menu action='Acquire'><menuitem action='AcquireGPSBabel'/></menu></menu>" \
4679 gtk_action_group_add_actions ( action_group, entries_gpsbabel, G_N_ELEMENTS (entries_gpsbabel), window );
4682 // GeoJSON import capability
4683 if ( g_find_program_in_path ( a_geojson_program_import() ) ) {
4684 if ( gtk_ui_manager_add_ui_from_string ( uim,
4685 "<ui><menubar name='MainMenu'><menu action='File'><menu action='Acquire'><menuitem action='AcquireGeoJSON'/></menu></menu></menubar></ui>",
4687 gtk_action_group_add_actions ( action_group, entries_geojson, G_N_ELEMENTS (entries_geojson), window );
4690 icon_factory = gtk_icon_factory_new ();
4691 gtk_icon_factory_add_default (icon_factory);
4693 register_vik_icons(icon_factory);
4695 // Copy the tool RadioActionEntries out of the main Window structure into an extending array 'tools'
4696 // so that it can be applied to the UI in one action group add function call below
4698 for (i=0; i<window->vt->n_tools; i++) {
4699 tools = g_renew(GtkRadioActionEntry, tools, ntools+1);
4700 radio = &tools[ntools];
4702 *radio = window->vt->tools[i].ti.radioActionEntry;
4703 radio->value = ntools;
4706 for (i=0; i<VIK_LAYER_NUM_TYPES; i++) {
4707 GtkActionEntry action;
4708 gtk_ui_manager_add_ui(uim, mid, "/ui/MainMenu/Layers/",
4709 vik_layer_get_interface(i)->fixed_layer_name,
4710 vik_layer_get_interface(i)->fixed_layer_name,
4711 GTK_UI_MANAGER_MENUITEM, FALSE);
4713 icon_definition_t icon = { vik_layer_get_interface(i)->icon,
4714 vik_layer_get_interface(i)->fixed_layer_name };
4715 a_register_icon ( icon_factory, icon );
4717 action.name = vik_layer_get_interface(i)->fixed_layer_name;
4718 action.stock_id = vik_layer_get_interface(i)->fixed_layer_name;
4719 action.label = g_strdup_printf( _("New _%s Layer"), _(vik_layer_get_interface(i)->name));
4720 action.accelerator = vik_layer_get_interface(i)->accelerator;
4721 action.tooltip = NULL;
4722 action.callback = (GCallback)menu_addlayer_cb;
4723 gtk_action_group_add_actions(action_group, &action, 1, window);
4725 g_free ( (gchar*)action.label );
4727 if ( vik_layer_get_interface(i)->tools_count ) {
4728 gtk_ui_manager_add_ui ( uim, mid, "/ui/MainMenu/Tools/",
4729 vik_layer_get_interface(i)->fixed_layer_name,
4730 vik_layer_get_interface(i)->fixed_layer_name,
4731 GTK_UI_MANAGER_SEPARATOR, FALSE );
4734 // Further tool copying for to apply to the UI, also apply menu UI setup
4735 for ( j = 0; j < vik_layer_get_interface(i)->tools_count; j++ ) {
4736 tools = g_renew(GtkRadioActionEntry, tools, ntools+1);
4737 radio = &tools[ntools];
4740 icon_definition_t icon = { vik_layer_get_interface(i)->tools[j].icon,
4741 vik_layer_get_interface(i)->tools[j].radioActionEntry.stock_id };
4742 a_register_icon ( icon_factory, icon );
4744 gtk_ui_manager_add_ui(uim, mid, "/ui/MainMenu/Tools",
4745 vik_layer_get_interface(i)->tools[j].radioActionEntry.label,
4746 vik_layer_get_interface(i)->tools[j].radioActionEntry.name,
4747 GTK_UI_MANAGER_MENUITEM, FALSE);
4749 toolbox_add_tool(window->vt, &(vik_layer_get_interface(i)->tools[j]), i);
4750 toolbar_action_tool_entry_register ( window->viking_vtb, &(vik_layer_get_interface(i)->tools[j].radioActionEntry) );
4752 *radio = vik_layer_get_interface(i)->tools[j].radioActionEntry;
4753 // Overwrite with actual number to use
4754 radio->value = ntools;
4757 GtkActionEntry action_dl;
4758 gchar *layername = g_strdup_printf ( "Layer%s", vik_layer_get_interface(i)->fixed_layer_name );
4759 gtk_ui_manager_add_ui(uim, mid, "/ui/MainMenu/Edit/LayerDefaults",
4760 vik_layer_get_interface(i)->fixed_layer_name,
4762 GTK_UI_MANAGER_MENUITEM, FALSE);
4765 // For default layers use action names of the form 'Layer<LayerName>'
4766 // This is to avoid clashing with just the layer name used above for the tool actions
4767 action_dl.name = g_strconcat("Layer", vik_layer_get_interface(i)->fixed_layer_name, NULL);
4768 action_dl.stock_id = vik_layer_get_interface(i)->fixed_layer_name;
4769 action_dl.label = g_strconcat(_(vik_layer_get_interface(i)->name), _("..."), NULL);
4770 action_dl.accelerator = NULL;
4771 action_dl.tooltip = NULL;
4772 action_dl.callback = (GCallback)layer_defaults_cb;
4773 gtk_action_group_add_actions(action_group, &action_dl, 1, window);
4774 g_free ( (gchar*)action_dl.name );
4775 g_free ( (gchar*)action_dl.label );
4777 g_object_unref (icon_factory);
4779 gtk_action_group_add_radio_actions(action_group, tools, ntools, 0, (GCallback)menu_cb, window);
4782 gtk_ui_manager_insert_action_group (uim, action_group, 0);
4784 for (i=0; i<VIK_LAYER_NUM_TYPES; i++) {
4785 for ( j = 0; j < vik_layer_get_interface(i)->tools_count; j++ ) {
4786 GtkAction *action = gtk_action_group_get_action(action_group,
4787 vik_layer_get_interface(i)->tools[j].radioActionEntry.name);
4788 g_object_set(action, "sensitive", FALSE, NULL);
4792 // This is done last so we don't need to track the value of mid anymore
4793 vik_ext_tools_add_action_items ( window, window->uim, action_group, mid );
4795 window->action_group = action_group;
4797 accel_group = gtk_ui_manager_get_accel_group (uim);
4798 gtk_window_add_accel_group (GTK_WINDOW (window), accel_group);
4799 gtk_ui_manager_ensure_update (uim);
4801 setup_recent_files(window);
4803 if ( vik_routing_number_of_engines(VIK_ROUTING_METHOD_DIRECTIONS) == 0 ) {
4804 GtkWidget *widget = gtk_ui_manager_get_widget ( uim, "/ui/MainMenu/File/Acquire/AcquireRouting" );
4806 g_object_set ( widget, "sensitive", FALSE, NULL );
4808 g_warning ( "No direction routing engines available" );
4812 static icon_definition_t vik_icons[] = {
4813 // Vikwindow specific icons:
4814 { &mover_22_pixbuf, "vik-icon-pan" },
4815 { &zoom_18_pixbuf, "vik-icon-zoom" },
4816 { &ruler_18_pixbuf, "vik-icon-ruler" },
4817 { &select_18_pixbuf, "vik-icon-select" },
4818 // Generic icons for use anywhere:
4819 { &showpic_18_pixbuf, VIK_ICON_SHOW_PICTURE },
4823 register_vik_icons ( GtkIconFactory *icon_factory )
4825 for (guint i = 0; i < G_N_ELEMENTS(vik_icons); i++) {
4826 a_register_icon ( icon_factory, vik_icons[i] );
4830 gpointer vik_window_get_selected_trw_layer ( VikWindow *vw )
4832 return vw->selected_vtl;
4835 void vik_window_set_selected_trw_layer ( VikWindow *vw, gpointer vtl )
4837 vw->selected_vtl = vtl;
4838 vw->containing_vtl = vtl;
4840 vw->selected_track = NULL;
4841 vw->selected_tracks = NULL;
4842 vw->selected_waypoint = NULL;
4843 vw->selected_waypoints = NULL;
4844 // Set highlight thickness
4845 vik_viewport_set_highlight_thickness ( vw->viking_vvp, vik_trw_layer_get_property_tracks_line_thickness (vw->containing_vtl) );
4848 GHashTable *vik_window_get_selected_tracks ( VikWindow *vw )
4850 return vw->selected_tracks;
4853 void vik_window_set_selected_tracks ( VikWindow *vw, GHashTable *ght, gpointer vtl )
4855 vw->selected_tracks = ght;
4856 vw->containing_vtl = vtl;
4858 vw->selected_vtl = NULL;
4859 vw->selected_track = NULL;
4860 vw->selected_waypoint = NULL;
4861 vw->selected_waypoints = NULL;
4862 // Set highlight thickness
4863 vik_viewport_set_highlight_thickness ( vw->viking_vvp, vik_trw_layer_get_property_tracks_line_thickness (vw->containing_vtl) );
4866 gpointer vik_window_get_selected_track ( VikWindow *vw )
4868 return vw->selected_track;
4871 void vik_window_set_selected_track ( VikWindow *vw, gpointer *vt, gpointer vtl )
4873 vw->selected_track = vt;
4874 vw->containing_vtl = vtl;
4876 vw->selected_vtl = NULL;
4877 vw->selected_tracks = NULL;
4878 vw->selected_waypoint = NULL;
4879 vw->selected_waypoints = NULL;
4880 // Set highlight thickness
4881 vik_viewport_set_highlight_thickness ( vw->viking_vvp, vik_trw_layer_get_property_tracks_line_thickness (vw->containing_vtl) );
4884 GHashTable *vik_window_get_selected_waypoints ( VikWindow *vw )
4886 return vw->selected_waypoints;
4889 void vik_window_set_selected_waypoints ( VikWindow *vw, GHashTable *ght, gpointer vtl )
4891 vw->selected_waypoints = ght;
4892 vw->containing_vtl = vtl;
4894 vw->selected_vtl = NULL;
4895 vw->selected_track = NULL;
4896 vw->selected_tracks = NULL;
4897 vw->selected_waypoint = NULL;
4900 gpointer vik_window_get_selected_waypoint ( VikWindow *vw )
4902 return vw->selected_waypoint;
4905 void vik_window_set_selected_waypoint ( VikWindow *vw, gpointer *vwp, gpointer vtl )
4907 vw->selected_waypoint = vwp;
4908 vw->containing_vtl = vtl;
4910 vw->selected_vtl = NULL;
4911 vw->selected_track = NULL;
4912 vw->selected_tracks = NULL;
4913 vw->selected_waypoints = NULL;
4916 gboolean vik_window_clear_highlight ( VikWindow *vw )
4918 gboolean need_redraw = FALSE;
4919 vw->containing_vtl = NULL;
4920 if ( vw->selected_vtl != NULL ) {
4921 vw->selected_vtl = NULL;
4924 if ( vw->selected_track != NULL ) {
4925 vw->selected_track = NULL;
4928 if ( vw->selected_tracks != NULL ) {
4929 vw->selected_tracks = NULL;
4932 if ( vw->selected_waypoint != NULL ) {
4933 vw->selected_waypoint = NULL;
4936 if ( vw->selected_waypoints != NULL ) {
4937 vw->selected_waypoints = NULL;
4944 * May return NULL if the window no longer exists
4946 GThread *vik_window_get_thread ( VikWindow *vw )