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-2014, 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 "geonamessearch.h"
60 #include <glib/gstdio.h>
61 #include <glib/gprintf.h>
62 #include <glib/gi18n.h>
64 #include <gdk/gdkkeysyms.h>
66 // This seems rather arbitary, quite large and pointless
67 // I mean, if you have a thousand windows open;
68 // why not be allowed to open a thousand more...
69 #define MAX_WINDOWS 1024
70 static guint window_count = 0;
71 static GSList *window_list = NULL;
73 #define VIKING_WINDOW_WIDTH 1000
74 #define VIKING_WINDOW_HEIGHT 800
75 #define DRAW_IMAGE_DEFAULT_WIDTH 1280
76 #define DRAW_IMAGE_DEFAULT_HEIGHT 1024
77 #define DRAW_IMAGE_DEFAULT_SAVE_AS_PNG TRUE
79 // The last used directories
80 static gchar *last_folder_files_uri = NULL;
81 static gchar *last_folder_images_uri = NULL;
83 static void window_finalize ( GObject *gob );
84 static GObjectClass *parent_class;
86 static void window_set_filename ( VikWindow *vw, const gchar *filename );
87 static const gchar *window_get_filename ( VikWindow *vw );
89 static VikWindow *window_new ();
91 static void draw_update ( VikWindow *vw );
93 static void newwindow_cb ( GtkAction *a, VikWindow *vw );
96 static void open_window ( VikWindow *vw, GSList *files );
97 static void destroy_window ( GtkWidget *widget,
100 /* Drawing & stuff */
102 static gboolean delete_event( VikWindow *vw );
104 static gboolean key_press_event( VikWindow *vw, GdkEventKey *event, gpointer data );
106 static void center_changed_cb ( VikWindow *vw );
107 static void window_configure_event ( VikWindow *vw );
108 static void draw_sync ( VikWindow *vw );
109 static void draw_redraw ( VikWindow *vw );
110 static void draw_scroll ( VikWindow *vw, GdkEventScroll *event );
111 static void draw_click ( VikWindow *vw, GdkEventButton *event );
112 static void draw_release ( VikWindow *vw, GdkEventButton *event );
113 static void draw_mouse_motion ( VikWindow *vw, GdkEventMotion *event );
114 static void draw_zoom_cb ( GtkAction *a, VikWindow *vw );
115 static void draw_goto_cb ( GtkAction *a, VikWindow *vw );
116 static void draw_refresh_cb ( GtkAction *a, VikWindow *vw );
118 static void draw_status ( VikWindow *vw );
120 /* End Drawing Functions */
122 static void toggle_draw_scale ( GtkAction *a, VikWindow *vw );
123 static void toggle_draw_centermark ( GtkAction *a, VikWindow *vw );
124 static void toggle_draw_highlight ( GtkAction *a, VikWindow *vw );
126 static void menu_addlayer_cb ( GtkAction *a, VikWindow *vw );
127 static void menu_properties_cb ( GtkAction *a, VikWindow *vw );
128 static void menu_delete_layer_cb ( GtkAction *a, VikWindow *vw );
130 /* tool management */
136 #define TOOL_LAYER_TYPE_NONE -1
141 toolbox_tool_t *tools;
145 static void menu_cb ( GtkAction *old, GtkAction *a, VikWindow *vw );
146 static void window_change_coord_mode_cb ( GtkAction *old, GtkAction *a, VikWindow *vw );
147 static toolbox_tools_t* toolbox_create(VikWindow *vw);
148 static void toolbox_add_tool(toolbox_tools_t *vt, VikToolInterface *vti, gint layer_type );
149 static int toolbox_get_tool(toolbox_tools_t *vt, const gchar *tool_name);
150 static void toolbox_activate(toolbox_tools_t *vt, const gchar *tool_name);
151 static const GdkCursor *toolbox_get_cursor(toolbox_tools_t *vt, const gchar *tool_name);
152 static void toolbox_click (toolbox_tools_t *vt, GdkEventButton *event);
153 static void toolbox_move (toolbox_tools_t *vt, GdkEventMotion *event);
154 static void toolbox_release (toolbox_tools_t *vt, GdkEventButton *event);
158 static void window_create_ui( VikWindow *window );
159 static void register_vik_icons (GtkIconFactory *icon_factory);
162 static void load_file ( GtkAction *a, VikWindow *vw );
163 static gboolean save_file_as ( GtkAction *a, VikWindow *vw );
164 static gboolean save_file ( GtkAction *a, VikWindow *vw );
165 static gboolean save_file_and_exit ( GtkAction *a, VikWindow *vw );
166 static gboolean window_save ( VikWindow *vw );
171 VikViewport *viking_vvp;
172 VikLayersPanel *viking_vlp;
173 VikStatusbar *viking_vs;
174 VikToolbar *viking_vtb;
176 GtkWidget *main_vbox;
177 GtkWidget *menu_hbox;
179 GdkCursor *busy_cursor;
180 GdkCursor *viewport_cursor; // only a reference
182 /* tool management state */
185 guint16 tool_layer_id;
186 guint16 tool_tool_id;
188 GtkActionGroup *action_group;
191 // NB scale, centermark and highlight are in viewport.
192 gboolean show_full_screen;
193 gboolean show_side_panel;
194 gboolean show_statusbar;
195 gboolean show_toolbar;
196 gboolean show_main_menu;
198 gboolean select_move;
201 gint delayed_pan_x, delayed_pan_y; // Temporary storage
202 gboolean single_click_pending;
204 guint draw_image_width, draw_image_height;
205 gboolean draw_image_save_as_png;
209 VikLoadType_t loaded_type;
211 GtkWidget *open_dia, *save_dia;
212 GtkWidget *save_img_dia, *save_img_dir_dia;
214 gboolean only_updating_coord_mode_ui; /* hack for a bug in GTK */
218 /* half-drawn update */
220 VikCoord trigger_center;
222 /* Store at this level for highlighted selection drawing since it applies to the viewport and the layers panel */
223 /* Only one of these items can be selected at the same time */
224 gpointer selected_vtl; /* notionally VikTrwLayer */
225 GHashTable *selected_tracks;
226 gpointer selected_track; /* notionally VikTrack */
227 GHashTable *selected_waypoints;
228 gpointer selected_waypoint; /* notionally VikWaypoint */
229 /* only use for individual track or waypoint */
230 /* For track(s) & waypoint(s) it is the layer they are in - this helps refering to the individual item easier */
231 gpointer containing_vtl; /* notionally VikTrwLayer */
245 VW_OPENWINDOW_SIGNAL,
249 static guint window_signals[VW_LAST_SIGNAL] = { 0 };
251 // TODO get rid of this as this is unnecessary duplication...
252 static gchar *tool_names[NUMBER_OF_TOOLS] = { N_("Pan"), N_("Zoom"), N_("Ruler"), N_("Select") };
254 G_DEFINE_TYPE (VikWindow, vik_window, GTK_TYPE_WINDOW)
256 VikViewport * vik_window_viewport(VikWindow *vw)
258 return(vw->viking_vvp);
261 VikLayersPanel * vik_window_layers_panel(VikWindow *vw)
263 return(vw->viking_vlp);
267 * Returns the statusbar for the window
269 VikStatusbar * vik_window_get_statusbar ( VikWindow *vw )
271 return vw->viking_vs;
275 * Returns the 'project' filename
277 const gchar *vik_window_get_filename (VikWindow *vw)
284 vik_statusbar_type_t vs_type;
285 gchar* message; // Always make a copy of this data
286 } statusbar_idle_data;
289 * For the actual statusbar update!
291 static gboolean statusbar_idle_update ( statusbar_idle_data *sid )
293 vik_statusbar_set_message ( sid->vs, sid->vs_type, sid->message );
294 g_free ( sid->message );
300 * vik_window_statusbar_update:
301 * @vw: The main window in which the statusbar will be updated.
302 * @message: The string to be displayed. This is copied.
303 * @vs_type: The part of the statusbar to be updated.
305 * This updates any part of the statusbar with the new string.
306 * It handles calling from the main thread or any background thread
307 * ATM this mostly used from background threads - as from the main thread
308 * one may use the vik_statusbar_set_message() directly.
310 void vik_window_statusbar_update ( VikWindow *vw, const gchar* message, vik_statusbar_type_t vs_type )
312 GThread *thread = vik_window_get_thread ( vw );
317 statusbar_idle_data *sid = g_malloc ( sizeof (statusbar_idle_data) );
318 sid->vs = vw->viking_vs;
319 sid->vs_type = vs_type;
320 sid->message = g_strdup ( message );
322 if ( g_thread_self() == thread ) {
323 g_idle_add ( (GSourceFunc) statusbar_idle_update, sid );
326 // From a background thread
327 gdk_threads_add_idle ( (GSourceFunc) statusbar_idle_update, sid );
331 // Actual signal handlers
332 static void destroy_window ( GtkWidget *widget,
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 g_signal_connect (G_OBJECT (vw), "destroy",
357 G_CALLBACK (destroy_window), NULL);
358 g_signal_connect (G_OBJECT (vw), "newwindow",
359 G_CALLBACK (vik_window_new_window), NULL);
360 g_signal_connect (G_OBJECT (vw), "openwindow",
361 G_CALLBACK (open_window), NULL);
363 gtk_widget_show_all ( GTK_WIDGET(vw) );
365 if ( a_vik_get_restore_window_state() ) {
366 // These settings are applied after the show all as these options hide widgets
368 if ( a_settings_get_boolean ( VIK_SETTINGS_WIN_SIDEPANEL, &sidepanel ) )
370 gtk_widget_hide ( GTK_WIDGET(vw->viking_vlp) );
371 GtkWidget *check_box = gtk_ui_manager_get_widget ( vw->uim, "/ui/MainMenu/View/SetShow/ViewSidePanel" );
372 gtk_check_menu_item_set_active ( GTK_CHECK_MENU_ITEM(check_box), FALSE );
376 if ( a_settings_get_boolean ( VIK_SETTINGS_WIN_STATUSBAR, &statusbar ) )
378 gtk_widget_hide ( GTK_WIDGET(vw->viking_vs) );
379 GtkWidget *check_box = gtk_ui_manager_get_widget ( vw->uim, "/ui/MainMenu/View/SetShow/ViewStatusBar" );
380 gtk_check_menu_item_set_active ( GTK_CHECK_MENU_ITEM(check_box), FALSE );
384 if ( a_settings_get_boolean ( VIK_SETTINGS_WIN_TOOLBAR, &toolbar ) )
386 gtk_widget_hide ( toolbar_get_widget (vw->viking_vtb) );
387 GtkWidget *check_box = gtk_ui_manager_get_widget ( vw->uim, "/ui/MainMenu/View/SetShow/ViewToolBar" );
388 gtk_check_menu_item_set_active ( GTK_CHECK_MENU_ITEM(check_box), FALSE );
392 if ( a_settings_get_boolean ( VIK_SETTINGS_WIN_MENUBAR, &menubar ) )
394 gtk_widget_hide ( gtk_ui_manager_get_widget ( vw->uim, "/ui/MainMenu" ) );
395 GtkWidget *check_box = gtk_ui_manager_get_widget ( vw->uim, "/ui/MainMenu/View/SetShow/ViewMainMenu" );
396 gtk_check_menu_item_set_active ( GTK_CHECK_MENU_ITEM(check_box), FALSE );
407 * determine_location_thread:
408 * @vw: The window that will get updated
409 * @threaddata: Data used by our background thread mechanism
411 * Use the features in vikgoto to determine where we are
412 * Then set up the viewport:
413 * 1. To goto the location
414 * 2. Set an appropriate level zoom for the location type
415 * 3. Some statusbar message feedback
417 static int determine_location_thread ( VikWindow *vw, gpointer threaddata )
421 gint ans = a_vik_goto_where_am_i ( vw->viking_vvp, &ll, &name );
423 int result = a_background_thread_progress ( threaddata, 1.0 );
425 vik_window_statusbar_update ( vw, _("Location lookup aborted"), VIK_STATUSBAR_INFO );
426 return -1; /* Abort thread */
434 // Position found with city precision - so zoom out more
437 else if ( ans == 3 ) {
438 // Position found via country name search - so zoom wayyyy out
442 vik_viewport_set_zoom ( vw->viking_vvp, zoom );
443 vik_viewport_set_center_latlon ( vw->viking_vvp, &ll, FALSE );
445 gchar *message = g_strdup_printf ( _("Location found: %s"), name );
446 vik_window_statusbar_update ( vw, message, VIK_STATUSBAR_INFO );
450 // Signal to redraw from the background
451 vik_layers_panel_emit_update ( vw->viking_vlp );
454 vik_window_statusbar_update ( vw, _("Unable to determine location"), VIK_STATUSBAR_INFO );
460 * Steps to be taken once initial loading has completed
462 void vik_window_new_window_finish ( VikWindow *vw )
464 // Don't add a map if we've loaded a Viking file already
468 if ( a_vik_get_startup_method ( ) == VIK_STARTUP_METHOD_SPECIFIED_FILE ) {
469 vik_window_open_file ( vw, a_vik_get_startup_file(), TRUE );
474 // Maybe add a default map layer
475 if ( a_vik_get_add_default_map_layer () ) {
476 VikMapsLayer *vml = VIK_MAPS_LAYER ( vik_layer_create(VIK_LAYER_MAPS, vw->viking_vvp, FALSE) );
477 vik_layer_rename ( VIK_LAYER(vml), _("Default Map") );
478 vik_aggregate_layer_add_layer ( vik_layers_panel_get_top_layer(vw->viking_vlp), VIK_LAYER(vml), TRUE );
483 // If not loaded any file, maybe try the location lookup
484 if ( vw->loaded_type == LOAD_TYPE_READ_FAILURE ) {
485 if ( a_vik_get_startup_method ( ) == VIK_STARTUP_METHOD_AUTO_LOCATION ) {
487 vik_statusbar_set_message ( vw->viking_vs, VIK_STATUSBAR_INFO, _("Trying to determine location...") );
489 a_background_thread ( BACKGROUND_POOL_REMOTE,
491 _("Determining location"),
492 (vik_thr_func) determine_location_thread,
501 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 != NULL && 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 );
907 vw->save_img_dia = NULL;
908 vw->save_img_dir_dia = NULL;
910 vw->show_side_panel = TRUE;
911 vw->show_statusbar = TRUE;
912 vw->show_toolbar = TRUE;
913 vw->show_main_menu = TRUE;
915 // Only accept Drag and Drop of files onto the viewport
916 gtk_drag_dest_set ( GTK_WIDGET(vw->viking_vvp), GTK_DEST_DEFAULT_ALL, NULL, 0, GDK_ACTION_COPY );
917 gtk_drag_dest_add_uri_targets ( GTK_WIDGET(vw->viking_vvp) );
918 g_signal_connect ( GTK_WIDGET(vw->viking_vvp), "drag-data-received", G_CALLBACK(drag_data_received_cb), NULL );
920 // Store the thread value so comparisons can be made to determine the gdk update method
921 // Hopefully we are storing the main thread value here :)
922 // [ATM any window initialization is always be performed by the main thread]
923 vw->thread = g_thread_self();
925 // Set the default tool + mode
926 gtk_action_activate ( gtk_action_group_get_action ( vw->action_group, "Pan" ) );
927 gtk_action_activate ( gtk_action_group_get_action ( vw->action_group, "ModeMercator" ) );
929 gchar *accel_file_name = g_build_filename ( a_get_viking_dir(), VIKING_ACCELERATOR_KEY_FILE, NULL );
930 gtk_accel_map_load ( accel_file_name );
931 g_free ( accel_file_name );
934 static VikWindow *window_new ()
936 return VIK_WINDOW ( g_object_new ( VIK_WINDOW_TYPE, NULL ) );
940 * Update the displayed map
941 * Only update the top most visible map layer
942 * ATM this assumes (as per defaults) the top most map has full alpha setting
943 * such that other other maps even though they may be active will not be seen
944 * It's more complicated to work out which maps are actually visible due to alpha settings
945 * and overkill for this simple refresh method.
947 static void simple_map_update ( VikWindow *vw, gboolean only_new )
949 // Find the most relevent single map layer to operate on
950 VikLayer *vl = vik_aggregate_layer_get_top_visible_layer_of_type (vik_layers_panel_get_top_layer(vw->viking_vlp), VIK_LAYER_MAPS);
952 vik_maps_layer_download ( VIK_MAPS_LAYER(vl), vw->viking_vvp, only_new );
956 * This is the global key press handler
957 * Global shortcuts are available at any time and hence are not restricted to when a certain tool is enabled
959 static gboolean key_press_event( VikWindow *vw, GdkEventKey *event, gpointer data )
961 // The keys handled here are not in the menuing system for a couple of reasons:
962 // . Keeps the menu size compact (alebit at expense of discoverably)
963 // . Allows differing key bindings to perform the same actions
965 // First decide if key events are related to the maps layer
966 gboolean map_download = FALSE;
967 gboolean map_download_only_new = TRUE; // Only new or reload
969 GdkModifierType modifiers = gtk_accelerator_get_default_mod_mask();
971 // Standard 'Refresh' keys: F5 or Ctrl+r
972 // Note 'F5' is actually handled via draw_refresh_cb() later on
973 // (not 'R' it's 'r' notice the case difference!!)
974 if ( event->keyval == GDK_r && (event->state & modifiers) == GDK_CONTROL_MASK ) {
976 map_download_only_new = TRUE;
978 // Full cache reload with Ctrl+F5 or Ctrl+Shift+r [This is not in the menu system]
979 // Note the use of uppercase R here since shift key has been pressed
980 else if ( (event->keyval == GDK_F5 && (event->state & modifiers) == GDK_CONTROL_MASK ) ||
981 ( event->keyval == GDK_R && (event->state & modifiers) == (GDK_CONTROL_MASK + GDK_SHIFT_MASK) ) ) {
983 map_download_only_new = FALSE;
985 // Standard Ctrl+KP+ / Ctrl+KP- to zoom in/out respectively
986 else if ( event->keyval == GDK_KEY_KP_Add && (event->state & modifiers) == GDK_CONTROL_MASK ) {
987 vik_viewport_zoom_in ( vw->viking_vvp );
989 return TRUE; // handled keypress
991 else if ( event->keyval == GDK_KEY_KP_Subtract && (event->state & modifiers) == GDK_CONTROL_MASK ) {
992 vik_viewport_zoom_out ( vw->viking_vvp );
994 return TRUE; // handled keypress
997 if ( map_download ) {
998 simple_map_update ( vw, map_download_only_new );
999 return TRUE; // handled keypress
1002 VikLayer *vl = vik_layers_panel_get_selected ( vw->viking_vlp );
1003 if (vl && vw->vt->active_tool != -1 && vw->vt->tools[vw->vt->active_tool].ti.key_press ) {
1004 gint ltype = vw->vt->tools[vw->vt->active_tool].layer_type;
1005 if ( vl && ltype == vl->type )
1006 return vw->vt->tools[vw->vt->active_tool].ti.key_press(vl, event, vw->vt->tools[vw->vt->active_tool].state);
1009 // Ensure called only on window tools (i.e. not on any of the Layer tools since the layer is NULL)
1010 if ( vw->current_tool < TOOL_LAYER ) {
1011 // No layer - but enable window tool keypress processing - these should be able to handle a NULL layer
1012 if ( vw->vt->tools[vw->vt->active_tool].ti.key_press ) {
1013 return vw->vt->tools[vw->vt->active_tool].ti.key_press ( vl, event, vw->vt->tools[vw->vt->active_tool].state );
1017 /* Restore Main Menu via Escape key if the user has hidden it */
1018 /* This key is more likely to be used as they may not remember the function key */
1019 if ( event->keyval == GDK_Escape ) {
1020 GtkWidget *check_box = gtk_ui_manager_get_widget ( vw->uim, "/ui/MainMenu/View/SetShow/ViewMainMenu" );
1022 gboolean state = gtk_check_menu_item_get_active ( GTK_CHECK_MENU_ITEM(check_box) );
1024 gtk_widget_show ( gtk_ui_manager_get_widget ( vw->uim, "/ui/MainMenu" ) );
1025 gtk_check_menu_item_set_active ( GTK_CHECK_MENU_ITEM(check_box), TRUE );
1026 return TRUE; /* handled keypress */
1031 return FALSE; /* don't handle the keypress */
1034 static gboolean delete_event( VikWindow *vw )
1036 #ifdef VIKING_PROMPT_IF_MODIFIED
1043 dia = GTK_DIALOG ( gtk_message_dialog_new ( GTK_WINDOW(vw), GTK_DIALOG_DESTROY_WITH_PARENT, GTK_MESSAGE_QUESTION, GTK_BUTTONS_NONE,
1044 _("Do you want to save the changes you made to the document \"%s\"?\n"
1046 "Your changes will be lost if you don't save them."),
1047 window_get_filename ( vw ) ) );
1048 gtk_dialog_add_buttons ( dia, _("Don't Save"), GTK_RESPONSE_NO, GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, GTK_STOCK_SAVE, GTK_RESPONSE_YES, NULL );
1049 switch ( gtk_dialog_run ( dia ) )
1051 case GTK_RESPONSE_NO: gtk_widget_destroy ( GTK_WIDGET(dia) ); return FALSE;
1052 case GTK_RESPONSE_CANCEL: gtk_widget_destroy ( GTK_WIDGET(dia) ); return TRUE;
1053 default: gtk_widget_destroy ( GTK_WIDGET(dia) ); return ! save_file(NULL, vw);
1057 if ( window_count == 1 ) {
1058 // On the final window close - save latest state - if it's wanted...
1059 if ( a_vik_get_restore_window_state() ) {
1060 gint state = gdk_window_get_state ( GTK_WIDGET(vw)->window );
1061 gboolean state_max = state & GDK_WINDOW_STATE_MAXIMIZED;
1062 a_settings_set_boolean ( VIK_SETTINGS_WIN_MAX, state_max );
1064 gboolean state_fullscreen = state & GDK_WINDOW_STATE_FULLSCREEN;
1065 a_settings_set_boolean ( VIK_SETTINGS_WIN_FULLSCREEN, state_fullscreen );
1067 a_settings_set_boolean ( VIK_SETTINGS_WIN_SIDEPANEL, GTK_WIDGET_VISIBLE (GTK_WIDGET(vw->viking_vlp)) );
1069 a_settings_set_boolean ( VIK_SETTINGS_WIN_STATUSBAR, GTK_WIDGET_VISIBLE (GTK_WIDGET(vw->viking_vs)) );
1071 a_settings_set_boolean ( VIK_SETTINGS_WIN_TOOLBAR, GTK_WIDGET_VISIBLE (toolbar_get_widget(vw->viking_vtb)) );
1073 // If supersized - no need to save the enlarged width+height values
1074 if ( ! (state_fullscreen || state_max) ) {
1076 gtk_window_get_size ( GTK_WINDOW (vw), &width, &height );
1077 a_settings_set_integer ( VIK_SETTINGS_WIN_WIDTH, width );
1078 a_settings_set_integer ( VIK_SETTINGS_WIN_HEIGHT, height );
1081 a_settings_set_integer ( VIK_SETTINGS_WIN_PANE_POSITION, gtk_paned_get_position (GTK_PANED(vw->hpaned)) );
1084 a_settings_set_integer ( VIK_SETTINGS_WIN_SAVE_IMAGE_WIDTH, vw->draw_image_width );
1085 a_settings_set_integer ( VIK_SETTINGS_WIN_SAVE_IMAGE_HEIGHT, vw->draw_image_height );
1086 a_settings_set_boolean ( VIK_SETTINGS_WIN_SAVE_IMAGE_PNG, vw->draw_image_save_as_png );
1088 gchar *accel_file_name = g_build_filename ( a_get_viking_dir(), VIKING_ACCELERATOR_KEY_FILE, NULL );
1089 gtk_accel_map_save ( accel_file_name );
1090 g_free ( accel_file_name );
1097 static void newwindow_cb ( GtkAction *a, VikWindow *vw )
1099 g_signal_emit ( G_OBJECT(vw), window_signals[VW_NEWWINDOW_SIGNAL], 0 );
1102 static void draw_update ( VikWindow *vw )
1108 static void draw_sync ( VikWindow *vw )
1110 vik_viewport_sync(vw->viking_vvp);
1115 * Split the status update, as sometimes only need to update the tool part
1116 * also on initialization the zoom related stuff is not ready to be used
1118 static void draw_status_tool ( VikWindow *vw )
1120 if ( vw->current_tool == TOOL_LAYER )
1121 // Use tooltip rather than the internal name as the tooltip is i8n
1122 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 );
1124 vik_statusbar_set_message ( vw->viking_vs, VIK_STATUSBAR_TOOL, _(tool_names[vw->current_tool]) );
1127 static void draw_status ( VikWindow *vw )
1129 static gchar zoom_level[22];
1130 gdouble xmpp = vik_viewport_get_xmpp (vw->viking_vvp);
1131 gdouble ympp = vik_viewport_get_ympp(vw->viking_vvp);
1132 gchar *unit = vik_viewport_get_coord_mode(vw->viking_vvp) == VIK_COORD_UTM ? _("mpp") : _("pixelfact");
1134 g_snprintf ( zoom_level, 22, "%.3f/%.3f %s", xmpp, ympp, unit );
1136 if ( (int)xmpp - xmpp < 0.0 )
1137 g_snprintf ( zoom_level, 22, "%.3f %s", xmpp, unit );
1139 /* xmpp should be a whole number so don't show useless .000 bit */
1140 g_snprintf ( zoom_level, 22, "%d %s", (int)xmpp, unit );
1142 vik_statusbar_set_message ( vw->viking_vs, VIK_STATUSBAR_ZOOM, zoom_level );
1144 draw_status_tool ( vw );
1147 void vik_window_set_redraw_trigger(VikLayer *vl)
1149 VikWindow *vw = VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vl));
1154 static void window_configure_event ( VikWindow *vw )
1156 static int first = 1;
1159 // This is a hack to set the cursor corresponding to the first tool
1160 // FIXME find the correct way to initialize both tool and its cursor
1162 vw->viewport_cursor = (GdkCursor *)toolbox_get_cursor(vw->vt, "Pan");
1163 /* We set cursor, even if it is NULL: it resets to default */
1164 gdk_window_set_cursor ( gtk_widget_get_window(GTK_WIDGET(vw->viking_vvp)), vw->viewport_cursor );
1168 static void draw_redraw ( VikWindow *vw )
1170 VikCoord old_center = vw->trigger_center;
1171 vw->trigger_center = *(vik_viewport_get_center(vw->viking_vvp));
1172 VikLayer *new_trigger = vw->trigger;
1174 VikLayer *old_trigger = VIK_LAYER(vik_viewport_get_trigger(vw->viking_vvp));
1176 if ( ! new_trigger )
1177 ; /* do nothing -- have to redraw everything. */
1178 else if ( (old_trigger != new_trigger) || !vik_coord_equals(&old_center, &vw->trigger_center) || (new_trigger->type == VIK_LAYER_AGGREGATE) )
1179 vik_viewport_set_trigger ( vw->viking_vvp, new_trigger ); /* todo: set to half_drawn mode if new trigger is above old */
1181 vik_viewport_set_half_drawn ( vw->viking_vvp, TRUE );
1184 vik_viewport_clear ( vw->viking_vvp);
1185 // Main layer drawing
1186 vik_layers_panel_draw_all ( vw->viking_vlp );
1187 // Draw highlight (possibly again but ensures it is on top - especially for when tracks overlap)
1188 if ( vik_viewport_get_draw_highlight (vw->viking_vvp) ) {
1189 if ( vw->containing_vtl && (vw->selected_tracks || vw->selected_waypoints ) ) {
1190 vik_trw_layer_draw_highlight_items ( vw->containing_vtl, vw->selected_tracks, vw->selected_waypoints, vw->viking_vvp );
1192 else if ( vw->containing_vtl && (vw->selected_track || vw->selected_waypoint) ) {
1193 vik_trw_layer_draw_highlight_item ( vw->containing_vtl, vw->selected_track, vw->selected_waypoint, vw->viking_vvp );
1195 else if ( vw->selected_vtl ) {
1196 vik_trw_layer_draw_highlight ( vw->selected_vtl, vw->viking_vvp );
1199 // Other viewport decoration items on top if they are enabled/in use
1200 vik_viewport_draw_scale ( vw->viking_vvp );
1201 vik_viewport_draw_copyright ( vw->viking_vvp );
1202 vik_viewport_draw_centermark ( vw->viking_vvp );
1203 vik_viewport_draw_logo ( vw->viking_vvp );
1205 vik_viewport_set_half_drawn ( vw->viking_vvp, FALSE ); /* just in case. */
1208 gboolean draw_buf_done = TRUE;
1210 static gboolean draw_buf(gpointer data)
1212 gpointer *pass_along = data;
1213 gdk_threads_enter();
1214 gdk_draw_drawable (pass_along[0], pass_along[1],
1215 pass_along[2], 0, 0, 0, 0, -1, -1);
1216 draw_buf_done = TRUE;
1217 gdk_threads_leave();
1222 /* Mouse event handlers ************************************************************************/
1224 static void vik_window_pan_click (VikWindow *vw, GdkEventButton *event)
1226 /* set panning origin */
1227 vw->pan_move = FALSE;
1228 vw->pan_x = (gint) event->x;
1229 vw->pan_y = (gint) event->y;
1232 static void draw_click (VikWindow *vw, GdkEventButton *event)
1234 gtk_widget_grab_focus ( GTK_WIDGET(vw->viking_vvp) );
1236 /* middle button pressed. we reserve all middle button and scroll events
1237 * for panning and zooming; tools only get left/right/movement
1239 if ( event->button == 2) {
1240 if ( vw->vt->tools[vw->vt->active_tool].ti.pan_handler )
1241 // Tool still may need to do something (such as disable something)
1242 toolbox_click(vw->vt, event);
1243 vik_window_pan_click ( vw, event );
1246 toolbox_click(vw->vt, event);
1250 static void vik_window_pan_move (VikWindow *vw, GdkEventMotion *event)
1252 if ( vw->pan_x != -1 ) {
1253 vik_viewport_set_center_screen ( vw->viking_vvp, vik_viewport_get_width(vw->viking_vvp)/2 - event->x + vw->pan_x,
1254 vik_viewport_get_height(vw->viking_vvp)/2 - event->y + vw->pan_y );
1255 vw->pan_move = TRUE;
1256 vw->pan_x = event->x;
1257 vw->pan_y = event->y;
1263 * get_location_strings:
1265 * Utility function to get positional strings for the given location
1266 * lat and lon strings will get allocated and so need to be freed after use
1268 static void get_location_strings ( VikWindow *vw, struct UTM utm, gchar **lat, gchar **lon )
1270 if ( vik_viewport_get_drawmode ( vw->viking_vvp ) == VIK_VIEWPORT_DRAWMODE_UTM ) {
1271 // Reuse lat for the first part (Zone + N or S, and lon for the second part (easting and northing) of a UTM format:
1272 // ZONE[N|S] EASTING NORTHING
1273 *lat = g_malloc(4*sizeof(gchar));
1274 // NB zone is stored in a char but is an actual number
1275 g_snprintf (*lat, 4, "%d%c", utm.zone, utm.letter);
1276 *lon = g_malloc(16*sizeof(gchar));
1277 g_snprintf (*lon, 16, "%d %d", (gint)utm.easting, (gint)utm.northing);
1281 a_coords_utm_to_latlon ( &utm, &ll );
1282 a_coords_latlon_to_string ( &ll, lat, lon );
1286 static void draw_mouse_motion (VikWindow *vw, GdkEventMotion *event)
1288 static VikCoord coord;
1289 static struct UTM utm;
1290 #define BUFFER_SIZE 50
1291 static char pointer_buf[BUFFER_SIZE];
1292 gchar *lat = NULL, *lon = NULL;
1295 VikDemInterpol interpol_method;
1297 /* This is a hack, but work far the best, at least for single pointer systems.
1298 * See http://bugzilla.gnome.org/show_bug.cgi?id=587714 for more. */
1300 gdk_window_get_pointer (event->window, &x, &y, NULL);
1304 toolbox_move(vw->vt, event);
1306 vik_viewport_screen_to_coord ( vw->viking_vvp, event->x, event->y, &coord );
1307 vik_coord_to_utm ( &coord, &utm );
1309 get_location_strings ( vw, utm, &lat, &lon );
1311 /* Change interpolate method according to scale */
1312 zoom = vik_viewport_get_zoom(vw->viking_vvp);
1314 interpol_method = VIK_DEM_INTERPOL_NONE;
1315 else if (zoom >= 1.0)
1316 interpol_method = VIK_DEM_INTERPOL_SIMPLE;
1318 interpol_method = VIK_DEM_INTERPOL_BEST;
1319 if ((alt = a_dems_get_elev_by_coord(&coord, interpol_method)) != VIK_DEM_INVALID_ELEVATION) {
1320 if ( a_vik_get_units_height () == VIK_UNITS_HEIGHT_METRES )
1321 g_snprintf ( pointer_buf, BUFFER_SIZE, _("%s %s %dm"), lat, lon, alt );
1323 g_snprintf ( pointer_buf, BUFFER_SIZE, _("%s %s %dft"), lat, lon, (int)VIK_METERS_TO_FEET(alt) );
1326 g_snprintf ( pointer_buf, BUFFER_SIZE, _("%s %s"), lat, lon );
1331 vik_statusbar_set_message ( vw->viking_vs, VIK_STATUSBAR_POSITION, pointer_buf );
1333 vik_window_pan_move ( vw, event );
1335 /* This is recommended by the GTK+ documentation, but does not work properly.
1336 * Use deprecated way until GTK+ gets a solution for correct motion hint handling:
1337 * http://bugzilla.gnome.org/show_bug.cgi?id=587714
1339 /* gdk_event_request_motions ( event ); */
1343 * Action the single click after a small timeout
1344 * If a double click has occurred then this will do nothing
1346 static gboolean vik_window_pan_timeout (VikWindow *vw)
1348 if ( ! vw->single_click_pending ) {
1349 // Double click happened, so don't do anything
1353 /* set panning origin */
1354 vw->pan_move = FALSE;
1355 vw->single_click_pending = FALSE;
1356 vik_viewport_set_center_screen ( vw->viking_vvp, vw->delayed_pan_x, vw->delayed_pan_y );
1359 // Really turn off the pan moving!!
1360 vw->pan_x = vw->pan_y = -1;
1364 static void vik_window_pan_release ( VikWindow *vw, GdkEventButton *event )
1366 gboolean do_draw = TRUE;
1368 if ( vw->pan_move == FALSE ) {
1369 vw->single_click_pending = !vw->single_click_pending;
1371 if ( vw->single_click_pending ) {
1372 // Store offset to use
1373 vw->delayed_pan_x = vw->pan_x;
1374 vw->delayed_pan_y = vw->pan_y;
1375 // Get double click time
1376 GtkSettings *gs = gtk_widget_get_settings ( GTK_WIDGET(vw) );
1377 GValue dct = { 0 }; // = G_VALUE_INIT; // GLIB 2.30+ only
1378 g_value_init ( &dct, G_TYPE_INT );
1379 g_object_get_property ( G_OBJECT(gs), "gtk-double-click-time", &dct );
1380 // Give chance for a double click to occur
1381 gint timer = g_value_get_int ( &dct ) + 50;
1382 g_timeout_add ( timer, (GSourceFunc)vik_window_pan_timeout, vw );
1386 vik_viewport_set_center_screen ( vw->viking_vvp, vw->pan_x, vw->pan_y );
1390 vik_viewport_set_center_screen ( vw->viking_vvp, vik_viewport_get_width(vw->viking_vvp)/2 - event->x + vw->pan_x,
1391 vik_viewport_get_height(vw->viking_vvp)/2 - event->y + vw->pan_y );
1394 vw->pan_move = FALSE;
1395 vw->pan_x = vw->pan_y = -1;
1400 static void draw_release ( VikWindow *vw, GdkEventButton *event )
1402 gtk_widget_grab_focus ( GTK_WIDGET(vw->viking_vvp) );
1404 if ( event->button == 2 ) { /* move / pan */
1405 if ( vw->vt->tools[vw->vt->active_tool].ti.pan_handler )
1406 // Tool still may need to do something (such as reenable something)
1407 toolbox_release(vw->vt, event);
1408 vik_window_pan_release ( vw, event );
1411 toolbox_release(vw->vt, event);
1415 static void draw_scroll (VikWindow *vw, GdkEventScroll *event)
1417 guint modifiers = event->state & (GDK_SHIFT_MASK | GDK_CONTROL_MASK);
1418 if ( modifiers == GDK_CONTROL_MASK ) {
1419 /* control == pan up & down */
1420 if ( event->direction == GDK_SCROLL_UP )
1421 vik_viewport_set_center_screen ( vw->viking_vvp, vik_viewport_get_width(vw->viking_vvp)/2, vik_viewport_get_height(vw->viking_vvp)/3 );
1423 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 );
1424 } else if ( modifiers == GDK_SHIFT_MASK ) {
1425 /* shift == pan left & right */
1426 if ( event->direction == GDK_SCROLL_UP )
1427 vik_viewport_set_center_screen ( vw->viking_vvp, vik_viewport_get_width(vw->viking_vvp)/3, vik_viewport_get_height(vw->viking_vvp)/2 );
1429 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 );
1430 } else if ( modifiers == (GDK_CONTROL_MASK | GDK_SHIFT_MASK) ) {
1431 // This zoom is on the center position
1432 if ( event->direction == GDK_SCROLL_UP )
1433 vik_viewport_zoom_in (vw->viking_vvp);
1435 vik_viewport_zoom_out (vw->viking_vvp);
1437 /* make sure mouse is still over the same point on the map when we zoom */
1440 gint center_x = vik_viewport_get_width ( vw->viking_vvp ) / 2;
1441 gint center_y = vik_viewport_get_height ( vw->viking_vvp ) / 2;
1442 vik_viewport_screen_to_coord ( vw->viking_vvp, event->x, event->y, &coord );
1443 if ( event->direction == GDK_SCROLL_UP )
1444 vik_viewport_zoom_in (vw->viking_vvp);
1446 vik_viewport_zoom_out(vw->viking_vvp);
1447 vik_viewport_coord_to_screen ( vw->viking_vvp, &coord, &x, &y );
1448 vik_viewport_set_center_screen ( vw->viking_vvp, center_x + (x - event->x),
1449 center_y + (y - event->y) );
1457 /********************************************************************************
1459 ********************************************************************************/
1460 static void draw_ruler(VikViewport *vvp, GdkDrawable *d, GdkGC *gc, gint x1, gint y1, gint x2, gint y2, gdouble distance)
1464 GdkGC *labgc = vik_viewport_new_gc ( vvp, "#cccccc", 1);
1465 GdkGC *thickgc = gdk_gc_new(d);
1467 gdouble len = sqrt((x1-x2)*(x1-x2) + (y1-y2)*(y1-y2));
1468 gdouble dx = (x2-x1)/len*10;
1469 gdouble dy = (y2-y1)/len*10;
1470 gdouble c = cos(DEG2RAD(15.0));
1471 gdouble s = sin(DEG2RAD(15.0));
1473 gdouble baseangle = 0;
1476 /* draw line with arrow ends */
1478 gint tmp_x1=x1, tmp_y1=y1, tmp_x2=x2, tmp_y2=y2;
1479 a_viewport_clip_line(&tmp_x1, &tmp_y1, &tmp_x2, &tmp_y2);
1480 gdk_draw_line(d, gc, tmp_x1, tmp_y1, tmp_x2, tmp_y2);
1483 a_viewport_clip_line(&x1, &y1, &x2, &y2);
1484 gdk_draw_line(d, gc, x1, y1, x2, y2);
1486 gdk_draw_line(d, gc, x1 - dy, y1 + dx, x1 + dy, y1 - dx);
1487 gdk_draw_line(d, gc, x2 - dy, y2 + dx, x2 + dy, y2 - dx);
1488 gdk_draw_line(d, gc, x2, y2, x2 - (dx * c + dy * s), y2 - (dy * c - dx * s));
1489 gdk_draw_line(d, gc, x2, y2, x2 - (dx * c - dy * s), y2 - (dy * c + dx * s));
1490 gdk_draw_line(d, gc, x1, y1, x1 + (dx * c + dy * s), y1 + (dy * c - dx * s));
1491 gdk_draw_line(d, gc, x1, y1, x1 + (dx * c - dy * s), y1 + (dy * c + dx * s));
1497 vik_viewport_compute_bearing ( vvp, x1, y1, x2, y2, &angle, &baseangle );
1501 gdk_gc_copy(thickgc, gc);
1502 gdk_gc_set_line_attributes(thickgc, CW, GDK_LINE_SOLID, GDK_CAP_BUTT, GDK_JOIN_MITER);
1503 gdk_color_parse("#2255cc", &color);
1504 gdk_gc_set_rgb_fg_color(thickgc, &color);
1506 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);
1509 gdk_gc_copy(thickgc, gc);
1510 gdk_gc_set_line_attributes(thickgc, 2, GDK_LINE_SOLID, GDK_CAP_BUTT, GDK_JOIN_MITER);
1511 for (i=0; i<180; i++) {
1512 c = cos(DEG2RAD(i)*2 + baseangle);
1513 s = sin(DEG2RAD(i)*2 + baseangle);
1516 gdk_draw_line (d, gc, x1 + CR*c, y1 + CR*s, x1 + (CR+CW)*c, y1 + (CR+CW)*s);
1518 gdouble ticksize = 2*CW;
1519 gdk_draw_line (d, thickgc, x1 + (CR-CW)*c, y1 + (CR-CW)*s, x1 + (CR+ticksize)*c, y1 + (CR+ticksize)*s);
1523 gdk_draw_arc (d, gc, FALSE, x1-CR, y1-CR, 2*CR, 2*CR, 0, 64*360);
1524 gdk_draw_arc (d, gc, FALSE, x1-CR-CW, y1-CR-CW, 2*(CR+CW), 2*(CR+CW), 0, 64*360);
1525 gdk_draw_arc (d, gc, FALSE, x1-CR+CW, y1-CR+CW, 2*(CR-CW), 2*(CR-CW), 0, 64*360);
1526 c = (CR+CW*2)*cos(baseangle);
1527 s = (CR+CW*2)*sin(baseangle);
1528 gdk_draw_line (d, gc, x1-c, y1-s, x1+c, y1+s);
1529 gdk_draw_line (d, gc, x1+s, y1-c, x1-s, y1+c);
1532 #define LABEL(x, y, w, h) { \
1533 gdk_draw_rectangle(d, labgc, TRUE, (x)-2, (y)-1, (w)+4, (h)+1); \
1534 gdk_draw_rectangle(d, gc, FALSE, (x)-2, (y)-1, (w)+4, (h)+1); \
1535 gdk_draw_layout(d, gc, (x), (y), pl); }
1537 gint wd, hd, xd, yd;
1538 gint wb, hb, xb, yb;
1540 pl = gtk_widget_create_pango_layout (GTK_WIDGET(vvp), NULL);
1541 pango_layout_set_font_description (pl, gtk_widget_get_style(GTK_WIDGET(vvp))->font_desc);
1542 pango_layout_set_text(pl, "N", -1);
1543 gdk_draw_layout(d, gc, x1-5, y1-CR-3*CW-8, pl);
1545 /* draw label with distance */
1546 vik_units_distance_t dist_units = a_vik_get_units_distance ();
1547 switch (dist_units) {
1548 case VIK_UNITS_DISTANCE_KILOMETRES:
1549 if (distance >= 1000 && distance < 100000) {
1550 g_sprintf(str, "%3.2f km", distance/1000.0);
1551 } else if (distance < 1000) {
1552 g_sprintf(str, "%d m", (int)distance);
1554 g_sprintf(str, "%d km", (int)distance/1000);
1557 case VIK_UNITS_DISTANCE_MILES:
1558 if (distance >= VIK_MILES_TO_METERS(1) && distance < VIK_MILES_TO_METERS(100)) {
1559 g_sprintf(str, "%3.2f miles", VIK_METERS_TO_MILES(distance));
1560 } else if (distance < VIK_MILES_TO_METERS(1)) {
1561 g_sprintf(str, "%d yards", (int)(distance*1.0936133));
1563 g_sprintf(str, "%d miles", (int)VIK_METERS_TO_MILES(distance));
1566 case VIK_UNITS_DISTANCE_NAUTICAL_MILES:
1567 if (distance >= VIK_NAUTICAL_MILES_TO_METERS(1) && distance < VIK_NAUTICAL_MILES_TO_METERS(100)) {
1568 g_sprintf(str, "%3.2f NM", VIK_METERS_TO_NAUTICAL_MILES(distance));
1569 } else if (distance < VIK_NAUTICAL_MILES_TO_METERS(1)) {
1570 g_sprintf(str, "%d yards", (int)(distance*1.0936133));
1572 g_sprintf(str, "%d NM", (int)VIK_METERS_TO_NAUTICAL_MILES(distance));
1576 g_critical("Houston, we've had a problem. distance=%d", dist_units);
1579 pango_layout_set_text(pl, str, -1);
1581 pango_layout_get_pixel_size ( pl, &wd, &hd );
1583 xd = (x1+x2)/2 + dy;
1584 yd = (y1+y2)/2 - hd/2 - dx;
1586 xd = (x1+x2)/2 - dy;
1587 yd = (y1+y2)/2 - hd/2 + dx;
1590 if ( xd < -5 || yd < -5 || xd > vik_viewport_get_width(vvp)+5 || yd > vik_viewport_get_height(vvp)+5 ) {
1595 LABEL(xd, yd, wd, hd);
1597 /* draw label with bearing */
1598 g_sprintf(str, "%3.1f°", RAD2DEG(angle));
1599 pango_layout_set_text(pl, str, -1);
1600 pango_layout_get_pixel_size ( pl, &wb, &hb );
1601 xb = x1 + CR*cos(angle-M_PI_2);
1602 yb = y1 + CR*sin(angle-M_PI_2);
1604 if ( xb < -5 || yb < -5 || xb > vik_viewport_get_width(vvp)+5 || yb > vik_viewport_get_height(vvp)+5 ) {
1610 GdkRectangle r1 = {xd-2, yd-1, wd+4, hd+1}, r2 = {xb-2, yb-1, wb+4, hb+1};
1611 if (gdk_rectangle_intersect(&r1, &r2, &r2)) {
1615 LABEL(xb, yb, wb, hb);
1619 g_object_unref ( G_OBJECT ( pl ) );
1620 g_object_unref ( G_OBJECT ( labgc ) );
1621 g_object_unref ( G_OBJECT ( thickgc ) );
1627 gboolean has_oldcoord;
1629 } ruler_tool_state_t;
1631 static gpointer ruler_create (VikWindow *vw, VikViewport *vvp)
1633 ruler_tool_state_t *s = g_new(ruler_tool_state_t, 1);
1636 s->has_oldcoord = FALSE;
1640 static void ruler_destroy (ruler_tool_state_t *s)
1645 static VikLayerToolFuncStatus ruler_click (VikLayer *vl, GdkEventButton *event, ruler_tool_state_t *s)
1650 if ( event->button == 1 ) {
1651 gchar *lat=NULL, *lon=NULL;
1652 vik_viewport_screen_to_coord ( s->vvp, (gint) event->x, (gint) event->y, &coord );
1653 vik_coord_to_latlon ( &coord, &ll );
1654 a_coords_latlon_to_string ( &ll, &lat, &lon );
1655 if ( s->has_oldcoord ) {
1656 vik_units_distance_t dist_units = a_vik_get_units_distance ();
1657 switch (dist_units) {
1658 case VIK_UNITS_DISTANCE_KILOMETRES:
1659 temp = g_strdup_printf ( "%s %s DIFF %f meters", lat, lon, vik_coord_diff( &coord, &(s->oldcoord) ) );
1661 case VIK_UNITS_DISTANCE_MILES:
1662 temp = g_strdup_printf ( "%s %s DIFF %f miles", lat, lon, VIK_METERS_TO_MILES(vik_coord_diff( &coord, &(s->oldcoord) )) );
1664 case VIK_UNITS_DISTANCE_NAUTICAL_MILES:
1665 temp = g_strdup_printf ( "%s %s DIFF %f NM", lat, lon, VIK_METERS_TO_NAUTICAL_MILES(vik_coord_diff( &coord, &(s->oldcoord) )) );
1668 temp = g_strdup_printf ("Just to keep the compiler happy");
1669 g_critical("Houston, we've had a problem. distance=%d", dist_units);
1672 s->has_oldcoord = FALSE;
1675 temp = g_strdup_printf ( "%s %s", lat, lon );
1676 s->has_oldcoord = TRUE;
1679 vik_statusbar_set_message ( s->vw->viking_vs, VIK_STATUSBAR_INFO, temp );
1682 s->oldcoord = coord;
1685 vik_viewport_set_center_screen ( s->vvp, (gint) event->x, (gint) event->y );
1686 draw_update ( s->vw );
1688 return VIK_LAYER_TOOL_ACK;
1691 static VikLayerToolFuncStatus ruler_move (VikLayer *vl, GdkEventMotion *event, ruler_tool_state_t *s)
1693 VikViewport *vvp = s->vvp;
1694 VikWindow *vw = s->vw;
1700 if ( s->has_oldcoord ) {
1701 int oldx, oldy, w1, h1, w2, h2;
1702 static GdkPixmap *buf = NULL;
1703 gchar *lat=NULL, *lon=NULL;
1704 w1 = vik_viewport_get_width(vvp);
1705 h1 = vik_viewport_get_height(vvp);
1707 buf = gdk_pixmap_new ( gtk_widget_get_window(GTK_WIDGET(vvp)), w1, h1, -1 );
1709 gdk_drawable_get_size(buf, &w2, &h2);
1710 if (w1 != w2 || h1 != h2) {
1711 g_object_unref ( G_OBJECT ( buf ) );
1712 buf = gdk_pixmap_new ( gtk_widget_get_window(GTK_WIDGET(vvp)), w1, h1, -1 );
1715 vik_viewport_screen_to_coord ( vvp, (gint) event->x, (gint) event->y, &coord );
1716 vik_coord_to_latlon ( &coord, &ll );
1717 vik_viewport_coord_to_screen ( vvp, &s->oldcoord, &oldx, &oldy );
1719 gdk_draw_drawable (buf, gtk_widget_get_style(GTK_WIDGET(vvp))->black_gc,
1720 vik_viewport_get_pixmap(vvp), 0, 0, 0, 0, -1, -1);
1721 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)) );
1722 if (draw_buf_done) {
1723 static gpointer pass_along[3];
1724 pass_along[0] = gtk_widget_get_window(GTK_WIDGET(vvp));
1725 pass_along[1] = gtk_widget_get_style(GTK_WIDGET(vvp))->black_gc;
1726 pass_along[2] = buf;
1727 g_idle_add_full (G_PRIORITY_HIGH_IDLE + 10, draw_buf, pass_along, NULL);
1728 draw_buf_done = FALSE;
1730 a_coords_latlon_to_string(&ll, &lat, &lon);
1731 vik_units_distance_t dist_units = a_vik_get_units_distance ();
1732 switch (dist_units) {
1733 case VIK_UNITS_DISTANCE_KILOMETRES:
1734 temp = g_strdup_printf ( "%s %s DIFF %f meters", lat, lon, vik_coord_diff( &coord, &(s->oldcoord) ) );
1736 case VIK_UNITS_DISTANCE_MILES:
1737 temp = g_strdup_printf ( "%s %s DIFF %f miles", lat, lon, VIK_METERS_TO_MILES (vik_coord_diff( &coord, &(s->oldcoord) )) );
1739 case VIK_UNITS_DISTANCE_NAUTICAL_MILES:
1740 temp = g_strdup_printf ( "%s %s DIFF %f NM", lat, lon, VIK_METERS_TO_NAUTICAL_MILES (vik_coord_diff( &coord, &(s->oldcoord) )) );
1743 temp = g_strdup_printf ("Just to keep the compiler happy");
1744 g_critical("Houston, we've had a problem. distance=%d", dist_units);
1746 vik_statusbar_set_message ( vw->viking_vs, VIK_STATUSBAR_INFO, temp );
1749 return VIK_LAYER_TOOL_ACK;
1752 static VikLayerToolFuncStatus ruler_release (VikLayer *vl, GdkEventButton *event, ruler_tool_state_t *s)
1754 return VIK_LAYER_TOOL_ACK;
1757 static void ruler_deactivate (VikLayer *vl, ruler_tool_state_t *s)
1759 draw_update ( s->vw );
1762 static gboolean ruler_key_press (VikLayer *vl, GdkEventKey *event, ruler_tool_state_t *s)
1764 if (event->keyval == GDK_Escape) {
1765 s->has_oldcoord = FALSE;
1766 ruler_deactivate ( vl, s );
1769 // Regardless of whether we used it, return false so other GTK things may use it
1773 static VikToolInterface ruler_tool =
1774 // NB Ctrl+Shift+R is used for Refresh (deemed more important), so use 'U' instead
1775 { { "Ruler", "vik-icon-ruler", N_("_Ruler"), "<control><shift>U", N_("Ruler Tool"), 2 },
1776 (VikToolConstructorFunc) ruler_create,
1777 (VikToolDestructorFunc) ruler_destroy,
1778 (VikToolActivationFunc) NULL,
1779 (VikToolActivationFunc) ruler_deactivate,
1780 (VikToolMouseFunc) ruler_click,
1781 (VikToolMouseMoveFunc) ruler_move,
1782 (VikToolMouseFunc) ruler_release,
1783 (VikToolKeyFunc) ruler_key_press,
1785 GDK_CURSOR_IS_PIXMAP,
1786 &cursor_ruler_pixbuf,
1788 /*** end ruler code ********************************************************/
1792 /********************************************************************************
1794 ********************************************************************************/
1799 // Track zoom bounds for zoom tool with shift modifier:
1800 gboolean bounds_active;
1803 } zoom_tool_state_t;
1806 * In case the screen size has changed
1808 static void zoomtool_resize_pixmap (zoom_tool_state_t *zts)
1812 // Allocate a drawing area the size of the viewport
1813 w1 = vik_viewport_get_width ( zts->vw->viking_vvp );
1814 h1 = vik_viewport_get_height ( zts->vw->viking_vvp );
1816 if ( !zts->pixmap ) {
1818 zts->pixmap = gdk_pixmap_new ( gtk_widget_get_window(GTK_WIDGET(zts->vw->viking_vvp)), w1, h1, -1 );
1821 gdk_drawable_get_size ( zts->pixmap, &w2, &h2 );
1823 if ( w1 != w2 || h1 != h2 ) {
1824 // Has changed - delete and recreate with new values
1825 g_object_unref ( G_OBJECT ( zts->pixmap ) );
1826 zts->pixmap = gdk_pixmap_new ( gtk_widget_get_window(GTK_WIDGET(zts->vw->viking_vvp)), w1, h1, -1 );
1830 static gpointer zoomtool_create (VikWindow *vw, VikViewport *vvp)
1832 zoom_tool_state_t *zts = g_new(zoom_tool_state_t, 1);
1837 zts->bounds_active = FALSE;
1841 static void zoomtool_destroy ( zoom_tool_state_t *zts)
1844 g_object_unref ( G_OBJECT ( zts->pixmap ) );
1848 static VikLayerToolFuncStatus zoomtool_click (VikLayer *vl, GdkEventButton *event, zoom_tool_state_t *zts)
1850 zts->vw->modified = TRUE;
1851 guint modifiers = event->state & (GDK_SHIFT_MASK | GDK_CONTROL_MASK);
1855 gint center_x = vik_viewport_get_width ( zts->vw->viking_vvp ) / 2;
1856 gint center_y = vik_viewport_get_height ( zts->vw->viking_vvp ) / 2;
1858 gboolean skip_update = FALSE;
1860 zts->bounds_active = FALSE;
1862 if ( modifiers == (GDK_CONTROL_MASK | GDK_SHIFT_MASK) ) {
1863 // This zoom is on the center position
1864 vik_viewport_set_center_screen ( zts->vw->viking_vvp, center_x, center_y );
1865 if ( event->button == 1 )
1866 vik_viewport_zoom_in (zts->vw->viking_vvp);
1867 else if ( event->button == 3 )
1868 vik_viewport_zoom_out (zts->vw->viking_vvp);
1870 else if ( modifiers == GDK_CONTROL_MASK ) {
1871 // This zoom is to recenter on the mouse position
1872 vik_viewport_set_center_screen ( zts->vw->viking_vvp, (gint) event->x, (gint) event->y );
1873 if ( event->button == 1 )
1874 vik_viewport_zoom_in (zts->vw->viking_vvp);
1875 else if ( event->button == 3 )
1876 vik_viewport_zoom_out (zts->vw->viking_vvp);
1878 else if ( modifiers == GDK_SHIFT_MASK ) {
1879 // Get start of new zoom bounds
1880 if ( event->button == 1 ) {
1881 zts->bounds_active = TRUE;
1882 zts->start_x = (gint) event->x;
1883 zts->start_y = (gint) event->y;
1888 /* make sure mouse is still over the same point on the map when we zoom */
1889 vik_viewport_screen_to_coord ( zts->vw->viking_vvp, event->x, event->y, &coord );
1890 if ( event->button == 1 )
1891 vik_viewport_zoom_in (zts->vw->viking_vvp);
1892 else if ( event->button == 3 )
1893 vik_viewport_zoom_out(zts->vw->viking_vvp);
1894 vik_viewport_coord_to_screen ( zts->vw->viking_vvp, &coord, &x, &y );
1895 vik_viewport_set_center_screen ( zts->vw->viking_vvp,
1896 center_x + (x - event->x),
1897 center_y + (y - event->y) );
1901 draw_update ( zts->vw );
1903 return VIK_LAYER_TOOL_ACK;
1906 static VikLayerToolFuncStatus zoomtool_move (VikLayer *vl, GdkEventMotion *event, zoom_tool_state_t *zts)
1908 guint modifiers = event->state & (GDK_SHIFT_MASK | GDK_CONTROL_MASK);
1910 if ( zts->bounds_active && modifiers == GDK_SHIFT_MASK ) {
1911 zoomtool_resize_pixmap ( zts );
1913 // Blank out currently drawn area
1914 gdk_draw_drawable ( zts->pixmap,
1915 gtk_widget_get_style(GTK_WIDGET(zts->vw->viking_vvp))->black_gc,
1916 vik_viewport_get_pixmap(zts->vw->viking_vvp),
1917 0, 0, 0, 0, -1, -1);
1919 // Calculate new box starting point & size in pixels
1920 int xx, yy, width, height;
1921 if ( event->y > zts->start_y ) {
1923 height = event->y-zts->start_y;
1927 height = zts->start_y-event->y;
1929 if ( event->x > zts->start_x ) {
1931 width = event->x-zts->start_x;
1935 width = zts->start_x-event->x;
1939 gdk_draw_rectangle (zts->pixmap, gtk_widget_get_style(GTK_WIDGET(zts->vw->viking_vvp))->black_gc, FALSE, xx, yy, width, height);
1941 // Only actually draw when there's time to do so
1942 if (draw_buf_done) {
1943 static gpointer pass_along[3];
1944 pass_along[0] = gtk_widget_get_window(GTK_WIDGET(zts->vw->viking_vvp));
1945 pass_along[1] = gtk_widget_get_style(GTK_WIDGET(zts->vw->viking_vvp))->black_gc;
1946 pass_along[2] = zts->pixmap;
1947 g_idle_add_full (G_PRIORITY_HIGH_IDLE + 10, draw_buf, pass_along, NULL);
1948 draw_buf_done = FALSE;
1952 zts->bounds_active = FALSE;
1954 return VIK_LAYER_TOOL_ACK;
1957 static VikLayerToolFuncStatus zoomtool_release (VikLayer *vl, GdkEventButton *event, zoom_tool_state_t *zts)
1959 guint modifiers = event->state & (GDK_SHIFT_MASK | GDK_CONTROL_MASK);
1961 // Ensure haven't just released on the exact same position
1962 // i.e. probably haven't moved the mouse at all
1963 if ( zts->bounds_active && modifiers == GDK_SHIFT_MASK &&
1964 ( event->x < zts->start_x-5 || event->x > zts->start_x+5 ) &&
1965 ( event->y < zts->start_y-5 || event->y > zts->start_y+5 ) ) {
1967 VikCoord coord1, coord2;
1968 vik_viewport_screen_to_coord ( zts->vw->viking_vvp, zts->start_x, zts->start_y, &coord1);
1969 vik_viewport_screen_to_coord ( zts->vw->viking_vvp, event->x, event->y, &coord2);
1971 // From the extend of the bounds pick the best zoom level
1972 // c.f. trw_layer_zoom_to_show_latlons()
1973 // Maybe refactor...
1974 struct LatLon ll1, ll2;
1975 vik_coord_to_latlon(&coord1, &ll1);
1976 vik_coord_to_latlon(&coord2, &ll2);
1977 struct LatLon average = { (ll1.lat+ll2.lat)/2,
1978 (ll1.lon+ll2.lon)/2 };
1980 VikCoord new_center;
1981 vik_coord_load_from_latlon ( &new_center, vik_viewport_get_coord_mode ( zts->vw->viking_vvp ), &average );
1982 vik_viewport_set_center_coord ( zts->vw->viking_vvp, &new_center, FALSE );
1984 /* Convert into definite 'smallest' and 'largest' positions */
1985 struct LatLon minmin;
1986 if ( ll1.lat < ll2.lat )
1987 minmin.lat = ll1.lat;
1989 minmin.lat = ll2.lat;
1991 struct LatLon maxmax;
1992 if ( ll1.lon > ll2.lon )
1993 maxmax.lon = ll1.lon;
1995 maxmax.lon = ll2.lon;
1997 /* Always recalculate the 'best' zoom level */
1998 gdouble zoom = VIK_VIEWPORT_MIN_ZOOM;
1999 vik_viewport_set_zoom ( zts->vw->viking_vvp, zoom );
2001 gdouble min_lat, max_lat, min_lon, max_lon;
2002 /* Should only be a maximum of about 18 iterations from min to max zoom levels */
2003 while ( zoom <= VIK_VIEWPORT_MAX_ZOOM ) {
2004 vik_viewport_get_min_max_lat_lon ( zts->vw->viking_vvp, &min_lat, &max_lat, &min_lon, &max_lon );
2005 /* NB I think the logic used in this test to determine if the bounds is within view
2006 fails if track goes across 180 degrees longitude.
2007 Hopefully that situation is not too common...
2008 Mind you viking doesn't really do edge locations to well anyway */
2009 if ( min_lat < minmin.lat &&
2010 max_lat > minmin.lat &&
2011 min_lon < maxmax.lon &&
2012 max_lon > maxmax.lon )
2013 /* Found within zoom level */
2018 vik_viewport_set_zoom ( zts->vw->viking_vvp, zoom );
2022 // When pressing shift and clicking for zoom, then jump three levels
2023 if ( modifiers == GDK_SHIFT_MASK ) {
2024 // Zoom in/out by three if possible
2025 vik_viewport_set_center_screen ( zts->vw->viking_vvp, event->x, event->y );
2026 if ( event->button == 1 ) {
2027 vik_viewport_zoom_in ( zts->vw->viking_vvp );
2028 vik_viewport_zoom_in ( zts->vw->viking_vvp );
2029 vik_viewport_zoom_in ( zts->vw->viking_vvp );
2031 else if ( event->button == 3 ) {
2032 vik_viewport_zoom_out ( zts->vw->viking_vvp );
2033 vik_viewport_zoom_out ( zts->vw->viking_vvp );
2034 vik_viewport_zoom_out ( zts->vw->viking_vvp );
2039 draw_update ( zts->vw );
2042 zts->bounds_active = FALSE;
2044 return VIK_LAYER_TOOL_ACK;
2047 static VikToolInterface zoom_tool =
2048 { { "Zoom", "vik-icon-zoom", N_("_Zoom"), "<control><shift>Z", N_("Zoom Tool"), 1 },
2049 (VikToolConstructorFunc) zoomtool_create,
2050 (VikToolDestructorFunc) zoomtool_destroy,
2051 (VikToolActivationFunc) NULL,
2052 (VikToolActivationFunc) NULL,
2053 (VikToolMouseFunc) zoomtool_click,
2054 (VikToolMouseMoveFunc) zoomtool_move,
2055 (VikToolMouseFunc) zoomtool_release,
2058 GDK_CURSOR_IS_PIXMAP,
2059 &cursor_zoom_pixbuf,
2061 /*** end zoom code ********************************************************/
2063 /********************************************************************************
2065 ********************************************************************************/
2066 static gpointer pantool_create (VikWindow *vw, VikViewport *vvp)
2071 // NB Double clicking means this gets called THREE times!!!
2072 static VikLayerToolFuncStatus pantool_click (VikLayer *vl, GdkEventButton *event, VikWindow *vw)
2074 vw->modified = TRUE;
2076 if ( event->type == GDK_2BUTTON_PRESS ) {
2077 // Zoom in / out on double click
2078 // No need to change the center as that has already occurred in the first click of a double click occurrence
2079 if ( event->button == 1 ) {
2080 guint modifier = event->state & GDK_SHIFT_MASK;
2082 vik_viewport_zoom_out ( vw->viking_vvp );
2084 vik_viewport_zoom_in ( vw->viking_vvp );
2086 else if ( event->button == 3 )
2087 vik_viewport_zoom_out ( vw->viking_vvp );
2092 // Standard pan click
2093 if ( event->button == 1 )
2094 vik_window_pan_click ( vw, event );
2096 return VIK_LAYER_TOOL_ACK;
2099 static VikLayerToolFuncStatus pantool_move (VikLayer *vl, GdkEventMotion *event, VikWindow *vw)
2101 vik_window_pan_move ( vw, event );
2102 return VIK_LAYER_TOOL_ACK;
2105 static VikLayerToolFuncStatus pantool_release (VikLayer *vl, GdkEventButton *event, VikWindow *vw)
2107 if ( event->button == 1 )
2108 vik_window_pan_release ( vw, event );
2109 return VIK_LAYER_TOOL_ACK;
2112 static VikToolInterface pan_tool =
2113 { { "Pan", "vik-icon-pan", N_("_Pan"), "<control><shift>P", N_("Pan Tool"), 0 },
2114 (VikToolConstructorFunc) pantool_create,
2115 (VikToolDestructorFunc) NULL,
2116 (VikToolActivationFunc) NULL,
2117 (VikToolActivationFunc) NULL,
2118 (VikToolMouseFunc) pantool_click,
2119 (VikToolMouseMoveFunc) pantool_move,
2120 (VikToolMouseFunc) pantool_release,
2126 /*** end pan code ********************************************************/
2128 /********************************************************************************
2130 ********************************************************************************/
2131 static gpointer selecttool_create (VikWindow *vw, VikViewport *vvp)
2133 tool_ed_t *t = g_new(tool_ed_t, 1);
2137 t->is_waypoint = FALSE;
2141 static void selecttool_destroy (tool_ed_t *t)
2149 GdkEventButton *event;
2150 tool_ed_t *tool_edit;
2153 static void click_layer_selected (VikLayer *vl, clicker *ck)
2155 /* Do nothing when function call returns true; */
2156 /* i.e. stop on first found item */
2159 if ( vik_layer_get_interface(vl->type)->select_click )
2160 ck->cont = !vik_layer_get_interface(vl->type)->select_click ( vl, ck->event, ck->vvp, ck->tool_edit );
2164 // Hopefully Alt keys by default
2165 #define VIK_MOVE_MODIFIER GDK_MOD1_MASK
2167 // Alt+mouse on Linux desktops tend to be used by the desktop manager
2168 // Thus use an alternate modifier - you may need to set something into this group
2169 #define VIK_MOVE_MODIFIER GDK_MOD5_MASK
2172 static VikLayerToolFuncStatus selecttool_click (VikLayer *vl, GdkEventButton *event, tool_ed_t *t)
2174 t->vw->select_move = FALSE;
2175 /* Only allow selection on primary button */
2176 if ( event->button == 1 ) {
2178 if ( event->state & VIK_MOVE_MODIFIER )
2179 vik_window_pan_click ( t->vw, event );
2181 /* Enable click to apply callback to potentially all track/waypoint layers */
2182 /* Useful as we can find things that aren't necessarily in the currently selected layer */
2183 GList* gl = vik_layers_panel_get_all_layers_of_type ( t->vw->viking_vlp, VIK_LAYER_TRW, FALSE ); // Don't get invisible layers
2186 ck.vvp = t->vw->viking_vvp;
2189 g_list_foreach ( gl, (GFunc) click_layer_selected, &ck );
2192 // If nothing found then deselect & redraw screen if necessary to remove the highlight
2195 VikTreeview *vtv = vik_layers_panel_get_treeview ( t->vw->viking_vlp );
2197 if ( vik_treeview_get_selected_iter ( vtv, &iter ) ) {
2198 // Only clear if selected thing is a TrackWaypoint layer or a sublayer
2199 gint type = vik_treeview_item_get_type ( vtv, &iter );
2200 if ( type == VIK_TREEVIEW_TYPE_SUBLAYER ||
2201 VIK_LAYER(vik_treeview_item_get_pointer ( vtv, &iter ))->type == VIK_LAYER_TRW ) {
2203 vik_treeview_item_unselect ( vtv, &iter );
2204 if ( vik_window_clear_highlight ( t->vw ) )
2205 draw_update ( t->vw );
2210 // Something found - so enable movement
2211 t->vw->select_move = TRUE;
2215 else if ( ( event->button == 3 ) && ( vl && ( vl->type == VIK_LAYER_TRW ) ) ) {
2217 /* Act on currently selected item to show menu */
2218 if ( t->vw->selected_track || t->vw->selected_waypoint )
2219 if ( vik_layer_get_interface(vl->type)->show_viewport_menu )
2220 vik_layer_get_interface(vl->type)->show_viewport_menu ( vl, event, t->vw->viking_vvp );
2223 return VIK_LAYER_TOOL_ACK;
2226 static VikLayerToolFuncStatus selecttool_move (VikLayer *vl, GdkEventMotion *event, tool_ed_t *t)
2228 if ( t->vw->select_move ) {
2229 // Don't care about vl here
2231 if ( vik_layer_get_interface(VIK_LAYER_TRW)->select_move )
2232 vik_layer_get_interface(VIK_LAYER_TRW)->select_move ( vl, event, t->vvp, t );
2236 if ( event->state & VIK_MOVE_MODIFIER )
2237 vik_window_pan_move ( t->vw, event );
2239 return VIK_LAYER_TOOL_ACK;
2242 static VikLayerToolFuncStatus selecttool_release (VikLayer *vl, GdkEventButton *event, tool_ed_t *t)
2244 if ( t->vw->select_move ) {
2245 // Don't care about vl here
2247 if ( vik_layer_get_interface(VIK_LAYER_TRW)->select_release )
2248 vik_layer_get_interface(VIK_LAYER_TRW)->select_release ( (VikLayer*)t->vtl, event, t->vvp, t );
2251 if ( event->button == 1 && (event->state & VIK_MOVE_MODIFIER) )
2252 vik_window_pan_release ( t->vw, event );
2254 // Force pan off incase it was on
2255 t->vw->pan_move = FALSE;
2256 t->vw->pan_x = t->vw->pan_y = -1;
2258 // End of this select movement
2259 t->vw->select_move = FALSE;
2261 return VIK_LAYER_TOOL_ACK;
2264 static VikToolInterface select_tool =
2265 { { "Select", "vik-icon-select", N_("_Select"), "<control><shift>S", N_("Select Tool"), 3 },
2266 (VikToolConstructorFunc) selecttool_create,
2267 (VikToolDestructorFunc) selecttool_destroy,
2268 (VikToolActivationFunc) NULL,
2269 (VikToolActivationFunc) NULL,
2270 (VikToolMouseFunc) selecttool_click,
2271 (VikToolMouseMoveFunc) selecttool_move,
2272 (VikToolMouseFunc) selecttool_release,
2273 (VikToolKeyFunc) NULL,
2278 /*** end select tool code ********************************************************/
2280 static void draw_pan_cb ( GtkAction *a, VikWindow *vw )
2282 // Since the treeview cell editting intercepts standard keyboard handlers, it means we can receive events here
2283 // Thus if currently editting, ensure we don't move the viewport when Ctrl+<arrow> is received
2284 VikLayer *sel = vik_layers_panel_get_selected ( vw->viking_vlp );
2285 if ( sel && vik_treeview_get_editing ( sel->vt ) )
2288 if (!strcmp(gtk_action_get_name(a), "PanNorth")) {
2289 vik_viewport_set_center_screen ( vw->viking_vvp, vik_viewport_get_width(vw->viking_vvp)/2, 0 );
2290 } else if (!strcmp(gtk_action_get_name(a), "PanEast")) {
2291 vik_viewport_set_center_screen ( vw->viking_vvp, vik_viewport_get_width(vw->viking_vvp), vik_viewport_get_height(vw->viking_vvp)/2 );
2292 } else if (!strcmp(gtk_action_get_name(a), "PanSouth")) {
2293 vik_viewport_set_center_screen ( vw->viking_vvp, vik_viewport_get_width(vw->viking_vvp)/2, vik_viewport_get_height(vw->viking_vvp) );
2294 } else if (!strcmp(gtk_action_get_name(a), "PanWest")) {
2295 vik_viewport_set_center_screen ( vw->viking_vvp, 0, vik_viewport_get_height(vw->viking_vvp)/2 );
2300 static void draw_zoom_cb ( GtkAction *a, VikWindow *vw )
2304 if (!strcmp(gtk_action_get_name(a), "ZoomIn")) {
2307 else if (!strcmp(gtk_action_get_name(a), "ZoomOut")) {
2310 else if (!strcmp(gtk_action_get_name(a), "Zoom0.25")) {
2313 else if (!strcmp(gtk_action_get_name(a), "Zoom0.5")) {
2317 gchar *s = (gchar *)gtk_action_get_name(a);
2323 case -3: vik_viewport_zoom_in ( vw->viking_vvp ); break;
2324 case -4: vik_viewport_zoom_out ( vw->viking_vvp ); break;
2325 case -1: vik_viewport_set_zoom ( vw->viking_vvp, 0.5 ); break;
2326 case -2: vik_viewport_set_zoom ( vw->viking_vvp, 0.25 ); break;
2327 default: vik_viewport_set_zoom ( vw->viking_vvp, what );
2332 static void draw_goto_cb ( GtkAction *a, VikWindow *vw )
2334 VikCoord new_center;
2336 if (!strcmp(gtk_action_get_name(a), "GotoLL")) {
2337 struct LatLon ll, llold;
2338 vik_coord_to_latlon ( vik_viewport_get_center ( vw->viking_vvp ), &llold );
2339 if ( a_dialog_goto_latlon ( GTK_WINDOW(vw), &ll, &llold ) )
2340 vik_coord_load_from_latlon ( &new_center, vik_viewport_get_coord_mode(vw->viking_vvp), &ll );
2344 else if (!strcmp(gtk_action_get_name(a), "GotoUTM")) {
2345 struct UTM utm, utmold;
2346 vik_coord_to_utm ( vik_viewport_get_center ( vw->viking_vvp ), &utmold );
2347 if ( a_dialog_goto_utm ( GTK_WINDOW(vw), &utm, &utmold ) )
2348 vik_coord_load_from_utm ( &new_center, vik_viewport_get_coord_mode(vw->viking_vvp), &utm );
2353 g_critical("Houston, we've had a problem.");
2357 vik_viewport_set_center_coord ( vw->viking_vvp, &new_center, TRUE );
2362 * center_changed_cb:
2364 static void center_changed_cb ( VikWindow *vw )
2366 // ATM Keep back always available, so when we pan - we can jump to the last requested position
2368 GtkAction* action_back = gtk_action_group_get_action ( vw->action_group, "GoBack" );
2369 if ( action_back ) {
2370 gtk_action_set_sensitive ( action_back, vik_viewport_back_available(vw->viking_vvp) );
2373 GtkAction* action_forward = gtk_action_group_get_action ( vw->action_group, "GoForward" );
2374 if ( action_forward ) {
2375 gtk_action_set_sensitive ( action_forward, vik_viewport_forward_available(vw->viking_vvp) );
2378 toolbar_action_set_sensitive ( vw->viking_vtb, "GoForward", vik_viewport_forward_available(vw->viking_vvp) );
2382 * draw_goto_back_and_forth:
2384 static void draw_goto_back_and_forth ( GtkAction *a, VikWindow *vw )
2386 gboolean changed = FALSE;
2387 if (!strcmp(gtk_action_get_name(a), "GoBack")) {
2388 changed = vik_viewport_go_back ( vw->viking_vvp );
2390 else if (!strcmp(gtk_action_get_name(a), "GoForward")) {
2391 changed = vik_viewport_go_forward ( vw->viking_vvp );
2397 // Recheck buttons sensitivities, as the center changed signal is not sent on back/forward changes
2398 // (otherwise we would get stuck in an infinite loop!)
2399 center_changed_cb ( vw );
2406 * Refresh maps displayed
2408 static void draw_refresh_cb ( GtkAction *a, VikWindow *vw )
2410 // Only get 'new' maps
2411 simple_map_update ( vw, TRUE );
2414 static void menu_addlayer_cb ( GtkAction *a, VikWindow *vw )
2416 VikLayerTypeEnum type;
2417 for ( type = 0; type < VIK_LAYER_NUM_TYPES; type++ ) {
2418 if (!strcmp(vik_layer_get_interface(type)->name, gtk_action_get_name(a))) {
2419 if ( vik_layers_panel_new_layer ( vw->viking_vlp, type ) ) {
2421 vw->modified = TRUE;
2427 static void menu_copy_layer_cb ( GtkAction *a, VikWindow *vw )
2429 a_clipboard_copy_selected ( vw->viking_vlp );
2432 static void menu_cut_layer_cb ( GtkAction *a, VikWindow *vw )
2434 vik_layers_panel_cut_selected ( vw->viking_vlp );
2435 vw->modified = TRUE;
2438 static void menu_paste_layer_cb ( GtkAction *a, VikWindow *vw )
2440 if ( vik_layers_panel_paste_selected ( vw->viking_vlp ) )
2442 vw->modified = TRUE;
2446 static void menu_properties_cb ( GtkAction *a, VikWindow *vw )
2448 if ( ! vik_layers_panel_properties ( vw->viking_vlp ) )
2449 a_dialog_info_msg ( GTK_WINDOW(vw), _("You must select a layer to show its properties.") );
2452 static void help_help_cb ( GtkAction *a, VikWindow *vw )
2455 ShellExecute(NULL, "open", ""PACKAGE".pdf", NULL, NULL, SW_SHOWNORMAL);
2458 uri = g_strdup_printf("ghelp:%s", PACKAGE);
2459 GError *error = NULL;
2460 gboolean show = gtk_show_uri (NULL, uri, GDK_CURRENT_TIME, &error);
2461 if ( !show && !error )
2462 // No error to show, so unlikely this will get called
2463 a_dialog_error_msg ( GTK_WINDOW(vw), _("The help system is not available.") );
2466 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 );
2467 g_error_free ( error );
2470 #endif /* WINDOWS */
2473 static void toggle_side_panel ( VikWindow *vw )
2475 vw->show_side_panel = !vw->show_side_panel;
2476 if ( vw->show_side_panel )
2477 gtk_widget_show(GTK_WIDGET(vw->viking_vlp));
2479 gtk_widget_hide(GTK_WIDGET(vw->viking_vlp));
2482 static void toggle_full_screen ( VikWindow *vw )
2484 vw->show_full_screen = !vw->show_full_screen;
2485 if ( vw->show_full_screen )
2486 gtk_window_fullscreen ( GTK_WINDOW(vw) );
2488 gtk_window_unfullscreen ( GTK_WINDOW(vw) );
2491 static void toggle_statusbar ( VikWindow *vw )
2493 vw->show_statusbar = !vw->show_statusbar;
2494 if ( vw->show_statusbar )
2495 gtk_widget_show ( GTK_WIDGET(vw->viking_vs) );
2497 gtk_widget_hide ( GTK_WIDGET(vw->viking_vs) );
2500 static void toggle_toolbar ( VikWindow *vw )
2502 vw->show_toolbar = !vw->show_toolbar;
2503 if ( vw->show_toolbar )
2504 gtk_widget_show ( toolbar_get_widget (vw->viking_vtb) );
2506 gtk_widget_hide ( toolbar_get_widget (vw->viking_vtb) );
2509 static void toggle_main_menu ( VikWindow *vw )
2511 vw->show_main_menu = !vw->show_main_menu;
2512 if ( vw->show_main_menu )
2513 gtk_widget_show ( gtk_ui_manager_get_widget ( vw->uim, "/ui/MainMenu" ) );
2515 gtk_widget_hide ( gtk_ui_manager_get_widget ( vw->uim, "/ui/MainMenu" ) );
2518 // Only for 'view' toggle menu widgets ATM.
2519 GtkWidget *get_show_widget_by_name(VikWindow *vw, const gchar *name)
2521 g_return_val_if_fail(name != NULL, NULL);
2523 // ATM only FullScreen is *not* in SetShow path
2525 if ( g_strcmp0 ("FullScreen", name ) )
2526 path = g_strconcat("/ui/MainMenu/View/SetShow/", name, NULL);
2528 path = g_strconcat("/ui/MainMenu/View/", name, NULL);
2530 GtkWidget *widget = gtk_ui_manager_get_widget(vw->uim, path);
2536 static void tb_view_side_panel_cb ( GtkAction *a, VikWindow *vw )
2538 gboolean next_state = !vw->show_side_panel;
2539 GtkWidget *check_box = get_show_widget_by_name ( vw, gtk_action_get_name(a) );
2540 gboolean menu_state = gtk_check_menu_item_get_active ( GTK_CHECK_MENU_ITEM(check_box) );
2541 if ( next_state != menu_state )
2542 gtk_check_menu_item_set_active ( GTK_CHECK_MENU_ITEM(check_box), next_state );
2544 toggle_side_panel ( vw );
2547 static void tb_full_screen_cb ( GtkAction *a, VikWindow *vw )
2549 gboolean next_state = !vw->show_full_screen;
2550 GtkWidget *check_box = get_show_widget_by_name ( vw, gtk_action_get_name(a) );
2551 gboolean menu_state = gtk_check_menu_item_get_active ( GTK_CHECK_MENU_ITEM(check_box) );
2552 if ( next_state != menu_state )
2553 gtk_check_menu_item_set_active ( GTK_CHECK_MENU_ITEM(check_box), next_state );
2555 toggle_full_screen ( vw );
2558 static void tb_view_statusbar_cb ( GtkAction *a, VikWindow *vw )
2560 gboolean next_state = !vw->show_statusbar;
2561 GtkWidget *check_box = get_show_widget_by_name ( vw, gtk_action_get_name(a) );
2562 gboolean menu_state = gtk_check_menu_item_get_active ( GTK_CHECK_MENU_ITEM(check_box) );
2563 if ( next_state != menu_state )
2564 gtk_check_menu_item_set_active ( GTK_CHECK_MENU_ITEM(check_box), next_state );
2566 toggle_statusbar ( vw );
2569 static void tb_view_toolbar_cb ( GtkAction *a, VikWindow *vw )
2571 gboolean next_state = !vw->show_toolbar;
2572 GtkWidget *check_box = get_show_widget_by_name ( vw, gtk_action_get_name(a) );
2573 gboolean menu_state = gtk_check_menu_item_get_active ( GTK_CHECK_MENU_ITEM(check_box) );
2574 if ( next_state != menu_state )
2575 gtk_check_menu_item_set_active ( GTK_CHECK_MENU_ITEM(check_box), next_state );
2577 toggle_toolbar ( vw );
2580 static void tb_view_main_menu_cb ( GtkAction *a, VikWindow *vw )
2582 gboolean next_state = !vw->show_main_menu;
2583 GtkWidget *check_box = get_show_widget_by_name ( vw, gtk_action_get_name(a) );
2584 gboolean menu_state = gtk_check_menu_item_get_active ( GTK_CHECK_MENU_ITEM(check_box) );
2585 if ( next_state != menu_state )
2586 gtk_check_menu_item_set_active ( GTK_CHECK_MENU_ITEM(check_box), next_state );
2588 toggle_main_menu ( vw );
2591 static void tb_set_draw_scale ( GtkAction *a, VikWindow *vw )
2593 gboolean next_state = !vik_viewport_get_draw_scale ( vw->viking_vvp );
2594 GtkWidget *check_box = get_show_widget_by_name ( vw, gtk_action_get_name(a) );
2595 gboolean menu_state = gtk_check_menu_item_get_active ( GTK_CHECK_MENU_ITEM(check_box) );
2596 if ( next_state != menu_state )
2597 gtk_check_menu_item_set_active ( GTK_CHECK_MENU_ITEM(check_box), next_state );
2599 vik_viewport_set_draw_scale ( vw->viking_vvp, next_state );
2604 static void tb_set_draw_centermark ( GtkAction *a, VikWindow *vw )
2606 gboolean next_state = !vik_viewport_get_draw_centermark ( vw->viking_vvp );
2607 GtkWidget *check_box = get_show_widget_by_name ( vw, gtk_action_get_name(a) );
2608 gboolean menu_state = gtk_check_menu_item_get_active ( GTK_CHECK_MENU_ITEM(check_box) );
2609 if ( next_state != menu_state )
2610 gtk_check_menu_item_set_active ( GTK_CHECK_MENU_ITEM(check_box), next_state );
2612 vik_viewport_set_draw_centermark ( vw->viking_vvp, next_state );
2617 static void tb_set_draw_highlight ( GtkAction *a, VikWindow *vw )
2619 gboolean next_state = !vik_viewport_get_draw_highlight ( vw->viking_vvp );
2620 GtkWidget *check_box = get_show_widget_by_name ( vw, gtk_action_get_name(a) );
2621 gboolean menu_state = gtk_check_menu_item_get_active ( GTK_CHECK_MENU_ITEM(check_box) );
2622 if ( next_state != menu_state )
2623 gtk_check_menu_item_set_active ( GTK_CHECK_MENU_ITEM(check_box), next_state );
2625 vik_viewport_set_draw_highlight ( vw->viking_vvp, next_state );
2630 static void help_about_cb ( GtkAction *a, VikWindow *vw )
2632 a_dialog_about(GTK_WINDOW(vw));
2635 static void help_cache_info_cb ( GtkAction *a, VikWindow *vw )
2637 // NB: No i18n as this is just for debug
2638 gint byte_size = a_mapcache_get_size();
2639 gchar *msg_sz = NULL;
2641 #if GLIB_CHECK_VERSION(2,30,0)
2642 msg_sz = g_format_size_full ( byte_size, G_FORMAT_SIZE_LONG_FORMAT );
2644 msg_sz = g_format_size_for_display ( byte_size );
2646 msg = g_strdup_printf ( "Map Cache size is %s with %d items", msg_sz, a_mapcache_get_count());
2647 a_dialog_info_msg_extra ( GTK_WINDOW(vw), "%s", msg );
2652 static void back_forward_info_cb ( GtkAction *a, VikWindow *vw )
2654 vik_viewport_show_centers ( vw->viking_vvp, GTK_WINDOW(vw) );
2657 static void menu_delete_layer_cb ( GtkAction *a, VikWindow *vw )
2659 if ( vik_layers_panel_get_selected ( vw->viking_vlp ) )
2661 vik_layers_panel_delete_selected ( vw->viking_vlp );
2662 vw->modified = TRUE;
2665 a_dialog_info_msg ( GTK_WINDOW(vw), _("You must select a layer to delete.") );
2668 static void full_screen_cb ( GtkAction *a, VikWindow *vw )
2670 gboolean next_state = !vw->show_full_screen;
2671 GtkToggleToolButton *tbutton = (GtkToggleToolButton *)toolbar_get_widget_by_name ( vw->viking_vtb, gtk_action_get_name(a) );
2673 gboolean tb_state = gtk_toggle_tool_button_get_active ( tbutton );
2674 if ( next_state != tb_state )
2675 gtk_toggle_tool_button_set_active ( tbutton, next_state );
2677 toggle_full_screen ( vw );
2680 toggle_full_screen ( vw );
2683 static void view_side_panel_cb ( GtkAction *a, VikWindow *vw )
2685 gboolean next_state = !vw->show_side_panel;
2686 GtkToggleToolButton *tbutton = (GtkToggleToolButton *)toolbar_get_widget_by_name ( vw->viking_vtb, gtk_action_get_name(a) );
2688 gboolean tb_state = gtk_toggle_tool_button_get_active ( tbutton );
2689 if ( next_state != tb_state )
2690 gtk_toggle_tool_button_set_active ( tbutton, next_state );
2692 toggle_side_panel ( vw );
2695 toggle_side_panel ( vw );
2698 static void view_statusbar_cb ( GtkAction *a, VikWindow *vw )
2700 gboolean next_state = !vw->show_statusbar;
2701 GtkToggleToolButton *tbutton = (GtkToggleToolButton *)toolbar_get_widget_by_name ( vw->viking_vtb, gtk_action_get_name(a) );
2703 gboolean tb_state = gtk_toggle_tool_button_get_active ( tbutton );
2704 if ( next_state != tb_state )
2705 gtk_toggle_tool_button_set_active ( tbutton, next_state );
2707 toggle_statusbar ( vw );
2710 toggle_statusbar ( vw );
2713 static void view_toolbar_cb ( GtkAction *a, VikWindow *vw )
2715 gboolean next_state = !vw->show_toolbar;
2716 GtkToggleToolButton *tbutton = (GtkToggleToolButton *)toolbar_get_widget_by_name ( vw->viking_vtb, gtk_action_get_name(a) );
2718 gboolean tb_state = gtk_toggle_tool_button_get_active ( tbutton );
2719 if ( next_state != tb_state )
2720 gtk_toggle_tool_button_set_active ( tbutton, next_state );
2722 toggle_toolbar ( vw );
2725 toggle_toolbar ( vw );
2728 static void view_main_menu_cb ( GtkAction *a, VikWindow *vw )
2730 gboolean next_state = !vw->show_main_menu;
2731 GtkToggleToolButton *tbutton = (GtkToggleToolButton *)toolbar_get_widget_by_name ( vw->viking_vtb, gtk_action_get_name(a) );
2733 gboolean tb_state = gtk_toggle_tool_button_get_active ( tbutton );
2734 if ( next_state != tb_state )
2735 gtk_toggle_tool_button_set_active ( tbutton, next_state );
2737 toggle_main_menu ( vw );
2740 toggle_toolbar ( vw );
2743 /***************************************
2744 ** tool management routines
2746 ***************************************/
2748 static toolbox_tools_t* toolbox_create(VikWindow *vw)
2750 toolbox_tools_t *vt = g_new(toolbox_tools_t, 1);
2753 vt->active_tool = -1;
2758 static void toolbox_add_tool(toolbox_tools_t *vt, VikToolInterface *vti, gint layer_type )
2760 vt->tools = g_renew(toolbox_tool_t, vt->tools, vt->n_tools+1);
2761 vt->tools[vt->n_tools].ti = *vti;
2762 vt->tools[vt->n_tools].layer_type = layer_type;
2764 vt->tools[vt->n_tools].state = vti->create(vt->vw, vt->vw->viking_vvp);
2767 vt->tools[vt->n_tools].state = NULL;
2772 static int toolbox_get_tool(toolbox_tools_t *vt, const gchar *tool_name)
2775 for (i=0; i<vt->n_tools; i++) {
2776 if (!strcmp(tool_name, vt->tools[i].ti.radioActionEntry.name)) {
2783 static void toolbox_activate(toolbox_tools_t *vt, const gchar *tool_name)
2785 int tool = toolbox_get_tool(vt, tool_name);
2786 toolbox_tool_t *t = &vt->tools[tool];
2787 VikLayer *vl = vik_layers_panel_get_selected ( vt->vw->viking_vlp );
2789 if (tool == vt->n_tools) {
2790 g_critical("trying to activate a non-existent tool...");
2793 /* is the tool already active? */
2794 if (vt->active_tool == tool) {
2798 if (vt->active_tool != -1) {
2799 if (vt->tools[vt->active_tool].ti.deactivate) {
2800 vt->tools[vt->active_tool].ti.deactivate(NULL, vt->tools[vt->active_tool].state);
2803 if (t->ti.activate) {
2804 t->ti.activate(vl, t->state);
2806 vt->active_tool = tool;
2809 static const GdkCursor *toolbox_get_cursor(toolbox_tools_t *vt, const gchar *tool_name)
2811 int tool = toolbox_get_tool(vt, tool_name);
2812 toolbox_tool_t *t = &vt->tools[tool];
2813 if (t->ti.cursor == NULL) {
2814 if (t->ti.cursor_type == GDK_CURSOR_IS_PIXMAP && t->ti.cursor_data != NULL) {
2815 GError *cursor_load_err = NULL;
2816 GdkPixbuf *cursor_pixbuf = gdk_pixbuf_from_pixdata (t->ti.cursor_data, FALSE, &cursor_load_err);
2817 /* TODO: settable offeset */
2818 t->ti.cursor = gdk_cursor_new_from_pixbuf ( gdk_display_get_default(), cursor_pixbuf, 3, 3 );
2819 g_object_unref ( G_OBJECT(cursor_pixbuf) );
2821 t->ti.cursor = gdk_cursor_new ( t->ti.cursor_type );
2824 return t->ti.cursor;
2827 static void toolbox_click (toolbox_tools_t *vt, GdkEventButton *event)
2829 VikLayer *vl = vik_layers_panel_get_selected ( vt->vw->viking_vlp );
2830 if (vt->active_tool != -1 && vt->tools[vt->active_tool].ti.click) {
2831 gint ltype = vt->tools[vt->active_tool].layer_type;
2832 if ( ltype == TOOL_LAYER_TYPE_NONE || (vl && ltype == vl->type) )
2833 vt->tools[vt->active_tool].ti.click(vl, event, vt->tools[vt->active_tool].state);
2837 static void toolbox_move (toolbox_tools_t *vt, GdkEventMotion *event)
2839 VikLayer *vl = vik_layers_panel_get_selected ( vt->vw->viking_vlp );
2840 if (vt->active_tool != -1 && vt->tools[vt->active_tool].ti.move) {
2841 gint ltype = vt->tools[vt->active_tool].layer_type;
2842 if ( ltype == TOOL_LAYER_TYPE_NONE || (vl && ltype == vl->type) )
2843 if ( VIK_LAYER_TOOL_ACK_GRAB_FOCUS == vt->tools[vt->active_tool].ti.move(vl, event, vt->tools[vt->active_tool].state) )
2844 gtk_widget_grab_focus ( GTK_WIDGET(vt->vw->viking_vvp) );
2848 static void toolbox_release (toolbox_tools_t *vt, GdkEventButton *event)
2850 VikLayer *vl = vik_layers_panel_get_selected ( vt->vw->viking_vlp );
2851 if (vt->active_tool != -1 && vt->tools[vt->active_tool].ti.release ) {
2852 gint ltype = vt->tools[vt->active_tool].layer_type;
2853 if ( ltype == TOOL_LAYER_TYPE_NONE || (vl && ltype == vl->type) )
2854 vt->tools[vt->active_tool].ti.release(vl, event, vt->tools[vt->active_tool].state);
2857 /** End tool management ************************************/
2859 void vik_window_enable_layer_tool ( VikWindow *vw, gint layer_id, gint tool_id )
2861 gtk_action_activate ( gtk_action_group_get_action ( vw->action_group, vik_layer_get_interface(layer_id)->tools[tool_id].radioActionEntry.name ) );
2864 // Be careful with usage - as it may trigger actions being continually alternately by the menu and toolbar items
2865 // DON'T Use this from menu callback with toggle toolbar items!!
2866 static void toolbar_sync ( VikWindow *vw, const gchar *name, gboolean state )
2868 GtkToggleToolButton *tbutton = (GtkToggleToolButton *)toolbar_get_widget_by_name ( vw->viking_vtb, name );
2870 // Causes toggle signal action to be raised.
2871 gtk_toggle_tool_button_set_active ( tbutton, state );
2875 /* this function gets called whenever a menu is clicked */
2876 // Note old is not used
2877 static void menu_cb ( GtkAction *old, GtkAction *a, VikWindow *vw )
2879 // Ensure Toolbar kept in sync
2880 const gchar *name = gtk_action_get_name(a);
2881 toolbar_sync ( vw, name, TRUE );
2883 /* White Magic, my friends ... White Magic... */
2885 toolbox_activate(vw->vt, name);
2887 vw->viewport_cursor = (GdkCursor *)toolbox_get_cursor(vw->vt, name);
2889 if ( gtk_widget_get_window(GTK_WIDGET(vw->viking_vvp)) )
2890 /* We set cursor, even if it is NULL: it resets to default */
2891 gdk_window_set_cursor ( gtk_widget_get_window(GTK_WIDGET(vw->viking_vvp)), vw->viewport_cursor );
2893 if (!g_strcmp0(name, "Pan")) {
2894 vw->current_tool = TOOL_PAN;
2896 else if (!g_strcmp0(name, "Zoom")) {
2897 vw->current_tool = TOOL_ZOOM;
2899 else if (!g_strcmp0(name, "Ruler")) {
2900 vw->current_tool = TOOL_RULER;
2902 else if (!g_strcmp0(name, "Select")) {
2903 vw->current_tool = TOOL_SELECT;
2906 VikLayerTypeEnum layer_id;
2907 for (layer_id=0; layer_id<VIK_LAYER_NUM_TYPES; layer_id++) {
2908 for ( tool_id = 0; tool_id < vik_layer_get_interface(layer_id)->tools_count; tool_id++ ) {
2909 if (!g_strcmp0(vik_layer_get_interface(layer_id)->tools[tool_id].radioActionEntry.name, name)) {
2910 vw->current_tool = TOOL_LAYER;
2911 vw->tool_layer_id = layer_id;
2912 vw->tool_tool_id = tool_id;
2917 draw_status_tool ( vw );
2920 static void window_set_filename ( VikWindow *vw, const gchar *filename )
2925 g_free ( vw->filename );
2926 if ( filename == NULL )
2928 vw->filename = NULL;
2932 vw->filename = g_strdup(filename);
2935 /* Refresh window's title */
2936 file = window_get_filename ( vw );
2937 title = g_strdup_printf( "%s - Viking", file );
2938 gtk_window_set_title ( GTK_WINDOW(vw), title );
2942 static const gchar *window_get_filename ( VikWindow *vw )
2944 return vw->filename ? a_file_basename ( vw->filename ) : _("Untitled");
2947 GtkWidget *vik_window_get_drawmode_button ( VikWindow *vw, VikViewportDrawMode mode )
2949 GtkWidget *mode_button;
2952 #ifdef VIK_CONFIG_EXPEDIA
2953 case VIK_VIEWPORT_DRAWMODE_EXPEDIA: buttonname = "/ui/MainMenu/View/ModeExpedia"; break;
2955 case VIK_VIEWPORT_DRAWMODE_MERCATOR: buttonname = "/ui/MainMenu/View/ModeMercator"; break;
2956 case VIK_VIEWPORT_DRAWMODE_LATLON: buttonname = "/ui/MainMenu/View/ModeLatLon"; break;
2957 default: buttonname = "/ui/MainMenu/View/ModeUTM";
2959 mode_button = gtk_ui_manager_get_widget ( vw->uim, buttonname );
2960 g_assert ( mode_button );
2965 * vik_window_get_pan_move:
2966 * @vw: some VikWindow
2968 * Retrieves @vw's pan_move.
2970 * Should be removed as soon as possible.
2972 * Returns: @vw's pan_move
2976 gboolean vik_window_get_pan_move ( VikWindow *vw )
2978 return vw->pan_move;
2981 static void on_activate_recent_item (GtkRecentChooser *chooser,
2986 filename = gtk_recent_chooser_get_current_uri (chooser);
2987 if (filename != NULL)
2989 GFile *file = g_file_new_for_uri ( filename );
2990 gchar *path = g_file_get_path ( file );
2991 g_object_unref ( file );
2992 if ( self->filename )
2994 GSList *filenames = NULL;
2995 filenames = g_slist_append ( filenames, path );
2996 g_signal_emit ( G_OBJECT(self), window_signals[VW_OPENWINDOW_SIGNAL], 0, filenames );
2997 // NB: GSList & contents are freed by main.open_window
3000 vik_window_open_file ( self, path, TRUE );
3008 static void setup_recent_files (VikWindow *self)
3010 GtkRecentManager *manager;
3011 GtkRecentFilter *filter;
3012 GtkWidget *menu, *menu_item;
3014 filter = gtk_recent_filter_new ();
3015 /* gtk_recent_filter_add_application (filter, g_get_application_name()); */
3016 gtk_recent_filter_add_group(filter, "viking");
3018 manager = gtk_recent_manager_get_default ();
3019 menu = gtk_recent_chooser_menu_new_for_manager (manager);
3020 gtk_recent_chooser_set_sort_type (GTK_RECENT_CHOOSER (menu), GTK_RECENT_SORT_MRU);
3021 gtk_recent_chooser_add_filter (GTK_RECENT_CHOOSER (menu), filter);
3022 gtk_recent_chooser_set_limit (GTK_RECENT_CHOOSER (menu), a_vik_get_recent_number_files() );
3024 menu_item = gtk_ui_manager_get_widget (self->uim, "/ui/MainMenu/File/OpenRecentFile");
3025 gtk_menu_item_set_submenu (GTK_MENU_ITEM (menu_item), menu);
3027 g_signal_connect (G_OBJECT (menu), "item-activated",
3028 G_CALLBACK (on_activate_recent_item), (gpointer) self);
3034 static void update_recently_used_document (VikWindow *vw, const gchar *filename)
3036 /* Update Recently Used Document framework */
3037 GtkRecentManager *manager = gtk_recent_manager_get_default();
3038 GtkRecentData *recent_data = g_slice_new (GtkRecentData);
3039 gchar *groups[] = {"viking", NULL};
3040 GFile *file = g_file_new_for_commandline_arg(filename);
3041 gchar *uri = g_file_get_uri(file);
3042 gchar *basename = g_path_get_basename(filename);
3043 g_object_unref(file);
3046 recent_data->display_name = basename;
3047 recent_data->description = NULL;
3048 recent_data->mime_type = "text/x-gps-data";
3049 recent_data->app_name = (gchar *) g_get_application_name ();
3050 recent_data->app_exec = g_strjoin (" ", g_get_prgname (), "%f", NULL);
3051 recent_data->groups = groups;
3052 recent_data->is_private = FALSE;
3053 if (!gtk_recent_manager_add_full (manager, uri, recent_data))
3055 gchar *msg = g_strdup_printf (_("Unable to add '%s' to the list of recently used documents"), uri);
3056 vik_statusbar_set_message ( vw->viking_vs, VIK_STATUSBAR_INFO, msg );
3062 g_free (recent_data->app_exec);
3063 g_slice_free (GtkRecentData, recent_data);
3067 * Call this before doing things that may take a long time and otherwise not show any other feedback
3068 * such as loading and saving files
3070 void vik_window_set_busy_cursor ( VikWindow *vw )
3072 gdk_window_set_cursor ( gtk_widget_get_window(GTK_WIDGET(vw)), vw->busy_cursor );
3073 // Viewport has a separate cursor
3074 gdk_window_set_cursor ( gtk_widget_get_window(GTK_WIDGET(vw->viking_vvp)), vw->busy_cursor );
3075 // Ensure cursor updated before doing stuff
3076 while( gtk_events_pending() )
3077 gtk_main_iteration();
3080 void vik_window_clear_busy_cursor ( VikWindow *vw )
3082 gdk_window_set_cursor ( gtk_widget_get_window(GTK_WIDGET(vw)), NULL );
3083 // Restore viewport cursor
3084 gdk_window_set_cursor ( gtk_widget_get_window(GTK_WIDGET(vw->viking_vvp)), vw->viewport_cursor );
3087 void vik_window_open_file ( VikWindow *vw, const gchar *filename, gboolean change_filename )
3089 vik_window_set_busy_cursor ( vw );
3091 // Enable the *new* filename to be accessible by the Layers codez
3092 gchar *original_filename = g_strdup ( vw->filename );
3093 g_free ( vw->filename );
3094 vw->filename = g_strdup ( filename );
3095 gboolean success = FALSE;
3096 gboolean restore_original_filename = FALSE;
3098 vw->loaded_type = a_file_load ( vik_layers_panel_get_top_layer(vw->viking_vlp), vw->viking_vvp, filename );
3099 switch ( vw->loaded_type )
3101 case LOAD_TYPE_READ_FAILURE:
3102 a_dialog_error_msg ( GTK_WINDOW(vw), _("The file you requested could not be opened.") );
3104 case LOAD_TYPE_GPSBABEL_FAILURE:
3105 a_dialog_error_msg ( GTK_WINDOW(vw), _("GPSBabel is required to load files of this type or GPSBabel encountered problems.") );
3107 case LOAD_TYPE_GPX_FAILURE:
3108 a_dialog_error_msg_extra ( GTK_WINDOW(vw), _("Unable to load malformed GPX file %s"), filename );
3110 case LOAD_TYPE_UNSUPPORTED_FAILURE:
3111 a_dialog_error_msg_extra ( GTK_WINDOW(vw), _("Unsupported file type for %s"), filename );
3113 case LOAD_TYPE_VIK_FAILURE_NON_FATAL:
3115 // Since we can process .vik files with issues just show a warning in the status bar
3116 // Not that a user can do much about it... or tells them what this issue is yet...
3117 gchar *msg = g_strdup_printf (_("WARNING: issues encountered loading %s"), a_file_basename (filename) );
3118 vik_statusbar_set_message ( vw->viking_vs, VIK_STATUSBAR_INFO, msg );
3121 // No break, carry on to show any data
3122 case LOAD_TYPE_VIK_SUCCESS:
3124 restore_original_filename = TRUE; // NB Will actually get inverted by the 'success' component below
3125 GtkWidget *mode_button;
3127 if ( change_filename )
3128 window_set_filename ( vw, filename );
3129 mode_button = vik_window_get_drawmode_button ( vw, vik_viewport_get_drawmode ( vw->viking_vvp ) );
3130 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. */
3131 gtk_check_menu_item_set_active ( GTK_CHECK_MENU_ITEM(mode_button), TRUE );
3132 vw->only_updating_coord_mode_ui = FALSE;
3134 vik_layers_panel_change_coord_mode ( vw->viking_vlp, vik_viewport_get_coord_mode ( vw->viking_vvp ) );
3136 // Slightly long winded methods to align loaded viewport settings with the UI
3137 // Since the rewrite for toolbar + menu actions
3138 // there no longer exists a simple way to directly change the UI to a value for toggle settings
3139 // it only supports toggling the existing setting (otherwise get infinite loops in trying to align tb+menu elements)
3140 // Thus get state, compare them, if different then invert viewport setting and (re)sync the setting (via toggling)
3141 gboolean vp_state_scale = vik_viewport_get_draw_scale ( vw->viking_vvp );
3142 gboolean ui_state_scale = gtk_check_menu_item_get_active ( GTK_CHECK_MENU_ITEM(get_show_widget_by_name(vw, "ShowScale")) );
3143 if ( vp_state_scale != ui_state_scale ) {
3144 vik_viewport_set_draw_scale ( vw->viking_vvp, !vp_state_scale );
3145 toggle_draw_scale ( NULL, vw );
3147 gboolean vp_state_centermark = vik_viewport_get_draw_centermark ( vw->viking_vvp );
3148 gboolean ui_state_centermark = gtk_check_menu_item_get_active ( GTK_CHECK_MENU_ITEM(get_show_widget_by_name(vw, "ShowCenterMark")) );
3149 if ( vp_state_centermark != ui_state_centermark ) {
3150 vik_viewport_set_draw_centermark ( vw->viking_vvp, !vp_state_centermark );
3151 toggle_draw_centermark ( NULL, vw );
3153 gboolean vp_state_highlight = vik_viewport_get_draw_highlight ( vw->viking_vvp );
3154 gboolean ui_state_highlight = gtk_check_menu_item_get_active ( GTK_CHECK_MENU_ITEM(get_show_widget_by_name(vw, "ShowHighlight")) );
3155 if ( vp_state_highlight != ui_state_highlight ) {
3156 vik_viewport_set_draw_highlight ( vw->viking_vvp, !vp_state_highlight );
3157 toggle_draw_highlight ( NULL, vw );
3160 // NB No break, carry on to redraw
3161 //case LOAD_TYPE_OTHER_SUCCESS:
3164 // When LOAD_TYPE_OTHER_SUCCESS *only*, this will maintain the existing Viking project
3165 restore_original_filename = ! restore_original_filename;
3166 update_recently_used_document (vw, filename);
3171 if ( ! success || restore_original_filename )
3172 // Load didn't work or want to keep as the existing Viking project, keep using the original name
3173 window_set_filename ( vw, original_filename );
3174 g_free ( original_filename );
3176 vik_window_clear_busy_cursor ( vw );
3179 static void load_file ( GtkAction *a, VikWindow *vw )
3181 GSList *files = NULL;
3182 GSList *cur_file = NULL;
3184 if (!strcmp(gtk_action_get_name(a), "Open")) {
3187 else if (!strcmp(gtk_action_get_name(a), "Append")) {
3191 g_critical("Houston, we've had a problem.");
3195 if ( ! vw->open_dia )
3197 vw->open_dia = gtk_file_chooser_dialog_new (_("Please select a GPS data file to open. "),
3199 GTK_FILE_CHOOSER_ACTION_OPEN,
3200 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
3201 GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT,
3203 if ( last_folder_files_uri )
3204 gtk_file_chooser_set_current_folder_uri ( GTK_FILE_CHOOSER(vw->open_dia), last_folder_files_uri );
3206 GtkFileFilter *filter;
3207 // NB file filters are listed this way for alphabetical ordering
3208 #ifdef VIK_CONFIG_GEOCACHES
3209 filter = gtk_file_filter_new ();
3210 gtk_file_filter_set_name( filter, _("Geocaching") );
3211 gtk_file_filter_add_pattern ( filter, "*.loc" ); // No MIME type available
3212 gtk_file_chooser_add_filter (GTK_FILE_CHOOSER(vw->open_dia), filter);
3215 filter = gtk_file_filter_new ();
3216 gtk_file_filter_set_name( filter, _("Google Earth") );
3217 gtk_file_filter_add_mime_type ( filter, "application/vnd.google-earth.kml+xml");
3218 gtk_file_chooser_add_filter (GTK_FILE_CHOOSER(vw->open_dia), filter);
3220 filter = gtk_file_filter_new ();
3221 gtk_file_filter_set_name( filter, _("GPX") );
3222 gtk_file_filter_add_pattern ( filter, "*.gpx" ); // No MIME type available
3223 gtk_file_chooser_add_filter (GTK_FILE_CHOOSER(vw->open_dia), filter);
3225 filter = gtk_file_filter_new ();
3226 gtk_file_filter_set_name ( filter, _("JPG") );
3227 gtk_file_filter_add_mime_type ( filter, "image/jpeg");
3228 gtk_file_chooser_add_filter (GTK_FILE_CHOOSER(vw->open_dia), filter);
3230 filter = gtk_file_filter_new ();
3231 gtk_file_filter_set_name( filter, _("Viking") );
3232 gtk_file_filter_add_pattern ( filter, "*.vik" );
3233 gtk_file_filter_add_pattern ( filter, "*.viking" );
3234 gtk_file_chooser_add_filter (GTK_FILE_CHOOSER(vw->open_dia), filter);
3236 // NB could have filters for gpspoint (*.gps,*.gpsoint?) + gpsmapper (*.gsm,*.gpsmapper?)
3237 // However assume this are barely used and thus not worthy of inclusion
3238 // as they'll just make the options too many and have no clear file pattern
3239 // one can always use the all option
3240 filter = gtk_file_filter_new ();
3241 gtk_file_filter_set_name( filter, _("All") );
3242 gtk_file_filter_add_pattern ( filter, "*" );
3243 gtk_file_chooser_add_filter (GTK_FILE_CHOOSER(vw->open_dia), filter);
3244 // Default to any file - same as before open filters were added
3245 gtk_file_chooser_set_filter (GTK_FILE_CHOOSER(vw->open_dia), filter);
3247 gtk_file_chooser_set_select_multiple ( GTK_FILE_CHOOSER(vw->open_dia), TRUE );
3248 gtk_window_set_transient_for ( GTK_WINDOW(vw->open_dia), GTK_WINDOW(vw) );
3249 gtk_window_set_destroy_with_parent ( GTK_WINDOW(vw->open_dia), TRUE );
3251 if ( gtk_dialog_run ( GTK_DIALOG(vw->open_dia) ) == GTK_RESPONSE_ACCEPT )
3253 g_free ( last_folder_files_uri );
3254 last_folder_files_uri = gtk_file_chooser_get_current_folder_uri ( GTK_FILE_CHOOSER(vw->open_dia) );
3256 gtk_widget_hide ( vw->open_dia );
3257 #ifdef VIKING_PROMPT_IF_MODIFIED
3258 if ( (vw->modified || vw->filename) && newwindow )
3260 if ( vw->filename && newwindow )
3262 g_signal_emit ( G_OBJECT(vw), window_signals[VW_OPENWINDOW_SIGNAL], 0, gtk_file_chooser_get_filenames (GTK_FILE_CHOOSER(vw->open_dia) ) );
3265 files = gtk_file_chooser_get_filenames (GTK_FILE_CHOOSER(vw->open_dia) );
3266 gboolean change_fn = newwindow && (g_slist_length(files)==1); /* only change fn if one file */
3267 gboolean first_vik_file = TRUE;
3269 while ( cur_file ) {
3271 gchar *file_name = cur_file->data;
3272 if ( newwindow && check_file_magic_vik ( file_name ) ) {
3273 // Load first of many .vik files in current window
3274 if ( first_vik_file ) {
3275 vik_window_open_file ( vw, file_name, TRUE );
3276 first_vik_file = FALSE;
3279 // Load each subsequent .vik file in a separate window
3280 VikWindow *newvw = vik_window_new_window ();
3282 vik_window_open_file ( newvw, file_name, TRUE );
3287 vik_window_open_file ( vw, file_name, change_fn );
3290 cur_file = g_slist_next (cur_file);
3292 g_slist_free (files);
3296 gtk_widget_hide ( vw->open_dia );
3299 static gboolean save_file_as ( GtkAction *a, VikWindow *vw )
3301 gboolean rv = FALSE;
3303 if ( ! vw->save_dia )
3305 vw->save_dia = gtk_file_chooser_dialog_new (_("Save as Viking File."),
3307 GTK_FILE_CHOOSER_ACTION_SAVE,
3308 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
3309 GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT,
3311 if ( last_folder_files_uri )
3312 gtk_file_chooser_set_current_folder_uri ( GTK_FILE_CHOOSER(vw->save_dia), last_folder_files_uri );
3314 GtkFileFilter *filter;
3315 filter = gtk_file_filter_new ();
3316 gtk_file_filter_set_name( filter, _("All") );
3317 gtk_file_filter_add_pattern ( filter, "*" );
3318 gtk_file_chooser_add_filter (GTK_FILE_CHOOSER(vw->save_dia), filter);
3320 filter = gtk_file_filter_new ();
3321 gtk_file_filter_set_name( filter, _("Viking") );
3322 gtk_file_filter_add_pattern ( filter, "*.vik" );
3323 gtk_file_filter_add_pattern ( filter, "*.viking" );
3324 gtk_file_chooser_add_filter (GTK_FILE_CHOOSER(vw->save_dia), filter);
3325 // Default to a Viking file
3326 gtk_file_chooser_set_filter (GTK_FILE_CHOOSER(vw->save_dia), filter);
3328 gtk_window_set_transient_for ( GTK_WINDOW(vw->save_dia), GTK_WINDOW(vw) );
3329 gtk_window_set_destroy_with_parent ( GTK_WINDOW(vw->save_dia), TRUE );
3331 // Auto append / replace extension with '.vik' to the suggested file name as it's going to be a Viking File
3332 gchar* auto_save_name = g_strdup ( window_get_filename ( vw ) );
3333 if ( ! a_file_check_ext ( auto_save_name, ".vik" ) )
3334 auto_save_name = g_strconcat ( auto_save_name, ".vik", NULL );
3336 gtk_file_chooser_set_current_name (GTK_FILE_CHOOSER(vw->save_dia), auto_save_name);
3338 while ( gtk_dialog_run ( GTK_DIALOG(vw->save_dia) ) == GTK_RESPONSE_ACCEPT )
3340 fn = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER(vw->save_dia) );
3341 if ( g_file_test ( fn, G_FILE_TEST_EXISTS ) == FALSE || a_dialog_yes_or_no ( GTK_WINDOW(vw->save_dia), _("The file \"%s\" exists, do you wish to overwrite it?"), a_file_basename ( fn ) ) )
3343 window_set_filename ( vw, fn );
3344 rv = window_save ( vw );
3346 vw->modified = FALSE;
3347 g_free ( last_folder_files_uri );
3348 last_folder_files_uri = gtk_file_chooser_get_current_folder_uri ( GTK_FILE_CHOOSER(vw->save_dia) );
3353 g_free ( auto_save_name );
3354 gtk_widget_hide ( vw->save_dia );
3358 static gboolean window_save ( VikWindow *vw )
3360 vik_window_set_busy_cursor ( vw );
3361 gboolean success = TRUE;
3363 if ( a_file_save ( vik_layers_panel_get_top_layer ( vw->viking_vlp ), vw->viking_vvp, vw->filename ) )
3365 update_recently_used_document ( vw, vw->filename );
3369 a_dialog_error_msg ( GTK_WINDOW(vw), _("The filename you requested could not be opened for writing.") );
3372 vik_window_clear_busy_cursor ( vw );
3376 static gboolean save_file ( GtkAction *a, VikWindow *vw )
3378 if ( ! vw->filename )
3379 return save_file_as ( NULL, vw );
3382 vw->modified = FALSE;
3383 return window_save ( vw );
3390 * Export all TRW Layers in the list to individual files in the specified directory
3392 * Returns: %TRUE on success
3394 static gboolean export_to ( VikWindow *vw, GList *gl, VikFileType_t vft, const gchar *dir, const gchar *extension )
3396 gboolean success = TRUE;
3398 gint export_count = 0;
3400 vik_window_set_busy_cursor ( vw );
3404 gchar *fn = g_strconcat ( dir, G_DIR_SEPARATOR_S, VIK_LAYER(gl->data)->name, extension, NULL );
3406 // Some protection in attempting to write too many same named files
3407 // As this will get horribly slow...
3408 gboolean safe = FALSE;
3410 while ( ii < 5000 ) {
3411 if ( g_file_test ( fn, G_FILE_TEST_EXISTS ) ) {
3414 fn = g_strdup_printf ( "%s%s%s#%03d%s", dir, G_DIR_SEPARATOR_S, VIK_LAYER(gl->data)->name, ii, extension );
3425 // NB: We allow exporting empty layers
3427 gboolean this_success = a_file_export ( VIK_TRW_LAYER(gl->data), fn, vft, NULL, TRUE );
3429 // Show some progress
3430 if ( this_success ) {
3432 gchar *message = g_strdup_printf ( _("Exporting to file: %s"), fn );
3433 vik_statusbar_set_message ( vw->viking_vs, VIK_STATUSBAR_INFO, message );
3434 while ( gtk_events_pending() )
3435 gtk_main_iteration ();
3439 success = success && this_success;
3443 gl = g_list_next ( gl );
3446 vik_window_clear_busy_cursor ( vw );
3448 // Confirm what happened.
3449 gchar *message = g_strdup_printf ( _("Exported files: %d"), export_count );
3450 vik_statusbar_set_message ( vw->viking_vs, VIK_STATUSBAR_INFO, message );
3456 static void export_to_common ( VikWindow *vw, VikFileType_t vft, const gchar *extension )
3458 GList *gl = vik_layers_panel_get_all_layers_of_type ( vw->viking_vlp, VIK_LAYER_TRW, TRUE );
3461 a_dialog_info_msg ( GTK_WINDOW(vw), _("Nothing to Export!") );
3465 GtkWidget *dialog = gtk_file_chooser_dialog_new ( _("Export to directory"),
3467 GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER,
3469 GTK_RESPONSE_REJECT,
3471 GTK_RESPONSE_ACCEPT,
3473 gtk_window_set_transient_for ( GTK_WINDOW(dialog), GTK_WINDOW(vw) );
3474 gtk_window_set_destroy_with_parent ( GTK_WINDOW(dialog), TRUE );
3475 gtk_window_set_modal ( GTK_WINDOW(dialog), TRUE );
3477 gtk_widget_show_all ( dialog );
3479 if ( gtk_dialog_run ( GTK_DIALOG(dialog) ) == GTK_RESPONSE_ACCEPT ) {
3480 gchar *dir = gtk_file_chooser_get_filename ( GTK_FILE_CHOOSER(dialog) );
3481 gtk_widget_destroy ( dialog );
3483 if ( !export_to ( vw, gl, vft, dir, extension ) )
3484 a_dialog_error_msg ( GTK_WINDOW(vw),_("Could not convert all files") );
3489 gtk_widget_destroy ( dialog );
3494 static void export_to_gpx ( GtkAction *a, VikWindow *vw )
3496 export_to_common ( vw, FILE_TYPE_GPX, ".gpx" );
3499 static void export_to_kml ( GtkAction *a, VikWindow *vw )
3501 export_to_common ( vw, FILE_TYPE_KML, ".kml" );
3504 #if !GLIB_CHECK_VERSION(2,26,0)
3505 typedef struct stat GStatBuf;
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 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,
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,
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();
3764 static void clear_cb ( GtkAction *a, VikWindow *vw )
3766 vik_layers_panel_clear ( vw->viking_vlp );
3767 window_set_filename ( vw, NULL );
3771 static void window_close ( GtkAction *a, VikWindow *vw )
3773 if ( ! delete_event ( vw ) )
3774 gtk_widget_destroy ( GTK_WIDGET(vw) );
3777 static gboolean save_file_and_exit ( GtkAction *a, VikWindow *vw )
3779 if (save_file( NULL, vw)) {
3780 window_close( NULL, vw);
3787 static void zoom_to_cb ( GtkAction *a, VikWindow *vw )
3789 gdouble xmpp = vik_viewport_get_xmpp ( vw->viking_vvp ), ympp = vik_viewport_get_ympp ( vw->viking_vvp );
3790 if ( a_dialog_custom_zoom ( GTK_WINDOW(vw), &xmpp, &ympp ) )
3792 vik_viewport_set_xmpp ( vw->viking_vvp, xmpp );
3793 vik_viewport_set_ympp ( vw->viking_vvp, ympp );
3798 static void save_image_file ( VikWindow *vw, const gchar *fn, guint w, guint h, gdouble zoom, gboolean save_as_png )
3800 /* more efficient way: stuff draws directly to pixbuf (fork viewport) */
3801 GdkPixbuf *pixbuf_to_save;
3802 gdouble old_xmpp, old_ympp;
3803 GError *error = NULL;
3805 GtkWidget *msgbox = gtk_message_dialog_new ( GTK_WINDOW(vw),
3806 GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
3809 _("Generating image file...") );
3811 g_signal_connect_swapped (msgbox, "response", G_CALLBACK (gtk_widget_destroy), msgbox);
3812 // Ensure dialog shown
3813 gtk_widget_show_all ( msgbox );
3815 vik_statusbar_set_message ( vw->viking_vs, VIK_STATUSBAR_INFO, _("Generating image file...") );
3816 while ( gtk_events_pending() )
3817 gtk_main_iteration ();
3818 // Despite many efforts & variations, GTK on my Linux system doesn't show the actual msgbox contents :(
3819 // At least the empty box can give a clue something's going on + the statusbar msg...
3820 // Windows version under Wine OK!
3822 /* backup old zoom & set new */
3823 old_xmpp = vik_viewport_get_xmpp ( vw->viking_vvp );
3824 old_ympp = vik_viewport_get_ympp ( vw->viking_vvp );
3825 vik_viewport_set_zoom ( vw->viking_vvp, zoom );
3827 /* reset width and height: */
3828 vik_viewport_configure_manually ( vw->viking_vvp, w, h );
3830 /* draw all layers */
3833 /* save buffer as file. */
3834 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);
3835 if ( !pixbuf_to_save ) {
3836 g_warning("Failed to generate internal pixmap size: %d x %d", w, h);
3837 gtk_message_dialog_set_markup ( GTK_MESSAGE_DIALOG(msgbox), _("Failed to generate internal image.\n\nTry creating a smaller image.") );
3841 gdk_pixbuf_save ( pixbuf_to_save, fn, save_as_png ? "png" : "jpeg", &error, NULL );
3844 g_warning("Unable to write to file %s: %s", fn, error->message );
3845 gtk_message_dialog_set_markup ( GTK_MESSAGE_DIALOG(msgbox), _("Failed to generate image file.") );
3846 g_error_free (error);
3850 gtk_message_dialog_set_markup ( GTK_MESSAGE_DIALOG(msgbox), _("Image file generated.") );
3852 g_object_unref ( G_OBJECT(pixbuf_to_save) );
3855 vik_statusbar_set_message ( vw->viking_vs, VIK_STATUSBAR_INFO, "" );
3856 gtk_dialog_add_button ( GTK_DIALOG(msgbox), GTK_STOCK_OK, GTK_RESPONSE_OK );
3857 gtk_dialog_run ( GTK_DIALOG(msgbox) ); // Don't care about the result
3859 /* pretend like nothing happened ;) */
3860 vik_viewport_set_xmpp ( vw->viking_vvp, old_xmpp );
3861 vik_viewport_set_ympp ( vw->viking_vvp, old_ympp );
3862 vik_viewport_configure ( vw->viking_vvp );
3866 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 )
3868 gulong size = sizeof(gchar) * (strlen(fn) + 15);
3869 gchar *name_of_file = g_malloc ( size );
3871 struct UTM utm_orig, utm;
3873 /* *** copied from above *** */
3874 GdkPixbuf *pixbuf_to_save;
3875 gdouble old_xmpp, old_ympp;
3876 GError *error = NULL;
3878 /* backup old zoom & set new */
3879 old_xmpp = vik_viewport_get_xmpp ( vw->viking_vvp );
3880 old_ympp = vik_viewport_get_ympp ( vw->viking_vvp );
3881 vik_viewport_set_zoom ( vw->viking_vvp, zoom );
3883 /* reset width and height: do this only once for all images (same size) */
3884 vik_viewport_configure_manually ( vw->viking_vvp, w, h );
3885 /* *** end copy from above *** */
3887 g_assert ( vik_viewport_get_coord_mode ( vw->viking_vvp ) == VIK_COORD_UTM );
3891 utm_orig = *((const struct UTM *)vik_viewport_get_center ( vw->viking_vvp ));
3893 for ( y = 1; y <= tiles_h; y++ )
3895 for ( x = 1; x <= tiles_w; x++ )
3897 g_snprintf ( name_of_file, size, "%s%cy%d-x%d.%s", fn, G_DIR_SEPARATOR, y, x, save_as_png ? "png" : "jpg" );
3899 if ( tiles_w & 0x1 )
3900 utm.easting += ((gdouble)x - ceil(((gdouble)tiles_w)/2)) * (w*zoom);
3902 utm.easting += ((gdouble)x - (((gdouble)tiles_w)+1)/2) * (w*zoom);
3903 if ( tiles_h & 0x1 ) /* odd */
3904 utm.northing -= ((gdouble)y - ceil(((gdouble)tiles_h)/2)) * (h*zoom);
3906 utm.northing -= ((gdouble)y - (((gdouble)tiles_h)+1)/2) * (h*zoom);
3908 /* move to correct place. */
3909 vik_viewport_set_center_utm ( vw->viking_vvp, &utm, FALSE );
3913 /* save buffer as file. */
3914 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);
3915 gdk_pixbuf_save ( pixbuf_to_save, name_of_file, save_as_png ? "png" : "jpeg", &error, NULL );
3918 gchar *msg = g_strdup_printf (_("Unable to write to file %s: %s"), name_of_file, error->message );
3919 vik_statusbar_set_message ( vw->viking_vs, VIK_STATUSBAR_INFO, msg );
3921 g_error_free (error);
3924 g_object_unref ( G_OBJECT(pixbuf_to_save) );
3928 vik_viewport_set_center_utm ( vw->viking_vvp, &utm_orig, FALSE );
3929 vik_viewport_set_xmpp ( vw->viking_vvp, old_xmpp );
3930 vik_viewport_set_ympp ( vw->viking_vvp, old_ympp );
3931 vik_viewport_configure ( vw->viking_vvp );
3934 g_free ( name_of_file );
3937 static void draw_to_image_file_current_window_cb(GtkWidget* widget,GdkEventButton *event,gpointer *pass_along)
3939 VikWindow *vw = VIK_WINDOW(pass_along[0]);
3940 GtkSpinButton *width_spin = GTK_SPIN_BUTTON(pass_along[1]), *height_spin = GTK_SPIN_BUTTON(pass_along[2]);
3942 gint active = gtk_combo_box_get_active ( GTK_COMBO_BOX(pass_along[3]) );
3943 gdouble zoom = pow (2, active-2 );
3945 gdouble width_min, width_max, height_min, height_max;
3948 gtk_spin_button_get_range ( width_spin, &width_min, &width_max );
3949 gtk_spin_button_get_range ( height_spin, &height_min, &height_max );
3951 /* TODO: support for xzoom and yzoom values */
3952 width = vik_viewport_get_width ( vw->viking_vvp ) * vik_viewport_get_xmpp ( vw->viking_vvp ) / zoom;
3953 height = vik_viewport_get_height ( vw->viking_vvp ) * vik_viewport_get_xmpp ( vw->viking_vvp ) / zoom;
3955 if ( width > width_max || width < width_min || height > height_max || height < height_min )
3956 a_dialog_info_msg ( GTK_WINDOW(vw), _("Viewable region outside allowable pixel size bounds for image. Clipping width/height values.") );
3958 gtk_spin_button_set_value ( width_spin, width );
3959 gtk_spin_button_set_value ( height_spin, height );
3962 static void draw_to_image_file_total_area_cb (GtkSpinButton *spinbutton, gpointer *pass_along)
3964 GtkSpinButton *width_spin = GTK_SPIN_BUTTON(pass_along[1]), *height_spin = GTK_SPIN_BUTTON(pass_along[2]);
3966 gint active = gtk_combo_box_get_active ( GTK_COMBO_BOX(pass_along[3]) );
3967 gdouble zoom = pow (2, active-2 );
3971 w = gtk_spin_button_get_value(width_spin) * zoom;
3972 h = gtk_spin_button_get_value(height_spin) * zoom;
3973 if (pass_along[4]) /* save many images; find TOTAL area covered */
3975 w *= gtk_spin_button_get_value(GTK_SPIN_BUTTON(pass_along[4]));
3976 h *= gtk_spin_button_get_value(GTK_SPIN_BUTTON(pass_along[5]));
3978 vik_units_distance_t dist_units = a_vik_get_units_distance ();
3979 switch (dist_units) {
3980 case VIK_UNITS_DISTANCE_KILOMETRES:
3981 label_text = g_strdup_printf ( _("Total area: %ldm x %ldm (%.3f sq. km)"), (glong)w, (glong)h, (w*h/1000000));
3983 case VIK_UNITS_DISTANCE_MILES:
3984 label_text = g_strdup_printf ( _("Total area: %ldm x %ldm (%.3f sq. miles)"), (glong)w, (glong)h, (w*h/2589988.11));
3986 case VIK_UNITS_DISTANCE_NAUTICAL_MILES:
3987 label_text = g_strdup_printf ( _("Total area: %ldm x %ldm (%.3f sq. NM)"), (glong)w, (glong)h, (w*h/(1852.0*1852.0)));
3990 label_text = g_strdup_printf ("Just to keep the compiler happy");
3991 g_critical("Houston, we've had a problem. distance=%d", dist_units);
3994 gtk_label_set_text(GTK_LABEL(pass_along[6]), label_text);
3995 g_free ( label_text );
3999 * Get an allocated filename (or directory as specified)
4001 static gchar* draw_image_filename ( VikWindow *vw, gboolean one_image_only )
4004 if ( one_image_only )
4007 if (!vw->save_img_dia) {
4008 vw->save_img_dia = gtk_file_chooser_dialog_new (_("Save Image"),
4010 GTK_FILE_CHOOSER_ACTION_SAVE,
4011 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
4012 GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT,
4014 if ( last_folder_images_uri )
4015 gtk_file_chooser_set_current_folder_uri ( GTK_FILE_CHOOSER(vw->save_img_dia), last_folder_images_uri );
4017 GtkFileChooser *chooser = GTK_FILE_CHOOSER ( vw->save_img_dia );
4019 GtkFileFilter *filter;
4020 filter = gtk_file_filter_new ();
4021 gtk_file_filter_set_name ( filter, _("All") );
4022 gtk_file_filter_add_pattern ( filter, "*" );
4023 gtk_file_chooser_add_filter ( chooser, filter );
4025 filter = gtk_file_filter_new ();
4026 gtk_file_filter_set_name ( filter, _("JPG") );
4027 gtk_file_filter_add_mime_type ( filter, "image/jpeg");
4028 gtk_file_chooser_add_filter ( chooser, filter );
4030 if ( !vw->draw_image_save_as_png )
4031 gtk_file_chooser_set_filter ( chooser, filter );
4033 filter = gtk_file_filter_new ();
4034 gtk_file_filter_set_name ( filter, _("PNG") );
4035 gtk_file_filter_add_mime_type ( filter, "image/png");
4036 gtk_file_chooser_add_filter ( chooser, filter );
4038 if ( vw->draw_image_save_as_png )
4039 gtk_file_chooser_set_filter ( chooser, filter );
4041 gtk_window_set_transient_for ( GTK_WINDOW(vw->save_img_dia), GTK_WINDOW(vw) );
4042 gtk_window_set_destroy_with_parent ( GTK_WINDOW(vw->save_img_dia), TRUE );
4045 if ( gtk_dialog_run ( GTK_DIALOG(vw->save_img_dia) ) == GTK_RESPONSE_ACCEPT ) {
4046 fn = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER(vw->save_img_dia) );
4047 if ( g_file_test ( fn, G_FILE_TEST_EXISTS ) )
4048 if ( ! a_dialog_yes_or_no ( GTK_WINDOW(vw->save_img_dia), _("The file \"%s\" exists, do you wish to overwrite it?"), a_file_basename ( fn ) ) )
4051 gtk_widget_hide ( vw->save_img_dia );
4055 // For some reason this method is only written to work in UTM...
4056 if ( vik_viewport_get_coord_mode(vw->viking_vvp) != VIK_COORD_UTM ) {
4057 a_dialog_error_msg ( GTK_WINDOW(vw), _("You must be in UTM mode to use this feature") );
4061 if (!vw->save_img_dir_dia) {
4062 vw->save_img_dir_dia = gtk_file_chooser_dialog_new (_("Choose a directory to hold images"),
4064 GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER,
4065 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
4066 GTK_STOCK_OK, GTK_RESPONSE_ACCEPT,
4068 gtk_window_set_transient_for ( GTK_WINDOW(vw->save_img_dir_dia), GTK_WINDOW(vw) );
4069 gtk_window_set_destroy_with_parent ( GTK_WINDOW(vw->save_img_dir_dia), TRUE );
4072 if ( gtk_dialog_run ( GTK_DIALOG(vw->save_img_dir_dia) ) == GTK_RESPONSE_ACCEPT ) {
4073 fn = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER(vw->save_img_dir_dia) );
4075 gtk_widget_hide ( vw->save_img_dir_dia );
4080 static void draw_to_image_file ( VikWindow *vw, gboolean one_image_only )
4082 /* todo: default for answers inside VikWindow or static (thruout instance) */
4083 GtkWidget *dialog = gtk_dialog_new_with_buttons ( _("Save to Image File"), GTK_WINDOW(vw),
4084 GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
4086 GTK_RESPONSE_REJECT,
4088 GTK_RESPONSE_ACCEPT,
4090 GtkWidget *width_label, *width_spin, *height_label, *height_spin;
4091 GtkWidget *png_radio, *jpeg_radio;
4092 GtkWidget *current_window_button;
4093 gpointer current_window_pass_along[7];
4094 GtkWidget *zoom_label, *zoom_combo;
4095 GtkWidget *total_size_label;
4097 /* only used if (!one_image_only) */
4098 GtkWidget *tiles_width_spin = NULL, *tiles_height_spin = NULL;
4100 width_label = gtk_label_new ( _("Width (pixels):") );
4101 width_spin = gtk_spin_button_new ( GTK_ADJUSTMENT(gtk_adjustment_new ( vw->draw_image_width, 10, 50000, 10, 100, 0 )), 10, 0 );
4102 height_label = gtk_label_new ( _("Height (pixels):") );
4103 height_spin = gtk_spin_button_new ( GTK_ADJUSTMENT(gtk_adjustment_new ( vw->draw_image_height, 10, 50000, 10, 100, 0 )), 10, 0 );
4105 GtkWidget *win_warning_label = gtk_label_new ( _("WARNING: USING LARGE IMAGES OVER 10000x10000\nMAY CRASH THE PROGRAM!") );
4107 zoom_label = gtk_label_new ( _("Zoom (meters per pixel):") );
4108 /* TODO: separate xzoom and yzoom factors */
4109 zoom_combo = create_zoom_combo_all_levels();
4111 gdouble mpp = vik_viewport_get_xmpp(vw->viking_vvp);
4112 gint active = 2 + round ( log (mpp) / log (2) );
4114 // Can we not hard code size here?
4119 gtk_combo_box_set_active ( GTK_COMBO_BOX(zoom_combo), active );
4121 total_size_label = gtk_label_new ( NULL );
4123 current_window_button = gtk_button_new_with_label ( _("Area in current viewable window") );
4124 current_window_pass_along [0] = vw;
4125 current_window_pass_along [1] = width_spin;
4126 current_window_pass_along [2] = height_spin;
4127 current_window_pass_along [3] = zoom_combo;
4128 current_window_pass_along [4] = NULL; /* used for one_image_only != 1 */
4129 current_window_pass_along [5] = NULL;
4130 current_window_pass_along [6] = total_size_label;
4131 g_signal_connect ( G_OBJECT(current_window_button), "button_press_event", G_CALLBACK(draw_to_image_file_current_window_cb), current_window_pass_along );
4133 png_radio = gtk_radio_button_new_with_label ( NULL, _("Save as PNG") );
4134 jpeg_radio = gtk_radio_button_new_with_label_from_widget ( GTK_RADIO_BUTTON(png_radio), _("Save as JPEG") );
4136 gtk_box_pack_start (GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), png_radio, FALSE, FALSE, 0);
4137 gtk_box_pack_start (GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), jpeg_radio, FALSE, FALSE, 0);
4139 if ( ! vw->draw_image_save_as_png )
4140 gtk_toggle_button_set_active ( GTK_TOGGLE_BUTTON(jpeg_radio), TRUE );
4142 gtk_box_pack_start (GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), width_label, FALSE, FALSE, 0);
4143 gtk_box_pack_start (GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), width_spin, FALSE, FALSE, 0);
4144 gtk_box_pack_start (GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), height_label, FALSE, FALSE, 0);
4145 gtk_box_pack_start (GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), height_spin, FALSE, FALSE, 0);
4147 gtk_box_pack_start (GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), win_warning_label, FALSE, FALSE, 0);
4149 gtk_box_pack_start (GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), current_window_button, FALSE, FALSE, 0);
4150 gtk_box_pack_start (GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), zoom_label, FALSE, FALSE, 0);
4151 gtk_box_pack_start (GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), zoom_combo, FALSE, FALSE, 0);
4153 if ( ! one_image_only )
4155 GtkWidget *tiles_width_label, *tiles_height_label;
4157 tiles_width_label = gtk_label_new ( _("East-west image tiles:") );
4158 tiles_width_spin = gtk_spin_button_new ( GTK_ADJUSTMENT(gtk_adjustment_new ( 5, 1, 10, 1, 100, 0 )), 1, 0 );
4159 tiles_height_label = gtk_label_new ( _("North-south image tiles:") );
4160 tiles_height_spin = gtk_spin_button_new ( GTK_ADJUSTMENT(gtk_adjustment_new ( 5, 1, 10, 1, 100, 0 )), 1, 0 );
4161 gtk_box_pack_start (GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), tiles_width_label, FALSE, FALSE, 0);
4162 gtk_box_pack_start (GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), tiles_width_spin, FALSE, FALSE, 0);
4163 gtk_box_pack_start (GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), tiles_height_label, FALSE, FALSE, 0);
4164 gtk_box_pack_start (GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), tiles_height_spin, FALSE, FALSE, 0);
4166 current_window_pass_along [4] = tiles_width_spin;
4167 current_window_pass_along [5] = tiles_height_spin;
4168 g_signal_connect ( G_OBJECT(tiles_width_spin), "value-changed", G_CALLBACK(draw_to_image_file_total_area_cb), current_window_pass_along );
4169 g_signal_connect ( G_OBJECT(tiles_height_spin), "value-changed", G_CALLBACK(draw_to_image_file_total_area_cb), current_window_pass_along );
4171 gtk_box_pack_start (GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), total_size_label, FALSE, FALSE, 0);
4172 g_signal_connect ( G_OBJECT(width_spin), "value-changed", G_CALLBACK(draw_to_image_file_total_area_cb), current_window_pass_along );
4173 g_signal_connect ( G_OBJECT(height_spin), "value-changed", G_CALLBACK(draw_to_image_file_total_area_cb), current_window_pass_along );
4174 g_signal_connect ( G_OBJECT(zoom_combo), "changed", G_CALLBACK(draw_to_image_file_total_area_cb), current_window_pass_along );
4176 draw_to_image_file_total_area_cb ( NULL, current_window_pass_along ); /* set correct size info now */
4178 gtk_dialog_set_default_response ( GTK_DIALOG(dialog), GTK_RESPONSE_ACCEPT );
4180 gtk_widget_show_all ( gtk_dialog_get_content_area(GTK_DIALOG(dialog)) );
4182 if ( gtk_dialog_run ( GTK_DIALOG(dialog) ) == GTK_RESPONSE_ACCEPT )
4184 gtk_widget_hide ( GTK_WIDGET(dialog) );
4186 gchar *fn = draw_image_filename ( vw, one_image_only );
4190 gint active_z = gtk_combo_box_get_active ( GTK_COMBO_BOX(zoom_combo) );
4191 gdouble zoom = pow (2, active_z-2 );
4193 if ( one_image_only )
4194 save_image_file ( vw, fn,
4195 vw->draw_image_width = gtk_spin_button_get_value_as_int ( GTK_SPIN_BUTTON(width_spin) ),
4196 vw->draw_image_height = gtk_spin_button_get_value_as_int ( GTK_SPIN_BUTTON(height_spin) ),
4198 vw->draw_image_save_as_png = gtk_toggle_button_get_active ( GTK_TOGGLE_BUTTON(png_radio) ) );
4200 // NB is in UTM mode ATM
4201 save_image_dir ( vw, fn,
4202 vw->draw_image_width = gtk_spin_button_get_value_as_int ( GTK_SPIN_BUTTON(width_spin) ),
4203 vw->draw_image_height = gtk_spin_button_get_value_as_int ( GTK_SPIN_BUTTON(height_spin) ),
4205 vw->draw_image_save_as_png = gtk_toggle_button_get_active ( GTK_TOGGLE_BUTTON(png_radio) ),
4206 gtk_spin_button_get_value ( GTK_SPIN_BUTTON(tiles_width_spin) ),
4207 gtk_spin_button_get_value ( GTK_SPIN_BUTTON(tiles_height_spin) ) );
4212 gtk_widget_destroy ( GTK_WIDGET(dialog) );
4216 static void draw_to_image_file_cb ( GtkAction *a, VikWindow *vw )
4218 draw_to_image_file ( vw, TRUE );
4221 static void draw_to_image_dir_cb ( GtkAction *a, VikWindow *vw )
4223 draw_to_image_file ( vw, FALSE );
4226 static void print_cb ( GtkAction *a, VikWindow *vw )
4228 a_print(vw, vw->viking_vvp);
4231 /* really a misnomer: changes coord mode (actual coordinates) AND/OR draw mode (viewport only) */
4232 static void window_change_coord_mode_cb ( GtkAction *old_a, GtkAction *a, VikWindow *vw )
4234 const gchar *name = gtk_action_get_name(a);
4235 GtkToggleToolButton *tbutton = (GtkToggleToolButton *)toolbar_get_widget_by_name ( vw->viking_vtb, name );
4237 gtk_toggle_tool_button_set_active ( tbutton, TRUE );
4239 VikViewportDrawMode drawmode;
4240 if (!g_strcmp0(name, "ModeUTM")) {
4241 drawmode = VIK_VIEWPORT_DRAWMODE_UTM;
4243 else if (!g_strcmp0(name, "ModeLatLon")) {
4244 drawmode = VIK_VIEWPORT_DRAWMODE_LATLON;
4246 else if (!g_strcmp0(name, "ModeExpedia")) {
4247 drawmode = VIK_VIEWPORT_DRAWMODE_EXPEDIA;
4249 else if (!g_strcmp0(name, "ModeMercator")) {
4250 drawmode = VIK_VIEWPORT_DRAWMODE_MERCATOR;
4253 g_critical("Houston, we've had a problem.");
4257 if ( !vw->only_updating_coord_mode_ui )
4259 VikViewportDrawMode olddrawmode = vik_viewport_get_drawmode ( vw->viking_vvp );
4260 if ( olddrawmode != drawmode )
4262 /* this takes care of coord mode too */
4263 vik_viewport_set_drawmode ( vw->viking_vvp, drawmode );
4264 if ( drawmode == VIK_VIEWPORT_DRAWMODE_UTM ) {
4265 vik_layers_panel_change_coord_mode ( vw->viking_vlp, VIK_COORD_UTM );
4266 } else if ( olddrawmode == VIK_VIEWPORT_DRAWMODE_UTM ) {
4267 vik_layers_panel_change_coord_mode ( vw->viking_vlp, VIK_COORD_LATLON );
4274 static void toggle_draw_scale ( GtkAction *a, VikWindow *vw )
4276 gboolean state = !vik_viewport_get_draw_scale ( vw->viking_vvp );
4277 GtkWidget *check_box = gtk_ui_manager_get_widget ( vw->uim, "/ui/MainMenu/View/SetShow/ShowScale" );
4280 gtk_check_menu_item_set_active ( GTK_CHECK_MENU_ITEM(check_box), state );
4281 vik_viewport_set_draw_scale ( vw->viking_vvp, state );
4285 static void toggle_draw_centermark ( GtkAction *a, VikWindow *vw )
4287 gboolean state = !vik_viewport_get_draw_centermark ( vw->viking_vvp );
4288 GtkWidget *check_box = gtk_ui_manager_get_widget ( vw->uim, "/ui/MainMenu/View/SetShow/ShowCenterMark" );
4291 gtk_check_menu_item_set_active ( GTK_CHECK_MENU_ITEM(check_box), state );
4292 vik_viewport_set_draw_centermark ( vw->viking_vvp, state );
4296 static void toggle_draw_highlight ( GtkAction *a, VikWindow *vw )
4298 gboolean state = !vik_viewport_get_draw_highlight ( vw->viking_vvp );
4299 GtkWidget *check_box = gtk_ui_manager_get_widget ( vw->uim, "/ui/MainMenu/View/SetShow/ShowHighlight" );
4302 gtk_check_menu_item_set_active ( GTK_CHECK_MENU_ITEM(check_box), state );
4303 vik_viewport_set_draw_highlight ( vw->viking_vvp, state );
4307 static void set_bg_color ( GtkAction *a, VikWindow *vw )
4309 GtkWidget *colorsd = gtk_color_selection_dialog_new ( _("Choose a background color") );
4310 GdkColor *color = vik_viewport_get_background_gdkcolor ( vw->viking_vvp );
4311 gtk_color_selection_set_previous_color ( GTK_COLOR_SELECTION(gtk_color_selection_dialog_get_color_selection(GTK_COLOR_SELECTION_DIALOG(colorsd))), color );
4312 gtk_color_selection_set_current_color ( GTK_COLOR_SELECTION(gtk_color_selection_dialog_get_color_selection(GTK_COLOR_SELECTION_DIALOG(colorsd))), color );
4313 if ( gtk_dialog_run ( GTK_DIALOG(colorsd) ) == GTK_RESPONSE_OK )
4315 gtk_color_selection_get_current_color ( GTK_COLOR_SELECTION(gtk_color_selection_dialog_get_color_selection(GTK_COLOR_SELECTION_DIALOG(colorsd))), color );
4316 vik_viewport_set_background_gdkcolor ( vw->viking_vvp, color );
4320 gtk_widget_destroy ( colorsd );
4323 static void set_highlight_color ( GtkAction *a, VikWindow *vw )
4325 GtkWidget *colorsd = gtk_color_selection_dialog_new ( _("Choose a track highlight color") );
4326 GdkColor *color = vik_viewport_get_highlight_gdkcolor ( vw->viking_vvp );
4327 gtk_color_selection_set_previous_color ( GTK_COLOR_SELECTION(gtk_color_selection_dialog_get_color_selection(GTK_COLOR_SELECTION_DIALOG(colorsd))), color );
4328 gtk_color_selection_set_current_color ( GTK_COLOR_SELECTION(gtk_color_selection_dialog_get_color_selection(GTK_COLOR_SELECTION_DIALOG(colorsd))), color );
4329 if ( gtk_dialog_run ( GTK_DIALOG(colorsd) ) == GTK_RESPONSE_OK )
4331 gtk_color_selection_get_current_color ( GTK_COLOR_SELECTION(gtk_color_selection_dialog_get_color_selection(GTK_COLOR_SELECTION_DIALOG(colorsd))), color );
4332 vik_viewport_set_highlight_gdkcolor ( vw->viking_vvp, color );
4336 gtk_widget_destroy ( colorsd );
4340 /***********************************************************************************************
4342 ***********************************************************************************************/
4344 static GtkActionEntry entries[] = {
4345 { "File", NULL, N_("_File"), 0, 0, 0 },
4346 { "Edit", NULL, N_("_Edit"), 0, 0, 0 },
4347 { "View", NULL, N_("_View"), 0, 0, 0 },
4348 { "SetShow", NULL, N_("_Show"), 0, 0, 0 },
4349 { "SetZoom", NULL, N_("_Zoom"), 0, 0, 0 },
4350 { "SetPan", NULL, N_("_Pan"), 0, 0, 0 },
4351 { "Layers", NULL, N_("_Layers"), 0, 0, 0 },
4352 { "Tools", NULL, N_("_Tools"), 0, 0, 0 },
4353 { "Exttools", NULL, N_("_Webtools"), 0, 0, 0 },
4354 { "Help", NULL, N_("_Help"), 0, 0, 0 },
4356 { "New", GTK_STOCK_NEW, N_("_New"), "<control>N", N_("New file"), (GCallback)newwindow_cb },
4357 { "Open", GTK_STOCK_OPEN, N_("_Open..."), "<control>O", N_("Open a file"), (GCallback)load_file },
4358 { "OpenRecentFile", NULL, N_("Open _Recent File"), NULL, NULL, (GCallback)NULL },
4359 { "Append", GTK_STOCK_ADD, N_("Append _File..."), NULL, N_("Append data from a different file"), (GCallback)load_file },
4360 { "Export", GTK_STOCK_CONVERT, N_("_Export All"), NULL, N_("Export All TrackWaypoint Layers"), (GCallback)NULL },
4361 { "ExportGPX", NULL, N_("_GPX..."), NULL, N_("Export as GPX"), (GCallback)export_to_gpx },
4362 { "Acquire", GTK_STOCK_GO_DOWN, N_("A_cquire"), NULL, NULL, (GCallback)NULL },
4363 { "AcquireGPS", NULL, N_("From _GPS..."), NULL, N_("Transfer data from a GPS device"), (GCallback)acquire_from_gps },
4364 { "AcquireGPSBabel", NULL, N_("Import File With GPS_Babel..."), NULL, N_("Import file via GPSBabel converter"), (GCallback)acquire_from_file },
4365 { "AcquireRouting", NULL, N_("_Directions..."), NULL, N_("Get driving directions"), (GCallback)acquire_from_routing },
4366 #ifdef VIK_CONFIG_OPENSTREETMAP
4367 { "AcquireOSM", NULL, N_("_OSM Traces..."), NULL, N_("Get traces from OpenStreetMap"), (GCallback)acquire_from_osm },
4368 { "AcquireMyOSM", NULL, N_("_My OSM Traces..."), NULL, N_("Get Your Own Traces from OpenStreetMap"), (GCallback)acquire_from_my_osm },
4370 #ifdef VIK_CONFIG_GEOCACHES
4371 { "AcquireGC", NULL, N_("Geo_caches..."), NULL, N_("Get Geocaches from geocaching.com"), (GCallback)acquire_from_gc },
4373 #ifdef VIK_CONFIG_GEOTAG
4374 { "AcquireGeotag", NULL, N_("From Geotagged _Images..."), NULL, N_("Create waypoints from geotagged images"), (GCallback)acquire_from_geotag },
4376 { "AcquireURL", NULL, N_("From _URL..."), NULL, N_("Get a file from a URL"), (GCallback)acquire_from_url },
4377 #ifdef VIK_CONFIG_GEONAMES
4378 { "AcquireWikipedia", NULL, N_("From _Wikipedia Waypoints"), NULL, N_("Create waypoints from Wikipedia items in the current view"), (GCallback)acquire_from_wikipedia },
4380 { "Save", GTK_STOCK_SAVE, N_("_Save"), "<control>S", N_("Save the file"), (GCallback)save_file },
4381 { "SaveAs", GTK_STOCK_SAVE_AS, N_("Save _As..."), NULL, N_("Save the file under different name"), (GCallback)save_file_as },
4382 { "FileProperties", NULL, N_("Properties..."), NULL, N_("File Properties"), (GCallback)file_properties_cb },
4383 { "GenImg", GTK_STOCK_CLEAR, N_("_Generate Image File..."), NULL, N_("Save a snapshot of the workspace into a file"), (GCallback)draw_to_image_file_cb },
4384 { "GenImgDir", GTK_STOCK_DND_MULTIPLE, N_("Generate _Directory of Images..."), NULL, N_("Generate _Directory of Images"), (GCallback)draw_to_image_dir_cb },
4385 { "Print", GTK_STOCK_PRINT, N_("_Print..."), NULL, N_("Print maps"), (GCallback)print_cb },
4386 { "Exit", GTK_STOCK_QUIT, N_("E_xit"), "<control>W", N_("Exit the program"), (GCallback)window_close },
4387 { "SaveExit", GTK_STOCK_QUIT, N_("Save and Exit"), NULL, N_("Save and Exit the program"), (GCallback)save_file_and_exit },
4389 { "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 },
4390 { "GoForward", GTK_STOCK_GO_FORWARD, N_("Go to the _Next Location"), NULL, N_("Go to the next location"), (GCallback)draw_goto_back_and_forth },
4391 { "GotoDefaultLocation", GTK_STOCK_HOME, N_("Go to the _Default Location"), NULL, N_("Go to the default location"), (GCallback)goto_default_location },
4392 { "GotoSearch", GTK_STOCK_JUMP_TO, N_("Go to _Location..."), NULL, N_("Go to address/place using text search"), (GCallback)goto_address },
4393 { "GotoLL", GTK_STOCK_JUMP_TO, N_("_Go to Lat/Lon..."), NULL, N_("Go to arbitrary lat/lon coordinate"), (GCallback)draw_goto_cb },
4394 { "GotoUTM", GTK_STOCK_JUMP_TO, N_("Go to UTM..."), NULL, N_("Go to arbitrary UTM coordinate"), (GCallback)draw_goto_cb },
4395 { "Refresh", GTK_STOCK_REFRESH, N_("_Refresh"), "F5", N_("Refresh any maps displayed"), (GCallback)draw_refresh_cb },
4396 { "SetHLColor",GTK_STOCK_SELECT_COLOR, N_("Set _Highlight Color..."), NULL, N_("Set Highlight Color"), (GCallback)set_highlight_color },
4397 { "SetBGColor",GTK_STOCK_SELECT_COLOR, N_("Set Bac_kground Color..."), NULL, N_("Set Background Color"), (GCallback)set_bg_color },
4398 { "ZoomIn", GTK_STOCK_ZOOM_IN, N_("Zoom _In"), "<control>plus", N_("Zoom In"), (GCallback)draw_zoom_cb },
4399 { "ZoomOut", GTK_STOCK_ZOOM_OUT, N_("Zoom _Out"), "<control>minus", N_("Zoom Out"), (GCallback)draw_zoom_cb },
4400 { "ZoomTo", GTK_STOCK_ZOOM_FIT, N_("Zoom _To..."), "<control>Z", N_("Zoom To"), (GCallback)zoom_to_cb },
4401 { "PanNorth", NULL, N_("Pan _North"), "<control>Up", NULL, (GCallback)draw_pan_cb },
4402 { "PanEast", NULL, N_("Pan _East"), "<control>Right", NULL, (GCallback)draw_pan_cb },
4403 { "PanSouth", NULL, N_("Pan _South"), "<control>Down", NULL, (GCallback)draw_pan_cb },
4404 { "PanWest", NULL, N_("Pan _West"), "<control>Left", NULL, (GCallback)draw_pan_cb },
4405 { "BGJobs", GTK_STOCK_EXECUTE, N_("Background _Jobs"), NULL, N_("Background Jobs"), (GCallback)a_background_show_window },
4407 { "Cut", GTK_STOCK_CUT, N_("Cu_t"), NULL, N_("Cut selected layer"), (GCallback)menu_cut_layer_cb },
4408 { "Copy", GTK_STOCK_COPY, N_("_Copy"), NULL, N_("Copy selected layer"), (GCallback)menu_copy_layer_cb },
4409 { "Paste", GTK_STOCK_PASTE, N_("_Paste"), NULL, N_("Paste layer into selected container layer or otherwise above selected layer"), (GCallback)menu_paste_layer_cb },
4410 { "Delete", GTK_STOCK_DELETE, N_("_Delete"), NULL, N_("Remove selected layer"), (GCallback)menu_delete_layer_cb },
4411 { "DeleteAll", NULL, N_("Delete All"), NULL, NULL, (GCallback)clear_cb },
4412 { "CopyCentre",NULL, N_("Copy Centre _Location"), "<control>h", NULL, (GCallback)menu_copy_centre_cb },
4413 { "MapCacheFlush",NULL, N_("_Flush Map Cache"), NULL, NULL, (GCallback)mapcache_flush_cb },
4414 { "SetDefaultLocation", GTK_STOCK_GO_FORWARD, N_("_Set the Default Location"), NULL, N_("Set the Default Location to the current position"),(GCallback)default_location_cb },
4415 { "Preferences",GTK_STOCK_PREFERENCES, N_("_Preferences"), NULL, N_("Program Preferences"), (GCallback)preferences_cb },
4416 { "LayerDefaults",GTK_STOCK_PROPERTIES, N_("_Layer Defaults"), NULL, NULL, NULL },
4417 { "Properties",GTK_STOCK_PROPERTIES, N_("_Properties"), NULL, N_("Layer Properties"), (GCallback)menu_properties_cb },
4419 { "HelpEntry", GTK_STOCK_HELP, N_("_Help"), "F1", N_("Help"), (GCallback)help_help_cb },
4420 { "About", GTK_STOCK_ABOUT, N_("_About"), NULL, N_("About"), (GCallback)help_about_cb },
4423 static GtkActionEntry debug_entries[] = {
4424 { "MapCacheInfo", NULL, "_Map Cache Info", NULL, NULL, (GCallback)help_cache_info_cb },
4425 { "BackForwardInfo", NULL, "_Back/Forward Info", NULL, NULL, (GCallback)back_forward_info_cb },
4428 static GtkActionEntry entries_gpsbabel[] = {
4429 { "ExportKML", NULL, N_("_KML..."), NULL, N_("Export as KML"), (GCallback)export_to_kml },
4432 static GtkActionEntry entries_geojson[] = {
4433 { "AcquireGeoJSON", NULL, N_("Import Geo_JSON File..."), NULL, N_("Import GeoJSON file"), (GCallback)acquire_from_geojson },
4437 static GtkRadioActionEntry mode_entries[] = {
4438 { "ModeUTM", NULL, N_("_UTM Mode"), "<control>u", NULL, VIK_VIEWPORT_DRAWMODE_UTM },
4439 { "ModeExpedia", NULL, N_("_Expedia Mode"), "<control>e", NULL, VIK_VIEWPORT_DRAWMODE_EXPEDIA },
4440 { "ModeMercator", NULL, N_("_Mercator Mode"), "<control>m", NULL, VIK_VIEWPORT_DRAWMODE_MERCATOR },
4441 { "ModeLatLon", NULL, N_("Lat_/Lon Mode"), "<control>l", NULL, VIK_VIEWPORT_DRAWMODE_LATLON },
4444 static GtkToggleActionEntry toggle_entries[] = {
4445 { "ShowScale", NULL, N_("Show _Scale"), "<shift>F5", N_("Show Scale"), (GCallback)toggle_draw_scale, TRUE },
4446 { "ShowCenterMark", NULL, N_("Show _Center Mark"), "F6", N_("Show Center Mark"), (GCallback)toggle_draw_centermark, TRUE },
4447 { "ShowHighlight", GTK_STOCK_UNDERLINE, N_("Show _Highlight"), "F7", N_("Show Highlight"), (GCallback)toggle_draw_highlight, TRUE },
4448 { "FullScreen", GTK_STOCK_FULLSCREEN, N_("_Full Screen"), "F11", N_("Activate full screen mode"), (GCallback)full_screen_cb, FALSE },
4449 { "ViewSidePanel", GTK_STOCK_INDEX, N_("Show Side _Panel"), "F9", N_("Show Side Panel"), (GCallback)view_side_panel_cb, TRUE },
4450 { "ViewStatusBar", NULL, N_("Show Status_bar"), "F12", N_("Show Statusbar"), (GCallback)view_statusbar_cb, TRUE },
4451 { "ViewToolbar", NULL, N_("Show _Toolbar"), "F3", N_("Show Toolbar"), (GCallback)view_toolbar_cb, TRUE },
4452 { "ViewMainMenu", NULL, N_("Show _Menu"), "F4", N_("Show Menu"), (GCallback)view_main_menu_cb, TRUE },
4455 // This must match the toggle entries order above
4456 static gpointer toggle_entries_toolbar_cb[] = {
4457 (GCallback)tb_set_draw_scale,
4458 (GCallback)tb_set_draw_centermark,
4459 (GCallback)tb_set_draw_highlight,
4460 (GCallback)tb_full_screen_cb,
4461 (GCallback)tb_view_side_panel_cb,
4462 (GCallback)tb_view_statusbar_cb,
4463 (GCallback)tb_view_toolbar_cb,
4464 (GCallback)tb_view_main_menu_cb,
4467 #include "menu.xml.h"
4468 static void window_create_ui( VikWindow *window )
4471 GtkActionGroup *action_group;
4472 GtkAccelGroup *accel_group;
4475 GtkIconFactory *icon_factory;
4476 GtkIconSet *icon_set;
4477 GtkRadioActionEntry *tools = NULL, *radio;
4480 uim = gtk_ui_manager_new ();
4483 toolbox_add_tool(window->vt, &ruler_tool, TOOL_LAYER_TYPE_NONE);
4484 toolbox_add_tool(window->vt, &zoom_tool, TOOL_LAYER_TYPE_NONE);
4485 toolbox_add_tool(window->vt, &pan_tool, TOOL_LAYER_TYPE_NONE);
4486 toolbox_add_tool(window->vt, &select_tool, TOOL_LAYER_TYPE_NONE);
4488 toolbar_action_tool_entry_register ( window->viking_vtb, &pan_tool.radioActionEntry );
4489 toolbar_action_tool_entry_register ( window->viking_vtb, &zoom_tool.radioActionEntry );
4490 toolbar_action_tool_entry_register ( window->viking_vtb, &ruler_tool.radioActionEntry );
4491 toolbar_action_tool_entry_register ( window->viking_vtb, &select_tool.radioActionEntry );
4494 if (!(mid = gtk_ui_manager_add_ui_from_string (uim, menu_xml, -1, &error))) {
4495 g_error_free (error);
4499 action_group = gtk_action_group_new ("MenuActions");
4500 gtk_action_group_set_translation_domain(action_group, PACKAGE_NAME);
4501 gtk_action_group_add_actions (action_group, entries, G_N_ELEMENTS (entries), window);
4502 gtk_action_group_add_toggle_actions (action_group, toggle_entries, G_N_ELEMENTS (toggle_entries), window);
4503 gtk_action_group_add_radio_actions (action_group, mode_entries, G_N_ELEMENTS (mode_entries), 4, (GCallback)window_change_coord_mode_cb, window);
4505 if ( gtk_ui_manager_add_ui_from_string ( uim,
4506 "<ui><menubar name='MainMenu'><menu action='Help'>"
4507 "<menuitem action='MapCacheInfo'/>"
4508 "<menuitem action='BackForwardInfo'/>"
4509 "</menu></menubar></ui>",
4511 gtk_action_group_add_actions (action_group, debug_entries, G_N_ELEMENTS (debug_entries), window);
4515 for ( i=0; i < G_N_ELEMENTS (entries); i++ ) {
4516 if ( entries[i].callback )
4517 toolbar_action_entry_register ( window->viking_vtb, &entries[i] );
4520 if ( G_N_ELEMENTS (toggle_entries) != G_N_ELEMENTS (toggle_entries_toolbar_cb) ) {
4521 g_print ( "Broken entries definitions\n" );
4524 for ( i=0; i < G_N_ELEMENTS (toggle_entries); i++ ) {
4525 if ( toggle_entries_toolbar_cb[i] )
4526 toolbar_action_toggle_entry_register ( window->viking_vtb, &toggle_entries[i], toggle_entries_toolbar_cb[i] );
4529 for ( i=0; i < G_N_ELEMENTS (mode_entries); i++ ) {
4530 toolbar_action_mode_entry_register ( window->viking_vtb, &mode_entries[i] );
4533 // Use this to see if GPSBabel is available:
4534 if ( a_babel_available () ) {
4535 // If going to add more entries then might be worth creating a menu_gpsbabel.xml.h file
4536 if ( gtk_ui_manager_add_ui_from_string ( uim,
4537 "<ui><menubar name='MainMenu'><menu action='File'><menu action='Export'><menuitem action='ExportKML'/></menu></menu></menubar></ui>",
4539 gtk_action_group_add_actions ( action_group, entries_gpsbabel, G_N_ELEMENTS (entries_gpsbabel), window );
4542 // GeoJSON import capability
4543 if ( g_find_program_in_path ( a_geojson_program_import() ) ) {
4544 if ( gtk_ui_manager_add_ui_from_string ( uim,
4545 "<ui><menubar name='MainMenu'><menu action='File'><menu action='Acquire'><menuitem action='AcquireGeoJSON'/></menu></menu></menubar></ui>",
4547 gtk_action_group_add_actions ( action_group, entries_geojson, G_N_ELEMENTS (entries_geojson), window );
4550 icon_factory = gtk_icon_factory_new ();
4551 gtk_icon_factory_add_default (icon_factory);
4553 register_vik_icons(icon_factory);
4555 // Copy the tool RadioActionEntries out of the main Window structure into an extending array 'tools'
4556 // so that it can be applied to the UI in one action group add function call below
4558 for (i=0; i<window->vt->n_tools; i++) {
4559 tools = g_renew(GtkRadioActionEntry, tools, ntools+1);
4560 radio = &tools[ntools];
4562 *radio = window->vt->tools[i].ti.radioActionEntry;
4563 radio->value = ntools;
4566 for (i=0; i<VIK_LAYER_NUM_TYPES; i++) {
4567 GtkActionEntry action;
4568 gtk_ui_manager_add_ui(uim, mid, "/ui/MainMenu/Layers/",
4569 vik_layer_get_interface(i)->name,
4570 vik_layer_get_interface(i)->name,
4571 GTK_UI_MANAGER_MENUITEM, FALSE);
4573 icon_set = gtk_icon_set_new_from_pixbuf (gdk_pixbuf_from_pixdata (vik_layer_get_interface(i)->icon, FALSE, NULL ));
4574 gtk_icon_factory_add (icon_factory, vik_layer_get_interface(i)->name, icon_set);
4575 gtk_icon_set_unref (icon_set);
4577 action.name = vik_layer_get_interface(i)->name;
4578 action.stock_id = vik_layer_get_interface(i)->name;
4579 action.label = g_strdup_printf( _("New _%s Layer"), vik_layer_get_interface(i)->name);
4580 action.accelerator = vik_layer_get_interface(i)->accelerator;
4581 action.tooltip = NULL;
4582 action.callback = (GCallback)menu_addlayer_cb;
4583 gtk_action_group_add_actions(action_group, &action, 1, window);
4585 g_free ( (gchar*)action.label );
4587 if ( vik_layer_get_interface(i)->tools_count ) {
4588 gtk_ui_manager_add_ui(uim, mid, "/ui/MainMenu/Tools/", vik_layer_get_interface(i)->name, NULL, GTK_UI_MANAGER_SEPARATOR, FALSE);
4591 // Further tool copying for to apply to the UI, also apply menu UI setup
4592 for ( j = 0; j < vik_layer_get_interface(i)->tools_count; j++ ) {
4593 tools = g_renew(GtkRadioActionEntry, tools, ntools+1);
4594 radio = &tools[ntools];
4597 gtk_ui_manager_add_ui(uim, mid, "/ui/MainMenu/Tools",
4598 vik_layer_get_interface(i)->tools[j].radioActionEntry.label,
4599 vik_layer_get_interface(i)->tools[j].radioActionEntry.name,
4600 GTK_UI_MANAGER_MENUITEM, FALSE);
4602 toolbox_add_tool(window->vt, &(vik_layer_get_interface(i)->tools[j]), i);
4603 toolbar_action_tool_entry_register ( window->viking_vtb, &(vik_layer_get_interface(i)->tools[j].radioActionEntry) );
4605 *radio = vik_layer_get_interface(i)->tools[j].radioActionEntry;
4606 // Overwrite with actual number to use
4607 radio->value = ntools;
4610 GtkActionEntry action_dl;
4611 gchar *layername = g_strdup_printf ( "Layer%s", vik_layer_get_interface(i)->fixed_layer_name );
4612 gtk_ui_manager_add_ui(uim, mid, "/ui/MainMenu/Edit/LayerDefaults",
4613 vik_layer_get_interface(i)->name,
4615 GTK_UI_MANAGER_MENUITEM, FALSE);
4618 // For default layers use action names of the form 'Layer<LayerName>'
4619 // This is to avoid clashing with just the layer name used above for the tool actions
4620 action_dl.name = g_strconcat("Layer", vik_layer_get_interface(i)->fixed_layer_name, NULL);
4621 action_dl.stock_id = NULL;
4622 action_dl.label = g_strconcat("_", vik_layer_get_interface(i)->name, "...", NULL); // Prepend marker for keyboard accelerator
4623 action_dl.accelerator = NULL;
4624 action_dl.tooltip = NULL;
4625 action_dl.callback = (GCallback)layer_defaults_cb;
4626 gtk_action_group_add_actions(action_group, &action_dl, 1, window);
4627 g_free ( (gchar*)action_dl.name );
4628 g_free ( (gchar*)action_dl.label );
4630 g_object_unref (icon_factory);
4632 gtk_action_group_add_radio_actions(action_group, tools, ntools, 0, (GCallback)menu_cb, window);
4635 gtk_ui_manager_insert_action_group (uim, action_group, 0);
4637 for (i=0; i<VIK_LAYER_NUM_TYPES; i++) {
4638 for ( j = 0; j < vik_layer_get_interface(i)->tools_count; j++ ) {
4639 GtkAction *action = gtk_action_group_get_action(action_group,
4640 vik_layer_get_interface(i)->tools[j].radioActionEntry.name);
4641 g_object_set(action, "sensitive", FALSE, NULL);
4645 // This is done last so we don't need to track the value of mid anymore
4646 vik_ext_tools_add_action_items ( window, window->uim, action_group, mid );
4648 window->action_group = action_group;
4650 accel_group = gtk_ui_manager_get_accel_group (uim);
4651 gtk_window_add_accel_group (GTK_WINDOW (window), accel_group);
4652 gtk_ui_manager_ensure_update (uim);
4654 setup_recent_files(window);
4658 // TODO - add method to add tool icons defined from outside this file
4659 // and remove the reverse dependency on icon definition from this file
4661 const GdkPixdata *data;
4664 { &mover_22_pixbuf, "vik-icon-pan" },
4665 { &zoom_18_pixbuf, "vik-icon-zoom" },
4666 { &ruler_18_pixbuf, "vik-icon-ruler" },
4667 { &select_18_pixbuf, "vik-icon-select" },
4668 { &vik_new_route_18_pixbuf, "vik-icon-Create Route" },
4669 { &route_finder_18_pixbuf, "vik-icon-Route Finder" },
4670 { &demdl_18_pixbuf, "vik-icon-DEM Download" },
4671 { &showpic_18_pixbuf, "vik-icon-Show Picture" },
4672 { &addtr_18_pixbuf, "vik-icon-Create Track" },
4673 { &edtr_18_pixbuf, "vik-icon-Edit Trackpoint" },
4674 { &addwp_18_pixbuf, "vik-icon-Create Waypoint" },
4675 { &edwp_18_pixbuf, "vik-icon-Edit Waypoint" },
4676 { &geozoom_18_pixbuf, "vik-icon-Georef Zoom Tool" },
4677 { &geomove_18_pixbuf, "vik-icon-Georef Move Map" },
4678 { &mapdl_18_pixbuf, "vik-icon-Maps Download" },
4681 static gint n_stock_icons = G_N_ELEMENTS (stock_icons);
4684 register_vik_icons (GtkIconFactory *icon_factory)
4686 GtkIconSet *icon_set;
4689 for (i = 0; i < n_stock_icons; i++) {
4690 icon_set = gtk_icon_set_new_from_pixbuf (gdk_pixbuf_from_pixdata (
4691 stock_icons[i].data, FALSE, NULL ));
4692 gtk_icon_factory_add (icon_factory, stock_icons[i].stock_id, icon_set);
4693 gtk_icon_set_unref (icon_set);
4697 gpointer vik_window_get_selected_trw_layer ( VikWindow *vw )
4699 return vw->selected_vtl;
4702 void vik_window_set_selected_trw_layer ( VikWindow *vw, gpointer vtl )
4704 vw->selected_vtl = vtl;
4705 vw->containing_vtl = vtl;
4707 vw->selected_track = NULL;
4708 vw->selected_tracks = NULL;
4709 vw->selected_waypoint = NULL;
4710 vw->selected_waypoints = NULL;
4711 // Set highlight thickness
4712 vik_viewport_set_highlight_thickness ( vw->viking_vvp, vik_trw_layer_get_property_tracks_line_thickness (vw->containing_vtl) );
4715 GHashTable *vik_window_get_selected_tracks ( VikWindow *vw )
4717 return vw->selected_tracks;
4720 void vik_window_set_selected_tracks ( VikWindow *vw, GHashTable *ght, gpointer vtl )
4722 vw->selected_tracks = ght;
4723 vw->containing_vtl = vtl;
4725 vw->selected_vtl = NULL;
4726 vw->selected_track = NULL;
4727 vw->selected_waypoint = NULL;
4728 vw->selected_waypoints = NULL;
4729 // Set highlight thickness
4730 vik_viewport_set_highlight_thickness ( vw->viking_vvp, vik_trw_layer_get_property_tracks_line_thickness (vw->containing_vtl) );
4733 gpointer vik_window_get_selected_track ( VikWindow *vw )
4735 return vw->selected_track;
4738 void vik_window_set_selected_track ( VikWindow *vw, gpointer *vt, gpointer vtl )
4740 vw->selected_track = vt;
4741 vw->containing_vtl = vtl;
4743 vw->selected_vtl = NULL;
4744 vw->selected_tracks = NULL;
4745 vw->selected_waypoint = NULL;
4746 vw->selected_waypoints = NULL;
4747 // Set highlight thickness
4748 vik_viewport_set_highlight_thickness ( vw->viking_vvp, vik_trw_layer_get_property_tracks_line_thickness (vw->containing_vtl) );
4751 GHashTable *vik_window_get_selected_waypoints ( VikWindow *vw )
4753 return vw->selected_waypoints;
4756 void vik_window_set_selected_waypoints ( VikWindow *vw, GHashTable *ght, gpointer vtl )
4758 vw->selected_waypoints = ght;
4759 vw->containing_vtl = vtl;
4761 vw->selected_vtl = NULL;
4762 vw->selected_track = NULL;
4763 vw->selected_tracks = NULL;
4764 vw->selected_waypoint = NULL;
4767 gpointer vik_window_get_selected_waypoint ( VikWindow *vw )
4769 return vw->selected_waypoint;
4772 void vik_window_set_selected_waypoint ( VikWindow *vw, gpointer *vwp, gpointer vtl )
4774 vw->selected_waypoint = vwp;
4775 vw->containing_vtl = vtl;
4777 vw->selected_vtl = NULL;
4778 vw->selected_track = NULL;
4779 vw->selected_tracks = NULL;
4780 vw->selected_waypoints = NULL;
4783 gboolean vik_window_clear_highlight ( VikWindow *vw )
4785 gboolean need_redraw = FALSE;
4786 if ( vw->selected_vtl != NULL ) {
4787 vw->selected_vtl = NULL;
4790 if ( vw->selected_track != NULL ) {
4791 vw->selected_track = NULL;
4794 if ( vw->selected_tracks != NULL ) {
4795 vw->selected_tracks = NULL;
4798 if ( vw->selected_waypoint != NULL ) {
4799 vw->selected_waypoint = NULL;
4802 if ( vw->selected_waypoints != NULL ) {
4803 vw->selected_waypoints = NULL;
4810 * May return NULL if the window no longer exists
4812 GThread *vik_window_get_thread ( VikWindow *vw )