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 static void window_finalize ( GObject *gob );
80 static GObjectClass *parent_class;
82 static void window_set_filename ( VikWindow *vw, const gchar *filename );
83 static const gchar *window_get_filename ( VikWindow *vw );
85 static VikWindow *window_new ();
87 static void draw_update ( VikWindow *vw );
89 static void newwindow_cb ( GtkAction *a, VikWindow *vw );
92 static void open_window ( VikWindow *vw, GSList *files );
93 static void destroy_window ( GtkWidget *widget,
98 static gboolean delete_event( VikWindow *vw );
100 static gboolean key_press_event( VikWindow *vw, GdkEventKey *event, gpointer data );
102 static void center_changed_cb ( VikWindow *vw );
103 static void window_configure_event ( VikWindow *vw );
104 static void draw_sync ( VikWindow *vw );
105 static void draw_redraw ( VikWindow *vw );
106 static void draw_scroll ( VikWindow *vw, GdkEventScroll *event );
107 static void draw_click ( VikWindow *vw, GdkEventButton *event );
108 static void draw_release ( VikWindow *vw, GdkEventButton *event );
109 static void draw_mouse_motion ( VikWindow *vw, GdkEventMotion *event );
110 static void draw_zoom_cb ( GtkAction *a, VikWindow *vw );
111 static void draw_goto_cb ( GtkAction *a, VikWindow *vw );
112 static void draw_refresh_cb ( GtkAction *a, VikWindow *vw );
114 static void draw_status ( VikWindow *vw );
116 /* End Drawing Functions */
118 static void toggle_draw_scale ( GtkAction *a, VikWindow *vw );
119 static void toggle_draw_centermark ( GtkAction *a, VikWindow *vw );
120 static void toggle_draw_highlight ( GtkAction *a, VikWindow *vw );
122 static void menu_addlayer_cb ( GtkAction *a, VikWindow *vw );
123 static void menu_properties_cb ( GtkAction *a, VikWindow *vw );
124 static void menu_delete_layer_cb ( GtkAction *a, VikWindow *vw );
126 /* tool management */
132 #define TOOL_LAYER_TYPE_NONE -1
137 toolbox_tool_t *tools;
141 static void menu_cb ( GtkAction *old, GtkAction *a, VikWindow *vw );
142 static void window_change_coord_mode_cb ( GtkAction *old, GtkAction *a, VikWindow *vw );
143 static toolbox_tools_t* toolbox_create(VikWindow *vw);
144 static void toolbox_add_tool(toolbox_tools_t *vt, VikToolInterface *vti, gint layer_type );
145 static int toolbox_get_tool(toolbox_tools_t *vt, const gchar *tool_name);
146 static void toolbox_activate(toolbox_tools_t *vt, const gchar *tool_name);
147 static const GdkCursor *toolbox_get_cursor(toolbox_tools_t *vt, const gchar *tool_name);
148 static void toolbox_click (toolbox_tools_t *vt, GdkEventButton *event);
149 static void toolbox_move (toolbox_tools_t *vt, GdkEventMotion *event);
150 static void toolbox_release (toolbox_tools_t *vt, GdkEventButton *event);
154 static void window_create_ui( VikWindow *window );
155 static void register_vik_icons (GtkIconFactory *icon_factory);
158 static void load_file ( GtkAction *a, VikWindow *vw );
159 static gboolean save_file_as ( GtkAction *a, VikWindow *vw );
160 static gboolean save_file ( GtkAction *a, VikWindow *vw );
161 static gboolean save_file_and_exit ( GtkAction *a, VikWindow *vw );
162 static gboolean window_save ( VikWindow *vw );
167 VikViewport *viking_vvp;
168 VikLayersPanel *viking_vlp;
169 VikStatusbar *viking_vs;
170 VikToolbar *viking_vtb;
172 GtkWidget *main_vbox;
173 GtkWidget *menu_hbox;
175 GdkCursor *busy_cursor;
176 GdkCursor *viewport_cursor; // only a reference
178 /* tool management state */
181 guint16 tool_layer_id;
182 guint16 tool_tool_id;
184 GtkActionGroup *action_group;
187 // NB scale, centermark and highlight are in viewport.
188 gboolean show_full_screen;
189 gboolean show_side_panel;
190 gboolean show_statusbar;
191 gboolean show_toolbar;
192 gboolean show_main_menu;
194 gboolean select_move;
197 gint delayed_pan_x, delayed_pan_y; // Temporary storage
198 gboolean single_click_pending;
200 guint draw_image_width, draw_image_height;
201 gboolean draw_image_save_as_png;
205 VikLoadType_t loaded_type;
207 GtkWidget *open_dia, *save_dia;
208 GtkWidget *save_img_dia, *save_img_dir_dia;
210 gboolean only_updating_coord_mode_ui; /* hack for a bug in GTK */
214 /* half-drawn update */
216 VikCoord trigger_center;
218 /* Store at this level for highlighted selection drawing since it applies to the viewport and the layers panel */
219 /* Only one of these items can be selected at the same time */
220 gpointer selected_vtl; /* notionally VikTrwLayer */
221 GHashTable *selected_tracks;
222 gpointer selected_track; /* notionally VikTrack */
223 GHashTable *selected_waypoints;
224 gpointer selected_waypoint; /* notionally VikWaypoint */
225 /* only use for individual track or waypoint */
226 /* For track(s) & waypoint(s) it is the layer they are in - this helps refering to the individual item easier */
227 gpointer containing_vtl; /* notionally VikTrwLayer */
241 VW_OPENWINDOW_SIGNAL,
245 static guint window_signals[VW_LAST_SIGNAL] = { 0 };
247 // TODO get rid of this as this is unnecessary duplication...
248 static gchar *tool_names[NUMBER_OF_TOOLS] = { N_("Pan"), N_("Zoom"), N_("Ruler"), N_("Select") };
250 G_DEFINE_TYPE (VikWindow, vik_window, GTK_TYPE_WINDOW)
252 VikViewport * vik_window_viewport(VikWindow *vw)
254 return(vw->viking_vvp);
257 VikLayersPanel * vik_window_layers_panel(VikWindow *vw)
259 return(vw->viking_vlp);
263 * Returns the statusbar for the window
265 VikStatusbar * vik_window_get_statusbar ( VikWindow *vw )
267 return vw->viking_vs;
271 * Returns the 'project' filename
273 const gchar *vik_window_get_filename (VikWindow *vw)
280 vik_statusbar_type_t vs_type;
281 gchar* message; // Always make a copy of this data
282 } statusbar_idle_data;
285 * For the actual statusbar update!
287 static gboolean statusbar_idle_update ( statusbar_idle_data *sid )
289 vik_statusbar_set_message ( sid->vs, sid->vs_type, sid->message );
290 g_free ( sid->message );
296 * vik_window_statusbar_update:
297 * @vw: The main window in which the statusbar will be updated.
298 * @message: The string to be displayed. This is copied.
299 * @vs_type: The part of the statusbar to be updated.
301 * This updates any part of the statusbar with the new string.
302 * It handles calling from the main thread or any background thread
303 * ATM this mostly used from background threads - as from the main thread
304 * one may use the vik_statusbar_set_message() directly.
306 void vik_window_statusbar_update ( VikWindow *vw, const gchar* message, vik_statusbar_type_t vs_type )
308 GThread *thread = vik_window_get_thread ( vw );
313 statusbar_idle_data *sid = g_malloc ( sizeof (statusbar_idle_data) );
314 sid->vs = vw->viking_vs;
315 sid->vs_type = vs_type;
316 sid->message = g_strdup ( message );
318 if ( g_thread_self() == thread ) {
319 g_idle_add ( (GSourceFunc) statusbar_idle_update, sid );
322 // From a background thread
323 gdk_threads_add_idle ( (GSourceFunc) statusbar_idle_update, sid );
327 // Actual signal handlers
328 static void destroy_window ( GtkWidget *widget,
331 if ( ! --window_count )
335 #define VIK_SETTINGS_WIN_SIDEPANEL "window_sidepanel"
336 #define VIK_SETTINGS_WIN_STATUSBAR "window_statusbar"
337 #define VIK_SETTINGS_WIN_TOOLBAR "window_toolbar"
338 // Menubar setting to off is never auto saved in case it's accidentally turned off
339 // It's not so obvious so to recover the menu visibility.
340 // Thus this value is for setting manually via editting the settings file directly
341 #define VIK_SETTINGS_WIN_MENUBAR "window_menubar"
343 VikWindow *vik_window_new_window ()
345 if ( window_count < MAX_WINDOWS )
347 VikWindow *vw = window_new ();
349 g_signal_connect (G_OBJECT (vw), "destroy",
350 G_CALLBACK (destroy_window), NULL);
351 g_signal_connect (G_OBJECT (vw), "newwindow",
352 G_CALLBACK (vik_window_new_window), NULL);
353 g_signal_connect (G_OBJECT (vw), "openwindow",
354 G_CALLBACK (open_window), NULL);
356 gtk_widget_show_all ( GTK_WIDGET(vw) );
358 if ( a_vik_get_restore_window_state() ) {
359 // These settings are applied after the show all as these options hide widgets
361 if ( a_settings_get_boolean ( VIK_SETTINGS_WIN_SIDEPANEL, &sidepanel ) )
363 gtk_widget_hide ( GTK_WIDGET(vw->viking_vlp) );
364 GtkWidget *check_box = gtk_ui_manager_get_widget ( vw->uim, "/ui/MainMenu/View/SetShow/ViewSidePanel" );
365 gtk_check_menu_item_set_active ( GTK_CHECK_MENU_ITEM(check_box), FALSE );
369 if ( a_settings_get_boolean ( VIK_SETTINGS_WIN_STATUSBAR, &statusbar ) )
371 gtk_widget_hide ( GTK_WIDGET(vw->viking_vs) );
372 GtkWidget *check_box = gtk_ui_manager_get_widget ( vw->uim, "/ui/MainMenu/View/SetShow/ViewStatusBar" );
373 gtk_check_menu_item_set_active ( GTK_CHECK_MENU_ITEM(check_box), FALSE );
377 if ( a_settings_get_boolean ( VIK_SETTINGS_WIN_TOOLBAR, &toolbar ) )
379 gtk_widget_hide ( toolbar_get_widget (vw->viking_vtb) );
380 GtkWidget *check_box = gtk_ui_manager_get_widget ( vw->uim, "/ui/MainMenu/View/SetShow/ViewToolBar" );
381 gtk_check_menu_item_set_active ( GTK_CHECK_MENU_ITEM(check_box), FALSE );
385 if ( a_settings_get_boolean ( VIK_SETTINGS_WIN_MENUBAR, &menubar ) )
387 gtk_widget_hide ( gtk_ui_manager_get_widget ( vw->uim, "/ui/MainMenu" ) );
388 GtkWidget *check_box = gtk_ui_manager_get_widget ( vw->uim, "/ui/MainMenu/View/SetShow/ViewMainMenu" );
389 gtk_check_menu_item_set_active ( GTK_CHECK_MENU_ITEM(check_box), FALSE );
400 * determine_location_thread:
401 * @vw: The window that will get updated
402 * @threaddata: Data used by our background thread mechanism
404 * Use the features in vikgoto to determine where we are
405 * Then set up the viewport:
406 * 1. To goto the location
407 * 2. Set an appropriate level zoom for the location type
408 * 3. Some statusbar message feedback
410 static int determine_location_thread ( VikWindow *vw, gpointer threaddata )
414 gint ans = a_vik_goto_where_am_i ( vw->viking_vvp, &ll, &name );
416 int result = a_background_thread_progress ( threaddata, 1.0 );
418 vik_window_statusbar_update ( vw, _("Location lookup aborted"), VIK_STATUSBAR_INFO );
419 return -1; /* Abort thread */
427 // Position found with city precision - so zoom out more
430 else if ( ans == 3 ) {
431 // Position found via country name search - so zoom wayyyy out
435 vik_viewport_set_zoom ( vw->viking_vvp, zoom );
436 vik_viewport_set_center_latlon ( vw->viking_vvp, &ll, FALSE );
438 gchar *message = g_strdup_printf ( _("Location found: %s"), name );
439 vik_window_statusbar_update ( vw, message, VIK_STATUSBAR_INFO );
443 // Signal to redraw from the background
444 vik_layers_panel_emit_update ( vw->viking_vlp );
447 vik_window_statusbar_update ( vw, _("Unable to determine location"), VIK_STATUSBAR_INFO );
453 * Steps to be taken once initial loading has completed
455 void vik_window_new_window_finish ( VikWindow *vw )
457 // Don't add a map if we've loaded a Viking file already
461 if ( a_vik_get_startup_method ( ) == VIK_STARTUP_METHOD_SPECIFIED_FILE ) {
462 vik_window_open_file ( vw, a_vik_get_startup_file(), TRUE );
467 // Maybe add a default map layer
468 if ( a_vik_get_add_default_map_layer () ) {
469 VikMapsLayer *vml = VIK_MAPS_LAYER ( vik_layer_create(VIK_LAYER_MAPS, vw->viking_vvp, FALSE) );
470 vik_layer_rename ( VIK_LAYER(vml), _("Default Map") );
471 vik_aggregate_layer_add_layer ( vik_layers_panel_get_top_layer(vw->viking_vlp), VIK_LAYER(vml), TRUE );
476 // If not loaded any file, maybe try the location lookup
477 if ( vw->loaded_type == LOAD_TYPE_READ_FAILURE ) {
478 if ( a_vik_get_startup_method ( ) == VIK_STARTUP_METHOD_AUTO_LOCATION ) {
480 vik_statusbar_set_message ( vw->viking_vs, VIK_STATUSBAR_INFO, _("Trying to determine location...") );
482 a_background_thread ( GTK_WINDOW(vw),
483 _("Determining location"),
484 (vik_thr_func) determine_location_thread,
493 static void open_window ( VikWindow *vw, GSList *files )
495 gboolean change_fn = (g_slist_length(files) == 1); /* only change fn if one file */
496 GSList *cur_file = files;
498 // Only open a new window if a viking file
499 gchar *file_name = cur_file->data;
500 if (vw != NULL && vw->filename && check_file_magic_vik ( file_name ) ) {
501 VikWindow *newvw = vik_window_new_window ();
503 vik_window_open_file ( newvw, file_name, TRUE );
506 vik_window_open_file ( vw, file_name, change_fn );
509 cur_file = g_slist_next (cur_file);
511 g_slist_free (files);
515 void vik_window_selected_layer(VikWindow *vw, VikLayer *vl)
517 int i, j, tool_count;
518 VikLayerInterface *layer_interface;
520 if (!vw->action_group) return;
522 for (i=0; i<VIK_LAYER_NUM_TYPES; i++) {
524 layer_interface = vik_layer_get_interface(i);
525 tool_count = layer_interface->tools_count;
527 for (j = 0; j < tool_count; j++) {
528 action = gtk_action_group_get_action(vw->action_group,
529 layer_interface->tools[j].radioActionEntry.name);
530 g_object_set(action, "sensitive", i == vl->type, NULL);
531 toolbar_action_set_sensitive ( vw->viking_vtb, vik_layer_get_interface(i)->tools[j].radioActionEntry.name, i == vl->type );
536 static void window_finalize ( GObject *gob )
538 VikWindow *vw = VIK_WINDOW(gob);
539 g_return_if_fail ( vw != NULL );
541 a_background_remove_window ( vw );
543 window_list = g_slist_remove ( window_list, vw );
545 gdk_cursor_unref ( vw->busy_cursor );
547 for (tt = 0; tt < vw->vt->n_tools; tt++ )
548 if ( vw->vt->tools[tt].ti.destroy )
549 vw->vt->tools[tt].ti.destroy ( vw->vt->tools[tt].state );
550 g_free ( vw->vt->tools );
553 vik_toolbar_finalize ( vw->viking_vtb );
555 G_OBJECT_CLASS(parent_class)->finalize(gob);
559 static void vik_window_class_init ( VikWindowClass *klass )
562 GObjectClass *object_class;
564 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);
565 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);
567 object_class = G_OBJECT_CLASS (klass);
569 object_class->finalize = window_finalize;
571 parent_class = g_type_class_peek_parent (klass);
575 static void zoom_changed (GtkMenuShell *menushell,
578 VikWindow *vw = VIK_WINDOW (user_data);
580 GtkWidget *aw = gtk_menu_get_active ( GTK_MENU (menushell) );
581 gint active = GPOINTER_TO_INT(g_object_get_data ( G_OBJECT (aw), "position" ));
583 gdouble zoom_request = pow (2, active-5 );
585 // But has it really changed?
586 gdouble current_zoom = vik_viewport_get_zoom ( vw->viking_vvp );
587 if ( current_zoom != 0.0 && zoom_request != current_zoom ) {
588 vik_viewport_set_zoom ( vw->viking_vvp, zoom_request );
589 // Force drawing update
595 * @mpp: The initial zoom level
597 static GtkWidget *create_zoom_menu_all_levels ( gdouble mpp )
599 GtkWidget *menu = gtk_menu_new ();
600 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" };
603 for (i = 0 ; i < G_N_ELEMENTS(itemLabels) ; i++)
605 GtkWidget *item = gtk_menu_item_new_with_label (itemLabels[i]);
606 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
607 gtk_widget_show (item);
608 g_object_set_data (G_OBJECT (item), "position", GINT_TO_POINTER(i));
611 gint active = 5 + round ( log (mpp) / log (2) );
612 // Ensure value derived from mpp is in bounds of the menu
613 if ( active >= G_N_ELEMENTS(itemLabels) )
614 active = G_N_ELEMENTS(itemLabels) - 1;
617 gtk_menu_set_active ( GTK_MENU(menu), active );
622 static GtkWidget *create_zoom_combo_all_levels ()
624 GtkWidget *combo = vik_combo_box_text_new();
625 vik_combo_box_text_append ( combo, "0.25");
626 vik_combo_box_text_append ( combo, "0.5");
627 vik_combo_box_text_append ( combo, "1");
628 vik_combo_box_text_append ( combo, "2");
629 vik_combo_box_text_append ( combo, "4");
630 vik_combo_box_text_append ( combo, "8");
631 vik_combo_box_text_append ( combo, "16");
632 vik_combo_box_text_append ( combo, "32");
633 vik_combo_box_text_append ( combo, "64");
634 vik_combo_box_text_append ( combo, "128");
635 vik_combo_box_text_append ( combo, "256");
636 vik_combo_box_text_append ( combo, "512");
637 vik_combo_box_text_append ( combo, "1024");
638 vik_combo_box_text_append ( combo, "2048");
639 vik_combo_box_text_append ( combo, "4096");
640 vik_combo_box_text_append ( combo, "8192");
641 vik_combo_box_text_append ( combo, "16384");
642 vik_combo_box_text_append ( combo, "32768");
644 gtk_widget_set_tooltip_text (combo, _("Select zoom level"));
648 static gint zoom_popup_handler (GtkWidget *widget)
652 g_return_val_if_fail (widget != NULL, FALSE);
653 g_return_val_if_fail (GTK_IS_MENU (widget), FALSE);
655 /* The "widget" is the menu that was supplied when
656 * g_signal_connect_swapped() was called.
658 menu = GTK_MENU (widget);
660 gtk_menu_popup (menu, NULL, NULL, NULL, NULL,
661 1, gtk_get_current_event_time());
669 static void drag_data_received_cb ( GtkWidget *widget,
670 GdkDragContext *context,
673 GtkSelectionData *selection_data,
678 gboolean success = FALSE;
680 if ( (selection_data != NULL) && (gtk_selection_data_get_length(selection_data) > 0) ) {
681 switch (target_type) {
683 gchar *str = (gchar*)gtk_selection_data_get_data(selection_data);
684 g_debug ("drag received string:%s \n", str);
686 // Convert string into GSList of individual entries for use with our open signal
687 gchar **entries = g_strsplit(str, "\r\n", 0);
688 GSList *filenames = NULL;
689 gint entry_runner = 0;
690 gchar *entry = entries[entry_runner];
692 if ( g_strcmp0 ( entry, "" ) ) {
693 // Drag+Drop gives URIs. And so in particular, %20 in place of spaces in filenames
694 // thus need to convert the text into a plain string
695 gchar *filename = g_filename_from_uri ( entry, NULL, NULL );
697 filenames = g_slist_append ( filenames, filename );
700 entry = entries[entry_runner];
704 g_signal_emit ( G_OBJECT(VIK_WINDOW_FROM_WIDGET(widget)), window_signals[VW_OPENWINDOW_SIGNAL], 0, filenames );
705 // NB: GSList & contents are freed by main.open_window
714 gtk_drag_finish ( context, success, FALSE, time );
717 static void toolbar_tool_cb ( GtkAction *old, GtkAction *current, gpointer gp )
719 VikWindow *vw = (VikWindow*)gp;
720 GtkAction *action = gtk_action_group_get_action ( vw->action_group, gtk_action_get_name(current) );
722 gtk_action_activate ( action );
725 static void toolbar_reload_cb ( GtkActionGroup *grp, gpointer gp )
727 VikWindow *vw = (VikWindow*)gp;
728 center_changed_cb ( vw );
731 #define VIK_SETTINGS_WIN_MAX "window_maximized"
732 #define VIK_SETTINGS_WIN_FULLSCREEN "window_fullscreen"
733 #define VIK_SETTINGS_WIN_WIDTH "window_width"
734 #define VIK_SETTINGS_WIN_HEIGHT "window_height"
735 #define VIK_SETTINGS_WIN_PANE_POSITION "window_horizontal_pane_position"
736 #define VIK_SETTINGS_WIN_SAVE_IMAGE_WIDTH "window_save_image_width"
737 #define VIK_SETTINGS_WIN_SAVE_IMAGE_HEIGHT "window_save_image_height"
738 #define VIK_SETTINGS_WIN_SAVE_IMAGE_PNG "window_save_image_as_png"
739 #define VIK_SETTINGS_WIN_COPY_CENTRE_FULL_FORMAT "window_copy_centre_full_format"
741 #define VIKING_ACCELERATOR_KEY_FILE "keys.rc"
743 static void vik_window_init ( VikWindow *vw )
745 vw->action_group = NULL;
747 vw->viking_vvp = vik_viewport_new();
748 vw->viking_vlp = vik_layers_panel_new();
749 vik_layers_panel_set_viewport ( vw->viking_vlp, vw->viking_vvp );
750 vw->viking_vs = vik_statusbar_new();
752 vw->vt = toolbox_create(vw);
753 vw->viking_vtb = vik_toolbar_new ();
754 window_create_ui(vw);
755 window_set_filename (vw, NULL);
757 vw->busy_cursor = gdk_cursor_new ( GDK_WATCH );
760 vw->loaded_type = LOAD_TYPE_READ_FAILURE; //AKA none
761 vw->modified = FALSE;
762 vw->only_updating_coord_mode_ui = FALSE;
764 vw->select_move = FALSE;
765 vw->pan_move = FALSE;
766 vw->pan_x = vw->pan_y = -1;
767 vw->single_click_pending = FALSE;
769 gint draw_image_width;
770 if ( a_settings_get_integer ( VIK_SETTINGS_WIN_SAVE_IMAGE_WIDTH, &draw_image_width ) )
771 vw->draw_image_width = draw_image_width;
773 vw->draw_image_width = DRAW_IMAGE_DEFAULT_WIDTH;
774 gint draw_image_height;
775 if ( a_settings_get_integer ( VIK_SETTINGS_WIN_SAVE_IMAGE_HEIGHT, &draw_image_height ) )
776 vw->draw_image_height = draw_image_height;
778 vw->draw_image_height = DRAW_IMAGE_DEFAULT_HEIGHT;
779 gboolean draw_image_save_as_png;
780 if ( a_settings_get_boolean ( VIK_SETTINGS_WIN_SAVE_IMAGE_PNG, &draw_image_save_as_png ) )
781 vw->draw_image_save_as_png = draw_image_save_as_png;
783 vw->draw_image_save_as_png = DRAW_IMAGE_DEFAULT_SAVE_AS_PNG;
785 vw->main_vbox = gtk_vbox_new(FALSE, 1);
786 gtk_container_add (GTK_CONTAINER (vw), vw->main_vbox);
787 vw->menu_hbox = gtk_hbox_new(FALSE, 1);
788 GtkWidget *menu_bar = gtk_ui_manager_get_widget (vw->uim, "/MainMenu");
789 gtk_box_pack_start (GTK_BOX(vw->menu_hbox), menu_bar, FALSE, TRUE, 0);
790 gtk_box_pack_start (GTK_BOX(vw->main_vbox), vw->menu_hbox, FALSE, TRUE, 0);
792 toolbar_init(vw->viking_vtb,
798 (gpointer)vw); // This auto packs toolbar into the vbox
799 // Must be performed post toolbar init
801 for (i=0; i<VIK_LAYER_NUM_TYPES; i++) {
802 for ( j = 0; j < vik_layer_get_interface(i)->tools_count; j++ ) {
803 toolbar_action_set_sensitive ( vw->viking_vtb, vik_layer_get_interface(i)->tools[j].radioActionEntry.name, FALSE );
807 vik_ext_tool_datasources_add_menu_items ( vw, vw->uim );
809 GtkWidget * zoom_levels = gtk_ui_manager_get_widget (vw->uim, "/MainMenu/View/SetZoom");
810 GtkWidget * zoom_levels_menu = create_zoom_menu_all_levels ( vik_viewport_get_zoom(vw->viking_vvp) );
811 gtk_menu_item_set_submenu (GTK_MENU_ITEM (zoom_levels), zoom_levels_menu);
812 g_signal_connect ( G_OBJECT(zoom_levels_menu), "selection-done", G_CALLBACK(zoom_changed), vw);
813 g_signal_connect_swapped ( G_OBJECT(vw->viking_vs), "clicked", G_CALLBACK(zoom_popup_handler), zoom_levels_menu );
815 g_signal_connect (G_OBJECT (vw), "delete_event", G_CALLBACK (delete_event), NULL);
818 g_signal_connect_swapped (G_OBJECT(vw->viking_vvp), "updated_center", G_CALLBACK(center_changed_cb), vw);
820 g_signal_connect_swapped (G_OBJECT(vw->viking_vvp), "expose_event", G_CALLBACK(draw_sync), vw);
821 g_signal_connect_swapped (G_OBJECT(vw->viking_vvp), "configure_event", G_CALLBACK(window_configure_event), vw);
822 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 );
823 g_signal_connect_swapped (G_OBJECT(vw->viking_vvp), "scroll_event", G_CALLBACK(draw_scroll), vw);
824 g_signal_connect_swapped (G_OBJECT(vw->viking_vvp), "button_press_event", G_CALLBACK(draw_click), vw);
825 g_signal_connect_swapped (G_OBJECT(vw->viking_vvp), "button_release_event", G_CALLBACK(draw_release), vw);
826 g_signal_connect_swapped (G_OBJECT(vw->viking_vvp), "motion_notify_event", G_CALLBACK(draw_mouse_motion), vw);
828 g_signal_connect_swapped (G_OBJECT(vw->viking_vlp), "update", G_CALLBACK(draw_update), vw);
829 g_signal_connect_swapped (G_OBJECT(vw->viking_vlp), "delete_layer", G_CALLBACK(vik_window_clear_highlight), vw);
831 // Allow key presses to be processed anywhere
832 g_signal_connect_swapped (G_OBJECT (vw), "key_press_event", G_CALLBACK (key_press_event), vw);
834 // Set initial button sensitivity
835 center_changed_cb ( vw );
837 vw->hpaned = gtk_hpaned_new ();
838 gtk_paned_pack1 ( GTK_PANED(vw->hpaned), GTK_WIDGET (vw->viking_vlp), FALSE, FALSE );
839 gtk_paned_pack2 ( GTK_PANED(vw->hpaned), GTK_WIDGET (vw->viking_vvp), TRUE, TRUE );
841 /* This packs the button into the window (a gtk container). */
842 gtk_box_pack_start (GTK_BOX(vw->main_vbox), vw->hpaned, TRUE, TRUE, 0);
844 gtk_box_pack_end (GTK_BOX(vw->main_vbox), GTK_WIDGET(vw->viking_vs), FALSE, TRUE, 0);
846 a_background_add_window ( vw );
848 window_list = g_slist_prepend ( window_list, vw);
850 gint height = VIKING_WINDOW_HEIGHT;
851 gint width = VIKING_WINDOW_WIDTH;
853 if ( a_vik_get_restore_window_state() ) {
854 if ( a_settings_get_integer ( VIK_SETTINGS_WIN_HEIGHT, &height ) ) {
855 // Enforce a basic minimum size
860 // No setting - so use default
861 height = VIKING_WINDOW_HEIGHT;
863 if ( a_settings_get_integer ( VIK_SETTINGS_WIN_WIDTH, &width ) ) {
864 // Enforce a basic minimum size
869 // No setting - so use default
870 width = VIKING_WINDOW_WIDTH;
873 if ( a_settings_get_boolean ( VIK_SETTINGS_WIN_MAX, &maxed ) )
875 gtk_window_maximize ( GTK_WINDOW(vw) );
878 if ( a_settings_get_boolean ( VIK_SETTINGS_WIN_FULLSCREEN, &full ) ) {
880 vw->show_full_screen = TRUE;
881 gtk_window_fullscreen ( GTK_WINDOW(vw) );
882 GtkWidget *check_box = gtk_ui_manager_get_widget ( vw->uim, "/ui/MainMenu/View/FullScreen" );
884 gtk_check_menu_item_set_active ( GTK_CHECK_MENU_ITEM(check_box), TRUE );
888 gint position = -1; // Let GTK determine default positioning
889 if ( !a_settings_get_integer ( VIK_SETTINGS_WIN_PANE_POSITION, &position ) ) {
892 gtk_paned_set_position ( GTK_PANED(vw->hpaned), position );
895 gtk_window_set_default_size ( GTK_WINDOW(vw), width, height );
899 vw->save_img_dia = NULL;
900 vw->save_img_dir_dia = NULL;
902 vw->show_side_panel = TRUE;
903 vw->show_statusbar = TRUE;
904 vw->show_toolbar = TRUE;
905 vw->show_main_menu = TRUE;
907 // Only accept Drag and Drop of files onto the viewport
908 gtk_drag_dest_set ( GTK_WIDGET(vw->viking_vvp), GTK_DEST_DEFAULT_ALL, NULL, 0, GDK_ACTION_COPY );
909 gtk_drag_dest_add_uri_targets ( GTK_WIDGET(vw->viking_vvp) );
910 g_signal_connect ( GTK_WIDGET(vw->viking_vvp), "drag-data-received", G_CALLBACK(drag_data_received_cb), NULL );
912 // Store the thread value so comparisons can be made to determine the gdk update method
913 // Hopefully we are storing the main thread value here :)
914 // [ATM any window initialization is always be performed by the main thread]
915 vw->thread = g_thread_self();
917 // Set the default tool + mode
918 gtk_action_activate ( gtk_action_group_get_action ( vw->action_group, "Pan" ) );
919 gtk_action_activate ( gtk_action_group_get_action ( vw->action_group, "ModeMercator" ) );
921 gchar *accel_file_name = g_build_filename ( a_get_viking_dir(), VIKING_ACCELERATOR_KEY_FILE, NULL );
922 gtk_accel_map_load ( accel_file_name );
923 g_free ( accel_file_name );
926 static VikWindow *window_new ()
928 return VIK_WINDOW ( g_object_new ( VIK_WINDOW_TYPE, NULL ) );
932 * Update the displayed map
933 * Only update the top most visible map layer
934 * ATM this assumes (as per defaults) the top most map has full alpha setting
935 * such that other other maps even though they may be active will not be seen
936 * It's more complicated to work out which maps are actually visible due to alpha settings
937 * and overkill for this simple refresh method.
939 static void simple_map_update ( VikWindow *vw, gboolean only_new )
941 // Find the most relevent single map layer to operate on
942 VikLayer *vl = vik_aggregate_layer_get_top_visible_layer_of_type (vik_layers_panel_get_top_layer(vw->viking_vlp), VIK_LAYER_MAPS);
944 vik_maps_layer_download ( VIK_MAPS_LAYER(vl), vw->viking_vvp, only_new );
948 * This is the global key press handler
949 * Global shortcuts are available at any time and hence are not restricted to when a certain tool is enabled
951 static gboolean key_press_event( VikWindow *vw, GdkEventKey *event, gpointer data )
953 // The keys handled here are not in the menuing system for a couple of reasons:
954 // . Keeps the menu size compact (alebit at expense of discoverably)
955 // . Allows differing key bindings to perform the same actions
957 // First decide if key events are related to the maps layer
958 gboolean map_download = FALSE;
959 gboolean map_download_only_new = TRUE; // Only new or reload
961 GdkModifierType modifiers = gtk_accelerator_get_default_mod_mask();
963 // Standard 'Refresh' keys: F5 or Ctrl+r
964 // Note 'F5' is actually handled via draw_refresh_cb() later on
965 // (not 'R' it's 'r' notice the case difference!!)
966 if ( event->keyval == GDK_r && (event->state & modifiers) == GDK_CONTROL_MASK ) {
968 map_download_only_new = TRUE;
970 // Full cache reload with Ctrl+F5 or Ctrl+Shift+r [This is not in the menu system]
971 // Note the use of uppercase R here since shift key has been pressed
972 else if ( (event->keyval == GDK_F5 && (event->state & modifiers) == GDK_CONTROL_MASK ) ||
973 ( event->keyval == GDK_R && (event->state & modifiers) == (GDK_CONTROL_MASK + GDK_SHIFT_MASK) ) ) {
975 map_download_only_new = FALSE;
977 // Standard Ctrl+KP+ / Ctrl+KP- to zoom in/out respectively
978 else if ( event->keyval == GDK_KEY_KP_Add && (event->state & modifiers) == GDK_CONTROL_MASK ) {
979 vik_viewport_zoom_in ( vw->viking_vvp );
981 return TRUE; // handled keypress
983 else if ( event->keyval == GDK_KEY_KP_Subtract && (event->state & modifiers) == GDK_CONTROL_MASK ) {
984 vik_viewport_zoom_out ( vw->viking_vvp );
986 return TRUE; // handled keypress
989 if ( map_download ) {
990 simple_map_update ( vw, map_download_only_new );
991 return TRUE; // handled keypress
994 VikLayer *vl = vik_layers_panel_get_selected ( vw->viking_vlp );
995 if (vl && vw->vt->active_tool != -1 && vw->vt->tools[vw->vt->active_tool].ti.key_press ) {
996 gint ltype = vw->vt->tools[vw->vt->active_tool].layer_type;
997 if ( vl && ltype == vl->type )
998 return vw->vt->tools[vw->vt->active_tool].ti.key_press(vl, event, vw->vt->tools[vw->vt->active_tool].state);
1001 // Ensure called only on window tools (i.e. not on any of the Layer tools since the layer is NULL)
1002 if ( vw->current_tool < TOOL_LAYER ) {
1003 // No layer - but enable window tool keypress processing - these should be able to handle a NULL layer
1004 if ( vw->vt->tools[vw->vt->active_tool].ti.key_press ) {
1005 return vw->vt->tools[vw->vt->active_tool].ti.key_press ( vl, event, vw->vt->tools[vw->vt->active_tool].state );
1009 /* Restore Main Menu via Escape key if the user has hidden it */
1010 /* This key is more likely to be used as they may not remember the function key */
1011 if ( event->keyval == GDK_Escape ) {
1012 GtkWidget *check_box = gtk_ui_manager_get_widget ( vw->uim, "/ui/MainMenu/View/SetShow/ViewMainMenu" );
1014 gboolean state = gtk_check_menu_item_get_active ( GTK_CHECK_MENU_ITEM(check_box) );
1016 gtk_widget_show ( gtk_ui_manager_get_widget ( vw->uim, "/ui/MainMenu" ) );
1017 gtk_check_menu_item_set_active ( GTK_CHECK_MENU_ITEM(check_box), TRUE );
1018 return TRUE; /* handled keypress */
1023 return FALSE; /* don't handle the keypress */
1026 static gboolean delete_event( VikWindow *vw )
1028 #ifdef VIKING_PROMPT_IF_MODIFIED
1035 dia = GTK_DIALOG ( gtk_message_dialog_new ( GTK_WINDOW(vw), GTK_DIALOG_DESTROY_WITH_PARENT, GTK_MESSAGE_QUESTION, GTK_BUTTONS_NONE,
1036 _("Do you want to save the changes you made to the document \"%s\"?\n"
1038 "Your changes will be lost if you don't save them."),
1039 window_get_filename ( vw ) ) );
1040 gtk_dialog_add_buttons ( dia, _("Don't Save"), GTK_RESPONSE_NO, GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, GTK_STOCK_SAVE, GTK_RESPONSE_YES, NULL );
1041 switch ( gtk_dialog_run ( dia ) )
1043 case GTK_RESPONSE_NO: gtk_widget_destroy ( GTK_WIDGET(dia) ); return FALSE;
1044 case GTK_RESPONSE_CANCEL: gtk_widget_destroy ( GTK_WIDGET(dia) ); return TRUE;
1045 default: gtk_widget_destroy ( GTK_WIDGET(dia) ); return ! save_file(NULL, vw);
1049 if ( window_count == 1 ) {
1050 // On the final window close - save latest state - if it's wanted...
1051 if ( a_vik_get_restore_window_state() ) {
1052 gint state = gdk_window_get_state ( GTK_WIDGET(vw)->window );
1053 gboolean state_max = state & GDK_WINDOW_STATE_MAXIMIZED;
1054 a_settings_set_boolean ( VIK_SETTINGS_WIN_MAX, state_max );
1056 gboolean state_fullscreen = state & GDK_WINDOW_STATE_FULLSCREEN;
1057 a_settings_set_boolean ( VIK_SETTINGS_WIN_FULLSCREEN, state_fullscreen );
1059 a_settings_set_boolean ( VIK_SETTINGS_WIN_SIDEPANEL, GTK_WIDGET_VISIBLE (GTK_WIDGET(vw->viking_vlp)) );
1061 a_settings_set_boolean ( VIK_SETTINGS_WIN_STATUSBAR, GTK_WIDGET_VISIBLE (GTK_WIDGET(vw->viking_vs)) );
1063 a_settings_set_boolean ( VIK_SETTINGS_WIN_TOOLBAR, GTK_WIDGET_VISIBLE (toolbar_get_widget(vw->viking_vtb)) );
1065 // If supersized - no need to save the enlarged width+height values
1066 if ( ! (state_fullscreen || state_max) ) {
1068 gtk_window_get_size ( GTK_WINDOW (vw), &width, &height );
1069 a_settings_set_integer ( VIK_SETTINGS_WIN_WIDTH, width );
1070 a_settings_set_integer ( VIK_SETTINGS_WIN_HEIGHT, height );
1073 a_settings_set_integer ( VIK_SETTINGS_WIN_PANE_POSITION, gtk_paned_get_position (GTK_PANED(vw->hpaned)) );
1076 a_settings_set_integer ( VIK_SETTINGS_WIN_SAVE_IMAGE_WIDTH, vw->draw_image_width );
1077 a_settings_set_integer ( VIK_SETTINGS_WIN_SAVE_IMAGE_HEIGHT, vw->draw_image_height );
1078 a_settings_set_boolean ( VIK_SETTINGS_WIN_SAVE_IMAGE_PNG, vw->draw_image_save_as_png );
1080 gchar *accel_file_name = g_build_filename ( a_get_viking_dir(), VIKING_ACCELERATOR_KEY_FILE, NULL );
1081 gtk_accel_map_save ( accel_file_name );
1082 g_free ( accel_file_name );
1089 static void newwindow_cb ( GtkAction *a, VikWindow *vw )
1091 g_signal_emit ( G_OBJECT(vw), window_signals[VW_NEWWINDOW_SIGNAL], 0 );
1094 static void draw_update ( VikWindow *vw )
1100 static void draw_sync ( VikWindow *vw )
1102 vik_viewport_sync(vw->viking_vvp);
1107 * Split the status update, as sometimes only need to update the tool part
1108 * also on initialization the zoom related stuff is not ready to be used
1110 static void draw_status_tool ( VikWindow *vw )
1112 if ( vw->current_tool == TOOL_LAYER )
1113 // Use tooltip rather than the internal name as the tooltip is i8n
1114 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 );
1116 vik_statusbar_set_message ( vw->viking_vs, VIK_STATUSBAR_TOOL, _(tool_names[vw->current_tool]) );
1119 static void draw_status ( VikWindow *vw )
1121 static gchar zoom_level[22];
1122 gdouble xmpp = vik_viewport_get_xmpp (vw->viking_vvp);
1123 gdouble ympp = vik_viewport_get_ympp(vw->viking_vvp);
1124 gchar *unit = vik_viewport_get_coord_mode(vw->viking_vvp) == VIK_COORD_UTM ? _("mpp") : _("pixelfact");
1126 g_snprintf ( zoom_level, 22, "%.3f/%.3f %s", xmpp, ympp, unit );
1128 if ( (int)xmpp - xmpp < 0.0 )
1129 g_snprintf ( zoom_level, 22, "%.3f %s", xmpp, unit );
1131 /* xmpp should be a whole number so don't show useless .000 bit */
1132 g_snprintf ( zoom_level, 22, "%d %s", (int)xmpp, unit );
1134 vik_statusbar_set_message ( vw->viking_vs, VIK_STATUSBAR_ZOOM, zoom_level );
1136 draw_status_tool ( vw );
1139 void vik_window_set_redraw_trigger(VikLayer *vl)
1141 VikWindow *vw = VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vl));
1146 static void window_configure_event ( VikWindow *vw )
1148 static int first = 1;
1151 // This is a hack to set the cursor corresponding to the first tool
1152 // FIXME find the correct way to initialize both tool and its cursor
1154 vw->viewport_cursor = (GdkCursor *)toolbox_get_cursor(vw->vt, "Pan");
1155 /* We set cursor, even if it is NULL: it resets to default */
1156 gdk_window_set_cursor ( gtk_widget_get_window(GTK_WIDGET(vw->viking_vvp)), vw->viewport_cursor );
1160 static void draw_redraw ( VikWindow *vw )
1162 VikCoord old_center = vw->trigger_center;
1163 vw->trigger_center = *(vik_viewport_get_center(vw->viking_vvp));
1164 VikLayer *new_trigger = vw->trigger;
1166 VikLayer *old_trigger = VIK_LAYER(vik_viewport_get_trigger(vw->viking_vvp));
1168 if ( ! new_trigger )
1169 ; /* do nothing -- have to redraw everything. */
1170 else if ( (old_trigger != new_trigger) || !vik_coord_equals(&old_center, &vw->trigger_center) || (new_trigger->type == VIK_LAYER_AGGREGATE) )
1171 vik_viewport_set_trigger ( vw->viking_vvp, new_trigger ); /* todo: set to half_drawn mode if new trigger is above old */
1173 vik_viewport_set_half_drawn ( vw->viking_vvp, TRUE );
1176 vik_viewport_clear ( vw->viking_vvp);
1177 // Main layer drawing
1178 vik_layers_panel_draw_all ( vw->viking_vlp );
1179 // Draw highlight (possibly again but ensures it is on top - especially for when tracks overlap)
1180 if ( vik_viewport_get_draw_highlight (vw->viking_vvp) ) {
1181 if ( vw->containing_vtl && (vw->selected_tracks || vw->selected_waypoints ) ) {
1182 vik_trw_layer_draw_highlight_items ( vw->containing_vtl, vw->selected_tracks, vw->selected_waypoints, vw->viking_vvp );
1184 else if ( vw->containing_vtl && (vw->selected_track || vw->selected_waypoint) ) {
1185 vik_trw_layer_draw_highlight_item ( vw->containing_vtl, vw->selected_track, vw->selected_waypoint, vw->viking_vvp );
1187 else if ( vw->selected_vtl ) {
1188 vik_trw_layer_draw_highlight ( vw->selected_vtl, vw->viking_vvp );
1191 // Other viewport decoration items on top if they are enabled/in use
1192 vik_viewport_draw_scale ( vw->viking_vvp );
1193 vik_viewport_draw_copyright ( vw->viking_vvp );
1194 vik_viewport_draw_centermark ( vw->viking_vvp );
1195 vik_viewport_draw_logo ( vw->viking_vvp );
1197 vik_viewport_set_half_drawn ( vw->viking_vvp, FALSE ); /* just in case. */
1200 gboolean draw_buf_done = TRUE;
1202 static gboolean draw_buf(gpointer data)
1204 gpointer *pass_along = data;
1205 gdk_threads_enter();
1206 gdk_draw_drawable (pass_along[0], pass_along[1],
1207 pass_along[2], 0, 0, 0, 0, -1, -1);
1208 draw_buf_done = TRUE;
1209 gdk_threads_leave();
1214 /* Mouse event handlers ************************************************************************/
1216 static void vik_window_pan_click (VikWindow *vw, GdkEventButton *event)
1218 /* set panning origin */
1219 vw->pan_move = FALSE;
1220 vw->pan_x = (gint) event->x;
1221 vw->pan_y = (gint) event->y;
1224 static void draw_click (VikWindow *vw, GdkEventButton *event)
1226 gtk_widget_grab_focus ( GTK_WIDGET(vw->viking_vvp) );
1228 /* middle button pressed. we reserve all middle button and scroll events
1229 * for panning and zooming; tools only get left/right/movement
1231 if ( event->button == 2) {
1232 if ( vw->vt->tools[vw->vt->active_tool].ti.pan_handler )
1233 // Tool still may need to do something (such as disable something)
1234 toolbox_click(vw->vt, event);
1235 vik_window_pan_click ( vw, event );
1238 toolbox_click(vw->vt, event);
1242 static void vik_window_pan_move (VikWindow *vw, GdkEventMotion *event)
1244 if ( vw->pan_x != -1 ) {
1245 vik_viewport_set_center_screen ( vw->viking_vvp, vik_viewport_get_width(vw->viking_vvp)/2 - event->x + vw->pan_x,
1246 vik_viewport_get_height(vw->viking_vvp)/2 - event->y + vw->pan_y );
1247 vw->pan_move = TRUE;
1248 vw->pan_x = event->x;
1249 vw->pan_y = event->y;
1255 * get_location_strings:
1257 * Utility function to get positional strings for the given location
1258 * lat and lon strings will get allocated and so need to be freed after use
1260 static void get_location_strings ( VikWindow *vw, struct UTM utm, gchar **lat, gchar **lon )
1262 if ( vik_viewport_get_drawmode ( vw->viking_vvp ) == VIK_VIEWPORT_DRAWMODE_UTM ) {
1263 // Reuse lat for the first part (Zone + N or S, and lon for the second part (easting and northing) of a UTM format:
1264 // ZONE[N|S] EASTING NORTHING
1265 *lat = g_malloc(4*sizeof(gchar));
1266 // NB zone is stored in a char but is an actual number
1267 g_snprintf (*lat, 4, "%d%c", utm.zone, utm.letter);
1268 *lon = g_malloc(16*sizeof(gchar));
1269 g_snprintf (*lon, 16, "%d %d", (gint)utm.easting, (gint)utm.northing);
1273 a_coords_utm_to_latlon ( &utm, &ll );
1274 a_coords_latlon_to_string ( &ll, lat, lon );
1278 static void draw_mouse_motion (VikWindow *vw, GdkEventMotion *event)
1280 static VikCoord coord;
1281 static struct UTM utm;
1282 #define BUFFER_SIZE 50
1283 static char pointer_buf[BUFFER_SIZE];
1284 gchar *lat = NULL, *lon = NULL;
1287 VikDemInterpol interpol_method;
1289 /* This is a hack, but work far the best, at least for single pointer systems.
1290 * See http://bugzilla.gnome.org/show_bug.cgi?id=587714 for more. */
1292 gdk_window_get_pointer (event->window, &x, &y, NULL);
1296 toolbox_move(vw->vt, event);
1298 vik_viewport_screen_to_coord ( vw->viking_vvp, event->x, event->y, &coord );
1299 vik_coord_to_utm ( &coord, &utm );
1301 get_location_strings ( vw, utm, &lat, &lon );
1303 /* Change interpolate method according to scale */
1304 zoom = vik_viewport_get_zoom(vw->viking_vvp);
1306 interpol_method = VIK_DEM_INTERPOL_NONE;
1307 else if (zoom >= 1.0)
1308 interpol_method = VIK_DEM_INTERPOL_SIMPLE;
1310 interpol_method = VIK_DEM_INTERPOL_BEST;
1311 if ((alt = a_dems_get_elev_by_coord(&coord, interpol_method)) != VIK_DEM_INVALID_ELEVATION) {
1312 if ( a_vik_get_units_height () == VIK_UNITS_HEIGHT_METRES )
1313 g_snprintf ( pointer_buf, BUFFER_SIZE, _("%s %s %dm"), lat, lon, alt );
1315 g_snprintf ( pointer_buf, BUFFER_SIZE, _("%s %s %dft"), lat, lon, (int)VIK_METERS_TO_FEET(alt) );
1318 g_snprintf ( pointer_buf, BUFFER_SIZE, _("%s %s"), lat, lon );
1323 vik_statusbar_set_message ( vw->viking_vs, VIK_STATUSBAR_POSITION, pointer_buf );
1325 vik_window_pan_move ( vw, event );
1327 /* This is recommended by the GTK+ documentation, but does not work properly.
1328 * Use deprecated way until GTK+ gets a solution for correct motion hint handling:
1329 * http://bugzilla.gnome.org/show_bug.cgi?id=587714
1331 /* gdk_event_request_motions ( event ); */
1335 * Action the single click after a small timeout
1336 * If a double click has occurred then this will do nothing
1338 static gboolean vik_window_pan_timeout (VikWindow *vw)
1340 if ( ! vw->single_click_pending ) {
1341 // Double click happened, so don't do anything
1345 /* set panning origin */
1346 vw->pan_move = FALSE;
1347 vw->single_click_pending = FALSE;
1348 vik_viewport_set_center_screen ( vw->viking_vvp, vw->delayed_pan_x, vw->delayed_pan_y );
1351 // Really turn off the pan moving!!
1352 vw->pan_x = vw->pan_y = -1;
1356 static void vik_window_pan_release ( VikWindow *vw, GdkEventButton *event )
1358 gboolean do_draw = TRUE;
1360 if ( vw->pan_move == FALSE ) {
1361 vw->single_click_pending = !vw->single_click_pending;
1363 if ( vw->single_click_pending ) {
1364 // Store offset to use
1365 vw->delayed_pan_x = vw->pan_x;
1366 vw->delayed_pan_y = vw->pan_y;
1367 // Get double click time
1368 GtkSettings *gs = gtk_widget_get_settings ( GTK_WIDGET(vw) );
1369 GValue dct = { 0 }; // = G_VALUE_INIT; // GLIB 2.30+ only
1370 g_value_init ( &dct, G_TYPE_INT );
1371 g_object_get_property ( G_OBJECT(gs), "gtk-double-click-time", &dct );
1372 // Give chance for a double click to occur
1373 gint timer = g_value_get_int ( &dct ) + 50;
1374 g_timeout_add ( timer, (GSourceFunc)vik_window_pan_timeout, vw );
1378 vik_viewport_set_center_screen ( vw->viking_vvp, vw->pan_x, vw->pan_y );
1382 vik_viewport_set_center_screen ( vw->viking_vvp, vik_viewport_get_width(vw->viking_vvp)/2 - event->x + vw->pan_x,
1383 vik_viewport_get_height(vw->viking_vvp)/2 - event->y + vw->pan_y );
1386 vw->pan_move = FALSE;
1387 vw->pan_x = vw->pan_y = -1;
1392 static void draw_release ( VikWindow *vw, GdkEventButton *event )
1394 gtk_widget_grab_focus ( GTK_WIDGET(vw->viking_vvp) );
1396 if ( event->button == 2 ) { /* move / pan */
1397 if ( vw->vt->tools[vw->vt->active_tool].ti.pan_handler )
1398 // Tool still may need to do something (such as reenable something)
1399 toolbox_release(vw->vt, event);
1400 vik_window_pan_release ( vw, event );
1403 toolbox_release(vw->vt, event);
1407 static void draw_scroll (VikWindow *vw, GdkEventScroll *event)
1409 guint modifiers = event->state & (GDK_SHIFT_MASK | GDK_CONTROL_MASK);
1410 if ( modifiers == GDK_CONTROL_MASK ) {
1411 /* control == pan up & down */
1412 if ( event->direction == GDK_SCROLL_UP )
1413 vik_viewport_set_center_screen ( vw->viking_vvp, vik_viewport_get_width(vw->viking_vvp)/2, vik_viewport_get_height(vw->viking_vvp)/3 );
1415 vik_viewport_set_center_screen ( vw->viking_vvp, vik_viewport_get_width(vw->viking_vvp)/2, vik_viewport_get_height(vw->viking_vvp)*2/3 );
1416 } else if ( modifiers == GDK_SHIFT_MASK ) {
1417 /* shift == pan left & right */
1418 if ( event->direction == GDK_SCROLL_UP )
1419 vik_viewport_set_center_screen ( vw->viking_vvp, vik_viewport_get_width(vw->viking_vvp)/3, vik_viewport_get_height(vw->viking_vvp)/2 );
1421 vik_viewport_set_center_screen ( vw->viking_vvp, vik_viewport_get_width(vw->viking_vvp)*2/3, vik_viewport_get_height(vw->viking_vvp)/2 );
1422 } else if ( modifiers == (GDK_CONTROL_MASK | GDK_SHIFT_MASK) ) {
1423 // This zoom is on the center position
1424 if ( event->direction == GDK_SCROLL_UP )
1425 vik_viewport_zoom_in (vw->viking_vvp);
1427 vik_viewport_zoom_out (vw->viking_vvp);
1429 /* make sure mouse is still over the same point on the map when we zoom */
1432 gint center_x = vik_viewport_get_width ( vw->viking_vvp ) / 2;
1433 gint center_y = vik_viewport_get_height ( vw->viking_vvp ) / 2;
1434 vik_viewport_screen_to_coord ( vw->viking_vvp, event->x, event->y, &coord );
1435 if ( event->direction == GDK_SCROLL_UP )
1436 vik_viewport_zoom_in (vw->viking_vvp);
1438 vik_viewport_zoom_out(vw->viking_vvp);
1439 vik_viewport_coord_to_screen ( vw->viking_vvp, &coord, &x, &y );
1440 vik_viewport_set_center_screen ( vw->viking_vvp, center_x + (x - event->x),
1441 center_y + (y - event->y) );
1449 /********************************************************************************
1451 ********************************************************************************/
1452 static void draw_ruler(VikViewport *vvp, GdkDrawable *d, GdkGC *gc, gint x1, gint y1, gint x2, gint y2, gdouble distance)
1456 GdkGC *labgc = vik_viewport_new_gc ( vvp, "#cccccc", 1);
1457 GdkGC *thickgc = gdk_gc_new(d);
1459 gdouble len = sqrt((x1-x2)*(x1-x2) + (y1-y2)*(y1-y2));
1460 gdouble dx = (x2-x1)/len*10;
1461 gdouble dy = (y2-y1)/len*10;
1462 gdouble c = cos(DEG2RAD(15.0));
1463 gdouble s = sin(DEG2RAD(15.0));
1465 gdouble baseangle = 0;
1468 /* draw line with arrow ends */
1470 gint tmp_x1=x1, tmp_y1=y1, tmp_x2=x2, tmp_y2=y2;
1471 a_viewport_clip_line(&tmp_x1, &tmp_y1, &tmp_x2, &tmp_y2);
1472 gdk_draw_line(d, gc, tmp_x1, tmp_y1, tmp_x2, tmp_y2);
1475 a_viewport_clip_line(&x1, &y1, &x2, &y2);
1476 gdk_draw_line(d, gc, x1, y1, x2, y2);
1478 gdk_draw_line(d, gc, x1 - dy, y1 + dx, x1 + dy, y1 - dx);
1479 gdk_draw_line(d, gc, x2 - dy, y2 + dx, x2 + dy, y2 - dx);
1480 gdk_draw_line(d, gc, x2, y2, x2 - (dx * c + dy * s), y2 - (dy * c - dx * s));
1481 gdk_draw_line(d, gc, x2, y2, x2 - (dx * c - dy * s), y2 - (dy * c + dx * s));
1482 gdk_draw_line(d, gc, x1, y1, x1 + (dx * c + dy * s), y1 + (dy * c - dx * s));
1483 gdk_draw_line(d, gc, x1, y1, x1 + (dx * c - dy * s), y1 + (dy * c + dx * s));
1489 vik_viewport_compute_bearing ( vvp, x1, y1, x2, y2, &angle, &baseangle );
1493 gdk_gc_copy(thickgc, gc);
1494 gdk_gc_set_line_attributes(thickgc, CW, GDK_LINE_SOLID, GDK_CAP_BUTT, GDK_JOIN_MITER);
1495 gdk_color_parse("#2255cc", &color);
1496 gdk_gc_set_rgb_fg_color(thickgc, &color);
1498 gdk_draw_arc (d, thickgc, FALSE, x1-CR+CW/2, y1-CR+CW/2, 2*CR-CW, 2*CR-CW, (90 - RAD2DEG(baseangle))*64, -RAD2DEG(angle)*64);
1501 gdk_gc_copy(thickgc, gc);
1502 gdk_gc_set_line_attributes(thickgc, 2, GDK_LINE_SOLID, GDK_CAP_BUTT, GDK_JOIN_MITER);
1503 for (i=0; i<180; i++) {
1504 c = cos(DEG2RAD(i)*2 + baseangle);
1505 s = sin(DEG2RAD(i)*2 + baseangle);
1508 gdk_draw_line (d, gc, x1 + CR*c, y1 + CR*s, x1 + (CR+CW)*c, y1 + (CR+CW)*s);
1510 gdouble ticksize = 2*CW;
1511 gdk_draw_line (d, thickgc, x1 + (CR-CW)*c, y1 + (CR-CW)*s, x1 + (CR+ticksize)*c, y1 + (CR+ticksize)*s);
1515 gdk_draw_arc (d, gc, FALSE, x1-CR, y1-CR, 2*CR, 2*CR, 0, 64*360);
1516 gdk_draw_arc (d, gc, FALSE, x1-CR-CW, y1-CR-CW, 2*(CR+CW), 2*(CR+CW), 0, 64*360);
1517 gdk_draw_arc (d, gc, FALSE, x1-CR+CW, y1-CR+CW, 2*(CR-CW), 2*(CR-CW), 0, 64*360);
1518 c = (CR+CW*2)*cos(baseangle);
1519 s = (CR+CW*2)*sin(baseangle);
1520 gdk_draw_line (d, gc, x1-c, y1-s, x1+c, y1+s);
1521 gdk_draw_line (d, gc, x1+s, y1-c, x1-s, y1+c);
1524 #define LABEL(x, y, w, h) { \
1525 gdk_draw_rectangle(d, labgc, TRUE, (x)-2, (y)-1, (w)+4, (h)+1); \
1526 gdk_draw_rectangle(d, gc, FALSE, (x)-2, (y)-1, (w)+4, (h)+1); \
1527 gdk_draw_layout(d, gc, (x), (y), pl); }
1529 gint wd, hd, xd, yd;
1530 gint wb, hb, xb, yb;
1532 pl = gtk_widget_create_pango_layout (GTK_WIDGET(vvp), NULL);
1533 pango_layout_set_font_description (pl, gtk_widget_get_style(GTK_WIDGET(vvp))->font_desc);
1534 pango_layout_set_text(pl, "N", -1);
1535 gdk_draw_layout(d, gc, x1-5, y1-CR-3*CW-8, pl);
1537 /* draw label with distance */
1538 vik_units_distance_t dist_units = a_vik_get_units_distance ();
1539 switch (dist_units) {
1540 case VIK_UNITS_DISTANCE_KILOMETRES:
1541 if (distance >= 1000 && distance < 100000) {
1542 g_sprintf(str, "%3.2f km", distance/1000.0);
1543 } else if (distance < 1000) {
1544 g_sprintf(str, "%d m", (int)distance);
1546 g_sprintf(str, "%d km", (int)distance/1000);
1549 case VIK_UNITS_DISTANCE_MILES:
1550 if (distance >= VIK_MILES_TO_METERS(1) && distance < VIK_MILES_TO_METERS(100)) {
1551 g_sprintf(str, "%3.2f miles", VIK_METERS_TO_MILES(distance));
1552 } else if (distance < VIK_MILES_TO_METERS(1)) {
1553 g_sprintf(str, "%d yards", (int)(distance*1.0936133));
1555 g_sprintf(str, "%d miles", (int)VIK_METERS_TO_MILES(distance));
1558 case VIK_UNITS_DISTANCE_NAUTICAL_MILES:
1559 if (distance >= VIK_NAUTICAL_MILES_TO_METERS(1) && distance < VIK_NAUTICAL_MILES_TO_METERS(100)) {
1560 g_sprintf(str, "%3.2f NM", VIK_METERS_TO_NAUTICAL_MILES(distance));
1561 } else if (distance < VIK_NAUTICAL_MILES_TO_METERS(1)) {
1562 g_sprintf(str, "%d yards", (int)(distance*1.0936133));
1564 g_sprintf(str, "%d NM", (int)VIK_METERS_TO_NAUTICAL_MILES(distance));
1568 g_critical("Houston, we've had a problem. distance=%d", dist_units);
1571 pango_layout_set_text(pl, str, -1);
1573 pango_layout_get_pixel_size ( pl, &wd, &hd );
1575 xd = (x1+x2)/2 + dy;
1576 yd = (y1+y2)/2 - hd/2 - dx;
1578 xd = (x1+x2)/2 - dy;
1579 yd = (y1+y2)/2 - hd/2 + dx;
1582 if ( xd < -5 || yd < -5 || xd > vik_viewport_get_width(vvp)+5 || yd > vik_viewport_get_height(vvp)+5 ) {
1587 LABEL(xd, yd, wd, hd);
1589 /* draw label with bearing */
1590 g_sprintf(str, "%3.1f°", RAD2DEG(angle));
1591 pango_layout_set_text(pl, str, -1);
1592 pango_layout_get_pixel_size ( pl, &wb, &hb );
1593 xb = x1 + CR*cos(angle-M_PI_2);
1594 yb = y1 + CR*sin(angle-M_PI_2);
1596 if ( xb < -5 || yb < -5 || xb > vik_viewport_get_width(vvp)+5 || yb > vik_viewport_get_height(vvp)+5 ) {
1602 GdkRectangle r1 = {xd-2, yd-1, wd+4, hd+1}, r2 = {xb-2, yb-1, wb+4, hb+1};
1603 if (gdk_rectangle_intersect(&r1, &r2, &r2)) {
1607 LABEL(xb, yb, wb, hb);
1611 g_object_unref ( G_OBJECT ( pl ) );
1612 g_object_unref ( G_OBJECT ( labgc ) );
1613 g_object_unref ( G_OBJECT ( thickgc ) );
1619 gboolean has_oldcoord;
1621 } ruler_tool_state_t;
1623 static gpointer ruler_create (VikWindow *vw, VikViewport *vvp)
1625 ruler_tool_state_t *s = g_new(ruler_tool_state_t, 1);
1628 s->has_oldcoord = FALSE;
1632 static void ruler_destroy (ruler_tool_state_t *s)
1637 static VikLayerToolFuncStatus ruler_click (VikLayer *vl, GdkEventButton *event, ruler_tool_state_t *s)
1642 if ( event->button == 1 ) {
1643 gchar *lat=NULL, *lon=NULL;
1644 vik_viewport_screen_to_coord ( s->vvp, (gint) event->x, (gint) event->y, &coord );
1645 vik_coord_to_latlon ( &coord, &ll );
1646 a_coords_latlon_to_string ( &ll, &lat, &lon );
1647 if ( s->has_oldcoord ) {
1648 vik_units_distance_t dist_units = a_vik_get_units_distance ();
1649 switch (dist_units) {
1650 case VIK_UNITS_DISTANCE_KILOMETRES:
1651 temp = g_strdup_printf ( "%s %s DIFF %f meters", lat, lon, vik_coord_diff( &coord, &(s->oldcoord) ) );
1653 case VIK_UNITS_DISTANCE_MILES:
1654 temp = g_strdup_printf ( "%s %s DIFF %f miles", lat, lon, VIK_METERS_TO_MILES(vik_coord_diff( &coord, &(s->oldcoord) )) );
1656 case VIK_UNITS_DISTANCE_NAUTICAL_MILES:
1657 temp = g_strdup_printf ( "%s %s DIFF %f NM", lat, lon, VIK_METERS_TO_NAUTICAL_MILES(vik_coord_diff( &coord, &(s->oldcoord) )) );
1660 temp = g_strdup_printf ("Just to keep the compiler happy");
1661 g_critical("Houston, we've had a problem. distance=%d", dist_units);
1664 s->has_oldcoord = FALSE;
1667 temp = g_strdup_printf ( "%s %s", lat, lon );
1668 s->has_oldcoord = TRUE;
1671 vik_statusbar_set_message ( s->vw->viking_vs, VIK_STATUSBAR_INFO, temp );
1674 s->oldcoord = coord;
1677 vik_viewport_set_center_screen ( s->vvp, (gint) event->x, (gint) event->y );
1678 draw_update ( s->vw );
1680 return VIK_LAYER_TOOL_ACK;
1683 static VikLayerToolFuncStatus ruler_move (VikLayer *vl, GdkEventMotion *event, ruler_tool_state_t *s)
1685 VikViewport *vvp = s->vvp;
1686 VikWindow *vw = s->vw;
1692 if ( s->has_oldcoord ) {
1693 int oldx, oldy, w1, h1, w2, h2;
1694 static GdkPixmap *buf = NULL;
1695 gchar *lat=NULL, *lon=NULL;
1696 w1 = vik_viewport_get_width(vvp);
1697 h1 = vik_viewport_get_height(vvp);
1699 buf = gdk_pixmap_new ( gtk_widget_get_window(GTK_WIDGET(vvp)), w1, h1, -1 );
1701 gdk_drawable_get_size(buf, &w2, &h2);
1702 if (w1 != w2 || h1 != h2) {
1703 g_object_unref ( G_OBJECT ( buf ) );
1704 buf = gdk_pixmap_new ( gtk_widget_get_window(GTK_WIDGET(vvp)), w1, h1, -1 );
1707 vik_viewport_screen_to_coord ( vvp, (gint) event->x, (gint) event->y, &coord );
1708 vik_coord_to_latlon ( &coord, &ll );
1709 vik_viewport_coord_to_screen ( vvp, &s->oldcoord, &oldx, &oldy );
1711 gdk_draw_drawable (buf, gtk_widget_get_style(GTK_WIDGET(vvp))->black_gc,
1712 vik_viewport_get_pixmap(vvp), 0, 0, 0, 0, -1, -1);
1713 draw_ruler(vvp, buf, gtk_widget_get_style(GTK_WIDGET(vvp))->black_gc, oldx, oldy, event->x, event->y, vik_coord_diff( &coord, &(s->oldcoord)) );
1714 if (draw_buf_done) {
1715 static gpointer pass_along[3];
1716 pass_along[0] = gtk_widget_get_window(GTK_WIDGET(vvp));
1717 pass_along[1] = gtk_widget_get_style(GTK_WIDGET(vvp))->black_gc;
1718 pass_along[2] = buf;
1719 g_idle_add_full (G_PRIORITY_HIGH_IDLE + 10, draw_buf, pass_along, NULL);
1720 draw_buf_done = FALSE;
1722 a_coords_latlon_to_string(&ll, &lat, &lon);
1723 vik_units_distance_t dist_units = a_vik_get_units_distance ();
1724 switch (dist_units) {
1725 case VIK_UNITS_DISTANCE_KILOMETRES:
1726 temp = g_strdup_printf ( "%s %s DIFF %f meters", lat, lon, vik_coord_diff( &coord, &(s->oldcoord) ) );
1728 case VIK_UNITS_DISTANCE_MILES:
1729 temp = g_strdup_printf ( "%s %s DIFF %f miles", lat, lon, VIK_METERS_TO_MILES (vik_coord_diff( &coord, &(s->oldcoord) )) );
1731 case VIK_UNITS_DISTANCE_NAUTICAL_MILES:
1732 temp = g_strdup_printf ( "%s %s DIFF %f NM", lat, lon, VIK_METERS_TO_NAUTICAL_MILES (vik_coord_diff( &coord, &(s->oldcoord) )) );
1735 temp = g_strdup_printf ("Just to keep the compiler happy");
1736 g_critical("Houston, we've had a problem. distance=%d", dist_units);
1738 vik_statusbar_set_message ( vw->viking_vs, VIK_STATUSBAR_INFO, temp );
1741 return VIK_LAYER_TOOL_ACK;
1744 static VikLayerToolFuncStatus ruler_release (VikLayer *vl, GdkEventButton *event, ruler_tool_state_t *s)
1746 return VIK_LAYER_TOOL_ACK;
1749 static void ruler_deactivate (VikLayer *vl, ruler_tool_state_t *s)
1751 draw_update ( s->vw );
1754 static gboolean ruler_key_press (VikLayer *vl, GdkEventKey *event, ruler_tool_state_t *s)
1756 if (event->keyval == GDK_Escape) {
1757 s->has_oldcoord = FALSE;
1758 ruler_deactivate ( vl, s );
1761 // Regardless of whether we used it, return false so other GTK things may use it
1765 static VikToolInterface ruler_tool =
1766 // NB Ctrl+Shift+R is used for Refresh (deemed more important), so use 'U' instead
1767 { { "Ruler", "vik-icon-ruler", N_("_Ruler"), "<control><shift>U", N_("Ruler Tool"), 2 },
1768 (VikToolConstructorFunc) ruler_create,
1769 (VikToolDestructorFunc) ruler_destroy,
1770 (VikToolActivationFunc) NULL,
1771 (VikToolActivationFunc) ruler_deactivate,
1772 (VikToolMouseFunc) ruler_click,
1773 (VikToolMouseMoveFunc) ruler_move,
1774 (VikToolMouseFunc) ruler_release,
1775 (VikToolKeyFunc) ruler_key_press,
1777 GDK_CURSOR_IS_PIXMAP,
1778 &cursor_ruler_pixbuf,
1780 /*** end ruler code ********************************************************/
1784 /********************************************************************************
1786 ********************************************************************************/
1791 // Track zoom bounds for zoom tool with shift modifier:
1792 gboolean bounds_active;
1795 } zoom_tool_state_t;
1798 * In case the screen size has changed
1800 static void zoomtool_resize_pixmap (zoom_tool_state_t *zts)
1804 // Allocate a drawing area the size of the viewport
1805 w1 = vik_viewport_get_width ( zts->vw->viking_vvp );
1806 h1 = vik_viewport_get_height ( zts->vw->viking_vvp );
1808 if ( !zts->pixmap ) {
1810 zts->pixmap = gdk_pixmap_new ( gtk_widget_get_window(GTK_WIDGET(zts->vw->viking_vvp)), w1, h1, -1 );
1813 gdk_drawable_get_size ( zts->pixmap, &w2, &h2 );
1815 if ( w1 != w2 || h1 != h2 ) {
1816 // Has changed - delete and recreate with new values
1817 g_object_unref ( G_OBJECT ( zts->pixmap ) );
1818 zts->pixmap = gdk_pixmap_new ( gtk_widget_get_window(GTK_WIDGET(zts->vw->viking_vvp)), w1, h1, -1 );
1822 static gpointer zoomtool_create (VikWindow *vw, VikViewport *vvp)
1824 zoom_tool_state_t *zts = g_new(zoom_tool_state_t, 1);
1829 zts->bounds_active = FALSE;
1833 static void zoomtool_destroy ( zoom_tool_state_t *zts)
1836 g_object_unref ( G_OBJECT ( zts->pixmap ) );
1840 static VikLayerToolFuncStatus zoomtool_click (VikLayer *vl, GdkEventButton *event, zoom_tool_state_t *zts)
1842 zts->vw->modified = TRUE;
1843 guint modifiers = event->state & (GDK_SHIFT_MASK | GDK_CONTROL_MASK);
1847 gint center_x = vik_viewport_get_width ( zts->vw->viking_vvp ) / 2;
1848 gint center_y = vik_viewport_get_height ( zts->vw->viking_vvp ) / 2;
1850 gboolean skip_update = FALSE;
1852 zts->bounds_active = FALSE;
1854 if ( modifiers == (GDK_CONTROL_MASK | GDK_SHIFT_MASK) ) {
1855 // This zoom is on the center position
1856 vik_viewport_set_center_screen ( zts->vw->viking_vvp, center_x, center_y );
1857 if ( event->button == 1 )
1858 vik_viewport_zoom_in (zts->vw->viking_vvp);
1859 else if ( event->button == 3 )
1860 vik_viewport_zoom_out (zts->vw->viking_vvp);
1862 else if ( modifiers == GDK_CONTROL_MASK ) {
1863 // This zoom is to recenter on the mouse position
1864 vik_viewport_set_center_screen ( zts->vw->viking_vvp, (gint) event->x, (gint) event->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_SHIFT_MASK ) {
1871 // Get start of new zoom bounds
1872 if ( event->button == 1 ) {
1873 zts->bounds_active = TRUE;
1874 zts->start_x = (gint) event->x;
1875 zts->start_y = (gint) event->y;
1880 /* make sure mouse is still over the same point on the map when we zoom */
1881 vik_viewport_screen_to_coord ( zts->vw->viking_vvp, event->x, event->y, &coord );
1882 if ( event->button == 1 )
1883 vik_viewport_zoom_in (zts->vw->viking_vvp);
1884 else if ( event->button == 3 )
1885 vik_viewport_zoom_out(zts->vw->viking_vvp);
1886 vik_viewport_coord_to_screen ( zts->vw->viking_vvp, &coord, &x, &y );
1887 vik_viewport_set_center_screen ( zts->vw->viking_vvp,
1888 center_x + (x - event->x),
1889 center_y + (y - event->y) );
1893 draw_update ( zts->vw );
1895 return VIK_LAYER_TOOL_ACK;
1898 static VikLayerToolFuncStatus zoomtool_move (VikLayer *vl, GdkEventMotion *event, zoom_tool_state_t *zts)
1900 guint modifiers = event->state & (GDK_SHIFT_MASK | GDK_CONTROL_MASK);
1902 if ( zts->bounds_active && modifiers == GDK_SHIFT_MASK ) {
1903 zoomtool_resize_pixmap ( zts );
1905 // Blank out currently drawn area
1906 gdk_draw_drawable ( zts->pixmap,
1907 gtk_widget_get_style(GTK_WIDGET(zts->vw->viking_vvp))->black_gc,
1908 vik_viewport_get_pixmap(zts->vw->viking_vvp),
1909 0, 0, 0, 0, -1, -1);
1911 // Calculate new box starting point & size in pixels
1912 int xx, yy, width, height;
1913 if ( event->y > zts->start_y ) {
1915 height = event->y-zts->start_y;
1919 height = zts->start_y-event->y;
1921 if ( event->x > zts->start_x ) {
1923 width = event->x-zts->start_x;
1927 width = zts->start_x-event->x;
1931 gdk_draw_rectangle (zts->pixmap, gtk_widget_get_style(GTK_WIDGET(zts->vw->viking_vvp))->black_gc, FALSE, xx, yy, width, height);
1933 // Only actually draw when there's time to do so
1934 if (draw_buf_done) {
1935 static gpointer pass_along[3];
1936 pass_along[0] = gtk_widget_get_window(GTK_WIDGET(zts->vw->viking_vvp));
1937 pass_along[1] = gtk_widget_get_style(GTK_WIDGET(zts->vw->viking_vvp))->black_gc;
1938 pass_along[2] = zts->pixmap;
1939 g_idle_add_full (G_PRIORITY_HIGH_IDLE + 10, draw_buf, pass_along, NULL);
1940 draw_buf_done = FALSE;
1944 zts->bounds_active = FALSE;
1946 return VIK_LAYER_TOOL_ACK;
1949 static VikLayerToolFuncStatus zoomtool_release (VikLayer *vl, GdkEventButton *event, zoom_tool_state_t *zts)
1951 guint modifiers = event->state & (GDK_SHIFT_MASK | GDK_CONTROL_MASK);
1953 // Ensure haven't just released on the exact same position
1954 // i.e. probably haven't moved the mouse at all
1955 if ( zts->bounds_active && modifiers == GDK_SHIFT_MASK &&
1956 ( event->x < zts->start_x-5 || event->x > zts->start_x+5 ) &&
1957 ( event->y < zts->start_y-5 || event->y > zts->start_y+5 ) ) {
1959 VikCoord coord1, coord2;
1960 vik_viewport_screen_to_coord ( zts->vw->viking_vvp, zts->start_x, zts->start_y, &coord1);
1961 vik_viewport_screen_to_coord ( zts->vw->viking_vvp, event->x, event->y, &coord2);
1963 // From the extend of the bounds pick the best zoom level
1964 // c.f. trw_layer_zoom_to_show_latlons()
1965 // Maybe refactor...
1966 struct LatLon ll1, ll2;
1967 vik_coord_to_latlon(&coord1, &ll1);
1968 vik_coord_to_latlon(&coord2, &ll2);
1969 struct LatLon average = { (ll1.lat+ll2.lat)/2,
1970 (ll1.lon+ll2.lon)/2 };
1972 VikCoord new_center;
1973 vik_coord_load_from_latlon ( &new_center, vik_viewport_get_coord_mode ( zts->vw->viking_vvp ), &average );
1974 vik_viewport_set_center_coord ( zts->vw->viking_vvp, &new_center, FALSE );
1976 /* Convert into definite 'smallest' and 'largest' positions */
1977 struct LatLon minmin;
1978 if ( ll1.lat < ll2.lat )
1979 minmin.lat = ll1.lat;
1981 minmin.lat = ll2.lat;
1983 struct LatLon maxmax;
1984 if ( ll1.lon > ll2.lon )
1985 maxmax.lon = ll1.lon;
1987 maxmax.lon = ll2.lon;
1989 /* Always recalculate the 'best' zoom level */
1990 gdouble zoom = VIK_VIEWPORT_MIN_ZOOM;
1991 vik_viewport_set_zoom ( zts->vw->viking_vvp, zoom );
1993 gdouble min_lat, max_lat, min_lon, max_lon;
1994 /* Should only be a maximum of about 18 iterations from min to max zoom levels */
1995 while ( zoom <= VIK_VIEWPORT_MAX_ZOOM ) {
1996 vik_viewport_get_min_max_lat_lon ( zts->vw->viking_vvp, &min_lat, &max_lat, &min_lon, &max_lon );
1997 /* NB I think the logic used in this test to determine if the bounds is within view
1998 fails if track goes across 180 degrees longitude.
1999 Hopefully that situation is not too common...
2000 Mind you viking doesn't really do edge locations to well anyway */
2001 if ( min_lat < minmin.lat &&
2002 max_lat > minmin.lat &&
2003 min_lon < maxmax.lon &&
2004 max_lon > maxmax.lon )
2005 /* Found within zoom level */
2010 vik_viewport_set_zoom ( zts->vw->viking_vvp, zoom );
2014 // When pressing shift and clicking for zoom, then jump three levels
2015 if ( modifiers == GDK_SHIFT_MASK ) {
2016 // Zoom in/out by three if possible
2017 vik_viewport_set_center_screen ( zts->vw->viking_vvp, event->x, event->y );
2018 if ( event->button == 1 ) {
2019 vik_viewport_zoom_in ( zts->vw->viking_vvp );
2020 vik_viewport_zoom_in ( zts->vw->viking_vvp );
2021 vik_viewport_zoom_in ( zts->vw->viking_vvp );
2023 else if ( event->button == 3 ) {
2024 vik_viewport_zoom_out ( zts->vw->viking_vvp );
2025 vik_viewport_zoom_out ( zts->vw->viking_vvp );
2026 vik_viewport_zoom_out ( zts->vw->viking_vvp );
2031 draw_update ( zts->vw );
2034 zts->bounds_active = FALSE;
2036 return VIK_LAYER_TOOL_ACK;
2039 static VikToolInterface zoom_tool =
2040 { { "Zoom", "vik-icon-zoom", N_("_Zoom"), "<control><shift>Z", N_("Zoom Tool"), 1 },
2041 (VikToolConstructorFunc) zoomtool_create,
2042 (VikToolDestructorFunc) zoomtool_destroy,
2043 (VikToolActivationFunc) NULL,
2044 (VikToolActivationFunc) NULL,
2045 (VikToolMouseFunc) zoomtool_click,
2046 (VikToolMouseMoveFunc) zoomtool_move,
2047 (VikToolMouseFunc) zoomtool_release,
2050 GDK_CURSOR_IS_PIXMAP,
2051 &cursor_zoom_pixbuf,
2053 /*** end zoom code ********************************************************/
2055 /********************************************************************************
2057 ********************************************************************************/
2058 static gpointer pantool_create (VikWindow *vw, VikViewport *vvp)
2063 // NB Double clicking means this gets called THREE times!!!
2064 static VikLayerToolFuncStatus pantool_click (VikLayer *vl, GdkEventButton *event, VikWindow *vw)
2066 vw->modified = TRUE;
2068 if ( event->type == GDK_2BUTTON_PRESS ) {
2069 // Zoom in / out on double click
2070 // No need to change the center as that has already occurred in the first click of a double click occurrence
2071 if ( event->button == 1 ) {
2072 guint modifier = event->state & GDK_SHIFT_MASK;
2074 vik_viewport_zoom_out ( vw->viking_vvp );
2076 vik_viewport_zoom_in ( vw->viking_vvp );
2078 else if ( event->button == 3 )
2079 vik_viewport_zoom_out ( vw->viking_vvp );
2084 // Standard pan click
2085 if ( event->button == 1 )
2086 vik_window_pan_click ( vw, event );
2088 return VIK_LAYER_TOOL_ACK;
2091 static VikLayerToolFuncStatus pantool_move (VikLayer *vl, GdkEventMotion *event, VikWindow *vw)
2093 vik_window_pan_move ( vw, event );
2094 return VIK_LAYER_TOOL_ACK;
2097 static VikLayerToolFuncStatus pantool_release (VikLayer *vl, GdkEventButton *event, VikWindow *vw)
2099 if ( event->button == 1 )
2100 vik_window_pan_release ( vw, event );
2101 return VIK_LAYER_TOOL_ACK;
2104 static VikToolInterface pan_tool =
2105 { { "Pan", "vik-icon-pan", N_("_Pan"), "<control><shift>P", N_("Pan Tool"), 0 },
2106 (VikToolConstructorFunc) pantool_create,
2107 (VikToolDestructorFunc) NULL,
2108 (VikToolActivationFunc) NULL,
2109 (VikToolActivationFunc) NULL,
2110 (VikToolMouseFunc) pantool_click,
2111 (VikToolMouseMoveFunc) pantool_move,
2112 (VikToolMouseFunc) pantool_release,
2118 /*** end pan code ********************************************************/
2120 /********************************************************************************
2122 ********************************************************************************/
2123 static gpointer selecttool_create (VikWindow *vw, VikViewport *vvp)
2125 tool_ed_t *t = g_new(tool_ed_t, 1);
2129 t->is_waypoint = FALSE;
2133 static void selecttool_destroy (tool_ed_t *t)
2141 GdkEventButton *event;
2142 tool_ed_t *tool_edit;
2145 static void click_layer_selected (VikLayer *vl, clicker *ck)
2147 /* Do nothing when function call returns true; */
2148 /* i.e. stop on first found item */
2151 if ( vik_layer_get_interface(vl->type)->select_click )
2152 ck->cont = !vik_layer_get_interface(vl->type)->select_click ( vl, ck->event, ck->vvp, ck->tool_edit );
2156 // Hopefully Alt keys by default
2157 #define VIK_MOVE_MODIFIER GDK_MOD1_MASK
2159 // Alt+mouse on Linux desktops tend to be used by the desktop manager
2160 // Thus use an alternate modifier - you may need to set something into this group
2161 #define VIK_MOVE_MODIFIER GDK_MOD5_MASK
2164 static VikLayerToolFuncStatus selecttool_click (VikLayer *vl, GdkEventButton *event, tool_ed_t *t)
2166 t->vw->select_move = FALSE;
2167 /* Only allow selection on primary button */
2168 if ( event->button == 1 ) {
2170 if ( event->state & VIK_MOVE_MODIFIER )
2171 vik_window_pan_click ( t->vw, event );
2173 /* Enable click to apply callback to potentially all track/waypoint layers */
2174 /* Useful as we can find things that aren't necessarily in the currently selected layer */
2175 GList* gl = vik_layers_panel_get_all_layers_of_type ( t->vw->viking_vlp, VIK_LAYER_TRW, FALSE ); // Don't get invisible layers
2178 ck.vvp = t->vw->viking_vvp;
2181 g_list_foreach ( gl, (GFunc) click_layer_selected, &ck );
2184 // If nothing found then deselect & redraw screen if necessary to remove the highlight
2187 VikTreeview *vtv = vik_layers_panel_get_treeview ( t->vw->viking_vlp );
2189 if ( vik_treeview_get_selected_iter ( vtv, &iter ) ) {
2190 // Only clear if selected thing is a TrackWaypoint layer or a sublayer
2191 gint type = vik_treeview_item_get_type ( vtv, &iter );
2192 if ( type == VIK_TREEVIEW_TYPE_SUBLAYER ||
2193 VIK_LAYER(vik_treeview_item_get_pointer ( vtv, &iter ))->type == VIK_LAYER_TRW ) {
2195 vik_treeview_item_unselect ( vtv, &iter );
2196 if ( vik_window_clear_highlight ( t->vw ) )
2197 draw_update ( t->vw );
2202 // Something found - so enable movement
2203 t->vw->select_move = TRUE;
2207 else if ( ( event->button == 3 ) && ( vl && ( vl->type == VIK_LAYER_TRW ) ) ) {
2209 /* Act on currently selected item to show menu */
2210 if ( t->vw->selected_track || t->vw->selected_waypoint )
2211 if ( vik_layer_get_interface(vl->type)->show_viewport_menu )
2212 vik_layer_get_interface(vl->type)->show_viewport_menu ( vl, event, t->vw->viking_vvp );
2215 return VIK_LAYER_TOOL_ACK;
2218 static VikLayerToolFuncStatus selecttool_move (VikLayer *vl, GdkEventMotion *event, tool_ed_t *t)
2220 if ( t->vw->select_move ) {
2221 // Don't care about vl here
2223 if ( vik_layer_get_interface(VIK_LAYER_TRW)->select_move )
2224 vik_layer_get_interface(VIK_LAYER_TRW)->select_move ( vl, event, t->vvp, t );
2228 if ( event->state & VIK_MOVE_MODIFIER )
2229 vik_window_pan_move ( t->vw, event );
2231 return VIK_LAYER_TOOL_ACK;
2234 static VikLayerToolFuncStatus selecttool_release (VikLayer *vl, GdkEventButton *event, tool_ed_t *t)
2236 if ( t->vw->select_move ) {
2237 // Don't care about vl here
2239 if ( vik_layer_get_interface(VIK_LAYER_TRW)->select_release )
2240 vik_layer_get_interface(VIK_LAYER_TRW)->select_release ( (VikLayer*)t->vtl, event, t->vvp, t );
2243 if ( event->button == 1 && (event->state & VIK_MOVE_MODIFIER) )
2244 vik_window_pan_release ( t->vw, event );
2246 // Force pan off incase it was on
2247 t->vw->pan_move = FALSE;
2248 t->vw->pan_x = t->vw->pan_y = -1;
2250 // End of this select movement
2251 t->vw->select_move = FALSE;
2253 return VIK_LAYER_TOOL_ACK;
2256 static VikToolInterface select_tool =
2257 { { "Select", "vik-icon-select", N_("_Select"), "<control><shift>S", N_("Select Tool"), 3 },
2258 (VikToolConstructorFunc) selecttool_create,
2259 (VikToolDestructorFunc) selecttool_destroy,
2260 (VikToolActivationFunc) NULL,
2261 (VikToolActivationFunc) NULL,
2262 (VikToolMouseFunc) selecttool_click,
2263 (VikToolMouseMoveFunc) selecttool_move,
2264 (VikToolMouseFunc) selecttool_release,
2265 (VikToolKeyFunc) NULL,
2270 /*** end select tool code ********************************************************/
2272 static void draw_pan_cb ( GtkAction *a, VikWindow *vw )
2274 // Since the treeview cell editting intercepts standard keyboard handlers, it means we can receive events here
2275 // Thus if currently editting, ensure we don't move the viewport when Ctrl+<arrow> is received
2276 VikLayer *sel = vik_layers_panel_get_selected ( vw->viking_vlp );
2277 if ( sel && vik_treeview_get_editing ( sel->vt ) )
2280 if (!strcmp(gtk_action_get_name(a), "PanNorth")) {
2281 vik_viewport_set_center_screen ( vw->viking_vvp, vik_viewport_get_width(vw->viking_vvp)/2, 0 );
2282 } else if (!strcmp(gtk_action_get_name(a), "PanEast")) {
2283 vik_viewport_set_center_screen ( vw->viking_vvp, vik_viewport_get_width(vw->viking_vvp), vik_viewport_get_height(vw->viking_vvp)/2 );
2284 } else if (!strcmp(gtk_action_get_name(a), "PanSouth")) {
2285 vik_viewport_set_center_screen ( vw->viking_vvp, vik_viewport_get_width(vw->viking_vvp)/2, vik_viewport_get_height(vw->viking_vvp) );
2286 } else if (!strcmp(gtk_action_get_name(a), "PanWest")) {
2287 vik_viewport_set_center_screen ( vw->viking_vvp, 0, vik_viewport_get_height(vw->viking_vvp)/2 );
2292 static void draw_zoom_cb ( GtkAction *a, VikWindow *vw )
2296 if (!strcmp(gtk_action_get_name(a), "ZoomIn")) {
2299 else if (!strcmp(gtk_action_get_name(a), "ZoomOut")) {
2302 else if (!strcmp(gtk_action_get_name(a), "Zoom0.25")) {
2305 else if (!strcmp(gtk_action_get_name(a), "Zoom0.5")) {
2309 gchar *s = (gchar *)gtk_action_get_name(a);
2315 case -3: vik_viewport_zoom_in ( vw->viking_vvp ); break;
2316 case -4: vik_viewport_zoom_out ( vw->viking_vvp ); break;
2317 case -1: vik_viewport_set_zoom ( vw->viking_vvp, 0.5 ); break;
2318 case -2: vik_viewport_set_zoom ( vw->viking_vvp, 0.25 ); break;
2319 default: vik_viewport_set_zoom ( vw->viking_vvp, what );
2324 static void draw_goto_cb ( GtkAction *a, VikWindow *vw )
2326 VikCoord new_center;
2328 if (!strcmp(gtk_action_get_name(a), "GotoLL")) {
2329 struct LatLon ll, llold;
2330 vik_coord_to_latlon ( vik_viewport_get_center ( vw->viking_vvp ), &llold );
2331 if ( a_dialog_goto_latlon ( GTK_WINDOW(vw), &ll, &llold ) )
2332 vik_coord_load_from_latlon ( &new_center, vik_viewport_get_coord_mode(vw->viking_vvp), &ll );
2336 else if (!strcmp(gtk_action_get_name(a), "GotoUTM")) {
2337 struct UTM utm, utmold;
2338 vik_coord_to_utm ( vik_viewport_get_center ( vw->viking_vvp ), &utmold );
2339 if ( a_dialog_goto_utm ( GTK_WINDOW(vw), &utm, &utmold ) )
2340 vik_coord_load_from_utm ( &new_center, vik_viewport_get_coord_mode(vw->viking_vvp), &utm );
2345 g_critical("Houston, we've had a problem.");
2349 vik_viewport_set_center_coord ( vw->viking_vvp, &new_center, TRUE );
2354 * center_changed_cb:
2356 static void center_changed_cb ( VikWindow *vw )
2358 // ATM Keep back always available, so when we pan - we can jump to the last requested position
2360 GtkAction* action_back = gtk_action_group_get_action ( vw->action_group, "GoBack" );
2361 if ( action_back ) {
2362 gtk_action_set_sensitive ( action_back, vik_viewport_back_available(vw->viking_vvp) );
2365 GtkAction* action_forward = gtk_action_group_get_action ( vw->action_group, "GoForward" );
2366 if ( action_forward ) {
2367 gtk_action_set_sensitive ( action_forward, vik_viewport_forward_available(vw->viking_vvp) );
2370 toolbar_action_set_sensitive ( vw->viking_vtb, "GoForward", vik_viewport_forward_available(vw->viking_vvp) );
2374 * draw_goto_back_and_forth:
2376 static void draw_goto_back_and_forth ( GtkAction *a, VikWindow *vw )
2378 gboolean changed = FALSE;
2379 if (!strcmp(gtk_action_get_name(a), "GoBack")) {
2380 changed = vik_viewport_go_back ( vw->viking_vvp );
2382 else if (!strcmp(gtk_action_get_name(a), "GoForward")) {
2383 changed = vik_viewport_go_forward ( vw->viking_vvp );
2389 // Recheck buttons sensitivities, as the center changed signal is not sent on back/forward changes
2390 // (otherwise we would get stuck in an infinite loop!)
2391 center_changed_cb ( vw );
2398 * Refresh maps displayed
2400 static void draw_refresh_cb ( GtkAction *a, VikWindow *vw )
2402 // Only get 'new' maps
2403 simple_map_update ( vw, TRUE );
2406 static void menu_addlayer_cb ( GtkAction *a, VikWindow *vw )
2408 VikLayerTypeEnum type;
2409 for ( type = 0; type < VIK_LAYER_NUM_TYPES; type++ ) {
2410 if (!strcmp(vik_layer_get_interface(type)->name, gtk_action_get_name(a))) {
2411 if ( vik_layers_panel_new_layer ( vw->viking_vlp, type ) ) {
2413 vw->modified = TRUE;
2419 static void menu_copy_layer_cb ( GtkAction *a, VikWindow *vw )
2421 a_clipboard_copy_selected ( vw->viking_vlp );
2424 static void menu_cut_layer_cb ( GtkAction *a, VikWindow *vw )
2426 vik_layers_panel_cut_selected ( vw->viking_vlp );
2427 vw->modified = TRUE;
2430 static void menu_paste_layer_cb ( GtkAction *a, VikWindow *vw )
2432 if ( vik_layers_panel_paste_selected ( vw->viking_vlp ) )
2434 vw->modified = TRUE;
2438 static void menu_properties_cb ( GtkAction *a, VikWindow *vw )
2440 if ( ! vik_layers_panel_properties ( vw->viking_vlp ) )
2441 a_dialog_info_msg ( GTK_WINDOW(vw), _("You must select a layer to show its properties.") );
2444 static void help_help_cb ( GtkAction *a, VikWindow *vw )
2447 ShellExecute(NULL, "open", ""PACKAGE".pdf", NULL, NULL, SW_SHOWNORMAL);
2450 uri = g_strdup_printf("ghelp:%s", PACKAGE);
2451 GError *error = NULL;
2452 gboolean show = gtk_show_uri (NULL, uri, GDK_CURRENT_TIME, &error);
2453 if ( !show && !error )
2454 // No error to show, so unlikely this will get called
2455 a_dialog_error_msg ( GTK_WINDOW(vw), _("The help system is not available.") );
2458 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 );
2459 g_error_free ( error );
2462 #endif /* WINDOWS */
2465 static void toggle_side_panel ( VikWindow *vw )
2467 vw->show_side_panel = !vw->show_side_panel;
2468 if ( vw->show_side_panel )
2469 gtk_widget_show(GTK_WIDGET(vw->viking_vlp));
2471 gtk_widget_hide(GTK_WIDGET(vw->viking_vlp));
2474 static void toggle_full_screen ( VikWindow *vw )
2476 vw->show_full_screen = !vw->show_full_screen;
2477 if ( vw->show_full_screen )
2478 gtk_window_fullscreen ( GTK_WINDOW(vw) );
2480 gtk_window_unfullscreen ( GTK_WINDOW(vw) );
2483 static void toggle_statusbar ( VikWindow *vw )
2485 vw->show_statusbar = !vw->show_statusbar;
2486 if ( vw->show_statusbar )
2487 gtk_widget_show ( GTK_WIDGET(vw->viking_vs) );
2489 gtk_widget_hide ( GTK_WIDGET(vw->viking_vs) );
2492 static void toggle_toolbar ( VikWindow *vw )
2494 vw->show_toolbar = !vw->show_toolbar;
2495 if ( vw->show_toolbar )
2496 gtk_widget_show ( toolbar_get_widget (vw->viking_vtb) );
2498 gtk_widget_hide ( toolbar_get_widget (vw->viking_vtb) );
2501 static void toggle_main_menu ( VikWindow *vw )
2503 vw->show_main_menu = !vw->show_main_menu;
2504 if ( vw->show_main_menu )
2505 gtk_widget_show ( gtk_ui_manager_get_widget ( vw->uim, "/ui/MainMenu" ) );
2507 gtk_widget_hide ( gtk_ui_manager_get_widget ( vw->uim, "/ui/MainMenu" ) );
2510 // Only for 'view' toggle menu widgets ATM.
2511 GtkWidget *get_show_widget_by_name(VikWindow *vw, const gchar *name)
2513 g_return_val_if_fail(name != NULL, NULL);
2515 // ATM only FullScreen is *not* in SetShow path
2517 if ( g_strcmp0 ("FullScreen", name ) )
2518 path = g_strconcat("/ui/MainMenu/View/SetShow/", name, NULL);
2520 path = g_strconcat("/ui/MainMenu/View/", name, NULL);
2522 GtkWidget *widget = gtk_ui_manager_get_widget(vw->uim, path);
2528 static void tb_view_side_panel_cb ( GtkAction *a, VikWindow *vw )
2530 gboolean next_state = !vw->show_side_panel;
2531 GtkWidget *check_box = get_show_widget_by_name ( vw, gtk_action_get_name(a) );
2532 gboolean menu_state = gtk_check_menu_item_get_active ( GTK_CHECK_MENU_ITEM(check_box) );
2533 if ( next_state != menu_state )
2534 gtk_check_menu_item_set_active ( GTK_CHECK_MENU_ITEM(check_box), next_state );
2536 toggle_side_panel ( vw );
2539 static void tb_full_screen_cb ( GtkAction *a, VikWindow *vw )
2541 gboolean next_state = !vw->show_full_screen;
2542 GtkWidget *check_box = get_show_widget_by_name ( vw, gtk_action_get_name(a) );
2543 gboolean menu_state = gtk_check_menu_item_get_active ( GTK_CHECK_MENU_ITEM(check_box) );
2544 if ( next_state != menu_state )
2545 gtk_check_menu_item_set_active ( GTK_CHECK_MENU_ITEM(check_box), next_state );
2547 toggle_full_screen ( vw );
2550 static void tb_view_statusbar_cb ( GtkAction *a, VikWindow *vw )
2552 gboolean next_state = !vw->show_statusbar;
2553 GtkWidget *check_box = get_show_widget_by_name ( vw, gtk_action_get_name(a) );
2554 gboolean menu_state = gtk_check_menu_item_get_active ( GTK_CHECK_MENU_ITEM(check_box) );
2555 if ( next_state != menu_state )
2556 gtk_check_menu_item_set_active ( GTK_CHECK_MENU_ITEM(check_box), next_state );
2558 toggle_statusbar ( vw );
2561 static void tb_view_toolbar_cb ( GtkAction *a, VikWindow *vw )
2563 gboolean next_state = !vw->show_toolbar;
2564 GtkWidget *check_box = get_show_widget_by_name ( vw, gtk_action_get_name(a) );
2565 gboolean menu_state = gtk_check_menu_item_get_active ( GTK_CHECK_MENU_ITEM(check_box) );
2566 if ( next_state != menu_state )
2567 gtk_check_menu_item_set_active ( GTK_CHECK_MENU_ITEM(check_box), next_state );
2569 toggle_toolbar ( vw );
2572 static void tb_view_main_menu_cb ( GtkAction *a, VikWindow *vw )
2574 gboolean next_state = !vw->show_main_menu;
2575 GtkWidget *check_box = get_show_widget_by_name ( vw, gtk_action_get_name(a) );
2576 gboolean menu_state = gtk_check_menu_item_get_active ( GTK_CHECK_MENU_ITEM(check_box) );
2577 if ( next_state != menu_state )
2578 gtk_check_menu_item_set_active ( GTK_CHECK_MENU_ITEM(check_box), next_state );
2580 toggle_main_menu ( vw );
2583 static void tb_set_draw_scale ( GtkAction *a, VikWindow *vw )
2585 gboolean next_state = !vik_viewport_get_draw_scale ( vw->viking_vvp );
2586 GtkWidget *check_box = get_show_widget_by_name ( vw, gtk_action_get_name(a) );
2587 gboolean menu_state = gtk_check_menu_item_get_active ( GTK_CHECK_MENU_ITEM(check_box) );
2588 if ( next_state != menu_state )
2589 gtk_check_menu_item_set_active ( GTK_CHECK_MENU_ITEM(check_box), next_state );
2591 vik_viewport_set_draw_scale ( vw->viking_vvp, next_state );
2596 static void tb_set_draw_centermark ( GtkAction *a, VikWindow *vw )
2598 gboolean next_state = !vik_viewport_get_draw_centermark ( vw->viking_vvp );
2599 GtkWidget *check_box = get_show_widget_by_name ( vw, gtk_action_get_name(a) );
2600 gboolean menu_state = gtk_check_menu_item_get_active ( GTK_CHECK_MENU_ITEM(check_box) );
2601 if ( next_state != menu_state )
2602 gtk_check_menu_item_set_active ( GTK_CHECK_MENU_ITEM(check_box), next_state );
2604 vik_viewport_set_draw_centermark ( vw->viking_vvp, next_state );
2609 static void tb_set_draw_highlight ( GtkAction *a, VikWindow *vw )
2611 gboolean next_state = !vik_viewport_get_draw_highlight ( vw->viking_vvp );
2612 GtkWidget *check_box = get_show_widget_by_name ( vw, gtk_action_get_name(a) );
2613 gboolean menu_state = gtk_check_menu_item_get_active ( GTK_CHECK_MENU_ITEM(check_box) );
2614 if ( next_state != menu_state )
2615 gtk_check_menu_item_set_active ( GTK_CHECK_MENU_ITEM(check_box), next_state );
2617 vik_viewport_set_draw_highlight ( vw->viking_vvp, next_state );
2622 static void help_about_cb ( GtkAction *a, VikWindow *vw )
2624 a_dialog_about(GTK_WINDOW(vw));
2627 static void help_cache_info_cb ( GtkAction *a, VikWindow *vw )
2629 // NB: No i18n as this is just for debug
2630 gint byte_size = a_mapcache_get_size();
2631 gchar *msg_sz = NULL;
2633 #if GLIB_CHECK_VERSION(2,30,0)
2634 msg_sz = g_format_size_full ( byte_size, G_FORMAT_SIZE_LONG_FORMAT );
2636 msg_sz = g_format_size_for_display ( byte_size );
2638 msg = g_strdup_printf ( "Map Cache size is %s with %d items", msg_sz, a_mapcache_get_count());
2639 a_dialog_info_msg_extra ( GTK_WINDOW(vw), "%s", msg );
2644 static void back_forward_info_cb ( GtkAction *a, VikWindow *vw )
2646 vik_viewport_show_centers ( vw->viking_vvp, GTK_WINDOW(vw) );
2649 static void menu_delete_layer_cb ( GtkAction *a, VikWindow *vw )
2651 if ( vik_layers_panel_get_selected ( vw->viking_vlp ) )
2653 vik_layers_panel_delete_selected ( vw->viking_vlp );
2654 vw->modified = TRUE;
2657 a_dialog_info_msg ( GTK_WINDOW(vw), _("You must select a layer to delete.") );
2660 static void full_screen_cb ( GtkAction *a, VikWindow *vw )
2662 gboolean next_state = !vw->show_full_screen;
2663 GtkToggleToolButton *tbutton = (GtkToggleToolButton *)toolbar_get_widget_by_name ( vw->viking_vtb, gtk_action_get_name(a) );
2665 gboolean tb_state = gtk_toggle_tool_button_get_active ( tbutton );
2666 if ( next_state != tb_state )
2667 gtk_toggle_tool_button_set_active ( tbutton, next_state );
2669 toggle_full_screen ( vw );
2672 toggle_full_screen ( vw );
2675 static void view_side_panel_cb ( GtkAction *a, VikWindow *vw )
2677 gboolean next_state = !vw->show_side_panel;
2678 GtkToggleToolButton *tbutton = (GtkToggleToolButton *)toolbar_get_widget_by_name ( vw->viking_vtb, gtk_action_get_name(a) );
2680 gboolean tb_state = gtk_toggle_tool_button_get_active ( tbutton );
2681 if ( next_state != tb_state )
2682 gtk_toggle_tool_button_set_active ( tbutton, next_state );
2684 toggle_side_panel ( vw );
2687 toggle_side_panel ( vw );
2690 static void view_statusbar_cb ( GtkAction *a, VikWindow *vw )
2692 gboolean next_state = !vw->show_statusbar;
2693 GtkToggleToolButton *tbutton = (GtkToggleToolButton *)toolbar_get_widget_by_name ( vw->viking_vtb, gtk_action_get_name(a) );
2695 gboolean tb_state = gtk_toggle_tool_button_get_active ( tbutton );
2696 if ( next_state != tb_state )
2697 gtk_toggle_tool_button_set_active ( tbutton, next_state );
2699 toggle_statusbar ( vw );
2702 toggle_statusbar ( vw );
2705 static void view_toolbar_cb ( GtkAction *a, VikWindow *vw )
2707 gboolean next_state = !vw->show_toolbar;
2708 GtkToggleToolButton *tbutton = (GtkToggleToolButton *)toolbar_get_widget_by_name ( vw->viking_vtb, gtk_action_get_name(a) );
2710 gboolean tb_state = gtk_toggle_tool_button_get_active ( tbutton );
2711 if ( next_state != tb_state )
2712 gtk_toggle_tool_button_set_active ( tbutton, next_state );
2714 toggle_toolbar ( vw );
2717 toggle_toolbar ( vw );
2720 static void view_main_menu_cb ( GtkAction *a, VikWindow *vw )
2722 gboolean next_state = !vw->show_main_menu;
2723 GtkToggleToolButton *tbutton = (GtkToggleToolButton *)toolbar_get_widget_by_name ( vw->viking_vtb, gtk_action_get_name(a) );
2725 gboolean tb_state = gtk_toggle_tool_button_get_active ( tbutton );
2726 if ( next_state != tb_state )
2727 gtk_toggle_tool_button_set_active ( tbutton, next_state );
2729 toggle_main_menu ( vw );
2732 toggle_toolbar ( vw );
2735 /***************************************
2736 ** tool management routines
2738 ***************************************/
2740 static toolbox_tools_t* toolbox_create(VikWindow *vw)
2742 toolbox_tools_t *vt = g_new(toolbox_tools_t, 1);
2745 vt->active_tool = -1;
2750 static void toolbox_add_tool(toolbox_tools_t *vt, VikToolInterface *vti, gint layer_type )
2752 vt->tools = g_renew(toolbox_tool_t, vt->tools, vt->n_tools+1);
2753 vt->tools[vt->n_tools].ti = *vti;
2754 vt->tools[vt->n_tools].layer_type = layer_type;
2756 vt->tools[vt->n_tools].state = vti->create(vt->vw, vt->vw->viking_vvp);
2759 vt->tools[vt->n_tools].state = NULL;
2764 static int toolbox_get_tool(toolbox_tools_t *vt, const gchar *tool_name)
2767 for (i=0; i<vt->n_tools; i++) {
2768 if (!strcmp(tool_name, vt->tools[i].ti.radioActionEntry.name)) {
2775 static void toolbox_activate(toolbox_tools_t *vt, const gchar *tool_name)
2777 int tool = toolbox_get_tool(vt, tool_name);
2778 toolbox_tool_t *t = &vt->tools[tool];
2779 VikLayer *vl = vik_layers_panel_get_selected ( vt->vw->viking_vlp );
2781 if (tool == vt->n_tools) {
2782 g_critical("trying to activate a non-existent tool...");
2785 /* is the tool already active? */
2786 if (vt->active_tool == tool) {
2790 if (vt->active_tool != -1) {
2791 if (vt->tools[vt->active_tool].ti.deactivate) {
2792 vt->tools[vt->active_tool].ti.deactivate(NULL, vt->tools[vt->active_tool].state);
2795 if (t->ti.activate) {
2796 t->ti.activate(vl, t->state);
2798 vt->active_tool = tool;
2801 static const GdkCursor *toolbox_get_cursor(toolbox_tools_t *vt, const gchar *tool_name)
2803 int tool = toolbox_get_tool(vt, tool_name);
2804 toolbox_tool_t *t = &vt->tools[tool];
2805 if (t->ti.cursor == NULL) {
2806 if (t->ti.cursor_type == GDK_CURSOR_IS_PIXMAP && t->ti.cursor_data != NULL) {
2807 GError *cursor_load_err = NULL;
2808 GdkPixbuf *cursor_pixbuf = gdk_pixbuf_from_pixdata (t->ti.cursor_data, FALSE, &cursor_load_err);
2809 /* TODO: settable offeset */
2810 t->ti.cursor = gdk_cursor_new_from_pixbuf ( gdk_display_get_default(), cursor_pixbuf, 3, 3 );
2811 g_object_unref ( G_OBJECT(cursor_pixbuf) );
2813 t->ti.cursor = gdk_cursor_new ( t->ti.cursor_type );
2816 return t->ti.cursor;
2819 static void toolbox_click (toolbox_tools_t *vt, GdkEventButton *event)
2821 VikLayer *vl = vik_layers_panel_get_selected ( vt->vw->viking_vlp );
2822 if (vt->active_tool != -1 && vt->tools[vt->active_tool].ti.click) {
2823 gint ltype = vt->tools[vt->active_tool].layer_type;
2824 if ( ltype == TOOL_LAYER_TYPE_NONE || (vl && ltype == vl->type) )
2825 vt->tools[vt->active_tool].ti.click(vl, event, vt->tools[vt->active_tool].state);
2829 static void toolbox_move (toolbox_tools_t *vt, GdkEventMotion *event)
2831 VikLayer *vl = vik_layers_panel_get_selected ( vt->vw->viking_vlp );
2832 if (vt->active_tool != -1 && vt->tools[vt->active_tool].ti.move) {
2833 gint ltype = vt->tools[vt->active_tool].layer_type;
2834 if ( ltype == TOOL_LAYER_TYPE_NONE || (vl && ltype == vl->type) )
2835 if ( VIK_LAYER_TOOL_ACK_GRAB_FOCUS == vt->tools[vt->active_tool].ti.move(vl, event, vt->tools[vt->active_tool].state) )
2836 gtk_widget_grab_focus ( GTK_WIDGET(vt->vw->viking_vvp) );
2840 static void toolbox_release (toolbox_tools_t *vt, GdkEventButton *event)
2842 VikLayer *vl = vik_layers_panel_get_selected ( vt->vw->viking_vlp );
2843 if (vt->active_tool != -1 && vt->tools[vt->active_tool].ti.release ) {
2844 gint ltype = vt->tools[vt->active_tool].layer_type;
2845 if ( ltype == TOOL_LAYER_TYPE_NONE || (vl && ltype == vl->type) )
2846 vt->tools[vt->active_tool].ti.release(vl, event, vt->tools[vt->active_tool].state);
2849 /** End tool management ************************************/
2851 void vik_window_enable_layer_tool ( VikWindow *vw, gint layer_id, gint tool_id )
2853 gtk_action_activate ( gtk_action_group_get_action ( vw->action_group, vik_layer_get_interface(layer_id)->tools[tool_id].radioActionEntry.name ) );
2856 // Be careful with usage - as it may trigger actions being continually alternately by the menu and toolbar items
2857 // DON'T Use this from menu callback with toggle toolbar items!!
2858 static void toolbar_sync ( VikWindow *vw, const gchar *name, gboolean state )
2860 GtkToggleToolButton *tbutton = (GtkToggleToolButton *)toolbar_get_widget_by_name ( vw->viking_vtb, name );
2862 // Causes toggle signal action to be raised.
2863 gtk_toggle_tool_button_set_active ( tbutton, state );
2867 /* this function gets called whenever a menu is clicked */
2868 // Note old is not used
2869 static void menu_cb ( GtkAction *old, GtkAction *a, VikWindow *vw )
2871 // Ensure Toolbar kept in sync
2872 const gchar *name = gtk_action_get_name(a);
2873 toolbar_sync ( vw, name, TRUE );
2875 /* White Magic, my friends ... White Magic... */
2877 toolbox_activate(vw->vt, name);
2879 vw->viewport_cursor = (GdkCursor *)toolbox_get_cursor(vw->vt, name);
2881 if ( gtk_widget_get_window(GTK_WIDGET(vw->viking_vvp)) )
2882 /* We set cursor, even if it is NULL: it resets to default */
2883 gdk_window_set_cursor ( gtk_widget_get_window(GTK_WIDGET(vw->viking_vvp)), vw->viewport_cursor );
2885 if (!g_strcmp0(name, "Pan")) {
2886 vw->current_tool = TOOL_PAN;
2888 else if (!g_strcmp0(name, "Zoom")) {
2889 vw->current_tool = TOOL_ZOOM;
2891 else if (!g_strcmp0(name, "Ruler")) {
2892 vw->current_tool = TOOL_RULER;
2894 else if (!g_strcmp0(name, "Select")) {
2895 vw->current_tool = TOOL_SELECT;
2898 VikLayerTypeEnum layer_id;
2899 for (layer_id=0; layer_id<VIK_LAYER_NUM_TYPES; layer_id++) {
2900 for ( tool_id = 0; tool_id < vik_layer_get_interface(layer_id)->tools_count; tool_id++ ) {
2901 if (!g_strcmp0(vik_layer_get_interface(layer_id)->tools[tool_id].radioActionEntry.name, name)) {
2902 vw->current_tool = TOOL_LAYER;
2903 vw->tool_layer_id = layer_id;
2904 vw->tool_tool_id = tool_id;
2909 draw_status_tool ( vw );
2912 static void window_set_filename ( VikWindow *vw, const gchar *filename )
2917 g_free ( vw->filename );
2918 if ( filename == NULL )
2920 vw->filename = NULL;
2924 vw->filename = g_strdup(filename);
2927 /* Refresh window's title */
2928 file = window_get_filename ( vw );
2929 title = g_strdup_printf( "%s - Viking", file );
2930 gtk_window_set_title ( GTK_WINDOW(vw), title );
2934 static const gchar *window_get_filename ( VikWindow *vw )
2936 return vw->filename ? a_file_basename ( vw->filename ) : _("Untitled");
2939 GtkWidget *vik_window_get_drawmode_button ( VikWindow *vw, VikViewportDrawMode mode )
2941 GtkWidget *mode_button;
2944 #ifdef VIK_CONFIG_EXPEDIA
2945 case VIK_VIEWPORT_DRAWMODE_EXPEDIA: buttonname = "/ui/MainMenu/View/ModeExpedia"; break;
2947 case VIK_VIEWPORT_DRAWMODE_MERCATOR: buttonname = "/ui/MainMenu/View/ModeMercator"; break;
2948 case VIK_VIEWPORT_DRAWMODE_LATLON: buttonname = "/ui/MainMenu/View/ModeLatLon"; break;
2949 default: buttonname = "/ui/MainMenu/View/ModeUTM";
2951 mode_button = gtk_ui_manager_get_widget ( vw->uim, buttonname );
2952 g_assert ( mode_button );
2957 * vik_window_get_pan_move:
2958 * @vw: some VikWindow
2960 * Retrieves @vw's pan_move.
2962 * Should be removed as soon as possible.
2964 * Returns: @vw's pan_move
2968 gboolean vik_window_get_pan_move ( VikWindow *vw )
2970 return vw->pan_move;
2973 static void on_activate_recent_item (GtkRecentChooser *chooser,
2978 filename = gtk_recent_chooser_get_current_uri (chooser);
2979 if (filename != NULL)
2981 GFile *file = g_file_new_for_uri ( filename );
2982 gchar *path = g_file_get_path ( file );
2983 g_object_unref ( file );
2984 if ( self->filename )
2986 GSList *filenames = NULL;
2987 filenames = g_slist_append ( filenames, path );
2988 g_signal_emit ( G_OBJECT(self), window_signals[VW_OPENWINDOW_SIGNAL], 0, filenames );
2989 // NB: GSList & contents are freed by main.open_window
2992 vik_window_open_file ( self, path, TRUE );
3000 static void setup_recent_files (VikWindow *self)
3002 GtkRecentManager *manager;
3003 GtkRecentFilter *filter;
3004 GtkWidget *menu, *menu_item;
3006 filter = gtk_recent_filter_new ();
3007 /* gtk_recent_filter_add_application (filter, g_get_application_name()); */
3008 gtk_recent_filter_add_group(filter, "viking");
3010 manager = gtk_recent_manager_get_default ();
3011 menu = gtk_recent_chooser_menu_new_for_manager (manager);
3012 gtk_recent_chooser_set_sort_type (GTK_RECENT_CHOOSER (menu), GTK_RECENT_SORT_MRU);
3013 gtk_recent_chooser_add_filter (GTK_RECENT_CHOOSER (menu), filter);
3014 gtk_recent_chooser_set_limit (GTK_RECENT_CHOOSER (menu), a_vik_get_recent_number_files() );
3016 menu_item = gtk_ui_manager_get_widget (self->uim, "/ui/MainMenu/File/OpenRecentFile");
3017 gtk_menu_item_set_submenu (GTK_MENU_ITEM (menu_item), menu);
3019 g_signal_connect (G_OBJECT (menu), "item-activated",
3020 G_CALLBACK (on_activate_recent_item), (gpointer) self);
3026 static void update_recently_used_document (VikWindow *vw, const gchar *filename)
3028 /* Update Recently Used Document framework */
3029 GtkRecentManager *manager = gtk_recent_manager_get_default();
3030 GtkRecentData *recent_data = g_slice_new (GtkRecentData);
3031 gchar *groups[] = {"viking", NULL};
3032 GFile *file = g_file_new_for_commandline_arg(filename);
3033 gchar *uri = g_file_get_uri(file);
3034 gchar *basename = g_path_get_basename(filename);
3035 g_object_unref(file);
3038 recent_data->display_name = basename;
3039 recent_data->description = NULL;
3040 recent_data->mime_type = "text/x-gps-data";
3041 recent_data->app_name = (gchar *) g_get_application_name ();
3042 recent_data->app_exec = g_strjoin (" ", g_get_prgname (), "%f", NULL);
3043 recent_data->groups = groups;
3044 recent_data->is_private = FALSE;
3045 if (!gtk_recent_manager_add_full (manager, uri, recent_data))
3047 gchar *msg = g_strdup_printf (_("Unable to add '%s' to the list of recently used documents"), uri);
3048 vik_statusbar_set_message ( vw->viking_vs, VIK_STATUSBAR_INFO, msg );
3054 g_free (recent_data->app_exec);
3055 g_slice_free (GtkRecentData, recent_data);
3059 * Call this before doing things that may take a long time and otherwise not show any other feedback
3060 * such as loading and saving files
3062 void vik_window_set_busy_cursor ( VikWindow *vw )
3064 gdk_window_set_cursor ( gtk_widget_get_window(GTK_WIDGET(vw)), vw->busy_cursor );
3065 // Viewport has a separate cursor
3066 gdk_window_set_cursor ( gtk_widget_get_window(GTK_WIDGET(vw->viking_vvp)), vw->busy_cursor );
3067 // Ensure cursor updated before doing stuff
3068 while( gtk_events_pending() )
3069 gtk_main_iteration();
3072 void vik_window_clear_busy_cursor ( VikWindow *vw )
3074 gdk_window_set_cursor ( gtk_widget_get_window(GTK_WIDGET(vw)), NULL );
3075 // Restore viewport cursor
3076 gdk_window_set_cursor ( gtk_widget_get_window(GTK_WIDGET(vw->viking_vvp)), vw->viewport_cursor );
3079 void vik_window_open_file ( VikWindow *vw, const gchar *filename, gboolean change_filename )
3081 vik_window_set_busy_cursor ( vw );
3083 // Enable the *new* filename to be accessible by the Layers codez
3084 gchar *original_filename = g_strdup ( vw->filename );
3085 g_free ( vw->filename );
3086 vw->filename = g_strdup ( filename );
3087 gboolean success = FALSE;
3088 gboolean restore_original_filename = FALSE;
3090 vw->loaded_type = a_file_load ( vik_layers_panel_get_top_layer(vw->viking_vlp), vw->viking_vvp, filename );
3091 switch ( vw->loaded_type )
3093 case LOAD_TYPE_READ_FAILURE:
3094 a_dialog_error_msg ( GTK_WINDOW(vw), _("The file you requested could not be opened.") );
3096 case LOAD_TYPE_GPSBABEL_FAILURE:
3097 a_dialog_error_msg ( GTK_WINDOW(vw), _("GPSBabel is required to load files of this type or GPSBabel encountered problems.") );
3099 case LOAD_TYPE_GPX_FAILURE:
3100 a_dialog_error_msg_extra ( GTK_WINDOW(vw), _("Unable to load malformed GPX file %s"), filename );
3102 case LOAD_TYPE_UNSUPPORTED_FAILURE:
3103 a_dialog_error_msg_extra ( GTK_WINDOW(vw), _("Unsupported file type for %s"), filename );
3105 case LOAD_TYPE_VIK_FAILURE_NON_FATAL:
3107 // Since we can process .vik files with issues just show a warning in the status bar
3108 // Not that a user can do much about it... or tells them what this issue is yet...
3109 gchar *msg = g_strdup_printf (_("WARNING: issues encountered loading %s"), a_file_basename (filename) );
3110 vik_statusbar_set_message ( vw->viking_vs, VIK_STATUSBAR_INFO, msg );
3113 // No break, carry on to show any data
3114 case LOAD_TYPE_VIK_SUCCESS:
3116 restore_original_filename = TRUE; // NB Will actually get inverted by the 'success' component below
3117 GtkWidget *mode_button;
3119 if ( change_filename )
3120 window_set_filename ( vw, filename );
3121 mode_button = vik_window_get_drawmode_button ( vw, vik_viewport_get_drawmode ( vw->viking_vvp ) );
3122 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. */
3123 gtk_check_menu_item_set_active ( GTK_CHECK_MENU_ITEM(mode_button), TRUE );
3124 vw->only_updating_coord_mode_ui = FALSE;
3126 vik_layers_panel_change_coord_mode ( vw->viking_vlp, vik_viewport_get_coord_mode ( vw->viking_vvp ) );
3128 // Slightly long winded methods to align loaded viewport settings with the UI
3129 // Since the rewrite for toolbar + menu actions
3130 // there no longer exists a simple way to directly change the UI to a value for toggle settings
3131 // it only supports toggling the existing setting (otherwise get infinite loops in trying to align tb+menu elements)
3132 // Thus get state, compare them, if different then invert viewport setting and (re)sync the setting (via toggling)
3133 gboolean vp_state_scale = vik_viewport_get_draw_scale ( vw->viking_vvp );
3134 gboolean ui_state_scale = gtk_check_menu_item_get_active ( GTK_CHECK_MENU_ITEM(get_show_widget_by_name(vw, "ShowScale")) );
3135 if ( vp_state_scale != ui_state_scale ) {
3136 vik_viewport_set_draw_scale ( vw->viking_vvp, !vp_state_scale );
3137 toggle_draw_scale ( NULL, vw );
3139 gboolean vp_state_centermark = vik_viewport_get_draw_centermark ( vw->viking_vvp );
3140 gboolean ui_state_centermark = gtk_check_menu_item_get_active ( GTK_CHECK_MENU_ITEM(get_show_widget_by_name(vw, "ShowCenterMark")) );
3141 if ( vp_state_centermark != ui_state_centermark ) {
3142 vik_viewport_set_draw_centermark ( vw->viking_vvp, !vp_state_centermark );
3143 toggle_draw_centermark ( NULL, vw );
3145 gboolean vp_state_highlight = vik_viewport_get_draw_highlight ( vw->viking_vvp );
3146 gboolean ui_state_highlight = gtk_check_menu_item_get_active ( GTK_CHECK_MENU_ITEM(get_show_widget_by_name(vw, "ShowHighlight")) );
3147 if ( vp_state_highlight != ui_state_highlight ) {
3148 vik_viewport_set_draw_highlight ( vw->viking_vvp, !vp_state_highlight );
3149 toggle_draw_highlight ( NULL, vw );
3152 // NB No break, carry on to redraw
3153 //case LOAD_TYPE_OTHER_SUCCESS:
3156 // When LOAD_TYPE_OTHER_SUCCESS *only*, this will maintain the existing Viking project
3157 restore_original_filename = ! restore_original_filename;
3158 update_recently_used_document (vw, filename);
3163 if ( ! success || restore_original_filename )
3164 // Load didn't work or want to keep as the existing Viking project, keep using the original name
3165 window_set_filename ( vw, original_filename );
3166 g_free ( original_filename );
3168 vik_window_clear_busy_cursor ( vw );
3171 static void load_file ( GtkAction *a, VikWindow *vw )
3173 GSList *files = NULL;
3174 GSList *cur_file = NULL;
3176 if (!strcmp(gtk_action_get_name(a), "Open")) {
3179 else if (!strcmp(gtk_action_get_name(a), "Append")) {
3183 g_critical("Houston, we've had a problem.");
3187 if ( ! vw->open_dia )
3189 vw->open_dia = gtk_file_chooser_dialog_new (_("Please select a GPS data file to open. "),
3191 GTK_FILE_CHOOSER_ACTION_OPEN,
3192 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
3193 GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT,
3195 gchar *cwd = g_get_current_dir();
3197 gtk_file_chooser_set_current_folder ( GTK_FILE_CHOOSER(vw->open_dia), cwd );
3201 GtkFileFilter *filter;
3202 // NB file filters are listed this way for alphabetical ordering
3203 #ifdef VIK_CONFIG_GEOCACHES
3204 filter = gtk_file_filter_new ();
3205 gtk_file_filter_set_name( filter, _("Geocaching") );
3206 gtk_file_filter_add_pattern ( filter, "*.loc" ); // No MIME type available
3207 gtk_file_chooser_add_filter (GTK_FILE_CHOOSER(vw->open_dia), filter);
3210 filter = gtk_file_filter_new ();
3211 gtk_file_filter_set_name( filter, _("Google Earth") );
3212 gtk_file_filter_add_mime_type ( filter, "application/vnd.google-earth.kml+xml");
3213 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, _("GPX") );
3217 gtk_file_filter_add_pattern ( filter, "*.gpx" ); // No MIME type available
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, _("JPG") );
3222 gtk_file_filter_add_mime_type ( filter, "image/jpeg");
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, _("Viking") );
3227 gtk_file_filter_add_pattern ( filter, "*.vik" );
3228 gtk_file_filter_add_pattern ( filter, "*.viking" );
3229 gtk_file_chooser_add_filter (GTK_FILE_CHOOSER(vw->open_dia), filter);
3231 // NB could have filters for gpspoint (*.gps,*.gpsoint?) + gpsmapper (*.gsm,*.gpsmapper?)
3232 // However assume this are barely used and thus not worthy of inclusion
3233 // as they'll just make the options too many and have no clear file pattern
3234 // one can always use the all option
3235 filter = gtk_file_filter_new ();
3236 gtk_file_filter_set_name( filter, _("All") );
3237 gtk_file_filter_add_pattern ( filter, "*" );
3238 gtk_file_chooser_add_filter (GTK_FILE_CHOOSER(vw->open_dia), filter);
3239 // Default to any file - same as before open filters were added
3240 gtk_file_chooser_set_filter (GTK_FILE_CHOOSER(vw->open_dia), filter);
3242 gtk_file_chooser_set_select_multiple ( GTK_FILE_CHOOSER(vw->open_dia), TRUE );
3243 gtk_window_set_transient_for ( GTK_WINDOW(vw->open_dia), GTK_WINDOW(vw) );
3244 gtk_window_set_destroy_with_parent ( GTK_WINDOW(vw->open_dia), TRUE );
3246 if ( gtk_dialog_run ( GTK_DIALOG(vw->open_dia) ) == GTK_RESPONSE_ACCEPT )
3248 gtk_widget_hide ( vw->open_dia );
3249 #ifdef VIKING_PROMPT_IF_MODIFIED
3250 if ( (vw->modified || vw->filename) && newwindow )
3252 if ( vw->filename && newwindow )
3254 g_signal_emit ( G_OBJECT(vw), window_signals[VW_OPENWINDOW_SIGNAL], 0, gtk_file_chooser_get_filenames (GTK_FILE_CHOOSER(vw->open_dia) ) );
3256 files = gtk_file_chooser_get_filenames (GTK_FILE_CHOOSER(vw->open_dia) );
3257 gboolean change_fn = newwindow && (g_slist_length(files)==1); /* only change fn if one file */
3258 gboolean first_vik_file = TRUE;
3260 while ( cur_file ) {
3262 gchar *file_name = cur_file->data;
3263 if ( newwindow && check_file_magic_vik ( file_name ) ) {
3264 // Load first of many .vik files in current window
3265 if ( first_vik_file ) {
3266 vik_window_open_file ( vw, file_name, TRUE );
3267 first_vik_file = FALSE;
3270 // Load each subsequent .vik file in a separate window
3271 VikWindow *newvw = vik_window_new_window ();
3273 vik_window_open_file ( newvw, file_name, TRUE );
3278 vik_window_open_file ( vw, file_name, change_fn );
3281 cur_file = g_slist_next (cur_file);
3283 g_slist_free (files);
3287 gtk_widget_hide ( vw->open_dia );
3290 static gboolean save_file_as ( GtkAction *a, VikWindow *vw )
3292 gboolean rv = FALSE;
3294 if ( ! vw->save_dia )
3296 vw->save_dia = gtk_file_chooser_dialog_new (_("Save as Viking File."),
3298 GTK_FILE_CHOOSER_ACTION_SAVE,
3299 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
3300 GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT,
3302 gchar *cwd = g_get_current_dir();
3304 gtk_file_chooser_set_current_folder ( GTK_FILE_CHOOSER(vw->save_dia), cwd );
3308 GtkFileFilter *filter;
3309 filter = gtk_file_filter_new ();
3310 gtk_file_filter_set_name( filter, _("All") );
3311 gtk_file_filter_add_pattern ( filter, "*" );
3312 gtk_file_chooser_add_filter (GTK_FILE_CHOOSER(vw->save_dia), filter);
3314 filter = gtk_file_filter_new ();
3315 gtk_file_filter_set_name( filter, _("Viking") );
3316 gtk_file_filter_add_pattern ( filter, "*.vik" );
3317 gtk_file_filter_add_pattern ( filter, "*.viking" );
3318 gtk_file_chooser_add_filter (GTK_FILE_CHOOSER(vw->save_dia), filter);
3319 // Default to a Viking file
3320 gtk_file_chooser_set_filter (GTK_FILE_CHOOSER(vw->save_dia), filter);
3322 gtk_window_set_transient_for ( GTK_WINDOW(vw->save_dia), GTK_WINDOW(vw) );
3323 gtk_window_set_destroy_with_parent ( GTK_WINDOW(vw->save_dia), TRUE );
3325 // Auto append / replace extension with '.vik' to the suggested file name as it's going to be a Viking File
3326 gchar* auto_save_name = g_strdup ( window_get_filename ( vw ) );
3327 if ( ! a_file_check_ext ( auto_save_name, ".vik" ) )
3328 auto_save_name = g_strconcat ( auto_save_name, ".vik", NULL );
3330 gtk_file_chooser_set_current_name (GTK_FILE_CHOOSER(vw->save_dia), auto_save_name);
3332 while ( gtk_dialog_run ( GTK_DIALOG(vw->save_dia) ) == GTK_RESPONSE_ACCEPT )
3334 fn = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER(vw->save_dia) );
3335 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 ) ) )
3337 window_set_filename ( vw, fn );
3338 rv = window_save ( vw );
3339 vw->modified = FALSE;
3343 g_free ( auto_save_name );
3344 gtk_widget_hide ( vw->save_dia );
3348 static gboolean window_save ( VikWindow *vw )
3350 vik_window_set_busy_cursor ( vw );
3351 gboolean success = TRUE;
3353 if ( a_file_save ( vik_layers_panel_get_top_layer ( vw->viking_vlp ), vw->viking_vvp, vw->filename ) )
3355 update_recently_used_document ( vw, vw->filename );
3359 a_dialog_error_msg ( GTK_WINDOW(vw), _("The filename you requested could not be opened for writing.") );
3362 vik_window_clear_busy_cursor ( vw );
3366 static gboolean save_file ( GtkAction *a, VikWindow *vw )
3368 if ( ! vw->filename )
3369 return save_file_as ( NULL, vw );
3372 vw->modified = FALSE;
3373 return window_save ( vw );
3380 * Export all TRW Layers in the list to individual files in the specified directory
3382 * Returns: %TRUE on success
3384 static gboolean export_to ( VikWindow *vw, GList *gl, VikFileType_t vft, const gchar *dir, const gchar *extension )
3386 gboolean success = TRUE;
3388 gint export_count = 0;
3390 vik_window_set_busy_cursor ( vw );
3394 gchar *fn = g_strconcat ( dir, G_DIR_SEPARATOR_S, VIK_LAYER(gl->data)->name, extension, NULL );
3396 // Some protection in attempting to write too many same named files
3397 // As this will get horribly slow...
3398 gboolean safe = FALSE;
3400 while ( ii < 5000 ) {
3401 if ( g_file_test ( fn, G_FILE_TEST_EXISTS ) ) {
3404 fn = g_strdup_printf ( "%s%s%s#%03d%s", dir, G_DIR_SEPARATOR_S, VIK_LAYER(gl->data)->name, ii, extension );
3415 // NB: We allow exporting empty layers
3417 gboolean this_success = a_file_export ( VIK_TRW_LAYER(gl->data), fn, vft, NULL, TRUE );
3419 // Show some progress
3420 if ( this_success ) {
3422 gchar *message = g_strdup_printf ( _("Exporting to file: %s"), fn );
3423 vik_statusbar_set_message ( vw->viking_vs, VIK_STATUSBAR_INFO, message );
3424 while ( gtk_events_pending() )
3425 gtk_main_iteration ();
3429 success = success && this_success;
3433 gl = g_list_next ( gl );
3436 vik_window_clear_busy_cursor ( vw );
3438 // Confirm what happened.
3439 gchar *message = g_strdup_printf ( _("Exported files: %d"), export_count );
3440 vik_statusbar_set_message ( vw->viking_vs, VIK_STATUSBAR_INFO, message );
3446 static void export_to_common ( VikWindow *vw, VikFileType_t vft, const gchar *extension )
3448 GList *gl = vik_layers_panel_get_all_layers_of_type ( vw->viking_vlp, VIK_LAYER_TRW, TRUE );
3451 a_dialog_info_msg ( GTK_WINDOW(vw), _("Nothing to Export!") );
3455 GtkWidget *dialog = gtk_file_chooser_dialog_new ( _("Export to directory"),
3457 GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER,
3459 GTK_RESPONSE_REJECT,
3461 GTK_RESPONSE_ACCEPT,
3463 gtk_window_set_transient_for ( GTK_WINDOW(dialog), GTK_WINDOW(vw) );
3464 gtk_window_set_destroy_with_parent ( GTK_WINDOW(dialog), TRUE );
3465 gtk_window_set_modal ( GTK_WINDOW(dialog), TRUE );
3467 gtk_widget_show_all ( dialog );
3469 if ( gtk_dialog_run ( GTK_DIALOG(dialog) ) == GTK_RESPONSE_ACCEPT ) {
3470 gchar *dir = gtk_file_chooser_get_filename ( GTK_FILE_CHOOSER(dialog) );
3471 gtk_widget_destroy ( dialog );
3473 if ( !export_to ( vw, gl, vft, dir, extension ) )
3474 a_dialog_error_msg ( GTK_WINDOW(vw),_("Could not convert all files") );
3479 gtk_widget_destroy ( dialog );
3484 static void export_to_gpx ( GtkAction *a, VikWindow *vw )
3486 export_to_common ( vw, FILE_TYPE_GPX, ".gpx" );
3489 static void export_to_kml ( GtkAction *a, VikWindow *vw )
3491 export_to_common ( vw, FILE_TYPE_KML, ".kml" );
3494 #if !GLIB_CHECK_VERSION(2,26,0)
3495 typedef struct stat GStatBuf;
3498 static void file_properties_cb ( GtkAction *a, VikWindow *vw )
3500 gchar *message = NULL;
3501 if ( vw->filename ) {
3502 if ( g_file_test ( vw->filename, G_FILE_TEST_EXISTS ) ) {
3503 // Get some timestamp information of the file
3505 if ( g_stat ( vw->filename, &stat_buf ) == 0 ) {
3507 strftime ( time_buf, sizeof(time_buf), "%c", gmtime((const time_t *)&stat_buf.st_mtime) );
3509 gint byte_size = stat_buf.st_size;
3510 #if GLIB_CHECK_VERSION(2,30,0)
3511 size = g_format_size_full ( byte_size, G_FORMAT_SIZE_DEFAULT );
3513 size = g_format_size_for_display ( byte_size );
3515 message = g_strdup_printf ( "%s\n\n%s\n\n%s", vw->filename, time_buf, size );
3520 message = g_strdup ( _("File not accessible") );
3523 message = g_strdup ( _("No Viking File") );
3526 a_dialog_info_msg ( GTK_WINDOW(vw), message );
3530 static void my_acquire ( VikWindow *vw, VikDataSourceInterface *datasource )
3532 vik_datasource_mode_t mode = datasource->mode;
3533 if ( mode == VIK_DATASOURCE_AUTO_LAYER_MANAGEMENT )
3534 mode = VIK_DATASOURCE_CREATENEWLAYER;
3535 a_acquire ( vw, vw->viking_vlp, vw->viking_vvp, mode, datasource, NULL, NULL );
3538 static void acquire_from_gps ( GtkAction *a, VikWindow *vw )
3540 my_acquire ( vw, &vik_datasource_gps_interface );
3543 static void acquire_from_file ( GtkAction *a, VikWindow *vw )
3545 my_acquire ( vw, &vik_datasource_file_interface );
3548 static void acquire_from_geojson ( GtkAction *a, VikWindow *vw )
3550 my_acquire ( vw, &vik_datasource_geojson_interface );
3553 static void acquire_from_routing ( GtkAction *a, VikWindow *vw )
3555 my_acquire ( vw, &vik_datasource_routing_interface );
3558 #ifdef VIK_CONFIG_OPENSTREETMAP
3559 static void acquire_from_osm ( GtkAction *a, VikWindow *vw )
3561 my_acquire ( vw, &vik_datasource_osm_interface );
3564 static void acquire_from_my_osm ( GtkAction *a, VikWindow *vw )
3566 my_acquire ( vw, &vik_datasource_osm_my_traces_interface );
3570 #ifdef VIK_CONFIG_GEOCACHES
3571 static void acquire_from_gc ( GtkAction *a, VikWindow *vw )
3573 my_acquire ( vw, &vik_datasource_gc_interface );
3577 #ifdef VIK_CONFIG_GEOTAG
3578 static void acquire_from_geotag ( GtkAction *a, VikWindow *vw )
3580 my_acquire ( vw, &vik_datasource_geotag_interface );
3584 #ifdef VIK_CONFIG_GEONAMES
3585 static void acquire_from_wikipedia ( GtkAction *a, VikWindow *vw )
3587 my_acquire ( vw, &vik_datasource_wikipedia_interface );
3591 static void acquire_from_url ( GtkAction *a, VikWindow *vw )
3593 my_acquire ( vw, &vik_datasource_url_interface );
3596 static void goto_default_location( GtkAction *a, VikWindow *vw)
3599 ll.lat = a_vik_get_default_lat();
3600 ll.lon = a_vik_get_default_long();
3601 vik_viewport_set_center_latlon(vw->viking_vvp, &ll, TRUE);
3602 vik_layers_panel_emit_update(vw->viking_vlp);
3606 static void goto_address( GtkAction *a, VikWindow *vw)
3608 a_vik_goto ( vw, vw->viking_vvp );
3609 vik_layers_panel_emit_update ( vw->viking_vlp );
3612 static void mapcache_flush_cb ( GtkAction *a, VikWindow *vw )
3617 static void menu_copy_centre_cb ( GtkAction *a, VikWindow *vw )
3619 const VikCoord* coord;
3621 gchar *lat = NULL, *lon = NULL;
3623 coord = vik_viewport_get_center ( vw->viking_vvp );
3624 vik_coord_to_utm ( coord, &utm );
3626 gboolean full_format = FALSE;
3627 a_settings_get_boolean ( VIK_SETTINGS_WIN_COPY_CENTRE_FULL_FORMAT, &full_format );
3630 // Bells & Whistles - may include degrees, minutes and second symbols
3631 get_location_strings ( vw, utm, &lat, &lon );
3633 // Simple x.xx y.yy format
3635 a_coords_utm_to_latlon ( &utm, &ll );
3636 lat = g_strdup_printf ( "%.6f", ll.lat );
3637 lon = g_strdup_printf ( "%.6f", ll.lon );
3640 gchar *msg = g_strdup_printf ( "%s %s", lat, lon );
3644 a_clipboard_copy ( VIK_CLIPBOARD_DATA_TEXT, 0, 0, 0, msg, NULL );
3649 static void layer_defaults_cb ( GtkAction *a, VikWindow *vw )
3651 gchar **texts = g_strsplit ( gtk_action_get_name(a), "Layer", 0 );
3654 return; // Internally broken :(
3656 if ( ! a_layer_defaults_show_window ( GTK_WINDOW(vw), texts[1] ) )
3657 a_dialog_info_msg ( GTK_WINDOW(vw), _("This layer has no configurable properties.") );
3658 // NB no update needed
3660 g_strfreev ( texts );
3663 static void preferences_change_update ( VikWindow *vw, gpointer data )
3665 // Want to update all TrackWaypoint layers
3666 GList *layers = vik_layers_panel_get_all_layers_of_type ( vw->viking_vlp, VIK_LAYER_TRW, TRUE );
3672 // Reset the individual waypoints themselves due to the preferences change
3673 VikTrwLayer *vtl = VIK_TRW_LAYER(layers->data);
3674 vik_trw_layer_reset_waypoints ( vtl );
3675 layers = g_list_next ( layers );
3678 g_list_free ( layers );
3683 static void preferences_cb ( GtkAction *a, VikWindow *vw )
3685 gboolean wp_icon_size = a_vik_get_use_large_waypoint_icons();
3687 a_preferences_show_window ( GTK_WINDOW(vw) );
3689 // Has the waypoint size setting changed?
3690 if (wp_icon_size != a_vik_get_use_large_waypoint_icons()) {
3691 // Delete icon indexing 'cache' and so automatically regenerates with the new setting when changed
3692 clear_garmin_icon_syms ();
3694 // Update all windows
3695 g_slist_foreach ( window_list, (GFunc) preferences_change_update, NULL );
3698 // Ensure TZ Lookup initialized
3699 if ( a_vik_get_time_ref_frame() == VIK_TIME_REF_WORLD )
3700 vu_setup_lat_lon_tz_lookup();
3702 toolbar_apply_settings ( vw->viking_vtb, vw->main_vbox, vw->menu_hbox, TRUE );
3705 static void default_location_cb ( GtkAction *a, VikWindow *vw )
3707 /* Simplistic repeat of preference setting
3708 Only the name & type are important for setting the preference via this 'external' way */
3709 VikLayerParam pref_lat[] = {
3710 { VIK_LAYER_NUM_TYPES,
3711 VIKING_PREFERENCES_NAMESPACE "default_latitude",
3712 VIK_LAYER_PARAM_DOUBLE,
3715 VIK_LAYER_WIDGET_SPINBUTTON,
3724 VikLayerParam pref_lon[] = {
3725 { VIK_LAYER_NUM_TYPES,
3726 VIKING_PREFERENCES_NAMESPACE "default_longitude",
3727 VIK_LAYER_PARAM_DOUBLE,
3730 VIK_LAYER_WIDGET_SPINBUTTON,
3740 /* Get current center */
3742 vik_coord_to_latlon ( vik_viewport_get_center ( vw->viking_vvp ), &ll );
3744 /* Apply to preferences */
3745 VikLayerParamData vlp_data;
3746 vlp_data.d = ll.lat;
3747 a_preferences_run_setparam (vlp_data, pref_lat);
3748 vlp_data.d = ll.lon;
3749 a_preferences_run_setparam (vlp_data, pref_lon);
3750 /* Remember to save */
3751 a_preferences_save_to_file();
3754 static void clear_cb ( GtkAction *a, VikWindow *vw )
3756 vik_layers_panel_clear ( vw->viking_vlp );
3757 window_set_filename ( vw, NULL );
3761 static void window_close ( GtkAction *a, VikWindow *vw )
3763 if ( ! delete_event ( vw ) )
3764 gtk_widget_destroy ( GTK_WIDGET(vw) );
3767 static gboolean save_file_and_exit ( GtkAction *a, VikWindow *vw )
3769 if (save_file( NULL, vw)) {
3770 window_close( NULL, vw);
3777 static void zoom_to_cb ( GtkAction *a, VikWindow *vw )
3779 gdouble xmpp = vik_viewport_get_xmpp ( vw->viking_vvp ), ympp = vik_viewport_get_ympp ( vw->viking_vvp );
3780 if ( a_dialog_custom_zoom ( GTK_WINDOW(vw), &xmpp, &ympp ) )
3782 vik_viewport_set_xmpp ( vw->viking_vvp, xmpp );
3783 vik_viewport_set_ympp ( vw->viking_vvp, ympp );
3788 static void save_image_file ( VikWindow *vw, const gchar *fn, guint w, guint h, gdouble zoom, gboolean save_as_png )
3790 /* more efficient way: stuff draws directly to pixbuf (fork viewport) */
3791 GdkPixbuf *pixbuf_to_save;
3792 gdouble old_xmpp, old_ympp;
3793 GError *error = NULL;
3795 GtkWidget *msgbox = gtk_message_dialog_new ( GTK_WINDOW(vw),
3796 GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
3799 _("Generating image file...") );
3801 g_signal_connect_swapped (msgbox, "response", G_CALLBACK (gtk_widget_destroy), msgbox);
3802 // Ensure dialog shown
3803 gtk_widget_show_all ( msgbox );
3805 vik_statusbar_set_message ( vw->viking_vs, VIK_STATUSBAR_INFO, _("Generating image file...") );
3806 while ( gtk_events_pending() )
3807 gtk_main_iteration ();
3808 // Despite many efforts & variations, GTK on my Linux system doesn't show the actual msgbox contents :(
3809 // At least the empty box can give a clue something's going on + the statusbar msg...
3810 // Windows version under Wine OK!
3812 /* backup old zoom & set new */
3813 old_xmpp = vik_viewport_get_xmpp ( vw->viking_vvp );
3814 old_ympp = vik_viewport_get_ympp ( vw->viking_vvp );
3815 vik_viewport_set_zoom ( vw->viking_vvp, zoom );
3817 /* reset width and height: */
3818 vik_viewport_configure_manually ( vw->viking_vvp, w, h );
3820 /* draw all layers */
3823 /* save buffer as file. */
3824 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);
3825 if ( !pixbuf_to_save ) {
3826 g_warning("Failed to generate internal pixmap size: %d x %d", w, h);
3827 gtk_message_dialog_set_markup ( GTK_MESSAGE_DIALOG(msgbox), _("Failed to generate internal image.\n\nTry creating a smaller image.") );
3831 gdk_pixbuf_save ( pixbuf_to_save, fn, save_as_png ? "png" : "jpeg", &error, NULL );
3834 g_warning("Unable to write to file %s: %s", fn, error->message );
3835 gtk_message_dialog_set_markup ( GTK_MESSAGE_DIALOG(msgbox), _("Failed to generate image file.") );
3836 g_error_free (error);
3840 gtk_message_dialog_set_markup ( GTK_MESSAGE_DIALOG(msgbox), _("Image file generated.") );
3842 g_object_unref ( G_OBJECT(pixbuf_to_save) );
3845 vik_statusbar_set_message ( vw->viking_vs, VIK_STATUSBAR_INFO, "" );
3846 gtk_dialog_add_button ( GTK_DIALOG(msgbox), GTK_STOCK_OK, GTK_RESPONSE_OK );
3847 gtk_dialog_run ( GTK_DIALOG(msgbox) ); // Don't care about the result
3849 /* pretend like nothing happened ;) */
3850 vik_viewport_set_xmpp ( vw->viking_vvp, old_xmpp );
3851 vik_viewport_set_ympp ( vw->viking_vvp, old_ympp );
3852 vik_viewport_configure ( vw->viking_vvp );
3856 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 )
3858 gulong size = sizeof(gchar) * (strlen(fn) + 15);
3859 gchar *name_of_file = g_malloc ( size );
3861 struct UTM utm_orig, utm;
3863 /* *** copied from above *** */
3864 GdkPixbuf *pixbuf_to_save;
3865 gdouble old_xmpp, old_ympp;
3866 GError *error = NULL;
3868 /* backup old zoom & set new */
3869 old_xmpp = vik_viewport_get_xmpp ( vw->viking_vvp );
3870 old_ympp = vik_viewport_get_ympp ( vw->viking_vvp );
3871 vik_viewport_set_zoom ( vw->viking_vvp, zoom );
3873 /* reset width and height: do this only once for all images (same size) */
3874 vik_viewport_configure_manually ( vw->viking_vvp, w, h );
3875 /* *** end copy from above *** */
3877 g_assert ( vik_viewport_get_coord_mode ( vw->viking_vvp ) == VIK_COORD_UTM );
3881 utm_orig = *((const struct UTM *)vik_viewport_get_center ( vw->viking_vvp ));
3883 for ( y = 1; y <= tiles_h; y++ )
3885 for ( x = 1; x <= tiles_w; x++ )
3887 g_snprintf ( name_of_file, size, "%s%cy%d-x%d.%s", fn, G_DIR_SEPARATOR, y, x, save_as_png ? "png" : "jpg" );
3889 if ( tiles_w & 0x1 )
3890 utm.easting += ((gdouble)x - ceil(((gdouble)tiles_w)/2)) * (w*zoom);
3892 utm.easting += ((gdouble)x - (((gdouble)tiles_w)+1)/2) * (w*zoom);
3893 if ( tiles_h & 0x1 ) /* odd */
3894 utm.northing -= ((gdouble)y - ceil(((gdouble)tiles_h)/2)) * (h*zoom);
3896 utm.northing -= ((gdouble)y - (((gdouble)tiles_h)+1)/2) * (h*zoom);
3898 /* move to correct place. */
3899 vik_viewport_set_center_utm ( vw->viking_vvp, &utm, FALSE );
3903 /* save buffer as file. */
3904 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);
3905 gdk_pixbuf_save ( pixbuf_to_save, name_of_file, save_as_png ? "png" : "jpeg", &error, NULL );
3908 gchar *msg = g_strdup_printf (_("Unable to write to file %s: %s"), name_of_file, error->message );
3909 vik_statusbar_set_message ( vw->viking_vs, VIK_STATUSBAR_INFO, msg );
3911 g_error_free (error);
3914 g_object_unref ( G_OBJECT(pixbuf_to_save) );
3918 vik_viewport_set_center_utm ( vw->viking_vvp, &utm_orig, FALSE );
3919 vik_viewport_set_xmpp ( vw->viking_vvp, old_xmpp );
3920 vik_viewport_set_ympp ( vw->viking_vvp, old_ympp );
3921 vik_viewport_configure ( vw->viking_vvp );
3924 g_free ( name_of_file );
3927 static void draw_to_image_file_current_window_cb(GtkWidget* widget,GdkEventButton *event,gpointer *pass_along)
3929 VikWindow *vw = VIK_WINDOW(pass_along[0]);
3930 GtkSpinButton *width_spin = GTK_SPIN_BUTTON(pass_along[1]), *height_spin = GTK_SPIN_BUTTON(pass_along[2]);
3932 gint active = gtk_combo_box_get_active ( GTK_COMBO_BOX(pass_along[3]) );
3933 gdouble zoom = pow (2, active-2 );
3935 gdouble width_min, width_max, height_min, height_max;
3938 gtk_spin_button_get_range ( width_spin, &width_min, &width_max );
3939 gtk_spin_button_get_range ( height_spin, &height_min, &height_max );
3941 /* TODO: support for xzoom and yzoom values */
3942 width = vik_viewport_get_width ( vw->viking_vvp ) * vik_viewport_get_xmpp ( vw->viking_vvp ) / zoom;
3943 height = vik_viewport_get_height ( vw->viking_vvp ) * vik_viewport_get_xmpp ( vw->viking_vvp ) / zoom;
3945 if ( width > width_max || width < width_min || height > height_max || height < height_min )
3946 a_dialog_info_msg ( GTK_WINDOW(vw), _("Viewable region outside allowable pixel size bounds for image. Clipping width/height values.") );
3948 gtk_spin_button_set_value ( width_spin, width );
3949 gtk_spin_button_set_value ( height_spin, height );
3952 static void draw_to_image_file_total_area_cb (GtkSpinButton *spinbutton, gpointer *pass_along)
3954 GtkSpinButton *width_spin = GTK_SPIN_BUTTON(pass_along[1]), *height_spin = GTK_SPIN_BUTTON(pass_along[2]);
3956 gint active = gtk_combo_box_get_active ( GTK_COMBO_BOX(pass_along[3]) );
3957 gdouble zoom = pow (2, active-2 );
3961 w = gtk_spin_button_get_value(width_spin) * zoom;
3962 h = gtk_spin_button_get_value(height_spin) * zoom;
3963 if (pass_along[4]) /* save many images; find TOTAL area covered */
3965 w *= gtk_spin_button_get_value(GTK_SPIN_BUTTON(pass_along[4]));
3966 h *= gtk_spin_button_get_value(GTK_SPIN_BUTTON(pass_along[5]));
3968 vik_units_distance_t dist_units = a_vik_get_units_distance ();
3969 switch (dist_units) {
3970 case VIK_UNITS_DISTANCE_KILOMETRES:
3971 label_text = g_strdup_printf ( _("Total area: %ldm x %ldm (%.3f sq. km)"), (glong)w, (glong)h, (w*h/1000000));
3973 case VIK_UNITS_DISTANCE_MILES:
3974 label_text = g_strdup_printf ( _("Total area: %ldm x %ldm (%.3f sq. miles)"), (glong)w, (glong)h, (w*h/2589988.11));
3976 case VIK_UNITS_DISTANCE_NAUTICAL_MILES:
3977 label_text = g_strdup_printf ( _("Total area: %ldm x %ldm (%.3f sq. NM)"), (glong)w, (glong)h, (w*h/(1852.0*1852.0)));
3980 label_text = g_strdup_printf ("Just to keep the compiler happy");
3981 g_critical("Houston, we've had a problem. distance=%d", dist_units);
3984 gtk_label_set_text(GTK_LABEL(pass_along[6]), label_text);
3985 g_free ( label_text );
3989 * Get an allocated filename (or directory as specified)
3991 static gchar* draw_image_filename ( VikWindow *vw, gboolean one_image_only )
3994 if ( one_image_only )
3997 if (!vw->save_img_dia) {
3998 vw->save_img_dia = gtk_file_chooser_dialog_new (_("Save Image"),
4000 GTK_FILE_CHOOSER_ACTION_SAVE,
4001 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
4002 GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT,
4005 gchar *cwd = g_get_current_dir();
4007 gtk_file_chooser_set_current_folder ( GTK_FILE_CHOOSER(vw->save_img_dia), cwd );
4011 GtkFileChooser *chooser = GTK_FILE_CHOOSER ( vw->save_img_dia );
4013 GtkFileFilter *filter;
4014 filter = gtk_file_filter_new ();
4015 gtk_file_filter_set_name ( filter, _("All") );
4016 gtk_file_filter_add_pattern ( filter, "*" );
4017 gtk_file_chooser_add_filter ( chooser, filter );
4019 filter = gtk_file_filter_new ();
4020 gtk_file_filter_set_name ( filter, _("JPG") );
4021 gtk_file_filter_add_mime_type ( filter, "image/jpeg");
4022 gtk_file_chooser_add_filter ( chooser, filter );
4024 if ( !vw->draw_image_save_as_png )
4025 gtk_file_chooser_set_filter ( chooser, filter );
4027 filter = gtk_file_filter_new ();
4028 gtk_file_filter_set_name ( filter, _("PNG") );
4029 gtk_file_filter_add_mime_type ( filter, "image/png");
4030 gtk_file_chooser_add_filter ( chooser, filter );
4032 if ( vw->draw_image_save_as_png )
4033 gtk_file_chooser_set_filter ( chooser, filter );
4035 gtk_window_set_transient_for ( GTK_WINDOW(vw->save_img_dia), GTK_WINDOW(vw) );
4036 gtk_window_set_destroy_with_parent ( GTK_WINDOW(vw->save_img_dia), TRUE );
4039 if ( gtk_dialog_run ( GTK_DIALOG(vw->save_img_dia) ) == GTK_RESPONSE_ACCEPT ) {
4040 fn = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER(vw->save_img_dia) );
4041 if ( g_file_test ( fn, G_FILE_TEST_EXISTS ) )
4042 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 ) ) )
4045 gtk_widget_hide ( vw->save_img_dia );
4049 // For some reason this method is only written to work in UTM...
4050 if ( vik_viewport_get_coord_mode(vw->viking_vvp) != VIK_COORD_UTM ) {
4051 a_dialog_error_msg ( GTK_WINDOW(vw), _("You must be in UTM mode to use this feature") );
4055 if (!vw->save_img_dir_dia) {
4056 vw->save_img_dir_dia = gtk_file_chooser_dialog_new (_("Choose a directory to hold images"),
4058 GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER,
4059 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
4060 GTK_STOCK_OK, GTK_RESPONSE_ACCEPT,
4062 gtk_window_set_transient_for ( GTK_WINDOW(vw->save_img_dir_dia), GTK_WINDOW(vw) );
4063 gtk_window_set_destroy_with_parent ( GTK_WINDOW(vw->save_img_dir_dia), TRUE );
4066 if ( gtk_dialog_run ( GTK_DIALOG(vw->save_img_dir_dia) ) == GTK_RESPONSE_ACCEPT ) {
4067 fn = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER(vw->save_img_dir_dia) );
4069 gtk_widget_hide ( vw->save_img_dir_dia );
4074 static void draw_to_image_file ( VikWindow *vw, gboolean one_image_only )
4076 /* todo: default for answers inside VikWindow or static (thruout instance) */
4077 GtkWidget *dialog = gtk_dialog_new_with_buttons ( _("Save to Image File"), GTK_WINDOW(vw),
4078 GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
4080 GTK_RESPONSE_REJECT,
4082 GTK_RESPONSE_ACCEPT,
4084 GtkWidget *width_label, *width_spin, *height_label, *height_spin;
4085 GtkWidget *png_radio, *jpeg_radio;
4086 GtkWidget *current_window_button;
4087 gpointer current_window_pass_along[7];
4088 GtkWidget *zoom_label, *zoom_combo;
4089 GtkWidget *total_size_label;
4091 /* only used if (!one_image_only) */
4092 GtkWidget *tiles_width_spin = NULL, *tiles_height_spin = NULL;
4094 width_label = gtk_label_new ( _("Width (pixels):") );
4095 width_spin = gtk_spin_button_new ( GTK_ADJUSTMENT(gtk_adjustment_new ( vw->draw_image_width, 10, 50000, 10, 100, 0 )), 10, 0 );
4096 height_label = gtk_label_new ( _("Height (pixels):") );
4097 height_spin = gtk_spin_button_new ( GTK_ADJUSTMENT(gtk_adjustment_new ( vw->draw_image_height, 10, 50000, 10, 100, 0 )), 10, 0 );
4099 GtkWidget *win_warning_label = gtk_label_new ( _("WARNING: USING LARGE IMAGES OVER 10000x10000\nMAY CRASH THE PROGRAM!") );
4101 zoom_label = gtk_label_new ( _("Zoom (meters per pixel):") );
4102 /* TODO: separate xzoom and yzoom factors */
4103 zoom_combo = create_zoom_combo_all_levels();
4105 gdouble mpp = vik_viewport_get_xmpp(vw->viking_vvp);
4106 gint active = 2 + round ( log (mpp) / log (2) );
4108 // Can we not hard code size here?
4113 gtk_combo_box_set_active ( GTK_COMBO_BOX(zoom_combo), active );
4115 total_size_label = gtk_label_new ( NULL );
4117 current_window_button = gtk_button_new_with_label ( _("Area in current viewable window") );
4118 current_window_pass_along [0] = vw;
4119 current_window_pass_along [1] = width_spin;
4120 current_window_pass_along [2] = height_spin;
4121 current_window_pass_along [3] = zoom_combo;
4122 current_window_pass_along [4] = NULL; /* used for one_image_only != 1 */
4123 current_window_pass_along [5] = NULL;
4124 current_window_pass_along [6] = total_size_label;
4125 g_signal_connect ( G_OBJECT(current_window_button), "button_press_event", G_CALLBACK(draw_to_image_file_current_window_cb), current_window_pass_along );
4127 png_radio = gtk_radio_button_new_with_label ( NULL, _("Save as PNG") );
4128 jpeg_radio = gtk_radio_button_new_with_label_from_widget ( GTK_RADIO_BUTTON(png_radio), _("Save as JPEG") );
4130 gtk_box_pack_start (GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), png_radio, FALSE, FALSE, 0);
4131 gtk_box_pack_start (GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), jpeg_radio, FALSE, FALSE, 0);
4133 if ( ! vw->draw_image_save_as_png )
4134 gtk_toggle_button_set_active ( GTK_TOGGLE_BUTTON(jpeg_radio), TRUE );
4136 gtk_box_pack_start (GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), width_label, FALSE, FALSE, 0);
4137 gtk_box_pack_start (GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), width_spin, FALSE, FALSE, 0);
4138 gtk_box_pack_start (GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), height_label, FALSE, FALSE, 0);
4139 gtk_box_pack_start (GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), height_spin, FALSE, FALSE, 0);
4141 gtk_box_pack_start (GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), win_warning_label, FALSE, FALSE, 0);
4143 gtk_box_pack_start (GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), current_window_button, FALSE, FALSE, 0);
4144 gtk_box_pack_start (GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), zoom_label, FALSE, FALSE, 0);
4145 gtk_box_pack_start (GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), zoom_combo, FALSE, FALSE, 0);
4147 if ( ! one_image_only )
4149 GtkWidget *tiles_width_label, *tiles_height_label;
4151 tiles_width_label = gtk_label_new ( _("East-west image tiles:") );
4152 tiles_width_spin = gtk_spin_button_new ( GTK_ADJUSTMENT(gtk_adjustment_new ( 5, 1, 10, 1, 100, 0 )), 1, 0 );
4153 tiles_height_label = gtk_label_new ( _("North-south image tiles:") );
4154 tiles_height_spin = gtk_spin_button_new ( GTK_ADJUSTMENT(gtk_adjustment_new ( 5, 1, 10, 1, 100, 0 )), 1, 0 );
4155 gtk_box_pack_start (GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), tiles_width_label, FALSE, FALSE, 0);
4156 gtk_box_pack_start (GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), tiles_width_spin, FALSE, FALSE, 0);
4157 gtk_box_pack_start (GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), tiles_height_label, FALSE, FALSE, 0);
4158 gtk_box_pack_start (GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), tiles_height_spin, FALSE, FALSE, 0);
4160 current_window_pass_along [4] = tiles_width_spin;
4161 current_window_pass_along [5] = tiles_height_spin;
4162 g_signal_connect ( G_OBJECT(tiles_width_spin), "value-changed", G_CALLBACK(draw_to_image_file_total_area_cb), current_window_pass_along );
4163 g_signal_connect ( G_OBJECT(tiles_height_spin), "value-changed", G_CALLBACK(draw_to_image_file_total_area_cb), current_window_pass_along );
4165 gtk_box_pack_start (GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), total_size_label, FALSE, FALSE, 0);
4166 g_signal_connect ( G_OBJECT(width_spin), "value-changed", G_CALLBACK(draw_to_image_file_total_area_cb), current_window_pass_along );
4167 g_signal_connect ( G_OBJECT(height_spin), "value-changed", G_CALLBACK(draw_to_image_file_total_area_cb), current_window_pass_along );
4168 g_signal_connect ( G_OBJECT(zoom_combo), "changed", G_CALLBACK(draw_to_image_file_total_area_cb), current_window_pass_along );
4170 draw_to_image_file_total_area_cb ( NULL, current_window_pass_along ); /* set correct size info now */
4172 gtk_dialog_set_default_response ( GTK_DIALOG(dialog), GTK_RESPONSE_ACCEPT );
4174 gtk_widget_show_all ( gtk_dialog_get_content_area(GTK_DIALOG(dialog)) );
4176 if ( gtk_dialog_run ( GTK_DIALOG(dialog) ) == GTK_RESPONSE_ACCEPT )
4178 gtk_widget_hide ( GTK_WIDGET(dialog) );
4180 gchar *fn = draw_image_filename ( vw, one_image_only );
4184 gint active_z = gtk_combo_box_get_active ( GTK_COMBO_BOX(zoom_combo) );
4185 gdouble zoom = pow (2, active_z-2 );
4187 if ( one_image_only )
4188 save_image_file ( vw, fn,
4189 vw->draw_image_width = gtk_spin_button_get_value_as_int ( GTK_SPIN_BUTTON(width_spin) ),
4190 vw->draw_image_height = gtk_spin_button_get_value_as_int ( GTK_SPIN_BUTTON(height_spin) ),
4192 vw->draw_image_save_as_png = gtk_toggle_button_get_active ( GTK_TOGGLE_BUTTON(png_radio) ) );
4194 // NB is in UTM mode ATM
4195 save_image_dir ( vw, fn,
4196 vw->draw_image_width = gtk_spin_button_get_value_as_int ( GTK_SPIN_BUTTON(width_spin) ),
4197 vw->draw_image_height = gtk_spin_button_get_value_as_int ( GTK_SPIN_BUTTON(height_spin) ),
4199 vw->draw_image_save_as_png = gtk_toggle_button_get_active ( GTK_TOGGLE_BUTTON(png_radio) ),
4200 gtk_spin_button_get_value ( GTK_SPIN_BUTTON(tiles_width_spin) ),
4201 gtk_spin_button_get_value ( GTK_SPIN_BUTTON(tiles_height_spin) ) );
4206 gtk_widget_destroy ( GTK_WIDGET(dialog) );
4210 static void draw_to_image_file_cb ( GtkAction *a, VikWindow *vw )
4212 draw_to_image_file ( vw, TRUE );
4215 static void draw_to_image_dir_cb ( GtkAction *a, VikWindow *vw )
4217 draw_to_image_file ( vw, FALSE );
4220 static void print_cb ( GtkAction *a, VikWindow *vw )
4222 a_print(vw, vw->viking_vvp);
4225 /* really a misnomer: changes coord mode (actual coordinates) AND/OR draw mode (viewport only) */
4226 static void window_change_coord_mode_cb ( GtkAction *old_a, GtkAction *a, VikWindow *vw )
4228 const gchar *name = gtk_action_get_name(a);
4229 GtkToggleToolButton *tbutton = (GtkToggleToolButton *)toolbar_get_widget_by_name ( vw->viking_vtb, name );
4231 gtk_toggle_tool_button_set_active ( tbutton, TRUE );
4233 VikViewportDrawMode drawmode;
4234 if (!g_strcmp0(name, "ModeUTM")) {
4235 drawmode = VIK_VIEWPORT_DRAWMODE_UTM;
4237 else if (!g_strcmp0(name, "ModeLatLon")) {
4238 drawmode = VIK_VIEWPORT_DRAWMODE_LATLON;
4240 else if (!g_strcmp0(name, "ModeExpedia")) {
4241 drawmode = VIK_VIEWPORT_DRAWMODE_EXPEDIA;
4243 else if (!g_strcmp0(name, "ModeMercator")) {
4244 drawmode = VIK_VIEWPORT_DRAWMODE_MERCATOR;
4247 g_critical("Houston, we've had a problem.");
4251 if ( !vw->only_updating_coord_mode_ui )
4253 VikViewportDrawMode olddrawmode = vik_viewport_get_drawmode ( vw->viking_vvp );
4254 if ( olddrawmode != drawmode )
4256 /* this takes care of coord mode too */
4257 vik_viewport_set_drawmode ( vw->viking_vvp, drawmode );
4258 if ( drawmode == VIK_VIEWPORT_DRAWMODE_UTM ) {
4259 vik_layers_panel_change_coord_mode ( vw->viking_vlp, VIK_COORD_UTM );
4260 } else if ( olddrawmode == VIK_VIEWPORT_DRAWMODE_UTM ) {
4261 vik_layers_panel_change_coord_mode ( vw->viking_vlp, VIK_COORD_LATLON );
4268 static void toggle_draw_scale ( GtkAction *a, VikWindow *vw )
4270 gboolean state = !vik_viewport_get_draw_scale ( vw->viking_vvp );
4271 GtkWidget *check_box = gtk_ui_manager_get_widget ( vw->uim, "/ui/MainMenu/View/SetShow/ShowScale" );
4274 gtk_check_menu_item_set_active ( GTK_CHECK_MENU_ITEM(check_box), state );
4275 vik_viewport_set_draw_scale ( vw->viking_vvp, state );
4279 static void toggle_draw_centermark ( GtkAction *a, VikWindow *vw )
4281 gboolean state = !vik_viewport_get_draw_centermark ( vw->viking_vvp );
4282 GtkWidget *check_box = gtk_ui_manager_get_widget ( vw->uim, "/ui/MainMenu/View/SetShow/ShowCenterMark" );
4285 gtk_check_menu_item_set_active ( GTK_CHECK_MENU_ITEM(check_box), state );
4286 vik_viewport_set_draw_centermark ( vw->viking_vvp, state );
4290 static void toggle_draw_highlight ( GtkAction *a, VikWindow *vw )
4292 gboolean state = !vik_viewport_get_draw_highlight ( vw->viking_vvp );
4293 GtkWidget *check_box = gtk_ui_manager_get_widget ( vw->uim, "/ui/MainMenu/View/SetShow/ShowHighlight" );
4296 gtk_check_menu_item_set_active ( GTK_CHECK_MENU_ITEM(check_box), state );
4297 vik_viewport_set_draw_highlight ( vw->viking_vvp, state );
4301 static void set_bg_color ( GtkAction *a, VikWindow *vw )
4303 GtkWidget *colorsd = gtk_color_selection_dialog_new ( _("Choose a background color") );
4304 GdkColor *color = vik_viewport_get_background_gdkcolor ( vw->viking_vvp );
4305 gtk_color_selection_set_previous_color ( GTK_COLOR_SELECTION(gtk_color_selection_dialog_get_color_selection(GTK_COLOR_SELECTION_DIALOG(colorsd))), color );
4306 gtk_color_selection_set_current_color ( GTK_COLOR_SELECTION(gtk_color_selection_dialog_get_color_selection(GTK_COLOR_SELECTION_DIALOG(colorsd))), color );
4307 if ( gtk_dialog_run ( GTK_DIALOG(colorsd) ) == GTK_RESPONSE_OK )
4309 gtk_color_selection_get_current_color ( GTK_COLOR_SELECTION(gtk_color_selection_dialog_get_color_selection(GTK_COLOR_SELECTION_DIALOG(colorsd))), color );
4310 vik_viewport_set_background_gdkcolor ( vw->viking_vvp, color );
4314 gtk_widget_destroy ( colorsd );
4317 static void set_highlight_color ( GtkAction *a, VikWindow *vw )
4319 GtkWidget *colorsd = gtk_color_selection_dialog_new ( _("Choose a track highlight color") );
4320 GdkColor *color = vik_viewport_get_highlight_gdkcolor ( vw->viking_vvp );
4321 gtk_color_selection_set_previous_color ( GTK_COLOR_SELECTION(gtk_color_selection_dialog_get_color_selection(GTK_COLOR_SELECTION_DIALOG(colorsd))), color );
4322 gtk_color_selection_set_current_color ( GTK_COLOR_SELECTION(gtk_color_selection_dialog_get_color_selection(GTK_COLOR_SELECTION_DIALOG(colorsd))), color );
4323 if ( gtk_dialog_run ( GTK_DIALOG(colorsd) ) == GTK_RESPONSE_OK )
4325 gtk_color_selection_get_current_color ( GTK_COLOR_SELECTION(gtk_color_selection_dialog_get_color_selection(GTK_COLOR_SELECTION_DIALOG(colorsd))), color );
4326 vik_viewport_set_highlight_gdkcolor ( vw->viking_vvp, color );
4330 gtk_widget_destroy ( colorsd );
4334 /***********************************************************************************************
4336 ***********************************************************************************************/
4338 static GtkActionEntry entries[] = {
4339 { "File", NULL, N_("_File"), 0, 0, 0 },
4340 { "Edit", NULL, N_("_Edit"), 0, 0, 0 },
4341 { "View", NULL, N_("_View"), 0, 0, 0 },
4342 { "SetShow", NULL, N_("_Show"), 0, 0, 0 },
4343 { "SetZoom", NULL, N_("_Zoom"), 0, 0, 0 },
4344 { "SetPan", NULL, N_("_Pan"), 0, 0, 0 },
4345 { "Layers", NULL, N_("_Layers"), 0, 0, 0 },
4346 { "Tools", NULL, N_("_Tools"), 0, 0, 0 },
4347 { "Exttools", NULL, N_("_Webtools"), 0, 0, 0 },
4348 { "Help", NULL, N_("_Help"), 0, 0, 0 },
4350 { "New", GTK_STOCK_NEW, N_("_New"), "<control>N", N_("New file"), (GCallback)newwindow_cb },
4351 { "Open", GTK_STOCK_OPEN, N_("_Open..."), "<control>O", N_("Open a file"), (GCallback)load_file },
4352 { "OpenRecentFile", NULL, N_("Open _Recent File"), NULL, NULL, (GCallback)NULL },
4353 { "Append", GTK_STOCK_ADD, N_("Append _File..."), NULL, N_("Append data from a different file"), (GCallback)load_file },
4354 { "Export", GTK_STOCK_CONVERT, N_("_Export All"), NULL, N_("Export All TrackWaypoint Layers"), (GCallback)NULL },
4355 { "ExportGPX", NULL, N_("_GPX..."), NULL, N_("Export as GPX"), (GCallback)export_to_gpx },
4356 { "Acquire", GTK_STOCK_GO_DOWN, N_("A_cquire"), NULL, NULL, (GCallback)NULL },
4357 { "AcquireGPS", NULL, N_("From _GPS..."), NULL, N_("Transfer data from a GPS device"), (GCallback)acquire_from_gps },
4358 { "AcquireGPSBabel", NULL, N_("Import File With GPS_Babel..."), NULL, N_("Import file via GPSBabel converter"), (GCallback)acquire_from_file },
4359 { "AcquireRouting", NULL, N_("_Directions..."), NULL, N_("Get driving directions"), (GCallback)acquire_from_routing },
4360 #ifdef VIK_CONFIG_OPENSTREETMAP
4361 { "AcquireOSM", NULL, N_("_OSM Traces..."), NULL, N_("Get traces from OpenStreetMap"), (GCallback)acquire_from_osm },
4362 { "AcquireMyOSM", NULL, N_("_My OSM Traces..."), NULL, N_("Get Your Own Traces from OpenStreetMap"), (GCallback)acquire_from_my_osm },
4364 #ifdef VIK_CONFIG_GEOCACHES
4365 { "AcquireGC", NULL, N_("Geo_caches..."), NULL, N_("Get Geocaches from geocaching.com"), (GCallback)acquire_from_gc },
4367 #ifdef VIK_CONFIG_GEOTAG
4368 { "AcquireGeotag", NULL, N_("From Geotagged _Images..."), NULL, N_("Create waypoints from geotagged images"), (GCallback)acquire_from_geotag },
4370 { "AcquireURL", NULL, N_("From _URL..."), NULL, N_("Get a file from a URL"), (GCallback)acquire_from_url },
4371 #ifdef VIK_CONFIG_GEONAMES
4372 { "AcquireWikipedia", NULL, N_("From _Wikipedia Waypoints"), NULL, N_("Create waypoints from Wikipedia items in the current view"), (GCallback)acquire_from_wikipedia },
4374 { "Save", GTK_STOCK_SAVE, N_("_Save"), "<control>S", N_("Save the file"), (GCallback)save_file },
4375 { "SaveAs", GTK_STOCK_SAVE_AS, N_("Save _As..."), NULL, N_("Save the file under different name"), (GCallback)save_file_as },
4376 { "FileProperties", NULL, N_("Properties..."), NULL, N_("File Properties"), (GCallback)file_properties_cb },
4377 { "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 },
4378 { "GenImgDir", GTK_STOCK_DND_MULTIPLE, N_("Generate _Directory of Images..."), NULL, N_("Generate _Directory of Images"), (GCallback)draw_to_image_dir_cb },
4379 { "Print", GTK_STOCK_PRINT, N_("_Print..."), NULL, N_("Print maps"), (GCallback)print_cb },
4380 { "Exit", GTK_STOCK_QUIT, N_("E_xit"), "<control>W", N_("Exit the program"), (GCallback)window_close },
4381 { "SaveExit", GTK_STOCK_QUIT, N_("Save and Exit"), NULL, N_("Save and Exit the program"), (GCallback)save_file_and_exit },
4383 { "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 },
4384 { "GoForward", GTK_STOCK_GO_FORWARD, N_("Go to the _Next Location"), NULL, N_("Go to the next location"), (GCallback)draw_goto_back_and_forth },
4385 { "GotoDefaultLocation", GTK_STOCK_HOME, N_("Go to the _Default Location"), NULL, N_("Go to the default location"), (GCallback)goto_default_location },
4386 { "GotoSearch", GTK_STOCK_JUMP_TO, N_("Go to _Location..."), NULL, N_("Go to address/place using text search"), (GCallback)goto_address },
4387 { "GotoLL", GTK_STOCK_JUMP_TO, N_("_Go to Lat/Lon..."), NULL, N_("Go to arbitrary lat/lon coordinate"), (GCallback)draw_goto_cb },
4388 { "GotoUTM", GTK_STOCK_JUMP_TO, N_("Go to UTM..."), NULL, N_("Go to arbitrary UTM coordinate"), (GCallback)draw_goto_cb },
4389 { "Refresh", GTK_STOCK_REFRESH, N_("_Refresh"), "F5", N_("Refresh any maps displayed"), (GCallback)draw_refresh_cb },
4390 { "SetHLColor",GTK_STOCK_SELECT_COLOR, N_("Set _Highlight Color..."), NULL, N_("Set Highlight Color"), (GCallback)set_highlight_color },
4391 { "SetBGColor",GTK_STOCK_SELECT_COLOR, N_("Set Bac_kground Color..."), NULL, N_("Set Background Color"), (GCallback)set_bg_color },
4392 { "ZoomIn", GTK_STOCK_ZOOM_IN, N_("Zoom _In"), "<control>plus", N_("Zoom In"), (GCallback)draw_zoom_cb },
4393 { "ZoomOut", GTK_STOCK_ZOOM_OUT, N_("Zoom _Out"), "<control>minus", N_("Zoom Out"), (GCallback)draw_zoom_cb },
4394 { "ZoomTo", GTK_STOCK_ZOOM_FIT, N_("Zoom _To..."), "<control>Z", N_("Zoom To"), (GCallback)zoom_to_cb },
4395 { "PanNorth", NULL, N_("Pan _North"), "<control>Up", NULL, (GCallback)draw_pan_cb },
4396 { "PanEast", NULL, N_("Pan _East"), "<control>Right", NULL, (GCallback)draw_pan_cb },
4397 { "PanSouth", NULL, N_("Pan _South"), "<control>Down", NULL, (GCallback)draw_pan_cb },
4398 { "PanWest", NULL, N_("Pan _West"), "<control>Left", NULL, (GCallback)draw_pan_cb },
4399 { "BGJobs", GTK_STOCK_EXECUTE, N_("Background _Jobs"), NULL, N_("Background Jobs"), (GCallback)a_background_show_window },
4401 { "Cut", GTK_STOCK_CUT, N_("Cu_t"), NULL, N_("Cut selected layer"), (GCallback)menu_cut_layer_cb },
4402 { "Copy", GTK_STOCK_COPY, N_("_Copy"), NULL, N_("Copy selected layer"), (GCallback)menu_copy_layer_cb },
4403 { "Paste", GTK_STOCK_PASTE, N_("_Paste"), NULL, N_("Paste layer into selected container layer or otherwise above selected layer"), (GCallback)menu_paste_layer_cb },
4404 { "Delete", GTK_STOCK_DELETE, N_("_Delete"), NULL, N_("Remove selected layer"), (GCallback)menu_delete_layer_cb },
4405 { "DeleteAll", NULL, N_("Delete All"), NULL, NULL, (GCallback)clear_cb },
4406 { "CopyCentre",NULL, N_("Copy Centre _Location"), "<control>h", NULL, (GCallback)menu_copy_centre_cb },
4407 { "MapCacheFlush",NULL, N_("_Flush Map Cache"), NULL, NULL, (GCallback)mapcache_flush_cb },
4408 { "SetDefaultLocation", GTK_STOCK_GO_FORWARD, N_("_Set the Default Location"), NULL, N_("Set the Default Location to the current position"),(GCallback)default_location_cb },
4409 { "Preferences",GTK_STOCK_PREFERENCES, N_("_Preferences"), NULL, N_("Program Preferences"), (GCallback)preferences_cb },
4410 { "LayerDefaults",GTK_STOCK_PROPERTIES, N_("_Layer Defaults"), NULL, NULL, NULL },
4411 { "Properties",GTK_STOCK_PROPERTIES, N_("_Properties"), NULL, N_("Layer Properties"), (GCallback)menu_properties_cb },
4413 { "HelpEntry", GTK_STOCK_HELP, N_("_Help"), "F1", N_("Help"), (GCallback)help_help_cb },
4414 { "About", GTK_STOCK_ABOUT, N_("_About"), NULL, N_("About"), (GCallback)help_about_cb },
4417 static GtkActionEntry debug_entries[] = {
4418 { "MapCacheInfo", NULL, "_Map Cache Info", NULL, NULL, (GCallback)help_cache_info_cb },
4419 { "BackForwardInfo", NULL, "_Back/Forward Info", NULL, NULL, (GCallback)back_forward_info_cb },
4422 static GtkActionEntry entries_gpsbabel[] = {
4423 { "ExportKML", NULL, N_("_KML..."), NULL, N_("Export as KML"), (GCallback)export_to_kml },
4426 static GtkActionEntry entries_geojson[] = {
4427 { "AcquireGeoJSON", NULL, N_("Import Geo_JSON File..."), NULL, N_("Import GeoJSON file"), (GCallback)acquire_from_geojson },
4431 static GtkRadioActionEntry mode_entries[] = {
4432 { "ModeUTM", NULL, N_("_UTM Mode"), "<control>u", NULL, VIK_VIEWPORT_DRAWMODE_UTM },
4433 { "ModeExpedia", NULL, N_("_Expedia Mode"), "<control>e", NULL, VIK_VIEWPORT_DRAWMODE_EXPEDIA },
4434 { "ModeMercator", NULL, N_("_Mercator Mode"), "<control>m", NULL, VIK_VIEWPORT_DRAWMODE_MERCATOR },
4435 { "ModeLatLon", NULL, N_("Lat_/Lon Mode"), "<control>l", NULL, VIK_VIEWPORT_DRAWMODE_LATLON },
4438 static GtkToggleActionEntry toggle_entries[] = {
4439 { "ShowScale", NULL, N_("Show _Scale"), "<shift>F5", N_("Show Scale"), (GCallback)toggle_draw_scale, TRUE },
4440 { "ShowCenterMark", NULL, N_("Show _Center Mark"), "F6", N_("Show Center Mark"), (GCallback)toggle_draw_centermark, TRUE },
4441 { "ShowHighlight", GTK_STOCK_UNDERLINE, N_("Show _Highlight"), "F7", N_("Show Highlight"), (GCallback)toggle_draw_highlight, TRUE },
4442 { "FullScreen", GTK_STOCK_FULLSCREEN, N_("_Full Screen"), "F11", N_("Activate full screen mode"), (GCallback)full_screen_cb, FALSE },
4443 { "ViewSidePanel", GTK_STOCK_INDEX, N_("Show Side _Panel"), "F9", N_("Show Side Panel"), (GCallback)view_side_panel_cb, TRUE },
4444 { "ViewStatusBar", NULL, N_("Show Status_bar"), "F12", N_("Show Statusbar"), (GCallback)view_statusbar_cb, TRUE },
4445 { "ViewToolbar", NULL, N_("Show _Toolbar"), "F3", N_("Show Toolbar"), (GCallback)view_toolbar_cb, TRUE },
4446 { "ViewMainMenu", NULL, N_("Show _Menu"), "F4", N_("Show Menu"), (GCallback)view_main_menu_cb, TRUE },
4449 // This must match the toggle entries order above
4450 static gpointer toggle_entries_toolbar_cb[] = {
4451 (GCallback)tb_set_draw_scale,
4452 (GCallback)tb_set_draw_centermark,
4453 (GCallback)tb_set_draw_highlight,
4454 (GCallback)tb_full_screen_cb,
4455 (GCallback)tb_view_side_panel_cb,
4456 (GCallback)tb_view_statusbar_cb,
4457 (GCallback)tb_view_toolbar_cb,
4458 (GCallback)tb_view_main_menu_cb,
4461 #include "menu.xml.h"
4462 static void window_create_ui( VikWindow *window )
4465 GtkActionGroup *action_group;
4466 GtkAccelGroup *accel_group;
4469 GtkIconFactory *icon_factory;
4470 GtkIconSet *icon_set;
4471 GtkRadioActionEntry *tools = NULL, *radio;
4474 uim = gtk_ui_manager_new ();
4477 toolbox_add_tool(window->vt, &ruler_tool, TOOL_LAYER_TYPE_NONE);
4478 toolbox_add_tool(window->vt, &zoom_tool, TOOL_LAYER_TYPE_NONE);
4479 toolbox_add_tool(window->vt, &pan_tool, TOOL_LAYER_TYPE_NONE);
4480 toolbox_add_tool(window->vt, &select_tool, TOOL_LAYER_TYPE_NONE);
4482 toolbar_action_tool_entry_register ( window->viking_vtb, &pan_tool.radioActionEntry );
4483 toolbar_action_tool_entry_register ( window->viking_vtb, &zoom_tool.radioActionEntry );
4484 toolbar_action_tool_entry_register ( window->viking_vtb, &ruler_tool.radioActionEntry );
4485 toolbar_action_tool_entry_register ( window->viking_vtb, &select_tool.radioActionEntry );
4488 if (!(mid = gtk_ui_manager_add_ui_from_string (uim, menu_xml, -1, &error))) {
4489 g_error_free (error);
4493 action_group = gtk_action_group_new ("MenuActions");
4494 gtk_action_group_set_translation_domain(action_group, PACKAGE_NAME);
4495 gtk_action_group_add_actions (action_group, entries, G_N_ELEMENTS (entries), window);
4496 gtk_action_group_add_toggle_actions (action_group, toggle_entries, G_N_ELEMENTS (toggle_entries), window);
4497 gtk_action_group_add_radio_actions (action_group, mode_entries, G_N_ELEMENTS (mode_entries), 4, (GCallback)window_change_coord_mode_cb, window);
4499 if ( gtk_ui_manager_add_ui_from_string ( uim,
4500 "<ui><menubar name='MainMenu'><menu action='Help'>"
4501 "<menuitem action='MapCacheInfo'/>"
4502 "<menuitem action='BackForwardInfo'/>"
4503 "</menu></menubar></ui>",
4505 gtk_action_group_add_actions (action_group, debug_entries, G_N_ELEMENTS (debug_entries), window);
4509 for ( i=0; i < G_N_ELEMENTS (entries); i++ ) {
4510 if ( entries[i].callback )
4511 toolbar_action_entry_register ( window->viking_vtb, &entries[i] );
4514 if ( G_N_ELEMENTS (toggle_entries) != G_N_ELEMENTS (toggle_entries_toolbar_cb) ) {
4515 g_print ( "Broken entries definitions\n" );
4518 for ( i=0; i < G_N_ELEMENTS (toggle_entries); i++ ) {
4519 if ( toggle_entries_toolbar_cb[i] )
4520 toolbar_action_toggle_entry_register ( window->viking_vtb, &toggle_entries[i], toggle_entries_toolbar_cb[i] );
4523 for ( i=0; i < G_N_ELEMENTS (mode_entries); i++ ) {
4524 toolbar_action_mode_entry_register ( window->viking_vtb, &mode_entries[i] );
4527 // Use this to see if GPSBabel is available:
4528 if ( a_babel_available () ) {
4529 // If going to add more entries then might be worth creating a menu_gpsbabel.xml.h file
4530 if ( gtk_ui_manager_add_ui_from_string ( uim,
4531 "<ui><menubar name='MainMenu'><menu action='File'><menu action='Export'><menuitem action='ExportKML'/></menu></menu></menubar></ui>",
4533 gtk_action_group_add_actions ( action_group, entries_gpsbabel, G_N_ELEMENTS (entries_gpsbabel), window );
4536 // GeoJSON import capability
4537 if ( g_find_program_in_path ( a_geojson_program_import() ) ) {
4538 if ( gtk_ui_manager_add_ui_from_string ( uim,
4539 "<ui><menubar name='MainMenu'><menu action='File'><menu action='Acquire'><menuitem action='AcquireGeoJSON'/></menu></menu></menubar></ui>",
4541 gtk_action_group_add_actions ( action_group, entries_geojson, G_N_ELEMENTS (entries_geojson), window );
4544 icon_factory = gtk_icon_factory_new ();
4545 gtk_icon_factory_add_default (icon_factory);
4547 register_vik_icons(icon_factory);
4549 // Copy the tool RadioActionEntries out of the main Window structure into an extending array 'tools'
4550 // so that it can be applied to the UI in one action group add function call below
4552 for (i=0; i<window->vt->n_tools; i++) {
4553 tools = g_renew(GtkRadioActionEntry, tools, ntools+1);
4554 radio = &tools[ntools];
4556 *radio = window->vt->tools[i].ti.radioActionEntry;
4557 radio->value = ntools;
4560 for (i=0; i<VIK_LAYER_NUM_TYPES; i++) {
4561 GtkActionEntry action;
4562 gtk_ui_manager_add_ui(uim, mid, "/ui/MainMenu/Layers/",
4563 vik_layer_get_interface(i)->name,
4564 vik_layer_get_interface(i)->name,
4565 GTK_UI_MANAGER_MENUITEM, FALSE);
4567 icon_set = gtk_icon_set_new_from_pixbuf (gdk_pixbuf_from_pixdata (vik_layer_get_interface(i)->icon, FALSE, NULL ));
4568 gtk_icon_factory_add (icon_factory, vik_layer_get_interface(i)->name, icon_set);
4569 gtk_icon_set_unref (icon_set);
4571 action.name = vik_layer_get_interface(i)->name;
4572 action.stock_id = vik_layer_get_interface(i)->name;
4573 action.label = g_strdup_printf( _("New _%s Layer"), vik_layer_get_interface(i)->name);
4574 action.accelerator = vik_layer_get_interface(i)->accelerator;
4575 action.tooltip = NULL;
4576 action.callback = (GCallback)menu_addlayer_cb;
4577 gtk_action_group_add_actions(action_group, &action, 1, window);
4579 g_free ( (gchar*)action.label );
4581 if ( vik_layer_get_interface(i)->tools_count ) {
4582 gtk_ui_manager_add_ui(uim, mid, "/ui/MainMenu/Tools/", vik_layer_get_interface(i)->name, NULL, GTK_UI_MANAGER_SEPARATOR, FALSE);
4585 // Further tool copying for to apply to the UI, also apply menu UI setup
4586 for ( j = 0; j < vik_layer_get_interface(i)->tools_count; j++ ) {
4587 tools = g_renew(GtkRadioActionEntry, tools, ntools+1);
4588 radio = &tools[ntools];
4591 gtk_ui_manager_add_ui(uim, mid, "/ui/MainMenu/Tools",
4592 vik_layer_get_interface(i)->tools[j].radioActionEntry.label,
4593 vik_layer_get_interface(i)->tools[j].radioActionEntry.name,
4594 GTK_UI_MANAGER_MENUITEM, FALSE);
4596 toolbox_add_tool(window->vt, &(vik_layer_get_interface(i)->tools[j]), i);
4597 toolbar_action_tool_entry_register ( window->viking_vtb, &(vik_layer_get_interface(i)->tools[j].radioActionEntry) );
4599 *radio = vik_layer_get_interface(i)->tools[j].radioActionEntry;
4600 // Overwrite with actual number to use
4601 radio->value = ntools;
4604 GtkActionEntry action_dl;
4605 gchar *layername = g_strdup_printf ( "Layer%s", vik_layer_get_interface(i)->fixed_layer_name );
4606 gtk_ui_manager_add_ui(uim, mid, "/ui/MainMenu/Edit/LayerDefaults",
4607 vik_layer_get_interface(i)->name,
4609 GTK_UI_MANAGER_MENUITEM, FALSE);
4612 // For default layers use action names of the form 'Layer<LayerName>'
4613 // This is to avoid clashing with just the layer name used above for the tool actions
4614 action_dl.name = g_strconcat("Layer", vik_layer_get_interface(i)->fixed_layer_name, NULL);
4615 action_dl.stock_id = NULL;
4616 action_dl.label = g_strconcat("_", vik_layer_get_interface(i)->name, "...", NULL); // Prepend marker for keyboard accelerator
4617 action_dl.accelerator = NULL;
4618 action_dl.tooltip = NULL;
4619 action_dl.callback = (GCallback)layer_defaults_cb;
4620 gtk_action_group_add_actions(action_group, &action_dl, 1, window);
4621 g_free ( (gchar*)action_dl.name );
4622 g_free ( (gchar*)action_dl.label );
4624 g_object_unref (icon_factory);
4626 gtk_action_group_add_radio_actions(action_group, tools, ntools, 0, (GCallback)menu_cb, window);
4629 gtk_ui_manager_insert_action_group (uim, action_group, 0);
4631 for (i=0; i<VIK_LAYER_NUM_TYPES; i++) {
4632 for ( j = 0; j < vik_layer_get_interface(i)->tools_count; j++ ) {
4633 GtkAction *action = gtk_action_group_get_action(action_group,
4634 vik_layer_get_interface(i)->tools[j].radioActionEntry.name);
4635 g_object_set(action, "sensitive", FALSE, NULL);
4639 // This is done last so we don't need to track the value of mid anymore
4640 vik_ext_tools_add_action_items ( window, window->uim, action_group, mid );
4642 window->action_group = action_group;
4644 accel_group = gtk_ui_manager_get_accel_group (uim);
4645 gtk_window_add_accel_group (GTK_WINDOW (window), accel_group);
4646 gtk_ui_manager_ensure_update (uim);
4648 setup_recent_files(window);
4652 // TODO - add method to add tool icons defined from outside this file
4653 // and remove the reverse dependency on icon definition from this file
4655 const GdkPixdata *data;
4658 { &mover_22_pixbuf, "vik-icon-pan" },
4659 { &zoom_18_pixbuf, "vik-icon-zoom" },
4660 { &ruler_18_pixbuf, "vik-icon-ruler" },
4661 { &select_18_pixbuf, "vik-icon-select" },
4662 { &vik_new_route_18_pixbuf, "vik-icon-Create Route" },
4663 { &route_finder_18_pixbuf, "vik-icon-Route Finder" },
4664 { &demdl_18_pixbuf, "vik-icon-DEM Download" },
4665 { &showpic_18_pixbuf, "vik-icon-Show Picture" },
4666 { &addtr_18_pixbuf, "vik-icon-Create Track" },
4667 { &edtr_18_pixbuf, "vik-icon-Edit Trackpoint" },
4668 { &addwp_18_pixbuf, "vik-icon-Create Waypoint" },
4669 { &edwp_18_pixbuf, "vik-icon-Edit Waypoint" },
4670 { &geozoom_18_pixbuf, "vik-icon-Georef Zoom Tool" },
4671 { &geomove_18_pixbuf, "vik-icon-Georef Move Map" },
4672 { &mapdl_18_pixbuf, "vik-icon-Maps Download" },
4675 static gint n_stock_icons = G_N_ELEMENTS (stock_icons);
4678 register_vik_icons (GtkIconFactory *icon_factory)
4680 GtkIconSet *icon_set;
4683 for (i = 0; i < n_stock_icons; i++) {
4684 icon_set = gtk_icon_set_new_from_pixbuf (gdk_pixbuf_from_pixdata (
4685 stock_icons[i].data, FALSE, NULL ));
4686 gtk_icon_factory_add (icon_factory, stock_icons[i].stock_id, icon_set);
4687 gtk_icon_set_unref (icon_set);
4691 gpointer vik_window_get_selected_trw_layer ( VikWindow *vw )
4693 return vw->selected_vtl;
4696 void vik_window_set_selected_trw_layer ( VikWindow *vw, gpointer vtl )
4698 vw->selected_vtl = vtl;
4699 vw->containing_vtl = vtl;
4701 vw->selected_track = NULL;
4702 vw->selected_tracks = NULL;
4703 vw->selected_waypoint = NULL;
4704 vw->selected_waypoints = NULL;
4705 // Set highlight thickness
4706 vik_viewport_set_highlight_thickness ( vw->viking_vvp, vik_trw_layer_get_property_tracks_line_thickness (vw->containing_vtl) );
4709 GHashTable *vik_window_get_selected_tracks ( VikWindow *vw )
4711 return vw->selected_tracks;
4714 void vik_window_set_selected_tracks ( VikWindow *vw, GHashTable *ght, gpointer vtl )
4716 vw->selected_tracks = ght;
4717 vw->containing_vtl = vtl;
4719 vw->selected_vtl = NULL;
4720 vw->selected_track = NULL;
4721 vw->selected_waypoint = NULL;
4722 vw->selected_waypoints = NULL;
4723 // Set highlight thickness
4724 vik_viewport_set_highlight_thickness ( vw->viking_vvp, vik_trw_layer_get_property_tracks_line_thickness (vw->containing_vtl) );
4727 gpointer vik_window_get_selected_track ( VikWindow *vw )
4729 return vw->selected_track;
4732 void vik_window_set_selected_track ( VikWindow *vw, gpointer *vt, gpointer vtl )
4734 vw->selected_track = vt;
4735 vw->containing_vtl = vtl;
4737 vw->selected_vtl = NULL;
4738 vw->selected_tracks = NULL;
4739 vw->selected_waypoint = NULL;
4740 vw->selected_waypoints = NULL;
4741 // Set highlight thickness
4742 vik_viewport_set_highlight_thickness ( vw->viking_vvp, vik_trw_layer_get_property_tracks_line_thickness (vw->containing_vtl) );
4745 GHashTable *vik_window_get_selected_waypoints ( VikWindow *vw )
4747 return vw->selected_waypoints;
4750 void vik_window_set_selected_waypoints ( VikWindow *vw, GHashTable *ght, gpointer vtl )
4752 vw->selected_waypoints = ght;
4753 vw->containing_vtl = vtl;
4755 vw->selected_vtl = NULL;
4756 vw->selected_track = NULL;
4757 vw->selected_tracks = NULL;
4758 vw->selected_waypoint = NULL;
4761 gpointer vik_window_get_selected_waypoint ( VikWindow *vw )
4763 return vw->selected_waypoint;
4766 void vik_window_set_selected_waypoint ( VikWindow *vw, gpointer *vwp, gpointer vtl )
4768 vw->selected_waypoint = vwp;
4769 vw->containing_vtl = vtl;
4771 vw->selected_vtl = NULL;
4772 vw->selected_track = NULL;
4773 vw->selected_tracks = NULL;
4774 vw->selected_waypoints = NULL;
4777 gboolean vik_window_clear_highlight ( VikWindow *vw )
4779 gboolean need_redraw = FALSE;
4780 if ( vw->selected_vtl != NULL ) {
4781 vw->selected_vtl = NULL;
4784 if ( vw->selected_track != NULL ) {
4785 vw->selected_track = NULL;
4788 if ( vw->selected_tracks != NULL ) {
4789 vw->selected_tracks = NULL;
4792 if ( vw->selected_waypoint != NULL ) {
4793 vw->selected_waypoint = NULL;
4796 if ( vw->selected_waypoints != NULL ) {
4797 vw->selected_waypoints = NULL;
4804 * May return NULL if the window no longer exists
4806 GThread *vik_window_get_thread ( VikWindow *vw )