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 ( BACKGROUND_POOL_REMOTE,
484 _("Determining location"),
485 (vik_thr_func) determine_location_thread,
494 static void open_window ( VikWindow *vw, GSList *files )
496 gboolean change_fn = (g_slist_length(files) == 1); /* only change fn if one file */
497 GSList *cur_file = files;
499 // Only open a new window if a viking file
500 gchar *file_name = cur_file->data;
501 if (vw != NULL && vw->filename && check_file_magic_vik ( file_name ) ) {
502 VikWindow *newvw = vik_window_new_window ();
504 vik_window_open_file ( newvw, file_name, TRUE );
507 vik_window_open_file ( vw, file_name, change_fn );
510 cur_file = g_slist_next (cur_file);
512 g_slist_free (files);
516 void vik_window_selected_layer(VikWindow *vw, VikLayer *vl)
518 int i, j, tool_count;
519 VikLayerInterface *layer_interface;
521 if (!vw->action_group) return;
523 for (i=0; i<VIK_LAYER_NUM_TYPES; i++) {
525 layer_interface = vik_layer_get_interface(i);
526 tool_count = layer_interface->tools_count;
528 for (j = 0; j < tool_count; j++) {
529 action = gtk_action_group_get_action(vw->action_group,
530 layer_interface->tools[j].radioActionEntry.name);
531 g_object_set(action, "sensitive", i == vl->type, NULL);
532 toolbar_action_set_sensitive ( vw->viking_vtb, vik_layer_get_interface(i)->tools[j].radioActionEntry.name, i == vl->type );
537 static void window_finalize ( GObject *gob )
539 VikWindow *vw = VIK_WINDOW(gob);
540 g_return_if_fail ( vw != NULL );
542 a_background_remove_window ( vw );
544 window_list = g_slist_remove ( window_list, vw );
546 gdk_cursor_unref ( vw->busy_cursor );
548 for (tt = 0; tt < vw->vt->n_tools; tt++ )
549 if ( vw->vt->tools[tt].ti.destroy )
550 vw->vt->tools[tt].ti.destroy ( vw->vt->tools[tt].state );
551 g_free ( vw->vt->tools );
554 vik_toolbar_finalize ( vw->viking_vtb );
556 G_OBJECT_CLASS(parent_class)->finalize(gob);
560 static void vik_window_class_init ( VikWindowClass *klass )
563 GObjectClass *object_class;
565 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);
566 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);
568 object_class = G_OBJECT_CLASS (klass);
570 object_class->finalize = window_finalize;
572 parent_class = g_type_class_peek_parent (klass);
576 static void zoom_changed (GtkMenuShell *menushell,
579 VikWindow *vw = VIK_WINDOW (user_data);
581 GtkWidget *aw = gtk_menu_get_active ( GTK_MENU (menushell) );
582 gint active = GPOINTER_TO_INT(g_object_get_data ( G_OBJECT (aw), "position" ));
584 gdouble zoom_request = pow (2, active-5 );
586 // But has it really changed?
587 gdouble current_zoom = vik_viewport_get_zoom ( vw->viking_vvp );
588 if ( current_zoom != 0.0 && zoom_request != current_zoom ) {
589 vik_viewport_set_zoom ( vw->viking_vvp, zoom_request );
590 // Force drawing update
596 * @mpp: The initial zoom level
598 static GtkWidget *create_zoom_menu_all_levels ( gdouble mpp )
600 GtkWidget *menu = gtk_menu_new ();
601 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" };
604 for (i = 0 ; i < G_N_ELEMENTS(itemLabels) ; i++)
606 GtkWidget *item = gtk_menu_item_new_with_label (itemLabels[i]);
607 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
608 gtk_widget_show (item);
609 g_object_set_data (G_OBJECT (item), "position", GINT_TO_POINTER(i));
612 gint active = 5 + round ( log (mpp) / log (2) );
613 // Ensure value derived from mpp is in bounds of the menu
614 if ( active >= G_N_ELEMENTS(itemLabels) )
615 active = G_N_ELEMENTS(itemLabels) - 1;
618 gtk_menu_set_active ( GTK_MENU(menu), active );
623 static GtkWidget *create_zoom_combo_all_levels ()
625 GtkWidget *combo = vik_combo_box_text_new();
626 vik_combo_box_text_append ( combo, "0.25");
627 vik_combo_box_text_append ( combo, "0.5");
628 vik_combo_box_text_append ( combo, "1");
629 vik_combo_box_text_append ( combo, "2");
630 vik_combo_box_text_append ( combo, "4");
631 vik_combo_box_text_append ( combo, "8");
632 vik_combo_box_text_append ( combo, "16");
633 vik_combo_box_text_append ( combo, "32");
634 vik_combo_box_text_append ( combo, "64");
635 vik_combo_box_text_append ( combo, "128");
636 vik_combo_box_text_append ( combo, "256");
637 vik_combo_box_text_append ( combo, "512");
638 vik_combo_box_text_append ( combo, "1024");
639 vik_combo_box_text_append ( combo, "2048");
640 vik_combo_box_text_append ( combo, "4096");
641 vik_combo_box_text_append ( combo, "8192");
642 vik_combo_box_text_append ( combo, "16384");
643 vik_combo_box_text_append ( combo, "32768");
645 gtk_widget_set_tooltip_text (combo, _("Select zoom level"));
649 static gint zoom_popup_handler (GtkWidget *widget)
653 g_return_val_if_fail (widget != NULL, FALSE);
654 g_return_val_if_fail (GTK_IS_MENU (widget), FALSE);
656 /* The "widget" is the menu that was supplied when
657 * g_signal_connect_swapped() was called.
659 menu = GTK_MENU (widget);
661 gtk_menu_popup (menu, NULL, NULL, NULL, NULL,
662 1, gtk_get_current_event_time());
670 static void drag_data_received_cb ( GtkWidget *widget,
671 GdkDragContext *context,
674 GtkSelectionData *selection_data,
679 gboolean success = FALSE;
681 if ( (selection_data != NULL) && (gtk_selection_data_get_length(selection_data) > 0) ) {
682 switch (target_type) {
684 gchar *str = (gchar*)gtk_selection_data_get_data(selection_data);
685 g_debug ("drag received string:%s \n", str);
687 // Convert string into GSList of individual entries for use with our open signal
688 gchar **entries = g_strsplit(str, "\r\n", 0);
689 GSList *filenames = NULL;
690 gint entry_runner = 0;
691 gchar *entry = entries[entry_runner];
693 if ( g_strcmp0 ( entry, "" ) ) {
694 // Drag+Drop gives URIs. And so in particular, %20 in place of spaces in filenames
695 // thus need to convert the text into a plain string
696 gchar *filename = g_filename_from_uri ( entry, NULL, NULL );
698 filenames = g_slist_append ( filenames, filename );
701 entry = entries[entry_runner];
705 g_signal_emit ( G_OBJECT(VIK_WINDOW_FROM_WIDGET(widget)), window_signals[VW_OPENWINDOW_SIGNAL], 0, filenames );
706 // NB: GSList & contents are freed by main.open_window
715 gtk_drag_finish ( context, success, FALSE, time );
718 static void toolbar_tool_cb ( GtkAction *old, GtkAction *current, gpointer gp )
720 VikWindow *vw = (VikWindow*)gp;
721 GtkAction *action = gtk_action_group_get_action ( vw->action_group, gtk_action_get_name(current) );
723 gtk_action_activate ( action );
726 static void toolbar_reload_cb ( GtkActionGroup *grp, gpointer gp )
728 VikWindow *vw = (VikWindow*)gp;
729 center_changed_cb ( vw );
732 #define VIK_SETTINGS_WIN_MAX "window_maximized"
733 #define VIK_SETTINGS_WIN_FULLSCREEN "window_fullscreen"
734 #define VIK_SETTINGS_WIN_WIDTH "window_width"
735 #define VIK_SETTINGS_WIN_HEIGHT "window_height"
736 #define VIK_SETTINGS_WIN_PANE_POSITION "window_horizontal_pane_position"
737 #define VIK_SETTINGS_WIN_SAVE_IMAGE_WIDTH "window_save_image_width"
738 #define VIK_SETTINGS_WIN_SAVE_IMAGE_HEIGHT "window_save_image_height"
739 #define VIK_SETTINGS_WIN_SAVE_IMAGE_PNG "window_save_image_as_png"
740 #define VIK_SETTINGS_WIN_COPY_CENTRE_FULL_FORMAT "window_copy_centre_full_format"
742 #define VIKING_ACCELERATOR_KEY_FILE "keys.rc"
744 static void vik_window_init ( VikWindow *vw )
746 vw->action_group = NULL;
748 vw->viking_vvp = vik_viewport_new();
749 vw->viking_vlp = vik_layers_panel_new();
750 vik_layers_panel_set_viewport ( vw->viking_vlp, vw->viking_vvp );
751 vw->viking_vs = vik_statusbar_new();
753 vw->vt = toolbox_create(vw);
754 vw->viking_vtb = vik_toolbar_new ();
755 window_create_ui(vw);
756 window_set_filename (vw, NULL);
758 vw->busy_cursor = gdk_cursor_new ( GDK_WATCH );
761 vw->loaded_type = LOAD_TYPE_READ_FAILURE; //AKA none
762 vw->modified = FALSE;
763 vw->only_updating_coord_mode_ui = FALSE;
765 vw->select_move = FALSE;
766 vw->pan_move = FALSE;
767 vw->pan_x = vw->pan_y = -1;
768 vw->single_click_pending = FALSE;
770 gint draw_image_width;
771 if ( a_settings_get_integer ( VIK_SETTINGS_WIN_SAVE_IMAGE_WIDTH, &draw_image_width ) )
772 vw->draw_image_width = draw_image_width;
774 vw->draw_image_width = DRAW_IMAGE_DEFAULT_WIDTH;
775 gint draw_image_height;
776 if ( a_settings_get_integer ( VIK_SETTINGS_WIN_SAVE_IMAGE_HEIGHT, &draw_image_height ) )
777 vw->draw_image_height = draw_image_height;
779 vw->draw_image_height = DRAW_IMAGE_DEFAULT_HEIGHT;
780 gboolean draw_image_save_as_png;
781 if ( a_settings_get_boolean ( VIK_SETTINGS_WIN_SAVE_IMAGE_PNG, &draw_image_save_as_png ) )
782 vw->draw_image_save_as_png = draw_image_save_as_png;
784 vw->draw_image_save_as_png = DRAW_IMAGE_DEFAULT_SAVE_AS_PNG;
786 vw->main_vbox = gtk_vbox_new(FALSE, 1);
787 gtk_container_add (GTK_CONTAINER (vw), vw->main_vbox);
788 vw->menu_hbox = gtk_hbox_new(FALSE, 1);
789 GtkWidget *menu_bar = gtk_ui_manager_get_widget (vw->uim, "/MainMenu");
790 gtk_box_pack_start (GTK_BOX(vw->menu_hbox), menu_bar, FALSE, TRUE, 0);
791 gtk_box_pack_start (GTK_BOX(vw->main_vbox), vw->menu_hbox, FALSE, TRUE, 0);
793 toolbar_init(vw->viking_vtb,
799 (gpointer)vw); // This auto packs toolbar into the vbox
800 // Must be performed post toolbar init
802 for (i=0; i<VIK_LAYER_NUM_TYPES; i++) {
803 for ( j = 0; j < vik_layer_get_interface(i)->tools_count; j++ ) {
804 toolbar_action_set_sensitive ( vw->viking_vtb, vik_layer_get_interface(i)->tools[j].radioActionEntry.name, FALSE );
808 vik_ext_tool_datasources_add_menu_items ( vw, vw->uim );
810 GtkWidget * zoom_levels = gtk_ui_manager_get_widget (vw->uim, "/MainMenu/View/SetZoom");
811 GtkWidget * zoom_levels_menu = create_zoom_menu_all_levels ( vik_viewport_get_zoom(vw->viking_vvp) );
812 gtk_menu_item_set_submenu (GTK_MENU_ITEM (zoom_levels), zoom_levels_menu);
813 g_signal_connect ( G_OBJECT(zoom_levels_menu), "selection-done", G_CALLBACK(zoom_changed), vw);
814 g_signal_connect_swapped ( G_OBJECT(vw->viking_vs), "clicked", G_CALLBACK(zoom_popup_handler), zoom_levels_menu );
816 g_signal_connect (G_OBJECT (vw), "delete_event", G_CALLBACK (delete_event), NULL);
819 g_signal_connect_swapped (G_OBJECT(vw->viking_vvp), "updated_center", G_CALLBACK(center_changed_cb), vw);
821 g_signal_connect_swapped (G_OBJECT(vw->viking_vvp), "expose_event", G_CALLBACK(draw_sync), vw);
822 g_signal_connect_swapped (G_OBJECT(vw->viking_vvp), "configure_event", G_CALLBACK(window_configure_event), vw);
823 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 );
824 g_signal_connect_swapped (G_OBJECT(vw->viking_vvp), "scroll_event", G_CALLBACK(draw_scroll), vw);
825 g_signal_connect_swapped (G_OBJECT(vw->viking_vvp), "button_press_event", G_CALLBACK(draw_click), vw);
826 g_signal_connect_swapped (G_OBJECT(vw->viking_vvp), "button_release_event", G_CALLBACK(draw_release), vw);
827 g_signal_connect_swapped (G_OBJECT(vw->viking_vvp), "motion_notify_event", G_CALLBACK(draw_mouse_motion), vw);
829 g_signal_connect_swapped (G_OBJECT(vw->viking_vlp), "update", G_CALLBACK(draw_update), vw);
830 g_signal_connect_swapped (G_OBJECT(vw->viking_vlp), "delete_layer", G_CALLBACK(vik_window_clear_highlight), vw);
832 // Allow key presses to be processed anywhere
833 g_signal_connect_swapped (G_OBJECT (vw), "key_press_event", G_CALLBACK (key_press_event), vw);
835 // Set initial button sensitivity
836 center_changed_cb ( vw );
838 vw->hpaned = gtk_hpaned_new ();
839 gtk_paned_pack1 ( GTK_PANED(vw->hpaned), GTK_WIDGET (vw->viking_vlp), FALSE, FALSE );
840 gtk_paned_pack2 ( GTK_PANED(vw->hpaned), GTK_WIDGET (vw->viking_vvp), TRUE, TRUE );
842 /* This packs the button into the window (a gtk container). */
843 gtk_box_pack_start (GTK_BOX(vw->main_vbox), vw->hpaned, TRUE, TRUE, 0);
845 gtk_box_pack_end (GTK_BOX(vw->main_vbox), GTK_WIDGET(vw->viking_vs), FALSE, TRUE, 0);
847 a_background_add_window ( vw );
849 window_list = g_slist_prepend ( window_list, vw);
851 gint height = VIKING_WINDOW_HEIGHT;
852 gint width = VIKING_WINDOW_WIDTH;
854 if ( a_vik_get_restore_window_state() ) {
855 if ( a_settings_get_integer ( VIK_SETTINGS_WIN_HEIGHT, &height ) ) {
856 // Enforce a basic minimum size
861 // No setting - so use default
862 height = VIKING_WINDOW_HEIGHT;
864 if ( a_settings_get_integer ( VIK_SETTINGS_WIN_WIDTH, &width ) ) {
865 // Enforce a basic minimum size
870 // No setting - so use default
871 width = VIKING_WINDOW_WIDTH;
874 if ( a_settings_get_boolean ( VIK_SETTINGS_WIN_MAX, &maxed ) )
876 gtk_window_maximize ( GTK_WINDOW(vw) );
879 if ( a_settings_get_boolean ( VIK_SETTINGS_WIN_FULLSCREEN, &full ) ) {
881 vw->show_full_screen = TRUE;
882 gtk_window_fullscreen ( GTK_WINDOW(vw) );
883 GtkWidget *check_box = gtk_ui_manager_get_widget ( vw->uim, "/ui/MainMenu/View/FullScreen" );
885 gtk_check_menu_item_set_active ( GTK_CHECK_MENU_ITEM(check_box), TRUE );
889 gint position = -1; // Let GTK determine default positioning
890 if ( !a_settings_get_integer ( VIK_SETTINGS_WIN_PANE_POSITION, &position ) ) {
893 gtk_paned_set_position ( GTK_PANED(vw->hpaned), position );
896 gtk_window_set_default_size ( GTK_WINDOW(vw), width, height );
900 vw->save_img_dia = NULL;
901 vw->save_img_dir_dia = NULL;
903 vw->show_side_panel = TRUE;
904 vw->show_statusbar = TRUE;
905 vw->show_toolbar = TRUE;
906 vw->show_main_menu = TRUE;
908 // Only accept Drag and Drop of files onto the viewport
909 gtk_drag_dest_set ( GTK_WIDGET(vw->viking_vvp), GTK_DEST_DEFAULT_ALL, NULL, 0, GDK_ACTION_COPY );
910 gtk_drag_dest_add_uri_targets ( GTK_WIDGET(vw->viking_vvp) );
911 g_signal_connect ( GTK_WIDGET(vw->viking_vvp), "drag-data-received", G_CALLBACK(drag_data_received_cb), NULL );
913 // Store the thread value so comparisons can be made to determine the gdk update method
914 // Hopefully we are storing the main thread value here :)
915 // [ATM any window initialization is always be performed by the main thread]
916 vw->thread = g_thread_self();
918 // Set the default tool + mode
919 gtk_action_activate ( gtk_action_group_get_action ( vw->action_group, "Pan" ) );
920 gtk_action_activate ( gtk_action_group_get_action ( vw->action_group, "ModeMercator" ) );
922 gchar *accel_file_name = g_build_filename ( a_get_viking_dir(), VIKING_ACCELERATOR_KEY_FILE, NULL );
923 gtk_accel_map_load ( accel_file_name );
924 g_free ( accel_file_name );
927 static VikWindow *window_new ()
929 return VIK_WINDOW ( g_object_new ( VIK_WINDOW_TYPE, NULL ) );
933 * Update the displayed map
934 * Only update the top most visible map layer
935 * ATM this assumes (as per defaults) the top most map has full alpha setting
936 * such that other other maps even though they may be active will not be seen
937 * It's more complicated to work out which maps are actually visible due to alpha settings
938 * and overkill for this simple refresh method.
940 static void simple_map_update ( VikWindow *vw, gboolean only_new )
942 // Find the most relevent single map layer to operate on
943 VikLayer *vl = vik_aggregate_layer_get_top_visible_layer_of_type (vik_layers_panel_get_top_layer(vw->viking_vlp), VIK_LAYER_MAPS);
945 vik_maps_layer_download ( VIK_MAPS_LAYER(vl), vw->viking_vvp, only_new );
949 * This is the global key press handler
950 * Global shortcuts are available at any time and hence are not restricted to when a certain tool is enabled
952 static gboolean key_press_event( VikWindow *vw, GdkEventKey *event, gpointer data )
954 // The keys handled here are not in the menuing system for a couple of reasons:
955 // . Keeps the menu size compact (alebit at expense of discoverably)
956 // . Allows differing key bindings to perform the same actions
958 // First decide if key events are related to the maps layer
959 gboolean map_download = FALSE;
960 gboolean map_download_only_new = TRUE; // Only new or reload
962 GdkModifierType modifiers = gtk_accelerator_get_default_mod_mask();
964 // Standard 'Refresh' keys: F5 or Ctrl+r
965 // Note 'F5' is actually handled via draw_refresh_cb() later on
966 // (not 'R' it's 'r' notice the case difference!!)
967 if ( event->keyval == GDK_r && (event->state & modifiers) == GDK_CONTROL_MASK ) {
969 map_download_only_new = TRUE;
971 // Full cache reload with Ctrl+F5 or Ctrl+Shift+r [This is not in the menu system]
972 // Note the use of uppercase R here since shift key has been pressed
973 else if ( (event->keyval == GDK_F5 && (event->state & modifiers) == GDK_CONTROL_MASK ) ||
974 ( event->keyval == GDK_R && (event->state & modifiers) == (GDK_CONTROL_MASK + GDK_SHIFT_MASK) ) ) {
976 map_download_only_new = FALSE;
978 // Standard Ctrl+KP+ / Ctrl+KP- to zoom in/out respectively
979 else if ( event->keyval == GDK_KEY_KP_Add && (event->state & modifiers) == GDK_CONTROL_MASK ) {
980 vik_viewport_zoom_in ( vw->viking_vvp );
982 return TRUE; // handled keypress
984 else if ( event->keyval == GDK_KEY_KP_Subtract && (event->state & modifiers) == GDK_CONTROL_MASK ) {
985 vik_viewport_zoom_out ( vw->viking_vvp );
987 return TRUE; // handled keypress
990 if ( map_download ) {
991 simple_map_update ( vw, map_download_only_new );
992 return TRUE; // handled keypress
995 VikLayer *vl = vik_layers_panel_get_selected ( vw->viking_vlp );
996 if (vl && vw->vt->active_tool != -1 && vw->vt->tools[vw->vt->active_tool].ti.key_press ) {
997 gint ltype = vw->vt->tools[vw->vt->active_tool].layer_type;
998 if ( vl && ltype == vl->type )
999 return vw->vt->tools[vw->vt->active_tool].ti.key_press(vl, event, vw->vt->tools[vw->vt->active_tool].state);
1002 // Ensure called only on window tools (i.e. not on any of the Layer tools since the layer is NULL)
1003 if ( vw->current_tool < TOOL_LAYER ) {
1004 // No layer - but enable window tool keypress processing - these should be able to handle a NULL layer
1005 if ( vw->vt->tools[vw->vt->active_tool].ti.key_press ) {
1006 return vw->vt->tools[vw->vt->active_tool].ti.key_press ( vl, event, vw->vt->tools[vw->vt->active_tool].state );
1010 /* Restore Main Menu via Escape key if the user has hidden it */
1011 /* This key is more likely to be used as they may not remember the function key */
1012 if ( event->keyval == GDK_Escape ) {
1013 GtkWidget *check_box = gtk_ui_manager_get_widget ( vw->uim, "/ui/MainMenu/View/SetShow/ViewMainMenu" );
1015 gboolean state = gtk_check_menu_item_get_active ( GTK_CHECK_MENU_ITEM(check_box) );
1017 gtk_widget_show ( gtk_ui_manager_get_widget ( vw->uim, "/ui/MainMenu" ) );
1018 gtk_check_menu_item_set_active ( GTK_CHECK_MENU_ITEM(check_box), TRUE );
1019 return TRUE; /* handled keypress */
1024 return FALSE; /* don't handle the keypress */
1027 static gboolean delete_event( VikWindow *vw )
1029 #ifdef VIKING_PROMPT_IF_MODIFIED
1036 dia = GTK_DIALOG ( gtk_message_dialog_new ( GTK_WINDOW(vw), GTK_DIALOG_DESTROY_WITH_PARENT, GTK_MESSAGE_QUESTION, GTK_BUTTONS_NONE,
1037 _("Do you want to save the changes you made to the document \"%s\"?\n"
1039 "Your changes will be lost if you don't save them."),
1040 window_get_filename ( vw ) ) );
1041 gtk_dialog_add_buttons ( dia, _("Don't Save"), GTK_RESPONSE_NO, GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, GTK_STOCK_SAVE, GTK_RESPONSE_YES, NULL );
1042 switch ( gtk_dialog_run ( dia ) )
1044 case GTK_RESPONSE_NO: gtk_widget_destroy ( GTK_WIDGET(dia) ); return FALSE;
1045 case GTK_RESPONSE_CANCEL: gtk_widget_destroy ( GTK_WIDGET(dia) ); return TRUE;
1046 default: gtk_widget_destroy ( GTK_WIDGET(dia) ); return ! save_file(NULL, vw);
1050 if ( window_count == 1 ) {
1051 // On the final window close - save latest state - if it's wanted...
1052 if ( a_vik_get_restore_window_state() ) {
1053 gint state = gdk_window_get_state ( GTK_WIDGET(vw)->window );
1054 gboolean state_max = state & GDK_WINDOW_STATE_MAXIMIZED;
1055 a_settings_set_boolean ( VIK_SETTINGS_WIN_MAX, state_max );
1057 gboolean state_fullscreen = state & GDK_WINDOW_STATE_FULLSCREEN;
1058 a_settings_set_boolean ( VIK_SETTINGS_WIN_FULLSCREEN, state_fullscreen );
1060 a_settings_set_boolean ( VIK_SETTINGS_WIN_SIDEPANEL, GTK_WIDGET_VISIBLE (GTK_WIDGET(vw->viking_vlp)) );
1062 a_settings_set_boolean ( VIK_SETTINGS_WIN_STATUSBAR, GTK_WIDGET_VISIBLE (GTK_WIDGET(vw->viking_vs)) );
1064 a_settings_set_boolean ( VIK_SETTINGS_WIN_TOOLBAR, GTK_WIDGET_VISIBLE (toolbar_get_widget(vw->viking_vtb)) );
1066 // If supersized - no need to save the enlarged width+height values
1067 if ( ! (state_fullscreen || state_max) ) {
1069 gtk_window_get_size ( GTK_WINDOW (vw), &width, &height );
1070 a_settings_set_integer ( VIK_SETTINGS_WIN_WIDTH, width );
1071 a_settings_set_integer ( VIK_SETTINGS_WIN_HEIGHT, height );
1074 a_settings_set_integer ( VIK_SETTINGS_WIN_PANE_POSITION, gtk_paned_get_position (GTK_PANED(vw->hpaned)) );
1077 a_settings_set_integer ( VIK_SETTINGS_WIN_SAVE_IMAGE_WIDTH, vw->draw_image_width );
1078 a_settings_set_integer ( VIK_SETTINGS_WIN_SAVE_IMAGE_HEIGHT, vw->draw_image_height );
1079 a_settings_set_boolean ( VIK_SETTINGS_WIN_SAVE_IMAGE_PNG, vw->draw_image_save_as_png );
1081 gchar *accel_file_name = g_build_filename ( a_get_viking_dir(), VIKING_ACCELERATOR_KEY_FILE, NULL );
1082 gtk_accel_map_save ( accel_file_name );
1083 g_free ( accel_file_name );
1090 static void newwindow_cb ( GtkAction *a, VikWindow *vw )
1092 g_signal_emit ( G_OBJECT(vw), window_signals[VW_NEWWINDOW_SIGNAL], 0 );
1095 static void draw_update ( VikWindow *vw )
1101 static void draw_sync ( VikWindow *vw )
1103 vik_viewport_sync(vw->viking_vvp);
1108 * Split the status update, as sometimes only need to update the tool part
1109 * also on initialization the zoom related stuff is not ready to be used
1111 static void draw_status_tool ( VikWindow *vw )
1113 if ( vw->current_tool == TOOL_LAYER )
1114 // Use tooltip rather than the internal name as the tooltip is i8n
1115 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 );
1117 vik_statusbar_set_message ( vw->viking_vs, VIK_STATUSBAR_TOOL, _(tool_names[vw->current_tool]) );
1120 static void draw_status ( VikWindow *vw )
1122 static gchar zoom_level[22];
1123 gdouble xmpp = vik_viewport_get_xmpp (vw->viking_vvp);
1124 gdouble ympp = vik_viewport_get_ympp(vw->viking_vvp);
1125 gchar *unit = vik_viewport_get_coord_mode(vw->viking_vvp) == VIK_COORD_UTM ? _("mpp") : _("pixelfact");
1127 g_snprintf ( zoom_level, 22, "%.3f/%.3f %s", xmpp, ympp, unit );
1129 if ( (int)xmpp - xmpp < 0.0 )
1130 g_snprintf ( zoom_level, 22, "%.3f %s", xmpp, unit );
1132 /* xmpp should be a whole number so don't show useless .000 bit */
1133 g_snprintf ( zoom_level, 22, "%d %s", (int)xmpp, unit );
1135 vik_statusbar_set_message ( vw->viking_vs, VIK_STATUSBAR_ZOOM, zoom_level );
1137 draw_status_tool ( vw );
1140 void vik_window_set_redraw_trigger(VikLayer *vl)
1142 VikWindow *vw = VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vl));
1147 static void window_configure_event ( VikWindow *vw )
1149 static int first = 1;
1152 // This is a hack to set the cursor corresponding to the first tool
1153 // FIXME find the correct way to initialize both tool and its cursor
1155 vw->viewport_cursor = (GdkCursor *)toolbox_get_cursor(vw->vt, "Pan");
1156 /* We set cursor, even if it is NULL: it resets to default */
1157 gdk_window_set_cursor ( gtk_widget_get_window(GTK_WIDGET(vw->viking_vvp)), vw->viewport_cursor );
1161 static void draw_redraw ( VikWindow *vw )
1163 VikCoord old_center = vw->trigger_center;
1164 vw->trigger_center = *(vik_viewport_get_center(vw->viking_vvp));
1165 VikLayer *new_trigger = vw->trigger;
1167 VikLayer *old_trigger = VIK_LAYER(vik_viewport_get_trigger(vw->viking_vvp));
1169 if ( ! new_trigger )
1170 ; /* do nothing -- have to redraw everything. */
1171 else if ( (old_trigger != new_trigger) || !vik_coord_equals(&old_center, &vw->trigger_center) || (new_trigger->type == VIK_LAYER_AGGREGATE) )
1172 vik_viewport_set_trigger ( vw->viking_vvp, new_trigger ); /* todo: set to half_drawn mode if new trigger is above old */
1174 vik_viewport_set_half_drawn ( vw->viking_vvp, TRUE );
1177 vik_viewport_clear ( vw->viking_vvp);
1178 // Main layer drawing
1179 vik_layers_panel_draw_all ( vw->viking_vlp );
1180 // Draw highlight (possibly again but ensures it is on top - especially for when tracks overlap)
1181 if ( vik_viewport_get_draw_highlight (vw->viking_vvp) ) {
1182 if ( vw->containing_vtl && (vw->selected_tracks || vw->selected_waypoints ) ) {
1183 vik_trw_layer_draw_highlight_items ( vw->containing_vtl, vw->selected_tracks, vw->selected_waypoints, vw->viking_vvp );
1185 else if ( vw->containing_vtl && (vw->selected_track || vw->selected_waypoint) ) {
1186 vik_trw_layer_draw_highlight_item ( vw->containing_vtl, vw->selected_track, vw->selected_waypoint, vw->viking_vvp );
1188 else if ( vw->selected_vtl ) {
1189 vik_trw_layer_draw_highlight ( vw->selected_vtl, vw->viking_vvp );
1192 // Other viewport decoration items on top if they are enabled/in use
1193 vik_viewport_draw_scale ( vw->viking_vvp );
1194 vik_viewport_draw_copyright ( vw->viking_vvp );
1195 vik_viewport_draw_centermark ( vw->viking_vvp );
1196 vik_viewport_draw_logo ( vw->viking_vvp );
1198 vik_viewport_set_half_drawn ( vw->viking_vvp, FALSE ); /* just in case. */
1201 gboolean draw_buf_done = TRUE;
1203 static gboolean draw_buf(gpointer data)
1205 gpointer *pass_along = data;
1206 gdk_threads_enter();
1207 gdk_draw_drawable (pass_along[0], pass_along[1],
1208 pass_along[2], 0, 0, 0, 0, -1, -1);
1209 draw_buf_done = TRUE;
1210 gdk_threads_leave();
1215 /* Mouse event handlers ************************************************************************/
1217 static void vik_window_pan_click (VikWindow *vw, GdkEventButton *event)
1219 /* set panning origin */
1220 vw->pan_move = FALSE;
1221 vw->pan_x = (gint) event->x;
1222 vw->pan_y = (gint) event->y;
1225 static void draw_click (VikWindow *vw, GdkEventButton *event)
1227 gtk_widget_grab_focus ( GTK_WIDGET(vw->viking_vvp) );
1229 /* middle button pressed. we reserve all middle button and scroll events
1230 * for panning and zooming; tools only get left/right/movement
1232 if ( event->button == 2) {
1233 if ( vw->vt->tools[vw->vt->active_tool].ti.pan_handler )
1234 // Tool still may need to do something (such as disable something)
1235 toolbox_click(vw->vt, event);
1236 vik_window_pan_click ( vw, event );
1239 toolbox_click(vw->vt, event);
1243 static void vik_window_pan_move (VikWindow *vw, GdkEventMotion *event)
1245 if ( vw->pan_x != -1 ) {
1246 vik_viewport_set_center_screen ( vw->viking_vvp, vik_viewport_get_width(vw->viking_vvp)/2 - event->x + vw->pan_x,
1247 vik_viewport_get_height(vw->viking_vvp)/2 - event->y + vw->pan_y );
1248 vw->pan_move = TRUE;
1249 vw->pan_x = event->x;
1250 vw->pan_y = event->y;
1256 * get_location_strings:
1258 * Utility function to get positional strings for the given location
1259 * lat and lon strings will get allocated and so need to be freed after use
1261 static void get_location_strings ( VikWindow *vw, struct UTM utm, gchar **lat, gchar **lon )
1263 if ( vik_viewport_get_drawmode ( vw->viking_vvp ) == VIK_VIEWPORT_DRAWMODE_UTM ) {
1264 // Reuse lat for the first part (Zone + N or S, and lon for the second part (easting and northing) of a UTM format:
1265 // ZONE[N|S] EASTING NORTHING
1266 *lat = g_malloc(4*sizeof(gchar));
1267 // NB zone is stored in a char but is an actual number
1268 g_snprintf (*lat, 4, "%d%c", utm.zone, utm.letter);
1269 *lon = g_malloc(16*sizeof(gchar));
1270 g_snprintf (*lon, 16, "%d %d", (gint)utm.easting, (gint)utm.northing);
1274 a_coords_utm_to_latlon ( &utm, &ll );
1275 a_coords_latlon_to_string ( &ll, lat, lon );
1279 static void draw_mouse_motion (VikWindow *vw, GdkEventMotion *event)
1281 static VikCoord coord;
1282 static struct UTM utm;
1283 #define BUFFER_SIZE 50
1284 static char pointer_buf[BUFFER_SIZE];
1285 gchar *lat = NULL, *lon = NULL;
1288 VikDemInterpol interpol_method;
1290 /* This is a hack, but work far the best, at least for single pointer systems.
1291 * See http://bugzilla.gnome.org/show_bug.cgi?id=587714 for more. */
1293 gdk_window_get_pointer (event->window, &x, &y, NULL);
1297 toolbox_move(vw->vt, event);
1299 vik_viewport_screen_to_coord ( vw->viking_vvp, event->x, event->y, &coord );
1300 vik_coord_to_utm ( &coord, &utm );
1302 get_location_strings ( vw, utm, &lat, &lon );
1304 /* Change interpolate method according to scale */
1305 zoom = vik_viewport_get_zoom(vw->viking_vvp);
1307 interpol_method = VIK_DEM_INTERPOL_NONE;
1308 else if (zoom >= 1.0)
1309 interpol_method = VIK_DEM_INTERPOL_SIMPLE;
1311 interpol_method = VIK_DEM_INTERPOL_BEST;
1312 if ((alt = a_dems_get_elev_by_coord(&coord, interpol_method)) != VIK_DEM_INVALID_ELEVATION) {
1313 if ( a_vik_get_units_height () == VIK_UNITS_HEIGHT_METRES )
1314 g_snprintf ( pointer_buf, BUFFER_SIZE, _("%s %s %dm"), lat, lon, alt );
1316 g_snprintf ( pointer_buf, BUFFER_SIZE, _("%s %s %dft"), lat, lon, (int)VIK_METERS_TO_FEET(alt) );
1319 g_snprintf ( pointer_buf, BUFFER_SIZE, _("%s %s"), lat, lon );
1324 vik_statusbar_set_message ( vw->viking_vs, VIK_STATUSBAR_POSITION, pointer_buf );
1326 vik_window_pan_move ( vw, event );
1328 /* This is recommended by the GTK+ documentation, but does not work properly.
1329 * Use deprecated way until GTK+ gets a solution for correct motion hint handling:
1330 * http://bugzilla.gnome.org/show_bug.cgi?id=587714
1332 /* gdk_event_request_motions ( event ); */
1336 * Action the single click after a small timeout
1337 * If a double click has occurred then this will do nothing
1339 static gboolean vik_window_pan_timeout (VikWindow *vw)
1341 if ( ! vw->single_click_pending ) {
1342 // Double click happened, so don't do anything
1346 /* set panning origin */
1347 vw->pan_move = FALSE;
1348 vw->single_click_pending = FALSE;
1349 vik_viewport_set_center_screen ( vw->viking_vvp, vw->delayed_pan_x, vw->delayed_pan_y );
1352 // Really turn off the pan moving!!
1353 vw->pan_x = vw->pan_y = -1;
1357 static void vik_window_pan_release ( VikWindow *vw, GdkEventButton *event )
1359 gboolean do_draw = TRUE;
1361 if ( vw->pan_move == FALSE ) {
1362 vw->single_click_pending = !vw->single_click_pending;
1364 if ( vw->single_click_pending ) {
1365 // Store offset to use
1366 vw->delayed_pan_x = vw->pan_x;
1367 vw->delayed_pan_y = vw->pan_y;
1368 // Get double click time
1369 GtkSettings *gs = gtk_widget_get_settings ( GTK_WIDGET(vw) );
1370 GValue dct = { 0 }; // = G_VALUE_INIT; // GLIB 2.30+ only
1371 g_value_init ( &dct, G_TYPE_INT );
1372 g_object_get_property ( G_OBJECT(gs), "gtk-double-click-time", &dct );
1373 // Give chance for a double click to occur
1374 gint timer = g_value_get_int ( &dct ) + 50;
1375 g_timeout_add ( timer, (GSourceFunc)vik_window_pan_timeout, vw );
1379 vik_viewport_set_center_screen ( vw->viking_vvp, vw->pan_x, vw->pan_y );
1383 vik_viewport_set_center_screen ( vw->viking_vvp, vik_viewport_get_width(vw->viking_vvp)/2 - event->x + vw->pan_x,
1384 vik_viewport_get_height(vw->viking_vvp)/2 - event->y + vw->pan_y );
1387 vw->pan_move = FALSE;
1388 vw->pan_x = vw->pan_y = -1;
1393 static void draw_release ( VikWindow *vw, GdkEventButton *event )
1395 gtk_widget_grab_focus ( GTK_WIDGET(vw->viking_vvp) );
1397 if ( event->button == 2 ) { /* move / pan */
1398 if ( vw->vt->tools[vw->vt->active_tool].ti.pan_handler )
1399 // Tool still may need to do something (such as reenable something)
1400 toolbox_release(vw->vt, event);
1401 vik_window_pan_release ( vw, event );
1404 toolbox_release(vw->vt, event);
1408 static void draw_scroll (VikWindow *vw, GdkEventScroll *event)
1410 guint modifiers = event->state & (GDK_SHIFT_MASK | GDK_CONTROL_MASK);
1411 if ( modifiers == GDK_CONTROL_MASK ) {
1412 /* control == pan up & down */
1413 if ( event->direction == GDK_SCROLL_UP )
1414 vik_viewport_set_center_screen ( vw->viking_vvp, vik_viewport_get_width(vw->viking_vvp)/2, vik_viewport_get_height(vw->viking_vvp)/3 );
1416 vik_viewport_set_center_screen ( vw->viking_vvp, vik_viewport_get_width(vw->viking_vvp)/2, vik_viewport_get_height(vw->viking_vvp)*2/3 );
1417 } else if ( modifiers == GDK_SHIFT_MASK ) {
1418 /* shift == pan left & right */
1419 if ( event->direction == GDK_SCROLL_UP )
1420 vik_viewport_set_center_screen ( vw->viking_vvp, vik_viewport_get_width(vw->viking_vvp)/3, vik_viewport_get_height(vw->viking_vvp)/2 );
1422 vik_viewport_set_center_screen ( vw->viking_vvp, vik_viewport_get_width(vw->viking_vvp)*2/3, vik_viewport_get_height(vw->viking_vvp)/2 );
1423 } else if ( modifiers == (GDK_CONTROL_MASK | GDK_SHIFT_MASK) ) {
1424 // This zoom is on the center position
1425 if ( event->direction == GDK_SCROLL_UP )
1426 vik_viewport_zoom_in (vw->viking_vvp);
1428 vik_viewport_zoom_out (vw->viking_vvp);
1430 /* make sure mouse is still over the same point on the map when we zoom */
1433 gint center_x = vik_viewport_get_width ( vw->viking_vvp ) / 2;
1434 gint center_y = vik_viewport_get_height ( vw->viking_vvp ) / 2;
1435 vik_viewport_screen_to_coord ( vw->viking_vvp, event->x, event->y, &coord );
1436 if ( event->direction == GDK_SCROLL_UP )
1437 vik_viewport_zoom_in (vw->viking_vvp);
1439 vik_viewport_zoom_out(vw->viking_vvp);
1440 vik_viewport_coord_to_screen ( vw->viking_vvp, &coord, &x, &y );
1441 vik_viewport_set_center_screen ( vw->viking_vvp, center_x + (x - event->x),
1442 center_y + (y - event->y) );
1450 /********************************************************************************
1452 ********************************************************************************/
1453 static void draw_ruler(VikViewport *vvp, GdkDrawable *d, GdkGC *gc, gint x1, gint y1, gint x2, gint y2, gdouble distance)
1457 GdkGC *labgc = vik_viewport_new_gc ( vvp, "#cccccc", 1);
1458 GdkGC *thickgc = gdk_gc_new(d);
1460 gdouble len = sqrt((x1-x2)*(x1-x2) + (y1-y2)*(y1-y2));
1461 gdouble dx = (x2-x1)/len*10;
1462 gdouble dy = (y2-y1)/len*10;
1463 gdouble c = cos(DEG2RAD(15.0));
1464 gdouble s = sin(DEG2RAD(15.0));
1466 gdouble baseangle = 0;
1469 /* draw line with arrow ends */
1471 gint tmp_x1=x1, tmp_y1=y1, tmp_x2=x2, tmp_y2=y2;
1472 a_viewport_clip_line(&tmp_x1, &tmp_y1, &tmp_x2, &tmp_y2);
1473 gdk_draw_line(d, gc, tmp_x1, tmp_y1, tmp_x2, tmp_y2);
1476 a_viewport_clip_line(&x1, &y1, &x2, &y2);
1477 gdk_draw_line(d, gc, x1, y1, x2, y2);
1479 gdk_draw_line(d, gc, x1 - dy, y1 + dx, x1 + dy, y1 - dx);
1480 gdk_draw_line(d, gc, x2 - dy, y2 + dx, x2 + dy, y2 - dx);
1481 gdk_draw_line(d, gc, x2, y2, x2 - (dx * c + dy * s), y2 - (dy * c - dx * s));
1482 gdk_draw_line(d, gc, x2, y2, x2 - (dx * c - dy * s), y2 - (dy * c + dx * s));
1483 gdk_draw_line(d, gc, x1, y1, x1 + (dx * c + dy * s), y1 + (dy * c - dx * s));
1484 gdk_draw_line(d, gc, x1, y1, x1 + (dx * c - dy * s), y1 + (dy * c + dx * s));
1490 vik_viewport_compute_bearing ( vvp, x1, y1, x2, y2, &angle, &baseangle );
1494 gdk_gc_copy(thickgc, gc);
1495 gdk_gc_set_line_attributes(thickgc, CW, GDK_LINE_SOLID, GDK_CAP_BUTT, GDK_JOIN_MITER);
1496 gdk_color_parse("#2255cc", &color);
1497 gdk_gc_set_rgb_fg_color(thickgc, &color);
1499 gdk_draw_arc (d, thickgc, FALSE, x1-CR+CW/2, y1-CR+CW/2, 2*CR-CW, 2*CR-CW, (90 - RAD2DEG(baseangle))*64, -RAD2DEG(angle)*64);
1502 gdk_gc_copy(thickgc, gc);
1503 gdk_gc_set_line_attributes(thickgc, 2, GDK_LINE_SOLID, GDK_CAP_BUTT, GDK_JOIN_MITER);
1504 for (i=0; i<180; i++) {
1505 c = cos(DEG2RAD(i)*2 + baseangle);
1506 s = sin(DEG2RAD(i)*2 + baseangle);
1509 gdk_draw_line (d, gc, x1 + CR*c, y1 + CR*s, x1 + (CR+CW)*c, y1 + (CR+CW)*s);
1511 gdouble ticksize = 2*CW;
1512 gdk_draw_line (d, thickgc, x1 + (CR-CW)*c, y1 + (CR-CW)*s, x1 + (CR+ticksize)*c, y1 + (CR+ticksize)*s);
1516 gdk_draw_arc (d, gc, FALSE, x1-CR, y1-CR, 2*CR, 2*CR, 0, 64*360);
1517 gdk_draw_arc (d, gc, FALSE, x1-CR-CW, y1-CR-CW, 2*(CR+CW), 2*(CR+CW), 0, 64*360);
1518 gdk_draw_arc (d, gc, FALSE, x1-CR+CW, y1-CR+CW, 2*(CR-CW), 2*(CR-CW), 0, 64*360);
1519 c = (CR+CW*2)*cos(baseangle);
1520 s = (CR+CW*2)*sin(baseangle);
1521 gdk_draw_line (d, gc, x1-c, y1-s, x1+c, y1+s);
1522 gdk_draw_line (d, gc, x1+s, y1-c, x1-s, y1+c);
1525 #define LABEL(x, y, w, h) { \
1526 gdk_draw_rectangle(d, labgc, TRUE, (x)-2, (y)-1, (w)+4, (h)+1); \
1527 gdk_draw_rectangle(d, gc, FALSE, (x)-2, (y)-1, (w)+4, (h)+1); \
1528 gdk_draw_layout(d, gc, (x), (y), pl); }
1530 gint wd, hd, xd, yd;
1531 gint wb, hb, xb, yb;
1533 pl = gtk_widget_create_pango_layout (GTK_WIDGET(vvp), NULL);
1534 pango_layout_set_font_description (pl, gtk_widget_get_style(GTK_WIDGET(vvp))->font_desc);
1535 pango_layout_set_text(pl, "N", -1);
1536 gdk_draw_layout(d, gc, x1-5, y1-CR-3*CW-8, pl);
1538 /* draw label with distance */
1539 vik_units_distance_t dist_units = a_vik_get_units_distance ();
1540 switch (dist_units) {
1541 case VIK_UNITS_DISTANCE_KILOMETRES:
1542 if (distance >= 1000 && distance < 100000) {
1543 g_sprintf(str, "%3.2f km", distance/1000.0);
1544 } else if (distance < 1000) {
1545 g_sprintf(str, "%d m", (int)distance);
1547 g_sprintf(str, "%d km", (int)distance/1000);
1550 case VIK_UNITS_DISTANCE_MILES:
1551 if (distance >= VIK_MILES_TO_METERS(1) && distance < VIK_MILES_TO_METERS(100)) {
1552 g_sprintf(str, "%3.2f miles", VIK_METERS_TO_MILES(distance));
1553 } else if (distance < VIK_MILES_TO_METERS(1)) {
1554 g_sprintf(str, "%d yards", (int)(distance*1.0936133));
1556 g_sprintf(str, "%d miles", (int)VIK_METERS_TO_MILES(distance));
1559 case VIK_UNITS_DISTANCE_NAUTICAL_MILES:
1560 if (distance >= VIK_NAUTICAL_MILES_TO_METERS(1) && distance < VIK_NAUTICAL_MILES_TO_METERS(100)) {
1561 g_sprintf(str, "%3.2f NM", VIK_METERS_TO_NAUTICAL_MILES(distance));
1562 } else if (distance < VIK_NAUTICAL_MILES_TO_METERS(1)) {
1563 g_sprintf(str, "%d yards", (int)(distance*1.0936133));
1565 g_sprintf(str, "%d NM", (int)VIK_METERS_TO_NAUTICAL_MILES(distance));
1569 g_critical("Houston, we've had a problem. distance=%d", dist_units);
1572 pango_layout_set_text(pl, str, -1);
1574 pango_layout_get_pixel_size ( pl, &wd, &hd );
1576 xd = (x1+x2)/2 + dy;
1577 yd = (y1+y2)/2 - hd/2 - dx;
1579 xd = (x1+x2)/2 - dy;
1580 yd = (y1+y2)/2 - hd/2 + dx;
1583 if ( xd < -5 || yd < -5 || xd > vik_viewport_get_width(vvp)+5 || yd > vik_viewport_get_height(vvp)+5 ) {
1588 LABEL(xd, yd, wd, hd);
1590 /* draw label with bearing */
1591 g_sprintf(str, "%3.1f°", RAD2DEG(angle));
1592 pango_layout_set_text(pl, str, -1);
1593 pango_layout_get_pixel_size ( pl, &wb, &hb );
1594 xb = x1 + CR*cos(angle-M_PI_2);
1595 yb = y1 + CR*sin(angle-M_PI_2);
1597 if ( xb < -5 || yb < -5 || xb > vik_viewport_get_width(vvp)+5 || yb > vik_viewport_get_height(vvp)+5 ) {
1603 GdkRectangle r1 = {xd-2, yd-1, wd+4, hd+1}, r2 = {xb-2, yb-1, wb+4, hb+1};
1604 if (gdk_rectangle_intersect(&r1, &r2, &r2)) {
1608 LABEL(xb, yb, wb, hb);
1612 g_object_unref ( G_OBJECT ( pl ) );
1613 g_object_unref ( G_OBJECT ( labgc ) );
1614 g_object_unref ( G_OBJECT ( thickgc ) );
1620 gboolean has_oldcoord;
1622 } ruler_tool_state_t;
1624 static gpointer ruler_create (VikWindow *vw, VikViewport *vvp)
1626 ruler_tool_state_t *s = g_new(ruler_tool_state_t, 1);
1629 s->has_oldcoord = FALSE;
1633 static void ruler_destroy (ruler_tool_state_t *s)
1638 static VikLayerToolFuncStatus ruler_click (VikLayer *vl, GdkEventButton *event, ruler_tool_state_t *s)
1643 if ( event->button == 1 ) {
1644 gchar *lat=NULL, *lon=NULL;
1645 vik_viewport_screen_to_coord ( s->vvp, (gint) event->x, (gint) event->y, &coord );
1646 vik_coord_to_latlon ( &coord, &ll );
1647 a_coords_latlon_to_string ( &ll, &lat, &lon );
1648 if ( s->has_oldcoord ) {
1649 vik_units_distance_t dist_units = a_vik_get_units_distance ();
1650 switch (dist_units) {
1651 case VIK_UNITS_DISTANCE_KILOMETRES:
1652 temp = g_strdup_printf ( "%s %s DIFF %f meters", lat, lon, vik_coord_diff( &coord, &(s->oldcoord) ) );
1654 case VIK_UNITS_DISTANCE_MILES:
1655 temp = g_strdup_printf ( "%s %s DIFF %f miles", lat, lon, VIK_METERS_TO_MILES(vik_coord_diff( &coord, &(s->oldcoord) )) );
1657 case VIK_UNITS_DISTANCE_NAUTICAL_MILES:
1658 temp = g_strdup_printf ( "%s %s DIFF %f NM", lat, lon, VIK_METERS_TO_NAUTICAL_MILES(vik_coord_diff( &coord, &(s->oldcoord) )) );
1661 temp = g_strdup_printf ("Just to keep the compiler happy");
1662 g_critical("Houston, we've had a problem. distance=%d", dist_units);
1665 s->has_oldcoord = FALSE;
1668 temp = g_strdup_printf ( "%s %s", lat, lon );
1669 s->has_oldcoord = TRUE;
1672 vik_statusbar_set_message ( s->vw->viking_vs, VIK_STATUSBAR_INFO, temp );
1675 s->oldcoord = coord;
1678 vik_viewport_set_center_screen ( s->vvp, (gint) event->x, (gint) event->y );
1679 draw_update ( s->vw );
1681 return VIK_LAYER_TOOL_ACK;
1684 static VikLayerToolFuncStatus ruler_move (VikLayer *vl, GdkEventMotion *event, ruler_tool_state_t *s)
1686 VikViewport *vvp = s->vvp;
1687 VikWindow *vw = s->vw;
1693 if ( s->has_oldcoord ) {
1694 int oldx, oldy, w1, h1, w2, h2;
1695 static GdkPixmap *buf = NULL;
1696 gchar *lat=NULL, *lon=NULL;
1697 w1 = vik_viewport_get_width(vvp);
1698 h1 = vik_viewport_get_height(vvp);
1700 buf = gdk_pixmap_new ( gtk_widget_get_window(GTK_WIDGET(vvp)), w1, h1, -1 );
1702 gdk_drawable_get_size(buf, &w2, &h2);
1703 if (w1 != w2 || h1 != h2) {
1704 g_object_unref ( G_OBJECT ( buf ) );
1705 buf = gdk_pixmap_new ( gtk_widget_get_window(GTK_WIDGET(vvp)), w1, h1, -1 );
1708 vik_viewport_screen_to_coord ( vvp, (gint) event->x, (gint) event->y, &coord );
1709 vik_coord_to_latlon ( &coord, &ll );
1710 vik_viewport_coord_to_screen ( vvp, &s->oldcoord, &oldx, &oldy );
1712 gdk_draw_drawable (buf, gtk_widget_get_style(GTK_WIDGET(vvp))->black_gc,
1713 vik_viewport_get_pixmap(vvp), 0, 0, 0, 0, -1, -1);
1714 draw_ruler(vvp, buf, gtk_widget_get_style(GTK_WIDGET(vvp))->black_gc, oldx, oldy, event->x, event->y, vik_coord_diff( &coord, &(s->oldcoord)) );
1715 if (draw_buf_done) {
1716 static gpointer pass_along[3];
1717 pass_along[0] = gtk_widget_get_window(GTK_WIDGET(vvp));
1718 pass_along[1] = gtk_widget_get_style(GTK_WIDGET(vvp))->black_gc;
1719 pass_along[2] = buf;
1720 g_idle_add_full (G_PRIORITY_HIGH_IDLE + 10, draw_buf, pass_along, NULL);
1721 draw_buf_done = FALSE;
1723 a_coords_latlon_to_string(&ll, &lat, &lon);
1724 vik_units_distance_t dist_units = a_vik_get_units_distance ();
1725 switch (dist_units) {
1726 case VIK_UNITS_DISTANCE_KILOMETRES:
1727 temp = g_strdup_printf ( "%s %s DIFF %f meters", lat, lon, vik_coord_diff( &coord, &(s->oldcoord) ) );
1729 case VIK_UNITS_DISTANCE_MILES:
1730 temp = g_strdup_printf ( "%s %s DIFF %f miles", lat, lon, VIK_METERS_TO_MILES (vik_coord_diff( &coord, &(s->oldcoord) )) );
1732 case VIK_UNITS_DISTANCE_NAUTICAL_MILES:
1733 temp = g_strdup_printf ( "%s %s DIFF %f NM", lat, lon, VIK_METERS_TO_NAUTICAL_MILES (vik_coord_diff( &coord, &(s->oldcoord) )) );
1736 temp = g_strdup_printf ("Just to keep the compiler happy");
1737 g_critical("Houston, we've had a problem. distance=%d", dist_units);
1739 vik_statusbar_set_message ( vw->viking_vs, VIK_STATUSBAR_INFO, temp );
1742 return VIK_LAYER_TOOL_ACK;
1745 static VikLayerToolFuncStatus ruler_release (VikLayer *vl, GdkEventButton *event, ruler_tool_state_t *s)
1747 return VIK_LAYER_TOOL_ACK;
1750 static void ruler_deactivate (VikLayer *vl, ruler_tool_state_t *s)
1752 draw_update ( s->vw );
1755 static gboolean ruler_key_press (VikLayer *vl, GdkEventKey *event, ruler_tool_state_t *s)
1757 if (event->keyval == GDK_Escape) {
1758 s->has_oldcoord = FALSE;
1759 ruler_deactivate ( vl, s );
1762 // Regardless of whether we used it, return false so other GTK things may use it
1766 static VikToolInterface ruler_tool =
1767 // NB Ctrl+Shift+R is used for Refresh (deemed more important), so use 'U' instead
1768 { { "Ruler", "vik-icon-ruler", N_("_Ruler"), "<control><shift>U", N_("Ruler Tool"), 2 },
1769 (VikToolConstructorFunc) ruler_create,
1770 (VikToolDestructorFunc) ruler_destroy,
1771 (VikToolActivationFunc) NULL,
1772 (VikToolActivationFunc) ruler_deactivate,
1773 (VikToolMouseFunc) ruler_click,
1774 (VikToolMouseMoveFunc) ruler_move,
1775 (VikToolMouseFunc) ruler_release,
1776 (VikToolKeyFunc) ruler_key_press,
1778 GDK_CURSOR_IS_PIXMAP,
1779 &cursor_ruler_pixbuf,
1781 /*** end ruler code ********************************************************/
1785 /********************************************************************************
1787 ********************************************************************************/
1792 // Track zoom bounds for zoom tool with shift modifier:
1793 gboolean bounds_active;
1796 } zoom_tool_state_t;
1799 * In case the screen size has changed
1801 static void zoomtool_resize_pixmap (zoom_tool_state_t *zts)
1805 // Allocate a drawing area the size of the viewport
1806 w1 = vik_viewport_get_width ( zts->vw->viking_vvp );
1807 h1 = vik_viewport_get_height ( zts->vw->viking_vvp );
1809 if ( !zts->pixmap ) {
1811 zts->pixmap = gdk_pixmap_new ( gtk_widget_get_window(GTK_WIDGET(zts->vw->viking_vvp)), w1, h1, -1 );
1814 gdk_drawable_get_size ( zts->pixmap, &w2, &h2 );
1816 if ( w1 != w2 || h1 != h2 ) {
1817 // Has changed - delete and recreate with new values
1818 g_object_unref ( G_OBJECT ( zts->pixmap ) );
1819 zts->pixmap = gdk_pixmap_new ( gtk_widget_get_window(GTK_WIDGET(zts->vw->viking_vvp)), w1, h1, -1 );
1823 static gpointer zoomtool_create (VikWindow *vw, VikViewport *vvp)
1825 zoom_tool_state_t *zts = g_new(zoom_tool_state_t, 1);
1830 zts->bounds_active = FALSE;
1834 static void zoomtool_destroy ( zoom_tool_state_t *zts)
1837 g_object_unref ( G_OBJECT ( zts->pixmap ) );
1841 static VikLayerToolFuncStatus zoomtool_click (VikLayer *vl, GdkEventButton *event, zoom_tool_state_t *zts)
1843 zts->vw->modified = TRUE;
1844 guint modifiers = event->state & (GDK_SHIFT_MASK | GDK_CONTROL_MASK);
1848 gint center_x = vik_viewport_get_width ( zts->vw->viking_vvp ) / 2;
1849 gint center_y = vik_viewport_get_height ( zts->vw->viking_vvp ) / 2;
1851 gboolean skip_update = FALSE;
1853 zts->bounds_active = FALSE;
1855 if ( modifiers == (GDK_CONTROL_MASK | GDK_SHIFT_MASK) ) {
1856 // This zoom is on the center position
1857 vik_viewport_set_center_screen ( zts->vw->viking_vvp, center_x, center_y );
1858 if ( event->button == 1 )
1859 vik_viewport_zoom_in (zts->vw->viking_vvp);
1860 else if ( event->button == 3 )
1861 vik_viewport_zoom_out (zts->vw->viking_vvp);
1863 else if ( modifiers == GDK_CONTROL_MASK ) {
1864 // This zoom is to recenter on the mouse position
1865 vik_viewport_set_center_screen ( zts->vw->viking_vvp, (gint) event->x, (gint) event->y );
1866 if ( event->button == 1 )
1867 vik_viewport_zoom_in (zts->vw->viking_vvp);
1868 else if ( event->button == 3 )
1869 vik_viewport_zoom_out (zts->vw->viking_vvp);
1871 else if ( modifiers == GDK_SHIFT_MASK ) {
1872 // Get start of new zoom bounds
1873 if ( event->button == 1 ) {
1874 zts->bounds_active = TRUE;
1875 zts->start_x = (gint) event->x;
1876 zts->start_y = (gint) event->y;
1881 /* make sure mouse is still over the same point on the map when we zoom */
1882 vik_viewport_screen_to_coord ( zts->vw->viking_vvp, event->x, event->y, &coord );
1883 if ( event->button == 1 )
1884 vik_viewport_zoom_in (zts->vw->viking_vvp);
1885 else if ( event->button == 3 )
1886 vik_viewport_zoom_out(zts->vw->viking_vvp);
1887 vik_viewport_coord_to_screen ( zts->vw->viking_vvp, &coord, &x, &y );
1888 vik_viewport_set_center_screen ( zts->vw->viking_vvp,
1889 center_x + (x - event->x),
1890 center_y + (y - event->y) );
1894 draw_update ( zts->vw );
1896 return VIK_LAYER_TOOL_ACK;
1899 static VikLayerToolFuncStatus zoomtool_move (VikLayer *vl, GdkEventMotion *event, zoom_tool_state_t *zts)
1901 guint modifiers = event->state & (GDK_SHIFT_MASK | GDK_CONTROL_MASK);
1903 if ( zts->bounds_active && modifiers == GDK_SHIFT_MASK ) {
1904 zoomtool_resize_pixmap ( zts );
1906 // Blank out currently drawn area
1907 gdk_draw_drawable ( zts->pixmap,
1908 gtk_widget_get_style(GTK_WIDGET(zts->vw->viking_vvp))->black_gc,
1909 vik_viewport_get_pixmap(zts->vw->viking_vvp),
1910 0, 0, 0, 0, -1, -1);
1912 // Calculate new box starting point & size in pixels
1913 int xx, yy, width, height;
1914 if ( event->y > zts->start_y ) {
1916 height = event->y-zts->start_y;
1920 height = zts->start_y-event->y;
1922 if ( event->x > zts->start_x ) {
1924 width = event->x-zts->start_x;
1928 width = zts->start_x-event->x;
1932 gdk_draw_rectangle (zts->pixmap, gtk_widget_get_style(GTK_WIDGET(zts->vw->viking_vvp))->black_gc, FALSE, xx, yy, width, height);
1934 // Only actually draw when there's time to do so
1935 if (draw_buf_done) {
1936 static gpointer pass_along[3];
1937 pass_along[0] = gtk_widget_get_window(GTK_WIDGET(zts->vw->viking_vvp));
1938 pass_along[1] = gtk_widget_get_style(GTK_WIDGET(zts->vw->viking_vvp))->black_gc;
1939 pass_along[2] = zts->pixmap;
1940 g_idle_add_full (G_PRIORITY_HIGH_IDLE + 10, draw_buf, pass_along, NULL);
1941 draw_buf_done = FALSE;
1945 zts->bounds_active = FALSE;
1947 return VIK_LAYER_TOOL_ACK;
1950 static VikLayerToolFuncStatus zoomtool_release (VikLayer *vl, GdkEventButton *event, zoom_tool_state_t *zts)
1952 guint modifiers = event->state & (GDK_SHIFT_MASK | GDK_CONTROL_MASK);
1954 // Ensure haven't just released on the exact same position
1955 // i.e. probably haven't moved the mouse at all
1956 if ( zts->bounds_active && modifiers == GDK_SHIFT_MASK &&
1957 ( event->x < zts->start_x-5 || event->x > zts->start_x+5 ) &&
1958 ( event->y < zts->start_y-5 || event->y > zts->start_y+5 ) ) {
1960 VikCoord coord1, coord2;
1961 vik_viewport_screen_to_coord ( zts->vw->viking_vvp, zts->start_x, zts->start_y, &coord1);
1962 vik_viewport_screen_to_coord ( zts->vw->viking_vvp, event->x, event->y, &coord2);
1964 // From the extend of the bounds pick the best zoom level
1965 // c.f. trw_layer_zoom_to_show_latlons()
1966 // Maybe refactor...
1967 struct LatLon ll1, ll2;
1968 vik_coord_to_latlon(&coord1, &ll1);
1969 vik_coord_to_latlon(&coord2, &ll2);
1970 struct LatLon average = { (ll1.lat+ll2.lat)/2,
1971 (ll1.lon+ll2.lon)/2 };
1973 VikCoord new_center;
1974 vik_coord_load_from_latlon ( &new_center, vik_viewport_get_coord_mode ( zts->vw->viking_vvp ), &average );
1975 vik_viewport_set_center_coord ( zts->vw->viking_vvp, &new_center, FALSE );
1977 /* Convert into definite 'smallest' and 'largest' positions */
1978 struct LatLon minmin;
1979 if ( ll1.lat < ll2.lat )
1980 minmin.lat = ll1.lat;
1982 minmin.lat = ll2.lat;
1984 struct LatLon maxmax;
1985 if ( ll1.lon > ll2.lon )
1986 maxmax.lon = ll1.lon;
1988 maxmax.lon = ll2.lon;
1990 /* Always recalculate the 'best' zoom level */
1991 gdouble zoom = VIK_VIEWPORT_MIN_ZOOM;
1992 vik_viewport_set_zoom ( zts->vw->viking_vvp, zoom );
1994 gdouble min_lat, max_lat, min_lon, max_lon;
1995 /* Should only be a maximum of about 18 iterations from min to max zoom levels */
1996 while ( zoom <= VIK_VIEWPORT_MAX_ZOOM ) {
1997 vik_viewport_get_min_max_lat_lon ( zts->vw->viking_vvp, &min_lat, &max_lat, &min_lon, &max_lon );
1998 /* NB I think the logic used in this test to determine if the bounds is within view
1999 fails if track goes across 180 degrees longitude.
2000 Hopefully that situation is not too common...
2001 Mind you viking doesn't really do edge locations to well anyway */
2002 if ( min_lat < minmin.lat &&
2003 max_lat > minmin.lat &&
2004 min_lon < maxmax.lon &&
2005 max_lon > maxmax.lon )
2006 /* Found within zoom level */
2011 vik_viewport_set_zoom ( zts->vw->viking_vvp, zoom );
2015 // When pressing shift and clicking for zoom, then jump three levels
2016 if ( modifiers == GDK_SHIFT_MASK ) {
2017 // Zoom in/out by three if possible
2018 vik_viewport_set_center_screen ( zts->vw->viking_vvp, event->x, event->y );
2019 if ( event->button == 1 ) {
2020 vik_viewport_zoom_in ( zts->vw->viking_vvp );
2021 vik_viewport_zoom_in ( zts->vw->viking_vvp );
2022 vik_viewport_zoom_in ( zts->vw->viking_vvp );
2024 else if ( event->button == 3 ) {
2025 vik_viewport_zoom_out ( zts->vw->viking_vvp );
2026 vik_viewport_zoom_out ( zts->vw->viking_vvp );
2027 vik_viewport_zoom_out ( zts->vw->viking_vvp );
2032 draw_update ( zts->vw );
2035 zts->bounds_active = FALSE;
2037 return VIK_LAYER_TOOL_ACK;
2040 static VikToolInterface zoom_tool =
2041 { { "Zoom", "vik-icon-zoom", N_("_Zoom"), "<control><shift>Z", N_("Zoom Tool"), 1 },
2042 (VikToolConstructorFunc) zoomtool_create,
2043 (VikToolDestructorFunc) zoomtool_destroy,
2044 (VikToolActivationFunc) NULL,
2045 (VikToolActivationFunc) NULL,
2046 (VikToolMouseFunc) zoomtool_click,
2047 (VikToolMouseMoveFunc) zoomtool_move,
2048 (VikToolMouseFunc) zoomtool_release,
2051 GDK_CURSOR_IS_PIXMAP,
2052 &cursor_zoom_pixbuf,
2054 /*** end zoom code ********************************************************/
2056 /********************************************************************************
2058 ********************************************************************************/
2059 static gpointer pantool_create (VikWindow *vw, VikViewport *vvp)
2064 // NB Double clicking means this gets called THREE times!!!
2065 static VikLayerToolFuncStatus pantool_click (VikLayer *vl, GdkEventButton *event, VikWindow *vw)
2067 vw->modified = TRUE;
2069 if ( event->type == GDK_2BUTTON_PRESS ) {
2070 // Zoom in / out on double click
2071 // No need to change the center as that has already occurred in the first click of a double click occurrence
2072 if ( event->button == 1 ) {
2073 guint modifier = event->state & GDK_SHIFT_MASK;
2075 vik_viewport_zoom_out ( vw->viking_vvp );
2077 vik_viewport_zoom_in ( vw->viking_vvp );
2079 else if ( event->button == 3 )
2080 vik_viewport_zoom_out ( vw->viking_vvp );
2085 // Standard pan click
2086 if ( event->button == 1 )
2087 vik_window_pan_click ( vw, event );
2089 return VIK_LAYER_TOOL_ACK;
2092 static VikLayerToolFuncStatus pantool_move (VikLayer *vl, GdkEventMotion *event, VikWindow *vw)
2094 vik_window_pan_move ( vw, event );
2095 return VIK_LAYER_TOOL_ACK;
2098 static VikLayerToolFuncStatus pantool_release (VikLayer *vl, GdkEventButton *event, VikWindow *vw)
2100 if ( event->button == 1 )
2101 vik_window_pan_release ( vw, event );
2102 return VIK_LAYER_TOOL_ACK;
2105 static VikToolInterface pan_tool =
2106 { { "Pan", "vik-icon-pan", N_("_Pan"), "<control><shift>P", N_("Pan Tool"), 0 },
2107 (VikToolConstructorFunc) pantool_create,
2108 (VikToolDestructorFunc) NULL,
2109 (VikToolActivationFunc) NULL,
2110 (VikToolActivationFunc) NULL,
2111 (VikToolMouseFunc) pantool_click,
2112 (VikToolMouseMoveFunc) pantool_move,
2113 (VikToolMouseFunc) pantool_release,
2119 /*** end pan code ********************************************************/
2121 /********************************************************************************
2123 ********************************************************************************/
2124 static gpointer selecttool_create (VikWindow *vw, VikViewport *vvp)
2126 tool_ed_t *t = g_new(tool_ed_t, 1);
2130 t->is_waypoint = FALSE;
2134 static void selecttool_destroy (tool_ed_t *t)
2142 GdkEventButton *event;
2143 tool_ed_t *tool_edit;
2146 static void click_layer_selected (VikLayer *vl, clicker *ck)
2148 /* Do nothing when function call returns true; */
2149 /* i.e. stop on first found item */
2152 if ( vik_layer_get_interface(vl->type)->select_click )
2153 ck->cont = !vik_layer_get_interface(vl->type)->select_click ( vl, ck->event, ck->vvp, ck->tool_edit );
2157 // Hopefully Alt keys by default
2158 #define VIK_MOVE_MODIFIER GDK_MOD1_MASK
2160 // Alt+mouse on Linux desktops tend to be used by the desktop manager
2161 // Thus use an alternate modifier - you may need to set something into this group
2162 #define VIK_MOVE_MODIFIER GDK_MOD5_MASK
2165 static VikLayerToolFuncStatus selecttool_click (VikLayer *vl, GdkEventButton *event, tool_ed_t *t)
2167 t->vw->select_move = FALSE;
2168 /* Only allow selection on primary button */
2169 if ( event->button == 1 ) {
2171 if ( event->state & VIK_MOVE_MODIFIER )
2172 vik_window_pan_click ( t->vw, event );
2174 /* Enable click to apply callback to potentially all track/waypoint layers */
2175 /* Useful as we can find things that aren't necessarily in the currently selected layer */
2176 GList* gl = vik_layers_panel_get_all_layers_of_type ( t->vw->viking_vlp, VIK_LAYER_TRW, FALSE ); // Don't get invisible layers
2179 ck.vvp = t->vw->viking_vvp;
2182 g_list_foreach ( gl, (GFunc) click_layer_selected, &ck );
2185 // If nothing found then deselect & redraw screen if necessary to remove the highlight
2188 VikTreeview *vtv = vik_layers_panel_get_treeview ( t->vw->viking_vlp );
2190 if ( vik_treeview_get_selected_iter ( vtv, &iter ) ) {
2191 // Only clear if selected thing is a TrackWaypoint layer or a sublayer
2192 gint type = vik_treeview_item_get_type ( vtv, &iter );
2193 if ( type == VIK_TREEVIEW_TYPE_SUBLAYER ||
2194 VIK_LAYER(vik_treeview_item_get_pointer ( vtv, &iter ))->type == VIK_LAYER_TRW ) {
2196 vik_treeview_item_unselect ( vtv, &iter );
2197 if ( vik_window_clear_highlight ( t->vw ) )
2198 draw_update ( t->vw );
2203 // Something found - so enable movement
2204 t->vw->select_move = TRUE;
2208 else if ( ( event->button == 3 ) && ( vl && ( vl->type == VIK_LAYER_TRW ) ) ) {
2210 /* Act on currently selected item to show menu */
2211 if ( t->vw->selected_track || t->vw->selected_waypoint )
2212 if ( vik_layer_get_interface(vl->type)->show_viewport_menu )
2213 vik_layer_get_interface(vl->type)->show_viewport_menu ( vl, event, t->vw->viking_vvp );
2216 return VIK_LAYER_TOOL_ACK;
2219 static VikLayerToolFuncStatus selecttool_move (VikLayer *vl, GdkEventMotion *event, tool_ed_t *t)
2221 if ( t->vw->select_move ) {
2222 // Don't care about vl here
2224 if ( vik_layer_get_interface(VIK_LAYER_TRW)->select_move )
2225 vik_layer_get_interface(VIK_LAYER_TRW)->select_move ( vl, event, t->vvp, t );
2229 if ( event->state & VIK_MOVE_MODIFIER )
2230 vik_window_pan_move ( t->vw, event );
2232 return VIK_LAYER_TOOL_ACK;
2235 static VikLayerToolFuncStatus selecttool_release (VikLayer *vl, GdkEventButton *event, tool_ed_t *t)
2237 if ( t->vw->select_move ) {
2238 // Don't care about vl here
2240 if ( vik_layer_get_interface(VIK_LAYER_TRW)->select_release )
2241 vik_layer_get_interface(VIK_LAYER_TRW)->select_release ( (VikLayer*)t->vtl, event, t->vvp, t );
2244 if ( event->button == 1 && (event->state & VIK_MOVE_MODIFIER) )
2245 vik_window_pan_release ( t->vw, event );
2247 // Force pan off incase it was on
2248 t->vw->pan_move = FALSE;
2249 t->vw->pan_x = t->vw->pan_y = -1;
2251 // End of this select movement
2252 t->vw->select_move = FALSE;
2254 return VIK_LAYER_TOOL_ACK;
2257 static VikToolInterface select_tool =
2258 { { "Select", "vik-icon-select", N_("_Select"), "<control><shift>S", N_("Select Tool"), 3 },
2259 (VikToolConstructorFunc) selecttool_create,
2260 (VikToolDestructorFunc) selecttool_destroy,
2261 (VikToolActivationFunc) NULL,
2262 (VikToolActivationFunc) NULL,
2263 (VikToolMouseFunc) selecttool_click,
2264 (VikToolMouseMoveFunc) selecttool_move,
2265 (VikToolMouseFunc) selecttool_release,
2266 (VikToolKeyFunc) NULL,
2271 /*** end select tool code ********************************************************/
2273 static void draw_pan_cb ( GtkAction *a, VikWindow *vw )
2275 // Since the treeview cell editting intercepts standard keyboard handlers, it means we can receive events here
2276 // Thus if currently editting, ensure we don't move the viewport when Ctrl+<arrow> is received
2277 VikLayer *sel = vik_layers_panel_get_selected ( vw->viking_vlp );
2278 if ( sel && vik_treeview_get_editing ( sel->vt ) )
2281 if (!strcmp(gtk_action_get_name(a), "PanNorth")) {
2282 vik_viewport_set_center_screen ( vw->viking_vvp, vik_viewport_get_width(vw->viking_vvp)/2, 0 );
2283 } else if (!strcmp(gtk_action_get_name(a), "PanEast")) {
2284 vik_viewport_set_center_screen ( vw->viking_vvp, vik_viewport_get_width(vw->viking_vvp), vik_viewport_get_height(vw->viking_vvp)/2 );
2285 } else if (!strcmp(gtk_action_get_name(a), "PanSouth")) {
2286 vik_viewport_set_center_screen ( vw->viking_vvp, vik_viewport_get_width(vw->viking_vvp)/2, vik_viewport_get_height(vw->viking_vvp) );
2287 } else if (!strcmp(gtk_action_get_name(a), "PanWest")) {
2288 vik_viewport_set_center_screen ( vw->viking_vvp, 0, vik_viewport_get_height(vw->viking_vvp)/2 );
2293 static void draw_zoom_cb ( GtkAction *a, VikWindow *vw )
2297 if (!strcmp(gtk_action_get_name(a), "ZoomIn")) {
2300 else if (!strcmp(gtk_action_get_name(a), "ZoomOut")) {
2303 else if (!strcmp(gtk_action_get_name(a), "Zoom0.25")) {
2306 else if (!strcmp(gtk_action_get_name(a), "Zoom0.5")) {
2310 gchar *s = (gchar *)gtk_action_get_name(a);
2316 case -3: vik_viewport_zoom_in ( vw->viking_vvp ); break;
2317 case -4: vik_viewport_zoom_out ( vw->viking_vvp ); break;
2318 case -1: vik_viewport_set_zoom ( vw->viking_vvp, 0.5 ); break;
2319 case -2: vik_viewport_set_zoom ( vw->viking_vvp, 0.25 ); break;
2320 default: vik_viewport_set_zoom ( vw->viking_vvp, what );
2325 static void draw_goto_cb ( GtkAction *a, VikWindow *vw )
2327 VikCoord new_center;
2329 if (!strcmp(gtk_action_get_name(a), "GotoLL")) {
2330 struct LatLon ll, llold;
2331 vik_coord_to_latlon ( vik_viewport_get_center ( vw->viking_vvp ), &llold );
2332 if ( a_dialog_goto_latlon ( GTK_WINDOW(vw), &ll, &llold ) )
2333 vik_coord_load_from_latlon ( &new_center, vik_viewport_get_coord_mode(vw->viking_vvp), &ll );
2337 else if (!strcmp(gtk_action_get_name(a), "GotoUTM")) {
2338 struct UTM utm, utmold;
2339 vik_coord_to_utm ( vik_viewport_get_center ( vw->viking_vvp ), &utmold );
2340 if ( a_dialog_goto_utm ( GTK_WINDOW(vw), &utm, &utmold ) )
2341 vik_coord_load_from_utm ( &new_center, vik_viewport_get_coord_mode(vw->viking_vvp), &utm );
2346 g_critical("Houston, we've had a problem.");
2350 vik_viewport_set_center_coord ( vw->viking_vvp, &new_center, TRUE );
2355 * center_changed_cb:
2357 static void center_changed_cb ( VikWindow *vw )
2359 // ATM Keep back always available, so when we pan - we can jump to the last requested position
2361 GtkAction* action_back = gtk_action_group_get_action ( vw->action_group, "GoBack" );
2362 if ( action_back ) {
2363 gtk_action_set_sensitive ( action_back, vik_viewport_back_available(vw->viking_vvp) );
2366 GtkAction* action_forward = gtk_action_group_get_action ( vw->action_group, "GoForward" );
2367 if ( action_forward ) {
2368 gtk_action_set_sensitive ( action_forward, vik_viewport_forward_available(vw->viking_vvp) );
2371 toolbar_action_set_sensitive ( vw->viking_vtb, "GoForward", vik_viewport_forward_available(vw->viking_vvp) );
2375 * draw_goto_back_and_forth:
2377 static void draw_goto_back_and_forth ( GtkAction *a, VikWindow *vw )
2379 gboolean changed = FALSE;
2380 if (!strcmp(gtk_action_get_name(a), "GoBack")) {
2381 changed = vik_viewport_go_back ( vw->viking_vvp );
2383 else if (!strcmp(gtk_action_get_name(a), "GoForward")) {
2384 changed = vik_viewport_go_forward ( vw->viking_vvp );
2390 // Recheck buttons sensitivities, as the center changed signal is not sent on back/forward changes
2391 // (otherwise we would get stuck in an infinite loop!)
2392 center_changed_cb ( vw );
2399 * Refresh maps displayed
2401 static void draw_refresh_cb ( GtkAction *a, VikWindow *vw )
2403 // Only get 'new' maps
2404 simple_map_update ( vw, TRUE );
2407 static void menu_addlayer_cb ( GtkAction *a, VikWindow *vw )
2409 VikLayerTypeEnum type;
2410 for ( type = 0; type < VIK_LAYER_NUM_TYPES; type++ ) {
2411 if (!strcmp(vik_layer_get_interface(type)->name, gtk_action_get_name(a))) {
2412 if ( vik_layers_panel_new_layer ( vw->viking_vlp, type ) ) {
2414 vw->modified = TRUE;
2420 static void menu_copy_layer_cb ( GtkAction *a, VikWindow *vw )
2422 a_clipboard_copy_selected ( vw->viking_vlp );
2425 static void menu_cut_layer_cb ( GtkAction *a, VikWindow *vw )
2427 vik_layers_panel_cut_selected ( vw->viking_vlp );
2428 vw->modified = TRUE;
2431 static void menu_paste_layer_cb ( GtkAction *a, VikWindow *vw )
2433 if ( vik_layers_panel_paste_selected ( vw->viking_vlp ) )
2435 vw->modified = TRUE;
2439 static void menu_properties_cb ( GtkAction *a, VikWindow *vw )
2441 if ( ! vik_layers_panel_properties ( vw->viking_vlp ) )
2442 a_dialog_info_msg ( GTK_WINDOW(vw), _("You must select a layer to show its properties.") );
2445 static void help_help_cb ( GtkAction *a, VikWindow *vw )
2448 ShellExecute(NULL, "open", ""PACKAGE".pdf", NULL, NULL, SW_SHOWNORMAL);
2451 uri = g_strdup_printf("ghelp:%s", PACKAGE);
2452 GError *error = NULL;
2453 gboolean show = gtk_show_uri (NULL, uri, GDK_CURRENT_TIME, &error);
2454 if ( !show && !error )
2455 // No error to show, so unlikely this will get called
2456 a_dialog_error_msg ( GTK_WINDOW(vw), _("The help system is not available.") );
2459 a_dialog_error_msg_extra ( GTK_WINDOW(vw), _("Help is not available because: %s.\nEnsure a Mime Type ghelp handler program is installed (e.g. yelp)."), error->message );
2460 g_error_free ( error );
2463 #endif /* WINDOWS */
2466 static void toggle_side_panel ( VikWindow *vw )
2468 vw->show_side_panel = !vw->show_side_panel;
2469 if ( vw->show_side_panel )
2470 gtk_widget_show(GTK_WIDGET(vw->viking_vlp));
2472 gtk_widget_hide(GTK_WIDGET(vw->viking_vlp));
2475 static void toggle_full_screen ( VikWindow *vw )
2477 vw->show_full_screen = !vw->show_full_screen;
2478 if ( vw->show_full_screen )
2479 gtk_window_fullscreen ( GTK_WINDOW(vw) );
2481 gtk_window_unfullscreen ( GTK_WINDOW(vw) );
2484 static void toggle_statusbar ( VikWindow *vw )
2486 vw->show_statusbar = !vw->show_statusbar;
2487 if ( vw->show_statusbar )
2488 gtk_widget_show ( GTK_WIDGET(vw->viking_vs) );
2490 gtk_widget_hide ( GTK_WIDGET(vw->viking_vs) );
2493 static void toggle_toolbar ( VikWindow *vw )
2495 vw->show_toolbar = !vw->show_toolbar;
2496 if ( vw->show_toolbar )
2497 gtk_widget_show ( toolbar_get_widget (vw->viking_vtb) );
2499 gtk_widget_hide ( toolbar_get_widget (vw->viking_vtb) );
2502 static void toggle_main_menu ( VikWindow *vw )
2504 vw->show_main_menu = !vw->show_main_menu;
2505 if ( vw->show_main_menu )
2506 gtk_widget_show ( gtk_ui_manager_get_widget ( vw->uim, "/ui/MainMenu" ) );
2508 gtk_widget_hide ( gtk_ui_manager_get_widget ( vw->uim, "/ui/MainMenu" ) );
2511 // Only for 'view' toggle menu widgets ATM.
2512 GtkWidget *get_show_widget_by_name(VikWindow *vw, const gchar *name)
2514 g_return_val_if_fail(name != NULL, NULL);
2516 // ATM only FullScreen is *not* in SetShow path
2518 if ( g_strcmp0 ("FullScreen", name ) )
2519 path = g_strconcat("/ui/MainMenu/View/SetShow/", name, NULL);
2521 path = g_strconcat("/ui/MainMenu/View/", name, NULL);
2523 GtkWidget *widget = gtk_ui_manager_get_widget(vw->uim, path);
2529 static void tb_view_side_panel_cb ( GtkAction *a, VikWindow *vw )
2531 gboolean next_state = !vw->show_side_panel;
2532 GtkWidget *check_box = get_show_widget_by_name ( vw, gtk_action_get_name(a) );
2533 gboolean menu_state = gtk_check_menu_item_get_active ( GTK_CHECK_MENU_ITEM(check_box) );
2534 if ( next_state != menu_state )
2535 gtk_check_menu_item_set_active ( GTK_CHECK_MENU_ITEM(check_box), next_state );
2537 toggle_side_panel ( vw );
2540 static void tb_full_screen_cb ( GtkAction *a, VikWindow *vw )
2542 gboolean next_state = !vw->show_full_screen;
2543 GtkWidget *check_box = get_show_widget_by_name ( vw, gtk_action_get_name(a) );
2544 gboolean menu_state = gtk_check_menu_item_get_active ( GTK_CHECK_MENU_ITEM(check_box) );
2545 if ( next_state != menu_state )
2546 gtk_check_menu_item_set_active ( GTK_CHECK_MENU_ITEM(check_box), next_state );
2548 toggle_full_screen ( vw );
2551 static void tb_view_statusbar_cb ( GtkAction *a, VikWindow *vw )
2553 gboolean next_state = !vw->show_statusbar;
2554 GtkWidget *check_box = get_show_widget_by_name ( vw, gtk_action_get_name(a) );
2555 gboolean menu_state = gtk_check_menu_item_get_active ( GTK_CHECK_MENU_ITEM(check_box) );
2556 if ( next_state != menu_state )
2557 gtk_check_menu_item_set_active ( GTK_CHECK_MENU_ITEM(check_box), next_state );
2559 toggle_statusbar ( vw );
2562 static void tb_view_toolbar_cb ( GtkAction *a, VikWindow *vw )
2564 gboolean next_state = !vw->show_toolbar;
2565 GtkWidget *check_box = get_show_widget_by_name ( vw, gtk_action_get_name(a) );
2566 gboolean menu_state = gtk_check_menu_item_get_active ( GTK_CHECK_MENU_ITEM(check_box) );
2567 if ( next_state != menu_state )
2568 gtk_check_menu_item_set_active ( GTK_CHECK_MENU_ITEM(check_box), next_state );
2570 toggle_toolbar ( vw );
2573 static void tb_view_main_menu_cb ( GtkAction *a, VikWindow *vw )
2575 gboolean next_state = !vw->show_main_menu;
2576 GtkWidget *check_box = get_show_widget_by_name ( vw, gtk_action_get_name(a) );
2577 gboolean menu_state = gtk_check_menu_item_get_active ( GTK_CHECK_MENU_ITEM(check_box) );
2578 if ( next_state != menu_state )
2579 gtk_check_menu_item_set_active ( GTK_CHECK_MENU_ITEM(check_box), next_state );
2581 toggle_main_menu ( vw );
2584 static void tb_set_draw_scale ( GtkAction *a, VikWindow *vw )
2586 gboolean next_state = !vik_viewport_get_draw_scale ( vw->viking_vvp );
2587 GtkWidget *check_box = get_show_widget_by_name ( vw, gtk_action_get_name(a) );
2588 gboolean menu_state = gtk_check_menu_item_get_active ( GTK_CHECK_MENU_ITEM(check_box) );
2589 if ( next_state != menu_state )
2590 gtk_check_menu_item_set_active ( GTK_CHECK_MENU_ITEM(check_box), next_state );
2592 vik_viewport_set_draw_scale ( vw->viking_vvp, next_state );
2597 static void tb_set_draw_centermark ( GtkAction *a, VikWindow *vw )
2599 gboolean next_state = !vik_viewport_get_draw_centermark ( vw->viking_vvp );
2600 GtkWidget *check_box = get_show_widget_by_name ( vw, gtk_action_get_name(a) );
2601 gboolean menu_state = gtk_check_menu_item_get_active ( GTK_CHECK_MENU_ITEM(check_box) );
2602 if ( next_state != menu_state )
2603 gtk_check_menu_item_set_active ( GTK_CHECK_MENU_ITEM(check_box), next_state );
2605 vik_viewport_set_draw_centermark ( vw->viking_vvp, next_state );
2610 static void tb_set_draw_highlight ( GtkAction *a, VikWindow *vw )
2612 gboolean next_state = !vik_viewport_get_draw_highlight ( vw->viking_vvp );
2613 GtkWidget *check_box = get_show_widget_by_name ( vw, gtk_action_get_name(a) );
2614 gboolean menu_state = gtk_check_menu_item_get_active ( GTK_CHECK_MENU_ITEM(check_box) );
2615 if ( next_state != menu_state )
2616 gtk_check_menu_item_set_active ( GTK_CHECK_MENU_ITEM(check_box), next_state );
2618 vik_viewport_set_draw_highlight ( vw->viking_vvp, next_state );
2623 static void help_about_cb ( GtkAction *a, VikWindow *vw )
2625 a_dialog_about(GTK_WINDOW(vw));
2628 static void help_cache_info_cb ( GtkAction *a, VikWindow *vw )
2630 // NB: No i18n as this is just for debug
2631 gint byte_size = a_mapcache_get_size();
2632 gchar *msg_sz = NULL;
2634 #if GLIB_CHECK_VERSION(2,30,0)
2635 msg_sz = g_format_size_full ( byte_size, G_FORMAT_SIZE_LONG_FORMAT );
2637 msg_sz = g_format_size_for_display ( byte_size );
2639 msg = g_strdup_printf ( "Map Cache size is %s with %d items", msg_sz, a_mapcache_get_count());
2640 a_dialog_info_msg_extra ( GTK_WINDOW(vw), "%s", msg );
2645 static void back_forward_info_cb ( GtkAction *a, VikWindow *vw )
2647 vik_viewport_show_centers ( vw->viking_vvp, GTK_WINDOW(vw) );
2650 static void menu_delete_layer_cb ( GtkAction *a, VikWindow *vw )
2652 if ( vik_layers_panel_get_selected ( vw->viking_vlp ) )
2654 vik_layers_panel_delete_selected ( vw->viking_vlp );
2655 vw->modified = TRUE;
2658 a_dialog_info_msg ( GTK_WINDOW(vw), _("You must select a layer to delete.") );
2661 static void full_screen_cb ( GtkAction *a, VikWindow *vw )
2663 gboolean next_state = !vw->show_full_screen;
2664 GtkToggleToolButton *tbutton = (GtkToggleToolButton *)toolbar_get_widget_by_name ( vw->viking_vtb, gtk_action_get_name(a) );
2666 gboolean tb_state = gtk_toggle_tool_button_get_active ( tbutton );
2667 if ( next_state != tb_state )
2668 gtk_toggle_tool_button_set_active ( tbutton, next_state );
2670 toggle_full_screen ( vw );
2673 toggle_full_screen ( vw );
2676 static void view_side_panel_cb ( GtkAction *a, VikWindow *vw )
2678 gboolean next_state = !vw->show_side_panel;
2679 GtkToggleToolButton *tbutton = (GtkToggleToolButton *)toolbar_get_widget_by_name ( vw->viking_vtb, gtk_action_get_name(a) );
2681 gboolean tb_state = gtk_toggle_tool_button_get_active ( tbutton );
2682 if ( next_state != tb_state )
2683 gtk_toggle_tool_button_set_active ( tbutton, next_state );
2685 toggle_side_panel ( vw );
2688 toggle_side_panel ( vw );
2691 static void view_statusbar_cb ( GtkAction *a, VikWindow *vw )
2693 gboolean next_state = !vw->show_statusbar;
2694 GtkToggleToolButton *tbutton = (GtkToggleToolButton *)toolbar_get_widget_by_name ( vw->viking_vtb, gtk_action_get_name(a) );
2696 gboolean tb_state = gtk_toggle_tool_button_get_active ( tbutton );
2697 if ( next_state != tb_state )
2698 gtk_toggle_tool_button_set_active ( tbutton, next_state );
2700 toggle_statusbar ( vw );
2703 toggle_statusbar ( vw );
2706 static void view_toolbar_cb ( GtkAction *a, VikWindow *vw )
2708 gboolean next_state = !vw->show_toolbar;
2709 GtkToggleToolButton *tbutton = (GtkToggleToolButton *)toolbar_get_widget_by_name ( vw->viking_vtb, gtk_action_get_name(a) );
2711 gboolean tb_state = gtk_toggle_tool_button_get_active ( tbutton );
2712 if ( next_state != tb_state )
2713 gtk_toggle_tool_button_set_active ( tbutton, next_state );
2715 toggle_toolbar ( vw );
2718 toggle_toolbar ( vw );
2721 static void view_main_menu_cb ( GtkAction *a, VikWindow *vw )
2723 gboolean next_state = !vw->show_main_menu;
2724 GtkToggleToolButton *tbutton = (GtkToggleToolButton *)toolbar_get_widget_by_name ( vw->viking_vtb, gtk_action_get_name(a) );
2726 gboolean tb_state = gtk_toggle_tool_button_get_active ( tbutton );
2727 if ( next_state != tb_state )
2728 gtk_toggle_tool_button_set_active ( tbutton, next_state );
2730 toggle_main_menu ( vw );
2733 toggle_toolbar ( vw );
2736 /***************************************
2737 ** tool management routines
2739 ***************************************/
2741 static toolbox_tools_t* toolbox_create(VikWindow *vw)
2743 toolbox_tools_t *vt = g_new(toolbox_tools_t, 1);
2746 vt->active_tool = -1;
2751 static void toolbox_add_tool(toolbox_tools_t *vt, VikToolInterface *vti, gint layer_type )
2753 vt->tools = g_renew(toolbox_tool_t, vt->tools, vt->n_tools+1);
2754 vt->tools[vt->n_tools].ti = *vti;
2755 vt->tools[vt->n_tools].layer_type = layer_type;
2757 vt->tools[vt->n_tools].state = vti->create(vt->vw, vt->vw->viking_vvp);
2760 vt->tools[vt->n_tools].state = NULL;
2765 static int toolbox_get_tool(toolbox_tools_t *vt, const gchar *tool_name)
2768 for (i=0; i<vt->n_tools; i++) {
2769 if (!strcmp(tool_name, vt->tools[i].ti.radioActionEntry.name)) {
2776 static void toolbox_activate(toolbox_tools_t *vt, const gchar *tool_name)
2778 int tool = toolbox_get_tool(vt, tool_name);
2779 toolbox_tool_t *t = &vt->tools[tool];
2780 VikLayer *vl = vik_layers_panel_get_selected ( vt->vw->viking_vlp );
2782 if (tool == vt->n_tools) {
2783 g_critical("trying to activate a non-existent tool...");
2786 /* is the tool already active? */
2787 if (vt->active_tool == tool) {
2791 if (vt->active_tool != -1) {
2792 if (vt->tools[vt->active_tool].ti.deactivate) {
2793 vt->tools[vt->active_tool].ti.deactivate(NULL, vt->tools[vt->active_tool].state);
2796 if (t->ti.activate) {
2797 t->ti.activate(vl, t->state);
2799 vt->active_tool = tool;
2802 static const GdkCursor *toolbox_get_cursor(toolbox_tools_t *vt, const gchar *tool_name)
2804 int tool = toolbox_get_tool(vt, tool_name);
2805 toolbox_tool_t *t = &vt->tools[tool];
2806 if (t->ti.cursor == NULL) {
2807 if (t->ti.cursor_type == GDK_CURSOR_IS_PIXMAP && t->ti.cursor_data != NULL) {
2808 GError *cursor_load_err = NULL;
2809 GdkPixbuf *cursor_pixbuf = gdk_pixbuf_from_pixdata (t->ti.cursor_data, FALSE, &cursor_load_err);
2810 /* TODO: settable offeset */
2811 t->ti.cursor = gdk_cursor_new_from_pixbuf ( gdk_display_get_default(), cursor_pixbuf, 3, 3 );
2812 g_object_unref ( G_OBJECT(cursor_pixbuf) );
2814 t->ti.cursor = gdk_cursor_new ( t->ti.cursor_type );
2817 return t->ti.cursor;
2820 static void toolbox_click (toolbox_tools_t *vt, GdkEventButton *event)
2822 VikLayer *vl = vik_layers_panel_get_selected ( vt->vw->viking_vlp );
2823 if (vt->active_tool != -1 && vt->tools[vt->active_tool].ti.click) {
2824 gint ltype = vt->tools[vt->active_tool].layer_type;
2825 if ( ltype == TOOL_LAYER_TYPE_NONE || (vl && ltype == vl->type) )
2826 vt->tools[vt->active_tool].ti.click(vl, event, vt->tools[vt->active_tool].state);
2830 static void toolbox_move (toolbox_tools_t *vt, GdkEventMotion *event)
2832 VikLayer *vl = vik_layers_panel_get_selected ( vt->vw->viking_vlp );
2833 if (vt->active_tool != -1 && vt->tools[vt->active_tool].ti.move) {
2834 gint ltype = vt->tools[vt->active_tool].layer_type;
2835 if ( ltype == TOOL_LAYER_TYPE_NONE || (vl && ltype == vl->type) )
2836 if ( VIK_LAYER_TOOL_ACK_GRAB_FOCUS == vt->tools[vt->active_tool].ti.move(vl, event, vt->tools[vt->active_tool].state) )
2837 gtk_widget_grab_focus ( GTK_WIDGET(vt->vw->viking_vvp) );
2841 static void toolbox_release (toolbox_tools_t *vt, GdkEventButton *event)
2843 VikLayer *vl = vik_layers_panel_get_selected ( vt->vw->viking_vlp );
2844 if (vt->active_tool != -1 && vt->tools[vt->active_tool].ti.release ) {
2845 gint ltype = vt->tools[vt->active_tool].layer_type;
2846 if ( ltype == TOOL_LAYER_TYPE_NONE || (vl && ltype == vl->type) )
2847 vt->tools[vt->active_tool].ti.release(vl, event, vt->tools[vt->active_tool].state);
2850 /** End tool management ************************************/
2852 void vik_window_enable_layer_tool ( VikWindow *vw, gint layer_id, gint tool_id )
2854 gtk_action_activate ( gtk_action_group_get_action ( vw->action_group, vik_layer_get_interface(layer_id)->tools[tool_id].radioActionEntry.name ) );
2857 // Be careful with usage - as it may trigger actions being continually alternately by the menu and toolbar items
2858 // DON'T Use this from menu callback with toggle toolbar items!!
2859 static void toolbar_sync ( VikWindow *vw, const gchar *name, gboolean state )
2861 GtkToggleToolButton *tbutton = (GtkToggleToolButton *)toolbar_get_widget_by_name ( vw->viking_vtb, name );
2863 // Causes toggle signal action to be raised.
2864 gtk_toggle_tool_button_set_active ( tbutton, state );
2868 /* this function gets called whenever a menu is clicked */
2869 // Note old is not used
2870 static void menu_cb ( GtkAction *old, GtkAction *a, VikWindow *vw )
2872 // Ensure Toolbar kept in sync
2873 const gchar *name = gtk_action_get_name(a);
2874 toolbar_sync ( vw, name, TRUE );
2876 /* White Magic, my friends ... White Magic... */
2878 toolbox_activate(vw->vt, name);
2880 vw->viewport_cursor = (GdkCursor *)toolbox_get_cursor(vw->vt, name);
2882 if ( gtk_widget_get_window(GTK_WIDGET(vw->viking_vvp)) )
2883 /* We set cursor, even if it is NULL: it resets to default */
2884 gdk_window_set_cursor ( gtk_widget_get_window(GTK_WIDGET(vw->viking_vvp)), vw->viewport_cursor );
2886 if (!g_strcmp0(name, "Pan")) {
2887 vw->current_tool = TOOL_PAN;
2889 else if (!g_strcmp0(name, "Zoom")) {
2890 vw->current_tool = TOOL_ZOOM;
2892 else if (!g_strcmp0(name, "Ruler")) {
2893 vw->current_tool = TOOL_RULER;
2895 else if (!g_strcmp0(name, "Select")) {
2896 vw->current_tool = TOOL_SELECT;
2899 VikLayerTypeEnum layer_id;
2900 for (layer_id=0; layer_id<VIK_LAYER_NUM_TYPES; layer_id++) {
2901 for ( tool_id = 0; tool_id < vik_layer_get_interface(layer_id)->tools_count; tool_id++ ) {
2902 if (!g_strcmp0(vik_layer_get_interface(layer_id)->tools[tool_id].radioActionEntry.name, name)) {
2903 vw->current_tool = TOOL_LAYER;
2904 vw->tool_layer_id = layer_id;
2905 vw->tool_tool_id = tool_id;
2910 draw_status_tool ( vw );
2913 static void window_set_filename ( VikWindow *vw, const gchar *filename )
2918 g_free ( vw->filename );
2919 if ( filename == NULL )
2921 vw->filename = NULL;
2925 vw->filename = g_strdup(filename);
2928 /* Refresh window's title */
2929 file = window_get_filename ( vw );
2930 title = g_strdup_printf( "%s - Viking", file );
2931 gtk_window_set_title ( GTK_WINDOW(vw), title );
2935 static const gchar *window_get_filename ( VikWindow *vw )
2937 return vw->filename ? a_file_basename ( vw->filename ) : _("Untitled");
2940 GtkWidget *vik_window_get_drawmode_button ( VikWindow *vw, VikViewportDrawMode mode )
2942 GtkWidget *mode_button;
2945 #ifdef VIK_CONFIG_EXPEDIA
2946 case VIK_VIEWPORT_DRAWMODE_EXPEDIA: buttonname = "/ui/MainMenu/View/ModeExpedia"; break;
2948 case VIK_VIEWPORT_DRAWMODE_MERCATOR: buttonname = "/ui/MainMenu/View/ModeMercator"; break;
2949 case VIK_VIEWPORT_DRAWMODE_LATLON: buttonname = "/ui/MainMenu/View/ModeLatLon"; break;
2950 default: buttonname = "/ui/MainMenu/View/ModeUTM";
2952 mode_button = gtk_ui_manager_get_widget ( vw->uim, buttonname );
2953 g_assert ( mode_button );
2958 * vik_window_get_pan_move:
2959 * @vw: some VikWindow
2961 * Retrieves @vw's pan_move.
2963 * Should be removed as soon as possible.
2965 * Returns: @vw's pan_move
2969 gboolean vik_window_get_pan_move ( VikWindow *vw )
2971 return vw->pan_move;
2974 static void on_activate_recent_item (GtkRecentChooser *chooser,
2979 filename = gtk_recent_chooser_get_current_uri (chooser);
2980 if (filename != NULL)
2982 GFile *file = g_file_new_for_uri ( filename );
2983 gchar *path = g_file_get_path ( file );
2984 g_object_unref ( file );
2985 if ( self->filename )
2987 GSList *filenames = NULL;
2988 filenames = g_slist_append ( filenames, path );
2989 g_signal_emit ( G_OBJECT(self), window_signals[VW_OPENWINDOW_SIGNAL], 0, filenames );
2990 // NB: GSList & contents are freed by main.open_window
2993 vik_window_open_file ( self, path, TRUE );
3001 static void setup_recent_files (VikWindow *self)
3003 GtkRecentManager *manager;
3004 GtkRecentFilter *filter;
3005 GtkWidget *menu, *menu_item;
3007 filter = gtk_recent_filter_new ();
3008 /* gtk_recent_filter_add_application (filter, g_get_application_name()); */
3009 gtk_recent_filter_add_group(filter, "viking");
3011 manager = gtk_recent_manager_get_default ();
3012 menu = gtk_recent_chooser_menu_new_for_manager (manager);
3013 gtk_recent_chooser_set_sort_type (GTK_RECENT_CHOOSER (menu), GTK_RECENT_SORT_MRU);
3014 gtk_recent_chooser_add_filter (GTK_RECENT_CHOOSER (menu), filter);
3015 gtk_recent_chooser_set_limit (GTK_RECENT_CHOOSER (menu), a_vik_get_recent_number_files() );
3017 menu_item = gtk_ui_manager_get_widget (self->uim, "/ui/MainMenu/File/OpenRecentFile");
3018 gtk_menu_item_set_submenu (GTK_MENU_ITEM (menu_item), menu);
3020 g_signal_connect (G_OBJECT (menu), "item-activated",
3021 G_CALLBACK (on_activate_recent_item), (gpointer) self);
3027 static void update_recently_used_document (VikWindow *vw, const gchar *filename)
3029 /* Update Recently Used Document framework */
3030 GtkRecentManager *manager = gtk_recent_manager_get_default();
3031 GtkRecentData *recent_data = g_slice_new (GtkRecentData);
3032 gchar *groups[] = {"viking", NULL};
3033 GFile *file = g_file_new_for_commandline_arg(filename);
3034 gchar *uri = g_file_get_uri(file);
3035 gchar *basename = g_path_get_basename(filename);
3036 g_object_unref(file);
3039 recent_data->display_name = basename;
3040 recent_data->description = NULL;
3041 recent_data->mime_type = "text/x-gps-data";
3042 recent_data->app_name = (gchar *) g_get_application_name ();
3043 recent_data->app_exec = g_strjoin (" ", g_get_prgname (), "%f", NULL);
3044 recent_data->groups = groups;
3045 recent_data->is_private = FALSE;
3046 if (!gtk_recent_manager_add_full (manager, uri, recent_data))
3048 gchar *msg = g_strdup_printf (_("Unable to add '%s' to the list of recently used documents"), uri);
3049 vik_statusbar_set_message ( vw->viking_vs, VIK_STATUSBAR_INFO, msg );
3055 g_free (recent_data->app_exec);
3056 g_slice_free (GtkRecentData, recent_data);
3060 * Call this before doing things that may take a long time and otherwise not show any other feedback
3061 * such as loading and saving files
3063 void vik_window_set_busy_cursor ( VikWindow *vw )
3065 gdk_window_set_cursor ( gtk_widget_get_window(GTK_WIDGET(vw)), vw->busy_cursor );
3066 // Viewport has a separate cursor
3067 gdk_window_set_cursor ( gtk_widget_get_window(GTK_WIDGET(vw->viking_vvp)), vw->busy_cursor );
3068 // Ensure cursor updated before doing stuff
3069 while( gtk_events_pending() )
3070 gtk_main_iteration();
3073 void vik_window_clear_busy_cursor ( VikWindow *vw )
3075 gdk_window_set_cursor ( gtk_widget_get_window(GTK_WIDGET(vw)), NULL );
3076 // Restore viewport cursor
3077 gdk_window_set_cursor ( gtk_widget_get_window(GTK_WIDGET(vw->viking_vvp)), vw->viewport_cursor );
3080 void vik_window_open_file ( VikWindow *vw, const gchar *filename, gboolean change_filename )
3082 vik_window_set_busy_cursor ( vw );
3084 // Enable the *new* filename to be accessible by the Layers codez
3085 gchar *original_filename = g_strdup ( vw->filename );
3086 g_free ( vw->filename );
3087 vw->filename = g_strdup ( filename );
3088 gboolean success = FALSE;
3089 gboolean restore_original_filename = FALSE;
3091 vw->loaded_type = a_file_load ( vik_layers_panel_get_top_layer(vw->viking_vlp), vw->viking_vvp, filename );
3092 switch ( vw->loaded_type )
3094 case LOAD_TYPE_READ_FAILURE:
3095 a_dialog_error_msg ( GTK_WINDOW(vw), _("The file you requested could not be opened.") );
3097 case LOAD_TYPE_GPSBABEL_FAILURE:
3098 a_dialog_error_msg ( GTK_WINDOW(vw), _("GPSBabel is required to load files of this type or GPSBabel encountered problems.") );
3100 case LOAD_TYPE_GPX_FAILURE:
3101 a_dialog_error_msg_extra ( GTK_WINDOW(vw), _("Unable to load malformed GPX file %s"), filename );
3103 case LOAD_TYPE_UNSUPPORTED_FAILURE:
3104 a_dialog_error_msg_extra ( GTK_WINDOW(vw), _("Unsupported file type for %s"), filename );
3106 case LOAD_TYPE_VIK_FAILURE_NON_FATAL:
3108 // Since we can process .vik files with issues just show a warning in the status bar
3109 // Not that a user can do much about it... or tells them what this issue is yet...
3110 gchar *msg = g_strdup_printf (_("WARNING: issues encountered loading %s"), a_file_basename (filename) );
3111 vik_statusbar_set_message ( vw->viking_vs, VIK_STATUSBAR_INFO, msg );
3114 // No break, carry on to show any data
3115 case LOAD_TYPE_VIK_SUCCESS:
3117 restore_original_filename = TRUE; // NB Will actually get inverted by the 'success' component below
3118 GtkWidget *mode_button;
3120 if ( change_filename )
3121 window_set_filename ( vw, filename );
3122 mode_button = vik_window_get_drawmode_button ( vw, vik_viewport_get_drawmode ( vw->viking_vvp ) );
3123 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. */
3124 gtk_check_menu_item_set_active ( GTK_CHECK_MENU_ITEM(mode_button), TRUE );
3125 vw->only_updating_coord_mode_ui = FALSE;
3127 vik_layers_panel_change_coord_mode ( vw->viking_vlp, vik_viewport_get_coord_mode ( vw->viking_vvp ) );
3129 // Slightly long winded methods to align loaded viewport settings with the UI
3130 // Since the rewrite for toolbar + menu actions
3131 // there no longer exists a simple way to directly change the UI to a value for toggle settings
3132 // it only supports toggling the existing setting (otherwise get infinite loops in trying to align tb+menu elements)
3133 // Thus get state, compare them, if different then invert viewport setting and (re)sync the setting (via toggling)
3134 gboolean vp_state_scale = vik_viewport_get_draw_scale ( vw->viking_vvp );
3135 gboolean ui_state_scale = gtk_check_menu_item_get_active ( GTK_CHECK_MENU_ITEM(get_show_widget_by_name(vw, "ShowScale")) );
3136 if ( vp_state_scale != ui_state_scale ) {
3137 vik_viewport_set_draw_scale ( vw->viking_vvp, !vp_state_scale );
3138 toggle_draw_scale ( NULL, vw );
3140 gboolean vp_state_centermark = vik_viewport_get_draw_centermark ( vw->viking_vvp );
3141 gboolean ui_state_centermark = gtk_check_menu_item_get_active ( GTK_CHECK_MENU_ITEM(get_show_widget_by_name(vw, "ShowCenterMark")) );
3142 if ( vp_state_centermark != ui_state_centermark ) {
3143 vik_viewport_set_draw_centermark ( vw->viking_vvp, !vp_state_centermark );
3144 toggle_draw_centermark ( NULL, vw );
3146 gboolean vp_state_highlight = vik_viewport_get_draw_highlight ( vw->viking_vvp );
3147 gboolean ui_state_highlight = gtk_check_menu_item_get_active ( GTK_CHECK_MENU_ITEM(get_show_widget_by_name(vw, "ShowHighlight")) );
3148 if ( vp_state_highlight != ui_state_highlight ) {
3149 vik_viewport_set_draw_highlight ( vw->viking_vvp, !vp_state_highlight );
3150 toggle_draw_highlight ( NULL, vw );
3153 // NB No break, carry on to redraw
3154 //case LOAD_TYPE_OTHER_SUCCESS:
3157 // When LOAD_TYPE_OTHER_SUCCESS *only*, this will maintain the existing Viking project
3158 restore_original_filename = ! restore_original_filename;
3159 update_recently_used_document (vw, filename);
3164 if ( ! success || restore_original_filename )
3165 // Load didn't work or want to keep as the existing Viking project, keep using the original name
3166 window_set_filename ( vw, original_filename );
3167 g_free ( original_filename );
3169 vik_window_clear_busy_cursor ( vw );
3172 static void load_file ( GtkAction *a, VikWindow *vw )
3174 GSList *files = NULL;
3175 GSList *cur_file = NULL;
3177 if (!strcmp(gtk_action_get_name(a), "Open")) {
3180 else if (!strcmp(gtk_action_get_name(a), "Append")) {
3184 g_critical("Houston, we've had a problem.");
3188 if ( ! vw->open_dia )
3190 vw->open_dia = gtk_file_chooser_dialog_new (_("Please select a GPS data file to open. "),
3192 GTK_FILE_CHOOSER_ACTION_OPEN,
3193 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
3194 GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT,
3196 gchar *cwd = g_get_current_dir();
3198 gtk_file_chooser_set_current_folder ( GTK_FILE_CHOOSER(vw->open_dia), cwd );
3202 GtkFileFilter *filter;
3203 // NB file filters are listed this way for alphabetical ordering
3204 #ifdef VIK_CONFIG_GEOCACHES
3205 filter = gtk_file_filter_new ();
3206 gtk_file_filter_set_name( filter, _("Geocaching") );
3207 gtk_file_filter_add_pattern ( filter, "*.loc" ); // No MIME type available
3208 gtk_file_chooser_add_filter (GTK_FILE_CHOOSER(vw->open_dia), filter);
3211 filter = gtk_file_filter_new ();
3212 gtk_file_filter_set_name( filter, _("Google Earth") );
3213 gtk_file_filter_add_mime_type ( filter, "application/vnd.google-earth.kml+xml");
3214 gtk_file_chooser_add_filter (GTK_FILE_CHOOSER(vw->open_dia), filter);
3216 filter = gtk_file_filter_new ();
3217 gtk_file_filter_set_name( filter, _("GPX") );
3218 gtk_file_filter_add_pattern ( filter, "*.gpx" ); // No MIME type available
3219 gtk_file_chooser_add_filter (GTK_FILE_CHOOSER(vw->open_dia), filter);
3221 filter = gtk_file_filter_new ();
3222 gtk_file_filter_set_name ( filter, _("JPG") );
3223 gtk_file_filter_add_mime_type ( filter, "image/jpeg");
3224 gtk_file_chooser_add_filter (GTK_FILE_CHOOSER(vw->open_dia), filter);
3226 filter = gtk_file_filter_new ();
3227 gtk_file_filter_set_name( filter, _("Viking") );
3228 gtk_file_filter_add_pattern ( filter, "*.vik" );
3229 gtk_file_filter_add_pattern ( filter, "*.viking" );
3230 gtk_file_chooser_add_filter (GTK_FILE_CHOOSER(vw->open_dia), filter);
3232 // NB could have filters for gpspoint (*.gps,*.gpsoint?) + gpsmapper (*.gsm,*.gpsmapper?)
3233 // However assume this are barely used and thus not worthy of inclusion
3234 // as they'll just make the options too many and have no clear file pattern
3235 // one can always use the all option
3236 filter = gtk_file_filter_new ();
3237 gtk_file_filter_set_name( filter, _("All") );
3238 gtk_file_filter_add_pattern ( filter, "*" );
3239 gtk_file_chooser_add_filter (GTK_FILE_CHOOSER(vw->open_dia), filter);
3240 // Default to any file - same as before open filters were added
3241 gtk_file_chooser_set_filter (GTK_FILE_CHOOSER(vw->open_dia), filter);
3243 gtk_file_chooser_set_select_multiple ( GTK_FILE_CHOOSER(vw->open_dia), TRUE );
3244 gtk_window_set_transient_for ( GTK_WINDOW(vw->open_dia), GTK_WINDOW(vw) );
3245 gtk_window_set_destroy_with_parent ( GTK_WINDOW(vw->open_dia), TRUE );
3247 if ( gtk_dialog_run ( GTK_DIALOG(vw->open_dia) ) == GTK_RESPONSE_ACCEPT )
3249 gtk_widget_hide ( vw->open_dia );
3250 #ifdef VIKING_PROMPT_IF_MODIFIED
3251 if ( (vw->modified || vw->filename) && newwindow )
3253 if ( vw->filename && newwindow )
3255 g_signal_emit ( G_OBJECT(vw), window_signals[VW_OPENWINDOW_SIGNAL], 0, gtk_file_chooser_get_filenames (GTK_FILE_CHOOSER(vw->open_dia) ) );
3257 files = gtk_file_chooser_get_filenames (GTK_FILE_CHOOSER(vw->open_dia) );
3258 gboolean change_fn = newwindow && (g_slist_length(files)==1); /* only change fn if one file */
3259 gboolean first_vik_file = TRUE;
3261 while ( cur_file ) {
3263 gchar *file_name = cur_file->data;
3264 if ( newwindow && check_file_magic_vik ( file_name ) ) {
3265 // Load first of many .vik files in current window
3266 if ( first_vik_file ) {
3267 vik_window_open_file ( vw, file_name, TRUE );
3268 first_vik_file = FALSE;
3271 // Load each subsequent .vik file in a separate window
3272 VikWindow *newvw = vik_window_new_window ();
3274 vik_window_open_file ( newvw, file_name, TRUE );
3279 vik_window_open_file ( vw, file_name, change_fn );
3282 cur_file = g_slist_next (cur_file);
3284 g_slist_free (files);
3288 gtk_widget_hide ( vw->open_dia );
3291 static gboolean save_file_as ( GtkAction *a, VikWindow *vw )
3293 gboolean rv = FALSE;
3295 if ( ! vw->save_dia )
3297 vw->save_dia = gtk_file_chooser_dialog_new (_("Save as Viking File."),
3299 GTK_FILE_CHOOSER_ACTION_SAVE,
3300 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
3301 GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT,
3303 gchar *cwd = g_get_current_dir();
3305 gtk_file_chooser_set_current_folder ( GTK_FILE_CHOOSER(vw->save_dia), cwd );
3309 GtkFileFilter *filter;
3310 filter = gtk_file_filter_new ();
3311 gtk_file_filter_set_name( filter, _("All") );
3312 gtk_file_filter_add_pattern ( filter, "*" );
3313 gtk_file_chooser_add_filter (GTK_FILE_CHOOSER(vw->save_dia), filter);
3315 filter = gtk_file_filter_new ();
3316 gtk_file_filter_set_name( filter, _("Viking") );
3317 gtk_file_filter_add_pattern ( filter, "*.vik" );
3318 gtk_file_filter_add_pattern ( filter, "*.viking" );
3319 gtk_file_chooser_add_filter (GTK_FILE_CHOOSER(vw->save_dia), filter);
3320 // Default to a Viking file
3321 gtk_file_chooser_set_filter (GTK_FILE_CHOOSER(vw->save_dia), filter);
3323 gtk_window_set_transient_for ( GTK_WINDOW(vw->save_dia), GTK_WINDOW(vw) );
3324 gtk_window_set_destroy_with_parent ( GTK_WINDOW(vw->save_dia), TRUE );
3326 // Auto append / replace extension with '.vik' to the suggested file name as it's going to be a Viking File
3327 gchar* auto_save_name = g_strdup ( window_get_filename ( vw ) );
3328 if ( ! a_file_check_ext ( auto_save_name, ".vik" ) )
3329 auto_save_name = g_strconcat ( auto_save_name, ".vik", NULL );
3331 gtk_file_chooser_set_current_name (GTK_FILE_CHOOSER(vw->save_dia), auto_save_name);
3333 while ( gtk_dialog_run ( GTK_DIALOG(vw->save_dia) ) == GTK_RESPONSE_ACCEPT )
3335 fn = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER(vw->save_dia) );
3336 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 ) ) )
3338 window_set_filename ( vw, fn );
3339 rv = window_save ( vw );
3340 vw->modified = FALSE;
3344 g_free ( auto_save_name );
3345 gtk_widget_hide ( vw->save_dia );
3349 static gboolean window_save ( VikWindow *vw )
3351 vik_window_set_busy_cursor ( vw );
3352 gboolean success = TRUE;
3354 if ( a_file_save ( vik_layers_panel_get_top_layer ( vw->viking_vlp ), vw->viking_vvp, vw->filename ) )
3356 update_recently_used_document ( vw, vw->filename );
3360 a_dialog_error_msg ( GTK_WINDOW(vw), _("The filename you requested could not be opened for writing.") );
3363 vik_window_clear_busy_cursor ( vw );
3367 static gboolean save_file ( GtkAction *a, VikWindow *vw )
3369 if ( ! vw->filename )
3370 return save_file_as ( NULL, vw );
3373 vw->modified = FALSE;
3374 return window_save ( vw );
3381 * Export all TRW Layers in the list to individual files in the specified directory
3383 * Returns: %TRUE on success
3385 static gboolean export_to ( VikWindow *vw, GList *gl, VikFileType_t vft, const gchar *dir, const gchar *extension )
3387 gboolean success = TRUE;
3389 gint export_count = 0;
3391 vik_window_set_busy_cursor ( vw );
3395 gchar *fn = g_strconcat ( dir, G_DIR_SEPARATOR_S, VIK_LAYER(gl->data)->name, extension, NULL );
3397 // Some protection in attempting to write too many same named files
3398 // As this will get horribly slow...
3399 gboolean safe = FALSE;
3401 while ( ii < 5000 ) {
3402 if ( g_file_test ( fn, G_FILE_TEST_EXISTS ) ) {
3405 fn = g_strdup_printf ( "%s%s%s#%03d%s", dir, G_DIR_SEPARATOR_S, VIK_LAYER(gl->data)->name, ii, extension );
3416 // NB: We allow exporting empty layers
3418 gboolean this_success = a_file_export ( VIK_TRW_LAYER(gl->data), fn, vft, NULL, TRUE );
3420 // Show some progress
3421 if ( this_success ) {
3423 gchar *message = g_strdup_printf ( _("Exporting to file: %s"), fn );
3424 vik_statusbar_set_message ( vw->viking_vs, VIK_STATUSBAR_INFO, message );
3425 while ( gtk_events_pending() )
3426 gtk_main_iteration ();
3430 success = success && this_success;
3434 gl = g_list_next ( gl );
3437 vik_window_clear_busy_cursor ( vw );
3439 // Confirm what happened.
3440 gchar *message = g_strdup_printf ( _("Exported files: %d"), export_count );
3441 vik_statusbar_set_message ( vw->viking_vs, VIK_STATUSBAR_INFO, message );
3447 static void export_to_common ( VikWindow *vw, VikFileType_t vft, const gchar *extension )
3449 GList *gl = vik_layers_panel_get_all_layers_of_type ( vw->viking_vlp, VIK_LAYER_TRW, TRUE );
3452 a_dialog_info_msg ( GTK_WINDOW(vw), _("Nothing to Export!") );
3456 GtkWidget *dialog = gtk_file_chooser_dialog_new ( _("Export to directory"),
3458 GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER,
3460 GTK_RESPONSE_REJECT,
3462 GTK_RESPONSE_ACCEPT,
3464 gtk_window_set_transient_for ( GTK_WINDOW(dialog), GTK_WINDOW(vw) );
3465 gtk_window_set_destroy_with_parent ( GTK_WINDOW(dialog), TRUE );
3466 gtk_window_set_modal ( GTK_WINDOW(dialog), TRUE );
3468 gtk_widget_show_all ( dialog );
3470 if ( gtk_dialog_run ( GTK_DIALOG(dialog) ) == GTK_RESPONSE_ACCEPT ) {
3471 gchar *dir = gtk_file_chooser_get_filename ( GTK_FILE_CHOOSER(dialog) );
3472 gtk_widget_destroy ( dialog );
3474 if ( !export_to ( vw, gl, vft, dir, extension ) )
3475 a_dialog_error_msg ( GTK_WINDOW(vw),_("Could not convert all files") );
3480 gtk_widget_destroy ( dialog );
3485 static void export_to_gpx ( GtkAction *a, VikWindow *vw )
3487 export_to_common ( vw, FILE_TYPE_GPX, ".gpx" );
3490 static void export_to_kml ( GtkAction *a, VikWindow *vw )
3492 export_to_common ( vw, FILE_TYPE_KML, ".kml" );
3495 #if !GLIB_CHECK_VERSION(2,26,0)
3496 typedef struct stat GStatBuf;
3499 static void file_properties_cb ( GtkAction *a, VikWindow *vw )
3501 gchar *message = NULL;
3502 if ( vw->filename ) {
3503 if ( g_file_test ( vw->filename, G_FILE_TEST_EXISTS ) ) {
3504 // Get some timestamp information of the file
3506 if ( g_stat ( vw->filename, &stat_buf ) == 0 ) {
3508 strftime ( time_buf, sizeof(time_buf), "%c", gmtime((const time_t *)&stat_buf.st_mtime) );
3510 gint byte_size = stat_buf.st_size;
3511 #if GLIB_CHECK_VERSION(2,30,0)
3512 size = g_format_size_full ( byte_size, G_FORMAT_SIZE_DEFAULT );
3514 size = g_format_size_for_display ( byte_size );
3516 message = g_strdup_printf ( "%s\n\n%s\n\n%s", vw->filename, time_buf, size );
3521 message = g_strdup ( _("File not accessible") );
3524 message = g_strdup ( _("No Viking File") );
3527 a_dialog_info_msg ( GTK_WINDOW(vw), message );
3531 static void my_acquire ( VikWindow *vw, VikDataSourceInterface *datasource )
3533 vik_datasource_mode_t mode = datasource->mode;
3534 if ( mode == VIK_DATASOURCE_AUTO_LAYER_MANAGEMENT )
3535 mode = VIK_DATASOURCE_CREATENEWLAYER;
3536 a_acquire ( vw, vw->viking_vlp, vw->viking_vvp, mode, datasource, NULL, NULL );
3539 static void acquire_from_gps ( GtkAction *a, VikWindow *vw )
3541 my_acquire ( vw, &vik_datasource_gps_interface );
3544 static void acquire_from_file ( GtkAction *a, VikWindow *vw )
3546 my_acquire ( vw, &vik_datasource_file_interface );
3549 static void acquire_from_geojson ( GtkAction *a, VikWindow *vw )
3551 my_acquire ( vw, &vik_datasource_geojson_interface );
3554 static void acquire_from_routing ( GtkAction *a, VikWindow *vw )
3556 my_acquire ( vw, &vik_datasource_routing_interface );
3559 #ifdef VIK_CONFIG_OPENSTREETMAP
3560 static void acquire_from_osm ( GtkAction *a, VikWindow *vw )
3562 my_acquire ( vw, &vik_datasource_osm_interface );
3565 static void acquire_from_my_osm ( GtkAction *a, VikWindow *vw )
3567 my_acquire ( vw, &vik_datasource_osm_my_traces_interface );
3571 #ifdef VIK_CONFIG_GEOCACHES
3572 static void acquire_from_gc ( GtkAction *a, VikWindow *vw )
3574 my_acquire ( vw, &vik_datasource_gc_interface );
3578 #ifdef VIK_CONFIG_GEOTAG
3579 static void acquire_from_geotag ( GtkAction *a, VikWindow *vw )
3581 my_acquire ( vw, &vik_datasource_geotag_interface );
3585 #ifdef VIK_CONFIG_GEONAMES
3586 static void acquire_from_wikipedia ( GtkAction *a, VikWindow *vw )
3588 my_acquire ( vw, &vik_datasource_wikipedia_interface );
3592 static void acquire_from_url ( GtkAction *a, VikWindow *vw )
3594 my_acquire ( vw, &vik_datasource_url_interface );
3597 static void goto_default_location( GtkAction *a, VikWindow *vw)
3600 ll.lat = a_vik_get_default_lat();
3601 ll.lon = a_vik_get_default_long();
3602 vik_viewport_set_center_latlon(vw->viking_vvp, &ll, TRUE);
3603 vik_layers_panel_emit_update(vw->viking_vlp);
3607 static void goto_address( GtkAction *a, VikWindow *vw)
3609 a_vik_goto ( vw, vw->viking_vvp );
3610 vik_layers_panel_emit_update ( vw->viking_vlp );
3613 static void mapcache_flush_cb ( GtkAction *a, VikWindow *vw )
3618 static void menu_copy_centre_cb ( GtkAction *a, VikWindow *vw )
3620 const VikCoord* coord;
3622 gchar *lat = NULL, *lon = NULL;
3624 coord = vik_viewport_get_center ( vw->viking_vvp );
3625 vik_coord_to_utm ( coord, &utm );
3627 gboolean full_format = FALSE;
3628 a_settings_get_boolean ( VIK_SETTINGS_WIN_COPY_CENTRE_FULL_FORMAT, &full_format );
3631 // Bells & Whistles - may include degrees, minutes and second symbols
3632 get_location_strings ( vw, utm, &lat, &lon );
3634 // Simple x.xx y.yy format
3636 a_coords_utm_to_latlon ( &utm, &ll );
3637 lat = g_strdup_printf ( "%.6f", ll.lat );
3638 lon = g_strdup_printf ( "%.6f", ll.lon );
3641 gchar *msg = g_strdup_printf ( "%s %s", lat, lon );
3645 a_clipboard_copy ( VIK_CLIPBOARD_DATA_TEXT, 0, 0, 0, msg, NULL );
3650 static void layer_defaults_cb ( GtkAction *a, VikWindow *vw )
3652 gchar **texts = g_strsplit ( gtk_action_get_name(a), "Layer", 0 );
3655 return; // Internally broken :(
3657 if ( ! a_layer_defaults_show_window ( GTK_WINDOW(vw), texts[1] ) )
3658 a_dialog_info_msg ( GTK_WINDOW(vw), _("This layer has no configurable properties.") );
3659 // NB no update needed
3661 g_strfreev ( texts );
3664 static void preferences_change_update ( VikWindow *vw, gpointer data )
3666 // Want to update all TrackWaypoint layers
3667 GList *layers = vik_layers_panel_get_all_layers_of_type ( vw->viking_vlp, VIK_LAYER_TRW, TRUE );
3673 // Reset the individual waypoints themselves due to the preferences change
3674 VikTrwLayer *vtl = VIK_TRW_LAYER(layers->data);
3675 vik_trw_layer_reset_waypoints ( vtl );
3676 layers = g_list_next ( layers );
3679 g_list_free ( layers );
3684 static void preferences_cb ( GtkAction *a, VikWindow *vw )
3686 gboolean wp_icon_size = a_vik_get_use_large_waypoint_icons();
3688 a_preferences_show_window ( GTK_WINDOW(vw) );
3690 // Has the waypoint size setting changed?
3691 if (wp_icon_size != a_vik_get_use_large_waypoint_icons()) {
3692 // Delete icon indexing 'cache' and so automatically regenerates with the new setting when changed
3693 clear_garmin_icon_syms ();
3695 // Update all windows
3696 g_slist_foreach ( window_list, (GFunc) preferences_change_update, NULL );
3699 // Ensure TZ Lookup initialized
3700 if ( a_vik_get_time_ref_frame() == VIK_TIME_REF_WORLD )
3701 vu_setup_lat_lon_tz_lookup();
3703 toolbar_apply_settings ( vw->viking_vtb, vw->main_vbox, vw->menu_hbox, TRUE );
3706 static void default_location_cb ( GtkAction *a, VikWindow *vw )
3708 /* Simplistic repeat of preference setting
3709 Only the name & type are important for setting the preference via this 'external' way */
3710 VikLayerParam pref_lat[] = {
3711 { VIK_LAYER_NUM_TYPES,
3712 VIKING_PREFERENCES_NAMESPACE "default_latitude",
3713 VIK_LAYER_PARAM_DOUBLE,
3716 VIK_LAYER_WIDGET_SPINBUTTON,
3725 VikLayerParam pref_lon[] = {
3726 { VIK_LAYER_NUM_TYPES,
3727 VIKING_PREFERENCES_NAMESPACE "default_longitude",
3728 VIK_LAYER_PARAM_DOUBLE,
3731 VIK_LAYER_WIDGET_SPINBUTTON,
3741 /* Get current center */
3743 vik_coord_to_latlon ( vik_viewport_get_center ( vw->viking_vvp ), &ll );
3745 /* Apply to preferences */
3746 VikLayerParamData vlp_data;
3747 vlp_data.d = ll.lat;
3748 a_preferences_run_setparam (vlp_data, pref_lat);
3749 vlp_data.d = ll.lon;
3750 a_preferences_run_setparam (vlp_data, pref_lon);
3751 /* Remember to save */
3752 a_preferences_save_to_file();
3755 static void clear_cb ( GtkAction *a, VikWindow *vw )
3757 vik_layers_panel_clear ( vw->viking_vlp );
3758 window_set_filename ( vw, NULL );
3762 static void window_close ( GtkAction *a, VikWindow *vw )
3764 if ( ! delete_event ( vw ) )
3765 gtk_widget_destroy ( GTK_WIDGET(vw) );
3768 static gboolean save_file_and_exit ( GtkAction *a, VikWindow *vw )
3770 if (save_file( NULL, vw)) {
3771 window_close( NULL, vw);
3778 static void zoom_to_cb ( GtkAction *a, VikWindow *vw )
3780 gdouble xmpp = vik_viewport_get_xmpp ( vw->viking_vvp ), ympp = vik_viewport_get_ympp ( vw->viking_vvp );
3781 if ( a_dialog_custom_zoom ( GTK_WINDOW(vw), &xmpp, &ympp ) )
3783 vik_viewport_set_xmpp ( vw->viking_vvp, xmpp );
3784 vik_viewport_set_ympp ( vw->viking_vvp, ympp );
3789 static void save_image_file ( VikWindow *vw, const gchar *fn, guint w, guint h, gdouble zoom, gboolean save_as_png )
3791 /* more efficient way: stuff draws directly to pixbuf (fork viewport) */
3792 GdkPixbuf *pixbuf_to_save;
3793 gdouble old_xmpp, old_ympp;
3794 GError *error = NULL;
3796 GtkWidget *msgbox = gtk_message_dialog_new ( GTK_WINDOW(vw),
3797 GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
3800 _("Generating image file...") );
3802 g_signal_connect_swapped (msgbox, "response", G_CALLBACK (gtk_widget_destroy), msgbox);
3803 // Ensure dialog shown
3804 gtk_widget_show_all ( msgbox );
3806 vik_statusbar_set_message ( vw->viking_vs, VIK_STATUSBAR_INFO, _("Generating image file...") );
3807 while ( gtk_events_pending() )
3808 gtk_main_iteration ();
3809 // Despite many efforts & variations, GTK on my Linux system doesn't show the actual msgbox contents :(
3810 // At least the empty box can give a clue something's going on + the statusbar msg...
3811 // Windows version under Wine OK!
3813 /* backup old zoom & set new */
3814 old_xmpp = vik_viewport_get_xmpp ( vw->viking_vvp );
3815 old_ympp = vik_viewport_get_ympp ( vw->viking_vvp );
3816 vik_viewport_set_zoom ( vw->viking_vvp, zoom );
3818 /* reset width and height: */
3819 vik_viewport_configure_manually ( vw->viking_vvp, w, h );
3821 /* draw all layers */
3824 /* save buffer as file. */
3825 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);
3826 if ( !pixbuf_to_save ) {
3827 g_warning("Failed to generate internal pixmap size: %d x %d", w, h);
3828 gtk_message_dialog_set_markup ( GTK_MESSAGE_DIALOG(msgbox), _("Failed to generate internal image.\n\nTry creating a smaller image.") );
3832 gdk_pixbuf_save ( pixbuf_to_save, fn, save_as_png ? "png" : "jpeg", &error, NULL );
3835 g_warning("Unable to write to file %s: %s", fn, error->message );
3836 gtk_message_dialog_set_markup ( GTK_MESSAGE_DIALOG(msgbox), _("Failed to generate image file.") );
3837 g_error_free (error);
3841 gtk_message_dialog_set_markup ( GTK_MESSAGE_DIALOG(msgbox), _("Image file generated.") );
3843 g_object_unref ( G_OBJECT(pixbuf_to_save) );
3846 vik_statusbar_set_message ( vw->viking_vs, VIK_STATUSBAR_INFO, "" );
3847 gtk_dialog_add_button ( GTK_DIALOG(msgbox), GTK_STOCK_OK, GTK_RESPONSE_OK );
3848 gtk_dialog_run ( GTK_DIALOG(msgbox) ); // Don't care about the result
3850 /* pretend like nothing happened ;) */
3851 vik_viewport_set_xmpp ( vw->viking_vvp, old_xmpp );
3852 vik_viewport_set_ympp ( vw->viking_vvp, old_ympp );
3853 vik_viewport_configure ( vw->viking_vvp );
3857 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 )
3859 gulong size = sizeof(gchar) * (strlen(fn) + 15);
3860 gchar *name_of_file = g_malloc ( size );
3862 struct UTM utm_orig, utm;
3864 /* *** copied from above *** */
3865 GdkPixbuf *pixbuf_to_save;
3866 gdouble old_xmpp, old_ympp;
3867 GError *error = NULL;
3869 /* backup old zoom & set new */
3870 old_xmpp = vik_viewport_get_xmpp ( vw->viking_vvp );
3871 old_ympp = vik_viewport_get_ympp ( vw->viking_vvp );
3872 vik_viewport_set_zoom ( vw->viking_vvp, zoom );
3874 /* reset width and height: do this only once for all images (same size) */
3875 vik_viewport_configure_manually ( vw->viking_vvp, w, h );
3876 /* *** end copy from above *** */
3878 g_assert ( vik_viewport_get_coord_mode ( vw->viking_vvp ) == VIK_COORD_UTM );
3882 utm_orig = *((const struct UTM *)vik_viewport_get_center ( vw->viking_vvp ));
3884 for ( y = 1; y <= tiles_h; y++ )
3886 for ( x = 1; x <= tiles_w; x++ )
3888 g_snprintf ( name_of_file, size, "%s%cy%d-x%d.%s", fn, G_DIR_SEPARATOR, y, x, save_as_png ? "png" : "jpg" );
3890 if ( tiles_w & 0x1 )
3891 utm.easting += ((gdouble)x - ceil(((gdouble)tiles_w)/2)) * (w*zoom);
3893 utm.easting += ((gdouble)x - (((gdouble)tiles_w)+1)/2) * (w*zoom);
3894 if ( tiles_h & 0x1 ) /* odd */
3895 utm.northing -= ((gdouble)y - ceil(((gdouble)tiles_h)/2)) * (h*zoom);
3897 utm.northing -= ((gdouble)y - (((gdouble)tiles_h)+1)/2) * (h*zoom);
3899 /* move to correct place. */
3900 vik_viewport_set_center_utm ( vw->viking_vvp, &utm, FALSE );
3904 /* save buffer as file. */
3905 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);
3906 gdk_pixbuf_save ( pixbuf_to_save, name_of_file, save_as_png ? "png" : "jpeg", &error, NULL );
3909 gchar *msg = g_strdup_printf (_("Unable to write to file %s: %s"), name_of_file, error->message );
3910 vik_statusbar_set_message ( vw->viking_vs, VIK_STATUSBAR_INFO, msg );
3912 g_error_free (error);
3915 g_object_unref ( G_OBJECT(pixbuf_to_save) );
3919 vik_viewport_set_center_utm ( vw->viking_vvp, &utm_orig, FALSE );
3920 vik_viewport_set_xmpp ( vw->viking_vvp, old_xmpp );
3921 vik_viewport_set_ympp ( vw->viking_vvp, old_ympp );
3922 vik_viewport_configure ( vw->viking_vvp );
3925 g_free ( name_of_file );
3928 static void draw_to_image_file_current_window_cb(GtkWidget* widget,GdkEventButton *event,gpointer *pass_along)
3930 VikWindow *vw = VIK_WINDOW(pass_along[0]);
3931 GtkSpinButton *width_spin = GTK_SPIN_BUTTON(pass_along[1]), *height_spin = GTK_SPIN_BUTTON(pass_along[2]);
3933 gint active = gtk_combo_box_get_active ( GTK_COMBO_BOX(pass_along[3]) );
3934 gdouble zoom = pow (2, active-2 );
3936 gdouble width_min, width_max, height_min, height_max;
3939 gtk_spin_button_get_range ( width_spin, &width_min, &width_max );
3940 gtk_spin_button_get_range ( height_spin, &height_min, &height_max );
3942 /* TODO: support for xzoom and yzoom values */
3943 width = vik_viewport_get_width ( vw->viking_vvp ) * vik_viewport_get_xmpp ( vw->viking_vvp ) / zoom;
3944 height = vik_viewport_get_height ( vw->viking_vvp ) * vik_viewport_get_xmpp ( vw->viking_vvp ) / zoom;
3946 if ( width > width_max || width < width_min || height > height_max || height < height_min )
3947 a_dialog_info_msg ( GTK_WINDOW(vw), _("Viewable region outside allowable pixel size bounds for image. Clipping width/height values.") );
3949 gtk_spin_button_set_value ( width_spin, width );
3950 gtk_spin_button_set_value ( height_spin, height );
3953 static void draw_to_image_file_total_area_cb (GtkSpinButton *spinbutton, gpointer *pass_along)
3955 GtkSpinButton *width_spin = GTK_SPIN_BUTTON(pass_along[1]), *height_spin = GTK_SPIN_BUTTON(pass_along[2]);
3957 gint active = gtk_combo_box_get_active ( GTK_COMBO_BOX(pass_along[3]) );
3958 gdouble zoom = pow (2, active-2 );
3962 w = gtk_spin_button_get_value(width_spin) * zoom;
3963 h = gtk_spin_button_get_value(height_spin) * zoom;
3964 if (pass_along[4]) /* save many images; find TOTAL area covered */
3966 w *= gtk_spin_button_get_value(GTK_SPIN_BUTTON(pass_along[4]));
3967 h *= gtk_spin_button_get_value(GTK_SPIN_BUTTON(pass_along[5]));
3969 vik_units_distance_t dist_units = a_vik_get_units_distance ();
3970 switch (dist_units) {
3971 case VIK_UNITS_DISTANCE_KILOMETRES:
3972 label_text = g_strdup_printf ( _("Total area: %ldm x %ldm (%.3f sq. km)"), (glong)w, (glong)h, (w*h/1000000));
3974 case VIK_UNITS_DISTANCE_MILES:
3975 label_text = g_strdup_printf ( _("Total area: %ldm x %ldm (%.3f sq. miles)"), (glong)w, (glong)h, (w*h/2589988.11));
3977 case VIK_UNITS_DISTANCE_NAUTICAL_MILES:
3978 label_text = g_strdup_printf ( _("Total area: %ldm x %ldm (%.3f sq. NM)"), (glong)w, (glong)h, (w*h/(1852.0*1852.0)));
3981 label_text = g_strdup_printf ("Just to keep the compiler happy");
3982 g_critical("Houston, we've had a problem. distance=%d", dist_units);
3985 gtk_label_set_text(GTK_LABEL(pass_along[6]), label_text);
3986 g_free ( label_text );
3990 * Get an allocated filename (or directory as specified)
3992 static gchar* draw_image_filename ( VikWindow *vw, gboolean one_image_only )
3995 if ( one_image_only )
3998 if (!vw->save_img_dia) {
3999 vw->save_img_dia = gtk_file_chooser_dialog_new (_("Save Image"),
4001 GTK_FILE_CHOOSER_ACTION_SAVE,
4002 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
4003 GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT,
4006 gchar *cwd = g_get_current_dir();
4008 gtk_file_chooser_set_current_folder ( GTK_FILE_CHOOSER(vw->save_img_dia), cwd );
4012 GtkFileChooser *chooser = GTK_FILE_CHOOSER ( vw->save_img_dia );
4014 GtkFileFilter *filter;
4015 filter = gtk_file_filter_new ();
4016 gtk_file_filter_set_name ( filter, _("All") );
4017 gtk_file_filter_add_pattern ( filter, "*" );
4018 gtk_file_chooser_add_filter ( chooser, filter );
4020 filter = gtk_file_filter_new ();
4021 gtk_file_filter_set_name ( filter, _("JPG") );
4022 gtk_file_filter_add_mime_type ( filter, "image/jpeg");
4023 gtk_file_chooser_add_filter ( chooser, filter );
4025 if ( !vw->draw_image_save_as_png )
4026 gtk_file_chooser_set_filter ( chooser, filter );
4028 filter = gtk_file_filter_new ();
4029 gtk_file_filter_set_name ( filter, _("PNG") );
4030 gtk_file_filter_add_mime_type ( filter, "image/png");
4031 gtk_file_chooser_add_filter ( chooser, filter );
4033 if ( vw->draw_image_save_as_png )
4034 gtk_file_chooser_set_filter ( chooser, filter );
4036 gtk_window_set_transient_for ( GTK_WINDOW(vw->save_img_dia), GTK_WINDOW(vw) );
4037 gtk_window_set_destroy_with_parent ( GTK_WINDOW(vw->save_img_dia), TRUE );
4040 if ( gtk_dialog_run ( GTK_DIALOG(vw->save_img_dia) ) == GTK_RESPONSE_ACCEPT ) {
4041 fn = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER(vw->save_img_dia) );
4042 if ( g_file_test ( fn, G_FILE_TEST_EXISTS ) )
4043 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 ) ) )
4046 gtk_widget_hide ( vw->save_img_dia );
4050 // For some reason this method is only written to work in UTM...
4051 if ( vik_viewport_get_coord_mode(vw->viking_vvp) != VIK_COORD_UTM ) {
4052 a_dialog_error_msg ( GTK_WINDOW(vw), _("You must be in UTM mode to use this feature") );
4056 if (!vw->save_img_dir_dia) {
4057 vw->save_img_dir_dia = gtk_file_chooser_dialog_new (_("Choose a directory to hold images"),
4059 GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER,
4060 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
4061 GTK_STOCK_OK, GTK_RESPONSE_ACCEPT,
4063 gtk_window_set_transient_for ( GTK_WINDOW(vw->save_img_dir_dia), GTK_WINDOW(vw) );
4064 gtk_window_set_destroy_with_parent ( GTK_WINDOW(vw->save_img_dir_dia), TRUE );
4067 if ( gtk_dialog_run ( GTK_DIALOG(vw->save_img_dir_dia) ) == GTK_RESPONSE_ACCEPT ) {
4068 fn = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER(vw->save_img_dir_dia) );
4070 gtk_widget_hide ( vw->save_img_dir_dia );
4075 static void draw_to_image_file ( VikWindow *vw, gboolean one_image_only )
4077 /* todo: default for answers inside VikWindow or static (thruout instance) */
4078 GtkWidget *dialog = gtk_dialog_new_with_buttons ( _("Save to Image File"), GTK_WINDOW(vw),
4079 GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
4081 GTK_RESPONSE_REJECT,
4083 GTK_RESPONSE_ACCEPT,
4085 GtkWidget *width_label, *width_spin, *height_label, *height_spin;
4086 GtkWidget *png_radio, *jpeg_radio;
4087 GtkWidget *current_window_button;
4088 gpointer current_window_pass_along[7];
4089 GtkWidget *zoom_label, *zoom_combo;
4090 GtkWidget *total_size_label;
4092 /* only used if (!one_image_only) */
4093 GtkWidget *tiles_width_spin = NULL, *tiles_height_spin = NULL;
4095 width_label = gtk_label_new ( _("Width (pixels):") );
4096 width_spin = gtk_spin_button_new ( GTK_ADJUSTMENT(gtk_adjustment_new ( vw->draw_image_width, 10, 50000, 10, 100, 0 )), 10, 0 );
4097 height_label = gtk_label_new ( _("Height (pixels):") );
4098 height_spin = gtk_spin_button_new ( GTK_ADJUSTMENT(gtk_adjustment_new ( vw->draw_image_height, 10, 50000, 10, 100, 0 )), 10, 0 );
4100 GtkWidget *win_warning_label = gtk_label_new ( _("WARNING: USING LARGE IMAGES OVER 10000x10000\nMAY CRASH THE PROGRAM!") );
4102 zoom_label = gtk_label_new ( _("Zoom (meters per pixel):") );
4103 /* TODO: separate xzoom and yzoom factors */
4104 zoom_combo = create_zoom_combo_all_levels();
4106 gdouble mpp = vik_viewport_get_xmpp(vw->viking_vvp);
4107 gint active = 2 + round ( log (mpp) / log (2) );
4109 // Can we not hard code size here?
4114 gtk_combo_box_set_active ( GTK_COMBO_BOX(zoom_combo), active );
4116 total_size_label = gtk_label_new ( NULL );
4118 current_window_button = gtk_button_new_with_label ( _("Area in current viewable window") );
4119 current_window_pass_along [0] = vw;
4120 current_window_pass_along [1] = width_spin;
4121 current_window_pass_along [2] = height_spin;
4122 current_window_pass_along [3] = zoom_combo;
4123 current_window_pass_along [4] = NULL; /* used for one_image_only != 1 */
4124 current_window_pass_along [5] = NULL;
4125 current_window_pass_along [6] = total_size_label;
4126 g_signal_connect ( G_OBJECT(current_window_button), "button_press_event", G_CALLBACK(draw_to_image_file_current_window_cb), current_window_pass_along );
4128 png_radio = gtk_radio_button_new_with_label ( NULL, _("Save as PNG") );
4129 jpeg_radio = gtk_radio_button_new_with_label_from_widget ( GTK_RADIO_BUTTON(png_radio), _("Save as JPEG") );
4131 gtk_box_pack_start (GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), png_radio, FALSE, FALSE, 0);
4132 gtk_box_pack_start (GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), jpeg_radio, FALSE, FALSE, 0);
4134 if ( ! vw->draw_image_save_as_png )
4135 gtk_toggle_button_set_active ( GTK_TOGGLE_BUTTON(jpeg_radio), TRUE );
4137 gtk_box_pack_start (GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), width_label, FALSE, FALSE, 0);
4138 gtk_box_pack_start (GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), width_spin, FALSE, FALSE, 0);
4139 gtk_box_pack_start (GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), height_label, FALSE, FALSE, 0);
4140 gtk_box_pack_start (GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), height_spin, FALSE, FALSE, 0);
4142 gtk_box_pack_start (GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), win_warning_label, FALSE, FALSE, 0);
4144 gtk_box_pack_start (GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), current_window_button, FALSE, FALSE, 0);
4145 gtk_box_pack_start (GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), zoom_label, FALSE, FALSE, 0);
4146 gtk_box_pack_start (GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), zoom_combo, FALSE, FALSE, 0);
4148 if ( ! one_image_only )
4150 GtkWidget *tiles_width_label, *tiles_height_label;
4152 tiles_width_label = gtk_label_new ( _("East-west image tiles:") );
4153 tiles_width_spin = gtk_spin_button_new ( GTK_ADJUSTMENT(gtk_adjustment_new ( 5, 1, 10, 1, 100, 0 )), 1, 0 );
4154 tiles_height_label = gtk_label_new ( _("North-south image tiles:") );
4155 tiles_height_spin = gtk_spin_button_new ( GTK_ADJUSTMENT(gtk_adjustment_new ( 5, 1, 10, 1, 100, 0 )), 1, 0 );
4156 gtk_box_pack_start (GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), tiles_width_label, FALSE, FALSE, 0);
4157 gtk_box_pack_start (GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), tiles_width_spin, FALSE, FALSE, 0);
4158 gtk_box_pack_start (GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), tiles_height_label, FALSE, FALSE, 0);
4159 gtk_box_pack_start (GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), tiles_height_spin, FALSE, FALSE, 0);
4161 current_window_pass_along [4] = tiles_width_spin;
4162 current_window_pass_along [5] = tiles_height_spin;
4163 g_signal_connect ( G_OBJECT(tiles_width_spin), "value-changed", G_CALLBACK(draw_to_image_file_total_area_cb), current_window_pass_along );
4164 g_signal_connect ( G_OBJECT(tiles_height_spin), "value-changed", G_CALLBACK(draw_to_image_file_total_area_cb), current_window_pass_along );
4166 gtk_box_pack_start (GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), total_size_label, FALSE, FALSE, 0);
4167 g_signal_connect ( G_OBJECT(width_spin), "value-changed", G_CALLBACK(draw_to_image_file_total_area_cb), current_window_pass_along );
4168 g_signal_connect ( G_OBJECT(height_spin), "value-changed", G_CALLBACK(draw_to_image_file_total_area_cb), current_window_pass_along );
4169 g_signal_connect ( G_OBJECT(zoom_combo), "changed", G_CALLBACK(draw_to_image_file_total_area_cb), current_window_pass_along );
4171 draw_to_image_file_total_area_cb ( NULL, current_window_pass_along ); /* set correct size info now */
4173 gtk_dialog_set_default_response ( GTK_DIALOG(dialog), GTK_RESPONSE_ACCEPT );
4175 gtk_widget_show_all ( gtk_dialog_get_content_area(GTK_DIALOG(dialog)) );
4177 if ( gtk_dialog_run ( GTK_DIALOG(dialog) ) == GTK_RESPONSE_ACCEPT )
4179 gtk_widget_hide ( GTK_WIDGET(dialog) );
4181 gchar *fn = draw_image_filename ( vw, one_image_only );
4185 gint active_z = gtk_combo_box_get_active ( GTK_COMBO_BOX(zoom_combo) );
4186 gdouble zoom = pow (2, active_z-2 );
4188 if ( one_image_only )
4189 save_image_file ( vw, fn,
4190 vw->draw_image_width = gtk_spin_button_get_value_as_int ( GTK_SPIN_BUTTON(width_spin) ),
4191 vw->draw_image_height = gtk_spin_button_get_value_as_int ( GTK_SPIN_BUTTON(height_spin) ),
4193 vw->draw_image_save_as_png = gtk_toggle_button_get_active ( GTK_TOGGLE_BUTTON(png_radio) ) );
4195 // NB is in UTM mode ATM
4196 save_image_dir ( vw, fn,
4197 vw->draw_image_width = gtk_spin_button_get_value_as_int ( GTK_SPIN_BUTTON(width_spin) ),
4198 vw->draw_image_height = gtk_spin_button_get_value_as_int ( GTK_SPIN_BUTTON(height_spin) ),
4200 vw->draw_image_save_as_png = gtk_toggle_button_get_active ( GTK_TOGGLE_BUTTON(png_radio) ),
4201 gtk_spin_button_get_value ( GTK_SPIN_BUTTON(tiles_width_spin) ),
4202 gtk_spin_button_get_value ( GTK_SPIN_BUTTON(tiles_height_spin) ) );
4207 gtk_widget_destroy ( GTK_WIDGET(dialog) );
4211 static void draw_to_image_file_cb ( GtkAction *a, VikWindow *vw )
4213 draw_to_image_file ( vw, TRUE );
4216 static void draw_to_image_dir_cb ( GtkAction *a, VikWindow *vw )
4218 draw_to_image_file ( vw, FALSE );
4221 static void print_cb ( GtkAction *a, VikWindow *vw )
4223 a_print(vw, vw->viking_vvp);
4226 /* really a misnomer: changes coord mode (actual coordinates) AND/OR draw mode (viewport only) */
4227 static void window_change_coord_mode_cb ( GtkAction *old_a, GtkAction *a, VikWindow *vw )
4229 const gchar *name = gtk_action_get_name(a);
4230 GtkToggleToolButton *tbutton = (GtkToggleToolButton *)toolbar_get_widget_by_name ( vw->viking_vtb, name );
4232 gtk_toggle_tool_button_set_active ( tbutton, TRUE );
4234 VikViewportDrawMode drawmode;
4235 if (!g_strcmp0(name, "ModeUTM")) {
4236 drawmode = VIK_VIEWPORT_DRAWMODE_UTM;
4238 else if (!g_strcmp0(name, "ModeLatLon")) {
4239 drawmode = VIK_VIEWPORT_DRAWMODE_LATLON;
4241 else if (!g_strcmp0(name, "ModeExpedia")) {
4242 drawmode = VIK_VIEWPORT_DRAWMODE_EXPEDIA;
4244 else if (!g_strcmp0(name, "ModeMercator")) {
4245 drawmode = VIK_VIEWPORT_DRAWMODE_MERCATOR;
4248 g_critical("Houston, we've had a problem.");
4252 if ( !vw->only_updating_coord_mode_ui )
4254 VikViewportDrawMode olddrawmode = vik_viewport_get_drawmode ( vw->viking_vvp );
4255 if ( olddrawmode != drawmode )
4257 /* this takes care of coord mode too */
4258 vik_viewport_set_drawmode ( vw->viking_vvp, drawmode );
4259 if ( drawmode == VIK_VIEWPORT_DRAWMODE_UTM ) {
4260 vik_layers_panel_change_coord_mode ( vw->viking_vlp, VIK_COORD_UTM );
4261 } else if ( olddrawmode == VIK_VIEWPORT_DRAWMODE_UTM ) {
4262 vik_layers_panel_change_coord_mode ( vw->viking_vlp, VIK_COORD_LATLON );
4269 static void toggle_draw_scale ( GtkAction *a, VikWindow *vw )
4271 gboolean state = !vik_viewport_get_draw_scale ( vw->viking_vvp );
4272 GtkWidget *check_box = gtk_ui_manager_get_widget ( vw->uim, "/ui/MainMenu/View/SetShow/ShowScale" );
4275 gtk_check_menu_item_set_active ( GTK_CHECK_MENU_ITEM(check_box), state );
4276 vik_viewport_set_draw_scale ( vw->viking_vvp, state );
4280 static void toggle_draw_centermark ( GtkAction *a, VikWindow *vw )
4282 gboolean state = !vik_viewport_get_draw_centermark ( vw->viking_vvp );
4283 GtkWidget *check_box = gtk_ui_manager_get_widget ( vw->uim, "/ui/MainMenu/View/SetShow/ShowCenterMark" );
4286 gtk_check_menu_item_set_active ( GTK_CHECK_MENU_ITEM(check_box), state );
4287 vik_viewport_set_draw_centermark ( vw->viking_vvp, state );
4291 static void toggle_draw_highlight ( GtkAction *a, VikWindow *vw )
4293 gboolean state = !vik_viewport_get_draw_highlight ( vw->viking_vvp );
4294 GtkWidget *check_box = gtk_ui_manager_get_widget ( vw->uim, "/ui/MainMenu/View/SetShow/ShowHighlight" );
4297 gtk_check_menu_item_set_active ( GTK_CHECK_MENU_ITEM(check_box), state );
4298 vik_viewport_set_draw_highlight ( vw->viking_vvp, state );
4302 static void set_bg_color ( GtkAction *a, VikWindow *vw )
4304 GtkWidget *colorsd = gtk_color_selection_dialog_new ( _("Choose a background color") );
4305 GdkColor *color = vik_viewport_get_background_gdkcolor ( vw->viking_vvp );
4306 gtk_color_selection_set_previous_color ( GTK_COLOR_SELECTION(gtk_color_selection_dialog_get_color_selection(GTK_COLOR_SELECTION_DIALOG(colorsd))), color );
4307 gtk_color_selection_set_current_color ( GTK_COLOR_SELECTION(gtk_color_selection_dialog_get_color_selection(GTK_COLOR_SELECTION_DIALOG(colorsd))), color );
4308 if ( gtk_dialog_run ( GTK_DIALOG(colorsd) ) == GTK_RESPONSE_OK )
4310 gtk_color_selection_get_current_color ( GTK_COLOR_SELECTION(gtk_color_selection_dialog_get_color_selection(GTK_COLOR_SELECTION_DIALOG(colorsd))), color );
4311 vik_viewport_set_background_gdkcolor ( vw->viking_vvp, color );
4315 gtk_widget_destroy ( colorsd );
4318 static void set_highlight_color ( GtkAction *a, VikWindow *vw )
4320 GtkWidget *colorsd = gtk_color_selection_dialog_new ( _("Choose a track highlight color") );
4321 GdkColor *color = vik_viewport_get_highlight_gdkcolor ( vw->viking_vvp );
4322 gtk_color_selection_set_previous_color ( GTK_COLOR_SELECTION(gtk_color_selection_dialog_get_color_selection(GTK_COLOR_SELECTION_DIALOG(colorsd))), color );
4323 gtk_color_selection_set_current_color ( GTK_COLOR_SELECTION(gtk_color_selection_dialog_get_color_selection(GTK_COLOR_SELECTION_DIALOG(colorsd))), color );
4324 if ( gtk_dialog_run ( GTK_DIALOG(colorsd) ) == GTK_RESPONSE_OK )
4326 gtk_color_selection_get_current_color ( GTK_COLOR_SELECTION(gtk_color_selection_dialog_get_color_selection(GTK_COLOR_SELECTION_DIALOG(colorsd))), color );
4327 vik_viewport_set_highlight_gdkcolor ( vw->viking_vvp, color );
4331 gtk_widget_destroy ( colorsd );
4335 /***********************************************************************************************
4337 ***********************************************************************************************/
4339 static GtkActionEntry entries[] = {
4340 { "File", NULL, N_("_File"), 0, 0, 0 },
4341 { "Edit", NULL, N_("_Edit"), 0, 0, 0 },
4342 { "View", NULL, N_("_View"), 0, 0, 0 },
4343 { "SetShow", NULL, N_("_Show"), 0, 0, 0 },
4344 { "SetZoom", NULL, N_("_Zoom"), 0, 0, 0 },
4345 { "SetPan", NULL, N_("_Pan"), 0, 0, 0 },
4346 { "Layers", NULL, N_("_Layers"), 0, 0, 0 },
4347 { "Tools", NULL, N_("_Tools"), 0, 0, 0 },
4348 { "Exttools", NULL, N_("_Webtools"), 0, 0, 0 },
4349 { "Help", NULL, N_("_Help"), 0, 0, 0 },
4351 { "New", GTK_STOCK_NEW, N_("_New"), "<control>N", N_("New file"), (GCallback)newwindow_cb },
4352 { "Open", GTK_STOCK_OPEN, N_("_Open..."), "<control>O", N_("Open a file"), (GCallback)load_file },
4353 { "OpenRecentFile", NULL, N_("Open _Recent File"), NULL, NULL, (GCallback)NULL },
4354 { "Append", GTK_STOCK_ADD, N_("Append _File..."), NULL, N_("Append data from a different file"), (GCallback)load_file },
4355 { "Export", GTK_STOCK_CONVERT, N_("_Export All"), NULL, N_("Export All TrackWaypoint Layers"), (GCallback)NULL },
4356 { "ExportGPX", NULL, N_("_GPX..."), NULL, N_("Export as GPX"), (GCallback)export_to_gpx },
4357 { "Acquire", GTK_STOCK_GO_DOWN, N_("A_cquire"), NULL, NULL, (GCallback)NULL },
4358 { "AcquireGPS", NULL, N_("From _GPS..."), NULL, N_("Transfer data from a GPS device"), (GCallback)acquire_from_gps },
4359 { "AcquireGPSBabel", NULL, N_("Import File With GPS_Babel..."), NULL, N_("Import file via GPSBabel converter"), (GCallback)acquire_from_file },
4360 { "AcquireRouting", NULL, N_("_Directions..."), NULL, N_("Get driving directions"), (GCallback)acquire_from_routing },
4361 #ifdef VIK_CONFIG_OPENSTREETMAP
4362 { "AcquireOSM", NULL, N_("_OSM Traces..."), NULL, N_("Get traces from OpenStreetMap"), (GCallback)acquire_from_osm },
4363 { "AcquireMyOSM", NULL, N_("_My OSM Traces..."), NULL, N_("Get Your Own Traces from OpenStreetMap"), (GCallback)acquire_from_my_osm },
4365 #ifdef VIK_CONFIG_GEOCACHES
4366 { "AcquireGC", NULL, N_("Geo_caches..."), NULL, N_("Get Geocaches from geocaching.com"), (GCallback)acquire_from_gc },
4368 #ifdef VIK_CONFIG_GEOTAG
4369 { "AcquireGeotag", NULL, N_("From Geotagged _Images..."), NULL, N_("Create waypoints from geotagged images"), (GCallback)acquire_from_geotag },
4371 { "AcquireURL", NULL, N_("From _URL..."), NULL, N_("Get a file from a URL"), (GCallback)acquire_from_url },
4372 #ifdef VIK_CONFIG_GEONAMES
4373 { "AcquireWikipedia", NULL, N_("From _Wikipedia Waypoints"), NULL, N_("Create waypoints from Wikipedia items in the current view"), (GCallback)acquire_from_wikipedia },
4375 { "Save", GTK_STOCK_SAVE, N_("_Save"), "<control>S", N_("Save the file"), (GCallback)save_file },
4376 { "SaveAs", GTK_STOCK_SAVE_AS, N_("Save _As..."), NULL, N_("Save the file under different name"), (GCallback)save_file_as },
4377 { "FileProperties", NULL, N_("Properties..."), NULL, N_("File Properties"), (GCallback)file_properties_cb },
4378 { "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 },
4379 { "GenImgDir", GTK_STOCK_DND_MULTIPLE, N_("Generate _Directory of Images..."), NULL, N_("Generate _Directory of Images"), (GCallback)draw_to_image_dir_cb },
4380 { "Print", GTK_STOCK_PRINT, N_("_Print..."), NULL, N_("Print maps"), (GCallback)print_cb },
4381 { "Exit", GTK_STOCK_QUIT, N_("E_xit"), "<control>W", N_("Exit the program"), (GCallback)window_close },
4382 { "SaveExit", GTK_STOCK_QUIT, N_("Save and Exit"), NULL, N_("Save and Exit the program"), (GCallback)save_file_and_exit },
4384 { "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 },
4385 { "GoForward", GTK_STOCK_GO_FORWARD, N_("Go to the _Next Location"), NULL, N_("Go to the next location"), (GCallback)draw_goto_back_and_forth },
4386 { "GotoDefaultLocation", GTK_STOCK_HOME, N_("Go to the _Default Location"), NULL, N_("Go to the default location"), (GCallback)goto_default_location },
4387 { "GotoSearch", GTK_STOCK_JUMP_TO, N_("Go to _Location..."), NULL, N_("Go to address/place using text search"), (GCallback)goto_address },
4388 { "GotoLL", GTK_STOCK_JUMP_TO, N_("_Go to Lat/Lon..."), NULL, N_("Go to arbitrary lat/lon coordinate"), (GCallback)draw_goto_cb },
4389 { "GotoUTM", GTK_STOCK_JUMP_TO, N_("Go to UTM..."), NULL, N_("Go to arbitrary UTM coordinate"), (GCallback)draw_goto_cb },
4390 { "Refresh", GTK_STOCK_REFRESH, N_("_Refresh"), "F5", N_("Refresh any maps displayed"), (GCallback)draw_refresh_cb },
4391 { "SetHLColor",GTK_STOCK_SELECT_COLOR, N_("Set _Highlight Color..."), NULL, N_("Set Highlight Color"), (GCallback)set_highlight_color },
4392 { "SetBGColor",GTK_STOCK_SELECT_COLOR, N_("Set Bac_kground Color..."), NULL, N_("Set Background Color"), (GCallback)set_bg_color },
4393 { "ZoomIn", GTK_STOCK_ZOOM_IN, N_("Zoom _In"), "<control>plus", N_("Zoom In"), (GCallback)draw_zoom_cb },
4394 { "ZoomOut", GTK_STOCK_ZOOM_OUT, N_("Zoom _Out"), "<control>minus", N_("Zoom Out"), (GCallback)draw_zoom_cb },
4395 { "ZoomTo", GTK_STOCK_ZOOM_FIT, N_("Zoom _To..."), "<control>Z", N_("Zoom To"), (GCallback)zoom_to_cb },
4396 { "PanNorth", NULL, N_("Pan _North"), "<control>Up", NULL, (GCallback)draw_pan_cb },
4397 { "PanEast", NULL, N_("Pan _East"), "<control>Right", NULL, (GCallback)draw_pan_cb },
4398 { "PanSouth", NULL, N_("Pan _South"), "<control>Down", NULL, (GCallback)draw_pan_cb },
4399 { "PanWest", NULL, N_("Pan _West"), "<control>Left", NULL, (GCallback)draw_pan_cb },
4400 { "BGJobs", GTK_STOCK_EXECUTE, N_("Background _Jobs"), NULL, N_("Background Jobs"), (GCallback)a_background_show_window },
4402 { "Cut", GTK_STOCK_CUT, N_("Cu_t"), NULL, N_("Cut selected layer"), (GCallback)menu_cut_layer_cb },
4403 { "Copy", GTK_STOCK_COPY, N_("_Copy"), NULL, N_("Copy selected layer"), (GCallback)menu_copy_layer_cb },
4404 { "Paste", GTK_STOCK_PASTE, N_("_Paste"), NULL, N_("Paste layer into selected container layer or otherwise above selected layer"), (GCallback)menu_paste_layer_cb },
4405 { "Delete", GTK_STOCK_DELETE, N_("_Delete"), NULL, N_("Remove selected layer"), (GCallback)menu_delete_layer_cb },
4406 { "DeleteAll", NULL, N_("Delete All"), NULL, NULL, (GCallback)clear_cb },
4407 { "CopyCentre",NULL, N_("Copy Centre _Location"), "<control>h", NULL, (GCallback)menu_copy_centre_cb },
4408 { "MapCacheFlush",NULL, N_("_Flush Map Cache"), NULL, NULL, (GCallback)mapcache_flush_cb },
4409 { "SetDefaultLocation", GTK_STOCK_GO_FORWARD, N_("_Set the Default Location"), NULL, N_("Set the Default Location to the current position"),(GCallback)default_location_cb },
4410 { "Preferences",GTK_STOCK_PREFERENCES, N_("_Preferences"), NULL, N_("Program Preferences"), (GCallback)preferences_cb },
4411 { "LayerDefaults",GTK_STOCK_PROPERTIES, N_("_Layer Defaults"), NULL, NULL, NULL },
4412 { "Properties",GTK_STOCK_PROPERTIES, N_("_Properties"), NULL, N_("Layer Properties"), (GCallback)menu_properties_cb },
4414 { "HelpEntry", GTK_STOCK_HELP, N_("_Help"), "F1", N_("Help"), (GCallback)help_help_cb },
4415 { "About", GTK_STOCK_ABOUT, N_("_About"), NULL, N_("About"), (GCallback)help_about_cb },
4418 static GtkActionEntry debug_entries[] = {
4419 { "MapCacheInfo", NULL, "_Map Cache Info", NULL, NULL, (GCallback)help_cache_info_cb },
4420 { "BackForwardInfo", NULL, "_Back/Forward Info", NULL, NULL, (GCallback)back_forward_info_cb },
4423 static GtkActionEntry entries_gpsbabel[] = {
4424 { "ExportKML", NULL, N_("_KML..."), NULL, N_("Export as KML"), (GCallback)export_to_kml },
4427 static GtkActionEntry entries_geojson[] = {
4428 { "AcquireGeoJSON", NULL, N_("Import Geo_JSON File..."), NULL, N_("Import GeoJSON file"), (GCallback)acquire_from_geojson },
4432 static GtkRadioActionEntry mode_entries[] = {
4433 { "ModeUTM", NULL, N_("_UTM Mode"), "<control>u", NULL, VIK_VIEWPORT_DRAWMODE_UTM },
4434 { "ModeExpedia", NULL, N_("_Expedia Mode"), "<control>e", NULL, VIK_VIEWPORT_DRAWMODE_EXPEDIA },
4435 { "ModeMercator", NULL, N_("_Mercator Mode"), "<control>m", NULL, VIK_VIEWPORT_DRAWMODE_MERCATOR },
4436 { "ModeLatLon", NULL, N_("Lat_/Lon Mode"), "<control>l", NULL, VIK_VIEWPORT_DRAWMODE_LATLON },
4439 static GtkToggleActionEntry toggle_entries[] = {
4440 { "ShowScale", NULL, N_("Show _Scale"), "<shift>F5", N_("Show Scale"), (GCallback)toggle_draw_scale, TRUE },
4441 { "ShowCenterMark", NULL, N_("Show _Center Mark"), "F6", N_("Show Center Mark"), (GCallback)toggle_draw_centermark, TRUE },
4442 { "ShowHighlight", GTK_STOCK_UNDERLINE, N_("Show _Highlight"), "F7", N_("Show Highlight"), (GCallback)toggle_draw_highlight, TRUE },
4443 { "FullScreen", GTK_STOCK_FULLSCREEN, N_("_Full Screen"), "F11", N_("Activate full screen mode"), (GCallback)full_screen_cb, FALSE },
4444 { "ViewSidePanel", GTK_STOCK_INDEX, N_("Show Side _Panel"), "F9", N_("Show Side Panel"), (GCallback)view_side_panel_cb, TRUE },
4445 { "ViewStatusBar", NULL, N_("Show Status_bar"), "F12", N_("Show Statusbar"), (GCallback)view_statusbar_cb, TRUE },
4446 { "ViewToolbar", NULL, N_("Show _Toolbar"), "F3", N_("Show Toolbar"), (GCallback)view_toolbar_cb, TRUE },
4447 { "ViewMainMenu", NULL, N_("Show _Menu"), "F4", N_("Show Menu"), (GCallback)view_main_menu_cb, TRUE },
4450 // This must match the toggle entries order above
4451 static gpointer toggle_entries_toolbar_cb[] = {
4452 (GCallback)tb_set_draw_scale,
4453 (GCallback)tb_set_draw_centermark,
4454 (GCallback)tb_set_draw_highlight,
4455 (GCallback)tb_full_screen_cb,
4456 (GCallback)tb_view_side_panel_cb,
4457 (GCallback)tb_view_statusbar_cb,
4458 (GCallback)tb_view_toolbar_cb,
4459 (GCallback)tb_view_main_menu_cb,
4462 #include "menu.xml.h"
4463 static void window_create_ui( VikWindow *window )
4466 GtkActionGroup *action_group;
4467 GtkAccelGroup *accel_group;
4470 GtkIconFactory *icon_factory;
4471 GtkIconSet *icon_set;
4472 GtkRadioActionEntry *tools = NULL, *radio;
4475 uim = gtk_ui_manager_new ();
4478 toolbox_add_tool(window->vt, &ruler_tool, TOOL_LAYER_TYPE_NONE);
4479 toolbox_add_tool(window->vt, &zoom_tool, TOOL_LAYER_TYPE_NONE);
4480 toolbox_add_tool(window->vt, &pan_tool, TOOL_LAYER_TYPE_NONE);
4481 toolbox_add_tool(window->vt, &select_tool, TOOL_LAYER_TYPE_NONE);
4483 toolbar_action_tool_entry_register ( window->viking_vtb, &pan_tool.radioActionEntry );
4484 toolbar_action_tool_entry_register ( window->viking_vtb, &zoom_tool.radioActionEntry );
4485 toolbar_action_tool_entry_register ( window->viking_vtb, &ruler_tool.radioActionEntry );
4486 toolbar_action_tool_entry_register ( window->viking_vtb, &select_tool.radioActionEntry );
4489 if (!(mid = gtk_ui_manager_add_ui_from_string (uim, menu_xml, -1, &error))) {
4490 g_error_free (error);
4494 action_group = gtk_action_group_new ("MenuActions");
4495 gtk_action_group_set_translation_domain(action_group, PACKAGE_NAME);
4496 gtk_action_group_add_actions (action_group, entries, G_N_ELEMENTS (entries), window);
4497 gtk_action_group_add_toggle_actions (action_group, toggle_entries, G_N_ELEMENTS (toggle_entries), window);
4498 gtk_action_group_add_radio_actions (action_group, mode_entries, G_N_ELEMENTS (mode_entries), 4, (GCallback)window_change_coord_mode_cb, window);
4500 if ( gtk_ui_manager_add_ui_from_string ( uim,
4501 "<ui><menubar name='MainMenu'><menu action='Help'>"
4502 "<menuitem action='MapCacheInfo'/>"
4503 "<menuitem action='BackForwardInfo'/>"
4504 "</menu></menubar></ui>",
4506 gtk_action_group_add_actions (action_group, debug_entries, G_N_ELEMENTS (debug_entries), window);
4510 for ( i=0; i < G_N_ELEMENTS (entries); i++ ) {
4511 if ( entries[i].callback )
4512 toolbar_action_entry_register ( window->viking_vtb, &entries[i] );
4515 if ( G_N_ELEMENTS (toggle_entries) != G_N_ELEMENTS (toggle_entries_toolbar_cb) ) {
4516 g_print ( "Broken entries definitions\n" );
4519 for ( i=0; i < G_N_ELEMENTS (toggle_entries); i++ ) {
4520 if ( toggle_entries_toolbar_cb[i] )
4521 toolbar_action_toggle_entry_register ( window->viking_vtb, &toggle_entries[i], toggle_entries_toolbar_cb[i] );
4524 for ( i=0; i < G_N_ELEMENTS (mode_entries); i++ ) {
4525 toolbar_action_mode_entry_register ( window->viking_vtb, &mode_entries[i] );
4528 // Use this to see if GPSBabel is available:
4529 if ( a_babel_available () ) {
4530 // If going to add more entries then might be worth creating a menu_gpsbabel.xml.h file
4531 if ( gtk_ui_manager_add_ui_from_string ( uim,
4532 "<ui><menubar name='MainMenu'><menu action='File'><menu action='Export'><menuitem action='ExportKML'/></menu></menu></menubar></ui>",
4534 gtk_action_group_add_actions ( action_group, entries_gpsbabel, G_N_ELEMENTS (entries_gpsbabel), window );
4537 // GeoJSON import capability
4538 if ( g_find_program_in_path ( a_geojson_program_import() ) ) {
4539 if ( gtk_ui_manager_add_ui_from_string ( uim,
4540 "<ui><menubar name='MainMenu'><menu action='File'><menu action='Acquire'><menuitem action='AcquireGeoJSON'/></menu></menu></menubar></ui>",
4542 gtk_action_group_add_actions ( action_group, entries_geojson, G_N_ELEMENTS (entries_geojson), window );
4545 icon_factory = gtk_icon_factory_new ();
4546 gtk_icon_factory_add_default (icon_factory);
4548 register_vik_icons(icon_factory);
4550 // Copy the tool RadioActionEntries out of the main Window structure into an extending array 'tools'
4551 // so that it can be applied to the UI in one action group add function call below
4553 for (i=0; i<window->vt->n_tools; i++) {
4554 tools = g_renew(GtkRadioActionEntry, tools, ntools+1);
4555 radio = &tools[ntools];
4557 *radio = window->vt->tools[i].ti.radioActionEntry;
4558 radio->value = ntools;
4561 for (i=0; i<VIK_LAYER_NUM_TYPES; i++) {
4562 GtkActionEntry action;
4563 gtk_ui_manager_add_ui(uim, mid, "/ui/MainMenu/Layers/",
4564 vik_layer_get_interface(i)->name,
4565 vik_layer_get_interface(i)->name,
4566 GTK_UI_MANAGER_MENUITEM, FALSE);
4568 icon_set = gtk_icon_set_new_from_pixbuf (gdk_pixbuf_from_pixdata (vik_layer_get_interface(i)->icon, FALSE, NULL ));
4569 gtk_icon_factory_add (icon_factory, vik_layer_get_interface(i)->name, icon_set);
4570 gtk_icon_set_unref (icon_set);
4572 action.name = vik_layer_get_interface(i)->name;
4573 action.stock_id = vik_layer_get_interface(i)->name;
4574 action.label = g_strdup_printf( _("New _%s Layer"), vik_layer_get_interface(i)->name);
4575 action.accelerator = vik_layer_get_interface(i)->accelerator;
4576 action.tooltip = NULL;
4577 action.callback = (GCallback)menu_addlayer_cb;
4578 gtk_action_group_add_actions(action_group, &action, 1, window);
4580 g_free ( (gchar*)action.label );
4582 if ( vik_layer_get_interface(i)->tools_count ) {
4583 gtk_ui_manager_add_ui(uim, mid, "/ui/MainMenu/Tools/", vik_layer_get_interface(i)->name, NULL, GTK_UI_MANAGER_SEPARATOR, FALSE);
4586 // Further tool copying for to apply to the UI, also apply menu UI setup
4587 for ( j = 0; j < vik_layer_get_interface(i)->tools_count; j++ ) {
4588 tools = g_renew(GtkRadioActionEntry, tools, ntools+1);
4589 radio = &tools[ntools];
4592 gtk_ui_manager_add_ui(uim, mid, "/ui/MainMenu/Tools",
4593 vik_layer_get_interface(i)->tools[j].radioActionEntry.label,
4594 vik_layer_get_interface(i)->tools[j].radioActionEntry.name,
4595 GTK_UI_MANAGER_MENUITEM, FALSE);
4597 toolbox_add_tool(window->vt, &(vik_layer_get_interface(i)->tools[j]), i);
4598 toolbar_action_tool_entry_register ( window->viking_vtb, &(vik_layer_get_interface(i)->tools[j].radioActionEntry) );
4600 *radio = vik_layer_get_interface(i)->tools[j].radioActionEntry;
4601 // Overwrite with actual number to use
4602 radio->value = ntools;
4605 GtkActionEntry action_dl;
4606 gchar *layername = g_strdup_printf ( "Layer%s", vik_layer_get_interface(i)->fixed_layer_name );
4607 gtk_ui_manager_add_ui(uim, mid, "/ui/MainMenu/Edit/LayerDefaults",
4608 vik_layer_get_interface(i)->name,
4610 GTK_UI_MANAGER_MENUITEM, FALSE);
4613 // For default layers use action names of the form 'Layer<LayerName>'
4614 // This is to avoid clashing with just the layer name used above for the tool actions
4615 action_dl.name = g_strconcat("Layer", vik_layer_get_interface(i)->fixed_layer_name, NULL);
4616 action_dl.stock_id = NULL;
4617 action_dl.label = g_strconcat("_", vik_layer_get_interface(i)->name, "...", NULL); // Prepend marker for keyboard accelerator
4618 action_dl.accelerator = NULL;
4619 action_dl.tooltip = NULL;
4620 action_dl.callback = (GCallback)layer_defaults_cb;
4621 gtk_action_group_add_actions(action_group, &action_dl, 1, window);
4622 g_free ( (gchar*)action_dl.name );
4623 g_free ( (gchar*)action_dl.label );
4625 g_object_unref (icon_factory);
4627 gtk_action_group_add_radio_actions(action_group, tools, ntools, 0, (GCallback)menu_cb, window);
4630 gtk_ui_manager_insert_action_group (uim, action_group, 0);
4632 for (i=0; i<VIK_LAYER_NUM_TYPES; i++) {
4633 for ( j = 0; j < vik_layer_get_interface(i)->tools_count; j++ ) {
4634 GtkAction *action = gtk_action_group_get_action(action_group,
4635 vik_layer_get_interface(i)->tools[j].radioActionEntry.name);
4636 g_object_set(action, "sensitive", FALSE, NULL);
4640 // This is done last so we don't need to track the value of mid anymore
4641 vik_ext_tools_add_action_items ( window, window->uim, action_group, mid );
4643 window->action_group = action_group;
4645 accel_group = gtk_ui_manager_get_accel_group (uim);
4646 gtk_window_add_accel_group (GTK_WINDOW (window), accel_group);
4647 gtk_ui_manager_ensure_update (uim);
4649 setup_recent_files(window);
4653 // TODO - add method to add tool icons defined from outside this file
4654 // and remove the reverse dependency on icon definition from this file
4656 const GdkPixdata *data;
4659 { &mover_22_pixbuf, "vik-icon-pan" },
4660 { &zoom_18_pixbuf, "vik-icon-zoom" },
4661 { &ruler_18_pixbuf, "vik-icon-ruler" },
4662 { &select_18_pixbuf, "vik-icon-select" },
4663 { &vik_new_route_18_pixbuf, "vik-icon-Create Route" },
4664 { &route_finder_18_pixbuf, "vik-icon-Route Finder" },
4665 { &demdl_18_pixbuf, "vik-icon-DEM Download" },
4666 { &showpic_18_pixbuf, "vik-icon-Show Picture" },
4667 { &addtr_18_pixbuf, "vik-icon-Create Track" },
4668 { &edtr_18_pixbuf, "vik-icon-Edit Trackpoint" },
4669 { &addwp_18_pixbuf, "vik-icon-Create Waypoint" },
4670 { &edwp_18_pixbuf, "vik-icon-Edit Waypoint" },
4671 { &geozoom_18_pixbuf, "vik-icon-Georef Zoom Tool" },
4672 { &geomove_18_pixbuf, "vik-icon-Georef Move Map" },
4673 { &mapdl_18_pixbuf, "vik-icon-Maps Download" },
4676 static gint n_stock_icons = G_N_ELEMENTS (stock_icons);
4679 register_vik_icons (GtkIconFactory *icon_factory)
4681 GtkIconSet *icon_set;
4684 for (i = 0; i < n_stock_icons; i++) {
4685 icon_set = gtk_icon_set_new_from_pixbuf (gdk_pixbuf_from_pixdata (
4686 stock_icons[i].data, FALSE, NULL ));
4687 gtk_icon_factory_add (icon_factory, stock_icons[i].stock_id, icon_set);
4688 gtk_icon_set_unref (icon_set);
4692 gpointer vik_window_get_selected_trw_layer ( VikWindow *vw )
4694 return vw->selected_vtl;
4697 void vik_window_set_selected_trw_layer ( VikWindow *vw, gpointer vtl )
4699 vw->selected_vtl = vtl;
4700 vw->containing_vtl = vtl;
4702 vw->selected_track = NULL;
4703 vw->selected_tracks = NULL;
4704 vw->selected_waypoint = NULL;
4705 vw->selected_waypoints = NULL;
4706 // Set highlight thickness
4707 vik_viewport_set_highlight_thickness ( vw->viking_vvp, vik_trw_layer_get_property_tracks_line_thickness (vw->containing_vtl) );
4710 GHashTable *vik_window_get_selected_tracks ( VikWindow *vw )
4712 return vw->selected_tracks;
4715 void vik_window_set_selected_tracks ( VikWindow *vw, GHashTable *ght, gpointer vtl )
4717 vw->selected_tracks = ght;
4718 vw->containing_vtl = vtl;
4720 vw->selected_vtl = NULL;
4721 vw->selected_track = NULL;
4722 vw->selected_waypoint = NULL;
4723 vw->selected_waypoints = NULL;
4724 // Set highlight thickness
4725 vik_viewport_set_highlight_thickness ( vw->viking_vvp, vik_trw_layer_get_property_tracks_line_thickness (vw->containing_vtl) );
4728 gpointer vik_window_get_selected_track ( VikWindow *vw )
4730 return vw->selected_track;
4733 void vik_window_set_selected_track ( VikWindow *vw, gpointer *vt, gpointer vtl )
4735 vw->selected_track = vt;
4736 vw->containing_vtl = vtl;
4738 vw->selected_vtl = NULL;
4739 vw->selected_tracks = NULL;
4740 vw->selected_waypoint = NULL;
4741 vw->selected_waypoints = NULL;
4742 // Set highlight thickness
4743 vik_viewport_set_highlight_thickness ( vw->viking_vvp, vik_trw_layer_get_property_tracks_line_thickness (vw->containing_vtl) );
4746 GHashTable *vik_window_get_selected_waypoints ( VikWindow *vw )
4748 return vw->selected_waypoints;
4751 void vik_window_set_selected_waypoints ( VikWindow *vw, GHashTable *ght, gpointer vtl )
4753 vw->selected_waypoints = ght;
4754 vw->containing_vtl = vtl;
4756 vw->selected_vtl = NULL;
4757 vw->selected_track = NULL;
4758 vw->selected_tracks = NULL;
4759 vw->selected_waypoint = NULL;
4762 gpointer vik_window_get_selected_waypoint ( VikWindow *vw )
4764 return vw->selected_waypoint;
4767 void vik_window_set_selected_waypoint ( VikWindow *vw, gpointer *vwp, gpointer vtl )
4769 vw->selected_waypoint = vwp;
4770 vw->containing_vtl = vtl;
4772 vw->selected_vtl = NULL;
4773 vw->selected_track = NULL;
4774 vw->selected_tracks = NULL;
4775 vw->selected_waypoints = NULL;
4778 gboolean vik_window_clear_highlight ( VikWindow *vw )
4780 gboolean need_redraw = FALSE;
4781 if ( vw->selected_vtl != NULL ) {
4782 vw->selected_vtl = NULL;
4785 if ( vw->selected_track != NULL ) {
4786 vw->selected_track = NULL;
4789 if ( vw->selected_tracks != NULL ) {
4790 vw->selected_tracks = NULL;
4793 if ( vw->selected_waypoint != NULL ) {
4794 vw->selected_waypoint = NULL;
4797 if ( vw->selected_waypoints != NULL ) {
4798 vw->selected_waypoints = NULL;
4805 * May return NULL if the window no longer exists
4807 GThread *vik_window_get_thread ( VikWindow *vw )