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"
62 #include <glib/gstdio.h>
63 #include <glib/gprintf.h>
64 #include <glib/gi18n.h>
66 #include <gdk/gdkkeysyms.h>
68 // This seems rather arbitary, quite large and pointless
69 // I mean, if you have a thousand windows open;
70 // why not be allowed to open a thousand more...
71 #define MAX_WINDOWS 1024
72 static guint window_count = 0;
73 static GSList *window_list = NULL;
75 #define VIKING_WINDOW_WIDTH 1000
76 #define VIKING_WINDOW_HEIGHT 800
77 #define DRAW_IMAGE_DEFAULT_WIDTH 1280
78 #define DRAW_IMAGE_DEFAULT_HEIGHT 1024
79 #define DRAW_IMAGE_DEFAULT_SAVE_AS_PNG TRUE
81 // The last used directories
82 static gchar *last_folder_files_uri = NULL;
83 static gchar *last_folder_images_uri = NULL;
85 static void window_finalize ( GObject *gob );
86 static GObjectClass *parent_class;
88 static void window_set_filename ( VikWindow *vw, const gchar *filename );
89 static const gchar *window_get_filename ( VikWindow *vw );
91 static VikWindow *window_new ();
93 static void draw_update ( VikWindow *vw );
95 static void newwindow_cb ( GtkAction *a, VikWindow *vw );
98 static void open_window ( VikWindow *vw, GSList *files );
99 static void destroy_window ( GtkWidget *widget,
102 /* Drawing & stuff */
104 static gboolean delete_event( VikWindow *vw );
106 static gboolean key_press_event( VikWindow *vw, GdkEventKey *event, gpointer data );
108 static void center_changed_cb ( VikWindow *vw );
109 static void window_configure_event ( VikWindow *vw );
110 static void draw_sync ( VikWindow *vw );
111 static void draw_redraw ( VikWindow *vw );
112 static void draw_scroll ( VikWindow *vw, GdkEventScroll *event );
113 static void draw_click ( VikWindow *vw, GdkEventButton *event );
114 static void draw_release ( VikWindow *vw, GdkEventButton *event );
115 static void draw_mouse_motion ( VikWindow *vw, GdkEventMotion *event );
116 static void draw_zoom_cb ( GtkAction *a, VikWindow *vw );
117 static void draw_goto_cb ( GtkAction *a, VikWindow *vw );
118 static void draw_refresh_cb ( GtkAction *a, VikWindow *vw );
120 static void draw_status ( VikWindow *vw );
122 /* End Drawing Functions */
124 static void toggle_draw_scale ( GtkAction *a, VikWindow *vw );
125 static void toggle_draw_centermark ( GtkAction *a, VikWindow *vw );
126 static void toggle_draw_highlight ( GtkAction *a, VikWindow *vw );
128 static void menu_addlayer_cb ( GtkAction *a, VikWindow *vw );
129 static void menu_properties_cb ( GtkAction *a, VikWindow *vw );
130 static void menu_delete_layer_cb ( GtkAction *a, VikWindow *vw );
132 /* tool management */
138 #define TOOL_LAYER_TYPE_NONE -1
143 toolbox_tool_t *tools;
147 static void menu_cb ( GtkAction *old, GtkAction *a, VikWindow *vw );
148 static void window_change_coord_mode_cb ( GtkAction *old, GtkAction *a, VikWindow *vw );
149 static toolbox_tools_t* toolbox_create(VikWindow *vw);
150 static void toolbox_add_tool(toolbox_tools_t *vt, VikToolInterface *vti, gint layer_type );
151 static int toolbox_get_tool(toolbox_tools_t *vt, const gchar *tool_name);
152 static void toolbox_activate(toolbox_tools_t *vt, const gchar *tool_name);
153 static const GdkCursor *toolbox_get_cursor(toolbox_tools_t *vt, const gchar *tool_name);
154 static void toolbox_click (toolbox_tools_t *vt, GdkEventButton *event);
155 static void toolbox_move (toolbox_tools_t *vt, GdkEventMotion *event);
156 static void toolbox_release (toolbox_tools_t *vt, GdkEventButton *event);
160 static void window_create_ui( VikWindow *window );
161 static void register_vik_icons (GtkIconFactory *icon_factory);
164 static void load_file ( GtkAction *a, VikWindow *vw );
165 static gboolean save_file_as ( GtkAction *a, VikWindow *vw );
166 static gboolean save_file ( GtkAction *a, VikWindow *vw );
167 static gboolean save_file_and_exit ( GtkAction *a, VikWindow *vw );
168 static gboolean window_save ( VikWindow *vw );
173 VikViewport *viking_vvp;
174 VikLayersPanel *viking_vlp;
175 VikStatusbar *viking_vs;
176 VikToolbar *viking_vtb;
178 GtkWidget *main_vbox;
179 GtkWidget *menu_hbox;
181 GdkCursor *busy_cursor;
182 GdkCursor *viewport_cursor; // only a reference
184 /* tool management state */
187 guint16 tool_layer_id;
188 guint16 tool_tool_id;
190 GtkActionGroup *action_group;
193 // NB scale, centermark and highlight are in viewport.
194 gboolean show_full_screen;
195 gboolean show_side_panel;
196 gboolean show_statusbar;
197 gboolean show_toolbar;
198 gboolean show_main_menu;
200 gboolean select_move;
203 gint delayed_pan_x, delayed_pan_y; // Temporary storage
204 gboolean single_click_pending;
206 guint draw_image_width, draw_image_height;
207 gboolean draw_image_save_as_png;
211 VikLoadType_t loaded_type;
213 gboolean only_updating_coord_mode_ui; /* hack for a bug in GTK */
217 /* half-drawn update */
219 VikCoord trigger_center;
221 /* Store at this level for highlighted selection drawing since it applies to the viewport and the layers panel */
222 /* Only one of these items can be selected at the same time */
223 gpointer selected_vtl; /* notionally VikTrwLayer */
224 GHashTable *selected_tracks;
225 gpointer selected_track; /* notionally VikTrack */
226 GHashTable *selected_waypoints;
227 gpointer selected_waypoint; /* notionally VikWaypoint */
228 /* only use for individual track or waypoint */
229 /* For track(s) & waypoint(s) it is the layer they are in - this helps refering to the individual item easier */
230 gpointer containing_vtl; /* notionally VikTrwLayer */
244 VW_OPENWINDOW_SIGNAL,
248 static guint window_signals[VW_LAST_SIGNAL] = { 0 };
250 // TODO get rid of this as this is unnecessary duplication...
251 static gchar *tool_names[NUMBER_OF_TOOLS] = { N_("Pan"), N_("Zoom"), N_("Ruler"), N_("Select") };
253 G_DEFINE_TYPE (VikWindow, vik_window, GTK_TYPE_WINDOW)
255 VikViewport * vik_window_viewport(VikWindow *vw)
257 return(vw->viking_vvp);
260 VikLayersPanel * vik_window_layers_panel(VikWindow *vw)
262 return(vw->viking_vlp);
266 * Returns the statusbar for the window
268 VikStatusbar * vik_window_get_statusbar ( VikWindow *vw )
270 return vw->viking_vs;
274 * Returns the 'project' filename
276 const gchar *vik_window_get_filename (VikWindow *vw)
283 vik_statusbar_type_t vs_type;
284 gchar* message; // Always make a copy of this data
285 } statusbar_idle_data;
288 * For the actual statusbar update!
290 static gboolean statusbar_idle_update ( statusbar_idle_data *sid )
292 vik_statusbar_set_message ( sid->vs, sid->vs_type, sid->message );
293 g_free ( sid->message );
299 * vik_window_statusbar_update:
300 * @vw: The main window in which the statusbar will be updated.
301 * @message: The string to be displayed. This is copied.
302 * @vs_type: The part of the statusbar to be updated.
304 * This updates any part of the statusbar with the new string.
305 * It handles calling from the main thread or any background thread
306 * ATM this mostly used from background threads - as from the main thread
307 * one may use the vik_statusbar_set_message() directly.
309 void vik_window_statusbar_update ( VikWindow *vw, const gchar* message, vik_statusbar_type_t vs_type )
311 GThread *thread = vik_window_get_thread ( vw );
316 statusbar_idle_data *sid = g_malloc ( sizeof (statusbar_idle_data) );
317 sid->vs = vw->viking_vs;
318 sid->vs_type = vs_type;
319 sid->message = g_strdup ( message );
321 if ( g_thread_self() == thread ) {
322 g_idle_add ( (GSourceFunc) statusbar_idle_update, sid );
325 // From a background thread
326 gdk_threads_add_idle ( (GSourceFunc) statusbar_idle_update, sid );
330 // Actual signal handlers
331 static void destroy_window ( GtkWidget *widget,
334 g_debug ( "%s", __FUNCTION__ );
335 if ( ! --window_count ) {
336 g_free ( last_folder_files_uri );
337 g_free ( last_folder_images_uri );
342 #define VIK_SETTINGS_WIN_SIDEPANEL "window_sidepanel"
343 #define VIK_SETTINGS_WIN_STATUSBAR "window_statusbar"
344 #define VIK_SETTINGS_WIN_TOOLBAR "window_toolbar"
345 // Menubar setting to off is never auto saved in case it's accidentally turned off
346 // It's not so obvious so to recover the menu visibility.
347 // Thus this value is for setting manually via editting the settings file directly
348 #define VIK_SETTINGS_WIN_MENUBAR "window_menubar"
350 VikWindow *vik_window_new_window ()
352 if ( window_count < MAX_WINDOWS )
354 VikWindow *vw = window_new ();
356 if ( window_count == 0 ) {
357 vik_window_statusbar_update ( vw, _("This is Viking "VIKING_VERSION), VIK_STATUSBAR_INFO );
360 g_signal_connect (G_OBJECT (vw), "destroy",
361 G_CALLBACK (destroy_window), NULL);
362 g_signal_connect (G_OBJECT (vw), "newwindow",
363 G_CALLBACK (vik_window_new_window), NULL);
364 g_signal_connect (G_OBJECT (vw), "openwindow",
365 G_CALLBACK (open_window), NULL);
367 gtk_widget_show_all ( GTK_WIDGET(vw) );
369 if ( a_vik_get_restore_window_state() ) {
370 // These settings are applied after the show all as these options hide widgets
372 if ( a_settings_get_boolean ( VIK_SETTINGS_WIN_SIDEPANEL, &sidepanel ) )
374 gtk_widget_hide ( GTK_WIDGET(vw->viking_vlp) );
375 GtkWidget *check_box = gtk_ui_manager_get_widget ( vw->uim, "/ui/MainMenu/View/SetShow/ViewSidePanel" );
376 gtk_check_menu_item_set_active ( GTK_CHECK_MENU_ITEM(check_box), FALSE );
380 if ( a_settings_get_boolean ( VIK_SETTINGS_WIN_STATUSBAR, &statusbar ) )
382 gtk_widget_hide ( GTK_WIDGET(vw->viking_vs) );
383 GtkWidget *check_box = gtk_ui_manager_get_widget ( vw->uim, "/ui/MainMenu/View/SetShow/ViewStatusBar" );
384 gtk_check_menu_item_set_active ( GTK_CHECK_MENU_ITEM(check_box), FALSE );
388 if ( a_settings_get_boolean ( VIK_SETTINGS_WIN_TOOLBAR, &toolbar ) )
390 gtk_widget_hide ( toolbar_get_widget (vw->viking_vtb) );
391 GtkWidget *check_box = gtk_ui_manager_get_widget ( vw->uim, "/ui/MainMenu/View/SetShow/ViewToolBar" );
392 gtk_check_menu_item_set_active ( GTK_CHECK_MENU_ITEM(check_box), FALSE );
396 if ( a_settings_get_boolean ( VIK_SETTINGS_WIN_MENUBAR, &menubar ) )
398 gtk_widget_hide ( gtk_ui_manager_get_widget ( vw->uim, "/ui/MainMenu" ) );
399 GtkWidget *check_box = gtk_ui_manager_get_widget ( vw->uim, "/ui/MainMenu/View/SetShow/ViewMainMenu" );
400 gtk_check_menu_item_set_active ( GTK_CHECK_MENU_ITEM(check_box), FALSE );
411 * determine_location_thread:
412 * @vw: The window that will get updated
413 * @threaddata: Data used by our background thread mechanism
415 * Use the features in vikgoto to determine where we are
416 * Then set up the viewport:
417 * 1. To goto the location
418 * 2. Set an appropriate level zoom for the location type
419 * 3. Some statusbar message feedback
421 static int determine_location_thread ( VikWindow *vw, gpointer threaddata )
425 gint ans = a_vik_goto_where_am_i ( vw->viking_vvp, &ll, &name );
427 int result = a_background_thread_progress ( threaddata, 1.0 );
429 vik_window_statusbar_update ( vw, _("Location lookup aborted"), VIK_STATUSBAR_INFO );
430 return -1; /* Abort thread */
438 // Position found with city precision - so zoom out more
441 else if ( ans == 3 ) {
442 // Position found via country name search - so zoom wayyyy out
446 vik_viewport_set_zoom ( vw->viking_vvp, zoom );
447 vik_viewport_set_center_latlon ( vw->viking_vvp, &ll, FALSE );
449 gchar *message = g_strdup_printf ( _("Location found: %s"), name );
450 vik_window_statusbar_update ( vw, message, VIK_STATUSBAR_INFO );
454 // Signal to redraw from the background
455 vik_layers_panel_emit_update ( vw->viking_vlp );
458 vik_window_statusbar_update ( vw, _("Unable to determine location"), VIK_STATUSBAR_INFO );
464 * Steps to be taken once initial loading has completed
466 void vik_window_new_window_finish ( VikWindow *vw )
468 // Don't add a map if we've loaded a Viking file already
472 // Maybe add a default map layer
473 if ( a_vik_get_add_default_map_layer () ) {
474 VikMapsLayer *vml = VIK_MAPS_LAYER ( vik_layer_create(VIK_LAYER_MAPS, vw->viking_vvp, FALSE) );
475 vik_layer_rename ( VIK_LAYER(vml), _("Default Map") );
476 vik_aggregate_layer_add_layer ( vik_layers_panel_get_top_layer(vw->viking_vlp), VIK_LAYER(vml), TRUE );
481 // If not loaded any file, maybe try the location lookup
482 if ( vw->loaded_type == LOAD_TYPE_READ_FAILURE ) {
483 if ( a_vik_get_startup_method ( ) == VIK_STARTUP_METHOD_AUTO_LOCATION ) {
485 vik_statusbar_set_message ( vw->viking_vs, VIK_STATUSBAR_INFO, _("Trying to determine location...") );
487 a_background_thread ( BACKGROUND_POOL_REMOTE,
489 _("Determining location"),
490 (vik_thr_func) determine_location_thread,
499 static void open_window ( VikWindow *vw, GSList *files )
503 gboolean change_fn = (g_slist_length(files) == 1); /* only change fn if one file */
504 GSList *cur_file = files;
506 // Only open a new window if a viking file
507 gchar *file_name = cur_file->data;
508 if (vw->filename && check_file_magic_vik ( file_name ) ) {
509 VikWindow *newvw = vik_window_new_window ();
511 vik_window_open_file ( newvw, file_name, TRUE );
514 vik_window_open_file ( vw, file_name, change_fn );
517 cur_file = g_slist_next (cur_file);
519 g_slist_free (files);
523 void vik_window_selected_layer(VikWindow *vw, VikLayer *vl)
525 int i, j, tool_count;
526 VikLayerInterface *layer_interface;
528 if (!vw->action_group) return;
530 for (i=0; i<VIK_LAYER_NUM_TYPES; i++) {
532 layer_interface = vik_layer_get_interface(i);
533 tool_count = layer_interface->tools_count;
535 for (j = 0; j < tool_count; j++) {
536 action = gtk_action_group_get_action(vw->action_group,
537 layer_interface->tools[j].radioActionEntry.name);
538 g_object_set(action, "sensitive", i == vl->type, NULL);
539 toolbar_action_set_sensitive ( vw->viking_vtb, vik_layer_get_interface(i)->tools[j].radioActionEntry.name, i == vl->type );
544 static void window_finalize ( GObject *gob )
546 VikWindow *vw = VIK_WINDOW(gob);
547 g_return_if_fail ( vw != NULL );
549 a_background_remove_window ( vw );
551 window_list = g_slist_remove ( window_list, vw );
553 gdk_cursor_unref ( vw->busy_cursor );
555 for (tt = 0; tt < vw->vt->n_tools; tt++ )
556 if ( vw->vt->tools[tt].ti.destroy )
557 vw->vt->tools[tt].ti.destroy ( vw->vt->tools[tt].state );
558 g_free ( vw->vt->tools );
561 vik_toolbar_finalize ( vw->viking_vtb );
563 G_OBJECT_CLASS(parent_class)->finalize(gob);
567 static void vik_window_class_init ( VikWindowClass *klass )
570 GObjectClass *object_class;
572 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);
573 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);
575 object_class = G_OBJECT_CLASS (klass);
577 object_class->finalize = window_finalize;
579 parent_class = g_type_class_peek_parent (klass);
583 static void zoom_changed (GtkMenuShell *menushell,
586 VikWindow *vw = VIK_WINDOW (user_data);
588 GtkWidget *aw = gtk_menu_get_active ( GTK_MENU (menushell) );
589 gint active = GPOINTER_TO_INT(g_object_get_data ( G_OBJECT (aw), "position" ));
591 gdouble zoom_request = pow (2, active-5 );
593 // But has it really changed?
594 gdouble current_zoom = vik_viewport_get_zoom ( vw->viking_vvp );
595 if ( current_zoom != 0.0 && zoom_request != current_zoom ) {
596 vik_viewport_set_zoom ( vw->viking_vvp, zoom_request );
597 // Force drawing update
603 * @mpp: The initial zoom level
605 static GtkWidget *create_zoom_menu_all_levels ( gdouble mpp )
607 GtkWidget *menu = gtk_menu_new ();
608 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" };
611 for (i = 0 ; i < G_N_ELEMENTS(itemLabels) ; i++)
613 GtkWidget *item = gtk_menu_item_new_with_label (itemLabels[i]);
614 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
615 gtk_widget_show (item);
616 g_object_set_data (G_OBJECT (item), "position", GINT_TO_POINTER(i));
619 gint active = 5 + round ( log (mpp) / log (2) );
620 // Ensure value derived from mpp is in bounds of the menu
621 if ( active >= G_N_ELEMENTS(itemLabels) )
622 active = G_N_ELEMENTS(itemLabels) - 1;
625 gtk_menu_set_active ( GTK_MENU(menu), active );
630 static GtkWidget *create_zoom_combo_all_levels ()
632 GtkWidget *combo = vik_combo_box_text_new();
633 vik_combo_box_text_append ( combo, "0.25");
634 vik_combo_box_text_append ( combo, "0.5");
635 vik_combo_box_text_append ( combo, "1");
636 vik_combo_box_text_append ( combo, "2");
637 vik_combo_box_text_append ( combo, "4");
638 vik_combo_box_text_append ( combo, "8");
639 vik_combo_box_text_append ( combo, "16");
640 vik_combo_box_text_append ( combo, "32");
641 vik_combo_box_text_append ( combo, "64");
642 vik_combo_box_text_append ( combo, "128");
643 vik_combo_box_text_append ( combo, "256");
644 vik_combo_box_text_append ( combo, "512");
645 vik_combo_box_text_append ( combo, "1024");
646 vik_combo_box_text_append ( combo, "2048");
647 vik_combo_box_text_append ( combo, "4096");
648 vik_combo_box_text_append ( combo, "8192");
649 vik_combo_box_text_append ( combo, "16384");
650 vik_combo_box_text_append ( combo, "32768");
652 gtk_widget_set_tooltip_text (combo, _("Select zoom level"));
656 static gint zoom_popup_handler (GtkWidget *widget)
660 g_return_val_if_fail (widget != NULL, FALSE);
661 g_return_val_if_fail (GTK_IS_MENU (widget), FALSE);
663 /* The "widget" is the menu that was supplied when
664 * g_signal_connect_swapped() was called.
666 menu = GTK_MENU (widget);
668 gtk_menu_popup (menu, NULL, NULL, NULL, NULL,
669 1, gtk_get_current_event_time());
677 static void drag_data_received_cb ( GtkWidget *widget,
678 GdkDragContext *context,
681 GtkSelectionData *selection_data,
686 gboolean success = FALSE;
688 if ( (selection_data != NULL) && (gtk_selection_data_get_length(selection_data) > 0) ) {
689 switch (target_type) {
691 gchar *str = (gchar*)gtk_selection_data_get_data(selection_data);
692 g_debug ("drag received string:%s \n", str);
694 // Convert string into GSList of individual entries for use with our open signal
695 gchar **entries = g_strsplit(str, "\r\n", 0);
696 GSList *filenames = NULL;
697 gint entry_runner = 0;
698 gchar *entry = entries[entry_runner];
700 if ( g_strcmp0 ( entry, "" ) ) {
701 // Drag+Drop gives URIs. And so in particular, %20 in place of spaces in filenames
702 // thus need to convert the text into a plain string
703 gchar *filename = g_filename_from_uri ( entry, NULL, NULL );
705 filenames = g_slist_append ( filenames, filename );
708 entry = entries[entry_runner];
712 g_signal_emit ( G_OBJECT(VIK_WINDOW_FROM_WIDGET(widget)), window_signals[VW_OPENWINDOW_SIGNAL], 0, filenames );
713 // NB: GSList & contents are freed by main.open_window
722 gtk_drag_finish ( context, success, FALSE, time );
725 static void toolbar_tool_cb ( GtkAction *old, GtkAction *current, gpointer gp )
727 VikWindow *vw = (VikWindow*)gp;
728 GtkAction *action = gtk_action_group_get_action ( vw->action_group, gtk_action_get_name(current) );
730 gtk_action_activate ( action );
733 static void toolbar_reload_cb ( GtkActionGroup *grp, gpointer gp )
735 VikWindow *vw = (VikWindow*)gp;
736 center_changed_cb ( vw );
739 #define VIK_SETTINGS_WIN_MAX "window_maximized"
740 #define VIK_SETTINGS_WIN_FULLSCREEN "window_fullscreen"
741 #define VIK_SETTINGS_WIN_WIDTH "window_width"
742 #define VIK_SETTINGS_WIN_HEIGHT "window_height"
743 #define VIK_SETTINGS_WIN_PANE_POSITION "window_horizontal_pane_position"
744 #define VIK_SETTINGS_WIN_SAVE_IMAGE_WIDTH "window_save_image_width"
745 #define VIK_SETTINGS_WIN_SAVE_IMAGE_HEIGHT "window_save_image_height"
746 #define VIK_SETTINGS_WIN_SAVE_IMAGE_PNG "window_save_image_as_png"
747 #define VIK_SETTINGS_WIN_COPY_CENTRE_FULL_FORMAT "window_copy_centre_full_format"
749 #define VIKING_ACCELERATOR_KEY_FILE "keys.rc"
751 static void vik_window_init ( VikWindow *vw )
753 vw->action_group = NULL;
755 vw->viking_vvp = vik_viewport_new();
756 vw->viking_vlp = vik_layers_panel_new();
757 vik_layers_panel_set_viewport ( vw->viking_vlp, vw->viking_vvp );
758 vw->viking_vs = vik_statusbar_new();
760 vw->vt = toolbox_create(vw);
761 vw->viking_vtb = vik_toolbar_new ();
762 window_create_ui(vw);
763 window_set_filename (vw, NULL);
765 vw->busy_cursor = gdk_cursor_new ( GDK_WATCH );
768 vw->loaded_type = LOAD_TYPE_READ_FAILURE; //AKA none
769 vw->modified = FALSE;
770 vw->only_updating_coord_mode_ui = FALSE;
772 vw->select_move = FALSE;
773 vw->pan_move = FALSE;
774 vw->pan_x = vw->pan_y = -1;
775 vw->single_click_pending = FALSE;
777 gint draw_image_width;
778 if ( a_settings_get_integer ( VIK_SETTINGS_WIN_SAVE_IMAGE_WIDTH, &draw_image_width ) )
779 vw->draw_image_width = draw_image_width;
781 vw->draw_image_width = DRAW_IMAGE_DEFAULT_WIDTH;
782 gint draw_image_height;
783 if ( a_settings_get_integer ( VIK_SETTINGS_WIN_SAVE_IMAGE_HEIGHT, &draw_image_height ) )
784 vw->draw_image_height = draw_image_height;
786 vw->draw_image_height = DRAW_IMAGE_DEFAULT_HEIGHT;
787 gboolean draw_image_save_as_png;
788 if ( a_settings_get_boolean ( VIK_SETTINGS_WIN_SAVE_IMAGE_PNG, &draw_image_save_as_png ) )
789 vw->draw_image_save_as_png = draw_image_save_as_png;
791 vw->draw_image_save_as_png = DRAW_IMAGE_DEFAULT_SAVE_AS_PNG;
793 vw->main_vbox = gtk_vbox_new(FALSE, 1);
794 gtk_container_add (GTK_CONTAINER (vw), vw->main_vbox);
795 vw->menu_hbox = gtk_hbox_new(FALSE, 1);
796 GtkWidget *menu_bar = gtk_ui_manager_get_widget (vw->uim, "/MainMenu");
797 gtk_box_pack_start (GTK_BOX(vw->menu_hbox), menu_bar, FALSE, TRUE, 0);
798 gtk_box_pack_start (GTK_BOX(vw->main_vbox), vw->menu_hbox, FALSE, TRUE, 0);
800 toolbar_init(vw->viking_vtb,
806 (gpointer)vw); // This auto packs toolbar into the vbox
807 // Must be performed post toolbar init
809 for (i=0; i<VIK_LAYER_NUM_TYPES; i++) {
810 for ( j = 0; j < vik_layer_get_interface(i)->tools_count; j++ ) {
811 toolbar_action_set_sensitive ( vw->viking_vtb, vik_layer_get_interface(i)->tools[j].radioActionEntry.name, FALSE );
815 vik_ext_tool_datasources_add_menu_items ( vw, vw->uim );
817 GtkWidget * zoom_levels = gtk_ui_manager_get_widget (vw->uim, "/MainMenu/View/SetZoom");
818 GtkWidget * zoom_levels_menu = create_zoom_menu_all_levels ( vik_viewport_get_zoom(vw->viking_vvp) );
819 gtk_menu_item_set_submenu (GTK_MENU_ITEM (zoom_levels), zoom_levels_menu);
820 g_signal_connect ( G_OBJECT(zoom_levels_menu), "selection-done", G_CALLBACK(zoom_changed), vw);
821 g_signal_connect_swapped ( G_OBJECT(vw->viking_vs), "clicked", G_CALLBACK(zoom_popup_handler), zoom_levels_menu );
823 g_signal_connect (G_OBJECT (vw), "delete_event", G_CALLBACK (delete_event), NULL);
826 g_signal_connect_swapped (G_OBJECT(vw->viking_vvp), "updated_center", G_CALLBACK(center_changed_cb), vw);
828 g_signal_connect_swapped (G_OBJECT(vw->viking_vvp), "expose_event", G_CALLBACK(draw_sync), vw);
829 g_signal_connect_swapped (G_OBJECT(vw->viking_vvp), "configure_event", G_CALLBACK(window_configure_event), vw);
830 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 );
831 g_signal_connect_swapped (G_OBJECT(vw->viking_vvp), "scroll_event", G_CALLBACK(draw_scroll), vw);
832 g_signal_connect_swapped (G_OBJECT(vw->viking_vvp), "button_press_event", G_CALLBACK(draw_click), vw);
833 g_signal_connect_swapped (G_OBJECT(vw->viking_vvp), "button_release_event", G_CALLBACK(draw_release), vw);
834 g_signal_connect_swapped (G_OBJECT(vw->viking_vvp), "motion_notify_event", G_CALLBACK(draw_mouse_motion), vw);
836 g_signal_connect_swapped (G_OBJECT(vw->viking_vlp), "update", G_CALLBACK(draw_update), vw);
837 g_signal_connect_swapped (G_OBJECT(vw->viking_vlp), "delete_layer", G_CALLBACK(vik_window_clear_highlight), vw);
839 // Allow key presses to be processed anywhere
840 g_signal_connect_swapped (G_OBJECT (vw), "key_press_event", G_CALLBACK (key_press_event), vw);
842 // Set initial button sensitivity
843 center_changed_cb ( vw );
845 vw->hpaned = gtk_hpaned_new ();
846 gtk_paned_pack1 ( GTK_PANED(vw->hpaned), GTK_WIDGET (vw->viking_vlp), FALSE, TRUE );
847 gtk_paned_pack2 ( GTK_PANED(vw->hpaned), GTK_WIDGET (vw->viking_vvp), TRUE, TRUE );
849 /* This packs the button into the window (a gtk container). */
850 gtk_box_pack_start (GTK_BOX(vw->main_vbox), vw->hpaned, TRUE, TRUE, 0);
852 gtk_box_pack_end (GTK_BOX(vw->main_vbox), GTK_WIDGET(vw->viking_vs), FALSE, TRUE, 0);
854 a_background_add_window ( vw );
856 window_list = g_slist_prepend ( window_list, vw);
858 gint height = VIKING_WINDOW_HEIGHT;
859 gint width = VIKING_WINDOW_WIDTH;
861 if ( a_vik_get_restore_window_state() ) {
862 if ( a_settings_get_integer ( VIK_SETTINGS_WIN_HEIGHT, &height ) ) {
863 // Enforce a basic minimum size
868 // No setting - so use default
869 height = VIKING_WINDOW_HEIGHT;
871 if ( a_settings_get_integer ( VIK_SETTINGS_WIN_WIDTH, &width ) ) {
872 // Enforce a basic minimum size
877 // No setting - so use default
878 width = VIKING_WINDOW_WIDTH;
881 if ( a_settings_get_boolean ( VIK_SETTINGS_WIN_MAX, &maxed ) )
883 gtk_window_maximize ( GTK_WINDOW(vw) );
886 if ( a_settings_get_boolean ( VIK_SETTINGS_WIN_FULLSCREEN, &full ) ) {
888 vw->show_full_screen = TRUE;
889 gtk_window_fullscreen ( GTK_WINDOW(vw) );
890 GtkWidget *check_box = gtk_ui_manager_get_widget ( vw->uim, "/ui/MainMenu/View/FullScreen" );
892 gtk_check_menu_item_set_active ( GTK_CHECK_MENU_ITEM(check_box), TRUE );
896 gint position = -1; // Let GTK determine default positioning
897 if ( !a_settings_get_integer ( VIK_SETTINGS_WIN_PANE_POSITION, &position ) ) {
900 gtk_paned_set_position ( GTK_PANED(vw->hpaned), position );
903 gtk_window_set_default_size ( GTK_WINDOW(vw), width, height );
905 vw->show_side_panel = TRUE;
906 vw->show_statusbar = TRUE;
907 vw->show_toolbar = TRUE;
908 vw->show_main_menu = TRUE;
910 // Only accept Drag and Drop of files onto the viewport
911 gtk_drag_dest_set ( GTK_WIDGET(vw->viking_vvp), GTK_DEST_DEFAULT_ALL, NULL, 0, GDK_ACTION_COPY );
912 gtk_drag_dest_add_uri_targets ( GTK_WIDGET(vw->viking_vvp) );
913 g_signal_connect ( GTK_WIDGET(vw->viking_vvp), "drag-data-received", G_CALLBACK(drag_data_received_cb), NULL );
915 // Store the thread value so comparisons can be made to determine the gdk update method
916 // Hopefully we are storing the main thread value here :)
917 // [ATM any window initialization is always be performed by the main thread]
918 vw->thread = g_thread_self();
920 // Set the default tool + mode
921 gtk_action_activate ( gtk_action_group_get_action ( vw->action_group, "Pan" ) );
922 gtk_action_activate ( gtk_action_group_get_action ( vw->action_group, "ModeMercator" ) );
924 gchar *accel_file_name = g_build_filename ( a_get_viking_dir(), VIKING_ACCELERATOR_KEY_FILE, NULL );
925 gtk_accel_map_load ( accel_file_name );
926 g_free ( accel_file_name );
929 static VikWindow *window_new ()
931 return VIK_WINDOW ( g_object_new ( VIK_WINDOW_TYPE, NULL ) );
935 * Update the displayed map
936 * Only update the top most visible map layer
937 * ATM this assumes (as per defaults) the top most map has full alpha setting
938 * such that other other maps even though they may be active will not be seen
939 * It's more complicated to work out which maps are actually visible due to alpha settings
940 * and overkill for this simple refresh method.
942 static void simple_map_update ( VikWindow *vw, gboolean only_new )
944 // Find the most relevent single map layer to operate on
945 VikLayer *vl = vik_aggregate_layer_get_top_visible_layer_of_type (vik_layers_panel_get_top_layer(vw->viking_vlp), VIK_LAYER_MAPS);
947 vik_maps_layer_download ( VIK_MAPS_LAYER(vl), vw->viking_vvp, only_new );
951 * This is the global key press handler
952 * Global shortcuts are available at any time and hence are not restricted to when a certain tool is enabled
954 static gboolean key_press_event( VikWindow *vw, GdkEventKey *event, gpointer data )
956 // The keys handled here are not in the menuing system for a couple of reasons:
957 // . Keeps the menu size compact (alebit at expense of discoverably)
958 // . Allows differing key bindings to perform the same actions
960 // First decide if key events are related to the maps layer
961 gboolean map_download = FALSE;
962 gboolean map_download_only_new = TRUE; // Only new or reload
964 GdkModifierType modifiers = gtk_accelerator_get_default_mod_mask();
966 // Standard 'Refresh' keys: F5 or Ctrl+r
967 // Note 'F5' is actually handled via draw_refresh_cb() later on
968 // (not 'R' it's 'r' notice the case difference!!)
969 if ( event->keyval == GDK_r && (event->state & modifiers) == GDK_CONTROL_MASK ) {
971 map_download_only_new = TRUE;
973 // Full cache reload with Ctrl+F5 or Ctrl+Shift+r [This is not in the menu system]
974 // Note the use of uppercase R here since shift key has been pressed
975 else if ( (event->keyval == GDK_F5 && (event->state & modifiers) == GDK_CONTROL_MASK ) ||
976 ( event->keyval == GDK_R && (event->state & modifiers) == (GDK_CONTROL_MASK + GDK_SHIFT_MASK) ) ) {
978 map_download_only_new = FALSE;
980 // Standard Ctrl+KP+ / Ctrl+KP- to zoom in/out respectively
981 else if ( event->keyval == GDK_KEY_KP_Add && (event->state & modifiers) == GDK_CONTROL_MASK ) {
982 vik_viewport_zoom_in ( vw->viking_vvp );
984 return TRUE; // handled keypress
986 else if ( event->keyval == GDK_KEY_KP_Subtract && (event->state & modifiers) == GDK_CONTROL_MASK ) {
987 vik_viewport_zoom_out ( vw->viking_vvp );
989 return TRUE; // handled keypress
992 if ( map_download ) {
993 simple_map_update ( vw, map_download_only_new );
994 return TRUE; // handled keypress
997 VikLayer *vl = vik_layers_panel_get_selected ( vw->viking_vlp );
998 if (vl && vw->vt->active_tool != -1 && vw->vt->tools[vw->vt->active_tool].ti.key_press ) {
999 gint ltype = vw->vt->tools[vw->vt->active_tool].layer_type;
1000 if ( vl && ltype == vl->type )
1001 return vw->vt->tools[vw->vt->active_tool].ti.key_press(vl, event, vw->vt->tools[vw->vt->active_tool].state);
1004 // Ensure called only on window tools (i.e. not on any of the Layer tools since the layer is NULL)
1005 if ( vw->current_tool < TOOL_LAYER ) {
1006 // No layer - but enable window tool keypress processing - these should be able to handle a NULL layer
1007 if ( vw->vt->tools[vw->vt->active_tool].ti.key_press ) {
1008 return vw->vt->tools[vw->vt->active_tool].ti.key_press ( vl, event, vw->vt->tools[vw->vt->active_tool].state );
1012 /* Restore Main Menu via Escape key if the user has hidden it */
1013 /* This key is more likely to be used as they may not remember the function key */
1014 if ( event->keyval == GDK_Escape ) {
1015 GtkWidget *check_box = gtk_ui_manager_get_widget ( vw->uim, "/ui/MainMenu/View/SetShow/ViewMainMenu" );
1017 gboolean state = gtk_check_menu_item_get_active ( GTK_CHECK_MENU_ITEM(check_box) );
1019 gtk_widget_show ( gtk_ui_manager_get_widget ( vw->uim, "/ui/MainMenu" ) );
1020 gtk_check_menu_item_set_active ( GTK_CHECK_MENU_ITEM(check_box), TRUE );
1021 return TRUE; /* handled keypress */
1026 return FALSE; /* don't handle the keypress */
1029 static gboolean delete_event( VikWindow *vw )
1031 #ifdef VIKING_PROMPT_IF_MODIFIED
1038 dia = GTK_DIALOG ( gtk_message_dialog_new ( GTK_WINDOW(vw), GTK_DIALOG_DESTROY_WITH_PARENT, GTK_MESSAGE_QUESTION, GTK_BUTTONS_NONE,
1039 _("Do you want to save the changes you made to the document \"%s\"?\n"
1041 "Your changes will be lost if you don't save them."),
1042 window_get_filename ( vw ) ) );
1043 gtk_dialog_add_buttons ( dia, _("Don't Save"), GTK_RESPONSE_NO, GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, GTK_STOCK_SAVE, GTK_RESPONSE_YES, NULL );
1044 switch ( gtk_dialog_run ( dia ) )
1046 case GTK_RESPONSE_NO: gtk_widget_destroy ( GTK_WIDGET(dia) ); return FALSE;
1047 case GTK_RESPONSE_CANCEL: gtk_widget_destroy ( GTK_WIDGET(dia) ); return TRUE;
1048 default: gtk_widget_destroy ( GTK_WIDGET(dia) ); return ! save_file(NULL, vw);
1052 if ( window_count == 1 ) {
1053 // On the final window close - save latest state - if it's wanted...
1054 if ( a_vik_get_restore_window_state() ) {
1055 gint state = gdk_window_get_state ( GTK_WIDGET(vw)->window );
1056 gboolean state_max = state & GDK_WINDOW_STATE_MAXIMIZED;
1057 a_settings_set_boolean ( VIK_SETTINGS_WIN_MAX, state_max );
1059 gboolean state_fullscreen = state & GDK_WINDOW_STATE_FULLSCREEN;
1060 a_settings_set_boolean ( VIK_SETTINGS_WIN_FULLSCREEN, state_fullscreen );
1062 a_settings_set_boolean ( VIK_SETTINGS_WIN_SIDEPANEL, GTK_WIDGET_VISIBLE (GTK_WIDGET(vw->viking_vlp)) );
1064 a_settings_set_boolean ( VIK_SETTINGS_WIN_STATUSBAR, GTK_WIDGET_VISIBLE (GTK_WIDGET(vw->viking_vs)) );
1066 a_settings_set_boolean ( VIK_SETTINGS_WIN_TOOLBAR, GTK_WIDGET_VISIBLE (toolbar_get_widget(vw->viking_vtb)) );
1068 // If supersized - no need to save the enlarged width+height values
1069 if ( ! (state_fullscreen || state_max) ) {
1071 gtk_window_get_size ( GTK_WINDOW (vw), &width, &height );
1072 a_settings_set_integer ( VIK_SETTINGS_WIN_WIDTH, width );
1073 a_settings_set_integer ( VIK_SETTINGS_WIN_HEIGHT, height );
1076 a_settings_set_integer ( VIK_SETTINGS_WIN_PANE_POSITION, gtk_paned_get_position (GTK_PANED(vw->hpaned)) );
1079 a_settings_set_integer ( VIK_SETTINGS_WIN_SAVE_IMAGE_WIDTH, vw->draw_image_width );
1080 a_settings_set_integer ( VIK_SETTINGS_WIN_SAVE_IMAGE_HEIGHT, vw->draw_image_height );
1081 a_settings_set_boolean ( VIK_SETTINGS_WIN_SAVE_IMAGE_PNG, vw->draw_image_save_as_png );
1083 gchar *accel_file_name = g_build_filename ( a_get_viking_dir(), VIKING_ACCELERATOR_KEY_FILE, NULL );
1084 gtk_accel_map_save ( accel_file_name );
1085 g_free ( accel_file_name );
1092 static void newwindow_cb ( GtkAction *a, VikWindow *vw )
1094 g_signal_emit ( G_OBJECT(vw), window_signals[VW_NEWWINDOW_SIGNAL], 0 );
1097 static void draw_update ( VikWindow *vw )
1103 static void draw_sync ( VikWindow *vw )
1105 vik_viewport_sync(vw->viking_vvp);
1110 * Split the status update, as sometimes only need to update the tool part
1111 * also on initialization the zoom related stuff is not ready to be used
1113 static void draw_status_tool ( VikWindow *vw )
1115 if ( vw->current_tool == TOOL_LAYER )
1116 // Use tooltip rather than the internal name as the tooltip is i8n
1117 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 );
1119 vik_statusbar_set_message ( vw->viking_vs, VIK_STATUSBAR_TOOL, _(tool_names[vw->current_tool]) );
1122 static void draw_status ( VikWindow *vw )
1124 static gchar zoom_level[22];
1125 gdouble xmpp = vik_viewport_get_xmpp (vw->viking_vvp);
1126 gdouble ympp = vik_viewport_get_ympp(vw->viking_vvp);
1127 gchar *unit = vik_viewport_get_coord_mode(vw->viking_vvp) == VIK_COORD_UTM ? _("mpp") : _("pixelfact");
1129 g_snprintf ( zoom_level, 22, "%.3f/%.3f %s", xmpp, ympp, unit );
1131 if ( (int)xmpp - xmpp < 0.0 )
1132 g_snprintf ( zoom_level, 22, "%.3f %s", xmpp, unit );
1134 /* xmpp should be a whole number so don't show useless .000 bit */
1135 g_snprintf ( zoom_level, 22, "%d %s", (int)xmpp, unit );
1137 vik_statusbar_set_message ( vw->viking_vs, VIK_STATUSBAR_ZOOM, zoom_level );
1139 draw_status_tool ( vw );
1142 void vik_window_set_redraw_trigger(VikLayer *vl)
1144 VikWindow *vw = VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vl));
1149 static void window_configure_event ( VikWindow *vw )
1151 static int first = 1;
1154 // This is a hack to set the cursor corresponding to the first tool
1155 // FIXME find the correct way to initialize both tool and its cursor
1157 vw->viewport_cursor = (GdkCursor *)toolbox_get_cursor(vw->vt, "Pan");
1158 /* We set cursor, even if it is NULL: it resets to default */
1159 gdk_window_set_cursor ( gtk_widget_get_window(GTK_WIDGET(vw->viking_vvp)), vw->viewport_cursor );
1163 static void draw_redraw ( VikWindow *vw )
1165 VikCoord old_center = vw->trigger_center;
1166 vw->trigger_center = *(vik_viewport_get_center(vw->viking_vvp));
1167 VikLayer *new_trigger = vw->trigger;
1169 VikLayer *old_trigger = VIK_LAYER(vik_viewport_get_trigger(vw->viking_vvp));
1171 if ( ! new_trigger )
1172 ; /* do nothing -- have to redraw everything. */
1173 else if ( (old_trigger != new_trigger) || !vik_coord_equals(&old_center, &vw->trigger_center) || (new_trigger->type == VIK_LAYER_AGGREGATE) )
1174 vik_viewport_set_trigger ( vw->viking_vvp, new_trigger ); /* todo: set to half_drawn mode if new trigger is above old */
1176 vik_viewport_set_half_drawn ( vw->viking_vvp, TRUE );
1179 vik_viewport_clear ( vw->viking_vvp);
1180 // Main layer drawing
1181 vik_layers_panel_draw_all ( vw->viking_vlp );
1182 // Draw highlight (possibly again but ensures it is on top - especially for when tracks overlap)
1183 if ( vik_viewport_get_draw_highlight (vw->viking_vvp) ) {
1184 if ( vw->containing_vtl && (vw->selected_tracks || vw->selected_waypoints ) ) {
1185 vik_trw_layer_draw_highlight_items ( vw->containing_vtl, vw->selected_tracks, vw->selected_waypoints, vw->viking_vvp );
1187 else if ( vw->containing_vtl && (vw->selected_track || vw->selected_waypoint) ) {
1188 vik_trw_layer_draw_highlight_item ( vw->containing_vtl, vw->selected_track, vw->selected_waypoint, vw->viking_vvp );
1190 else if ( vw->selected_vtl ) {
1191 vik_trw_layer_draw_highlight ( vw->selected_vtl, vw->viking_vvp );
1194 // Other viewport decoration items on top if they are enabled/in use
1195 vik_viewport_draw_scale ( vw->viking_vvp );
1196 vik_viewport_draw_copyright ( vw->viking_vvp );
1197 vik_viewport_draw_centermark ( vw->viking_vvp );
1198 vik_viewport_draw_logo ( vw->viking_vvp );
1200 vik_viewport_set_half_drawn ( vw->viking_vvp, FALSE ); /* just in case. */
1203 gboolean draw_buf_done = TRUE;
1205 static gboolean draw_buf(gpointer data)
1207 gpointer *pass_along = data;
1208 gdk_draw_drawable (pass_along[0], pass_along[1],
1209 pass_along[2], 0, 0, 0, 0, -1, -1);
1210 draw_buf_done = TRUE;
1215 /* Mouse event handlers ************************************************************************/
1217 static void vik_window_pan_click (VikWindow *vw, GdkEventButton *event)
1219 /* set panning origin */
1220 vw->pan_move = FALSE;
1221 vw->pan_x = (gint) event->x;
1222 vw->pan_y = (gint) event->y;
1225 static void draw_click (VikWindow *vw, GdkEventButton *event)
1227 gtk_widget_grab_focus ( GTK_WIDGET(vw->viking_vvp) );
1229 /* middle button pressed. we reserve all middle button and scroll events
1230 * for panning and zooming; tools only get left/right/movement
1232 if ( event->button == 2) {
1233 if ( vw->vt->tools[vw->vt->active_tool].ti.pan_handler )
1234 // Tool still may need to do something (such as disable something)
1235 toolbox_click(vw->vt, event);
1236 vik_window_pan_click ( vw, event );
1239 toolbox_click(vw->vt, event);
1243 static void vik_window_pan_move (VikWindow *vw, GdkEventMotion *event)
1245 if ( vw->pan_x != -1 ) {
1246 vik_viewport_set_center_screen ( vw->viking_vvp, vik_viewport_get_width(vw->viking_vvp)/2 - event->x + vw->pan_x,
1247 vik_viewport_get_height(vw->viking_vvp)/2 - event->y + vw->pan_y );
1248 vw->pan_move = TRUE;
1249 vw->pan_x = event->x;
1250 vw->pan_y = event->y;
1256 * get_location_strings:
1258 * Utility function to get positional strings for the given location
1259 * lat and lon strings will get allocated and so need to be freed after use
1261 static void get_location_strings ( VikWindow *vw, struct UTM utm, gchar **lat, gchar **lon )
1263 if ( vik_viewport_get_drawmode ( vw->viking_vvp ) == VIK_VIEWPORT_DRAWMODE_UTM ) {
1264 // Reuse lat for the first part (Zone + N or S, and lon for the second part (easting and northing) of a UTM format:
1265 // ZONE[N|S] EASTING NORTHING
1266 *lat = g_malloc(4*sizeof(gchar));
1267 // NB zone is stored in a char but is an actual number
1268 g_snprintf (*lat, 4, "%d%c", utm.zone, utm.letter);
1269 *lon = g_malloc(16*sizeof(gchar));
1270 g_snprintf (*lon, 16, "%d %d", (gint)utm.easting, (gint)utm.northing);
1274 a_coords_utm_to_latlon ( &utm, &ll );
1275 a_coords_latlon_to_string ( &ll, lat, lon );
1279 static void draw_mouse_motion (VikWindow *vw, GdkEventMotion *event)
1281 static VikCoord coord;
1282 static struct UTM utm;
1283 #define BUFFER_SIZE 50
1284 static char pointer_buf[BUFFER_SIZE];
1285 gchar *lat = NULL, *lon = NULL;
1288 VikDemInterpol interpol_method;
1290 /* This is a hack, but work far the best, at least for single pointer systems.
1291 * See http://bugzilla.gnome.org/show_bug.cgi?id=587714 for more. */
1293 gdk_window_get_pointer (event->window, &x, &y, NULL);
1297 toolbox_move(vw->vt, event);
1299 vik_viewport_screen_to_coord ( vw->viking_vvp, event->x, event->y, &coord );
1300 vik_coord_to_utm ( &coord, &utm );
1302 get_location_strings ( vw, utm, &lat, &lon );
1304 /* Change interpolate method according to scale */
1305 zoom = vik_viewport_get_zoom(vw->viking_vvp);
1307 interpol_method = VIK_DEM_INTERPOL_NONE;
1308 else if (zoom >= 1.0)
1309 interpol_method = VIK_DEM_INTERPOL_SIMPLE;
1311 interpol_method = VIK_DEM_INTERPOL_BEST;
1312 if ((alt = a_dems_get_elev_by_coord(&coord, interpol_method)) != VIK_DEM_INVALID_ELEVATION) {
1313 if ( a_vik_get_units_height () == VIK_UNITS_HEIGHT_METRES )
1314 g_snprintf ( pointer_buf, BUFFER_SIZE, _("%s %s %dm"), lat, lon, alt );
1316 g_snprintf ( pointer_buf, BUFFER_SIZE, _("%s %s %dft"), lat, lon, (int)VIK_METERS_TO_FEET(alt) );
1319 g_snprintf ( pointer_buf, BUFFER_SIZE, _("%s %s"), lat, lon );
1324 vik_statusbar_set_message ( vw->viking_vs, VIK_STATUSBAR_POSITION, pointer_buf );
1326 vik_window_pan_move ( vw, event );
1328 /* This is recommended by the GTK+ documentation, but does not work properly.
1329 * Use deprecated way until GTK+ gets a solution for correct motion hint handling:
1330 * http://bugzilla.gnome.org/show_bug.cgi?id=587714
1332 /* gdk_event_request_motions ( event ); */
1336 * Action the single click after a small timeout
1337 * If a double click has occurred then this will do nothing
1339 static gboolean vik_window_pan_timeout (VikWindow *vw)
1341 if ( ! vw->single_click_pending ) {
1342 // Double click happened, so don't do anything
1346 /* set panning origin */
1347 vw->pan_move = FALSE;
1348 vw->single_click_pending = FALSE;
1349 vik_viewport_set_center_screen ( vw->viking_vvp, vw->delayed_pan_x, vw->delayed_pan_y );
1352 // Really turn off the pan moving!!
1353 vw->pan_x = vw->pan_y = -1;
1357 static void vik_window_pan_release ( VikWindow *vw, GdkEventButton *event )
1359 gboolean do_draw = TRUE;
1361 if ( vw->pan_move == FALSE ) {
1362 vw->single_click_pending = !vw->single_click_pending;
1364 if ( vw->single_click_pending ) {
1365 // Store offset to use
1366 vw->delayed_pan_x = vw->pan_x;
1367 vw->delayed_pan_y = vw->pan_y;
1368 // Get double click time
1369 GtkSettings *gs = gtk_widget_get_settings ( GTK_WIDGET(vw) );
1370 GValue dct = { 0 }; // = G_VALUE_INIT; // GLIB 2.30+ only
1371 g_value_init ( &dct, G_TYPE_INT );
1372 g_object_get_property ( G_OBJECT(gs), "gtk-double-click-time", &dct );
1373 // Give chance for a double click to occur
1374 gint timer = g_value_get_int ( &dct ) + 50;
1375 g_timeout_add ( timer, (GSourceFunc)vik_window_pan_timeout, vw );
1379 vik_viewport_set_center_screen ( vw->viking_vvp, vw->pan_x, vw->pan_y );
1383 vik_viewport_set_center_screen ( vw->viking_vvp, vik_viewport_get_width(vw->viking_vvp)/2 - event->x + vw->pan_x,
1384 vik_viewport_get_height(vw->viking_vvp)/2 - event->y + vw->pan_y );
1387 vw->pan_move = FALSE;
1388 vw->pan_x = vw->pan_y = -1;
1393 static void draw_release ( VikWindow *vw, GdkEventButton *event )
1395 gtk_widget_grab_focus ( GTK_WIDGET(vw->viking_vvp) );
1397 if ( event->button == 2 ) { /* move / pan */
1398 if ( vw->vt->tools[vw->vt->active_tool].ti.pan_handler )
1399 // Tool still may need to do something (such as reenable something)
1400 toolbox_release(vw->vt, event);
1401 vik_window_pan_release ( vw, event );
1404 toolbox_release(vw->vt, event);
1408 static void draw_scroll (VikWindow *vw, GdkEventScroll *event)
1410 guint modifiers = event->state & (GDK_SHIFT_MASK | GDK_CONTROL_MASK);
1411 if ( modifiers == GDK_CONTROL_MASK ) {
1412 /* control == pan up & down */
1413 if ( event->direction == GDK_SCROLL_UP )
1414 vik_viewport_set_center_screen ( vw->viking_vvp, vik_viewport_get_width(vw->viking_vvp)/2, vik_viewport_get_height(vw->viking_vvp)/3 );
1416 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 );
1417 } else if ( modifiers == GDK_SHIFT_MASK ) {
1418 /* shift == pan left & right */
1419 if ( event->direction == GDK_SCROLL_UP )
1420 vik_viewport_set_center_screen ( vw->viking_vvp, vik_viewport_get_width(vw->viking_vvp)/3, vik_viewport_get_height(vw->viking_vvp)/2 );
1422 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 );
1423 } else if ( modifiers == (GDK_CONTROL_MASK | GDK_SHIFT_MASK) ) {
1424 // This zoom is on the center position
1425 if ( event->direction == GDK_SCROLL_UP )
1426 vik_viewport_zoom_in (vw->viking_vvp);
1428 vik_viewport_zoom_out (vw->viking_vvp);
1430 /* make sure mouse is still over the same point on the map when we zoom */
1433 gint center_x = vik_viewport_get_width ( vw->viking_vvp ) / 2;
1434 gint center_y = vik_viewport_get_height ( vw->viking_vvp ) / 2;
1435 vik_viewport_screen_to_coord ( vw->viking_vvp, event->x, event->y, &coord );
1436 if ( event->direction == GDK_SCROLL_UP )
1437 vik_viewport_zoom_in (vw->viking_vvp);
1439 vik_viewport_zoom_out(vw->viking_vvp);
1440 vik_viewport_coord_to_screen ( vw->viking_vvp, &coord, &x, &y );
1441 vik_viewport_set_center_screen ( vw->viking_vvp, center_x + (x - event->x),
1442 center_y + (y - event->y) );
1450 /********************************************************************************
1452 ********************************************************************************/
1453 static void draw_ruler(VikViewport *vvp, GdkDrawable *d, GdkGC *gc, gint x1, gint y1, gint x2, gint y2, gdouble distance)
1457 GdkGC *labgc = vik_viewport_new_gc ( vvp, "#cccccc", 1);
1458 GdkGC *thickgc = gdk_gc_new(d);
1460 gdouble len = sqrt((x1-x2)*(x1-x2) + (y1-y2)*(y1-y2));
1461 gdouble dx = (x2-x1)/len*10;
1462 gdouble dy = (y2-y1)/len*10;
1463 gdouble c = cos(DEG2RAD(15.0));
1464 gdouble s = sin(DEG2RAD(15.0));
1466 gdouble baseangle = 0;
1469 /* draw line with arrow ends */
1471 gint tmp_x1=x1, tmp_y1=y1, tmp_x2=x2, tmp_y2=y2;
1472 a_viewport_clip_line(&tmp_x1, &tmp_y1, &tmp_x2, &tmp_y2);
1473 gdk_draw_line(d, gc, tmp_x1, tmp_y1, tmp_x2, tmp_y2);
1476 a_viewport_clip_line(&x1, &y1, &x2, &y2);
1477 gdk_draw_line(d, gc, x1, y1, x2, y2);
1479 gdk_draw_line(d, gc, x1 - dy, y1 + dx, x1 + dy, y1 - dx);
1480 gdk_draw_line(d, gc, x2 - dy, y2 + dx, x2 + dy, y2 - dx);
1481 gdk_draw_line(d, gc, x2, y2, x2 - (dx * c + dy * s), y2 - (dy * c - dx * s));
1482 gdk_draw_line(d, gc, x2, y2, x2 - (dx * c - dy * s), y2 - (dy * c + dx * s));
1483 gdk_draw_line(d, gc, x1, y1, x1 + (dx * c + dy * s), y1 + (dy * c - dx * s));
1484 gdk_draw_line(d, gc, x1, y1, x1 + (dx * c - dy * s), y1 + (dy * c + dx * s));
1490 vik_viewport_compute_bearing ( vvp, x1, y1, x2, y2, &angle, &baseangle );
1494 gdk_gc_copy(thickgc, gc);
1495 gdk_gc_set_line_attributes(thickgc, CW, GDK_LINE_SOLID, GDK_CAP_BUTT, GDK_JOIN_MITER);
1496 gdk_color_parse("#2255cc", &color);
1497 gdk_gc_set_rgb_fg_color(thickgc, &color);
1499 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);
1502 gdk_gc_copy(thickgc, gc);
1503 gdk_gc_set_line_attributes(thickgc, 2, GDK_LINE_SOLID, GDK_CAP_BUTT, GDK_JOIN_MITER);
1504 for (i=0; i<180; i++) {
1505 c = cos(DEG2RAD(i)*2 + baseangle);
1506 s = sin(DEG2RAD(i)*2 + baseangle);
1509 gdk_draw_line (d, gc, x1 + CR*c, y1 + CR*s, x1 + (CR+CW)*c, y1 + (CR+CW)*s);
1511 gdouble ticksize = 2*CW;
1512 gdk_draw_line (d, thickgc, x1 + (CR-CW)*c, y1 + (CR-CW)*s, x1 + (CR+ticksize)*c, y1 + (CR+ticksize)*s);
1516 gdk_draw_arc (d, gc, FALSE, x1-CR, y1-CR, 2*CR, 2*CR, 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 gdk_draw_arc (d, gc, FALSE, x1-CR+CW, y1-CR+CW, 2*(CR-CW), 2*(CR-CW), 0, 64*360);
1519 c = (CR+CW*2)*cos(baseangle);
1520 s = (CR+CW*2)*sin(baseangle);
1521 gdk_draw_line (d, gc, x1-c, y1-s, x1+c, y1+s);
1522 gdk_draw_line (d, gc, x1+s, y1-c, x1-s, y1+c);
1525 #define LABEL(x, y, w, h) { \
1526 gdk_draw_rectangle(d, labgc, TRUE, (x)-2, (y)-1, (w)+4, (h)+1); \
1527 gdk_draw_rectangle(d, gc, FALSE, (x)-2, (y)-1, (w)+4, (h)+1); \
1528 gdk_draw_layout(d, gc, (x), (y), pl); }
1530 gint wd, hd, xd, yd;
1531 gint wb, hb, xb, yb;
1533 pl = gtk_widget_create_pango_layout (GTK_WIDGET(vvp), NULL);
1534 pango_layout_set_font_description (pl, gtk_widget_get_style(GTK_WIDGET(vvp))->font_desc);
1535 pango_layout_set_text(pl, "N", -1);
1536 gdk_draw_layout(d, gc, x1-5, y1-CR-3*CW-8, pl);
1538 /* draw label with distance */
1539 vik_units_distance_t dist_units = a_vik_get_units_distance ();
1540 switch (dist_units) {
1541 case VIK_UNITS_DISTANCE_KILOMETRES:
1542 if (distance >= 1000 && distance < 100000) {
1543 g_sprintf(str, "%3.2f km", distance/1000.0);
1544 } else if (distance < 1000) {
1545 g_sprintf(str, "%d m", (int)distance);
1547 g_sprintf(str, "%d km", (int)distance/1000);
1550 case VIK_UNITS_DISTANCE_MILES:
1551 if (distance >= VIK_MILES_TO_METERS(1) && distance < VIK_MILES_TO_METERS(100)) {
1552 g_sprintf(str, "%3.2f miles", VIK_METERS_TO_MILES(distance));
1553 } else if (distance < VIK_MILES_TO_METERS(1)) {
1554 g_sprintf(str, "%d yards", (int)(distance*1.0936133));
1556 g_sprintf(str, "%d miles", (int)VIK_METERS_TO_MILES(distance));
1559 case VIK_UNITS_DISTANCE_NAUTICAL_MILES:
1560 if (distance >= VIK_NAUTICAL_MILES_TO_METERS(1) && distance < VIK_NAUTICAL_MILES_TO_METERS(100)) {
1561 g_sprintf(str, "%3.2f NM", VIK_METERS_TO_NAUTICAL_MILES(distance));
1562 } else if (distance < VIK_NAUTICAL_MILES_TO_METERS(1)) {
1563 g_sprintf(str, "%d yards", (int)(distance*1.0936133));
1565 g_sprintf(str, "%d NM", (int)VIK_METERS_TO_NAUTICAL_MILES(distance));
1569 g_critical("Houston, we've had a problem. distance=%d", dist_units);
1572 pango_layout_set_text(pl, str, -1);
1574 pango_layout_get_pixel_size ( pl, &wd, &hd );
1576 xd = (x1+x2)/2 + dy;
1577 yd = (y1+y2)/2 - hd/2 - dx;
1579 xd = (x1+x2)/2 - dy;
1580 yd = (y1+y2)/2 - hd/2 + dx;
1583 if ( xd < -5 || yd < -5 || xd > vik_viewport_get_width(vvp)+5 || yd > vik_viewport_get_height(vvp)+5 ) {
1588 LABEL(xd, yd, wd, hd);
1590 /* draw label with bearing */
1591 g_sprintf(str, "%3.1f°", RAD2DEG(angle));
1592 pango_layout_set_text(pl, str, -1);
1593 pango_layout_get_pixel_size ( pl, &wb, &hb );
1594 xb = x1 + CR*cos(angle-M_PI_2);
1595 yb = y1 + CR*sin(angle-M_PI_2);
1597 if ( xb < -5 || yb < -5 || xb > vik_viewport_get_width(vvp)+5 || yb > vik_viewport_get_height(vvp)+5 ) {
1603 GdkRectangle r1 = {xd-2, yd-1, wd+4, hd+1}, r2 = {xb-2, yb-1, wb+4, hb+1};
1604 if (gdk_rectangle_intersect(&r1, &r2, &r2)) {
1608 LABEL(xb, yb, wb, hb);
1612 g_object_unref ( G_OBJECT ( pl ) );
1613 g_object_unref ( G_OBJECT ( labgc ) );
1614 g_object_unref ( G_OBJECT ( thickgc ) );
1620 gboolean has_oldcoord;
1622 } ruler_tool_state_t;
1624 static gpointer ruler_create (VikWindow *vw, VikViewport *vvp)
1626 ruler_tool_state_t *s = g_new(ruler_tool_state_t, 1);
1629 s->has_oldcoord = FALSE;
1633 static void ruler_destroy (ruler_tool_state_t *s)
1638 static VikLayerToolFuncStatus ruler_click (VikLayer *vl, GdkEventButton *event, ruler_tool_state_t *s)
1643 if ( event->button == 1 ) {
1644 gchar *lat=NULL, *lon=NULL;
1645 vik_viewport_screen_to_coord ( s->vvp, (gint) event->x, (gint) event->y, &coord );
1646 vik_coord_to_latlon ( &coord, &ll );
1647 a_coords_latlon_to_string ( &ll, &lat, &lon );
1648 if ( s->has_oldcoord ) {
1649 vik_units_distance_t dist_units = a_vik_get_units_distance ();
1650 switch (dist_units) {
1651 case VIK_UNITS_DISTANCE_KILOMETRES:
1652 temp = g_strdup_printf ( "%s %s DIFF %f meters", lat, lon, vik_coord_diff( &coord, &(s->oldcoord) ) );
1654 case VIK_UNITS_DISTANCE_MILES:
1655 temp = g_strdup_printf ( "%s %s DIFF %f miles", lat, lon, VIK_METERS_TO_MILES(vik_coord_diff( &coord, &(s->oldcoord) )) );
1657 case VIK_UNITS_DISTANCE_NAUTICAL_MILES:
1658 temp = g_strdup_printf ( "%s %s DIFF %f NM", lat, lon, VIK_METERS_TO_NAUTICAL_MILES(vik_coord_diff( &coord, &(s->oldcoord) )) );
1661 temp = g_strdup_printf ("Just to keep the compiler happy");
1662 g_critical("Houston, we've had a problem. distance=%d", dist_units);
1665 s->has_oldcoord = FALSE;
1668 temp = g_strdup_printf ( "%s %s", lat, lon );
1669 s->has_oldcoord = TRUE;
1672 vik_statusbar_set_message ( s->vw->viking_vs, VIK_STATUSBAR_INFO, temp );
1675 s->oldcoord = coord;
1678 vik_viewport_set_center_screen ( s->vvp, (gint) event->x, (gint) event->y );
1679 draw_update ( s->vw );
1681 return VIK_LAYER_TOOL_ACK;
1684 static VikLayerToolFuncStatus ruler_move (VikLayer *vl, GdkEventMotion *event, ruler_tool_state_t *s)
1686 VikViewport *vvp = s->vvp;
1687 VikWindow *vw = s->vw;
1693 if ( s->has_oldcoord ) {
1694 int oldx, oldy, w1, h1, w2, h2;
1695 static GdkPixmap *buf = NULL;
1696 gchar *lat=NULL, *lon=NULL;
1697 w1 = vik_viewport_get_width(vvp);
1698 h1 = vik_viewport_get_height(vvp);
1700 buf = gdk_pixmap_new ( gtk_widget_get_window(GTK_WIDGET(vvp)), w1, h1, -1 );
1702 gdk_drawable_get_size(buf, &w2, &h2);
1703 if (w1 != w2 || h1 != h2) {
1704 g_object_unref ( G_OBJECT ( buf ) );
1705 buf = gdk_pixmap_new ( gtk_widget_get_window(GTK_WIDGET(vvp)), w1, h1, -1 );
1708 vik_viewport_screen_to_coord ( vvp, (gint) event->x, (gint) event->y, &coord );
1709 vik_coord_to_latlon ( &coord, &ll );
1710 vik_viewport_coord_to_screen ( vvp, &s->oldcoord, &oldx, &oldy );
1712 gdk_draw_drawable (buf, gtk_widget_get_style(GTK_WIDGET(vvp))->black_gc,
1713 vik_viewport_get_pixmap(vvp), 0, 0, 0, 0, -1, -1);
1714 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)) );
1715 if (draw_buf_done) {
1716 static gpointer pass_along[3];
1717 pass_along[0] = gtk_widget_get_window(GTK_WIDGET(vvp));
1718 pass_along[1] = gtk_widget_get_style(GTK_WIDGET(vvp))->black_gc;
1719 pass_along[2] = buf;
1720 g_idle_add_full (G_PRIORITY_HIGH_IDLE + 10, draw_buf, pass_along, NULL);
1721 draw_buf_done = FALSE;
1723 a_coords_latlon_to_string(&ll, &lat, &lon);
1724 vik_units_distance_t dist_units = a_vik_get_units_distance ();
1725 switch (dist_units) {
1726 case VIK_UNITS_DISTANCE_KILOMETRES:
1727 temp = g_strdup_printf ( "%s %s DIFF %f meters", lat, lon, vik_coord_diff( &coord, &(s->oldcoord) ) );
1729 case VIK_UNITS_DISTANCE_MILES:
1730 temp = g_strdup_printf ( "%s %s DIFF %f miles", lat, lon, VIK_METERS_TO_MILES (vik_coord_diff( &coord, &(s->oldcoord) )) );
1732 case VIK_UNITS_DISTANCE_NAUTICAL_MILES:
1733 temp = g_strdup_printf ( "%s %s DIFF %f NM", lat, lon, VIK_METERS_TO_NAUTICAL_MILES (vik_coord_diff( &coord, &(s->oldcoord) )) );
1736 temp = g_strdup_printf ("Just to keep the compiler happy");
1737 g_critical("Houston, we've had a problem. distance=%d", dist_units);
1739 vik_statusbar_set_message ( vw->viking_vs, VIK_STATUSBAR_INFO, temp );
1742 return VIK_LAYER_TOOL_ACK;
1745 static VikLayerToolFuncStatus ruler_release (VikLayer *vl, GdkEventButton *event, ruler_tool_state_t *s)
1747 return VIK_LAYER_TOOL_ACK;
1750 static void ruler_deactivate (VikLayer *vl, ruler_tool_state_t *s)
1752 draw_update ( s->vw );
1755 static gboolean ruler_key_press (VikLayer *vl, GdkEventKey *event, ruler_tool_state_t *s)
1757 if (event->keyval == GDK_Escape) {
1758 s->has_oldcoord = FALSE;
1759 ruler_deactivate ( vl, s );
1762 // Regardless of whether we used it, return false so other GTK things may use it
1766 static VikToolInterface ruler_tool =
1767 // 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 =
2041 { { "Zoom", "vik-icon-zoom", N_("_Zoom"), "<control><shift>Z", N_("Zoom Tool"), 1 },
2042 (VikToolConstructorFunc) zoomtool_create,
2043 (VikToolDestructorFunc) zoomtool_destroy,
2044 (VikToolActivationFunc) NULL,
2045 (VikToolActivationFunc) NULL,
2046 (VikToolMouseFunc) zoomtool_click,
2047 (VikToolMouseMoveFunc) zoomtool_move,
2048 (VikToolMouseFunc) zoomtool_release,
2051 GDK_CURSOR_IS_PIXMAP,
2052 &cursor_zoom_pixbuf,
2054 /*** end zoom code ********************************************************/
2056 /********************************************************************************
2058 ********************************************************************************/
2059 static gpointer pantool_create (VikWindow *vw, VikViewport *vvp)
2064 // NB Double clicking means this gets called THREE times!!!
2065 static VikLayerToolFuncStatus pantool_click (VikLayer *vl, GdkEventButton *event, VikWindow *vw)
2067 vw->modified = TRUE;
2069 if ( event->type == GDK_2BUTTON_PRESS ) {
2070 // Zoom in / out on double click
2071 // No need to change the center as that has already occurred in the first click of a double click occurrence
2072 if ( event->button == 1 ) {
2073 guint modifier = event->state & GDK_SHIFT_MASK;
2075 vik_viewport_zoom_out ( vw->viking_vvp );
2077 vik_viewport_zoom_in ( vw->viking_vvp );
2079 else if ( event->button == 3 )
2080 vik_viewport_zoom_out ( vw->viking_vvp );
2085 // Standard pan click
2086 if ( event->button == 1 )
2087 vik_window_pan_click ( vw, event );
2089 return VIK_LAYER_TOOL_ACK;
2092 static VikLayerToolFuncStatus pantool_move (VikLayer *vl, GdkEventMotion *event, VikWindow *vw)
2094 vik_window_pan_move ( vw, event );
2095 return VIK_LAYER_TOOL_ACK;
2098 static VikLayerToolFuncStatus pantool_release (VikLayer *vl, GdkEventButton *event, VikWindow *vw)
2100 if ( event->button == 1 )
2101 vik_window_pan_release ( vw, event );
2102 return VIK_LAYER_TOOL_ACK;
2105 static VikToolInterface pan_tool =
2106 { { "Pan", "vik-icon-pan", N_("_Pan"), "<control><shift>P", N_("Pan Tool"), 0 },
2107 (VikToolConstructorFunc) pantool_create,
2108 (VikToolDestructorFunc) NULL,
2109 (VikToolActivationFunc) NULL,
2110 (VikToolActivationFunc) NULL,
2111 (VikToolMouseFunc) pantool_click,
2112 (VikToolMouseMoveFunc) pantool_move,
2113 (VikToolMouseFunc) pantool_release,
2119 /*** end pan code ********************************************************/
2121 /********************************************************************************
2123 ********************************************************************************/
2124 static gpointer selecttool_create (VikWindow *vw, VikViewport *vvp)
2126 tool_ed_t *t = g_new(tool_ed_t, 1);
2130 t->is_waypoint = FALSE;
2134 static void selecttool_destroy (tool_ed_t *t)
2142 GdkEventButton *event;
2143 tool_ed_t *tool_edit;
2146 static void click_layer_selected (VikLayer *vl, clicker *ck)
2148 /* Do nothing when function call returns true; */
2149 /* i.e. stop on first found item */
2152 if ( vik_layer_get_interface(vl->type)->select_click )
2153 ck->cont = !vik_layer_get_interface(vl->type)->select_click ( vl, ck->event, ck->vvp, ck->tool_edit );
2157 // Hopefully Alt keys by default
2158 #define VIK_MOVE_MODIFIER GDK_MOD1_MASK
2160 // Alt+mouse on Linux desktops tend to be used by the desktop manager
2161 // Thus use an alternate modifier - you may need to set something into this group
2162 #define VIK_MOVE_MODIFIER GDK_MOD5_MASK
2165 static VikLayerToolFuncStatus selecttool_click (VikLayer *vl, GdkEventButton *event, tool_ed_t *t)
2167 t->vw->select_move = FALSE;
2168 /* Only allow selection on primary button */
2169 if ( event->button == 1 ) {
2171 if ( event->state & VIK_MOVE_MODIFIER )
2172 vik_window_pan_click ( t->vw, event );
2174 /* Enable click to apply callback to potentially all track/waypoint layers */
2175 /* Useful as we can find things that aren't necessarily in the currently selected layer */
2176 GList* gl = vik_layers_panel_get_all_layers_of_type ( t->vw->viking_vlp, VIK_LAYER_TRW, FALSE ); // Don't get invisible layers
2179 ck.vvp = t->vw->viking_vvp;
2182 g_list_foreach ( gl, (GFunc) click_layer_selected, &ck );
2185 // If nothing found then deselect & redraw screen if necessary to remove the highlight
2188 VikTreeview *vtv = vik_layers_panel_get_treeview ( t->vw->viking_vlp );
2190 if ( vik_treeview_get_selected_iter ( vtv, &iter ) ) {
2191 // Only clear if selected thing is a TrackWaypoint layer or a sublayer
2192 gint type = vik_treeview_item_get_type ( vtv, &iter );
2193 if ( type == VIK_TREEVIEW_TYPE_SUBLAYER ||
2194 VIK_LAYER(vik_treeview_item_get_pointer ( vtv, &iter ))->type == VIK_LAYER_TRW ) {
2196 vik_treeview_item_unselect ( vtv, &iter );
2197 if ( vik_window_clear_highlight ( t->vw ) )
2198 draw_update ( t->vw );
2203 // Something found - so enable movement
2204 t->vw->select_move = TRUE;
2208 else if ( ( event->button == 3 ) && ( vl && ( vl->type == VIK_LAYER_TRW ) ) ) {
2210 /* Act on currently selected item to show menu */
2211 if ( t->vw->selected_track || t->vw->selected_waypoint )
2212 if ( vik_layer_get_interface(vl->type)->show_viewport_menu )
2213 vik_layer_get_interface(vl->type)->show_viewport_menu ( vl, event, t->vw->viking_vvp );
2216 return VIK_LAYER_TOOL_ACK;
2219 static VikLayerToolFuncStatus selecttool_move (VikLayer *vl, GdkEventMotion *event, tool_ed_t *t)
2221 if ( t->vw->select_move ) {
2222 // Don't care about vl here
2224 if ( vik_layer_get_interface(VIK_LAYER_TRW)->select_move )
2225 vik_layer_get_interface(VIK_LAYER_TRW)->select_move ( vl, event, t->vvp, t );
2229 if ( event->state & VIK_MOVE_MODIFIER )
2230 vik_window_pan_move ( t->vw, event );
2232 return VIK_LAYER_TOOL_ACK;
2235 static VikLayerToolFuncStatus selecttool_release (VikLayer *vl, GdkEventButton *event, tool_ed_t *t)
2237 if ( t->vw->select_move ) {
2238 // Don't care about vl here
2240 if ( vik_layer_get_interface(VIK_LAYER_TRW)->select_release )
2241 vik_layer_get_interface(VIK_LAYER_TRW)->select_release ( (VikLayer*)t->vtl, event, t->vvp, t );
2244 if ( event->button == 1 && (event->state & VIK_MOVE_MODIFIER) )
2245 vik_window_pan_release ( t->vw, event );
2247 // Force pan off incase it was on
2248 t->vw->pan_move = FALSE;
2249 t->vw->pan_x = t->vw->pan_y = -1;
2251 // End of this select movement
2252 t->vw->select_move = FALSE;
2254 return VIK_LAYER_TOOL_ACK;
2257 static VikToolInterface select_tool =
2258 { { "Select", "vik-icon-select", N_("_Select"), "<control><shift>S", N_("Select Tool"), 3 },
2259 (VikToolConstructorFunc) selecttool_create,
2260 (VikToolDestructorFunc) selecttool_destroy,
2261 (VikToolActivationFunc) NULL,
2262 (VikToolActivationFunc) NULL,
2263 (VikToolMouseFunc) selecttool_click,
2264 (VikToolMouseMoveFunc) selecttool_move,
2265 (VikToolMouseFunc) selecttool_release,
2266 (VikToolKeyFunc) NULL,
2271 /*** end select tool code ********************************************************/
2273 static void draw_pan_cb ( GtkAction *a, VikWindow *vw )
2275 // Since the treeview cell editting intercepts standard keyboard handlers, it means we can receive events here
2276 // Thus if currently editting, ensure we don't move the viewport when Ctrl+<arrow> is received
2277 VikLayer *sel = vik_layers_panel_get_selected ( vw->viking_vlp );
2278 if ( sel && vik_treeview_get_editing ( sel->vt ) )
2281 if (!strcmp(gtk_action_get_name(a), "PanNorth")) {
2282 vik_viewport_set_center_screen ( vw->viking_vvp, vik_viewport_get_width(vw->viking_vvp)/2, 0 );
2283 } else if (!strcmp(gtk_action_get_name(a), "PanEast")) {
2284 vik_viewport_set_center_screen ( vw->viking_vvp, vik_viewport_get_width(vw->viking_vvp), vik_viewport_get_height(vw->viking_vvp)/2 );
2285 } else if (!strcmp(gtk_action_get_name(a), "PanSouth")) {
2286 vik_viewport_set_center_screen ( vw->viking_vvp, vik_viewport_get_width(vw->viking_vvp)/2, vik_viewport_get_height(vw->viking_vvp) );
2287 } else if (!strcmp(gtk_action_get_name(a), "PanWest")) {
2288 vik_viewport_set_center_screen ( vw->viking_vvp, 0, vik_viewport_get_height(vw->viking_vvp)/2 );
2293 static void draw_zoom_cb ( GtkAction *a, VikWindow *vw )
2297 if (!strcmp(gtk_action_get_name(a), "ZoomIn")) {
2300 else if (!strcmp(gtk_action_get_name(a), "ZoomOut")) {
2303 else if (!strcmp(gtk_action_get_name(a), "Zoom0.25")) {
2306 else if (!strcmp(gtk_action_get_name(a), "Zoom0.5")) {
2310 gchar *s = (gchar *)gtk_action_get_name(a);
2316 case -3: vik_viewport_zoom_in ( vw->viking_vvp ); break;
2317 case -4: vik_viewport_zoom_out ( vw->viking_vvp ); break;
2318 case -1: vik_viewport_set_zoom ( vw->viking_vvp, 0.5 ); break;
2319 case -2: vik_viewport_set_zoom ( vw->viking_vvp, 0.25 ); break;
2320 default: vik_viewport_set_zoom ( vw->viking_vvp, what );
2325 static void draw_goto_cb ( GtkAction *a, VikWindow *vw )
2327 VikCoord new_center;
2329 if (!strcmp(gtk_action_get_name(a), "GotoLL")) {
2330 struct LatLon ll, llold;
2331 vik_coord_to_latlon ( vik_viewport_get_center ( vw->viking_vvp ), &llold );
2332 if ( a_dialog_goto_latlon ( GTK_WINDOW(vw), &ll, &llold ) )
2333 vik_coord_load_from_latlon ( &new_center, vik_viewport_get_coord_mode(vw->viking_vvp), &ll );
2337 else if (!strcmp(gtk_action_get_name(a), "GotoUTM")) {
2338 struct UTM utm, utmold;
2339 vik_coord_to_utm ( vik_viewport_get_center ( vw->viking_vvp ), &utmold );
2340 if ( a_dialog_goto_utm ( GTK_WINDOW(vw), &utm, &utmold ) )
2341 vik_coord_load_from_utm ( &new_center, vik_viewport_get_coord_mode(vw->viking_vvp), &utm );
2346 g_critical("Houston, we've had a problem.");
2350 vik_viewport_set_center_coord ( vw->viking_vvp, &new_center, TRUE );
2355 * center_changed_cb:
2357 static void center_changed_cb ( VikWindow *vw )
2359 // ATM Keep back always available, so when we pan - we can jump to the last requested position
2361 GtkAction* action_back = gtk_action_group_get_action ( vw->action_group, "GoBack" );
2362 if ( action_back ) {
2363 gtk_action_set_sensitive ( action_back, vik_viewport_back_available(vw->viking_vvp) );
2366 GtkAction* action_forward = gtk_action_group_get_action ( vw->action_group, "GoForward" );
2367 if ( action_forward ) {
2368 gtk_action_set_sensitive ( action_forward, vik_viewport_forward_available(vw->viking_vvp) );
2371 toolbar_action_set_sensitive ( vw->viking_vtb, "GoForward", vik_viewport_forward_available(vw->viking_vvp) );
2375 * draw_goto_back_and_forth:
2377 static void draw_goto_back_and_forth ( GtkAction *a, VikWindow *vw )
2379 gboolean changed = FALSE;
2380 if (!strcmp(gtk_action_get_name(a), "GoBack")) {
2381 changed = vik_viewport_go_back ( vw->viking_vvp );
2383 else if (!strcmp(gtk_action_get_name(a), "GoForward")) {
2384 changed = vik_viewport_go_forward ( vw->viking_vvp );
2390 // Recheck buttons sensitivities, as the center changed signal is not sent on back/forward changes
2391 // (otherwise we would get stuck in an infinite loop!)
2392 center_changed_cb ( vw );
2399 * Refresh maps displayed
2401 static void draw_refresh_cb ( GtkAction *a, VikWindow *vw )
2403 // Only get 'new' maps
2404 simple_map_update ( vw, TRUE );
2407 static void menu_addlayer_cb ( GtkAction *a, VikWindow *vw )
2409 VikLayerTypeEnum type;
2410 for ( type = 0; type < VIK_LAYER_NUM_TYPES; type++ ) {
2411 if (!strcmp(vik_layer_get_interface(type)->name, gtk_action_get_name(a))) {
2412 if ( vik_layers_panel_new_layer ( vw->viking_vlp, type ) ) {
2414 vw->modified = TRUE;
2420 static void menu_copy_layer_cb ( GtkAction *a, VikWindow *vw )
2422 a_clipboard_copy_selected ( vw->viking_vlp );
2425 static void menu_cut_layer_cb ( GtkAction *a, VikWindow *vw )
2427 vik_layers_panel_cut_selected ( vw->viking_vlp );
2428 vw->modified = TRUE;
2431 static void menu_paste_layer_cb ( GtkAction *a, VikWindow *vw )
2433 if ( vik_layers_panel_paste_selected ( vw->viking_vlp ) )
2435 vw->modified = TRUE;
2439 static void menu_properties_cb ( GtkAction *a, VikWindow *vw )
2441 if ( ! vik_layers_panel_properties ( vw->viking_vlp ) )
2442 a_dialog_info_msg ( GTK_WINDOW(vw), _("You must select a layer to show its properties.") );
2445 static void help_help_cb ( GtkAction *a, VikWindow *vw )
2448 ShellExecute(NULL, "open", ""PACKAGE".pdf", NULL, NULL, SW_SHOWNORMAL);
2451 uri = g_strdup_printf("ghelp:%s", PACKAGE);
2452 GError *error = NULL;
2453 gboolean show = gtk_show_uri (NULL, uri, GDK_CURRENT_TIME, &error);
2454 if ( !show && !error )
2455 // No error to show, so unlikely this will get called
2456 a_dialog_error_msg ( GTK_WINDOW(vw), _("The help system is not available.") );
2459 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 );
2460 g_error_free ( error );
2463 #endif /* WINDOWS */
2466 static void toggle_side_panel ( VikWindow *vw )
2468 vw->show_side_panel = !vw->show_side_panel;
2469 if ( vw->show_side_panel )
2470 gtk_widget_show(GTK_WIDGET(vw->viking_vlp));
2472 gtk_widget_hide(GTK_WIDGET(vw->viking_vlp));
2475 static void toggle_full_screen ( VikWindow *vw )
2477 vw->show_full_screen = !vw->show_full_screen;
2478 if ( vw->show_full_screen )
2479 gtk_window_fullscreen ( GTK_WINDOW(vw) );
2481 gtk_window_unfullscreen ( GTK_WINDOW(vw) );
2484 static void toggle_statusbar ( VikWindow *vw )
2486 vw->show_statusbar = !vw->show_statusbar;
2487 if ( vw->show_statusbar )
2488 gtk_widget_show ( GTK_WIDGET(vw->viking_vs) );
2490 gtk_widget_hide ( GTK_WIDGET(vw->viking_vs) );
2493 static void toggle_toolbar ( VikWindow *vw )
2495 vw->show_toolbar = !vw->show_toolbar;
2496 if ( vw->show_toolbar )
2497 gtk_widget_show ( toolbar_get_widget (vw->viking_vtb) );
2499 gtk_widget_hide ( toolbar_get_widget (vw->viking_vtb) );
2502 static void toggle_main_menu ( VikWindow *vw )
2504 vw->show_main_menu = !vw->show_main_menu;
2505 if ( vw->show_main_menu )
2506 gtk_widget_show ( gtk_ui_manager_get_widget ( vw->uim, "/ui/MainMenu" ) );
2508 gtk_widget_hide ( gtk_ui_manager_get_widget ( vw->uim, "/ui/MainMenu" ) );
2511 // Only for 'view' toggle menu widgets ATM.
2512 GtkWidget *get_show_widget_by_name(VikWindow *vw, const gchar *name)
2514 g_return_val_if_fail(name != NULL, NULL);
2516 // ATM only FullScreen is *not* in SetShow path
2518 if ( g_strcmp0 ("FullScreen", name ) )
2519 path = g_strconcat("/ui/MainMenu/View/SetShow/", name, NULL);
2521 path = g_strconcat("/ui/MainMenu/View/", name, NULL);
2523 GtkWidget *widget = gtk_ui_manager_get_widget(vw->uim, path);
2529 static void tb_view_side_panel_cb ( GtkAction *a, VikWindow *vw )
2531 gboolean next_state = !vw->show_side_panel;
2532 GtkWidget *check_box = get_show_widget_by_name ( vw, gtk_action_get_name(a) );
2533 gboolean menu_state = gtk_check_menu_item_get_active ( GTK_CHECK_MENU_ITEM(check_box) );
2534 if ( next_state != menu_state )
2535 gtk_check_menu_item_set_active ( GTK_CHECK_MENU_ITEM(check_box), next_state );
2537 toggle_side_panel ( vw );
2540 static void tb_full_screen_cb ( GtkAction *a, VikWindow *vw )
2542 gboolean next_state = !vw->show_full_screen;
2543 GtkWidget *check_box = get_show_widget_by_name ( vw, gtk_action_get_name(a) );
2544 gboolean menu_state = gtk_check_menu_item_get_active ( GTK_CHECK_MENU_ITEM(check_box) );
2545 if ( next_state != menu_state )
2546 gtk_check_menu_item_set_active ( GTK_CHECK_MENU_ITEM(check_box), next_state );
2548 toggle_full_screen ( vw );
2551 static void tb_view_statusbar_cb ( GtkAction *a, VikWindow *vw )
2553 gboolean next_state = !vw->show_statusbar;
2554 GtkWidget *check_box = get_show_widget_by_name ( vw, gtk_action_get_name(a) );
2555 gboolean menu_state = gtk_check_menu_item_get_active ( GTK_CHECK_MENU_ITEM(check_box) );
2556 if ( next_state != menu_state )
2557 gtk_check_menu_item_set_active ( GTK_CHECK_MENU_ITEM(check_box), next_state );
2559 toggle_statusbar ( vw );
2562 static void tb_view_toolbar_cb ( GtkAction *a, VikWindow *vw )
2564 gboolean next_state = !vw->show_toolbar;
2565 GtkWidget *check_box = get_show_widget_by_name ( vw, gtk_action_get_name(a) );
2566 gboolean menu_state = gtk_check_menu_item_get_active ( GTK_CHECK_MENU_ITEM(check_box) );
2567 if ( next_state != menu_state )
2568 gtk_check_menu_item_set_active ( GTK_CHECK_MENU_ITEM(check_box), next_state );
2570 toggle_toolbar ( vw );
2573 static void tb_view_main_menu_cb ( GtkAction *a, VikWindow *vw )
2575 gboolean next_state = !vw->show_main_menu;
2576 GtkWidget *check_box = get_show_widget_by_name ( vw, gtk_action_get_name(a) );
2577 gboolean menu_state = gtk_check_menu_item_get_active ( GTK_CHECK_MENU_ITEM(check_box) );
2578 if ( next_state != menu_state )
2579 gtk_check_menu_item_set_active ( GTK_CHECK_MENU_ITEM(check_box), next_state );
2581 toggle_main_menu ( vw );
2584 static void tb_set_draw_scale ( GtkAction *a, VikWindow *vw )
2586 gboolean next_state = !vik_viewport_get_draw_scale ( vw->viking_vvp );
2587 GtkWidget *check_box = get_show_widget_by_name ( vw, gtk_action_get_name(a) );
2588 gboolean menu_state = gtk_check_menu_item_get_active ( GTK_CHECK_MENU_ITEM(check_box) );
2589 if ( next_state != menu_state )
2590 gtk_check_menu_item_set_active ( GTK_CHECK_MENU_ITEM(check_box), next_state );
2592 vik_viewport_set_draw_scale ( vw->viking_vvp, next_state );
2597 static void tb_set_draw_centermark ( GtkAction *a, VikWindow *vw )
2599 gboolean next_state = !vik_viewport_get_draw_centermark ( vw->viking_vvp );
2600 GtkWidget *check_box = get_show_widget_by_name ( vw, gtk_action_get_name(a) );
2601 gboolean menu_state = gtk_check_menu_item_get_active ( GTK_CHECK_MENU_ITEM(check_box) );
2602 if ( next_state != menu_state )
2603 gtk_check_menu_item_set_active ( GTK_CHECK_MENU_ITEM(check_box), next_state );
2605 vik_viewport_set_draw_centermark ( vw->viking_vvp, next_state );
2610 static void tb_set_draw_highlight ( GtkAction *a, VikWindow *vw )
2612 gboolean next_state = !vik_viewport_get_draw_highlight ( vw->viking_vvp );
2613 GtkWidget *check_box = get_show_widget_by_name ( vw, gtk_action_get_name(a) );
2614 gboolean menu_state = gtk_check_menu_item_get_active ( GTK_CHECK_MENU_ITEM(check_box) );
2615 if ( next_state != menu_state )
2616 gtk_check_menu_item_set_active ( GTK_CHECK_MENU_ITEM(check_box), next_state );
2618 vik_viewport_set_draw_highlight ( vw->viking_vvp, next_state );
2623 static void help_about_cb ( GtkAction *a, VikWindow *vw )
2625 a_dialog_about(GTK_WINDOW(vw));
2628 static void help_cache_info_cb ( GtkAction *a, VikWindow *vw )
2630 // NB: No i18n as this is just for debug
2631 gint byte_size = a_mapcache_get_size();
2632 gchar *msg_sz = NULL;
2634 #if GLIB_CHECK_VERSION(2,30,0)
2635 msg_sz = g_format_size_full ( byte_size, G_FORMAT_SIZE_LONG_FORMAT );
2637 msg_sz = g_format_size_for_display ( byte_size );
2639 msg = g_strdup_printf ( "Map Cache size is %s with %d items", msg_sz, a_mapcache_get_count());
2640 a_dialog_info_msg_extra ( GTK_WINDOW(vw), "%s", msg );
2645 static void back_forward_info_cb ( GtkAction *a, VikWindow *vw )
2647 vik_viewport_show_centers ( vw->viking_vvp, GTK_WINDOW(vw) );
2650 static void menu_delete_layer_cb ( GtkAction *a, VikWindow *vw )
2652 if ( vik_layers_panel_get_selected ( vw->viking_vlp ) )
2654 vik_layers_panel_delete_selected ( vw->viking_vlp );
2655 vw->modified = TRUE;
2658 a_dialog_info_msg ( GTK_WINDOW(vw), _("You must select a layer to delete.") );
2661 static void full_screen_cb ( GtkAction *a, VikWindow *vw )
2663 gboolean next_state = !vw->show_full_screen;
2664 GtkToggleToolButton *tbutton = (GtkToggleToolButton *)toolbar_get_widget_by_name ( vw->viking_vtb, gtk_action_get_name(a) );
2666 gboolean tb_state = gtk_toggle_tool_button_get_active ( tbutton );
2667 if ( next_state != tb_state )
2668 gtk_toggle_tool_button_set_active ( tbutton, next_state );
2670 toggle_full_screen ( vw );
2673 toggle_full_screen ( vw );
2676 static void view_side_panel_cb ( GtkAction *a, VikWindow *vw )
2678 gboolean next_state = !vw->show_side_panel;
2679 GtkToggleToolButton *tbutton = (GtkToggleToolButton *)toolbar_get_widget_by_name ( vw->viking_vtb, gtk_action_get_name(a) );
2681 gboolean tb_state = gtk_toggle_tool_button_get_active ( tbutton );
2682 if ( next_state != tb_state )
2683 gtk_toggle_tool_button_set_active ( tbutton, next_state );
2685 toggle_side_panel ( vw );
2688 toggle_side_panel ( vw );
2691 static void view_statusbar_cb ( GtkAction *a, VikWindow *vw )
2693 gboolean next_state = !vw->show_statusbar;
2694 GtkToggleToolButton *tbutton = (GtkToggleToolButton *)toolbar_get_widget_by_name ( vw->viking_vtb, gtk_action_get_name(a) );
2696 gboolean tb_state = gtk_toggle_tool_button_get_active ( tbutton );
2697 if ( next_state != tb_state )
2698 gtk_toggle_tool_button_set_active ( tbutton, next_state );
2700 toggle_statusbar ( vw );
2703 toggle_statusbar ( vw );
2706 static void view_toolbar_cb ( GtkAction *a, VikWindow *vw )
2708 gboolean next_state = !vw->show_toolbar;
2709 GtkToggleToolButton *tbutton = (GtkToggleToolButton *)toolbar_get_widget_by_name ( vw->viking_vtb, gtk_action_get_name(a) );
2711 gboolean tb_state = gtk_toggle_tool_button_get_active ( tbutton );
2712 if ( next_state != tb_state )
2713 gtk_toggle_tool_button_set_active ( tbutton, next_state );
2715 toggle_toolbar ( vw );
2718 toggle_toolbar ( vw );
2721 static void view_main_menu_cb ( GtkAction *a, VikWindow *vw )
2723 gboolean next_state = !vw->show_main_menu;
2724 GtkToggleToolButton *tbutton = (GtkToggleToolButton *)toolbar_get_widget_by_name ( vw->viking_vtb, gtk_action_get_name(a) );
2726 gboolean tb_state = gtk_toggle_tool_button_get_active ( tbutton );
2727 if ( next_state != tb_state )
2728 gtk_toggle_tool_button_set_active ( tbutton, next_state );
2730 toggle_main_menu ( vw );
2733 toggle_main_menu ( vw );
2736 /***************************************
2737 ** tool management routines
2739 ***************************************/
2741 static toolbox_tools_t* toolbox_create(VikWindow *vw)
2743 toolbox_tools_t *vt = g_new(toolbox_tools_t, 1);
2746 vt->active_tool = -1;
2751 static void toolbox_add_tool(toolbox_tools_t *vt, VikToolInterface *vti, gint layer_type )
2753 vt->tools = g_renew(toolbox_tool_t, vt->tools, vt->n_tools+1);
2754 vt->tools[vt->n_tools].ti = *vti;
2755 vt->tools[vt->n_tools].layer_type = layer_type;
2757 vt->tools[vt->n_tools].state = vti->create(vt->vw, vt->vw->viking_vvp);
2760 vt->tools[vt->n_tools].state = NULL;
2765 static int toolbox_get_tool(toolbox_tools_t *vt, const gchar *tool_name)
2768 for (i=0; i<vt->n_tools; i++) {
2769 if (!strcmp(tool_name, vt->tools[i].ti.radioActionEntry.name)) {
2776 static void toolbox_activate(toolbox_tools_t *vt, const gchar *tool_name)
2778 int tool = toolbox_get_tool(vt, tool_name);
2779 toolbox_tool_t *t = &vt->tools[tool];
2780 VikLayer *vl = vik_layers_panel_get_selected ( vt->vw->viking_vlp );
2782 if (tool == vt->n_tools) {
2783 g_critical("trying to activate a non-existent tool...");
2786 /* is the tool already active? */
2787 if (vt->active_tool == tool) {
2791 if (vt->active_tool != -1) {
2792 if (vt->tools[vt->active_tool].ti.deactivate) {
2793 vt->tools[vt->active_tool].ti.deactivate(NULL, vt->tools[vt->active_tool].state);
2796 if (t->ti.activate) {
2797 t->ti.activate(vl, t->state);
2799 vt->active_tool = tool;
2802 static const GdkCursor *toolbox_get_cursor(toolbox_tools_t *vt, const gchar *tool_name)
2804 int tool = toolbox_get_tool(vt, tool_name);
2805 toolbox_tool_t *t = &vt->tools[tool];
2806 if (t->ti.cursor == NULL) {
2807 if (t->ti.cursor_type == GDK_CURSOR_IS_PIXMAP && t->ti.cursor_data != NULL) {
2808 GdkPixbuf *cursor_pixbuf = gdk_pixbuf_from_pixdata (t->ti.cursor_data, FALSE, NULL);
2809 /* TODO: settable offeset */
2810 t->ti.cursor = gdk_cursor_new_from_pixbuf ( gdk_display_get_default(), cursor_pixbuf, 3, 3 );
2811 g_object_unref ( G_OBJECT(cursor_pixbuf) );
2813 t->ti.cursor = gdk_cursor_new ( t->ti.cursor_type );
2816 return t->ti.cursor;
2819 static void toolbox_click (toolbox_tools_t *vt, GdkEventButton *event)
2821 VikLayer *vl = vik_layers_panel_get_selected ( vt->vw->viking_vlp );
2822 if (vt->active_tool != -1 && vt->tools[vt->active_tool].ti.click) {
2823 gint ltype = vt->tools[vt->active_tool].layer_type;
2824 if ( ltype == TOOL_LAYER_TYPE_NONE || (vl && ltype == vl->type) )
2825 vt->tools[vt->active_tool].ti.click(vl, event, vt->tools[vt->active_tool].state);
2829 static void toolbox_move (toolbox_tools_t *vt, GdkEventMotion *event)
2831 VikLayer *vl = vik_layers_panel_get_selected ( vt->vw->viking_vlp );
2832 if (vt->active_tool != -1 && vt->tools[vt->active_tool].ti.move) {
2833 gint ltype = vt->tools[vt->active_tool].layer_type;
2834 if ( ltype == TOOL_LAYER_TYPE_NONE || (vl && ltype == vl->type) )
2835 if ( VIK_LAYER_TOOL_ACK_GRAB_FOCUS == vt->tools[vt->active_tool].ti.move(vl, event, vt->tools[vt->active_tool].state) )
2836 gtk_widget_grab_focus ( GTK_WIDGET(vt->vw->viking_vvp) );
2840 static void toolbox_release (toolbox_tools_t *vt, GdkEventButton *event)
2842 VikLayer *vl = vik_layers_panel_get_selected ( vt->vw->viking_vlp );
2843 if (vt->active_tool != -1 && vt->tools[vt->active_tool].ti.release ) {
2844 gint ltype = vt->tools[vt->active_tool].layer_type;
2845 if ( ltype == TOOL_LAYER_TYPE_NONE || (vl && ltype == vl->type) )
2846 vt->tools[vt->active_tool].ti.release(vl, event, vt->tools[vt->active_tool].state);
2849 /** End tool management ************************************/
2851 void vik_window_enable_layer_tool ( VikWindow *vw, gint layer_id, gint tool_id )
2853 gtk_action_activate ( gtk_action_group_get_action ( vw->action_group, vik_layer_get_interface(layer_id)->tools[tool_id].radioActionEntry.name ) );
2856 // Be careful with usage - as it may trigger actions being continually alternately by the menu and toolbar items
2857 // DON'T Use this from menu callback with toggle toolbar items!!
2858 static void toolbar_sync ( VikWindow *vw, const gchar *name, gboolean state )
2860 GtkToggleToolButton *tbutton = (GtkToggleToolButton *)toolbar_get_widget_by_name ( vw->viking_vtb, name );
2862 // Causes toggle signal action to be raised.
2863 gtk_toggle_tool_button_set_active ( tbutton, state );
2867 /* this function gets called whenever a menu is clicked */
2868 // Note old is not used
2869 static void menu_cb ( GtkAction *old, GtkAction *a, VikWindow *vw )
2871 // Ensure Toolbar kept in sync
2872 const gchar *name = gtk_action_get_name(a);
2873 toolbar_sync ( vw, name, TRUE );
2875 /* White Magic, my friends ... White Magic... */
2877 toolbox_activate(vw->vt, name);
2879 vw->viewport_cursor = (GdkCursor *)toolbox_get_cursor(vw->vt, name);
2881 if ( gtk_widget_get_window(GTK_WIDGET(vw->viking_vvp)) )
2882 /* We set cursor, even if it is NULL: it resets to default */
2883 gdk_window_set_cursor ( gtk_widget_get_window(GTK_WIDGET(vw->viking_vvp)), vw->viewport_cursor );
2885 if (!g_strcmp0(name, "Pan")) {
2886 vw->current_tool = TOOL_PAN;
2888 else if (!g_strcmp0(name, "Zoom")) {
2889 vw->current_tool = TOOL_ZOOM;
2891 else if (!g_strcmp0(name, "Ruler")) {
2892 vw->current_tool = TOOL_RULER;
2894 else if (!g_strcmp0(name, "Select")) {
2895 vw->current_tool = TOOL_SELECT;
2898 VikLayerTypeEnum layer_id;
2899 for (layer_id=0; layer_id<VIK_LAYER_NUM_TYPES; layer_id++) {
2900 for ( tool_id = 0; tool_id < vik_layer_get_interface(layer_id)->tools_count; tool_id++ ) {
2901 if (!g_strcmp0(vik_layer_get_interface(layer_id)->tools[tool_id].radioActionEntry.name, name)) {
2902 vw->current_tool = TOOL_LAYER;
2903 vw->tool_layer_id = layer_id;
2904 vw->tool_tool_id = tool_id;
2909 draw_status_tool ( vw );
2912 static void window_set_filename ( VikWindow *vw, const gchar *filename )
2917 g_free ( vw->filename );
2918 if ( filename == NULL )
2920 vw->filename = NULL;
2924 vw->filename = g_strdup(filename);
2927 /* Refresh window's title */
2928 file = window_get_filename ( vw );
2929 title = g_strdup_printf( "%s - Viking", file );
2930 gtk_window_set_title ( GTK_WINDOW(vw), title );
2934 static const gchar *window_get_filename ( VikWindow *vw )
2936 return vw->filename ? a_file_basename ( vw->filename ) : _("Untitled");
2939 GtkWidget *vik_window_get_drawmode_button ( VikWindow *vw, VikViewportDrawMode mode )
2941 GtkWidget *mode_button;
2944 #ifdef VIK_CONFIG_EXPEDIA
2945 case VIK_VIEWPORT_DRAWMODE_EXPEDIA: buttonname = "/ui/MainMenu/View/ModeExpedia"; break;
2947 case VIK_VIEWPORT_DRAWMODE_MERCATOR: buttonname = "/ui/MainMenu/View/ModeMercator"; break;
2948 case VIK_VIEWPORT_DRAWMODE_LATLON: buttonname = "/ui/MainMenu/View/ModeLatLon"; break;
2949 default: buttonname = "/ui/MainMenu/View/ModeUTM";
2951 mode_button = gtk_ui_manager_get_widget ( vw->uim, buttonname );
2952 g_assert ( mode_button );
2957 * vik_window_get_pan_move:
2958 * @vw: some VikWindow
2960 * Retrieves @vw's pan_move.
2962 * Should be removed as soon as possible.
2964 * Returns: @vw's pan_move
2968 gboolean vik_window_get_pan_move ( VikWindow *vw )
2970 return vw->pan_move;
2974 * Remove the potentially auto added map
2976 static void remove_default_map_layer ( VikWindow *vw )
2978 if ( a_vik_get_add_default_map_layer () ) {
2979 VikAggregateLayer *top = vik_layers_panel_get_top_layer(vw->viking_vlp);
2980 if ( vik_aggregate_layer_count(top) == 1 ) {
2981 VikLayer *vl = vik_aggregate_layer_get_top_visible_layer_of_type (top, VIK_LAYER_MAPS);
2982 // Could try to compare name vs _("Default Map") but this might have i8n issues if not translated
2984 vik_aggregate_layer_delete_layer ( top, vl );
2989 static void on_activate_recent_item (GtkRecentChooser *chooser,
2994 filename = gtk_recent_chooser_get_current_uri (chooser);
2995 if (filename != NULL)
2997 GFile *file = g_file_new_for_uri ( filename );
2998 gchar *path = g_file_get_path ( file );
2999 g_object_unref ( file );
3000 if ( self->filename )
3002 GSList *filenames = NULL;
3003 filenames = g_slist_append ( filenames, path );
3004 g_signal_emit ( G_OBJECT(self), window_signals[VW_OPENWINDOW_SIGNAL], 0, filenames );
3005 // NB: GSList & contents are freed by main.open_window
3008 remove_default_map_layer ( self );
3009 vik_window_open_file ( self, path, TRUE );
3017 static void setup_recent_files (VikWindow *self)
3019 GtkRecentManager *manager;
3020 GtkRecentFilter *filter;
3021 GtkWidget *menu, *menu_item;
3023 filter = gtk_recent_filter_new ();
3024 /* gtk_recent_filter_add_application (filter, g_get_application_name()); */
3025 gtk_recent_filter_add_group(filter, "viking");
3027 manager = gtk_recent_manager_get_default ();
3028 menu = gtk_recent_chooser_menu_new_for_manager (manager);
3029 gtk_recent_chooser_set_sort_type (GTK_RECENT_CHOOSER (menu), GTK_RECENT_SORT_MRU);
3030 gtk_recent_chooser_add_filter (GTK_RECENT_CHOOSER (menu), filter);
3031 gtk_recent_chooser_set_limit (GTK_RECENT_CHOOSER (menu), a_vik_get_recent_number_files() );
3033 menu_item = gtk_ui_manager_get_widget (self->uim, "/ui/MainMenu/File/OpenRecentFile");
3034 gtk_menu_item_set_submenu (GTK_MENU_ITEM (menu_item), menu);
3036 g_signal_connect (G_OBJECT (menu), "item-activated",
3037 G_CALLBACK (on_activate_recent_item), (gpointer) self);
3043 static void update_recently_used_document (VikWindow *vw, const gchar *filename)
3045 /* Update Recently Used Document framework */
3046 GtkRecentManager *manager = gtk_recent_manager_get_default();
3047 GtkRecentData *recent_data = g_slice_new (GtkRecentData);
3048 gchar *groups[] = {"viking", NULL};
3049 GFile *file = g_file_new_for_commandline_arg(filename);
3050 gchar *uri = g_file_get_uri(file);
3051 gchar *basename = g_path_get_basename(filename);
3052 g_object_unref(file);
3055 recent_data->display_name = basename;
3056 recent_data->description = NULL;
3057 recent_data->mime_type = "text/x-gps-data";
3058 recent_data->app_name = (gchar *) g_get_application_name ();
3059 recent_data->app_exec = g_strjoin (" ", g_get_prgname (), "%f", NULL);
3060 recent_data->groups = groups;
3061 recent_data->is_private = FALSE;
3062 if (!gtk_recent_manager_add_full (manager, uri, recent_data))
3064 gchar *msg = g_strdup_printf (_("Unable to add '%s' to the list of recently used documents"), uri);
3065 vik_statusbar_set_message ( vw->viking_vs, VIK_STATUSBAR_INFO, msg );
3071 g_free (recent_data->app_exec);
3072 g_slice_free (GtkRecentData, recent_data);
3076 * Call this before doing things that may take a long time and otherwise not show any other feedback
3077 * such as loading and saving files
3079 void vik_window_set_busy_cursor ( VikWindow *vw )
3081 gdk_window_set_cursor ( gtk_widget_get_window(GTK_WIDGET(vw)), vw->busy_cursor );
3082 // Viewport has a separate cursor
3083 gdk_window_set_cursor ( gtk_widget_get_window(GTK_WIDGET(vw->viking_vvp)), vw->busy_cursor );
3084 // Ensure cursor updated before doing stuff
3085 while( gtk_events_pending() )
3086 gtk_main_iteration();
3089 void vik_window_clear_busy_cursor ( VikWindow *vw )
3091 gdk_window_set_cursor ( gtk_widget_get_window(GTK_WIDGET(vw)), NULL );
3092 // Restore viewport cursor
3093 gdk_window_set_cursor ( gtk_widget_get_window(GTK_WIDGET(vw->viking_vvp)), vw->viewport_cursor );
3096 void vik_window_open_file ( VikWindow *vw, const gchar *filename, gboolean change_filename )
3098 vik_window_set_busy_cursor ( vw );
3100 // Enable the *new* filename to be accessible by the Layers codez
3101 gchar *original_filename = g_strdup ( vw->filename );
3102 g_free ( vw->filename );
3103 vw->filename = g_strdup ( filename );
3104 gboolean success = FALSE;
3105 gboolean restore_original_filename = FALSE;
3106 vw->loaded_type = a_file_load ( vik_layers_panel_get_top_layer(vw->viking_vlp), vw->viking_vvp, vw->containing_vtl, filename );
3107 switch ( vw->loaded_type )
3109 case LOAD_TYPE_READ_FAILURE:
3110 a_dialog_error_msg ( GTK_WINDOW(vw), _("The file you requested could not be opened.") );
3112 case LOAD_TYPE_GPSBABEL_FAILURE:
3113 a_dialog_error_msg ( GTK_WINDOW(vw), _("GPSBabel is required to load files of this type or GPSBabel encountered problems.") );
3115 case LOAD_TYPE_GPX_FAILURE:
3116 a_dialog_error_msg_extra ( GTK_WINDOW(vw), _("Unable to load malformed GPX file %s"), filename );
3118 case LOAD_TYPE_UNSUPPORTED_FAILURE:
3119 a_dialog_error_msg_extra ( GTK_WINDOW(vw), _("Unsupported file type for %s"), filename );
3121 case LOAD_TYPE_VIK_FAILURE_NON_FATAL:
3123 // Since we can process .vik files with issues just show a warning in the status bar
3124 // Not that a user can do much about it... or tells them what this issue is yet...
3125 gchar *msg = g_strdup_printf (_("WARNING: issues encountered loading %s"), a_file_basename (filename) );
3126 vik_statusbar_set_message ( vw->viking_vs, VIK_STATUSBAR_INFO, msg );
3129 // No break, carry on to show any data
3130 case LOAD_TYPE_VIK_SUCCESS:
3132 restore_original_filename = TRUE; // NB Will actually get inverted by the 'success' component below
3133 GtkWidget *mode_button;
3135 if ( change_filename )
3136 window_set_filename ( vw, filename );
3137 mode_button = vik_window_get_drawmode_button ( vw, vik_viewport_get_drawmode ( vw->viking_vvp ) );
3138 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. */
3139 gtk_check_menu_item_set_active ( GTK_CHECK_MENU_ITEM(mode_button), TRUE );
3140 vw->only_updating_coord_mode_ui = FALSE;
3142 vik_layers_panel_change_coord_mode ( vw->viking_vlp, vik_viewport_get_coord_mode ( vw->viking_vvp ) );
3144 // Slightly long winded methods to align loaded viewport settings with the UI
3145 // Since the rewrite for toolbar + menu actions
3146 // there no longer exists a simple way to directly change the UI to a value for toggle settings
3147 // it only supports toggling the existing setting (otherwise get infinite loops in trying to align tb+menu elements)
3148 // Thus get state, compare them, if different then invert viewport setting and (re)sync the setting (via toggling)
3149 gboolean vp_state_scale = vik_viewport_get_draw_scale ( vw->viking_vvp );
3150 gboolean ui_state_scale = gtk_check_menu_item_get_active ( GTK_CHECK_MENU_ITEM(get_show_widget_by_name(vw, "ShowScale")) );
3151 if ( vp_state_scale != ui_state_scale ) {
3152 vik_viewport_set_draw_scale ( vw->viking_vvp, !vp_state_scale );
3153 toggle_draw_scale ( NULL, vw );
3155 gboolean vp_state_centermark = vik_viewport_get_draw_centermark ( vw->viking_vvp );
3156 gboolean ui_state_centermark = gtk_check_menu_item_get_active ( GTK_CHECK_MENU_ITEM(get_show_widget_by_name(vw, "ShowCenterMark")) );
3157 if ( vp_state_centermark != ui_state_centermark ) {
3158 vik_viewport_set_draw_centermark ( vw->viking_vvp, !vp_state_centermark );
3159 toggle_draw_centermark ( NULL, vw );
3161 gboolean vp_state_highlight = vik_viewport_get_draw_highlight ( vw->viking_vvp );
3162 gboolean ui_state_highlight = gtk_check_menu_item_get_active ( GTK_CHECK_MENU_ITEM(get_show_widget_by_name(vw, "ShowHighlight")) );
3163 if ( vp_state_highlight != ui_state_highlight ) {
3164 vik_viewport_set_draw_highlight ( vw->viking_vvp, !vp_state_highlight );
3165 toggle_draw_highlight ( NULL, vw );
3168 // NB No break, carry on to redraw
3169 //case LOAD_TYPE_OTHER_SUCCESS:
3172 // When LOAD_TYPE_OTHER_SUCCESS *only*, this will maintain the existing Viking project
3173 restore_original_filename = ! restore_original_filename;
3174 update_recently_used_document (vw, filename);
3179 if ( ! success || restore_original_filename )
3180 // Load didn't work or want to keep as the existing Viking project, keep using the original name
3181 window_set_filename ( vw, original_filename );
3182 g_free ( original_filename );
3184 vik_window_clear_busy_cursor ( vw );
3187 static void load_file ( GtkAction *a, VikWindow *vw )
3189 GSList *files = NULL;
3190 GSList *cur_file = NULL;
3192 if (!strcmp(gtk_action_get_name(a), "Open")) {
3195 else if (!strcmp(gtk_action_get_name(a), "Append")) {
3199 g_critical("Houston, we've had a problem.");
3203 GtkWidget *dialog = gtk_file_chooser_dialog_new (_("Please select a GPS data file to open. "),
3205 GTK_FILE_CHOOSER_ACTION_OPEN,
3206 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
3207 GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT,
3209 if ( last_folder_files_uri )
3210 gtk_file_chooser_set_current_folder_uri ( GTK_FILE_CHOOSER(dialog), last_folder_files_uri );
3212 GtkFileFilter *filter;
3213 // NB file filters are listed this way for alphabetical ordering
3214 #ifdef VIK_CONFIG_GEOCACHES
3215 filter = gtk_file_filter_new ();
3216 gtk_file_filter_set_name( filter, _("Geocaching") );
3217 gtk_file_filter_add_pattern ( filter, "*.loc" ); // No MIME type available
3218 gtk_file_chooser_add_filter (GTK_FILE_CHOOSER(dialog), filter);
3221 filter = gtk_file_filter_new ();
3222 gtk_file_filter_set_name( filter, _("Google Earth") );
3223 gtk_file_filter_add_mime_type ( filter, "application/vnd.google-earth.kml+xml");
3224 gtk_file_chooser_add_filter (GTK_FILE_CHOOSER(dialog), filter);
3226 filter = gtk_file_filter_new ();
3227 gtk_file_filter_set_name( filter, _("GPX") );
3228 gtk_file_filter_add_pattern ( filter, "*.gpx" ); // No MIME type available
3229 gtk_file_chooser_add_filter (GTK_FILE_CHOOSER(dialog), filter);
3231 filter = gtk_file_filter_new ();
3232 gtk_file_filter_set_name ( filter, _("JPG") );
3233 gtk_file_filter_add_mime_type ( filter, "image/jpeg");
3234 gtk_file_chooser_add_filter (GTK_FILE_CHOOSER(dialog), filter);
3236 filter = gtk_file_filter_new ();
3237 gtk_file_filter_set_name( filter, _("Viking") );
3238 gtk_file_filter_add_pattern ( filter, "*.vik" );
3239 gtk_file_filter_add_pattern ( filter, "*.viking" );
3240 gtk_file_chooser_add_filter (GTK_FILE_CHOOSER(dialog), filter);
3242 // NB could have filters for gpspoint (*.gps,*.gpsoint?) + gpsmapper (*.gsm,*.gpsmapper?)
3243 // However assume this are barely used and thus not worthy of inclusion
3244 // as they'll just make the options too many and have no clear file pattern
3245 // one can always use the all option
3246 filter = gtk_file_filter_new ();
3247 gtk_file_filter_set_name( filter, _("All") );
3248 gtk_file_filter_add_pattern ( filter, "*" );
3249 gtk_file_chooser_add_filter (GTK_FILE_CHOOSER(dialog), filter);
3250 // Default to any file - same as before open filters were added
3251 gtk_file_chooser_set_filter (GTK_FILE_CHOOSER(dialog), filter);
3253 gtk_file_chooser_set_select_multiple ( GTK_FILE_CHOOSER(dialog), TRUE );
3254 gtk_window_set_transient_for ( GTK_WINDOW(dialog), GTK_WINDOW(vw) );
3255 gtk_window_set_destroy_with_parent ( GTK_WINDOW(dialog), TRUE );
3257 if ( gtk_dialog_run ( GTK_DIALOG(dialog) ) == GTK_RESPONSE_ACCEPT )
3259 g_free ( last_folder_files_uri );
3260 last_folder_files_uri = gtk_file_chooser_get_current_folder_uri ( GTK_FILE_CHOOSER(dialog) );
3262 #ifdef VIKING_PROMPT_IF_MODIFIED
3263 if ( (vw->modified || vw->filename) && newwindow )
3265 if ( vw->filename && newwindow )
3267 g_signal_emit ( G_OBJECT(vw), window_signals[VW_OPENWINDOW_SIGNAL], 0, gtk_file_chooser_get_filenames (GTK_FILE_CHOOSER(dialog) ) );
3270 files = gtk_file_chooser_get_filenames (GTK_FILE_CHOOSER(dialog) );
3271 gboolean change_fn = newwindow && (g_slist_length(files)==1); /* only change fn if one file */
3272 gboolean first_vik_file = TRUE;
3274 while ( cur_file ) {
3276 gchar *file_name = cur_file->data;
3277 if ( newwindow && check_file_magic_vik ( file_name ) ) {
3278 // Load first of many .vik files in current window
3279 if ( first_vik_file ) {
3280 remove_default_map_layer ( vw );
3281 vik_window_open_file ( vw, file_name, TRUE );
3282 first_vik_file = FALSE;
3285 // Load each subsequent .vik file in a separate window
3286 VikWindow *newvw = vik_window_new_window ();
3288 vik_window_open_file ( newvw, file_name, TRUE );
3293 vik_window_open_file ( vw, file_name, change_fn );
3296 cur_file = g_slist_next (cur_file);
3298 g_slist_free (files);
3301 gtk_widget_destroy ( dialog );
3304 static gboolean save_file_as ( GtkAction *a, VikWindow *vw )
3306 gboolean rv = FALSE;
3309 GtkWidget *dialog = gtk_file_chooser_dialog_new (_("Save as Viking File."),
3311 GTK_FILE_CHOOSER_ACTION_SAVE,
3312 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
3313 GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT,
3315 if ( last_folder_files_uri )
3316 gtk_file_chooser_set_current_folder_uri ( GTK_FILE_CHOOSER(dialog), last_folder_files_uri );
3318 GtkFileFilter *filter;
3319 filter = gtk_file_filter_new ();
3320 gtk_file_filter_set_name( filter, _("All") );
3321 gtk_file_filter_add_pattern ( filter, "*" );
3322 gtk_file_chooser_add_filter (GTK_FILE_CHOOSER(dialog), filter);
3324 filter = gtk_file_filter_new ();
3325 gtk_file_filter_set_name( filter, _("Viking") );
3326 gtk_file_filter_add_pattern ( filter, "*.vik" );
3327 gtk_file_filter_add_pattern ( filter, "*.viking" );
3328 gtk_file_chooser_add_filter (GTK_FILE_CHOOSER(dialog), filter);
3329 // Default to a Viking file
3330 gtk_file_chooser_set_filter (GTK_FILE_CHOOSER(dialog), filter);
3332 gtk_window_set_transient_for ( GTK_WINDOW(dialog), GTK_WINDOW(vw) );
3333 gtk_window_set_destroy_with_parent ( GTK_WINDOW(dialog), TRUE );
3335 // Auto append / replace extension with '.vik' to the suggested file name as it's going to be a Viking File
3336 gchar* auto_save_name = g_strdup ( window_get_filename ( vw ) );
3337 if ( ! a_file_check_ext ( auto_save_name, ".vik" ) )
3338 auto_save_name = g_strconcat ( auto_save_name, ".vik", NULL );
3340 gtk_file_chooser_set_current_name (GTK_FILE_CHOOSER(dialog), auto_save_name);
3342 while ( gtk_dialog_run ( GTK_DIALOG(dialog) ) == GTK_RESPONSE_ACCEPT )
3344 fn = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER(dialog) );
3345 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 ) ) )
3347 window_set_filename ( vw, fn );
3348 rv = window_save ( vw );
3350 vw->modified = FALSE;
3351 g_free ( last_folder_files_uri );
3352 last_folder_files_uri = gtk_file_chooser_get_current_folder_uri ( GTK_FILE_CHOOSER(dialog) );
3357 g_free ( auto_save_name );
3358 gtk_widget_destroy ( dialog );
3362 static gboolean window_save ( VikWindow *vw )
3364 vik_window_set_busy_cursor ( vw );
3365 gboolean success = TRUE;
3367 if ( a_file_save ( vik_layers_panel_get_top_layer ( vw->viking_vlp ), vw->viking_vvp, vw->filename ) )
3369 update_recently_used_document ( vw, vw->filename );
3373 a_dialog_error_msg ( GTK_WINDOW(vw), _("The filename you requested could not be opened for writing.") );
3376 vik_window_clear_busy_cursor ( vw );
3380 static gboolean save_file ( GtkAction *a, VikWindow *vw )
3382 if ( ! vw->filename )
3383 return save_file_as ( NULL, vw );
3386 vw->modified = FALSE;
3387 return window_save ( vw );
3394 * Export all TRW Layers in the list to individual files in the specified directory
3396 * Returns: %TRUE on success
3398 static gboolean export_to ( VikWindow *vw, GList *gl, VikFileType_t vft, const gchar *dir, const gchar *extension )
3400 gboolean success = TRUE;
3402 gint export_count = 0;
3404 vik_window_set_busy_cursor ( vw );
3408 gchar *fn = g_strconcat ( dir, G_DIR_SEPARATOR_S, VIK_LAYER(gl->data)->name, extension, NULL );
3410 // Some protection in attempting to write too many same named files
3411 // As this will get horribly slow...
3412 gboolean safe = FALSE;
3414 while ( ii < 5000 ) {
3415 if ( g_file_test ( fn, G_FILE_TEST_EXISTS ) ) {
3418 fn = g_strdup_printf ( "%s%s%s#%03d%s", dir, G_DIR_SEPARATOR_S, VIK_LAYER(gl->data)->name, ii, extension );
3429 // NB: We allow exporting empty layers
3431 gboolean this_success = a_file_export ( VIK_TRW_LAYER(gl->data), fn, vft, NULL, TRUE );
3433 // Show some progress
3434 if ( this_success ) {
3436 gchar *message = g_strdup_printf ( _("Exporting to file: %s"), fn );
3437 vik_statusbar_set_message ( vw->viking_vs, VIK_STATUSBAR_INFO, message );
3438 while ( gtk_events_pending() )
3439 gtk_main_iteration ();
3443 success = success && this_success;
3447 gl = g_list_next ( gl );
3450 vik_window_clear_busy_cursor ( vw );
3452 // Confirm what happened.
3453 gchar *message = g_strdup_printf ( _("Exported files: %d"), export_count );
3454 vik_statusbar_set_message ( vw->viking_vs, VIK_STATUSBAR_INFO, message );
3460 static void export_to_common ( VikWindow *vw, VikFileType_t vft, const gchar *extension )
3462 GList *gl = vik_layers_panel_get_all_layers_of_type ( vw->viking_vlp, VIK_LAYER_TRW, TRUE );
3465 a_dialog_info_msg ( GTK_WINDOW(vw), _("Nothing to Export!") );
3469 GtkWidget *dialog = gtk_file_chooser_dialog_new ( _("Export to directory"),
3471 GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER,
3473 GTK_RESPONSE_REJECT,
3475 GTK_RESPONSE_ACCEPT,
3477 gtk_window_set_transient_for ( GTK_WINDOW(dialog), GTK_WINDOW(vw) );
3478 gtk_window_set_destroy_with_parent ( GTK_WINDOW(dialog), TRUE );
3479 gtk_window_set_modal ( GTK_WINDOW(dialog), TRUE );
3481 gtk_widget_show_all ( dialog );
3483 if ( gtk_dialog_run ( GTK_DIALOG(dialog) ) == GTK_RESPONSE_ACCEPT ) {
3484 gchar *dir = gtk_file_chooser_get_filename ( GTK_FILE_CHOOSER(dialog) );
3485 gtk_widget_destroy ( dialog );
3487 if ( !export_to ( vw, gl, vft, dir, extension ) )
3488 a_dialog_error_msg ( GTK_WINDOW(vw),_("Could not convert all files") );
3493 gtk_widget_destroy ( dialog );
3498 static void export_to_gpx ( GtkAction *a, VikWindow *vw )
3500 export_to_common ( vw, FILE_TYPE_GPX, ".gpx" );
3503 static void export_to_kml ( GtkAction *a, VikWindow *vw )
3505 export_to_common ( vw, FILE_TYPE_KML, ".kml" );
3508 static void file_properties_cb ( GtkAction *a, VikWindow *vw )
3510 gchar *message = NULL;
3511 if ( vw->filename ) {
3512 if ( g_file_test ( vw->filename, G_FILE_TEST_EXISTS ) ) {
3513 // Get some timestamp information of the file
3515 if ( g_stat ( vw->filename, &stat_buf ) == 0 ) {
3517 strftime ( time_buf, sizeof(time_buf), "%c", gmtime((const time_t *)&stat_buf.st_mtime) );
3519 gint byte_size = stat_buf.st_size;
3520 #if GLIB_CHECK_VERSION(2,30,0)
3521 size = g_format_size_full ( byte_size, G_FORMAT_SIZE_DEFAULT );
3523 size = g_format_size_for_display ( byte_size );
3525 message = g_strdup_printf ( "%s\n\n%s\n\n%s", vw->filename, time_buf, size );
3530 message = g_strdup ( _("File not accessible") );
3533 message = g_strdup ( _("No Viking File") );
3536 a_dialog_info_msg ( GTK_WINDOW(vw), message );
3540 static void my_acquire ( VikWindow *vw, VikDataSourceInterface *datasource )
3542 vik_datasource_mode_t mode = datasource->mode;
3543 if ( mode == VIK_DATASOURCE_AUTO_LAYER_MANAGEMENT )
3544 mode = VIK_DATASOURCE_CREATENEWLAYER;
3545 a_acquire ( vw, vw->viking_vlp, vw->viking_vvp, mode, datasource, NULL, NULL );
3548 static void acquire_from_gps ( GtkAction *a, VikWindow *vw )
3550 my_acquire ( vw, &vik_datasource_gps_interface );
3553 static void acquire_from_file ( GtkAction *a, VikWindow *vw )
3555 my_acquire ( vw, &vik_datasource_file_interface );
3558 static void acquire_from_geojson ( GtkAction *a, VikWindow *vw )
3560 my_acquire ( vw, &vik_datasource_geojson_interface );
3563 static void acquire_from_routing ( GtkAction *a, VikWindow *vw )
3565 my_acquire ( vw, &vik_datasource_routing_interface );
3568 #ifdef VIK_CONFIG_OPENSTREETMAP
3569 static void acquire_from_osm ( GtkAction *a, VikWindow *vw )
3571 my_acquire ( vw, &vik_datasource_osm_interface );
3574 static void acquire_from_my_osm ( GtkAction *a, VikWindow *vw )
3576 my_acquire ( vw, &vik_datasource_osm_my_traces_interface );
3580 #ifdef VIK_CONFIG_GEOCACHES
3581 static void acquire_from_gc ( GtkAction *a, VikWindow *vw )
3583 my_acquire ( vw, &vik_datasource_gc_interface );
3587 #ifdef VIK_CONFIG_GEOTAG
3588 static void acquire_from_geotag ( GtkAction *a, VikWindow *vw )
3590 my_acquire ( vw, &vik_datasource_geotag_interface );
3594 #ifdef VIK_CONFIG_GEONAMES
3595 static void acquire_from_wikipedia ( GtkAction *a, VikWindow *vw )
3597 my_acquire ( vw, &vik_datasource_wikipedia_interface );
3601 static void acquire_from_url ( GtkAction *a, VikWindow *vw )
3603 my_acquire ( vw, &vik_datasource_url_interface );
3606 static void goto_default_location( GtkAction *a, VikWindow *vw)
3609 ll.lat = a_vik_get_default_lat();
3610 ll.lon = a_vik_get_default_long();
3611 vik_viewport_set_center_latlon(vw->viking_vvp, &ll, TRUE);
3612 vik_layers_panel_emit_update(vw->viking_vlp);
3616 static void goto_address( GtkAction *a, VikWindow *vw)
3618 a_vik_goto ( vw, vw->viking_vvp );
3619 vik_layers_panel_emit_update ( vw->viking_vlp );
3622 static void mapcache_flush_cb ( GtkAction *a, VikWindow *vw )
3627 static void menu_copy_centre_cb ( GtkAction *a, VikWindow *vw )
3629 const VikCoord* coord;
3631 gchar *lat = NULL, *lon = NULL;
3633 coord = vik_viewport_get_center ( vw->viking_vvp );
3634 vik_coord_to_utm ( coord, &utm );
3636 gboolean full_format = FALSE;
3637 (void)a_settings_get_boolean ( VIK_SETTINGS_WIN_COPY_CENTRE_FULL_FORMAT, &full_format );
3640 // Bells & Whistles - may include degrees, minutes and second symbols
3641 get_location_strings ( vw, utm, &lat, &lon );
3643 // Simple x.xx y.yy format
3645 a_coords_utm_to_latlon ( &utm, &ll );
3646 lat = g_strdup_printf ( "%.6f", ll.lat );
3647 lon = g_strdup_printf ( "%.6f", ll.lon );
3650 gchar *msg = g_strdup_printf ( "%s %s", lat, lon );
3654 a_clipboard_copy ( VIK_CLIPBOARD_DATA_TEXT, 0, 0, 0, msg, NULL );
3659 static void layer_defaults_cb ( GtkAction *a, VikWindow *vw )
3661 gchar **texts = g_strsplit ( gtk_action_get_name(a), "Layer", 0 );
3664 return; // Internally broken :(
3666 if ( ! a_layer_defaults_show_window ( GTK_WINDOW(vw), texts[1] ) )
3667 a_dialog_info_msg ( GTK_WINDOW(vw), _("This layer has no configurable properties.") );
3668 // NB no update needed
3670 g_strfreev ( texts );
3673 static void preferences_change_update ( VikWindow *vw, gpointer data )
3675 // Want to update all TrackWaypoint layers
3676 GList *layers = vik_layers_panel_get_all_layers_of_type ( vw->viking_vlp, VIK_LAYER_TRW, TRUE );
3682 // Reset the individual waypoints themselves due to the preferences change
3683 VikTrwLayer *vtl = VIK_TRW_LAYER(layers->data);
3684 vik_trw_layer_reset_waypoints ( vtl );
3685 layers = g_list_next ( layers );
3688 g_list_free ( layers );
3693 static void preferences_cb ( GtkAction *a, VikWindow *vw )
3695 gboolean wp_icon_size = a_vik_get_use_large_waypoint_icons();
3697 a_preferences_show_window ( GTK_WINDOW(vw) );
3699 // Has the waypoint size setting changed?
3700 if (wp_icon_size != a_vik_get_use_large_waypoint_icons()) {
3701 // Delete icon indexing 'cache' and so automatically regenerates with the new setting when changed
3702 clear_garmin_icon_syms ();
3704 // Update all windows
3705 g_slist_foreach ( window_list, (GFunc) preferences_change_update, NULL );
3708 // Ensure TZ Lookup initialized
3709 if ( a_vik_get_time_ref_frame() == VIK_TIME_REF_WORLD )
3710 vu_setup_lat_lon_tz_lookup();
3712 toolbar_apply_settings ( vw->viking_vtb, vw->main_vbox, vw->menu_hbox, TRUE );
3715 static void default_location_cb ( GtkAction *a, VikWindow *vw )
3717 /* Simplistic repeat of preference setting
3718 Only the name & type are important for setting the preference via this 'external' way */
3719 VikLayerParam pref_lat[] = {
3720 { VIK_LAYER_NUM_TYPES,
3721 VIKING_PREFERENCES_NAMESPACE "default_latitude",
3722 VIK_LAYER_PARAM_DOUBLE,
3723 VIK_LAYER_GROUP_NONE,
3725 VIK_LAYER_WIDGET_SPINBUTTON,
3734 VikLayerParam pref_lon[] = {
3735 { VIK_LAYER_NUM_TYPES,
3736 VIKING_PREFERENCES_NAMESPACE "default_longitude",
3737 VIK_LAYER_PARAM_DOUBLE,
3738 VIK_LAYER_GROUP_NONE,
3740 VIK_LAYER_WIDGET_SPINBUTTON,
3750 /* Get current center */
3752 vik_coord_to_latlon ( vik_viewport_get_center ( vw->viking_vvp ), &ll );
3754 /* Apply to preferences */
3755 VikLayerParamData vlp_data;
3756 vlp_data.d = ll.lat;
3757 a_preferences_run_setparam (vlp_data, pref_lat);
3758 vlp_data.d = ll.lon;
3759 a_preferences_run_setparam (vlp_data, pref_lon);
3760 /* Remember to save */
3761 a_preferences_save_to_file();
3767 static void clear_cb ( GtkAction *a, VikWindow *vw )
3769 // Do nothing if empty
3770 VikAggregateLayer *top = vik_layers_panel_get_top_layer(vw->viking_vlp);
3771 if ( ! vik_aggregate_layer_is_empty(top) ) {
3772 if ( a_dialog_yes_or_no ( GTK_WINDOW(vw), _("Are you sure you wish to delete all layers?"), NULL ) ) {
3773 vik_layers_panel_clear ( vw->viking_vlp );
3774 window_set_filename ( vw, NULL );
3780 static void window_close ( GtkAction *a, VikWindow *vw )
3782 if ( ! delete_event ( vw ) )
3783 gtk_widget_destroy ( GTK_WIDGET(vw) );
3786 static gboolean save_file_and_exit ( GtkAction *a, VikWindow *vw )
3788 if (save_file( NULL, vw)) {
3789 window_close( NULL, vw);
3796 static void zoom_to_cb ( GtkAction *a, VikWindow *vw )
3798 gdouble xmpp = vik_viewport_get_xmpp ( vw->viking_vvp ), ympp = vik_viewport_get_ympp ( vw->viking_vvp );
3799 if ( a_dialog_custom_zoom ( GTK_WINDOW(vw), &xmpp, &ympp ) )
3801 vik_viewport_set_xmpp ( vw->viking_vvp, xmpp );
3802 vik_viewport_set_ympp ( vw->viking_vvp, ympp );
3807 static void save_image_file ( VikWindow *vw, const gchar *fn, guint w, guint h, gdouble zoom, gboolean save_as_png, gboolean save_kmz )
3809 /* more efficient way: stuff draws directly to pixbuf (fork viewport) */
3810 GdkPixbuf *pixbuf_to_save;
3811 gdouble old_xmpp, old_ympp;
3812 GError *error = NULL;
3814 GtkWidget *msgbox = gtk_message_dialog_new ( GTK_WINDOW(vw),
3815 GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
3818 _("Generating image file...") );
3820 g_signal_connect_swapped (msgbox, "response", G_CALLBACK (gtk_widget_destroy), msgbox);
3821 // Ensure dialog shown
3822 gtk_widget_show_all ( msgbox );
3824 vik_statusbar_set_message ( vw->viking_vs, VIK_STATUSBAR_INFO, _("Generating image file...") );
3825 while ( gtk_events_pending() )
3826 gtk_main_iteration ();
3827 // Despite many efforts & variations, GTK on my Linux system doesn't show the actual msgbox contents :(
3828 // At least the empty box can give a clue something's going on + the statusbar msg...
3829 // Windows version under Wine OK!
3831 /* backup old zoom & set new */
3832 old_xmpp = vik_viewport_get_xmpp ( vw->viking_vvp );
3833 old_ympp = vik_viewport_get_ympp ( vw->viking_vvp );
3834 vik_viewport_set_zoom ( vw->viking_vvp, zoom );
3836 /* reset width and height: */
3837 vik_viewport_configure_manually ( vw->viking_vvp, w, h );
3839 /* draw all layers */
3842 /* save buffer as file. */
3843 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);
3844 if ( !pixbuf_to_save ) {
3845 g_warning("Failed to generate internal pixmap size: %d x %d", w, h);
3846 gtk_message_dialog_set_markup ( GTK_MESSAGE_DIALOG(msgbox), _("Failed to generate internal image.\n\nTry creating a smaller image.") );
3850 int ans = 0; // Default to success
3853 gdouble north, east, south, west;
3854 vik_viewport_get_min_max_lat_lon ( vw->viking_vvp, &south, &north, &west, &east );
3855 ans = kmz_save_file ( pixbuf_to_save, fn, north, east, south, west );
3858 gdk_pixbuf_save ( pixbuf_to_save, fn, save_as_png ? "png" : "jpeg", &error, NULL );
3860 g_warning("Unable to write to file %s: %s", fn, error->message );
3861 g_error_free (error);
3867 gtk_message_dialog_set_markup ( GTK_MESSAGE_DIALOG(msgbox), _("Image file generated.") );
3869 gtk_message_dialog_set_markup ( GTK_MESSAGE_DIALOG(msgbox), _("Failed to generate image file.") );
3871 g_object_unref ( G_OBJECT(pixbuf_to_save) );
3874 vik_statusbar_set_message ( vw->viking_vs, VIK_STATUSBAR_INFO, "" );
3875 gtk_dialog_add_button ( GTK_DIALOG(msgbox), GTK_STOCK_OK, GTK_RESPONSE_OK );
3876 gtk_dialog_run ( GTK_DIALOG(msgbox) ); // Don't care about the result
3878 /* pretend like nothing happened ;) */
3879 vik_viewport_set_xmpp ( vw->viking_vvp, old_xmpp );
3880 vik_viewport_set_ympp ( vw->viking_vvp, old_ympp );
3881 vik_viewport_configure ( vw->viking_vvp );
3885 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 )
3887 gulong size = sizeof(gchar) * (strlen(fn) + 15);
3888 gchar *name_of_file = g_malloc ( size );
3890 struct UTM utm_orig, utm;
3892 /* *** copied from above *** */
3893 GdkPixbuf *pixbuf_to_save;
3894 gdouble old_xmpp, old_ympp;
3895 GError *error = NULL;
3897 /* backup old zoom & set new */
3898 old_xmpp = vik_viewport_get_xmpp ( vw->viking_vvp );
3899 old_ympp = vik_viewport_get_ympp ( vw->viking_vvp );
3900 vik_viewport_set_zoom ( vw->viking_vvp, zoom );
3902 /* reset width and height: do this only once for all images (same size) */
3903 vik_viewport_configure_manually ( vw->viking_vvp, w, h );
3904 /* *** end copy from above *** */
3906 g_assert ( vik_viewport_get_coord_mode ( vw->viking_vvp ) == VIK_COORD_UTM );
3908 if ( g_mkdir(fn,0777) != 0 )
3909 g_warning ( "%s: Failed to create directory %s", __FUNCTION__, fn );
3911 utm_orig = *((const struct UTM *)vik_viewport_get_center ( vw->viking_vvp ));
3913 for ( y = 1; y <= tiles_h; y++ )
3915 for ( x = 1; x <= tiles_w; x++ )
3917 g_snprintf ( name_of_file, size, "%s%cy%d-x%d.%s", fn, G_DIR_SEPARATOR, y, x, save_as_png ? "png" : "jpg" );
3919 if ( tiles_w & 0x1 )
3920 utm.easting += ((gdouble)x - ceil(((gdouble)tiles_w)/2)) * (w*zoom);
3922 utm.easting += ((gdouble)x - (((gdouble)tiles_w)+1)/2) * (w*zoom);
3923 if ( tiles_h & 0x1 ) /* odd */
3924 utm.northing -= ((gdouble)y - ceil(((gdouble)tiles_h)/2)) * (h*zoom);
3926 utm.northing -= ((gdouble)y - (((gdouble)tiles_h)+1)/2) * (h*zoom);
3928 /* move to correct place. */
3929 vik_viewport_set_center_utm ( vw->viking_vvp, &utm, FALSE );
3933 /* save buffer as file. */
3934 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);
3935 gdk_pixbuf_save ( pixbuf_to_save, name_of_file, save_as_png ? "png" : "jpeg", &error, NULL );
3938 gchar *msg = g_strdup_printf (_("Unable to write to file %s: %s"), name_of_file, error->message );
3939 vik_statusbar_set_message ( vw->viking_vs, VIK_STATUSBAR_INFO, msg );
3941 g_error_free (error);
3944 g_object_unref ( G_OBJECT(pixbuf_to_save) );
3948 vik_viewport_set_center_utm ( vw->viking_vvp, &utm_orig, FALSE );
3949 vik_viewport_set_xmpp ( vw->viking_vvp, old_xmpp );
3950 vik_viewport_set_ympp ( vw->viking_vvp, old_ympp );
3951 vik_viewport_configure ( vw->viking_vvp );
3954 g_free ( name_of_file );
3957 static void draw_to_image_file_current_window_cb(GtkWidget* widget,GdkEventButton *event,gpointer *pass_along)
3959 VikWindow *vw = VIK_WINDOW(pass_along[0]);
3960 GtkSpinButton *width_spin = GTK_SPIN_BUTTON(pass_along[1]), *height_spin = GTK_SPIN_BUTTON(pass_along[2]);
3962 gint active = gtk_combo_box_get_active ( GTK_COMBO_BOX(pass_along[3]) );
3963 gdouble zoom = pow (2, active-2 );
3965 gdouble width_min, width_max, height_min, height_max;
3968 gtk_spin_button_get_range ( width_spin, &width_min, &width_max );
3969 gtk_spin_button_get_range ( height_spin, &height_min, &height_max );
3971 /* TODO: support for xzoom and yzoom values */
3972 width = vik_viewport_get_width ( vw->viking_vvp ) * vik_viewport_get_xmpp ( vw->viking_vvp ) / zoom;
3973 height = vik_viewport_get_height ( vw->viking_vvp ) * vik_viewport_get_xmpp ( vw->viking_vvp ) / zoom;
3975 if ( width > width_max || width < width_min || height > height_max || height < height_min )
3976 a_dialog_info_msg ( GTK_WINDOW(vw), _("Viewable region outside allowable pixel size bounds for image. Clipping width/height values.") );
3978 gtk_spin_button_set_value ( width_spin, width );
3979 gtk_spin_button_set_value ( height_spin, height );
3982 static void draw_to_image_file_total_area_cb (GtkSpinButton *spinbutton, gpointer *pass_along)
3984 GtkSpinButton *width_spin = GTK_SPIN_BUTTON(pass_along[1]), *height_spin = GTK_SPIN_BUTTON(pass_along[2]);
3986 gint active = gtk_combo_box_get_active ( GTK_COMBO_BOX(pass_along[3]) );
3987 gdouble zoom = pow (2, active-2 );
3991 w = gtk_spin_button_get_value(width_spin) * zoom;
3992 h = gtk_spin_button_get_value(height_spin) * zoom;
3993 if (pass_along[4]) /* save many images; find TOTAL area covered */
3995 w *= gtk_spin_button_get_value(GTK_SPIN_BUTTON(pass_along[4]));
3996 h *= gtk_spin_button_get_value(GTK_SPIN_BUTTON(pass_along[5]));
3998 vik_units_distance_t dist_units = a_vik_get_units_distance ();
3999 switch (dist_units) {
4000 case VIK_UNITS_DISTANCE_KILOMETRES:
4001 label_text = g_strdup_printf ( _("Total area: %ldm x %ldm (%.3f sq. km)"), (glong)w, (glong)h, (w*h/1000000));
4003 case VIK_UNITS_DISTANCE_MILES:
4004 label_text = g_strdup_printf ( _("Total area: %ldm x %ldm (%.3f sq. miles)"), (glong)w, (glong)h, (w*h/2589988.11));
4006 case VIK_UNITS_DISTANCE_NAUTICAL_MILES:
4007 label_text = g_strdup_printf ( _("Total area: %ldm x %ldm (%.3f sq. NM)"), (glong)w, (glong)h, (w*h/(1852.0*1852.0)));
4010 label_text = g_strdup_printf ("Just to keep the compiler happy");
4011 g_critical("Houston, we've had a problem. distance=%d", dist_units);
4014 gtk_label_set_text(GTK_LABEL(pass_along[6]), label_text);
4015 g_free ( label_text );
4019 VW_GEN_SINGLE_IMAGE,
4020 VW_GEN_DIRECTORY_OF_IMAGES,
4025 * Get an allocated filename (or directory as specified)
4027 static gchar* draw_image_filename ( VikWindow *vw, img_generation_t img_gen )
4030 if ( img_gen != VW_GEN_DIRECTORY_OF_IMAGES )
4033 GtkWidget *dialog = gtk_file_chooser_dialog_new (_("Save Image"),
4035 GTK_FILE_CHOOSER_ACTION_SAVE,
4036 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
4037 GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT,
4039 if ( last_folder_images_uri )
4040 gtk_file_chooser_set_current_folder_uri ( GTK_FILE_CHOOSER(dialog), last_folder_images_uri );
4042 GtkFileChooser *chooser = GTK_FILE_CHOOSER ( dialog );
4044 GtkFileFilter *filter;
4045 filter = gtk_file_filter_new ();
4046 gtk_file_filter_set_name ( filter, _("All") );
4047 gtk_file_filter_add_pattern ( filter, "*" );
4048 gtk_file_chooser_add_filter ( chooser, filter );
4050 if ( img_gen == VW_GEN_KMZ_FILE ) {
4051 filter = gtk_file_filter_new ();
4052 gtk_file_filter_set_name ( filter, _("KMZ") );
4053 gtk_file_filter_add_mime_type ( filter, "vnd.google-earth.kmz");
4054 gtk_file_filter_add_pattern ( filter, "*.kmz" );
4055 gtk_file_chooser_add_filter ( chooser, filter );
4056 gtk_file_chooser_set_filter ( chooser, filter );
4059 filter = gtk_file_filter_new ();
4060 gtk_file_filter_set_name ( filter, _("JPG") );
4061 gtk_file_filter_add_mime_type ( filter, "image/jpeg");
4062 gtk_file_chooser_add_filter ( chooser, filter );
4064 if ( !vw->draw_image_save_as_png )
4065 gtk_file_chooser_set_filter ( chooser, filter );
4067 filter = gtk_file_filter_new ();
4068 gtk_file_filter_set_name ( filter, _("PNG") );
4069 gtk_file_filter_add_mime_type ( filter, "image/png");
4070 gtk_file_chooser_add_filter ( chooser, filter );
4072 if ( vw->draw_image_save_as_png )
4073 gtk_file_chooser_set_filter ( chooser, filter );
4076 gtk_window_set_transient_for ( GTK_WINDOW(dialog), GTK_WINDOW(vw) );
4077 gtk_window_set_destroy_with_parent ( GTK_WINDOW(dialog), TRUE );
4079 if ( gtk_dialog_run ( GTK_DIALOG(dialog) ) == GTK_RESPONSE_ACCEPT ) {
4080 g_free ( last_folder_images_uri );
4081 last_folder_images_uri = gtk_file_chooser_get_current_folder_uri ( GTK_FILE_CHOOSER(dialog) );
4083 fn = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER(dialog) );
4084 if ( g_file_test ( fn, G_FILE_TEST_EXISTS ) )
4085 if ( ! a_dialog_yes_or_no ( GTK_WINDOW(dialog), _("The file \"%s\" exists, do you wish to overwrite it?"), a_file_basename ( fn ) ) )
4088 gtk_widget_destroy ( dialog );
4092 // For some reason this method is only written to work in UTM...
4093 if ( vik_viewport_get_coord_mode(vw->viking_vvp) != VIK_COORD_UTM ) {
4094 a_dialog_error_msg ( GTK_WINDOW(vw), _("You must be in UTM mode to use this feature") );
4098 GtkWidget *dialog = gtk_file_chooser_dialog_new (_("Choose a directory to hold images"),
4100 GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER,
4101 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
4102 GTK_STOCK_OK, GTK_RESPONSE_ACCEPT,
4104 gtk_window_set_transient_for ( GTK_WINDOW(dialog), GTK_WINDOW(vw) );
4105 gtk_window_set_destroy_with_parent ( GTK_WINDOW(dialog), TRUE );
4107 if ( gtk_dialog_run ( GTK_DIALOG(dialog) ) == GTK_RESPONSE_ACCEPT ) {
4108 fn = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER(dialog) );
4110 gtk_widget_destroy ( dialog );
4115 static void draw_to_image_file ( VikWindow *vw, img_generation_t img_gen )
4117 /* todo: default for answers inside VikWindow or static (thruout instance) */
4118 GtkWidget *dialog = gtk_dialog_new_with_buttons ( _("Save to Image File"), GTK_WINDOW(vw),
4119 GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
4121 GTK_RESPONSE_REJECT,
4123 GTK_RESPONSE_ACCEPT,
4125 GtkWidget *width_label, *width_spin, *height_label, *height_spin;
4126 GtkWidget *current_window_button;
4127 gpointer current_window_pass_along[7];
4128 GtkWidget *zoom_label, *zoom_combo;
4129 GtkWidget *total_size_label;
4131 // only used for VW_GEN_DIRECTORY_OF_IMAGES
4132 GtkWidget *tiles_width_spin = NULL, *tiles_height_spin = NULL;
4134 width_label = gtk_label_new ( _("Width (pixels):") );
4135 width_spin = gtk_spin_button_new ( GTK_ADJUSTMENT(gtk_adjustment_new ( vw->draw_image_width, 10, 50000, 10, 100, 0 )), 10, 0 );
4136 height_label = gtk_label_new ( _("Height (pixels):") );
4137 height_spin = gtk_spin_button_new ( GTK_ADJUSTMENT(gtk_adjustment_new ( vw->draw_image_height, 10, 50000, 10, 100, 0 )), 10, 0 );
4139 GtkWidget *win_warning_label = gtk_label_new ( _("WARNING: USING LARGE IMAGES OVER 10000x10000\nMAY CRASH THE PROGRAM!") );
4141 zoom_label = gtk_label_new ( _("Zoom (meters per pixel):") );
4142 /* TODO: separate xzoom and yzoom factors */
4143 zoom_combo = create_zoom_combo_all_levels();
4145 gdouble mpp = vik_viewport_get_xmpp(vw->viking_vvp);
4146 gint active = 2 + round ( log (mpp) / log (2) );
4148 // Can we not hard code size here?
4153 gtk_combo_box_set_active ( GTK_COMBO_BOX(zoom_combo), active );
4155 total_size_label = gtk_label_new ( NULL );
4157 current_window_button = gtk_button_new_with_label ( _("Area in current viewable window") );
4158 current_window_pass_along [0] = vw;
4159 current_window_pass_along [1] = width_spin;
4160 current_window_pass_along [2] = height_spin;
4161 current_window_pass_along [3] = zoom_combo;
4162 current_window_pass_along [4] = NULL; // Only for directory of tiles: width
4163 current_window_pass_along [5] = NULL; // Only for directory of tiles: height
4164 current_window_pass_along [6] = total_size_label;
4165 g_signal_connect ( G_OBJECT(current_window_button), "button_press_event", G_CALLBACK(draw_to_image_file_current_window_cb), current_window_pass_along );
4167 GtkWidget *png_radio = gtk_radio_button_new_with_label ( NULL, _("Save as PNG") );
4168 GtkWidget *jpeg_radio = gtk_radio_button_new_with_label_from_widget ( GTK_RADIO_BUTTON(png_radio), _("Save as JPEG") );
4170 if ( img_gen == VW_GEN_KMZ_FILE ) {
4171 // Don't show image type selection if creating a KMZ (always JPG internally)
4172 // Start with viewable area by default
4173 draw_to_image_file_current_window_cb ( current_window_button, NULL, current_window_pass_along );
4175 gtk_box_pack_start (GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), jpeg_radio, FALSE, FALSE, 0);
4176 gtk_box_pack_start (GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), png_radio, FALSE, FALSE, 0);
4179 if ( ! vw->draw_image_save_as_png )
4180 gtk_toggle_button_set_active ( GTK_TOGGLE_BUTTON(jpeg_radio), TRUE );
4182 gtk_box_pack_start (GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), width_label, FALSE, FALSE, 0);
4183 gtk_box_pack_start (GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), width_spin, FALSE, FALSE, 0);
4184 gtk_box_pack_start (GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), height_label, FALSE, FALSE, 0);
4185 gtk_box_pack_start (GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), height_spin, FALSE, FALSE, 0);
4187 gtk_box_pack_start (GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), win_warning_label, FALSE, FALSE, 0);
4189 gtk_box_pack_start (GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), current_window_button, FALSE, FALSE, 0);
4190 gtk_box_pack_start (GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), zoom_label, FALSE, FALSE, 0);
4191 gtk_box_pack_start (GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), zoom_combo, FALSE, FALSE, 0);
4193 if ( img_gen == VW_GEN_DIRECTORY_OF_IMAGES )
4195 GtkWidget *tiles_width_label, *tiles_height_label;
4197 tiles_width_label = gtk_label_new ( _("East-west image tiles:") );
4198 tiles_width_spin = gtk_spin_button_new ( GTK_ADJUSTMENT(gtk_adjustment_new ( 5, 1, 10, 1, 100, 0 )), 1, 0 );
4199 tiles_height_label = gtk_label_new ( _("North-south image tiles:") );
4200 tiles_height_spin = gtk_spin_button_new ( GTK_ADJUSTMENT(gtk_adjustment_new ( 5, 1, 10, 1, 100, 0 )), 1, 0 );
4201 gtk_box_pack_start (GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), tiles_width_label, FALSE, FALSE, 0);
4202 gtk_box_pack_start (GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), tiles_width_spin, FALSE, FALSE, 0);
4203 gtk_box_pack_start (GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), tiles_height_label, FALSE, FALSE, 0);
4204 gtk_box_pack_start (GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), tiles_height_spin, FALSE, FALSE, 0);
4206 current_window_pass_along [4] = tiles_width_spin;
4207 current_window_pass_along [5] = tiles_height_spin;
4208 g_signal_connect ( G_OBJECT(tiles_width_spin), "value-changed", G_CALLBACK(draw_to_image_file_total_area_cb), current_window_pass_along );
4209 g_signal_connect ( G_OBJECT(tiles_height_spin), "value-changed", G_CALLBACK(draw_to_image_file_total_area_cb), current_window_pass_along );
4211 gtk_box_pack_start (GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), total_size_label, FALSE, FALSE, 0);
4212 g_signal_connect ( G_OBJECT(width_spin), "value-changed", G_CALLBACK(draw_to_image_file_total_area_cb), current_window_pass_along );
4213 g_signal_connect ( G_OBJECT(height_spin), "value-changed", G_CALLBACK(draw_to_image_file_total_area_cb), current_window_pass_along );
4214 g_signal_connect ( G_OBJECT(zoom_combo), "changed", G_CALLBACK(draw_to_image_file_total_area_cb), current_window_pass_along );
4216 draw_to_image_file_total_area_cb ( NULL, current_window_pass_along ); /* set correct size info now */
4218 gtk_dialog_set_default_response ( GTK_DIALOG(dialog), GTK_RESPONSE_ACCEPT );
4220 gtk_widget_show_all ( gtk_dialog_get_content_area(GTK_DIALOG(dialog)) );
4222 if ( gtk_dialog_run ( GTK_DIALOG(dialog) ) == GTK_RESPONSE_ACCEPT )
4224 gtk_widget_hide ( GTK_WIDGET(dialog) );
4226 gchar *fn = draw_image_filename ( vw, img_gen );
4230 gint active_z = gtk_combo_box_get_active ( GTK_COMBO_BOX(zoom_combo) );
4231 gdouble zoom = pow (2, active_z-2 );
4233 if ( img_gen == VW_GEN_SINGLE_IMAGE )
4234 save_image_file ( vw, fn,
4235 vw->draw_image_width = gtk_spin_button_get_value_as_int ( GTK_SPIN_BUTTON(width_spin) ),
4236 vw->draw_image_height = gtk_spin_button_get_value_as_int ( GTK_SPIN_BUTTON(height_spin) ),
4238 vw->draw_image_save_as_png = gtk_toggle_button_get_active ( GTK_TOGGLE_BUTTON(png_radio) ),
4240 else if ( img_gen == VW_GEN_KMZ_FILE ) {
4241 // Remove some viewport overlays as these aren't useful in KMZ file.
4242 gboolean restore_xhair = vik_viewport_get_draw_centermark ( vw->viking_vvp );
4243 if ( restore_xhair )
4244 vik_viewport_set_draw_centermark ( vw->viking_vvp, FALSE );
4245 gboolean restore_scale = vik_viewport_get_draw_scale ( vw->viking_vvp );
4246 if ( restore_scale )
4247 vik_viewport_set_draw_scale ( vw->viking_vvp, FALSE );
4249 save_image_file ( vw,
4251 gtk_spin_button_get_value_as_int ( GTK_SPIN_BUTTON(width_spin) ),
4252 gtk_spin_button_get_value_as_int ( GTK_SPIN_BUTTON(height_spin) ),
4257 if ( restore_xhair )
4258 vik_viewport_set_draw_centermark ( vw->viking_vvp, TRUE );
4259 if ( restore_scale )
4260 vik_viewport_set_draw_scale ( vw->viking_vvp, TRUE );
4261 if ( restore_xhair || restore_scale )
4265 // NB is in UTM mode ATM
4266 save_image_dir ( vw, fn,
4267 vw->draw_image_width = gtk_spin_button_get_value_as_int ( GTK_SPIN_BUTTON(width_spin) ),
4268 vw->draw_image_height = gtk_spin_button_get_value_as_int ( GTK_SPIN_BUTTON(height_spin) ),
4270 vw->draw_image_save_as_png = gtk_toggle_button_get_active ( GTK_TOGGLE_BUTTON(png_radio) ),
4271 gtk_spin_button_get_value ( GTK_SPIN_BUTTON(tiles_width_spin) ),
4272 gtk_spin_button_get_value ( GTK_SPIN_BUTTON(tiles_height_spin) ) );
4277 gtk_widget_destroy ( GTK_WIDGET(dialog) );
4280 static void draw_to_kmz_file_cb ( GtkAction *a, VikWindow *vw )
4282 if ( vik_viewport_get_coord_mode(vw->viking_vvp) == VIK_COORD_UTM ) {
4283 a_dialog_error_msg ( GTK_WINDOW(vw), _("This feature is not available in UTM mode") );
4286 // 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]
4287 // (it does *not* include a full KML dump of every track, waypoint etc...)
4288 draw_to_image_file ( vw, VW_GEN_KMZ_FILE );
4291 static void draw_to_image_file_cb ( GtkAction *a, VikWindow *vw )
4293 draw_to_image_file ( vw, VW_GEN_SINGLE_IMAGE );
4296 static void draw_to_image_dir_cb ( GtkAction *a, VikWindow *vw )
4298 draw_to_image_file ( vw, VW_GEN_DIRECTORY_OF_IMAGES );
4304 static void import_kmz_file_cb ( GtkAction *a, VikWindow *vw )
4306 GtkWidget *dialog = gtk_file_chooser_dialog_new (_("Open File"),
4308 GTK_FILE_CHOOSER_ACTION_OPEN,
4309 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
4310 GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT,
4313 GtkFileFilter *filter;
4314 filter = gtk_file_filter_new ();
4315 gtk_file_filter_set_name ( filter, _("KMZ") );
4316 gtk_file_filter_add_mime_type ( filter, "vnd.google-earth.kmz");
4317 gtk_file_filter_add_pattern ( filter, "*.kmz" );
4318 gtk_file_chooser_add_filter ( GTK_FILE_CHOOSER(dialog), filter );
4319 gtk_file_chooser_set_filter ( GTK_FILE_CHOOSER(dialog), filter );
4321 filter = gtk_file_filter_new ();
4322 gtk_file_filter_set_name( filter, _("All") );
4323 gtk_file_filter_add_pattern ( filter, "*" );
4324 gtk_file_chooser_add_filter ( GTK_FILE_CHOOSER(dialog), filter );
4325 // Default to any file - same as before open filters were added
4326 gtk_file_chooser_set_filter ( GTK_FILE_CHOOSER(dialog), filter );
4328 if ( gtk_dialog_run ( GTK_DIALOG(dialog) ) == GTK_RESPONSE_ACCEPT ) {
4329 gchar *fn = gtk_file_chooser_get_filename ( GTK_FILE_CHOOSER(dialog) );
4330 // TODO convert ans value into readable explaination of failure...
4331 int ans = kmz_open_file ( fn, vw->viking_vvp, vw->viking_vlp );
4333 a_dialog_error_msg_extra ( GTK_WINDOW(vw), _("Unable to import %s."), fn );
4337 gtk_widget_destroy ( dialog );
4340 static void print_cb ( GtkAction *a, VikWindow *vw )
4342 a_print(vw, vw->viking_vvp);
4345 /* really a misnomer: changes coord mode (actual coordinates) AND/OR draw mode (viewport only) */
4346 static void window_change_coord_mode_cb ( GtkAction *old_a, GtkAction *a, VikWindow *vw )
4348 const gchar *name = gtk_action_get_name(a);
4349 GtkToggleToolButton *tbutton = (GtkToggleToolButton *)toolbar_get_widget_by_name ( vw->viking_vtb, name );
4351 gtk_toggle_tool_button_set_active ( tbutton, TRUE );
4353 VikViewportDrawMode drawmode;
4354 if (!g_strcmp0(name, "ModeUTM")) {
4355 drawmode = VIK_VIEWPORT_DRAWMODE_UTM;
4357 else if (!g_strcmp0(name, "ModeLatLon")) {
4358 drawmode = VIK_VIEWPORT_DRAWMODE_LATLON;
4360 else if (!g_strcmp0(name, "ModeExpedia")) {
4361 drawmode = VIK_VIEWPORT_DRAWMODE_EXPEDIA;
4363 else if (!g_strcmp0(name, "ModeMercator")) {
4364 drawmode = VIK_VIEWPORT_DRAWMODE_MERCATOR;
4367 g_critical("Houston, we've had a problem.");
4371 if ( !vw->only_updating_coord_mode_ui )
4373 VikViewportDrawMode olddrawmode = vik_viewport_get_drawmode ( vw->viking_vvp );
4374 if ( olddrawmode != drawmode )
4376 /* this takes care of coord mode too */
4377 vik_viewport_set_drawmode ( vw->viking_vvp, drawmode );
4378 if ( drawmode == VIK_VIEWPORT_DRAWMODE_UTM ) {
4379 vik_layers_panel_change_coord_mode ( vw->viking_vlp, VIK_COORD_UTM );
4380 } else if ( olddrawmode == VIK_VIEWPORT_DRAWMODE_UTM ) {
4381 vik_layers_panel_change_coord_mode ( vw->viking_vlp, VIK_COORD_LATLON );
4388 static void toggle_draw_scale ( GtkAction *a, VikWindow *vw )
4390 gboolean state = !vik_viewport_get_draw_scale ( vw->viking_vvp );
4391 GtkWidget *check_box = gtk_ui_manager_get_widget ( vw->uim, "/ui/MainMenu/View/SetShow/ShowScale" );
4394 gtk_check_menu_item_set_active ( GTK_CHECK_MENU_ITEM(check_box), state );
4395 vik_viewport_set_draw_scale ( vw->viking_vvp, state );
4399 static void toggle_draw_centermark ( GtkAction *a, VikWindow *vw )
4401 gboolean state = !vik_viewport_get_draw_centermark ( vw->viking_vvp );
4402 GtkWidget *check_box = gtk_ui_manager_get_widget ( vw->uim, "/ui/MainMenu/View/SetShow/ShowCenterMark" );
4405 gtk_check_menu_item_set_active ( GTK_CHECK_MENU_ITEM(check_box), state );
4406 vik_viewport_set_draw_centermark ( vw->viking_vvp, state );
4410 static void toggle_draw_highlight ( GtkAction *a, VikWindow *vw )
4412 gboolean state = !vik_viewport_get_draw_highlight ( vw->viking_vvp );
4413 GtkWidget *check_box = gtk_ui_manager_get_widget ( vw->uim, "/ui/MainMenu/View/SetShow/ShowHighlight" );
4416 gtk_check_menu_item_set_active ( GTK_CHECK_MENU_ITEM(check_box), state );
4417 vik_viewport_set_draw_highlight ( vw->viking_vvp, state );
4421 static void set_bg_color ( GtkAction *a, VikWindow *vw )
4423 GtkWidget *colorsd = gtk_color_selection_dialog_new ( _("Choose a background color") );
4424 GdkColor *color = vik_viewport_get_background_gdkcolor ( vw->viking_vvp );
4425 gtk_color_selection_set_previous_color ( GTK_COLOR_SELECTION(gtk_color_selection_dialog_get_color_selection(GTK_COLOR_SELECTION_DIALOG(colorsd))), color );
4426 gtk_color_selection_set_current_color ( GTK_COLOR_SELECTION(gtk_color_selection_dialog_get_color_selection(GTK_COLOR_SELECTION_DIALOG(colorsd))), color );
4427 if ( gtk_dialog_run ( GTK_DIALOG(colorsd) ) == GTK_RESPONSE_OK )
4429 gtk_color_selection_get_current_color ( GTK_COLOR_SELECTION(gtk_color_selection_dialog_get_color_selection(GTK_COLOR_SELECTION_DIALOG(colorsd))), color );
4430 vik_viewport_set_background_gdkcolor ( vw->viking_vvp, color );
4434 gtk_widget_destroy ( colorsd );
4437 static void set_highlight_color ( GtkAction *a, VikWindow *vw )
4439 GtkWidget *colorsd = gtk_color_selection_dialog_new ( _("Choose a track highlight color") );
4440 GdkColor *color = vik_viewport_get_highlight_gdkcolor ( vw->viking_vvp );
4441 gtk_color_selection_set_previous_color ( GTK_COLOR_SELECTION(gtk_color_selection_dialog_get_color_selection(GTK_COLOR_SELECTION_DIALOG(colorsd))), color );
4442 gtk_color_selection_set_current_color ( GTK_COLOR_SELECTION(gtk_color_selection_dialog_get_color_selection(GTK_COLOR_SELECTION_DIALOG(colorsd))), color );
4443 if ( gtk_dialog_run ( GTK_DIALOG(colorsd) ) == GTK_RESPONSE_OK )
4445 gtk_color_selection_get_current_color ( GTK_COLOR_SELECTION(gtk_color_selection_dialog_get_color_selection(GTK_COLOR_SELECTION_DIALOG(colorsd))), color );
4446 vik_viewport_set_highlight_gdkcolor ( vw->viking_vvp, color );
4450 gtk_widget_destroy ( colorsd );
4454 /***********************************************************************************************
4456 ***********************************************************************************************/
4458 static GtkActionEntry entries[] = {
4459 { "File", NULL, N_("_File"), 0, 0, 0 },
4460 { "Edit", NULL, N_("_Edit"), 0, 0, 0 },
4461 { "View", NULL, N_("_View"), 0, 0, 0 },
4462 { "SetShow", NULL, N_("_Show"), 0, 0, 0 },
4463 { "SetZoom", NULL, N_("_Zoom"), 0, 0, 0 },
4464 { "SetPan", NULL, N_("_Pan"), 0, 0, 0 },
4465 { "Layers", NULL, N_("_Layers"), 0, 0, 0 },
4466 { "Tools", NULL, N_("_Tools"), 0, 0, 0 },
4467 { "Exttools", NULL, N_("_Webtools"), 0, 0, 0 },
4468 { "Help", NULL, N_("_Help"), 0, 0, 0 },
4470 { "New", GTK_STOCK_NEW, N_("_New"), "<control>N", N_("New file"), (GCallback)newwindow_cb },
4471 { "Open", GTK_STOCK_OPEN, N_("_Open..."), "<control>O", N_("Open a file"), (GCallback)load_file },
4472 { "OpenRecentFile", NULL, N_("Open _Recent File"), NULL, NULL, (GCallback)NULL },
4473 { "Append", GTK_STOCK_ADD, N_("Append _File..."), NULL, N_("Append data from a different file"), (GCallback)load_file },
4474 { "Export", GTK_STOCK_CONVERT, N_("_Export All"), NULL, N_("Export All TrackWaypoint Layers"), (GCallback)NULL },
4475 { "ExportGPX", NULL, N_("_GPX..."), NULL, N_("Export as GPX"), (GCallback)export_to_gpx },
4476 { "Acquire", GTK_STOCK_GO_DOWN, N_("A_cquire"), NULL, NULL, (GCallback)NULL },
4477 { "AcquireRouting", NULL, N_("_Directions..."), NULL, N_("Get driving directions"), (GCallback)acquire_from_routing },
4478 #ifdef VIK_CONFIG_OPENSTREETMAP
4479 { "AcquireOSM", NULL, N_("_OSM Traces..."), NULL, N_("Get traces from OpenStreetMap"), (GCallback)acquire_from_osm },
4480 { "AcquireMyOSM", NULL, N_("_My OSM Traces..."), NULL, N_("Get Your Own Traces from OpenStreetMap"), (GCallback)acquire_from_my_osm },
4482 #ifdef VIK_CONFIG_GEOCACHES
4483 { "AcquireGC", NULL, N_("Geo_caches..."), NULL, N_("Get Geocaches from geocaching.com"), (GCallback)acquire_from_gc },
4485 #ifdef VIK_CONFIG_GEOTAG
4486 { "AcquireGeotag", NULL, N_("From Geotagged _Images..."), NULL, N_("Create waypoints from geotagged images"), (GCallback)acquire_from_geotag },
4488 { "AcquireURL", NULL, N_("From _URL..."), NULL, N_("Get a file from a URL"), (GCallback)acquire_from_url },
4489 #ifdef VIK_CONFIG_GEONAMES
4490 { "AcquireWikipedia", NULL, N_("From _Wikipedia Waypoints"), NULL, N_("Create waypoints from Wikipedia items in the current view"), (GCallback)acquire_from_wikipedia },
4492 { "Save", GTK_STOCK_SAVE, N_("_Save"), "<control>S", N_("Save the file"), (GCallback)save_file },
4493 { "SaveAs", GTK_STOCK_SAVE_AS, N_("Save _As..."), NULL, N_("Save the file under different name"), (GCallback)save_file_as },
4494 { "FileProperties", NULL, N_("Properties..."), NULL, N_("File Properties"), (GCallback)file_properties_cb },
4496 { "ImportKMZ", GTK_STOCK_CONVERT, N_("Import KMZ _Map File..."), NULL, N_("Import a KMZ file"), (GCallback)import_kmz_file_cb },
4497 { "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 },
4499 { "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 },
4500 { "GenImgDir", GTK_STOCK_DND_MULTIPLE, N_("Generate _Directory of Images..."), NULL, N_("Generate _Directory of Images"), (GCallback)draw_to_image_dir_cb },
4501 { "Print", GTK_STOCK_PRINT, N_("_Print..."), NULL, N_("Print maps"), (GCallback)print_cb },
4502 { "Exit", GTK_STOCK_QUIT, N_("E_xit"), "<control>W", N_("Exit the program"), (GCallback)window_close },
4503 { "SaveExit", GTK_STOCK_QUIT, N_("Save and Exit"), NULL, N_("Save and Exit the program"), (GCallback)save_file_and_exit },
4505 { "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 },
4506 { "GoForward", GTK_STOCK_GO_FORWARD, N_("Go to the _Next Location"), NULL, N_("Go to the next location"), (GCallback)draw_goto_back_and_forth },
4507 { "GotoDefaultLocation", GTK_STOCK_HOME, N_("Go to the _Default Location"), NULL, N_("Go to the default location"), (GCallback)goto_default_location },
4508 { "GotoSearch", GTK_STOCK_JUMP_TO, N_("Go to _Location..."), NULL, N_("Go to address/place using text search"), (GCallback)goto_address },
4509 { "GotoLL", GTK_STOCK_JUMP_TO, N_("_Go to Lat/Lon..."), NULL, N_("Go to arbitrary lat/lon coordinate"), (GCallback)draw_goto_cb },
4510 { "GotoUTM", GTK_STOCK_JUMP_TO, N_("Go to UTM..."), NULL, N_("Go to arbitrary UTM coordinate"), (GCallback)draw_goto_cb },
4511 { "Refresh", GTK_STOCK_REFRESH, N_("_Refresh"), "F5", N_("Refresh any maps displayed"), (GCallback)draw_refresh_cb },
4512 { "SetHLColor",GTK_STOCK_SELECT_COLOR, N_("Set _Highlight Color..."), NULL, N_("Set Highlight Color"), (GCallback)set_highlight_color },
4513 { "SetBGColor",GTK_STOCK_SELECT_COLOR, N_("Set Bac_kground Color..."), NULL, N_("Set Background Color"), (GCallback)set_bg_color },
4514 { "ZoomIn", GTK_STOCK_ZOOM_IN, N_("Zoom _In"), "<control>plus", N_("Zoom In"), (GCallback)draw_zoom_cb },
4515 { "ZoomOut", GTK_STOCK_ZOOM_OUT, N_("Zoom _Out"), "<control>minus", N_("Zoom Out"), (GCallback)draw_zoom_cb },
4516 { "ZoomTo", GTK_STOCK_ZOOM_FIT, N_("Zoom _To..."), "<control>Z", N_("Zoom To"), (GCallback)zoom_to_cb },
4517 { "PanNorth", NULL, N_("Pan _North"), "<control>Up", NULL, (GCallback)draw_pan_cb },
4518 { "PanEast", NULL, N_("Pan _East"), "<control>Right", NULL, (GCallback)draw_pan_cb },
4519 { "PanSouth", NULL, N_("Pan _South"), "<control>Down", NULL, (GCallback)draw_pan_cb },
4520 { "PanWest", NULL, N_("Pan _West"), "<control>Left", NULL, (GCallback)draw_pan_cb },
4521 { "BGJobs", GTK_STOCK_EXECUTE, N_("Background _Jobs"), NULL, N_("Background Jobs"), (GCallback)a_background_show_window },
4523 { "Cut", GTK_STOCK_CUT, N_("Cu_t"), NULL, N_("Cut selected layer"), (GCallback)menu_cut_layer_cb },
4524 { "Copy", GTK_STOCK_COPY, N_("_Copy"), NULL, N_("Copy selected layer"), (GCallback)menu_copy_layer_cb },
4525 { "Paste", GTK_STOCK_PASTE, N_("_Paste"), NULL, N_("Paste layer into selected container layer or otherwise above selected layer"), (GCallback)menu_paste_layer_cb },
4526 { "Delete", GTK_STOCK_DELETE, N_("_Delete"), NULL, N_("Remove selected layer"), (GCallback)menu_delete_layer_cb },
4527 { "DeleteAll", NULL, N_("Delete All"), NULL, NULL, (GCallback)clear_cb },
4528 { "CopyCentre",NULL, N_("Copy Centre _Location"), "<control>h", NULL, (GCallback)menu_copy_centre_cb },
4529 { "MapCacheFlush",NULL, N_("_Flush Map Cache"), NULL, NULL, (GCallback)mapcache_flush_cb },
4530 { "SetDefaultLocation", GTK_STOCK_GO_FORWARD, N_("_Set the Default Location"), NULL, N_("Set the Default Location to the current position"),(GCallback)default_location_cb },
4531 { "Preferences",GTK_STOCK_PREFERENCES, N_("_Preferences"), NULL, N_("Program Preferences"), (GCallback)preferences_cb },
4532 { "LayerDefaults",GTK_STOCK_PROPERTIES, N_("_Layer Defaults"), NULL, NULL, NULL },
4533 { "Properties",GTK_STOCK_PROPERTIES, N_("_Properties"), NULL, N_("Layer Properties"), (GCallback)menu_properties_cb },
4535 { "HelpEntry", GTK_STOCK_HELP, N_("_Help"), "F1", N_("Help"), (GCallback)help_help_cb },
4536 { "About", GTK_STOCK_ABOUT, N_("_About"), NULL, N_("About"), (GCallback)help_about_cb },
4539 static GtkActionEntry debug_entries[] = {
4540 { "MapCacheInfo", NULL, "_Map Cache Info", NULL, NULL, (GCallback)help_cache_info_cb },
4541 { "BackForwardInfo", NULL, "_Back/Forward Info", NULL, NULL, (GCallback)back_forward_info_cb },
4544 static GtkActionEntry entries_gpsbabel[] = {
4545 { "ExportKML", NULL, N_("_KML..."), NULL, N_("Export as KML"), (GCallback)export_to_kml },
4546 { "AcquireGPS", NULL, N_("From _GPS..."), NULL, N_("Transfer data from a GPS device"), (GCallback)acquire_from_gps },
4547 { "AcquireGPSBabel", NULL, N_("Import File With GPS_Babel..."), NULL, N_("Import file via GPSBabel converter"), (GCallback)acquire_from_file },
4550 static GtkActionEntry entries_geojson[] = {
4551 { "AcquireGeoJSON", NULL, N_("Import Geo_JSON File..."), NULL, N_("Import GeoJSON file"), (GCallback)acquire_from_geojson },
4555 static GtkRadioActionEntry mode_entries[] = {
4556 { "ModeUTM", NULL, N_("_UTM Mode"), "<control>u", NULL, VIK_VIEWPORT_DRAWMODE_UTM },
4557 { "ModeExpedia", NULL, N_("_Expedia Mode"), "<control>e", NULL, VIK_VIEWPORT_DRAWMODE_EXPEDIA },
4558 { "ModeMercator", NULL, N_("_Mercator Mode"), "<control>m", NULL, VIK_VIEWPORT_DRAWMODE_MERCATOR },
4559 { "ModeLatLon", NULL, N_("Lat_/Lon Mode"), "<control>l", NULL, VIK_VIEWPORT_DRAWMODE_LATLON },
4562 static GtkToggleActionEntry toggle_entries[] = {
4563 { "ShowScale", NULL, N_("Show _Scale"), "<shift>F5", N_("Show Scale"), (GCallback)toggle_draw_scale, TRUE },
4564 { "ShowCenterMark", NULL, N_("Show _Center Mark"), "F6", N_("Show Center Mark"), (GCallback)toggle_draw_centermark, TRUE },
4565 { "ShowHighlight", GTK_STOCK_UNDERLINE, N_("Show _Highlight"), "F7", N_("Show Highlight"), (GCallback)toggle_draw_highlight, TRUE },
4566 { "FullScreen", GTK_STOCK_FULLSCREEN, N_("_Full Screen"), "F11", N_("Activate full screen mode"), (GCallback)full_screen_cb, FALSE },
4567 { "ViewSidePanel", GTK_STOCK_INDEX, N_("Show Side _Panel"), "F9", N_("Show Side Panel"), (GCallback)view_side_panel_cb, TRUE },
4568 { "ViewStatusBar", NULL, N_("Show Status_bar"), "F12", N_("Show Statusbar"), (GCallback)view_statusbar_cb, TRUE },
4569 { "ViewToolbar", NULL, N_("Show _Toolbar"), "F3", N_("Show Toolbar"), (GCallback)view_toolbar_cb, TRUE },
4570 { "ViewMainMenu", NULL, N_("Show _Menu"), "F4", N_("Show Menu"), (GCallback)view_main_menu_cb, TRUE },
4573 // This must match the toggle entries order above
4574 static gpointer toggle_entries_toolbar_cb[] = {
4575 (GCallback)tb_set_draw_scale,
4576 (GCallback)tb_set_draw_centermark,
4577 (GCallback)tb_set_draw_highlight,
4578 (GCallback)tb_full_screen_cb,
4579 (GCallback)tb_view_side_panel_cb,
4580 (GCallback)tb_view_statusbar_cb,
4581 (GCallback)tb_view_toolbar_cb,
4582 (GCallback)tb_view_main_menu_cb,
4585 #include "menu.xml.h"
4586 static void window_create_ui( VikWindow *window )
4589 GtkActionGroup *action_group;
4590 GtkAccelGroup *accel_group;
4593 GtkIconFactory *icon_factory;
4594 GtkIconSet *icon_set;
4595 GtkRadioActionEntry *tools = NULL, *radio;
4598 uim = gtk_ui_manager_new ();
4601 toolbox_add_tool(window->vt, &ruler_tool, TOOL_LAYER_TYPE_NONE);
4602 toolbox_add_tool(window->vt, &zoom_tool, TOOL_LAYER_TYPE_NONE);
4603 toolbox_add_tool(window->vt, &pan_tool, TOOL_LAYER_TYPE_NONE);
4604 toolbox_add_tool(window->vt, &select_tool, TOOL_LAYER_TYPE_NONE);
4606 toolbar_action_tool_entry_register ( window->viking_vtb, &pan_tool.radioActionEntry );
4607 toolbar_action_tool_entry_register ( window->viking_vtb, &zoom_tool.radioActionEntry );
4608 toolbar_action_tool_entry_register ( window->viking_vtb, &ruler_tool.radioActionEntry );
4609 toolbar_action_tool_entry_register ( window->viking_vtb, &select_tool.radioActionEntry );
4612 if (!(mid = gtk_ui_manager_add_ui_from_string (uim, menu_xml, -1, &error))) {
4613 g_error_free (error);
4617 action_group = gtk_action_group_new ("MenuActions");
4618 gtk_action_group_set_translation_domain(action_group, PACKAGE_NAME);
4619 gtk_action_group_add_actions (action_group, entries, G_N_ELEMENTS (entries), window);
4620 gtk_action_group_add_toggle_actions (action_group, toggle_entries, G_N_ELEMENTS (toggle_entries), window);
4621 gtk_action_group_add_radio_actions (action_group, mode_entries, G_N_ELEMENTS (mode_entries), 4, (GCallback)window_change_coord_mode_cb, window);
4623 if ( gtk_ui_manager_add_ui_from_string ( uim,
4624 "<ui><menubar name='MainMenu'><menu action='Help'>"
4625 "<menuitem action='MapCacheInfo'/>"
4626 "<menuitem action='BackForwardInfo'/>"
4627 "</menu></menubar></ui>",
4629 gtk_action_group_add_actions (action_group, debug_entries, G_N_ELEMENTS (debug_entries), window);
4633 for ( i=0; i < G_N_ELEMENTS (entries); i++ ) {
4634 if ( entries[i].callback )
4635 toolbar_action_entry_register ( window->viking_vtb, &entries[i] );
4638 if ( G_N_ELEMENTS (toggle_entries) != G_N_ELEMENTS (toggle_entries_toolbar_cb) ) {
4639 g_print ( "Broken entries definitions\n" );
4642 for ( i=0; i < G_N_ELEMENTS (toggle_entries); i++ ) {
4643 if ( toggle_entries_toolbar_cb[i] )
4644 toolbar_action_toggle_entry_register ( window->viking_vtb, &toggle_entries[i], toggle_entries_toolbar_cb[i] );
4647 for ( i=0; i < G_N_ELEMENTS (mode_entries); i++ ) {
4648 toolbar_action_mode_entry_register ( window->viking_vtb, &mode_entries[i] );
4651 // Use this to see if GPSBabel is available:
4652 if ( a_babel_available () ) {
4653 // If going to add more entries then might be worth creating a menu_gpsbabel.xml.h file
4654 if ( gtk_ui_manager_add_ui_from_string ( uim,
4656 "<menubar name='MainMenu'>" \
4657 "<menu action='File'><menu action='Export'><menuitem action='ExportKML'/></menu></menu>" \
4658 "<menu action='File'><menu action='Acquire'><menuitem action='AcquireGPS'/></menu></menu>" \
4659 "<menu action='File'><menu action='Acquire'><menuitem action='AcquireGPSBabel'/></menu></menu>" \
4663 gtk_action_group_add_actions ( action_group, entries_gpsbabel, G_N_ELEMENTS (entries_gpsbabel), window );
4666 // GeoJSON import capability
4667 if ( g_find_program_in_path ( a_geojson_program_import() ) ) {
4668 if ( gtk_ui_manager_add_ui_from_string ( uim,
4669 "<ui><menubar name='MainMenu'><menu action='File'><menu action='Acquire'><menuitem action='AcquireGeoJSON'/></menu></menu></menubar></ui>",
4671 gtk_action_group_add_actions ( action_group, entries_geojson, G_N_ELEMENTS (entries_geojson), window );
4674 icon_factory = gtk_icon_factory_new ();
4675 gtk_icon_factory_add_default (icon_factory);
4677 register_vik_icons(icon_factory);
4679 // Copy the tool RadioActionEntries out of the main Window structure into an extending array 'tools'
4680 // so that it can be applied to the UI in one action group add function call below
4682 for (i=0; i<window->vt->n_tools; i++) {
4683 tools = g_renew(GtkRadioActionEntry, tools, ntools+1);
4684 radio = &tools[ntools];
4686 *radio = window->vt->tools[i].ti.radioActionEntry;
4687 radio->value = ntools;
4690 for (i=0; i<VIK_LAYER_NUM_TYPES; i++) {
4691 GtkActionEntry action;
4692 gtk_ui_manager_add_ui(uim, mid, "/ui/MainMenu/Layers/",
4693 vik_layer_get_interface(i)->name,
4694 vik_layer_get_interface(i)->name,
4695 GTK_UI_MANAGER_MENUITEM, FALSE);
4697 icon_set = gtk_icon_set_new_from_pixbuf (gdk_pixbuf_from_pixdata (vik_layer_get_interface(i)->icon, FALSE, NULL ));
4698 gtk_icon_factory_add (icon_factory, vik_layer_get_interface(i)->name, icon_set);
4699 gtk_icon_set_unref (icon_set);
4701 action.name = vik_layer_get_interface(i)->name;
4702 action.stock_id = vik_layer_get_interface(i)->name;
4703 action.label = g_strdup_printf( _("New _%s Layer"), _(vik_layer_get_interface(i)->name));
4704 action.accelerator = vik_layer_get_interface(i)->accelerator;
4705 action.tooltip = NULL;
4706 action.callback = (GCallback)menu_addlayer_cb;
4707 gtk_action_group_add_actions(action_group, &action, 1, window);
4709 g_free ( (gchar*)action.label );
4711 if ( vik_layer_get_interface(i)->tools_count ) {
4712 gtk_ui_manager_add_ui(uim, mid, "/ui/MainMenu/Tools/", vik_layer_get_interface(i)->name, NULL, GTK_UI_MANAGER_SEPARATOR, FALSE);
4715 // Further tool copying for to apply to the UI, also apply menu UI setup
4716 for ( j = 0; j < vik_layer_get_interface(i)->tools_count; j++ ) {
4717 tools = g_renew(GtkRadioActionEntry, tools, ntools+1);
4718 radio = &tools[ntools];
4721 gtk_ui_manager_add_ui(uim, mid, "/ui/MainMenu/Tools",
4722 vik_layer_get_interface(i)->tools[j].radioActionEntry.label,
4723 vik_layer_get_interface(i)->tools[j].radioActionEntry.name,
4724 GTK_UI_MANAGER_MENUITEM, FALSE);
4726 toolbox_add_tool(window->vt, &(vik_layer_get_interface(i)->tools[j]), i);
4727 toolbar_action_tool_entry_register ( window->viking_vtb, &(vik_layer_get_interface(i)->tools[j].radioActionEntry) );
4729 *radio = vik_layer_get_interface(i)->tools[j].radioActionEntry;
4730 // Overwrite with actual number to use
4731 radio->value = ntools;
4734 GtkActionEntry action_dl;
4735 gchar *layername = g_strdup_printf ( "Layer%s", vik_layer_get_interface(i)->fixed_layer_name );
4736 gtk_ui_manager_add_ui(uim, mid, "/ui/MainMenu/Edit/LayerDefaults",
4737 vik_layer_get_interface(i)->name,
4739 GTK_UI_MANAGER_MENUITEM, FALSE);
4742 // For default layers use action names of the form 'Layer<LayerName>'
4743 // This is to avoid clashing with just the layer name used above for the tool actions
4744 action_dl.name = g_strconcat("Layer", vik_layer_get_interface(i)->fixed_layer_name, NULL);
4745 action_dl.stock_id = NULL;
4746 action_dl.label = g_strconcat(_(vik_layer_get_interface(i)->name), _("..."), NULL);
4747 action_dl.accelerator = NULL;
4748 action_dl.tooltip = NULL;
4749 action_dl.callback = (GCallback)layer_defaults_cb;
4750 gtk_action_group_add_actions(action_group, &action_dl, 1, window);
4751 g_free ( (gchar*)action_dl.name );
4752 g_free ( (gchar*)action_dl.label );
4754 g_object_unref (icon_factory);
4756 gtk_action_group_add_radio_actions(action_group, tools, ntools, 0, (GCallback)menu_cb, window);
4759 gtk_ui_manager_insert_action_group (uim, action_group, 0);
4761 for (i=0; i<VIK_LAYER_NUM_TYPES; i++) {
4762 for ( j = 0; j < vik_layer_get_interface(i)->tools_count; j++ ) {
4763 GtkAction *action = gtk_action_group_get_action(action_group,
4764 vik_layer_get_interface(i)->tools[j].radioActionEntry.name);
4765 g_object_set(action, "sensitive", FALSE, NULL);
4769 // This is done last so we don't need to track the value of mid anymore
4770 vik_ext_tools_add_action_items ( window, window->uim, action_group, mid );
4772 window->action_group = action_group;
4774 accel_group = gtk_ui_manager_get_accel_group (uim);
4775 gtk_window_add_accel_group (GTK_WINDOW (window), accel_group);
4776 gtk_ui_manager_ensure_update (uim);
4778 setup_recent_files(window);
4780 if ( vik_routing_number_of_engines(VIK_ROUTING_METHOD_DIRECTIONS) == 0 ) {
4781 GtkWidget *widget = gtk_ui_manager_get_widget ( uim, "/ui/MainMenu/File/Acquire/AcquireRouting" );
4783 g_object_set ( widget, "sensitive", FALSE, NULL );
4785 g_warning ( "No direction routing engines available" );
4790 // TODO - add method to add tool icons defined from outside this file
4791 // and remove the reverse dependency on icon definition from this file
4793 const GdkPixdata *data;
4796 { &mover_22_pixbuf, "vik-icon-pan" },
4797 { &zoom_18_pixbuf, "vik-icon-zoom" },
4798 { &ruler_18_pixbuf, "vik-icon-ruler" },
4799 { &select_18_pixbuf, "vik-icon-select" },
4800 { &vik_new_route_18_pixbuf, "vik-icon-Create Route" },
4801 { &route_finder_18_pixbuf, "vik-icon-Route Finder" },
4802 { &demdl_18_pixbuf, "vik-icon-DEM Download" },
4803 { &showpic_18_pixbuf, "vik-icon-Show Picture" },
4804 { &addtr_18_pixbuf, "vik-icon-Create Track" },
4805 { &edtr_18_pixbuf, "vik-icon-Edit Trackpoint" },
4806 { &addwp_18_pixbuf, "vik-icon-Create Waypoint" },
4807 { &edwp_18_pixbuf, "vik-icon-Edit Waypoint" },
4808 { &geozoom_18_pixbuf, "vik-icon-Georef Zoom Tool" },
4809 { &geomove_18_pixbuf, "vik-icon-Georef Move Map" },
4810 { &mapdl_18_pixbuf, "vik-icon-Maps Download" },
4813 static gint n_stock_icons = G_N_ELEMENTS (stock_icons);
4816 register_vik_icons (GtkIconFactory *icon_factory)
4818 GtkIconSet *icon_set;
4821 for (i = 0; i < n_stock_icons; i++) {
4822 icon_set = gtk_icon_set_new_from_pixbuf (gdk_pixbuf_from_pixdata (
4823 stock_icons[i].data, FALSE, NULL ));
4824 gtk_icon_factory_add (icon_factory, stock_icons[i].stock_id, icon_set);
4825 gtk_icon_set_unref (icon_set);
4829 gpointer vik_window_get_selected_trw_layer ( VikWindow *vw )
4831 return vw->selected_vtl;
4834 void vik_window_set_selected_trw_layer ( VikWindow *vw, gpointer vtl )
4836 vw->selected_vtl = vtl;
4837 vw->containing_vtl = vtl;
4839 vw->selected_track = NULL;
4840 vw->selected_tracks = NULL;
4841 vw->selected_waypoint = NULL;
4842 vw->selected_waypoints = NULL;
4843 // Set highlight thickness
4844 vik_viewport_set_highlight_thickness ( vw->viking_vvp, vik_trw_layer_get_property_tracks_line_thickness (vw->containing_vtl) );
4847 GHashTable *vik_window_get_selected_tracks ( VikWindow *vw )
4849 return vw->selected_tracks;
4852 void vik_window_set_selected_tracks ( VikWindow *vw, GHashTable *ght, gpointer vtl )
4854 vw->selected_tracks = ght;
4855 vw->containing_vtl = vtl;
4857 vw->selected_vtl = NULL;
4858 vw->selected_track = NULL;
4859 vw->selected_waypoint = NULL;
4860 vw->selected_waypoints = NULL;
4861 // Set highlight thickness
4862 vik_viewport_set_highlight_thickness ( vw->viking_vvp, vik_trw_layer_get_property_tracks_line_thickness (vw->containing_vtl) );
4865 gpointer vik_window_get_selected_track ( VikWindow *vw )
4867 return vw->selected_track;
4870 void vik_window_set_selected_track ( VikWindow *vw, gpointer *vt, gpointer vtl )
4872 vw->selected_track = vt;
4873 vw->containing_vtl = vtl;
4875 vw->selected_vtl = NULL;
4876 vw->selected_tracks = NULL;
4877 vw->selected_waypoint = NULL;
4878 vw->selected_waypoints = NULL;
4879 // Set highlight thickness
4880 vik_viewport_set_highlight_thickness ( vw->viking_vvp, vik_trw_layer_get_property_tracks_line_thickness (vw->containing_vtl) );
4883 GHashTable *vik_window_get_selected_waypoints ( VikWindow *vw )
4885 return vw->selected_waypoints;
4888 void vik_window_set_selected_waypoints ( VikWindow *vw, GHashTable *ght, gpointer vtl )
4890 vw->selected_waypoints = ght;
4891 vw->containing_vtl = vtl;
4893 vw->selected_vtl = NULL;
4894 vw->selected_track = NULL;
4895 vw->selected_tracks = NULL;
4896 vw->selected_waypoint = NULL;
4899 gpointer vik_window_get_selected_waypoint ( VikWindow *vw )
4901 return vw->selected_waypoint;
4904 void vik_window_set_selected_waypoint ( VikWindow *vw, gpointer *vwp, gpointer vtl )
4906 vw->selected_waypoint = vwp;
4907 vw->containing_vtl = vtl;
4909 vw->selected_vtl = NULL;
4910 vw->selected_track = NULL;
4911 vw->selected_tracks = NULL;
4912 vw->selected_waypoints = NULL;
4915 gboolean vik_window_clear_highlight ( VikWindow *vw )
4917 gboolean need_redraw = FALSE;
4918 vw->containing_vtl = NULL;
4919 if ( vw->selected_vtl != NULL ) {
4920 vw->selected_vtl = NULL;
4923 if ( vw->selected_track != NULL ) {
4924 vw->selected_track = NULL;
4927 if ( vw->selected_tracks != NULL ) {
4928 vw->selected_tracks = NULL;
4931 if ( vw->selected_waypoint != NULL ) {
4932 vw->selected_waypoint = NULL;
4935 if ( vw->selected_waypoints != NULL ) {
4936 vw->selected_waypoints = NULL;
4943 * May return NULL if the window no longer exists
4945 GThread *vik_window_get_thread ( VikWindow *vw )