2 * viking -- GPS Data and Topo Analyzer, Explorer, and Manager
4 * Copyright (C) 2003-2005, Evan Battaglia <gtoevan@gmx.net>
5 * Copyright (C) 2005-2006, Alex Foobarian <foobarian@gmail.com>
6 * Copyright (C) 2012-2015, Rob Norris <rw_norris@hotmail.com>
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
29 #include "background.h"
31 #include "datasources.h"
37 #include "preferences.h"
39 #include "viklayer_defaults.h"
40 #include "icons/icons.h"
41 #include "vikexttools.h"
42 #include "vikexttool_datasources.h"
43 #include "garminsymbols.h"
44 #include "vikmapslayer.h"
45 #include "geonamessearch.h"
61 #include <glib/gstdio.h>
62 #include <glib/gprintf.h>
63 #include <glib/gi18n.h>
65 #include <gdk/gdkkeysyms.h>
67 // This seems rather arbitary, quite large and pointless
68 // I mean, if you have a thousand windows open;
69 // why not be allowed to open a thousand more...
70 #define MAX_WINDOWS 1024
71 static guint window_count = 0;
72 static GSList *window_list = NULL;
74 #define VIKING_WINDOW_WIDTH 1000
75 #define VIKING_WINDOW_HEIGHT 800
76 #define DRAW_IMAGE_DEFAULT_WIDTH 1280
77 #define DRAW_IMAGE_DEFAULT_HEIGHT 1024
78 #define DRAW_IMAGE_DEFAULT_SAVE_AS_PNG TRUE
80 // The last used directories
81 static gchar *last_folder_files_uri = NULL;
82 static gchar *last_folder_images_uri = NULL;
84 static void window_finalize ( GObject *gob );
85 static GObjectClass *parent_class;
87 static void window_set_filename ( VikWindow *vw, const gchar *filename );
88 static const gchar *window_get_filename ( VikWindow *vw );
90 static VikWindow *window_new ();
92 static void draw_update ( VikWindow *vw );
94 static void newwindow_cb ( GtkAction *a, VikWindow *vw );
97 static void open_window ( VikWindow *vw, GSList *files );
98 static void destroy_window ( GtkWidget *widget,
101 /* Drawing & stuff */
103 static gboolean delete_event( VikWindow *vw );
105 static gboolean key_press_event( VikWindow *vw, GdkEventKey *event, gpointer data );
107 static void center_changed_cb ( VikWindow *vw );
108 static void window_configure_event ( VikWindow *vw );
109 static void draw_sync ( VikWindow *vw );
110 static void draw_redraw ( VikWindow *vw );
111 static void draw_scroll ( VikWindow *vw, GdkEventScroll *event );
112 static void draw_click ( VikWindow *vw, GdkEventButton *event );
113 static void draw_release ( VikWindow *vw, GdkEventButton *event );
114 static void draw_mouse_motion ( VikWindow *vw, GdkEventMotion *event );
115 static void draw_zoom_cb ( GtkAction *a, VikWindow *vw );
116 static void draw_goto_cb ( GtkAction *a, VikWindow *vw );
117 static void draw_refresh_cb ( GtkAction *a, VikWindow *vw );
119 static void draw_status ( VikWindow *vw );
121 /* End Drawing Functions */
123 static void toggle_draw_scale ( GtkAction *a, VikWindow *vw );
124 static void toggle_draw_centermark ( GtkAction *a, VikWindow *vw );
125 static void toggle_draw_highlight ( GtkAction *a, VikWindow *vw );
127 static void menu_addlayer_cb ( GtkAction *a, VikWindow *vw );
128 static void menu_properties_cb ( GtkAction *a, VikWindow *vw );
129 static void menu_delete_layer_cb ( GtkAction *a, VikWindow *vw );
131 /* tool management */
137 #define TOOL_LAYER_TYPE_NONE -1
142 toolbox_tool_t *tools;
146 static void menu_cb ( GtkAction *old, GtkAction *a, VikWindow *vw );
147 static void window_change_coord_mode_cb ( GtkAction *old, GtkAction *a, VikWindow *vw );
148 static toolbox_tools_t* toolbox_create(VikWindow *vw);
149 static void toolbox_add_tool(toolbox_tools_t *vt, VikToolInterface *vti, gint layer_type );
150 static int toolbox_get_tool(toolbox_tools_t *vt, const gchar *tool_name);
151 static void toolbox_activate(toolbox_tools_t *vt, const gchar *tool_name);
152 static const GdkCursor *toolbox_get_cursor(toolbox_tools_t *vt, const gchar *tool_name);
153 static void toolbox_click (toolbox_tools_t *vt, GdkEventButton *event);
154 static void toolbox_move (toolbox_tools_t *vt, GdkEventMotion *event);
155 static void toolbox_release (toolbox_tools_t *vt, GdkEventButton *event);
159 static void window_create_ui( VikWindow *window );
160 static void register_vik_icons (GtkIconFactory *icon_factory);
163 static void load_file ( GtkAction *a, VikWindow *vw );
164 static gboolean save_file_as ( GtkAction *a, VikWindow *vw );
165 static gboolean save_file ( GtkAction *a, VikWindow *vw );
166 static gboolean save_file_and_exit ( GtkAction *a, VikWindow *vw );
167 static gboolean window_save ( VikWindow *vw );
172 VikViewport *viking_vvp;
173 VikLayersPanel *viking_vlp;
174 VikStatusbar *viking_vs;
175 VikToolbar *viking_vtb;
177 GtkWidget *main_vbox;
178 GtkWidget *menu_hbox;
180 GdkCursor *busy_cursor;
181 GdkCursor *viewport_cursor; // only a reference
183 /* tool management state */
186 guint16 tool_layer_id;
187 guint16 tool_tool_id;
189 GtkActionGroup *action_group;
192 // NB scale, centermark and highlight are in viewport.
193 gboolean show_full_screen;
194 gboolean show_side_panel;
195 gboolean show_statusbar;
196 gboolean show_toolbar;
197 gboolean show_main_menu;
199 gboolean select_move;
202 gint delayed_pan_x, delayed_pan_y; // Temporary storage
203 gboolean single_click_pending;
205 guint draw_image_width, draw_image_height;
206 gboolean draw_image_save_as_png;
210 VikLoadType_t loaded_type;
212 gboolean only_updating_coord_mode_ui; /* hack for a bug in GTK */
216 /* half-drawn update */
218 VikCoord trigger_center;
220 /* Store at this level for highlighted selection drawing since it applies to the viewport and the layers panel */
221 /* Only one of these items can be selected at the same time */
222 gpointer selected_vtl; /* notionally VikTrwLayer */
223 GHashTable *selected_tracks;
224 gpointer selected_track; /* notionally VikTrack */
225 GHashTable *selected_waypoints;
226 gpointer selected_waypoint; /* notionally VikWaypoint */
227 /* only use for individual track or waypoint */
228 /* For track(s) & waypoint(s) it is the layer they are in - this helps refering to the individual item easier */
229 gpointer containing_vtl; /* notionally VikTrwLayer */
243 VW_OPENWINDOW_SIGNAL,
247 static guint window_signals[VW_LAST_SIGNAL] = { 0 };
249 // TODO get rid of this as this is unnecessary duplication...
250 static gchar *tool_names[NUMBER_OF_TOOLS] = { N_("Pan"), N_("Zoom"), N_("Ruler"), N_("Select") };
252 G_DEFINE_TYPE (VikWindow, vik_window, GTK_TYPE_WINDOW)
254 VikViewport * vik_window_viewport(VikWindow *vw)
256 return(vw->viking_vvp);
259 VikLayersPanel * vik_window_layers_panel(VikWindow *vw)
261 return(vw->viking_vlp);
265 * Returns the statusbar for the window
267 VikStatusbar * vik_window_get_statusbar ( VikWindow *vw )
269 return vw->viking_vs;
273 * Returns the 'project' filename
275 const gchar *vik_window_get_filename (VikWindow *vw)
282 vik_statusbar_type_t vs_type;
283 gchar* message; // Always make a copy of this data
284 } statusbar_idle_data;
287 * For the actual statusbar update!
289 static gboolean statusbar_idle_update ( statusbar_idle_data *sid )
291 vik_statusbar_set_message ( sid->vs, sid->vs_type, sid->message );
292 g_free ( sid->message );
298 * vik_window_statusbar_update:
299 * @vw: The main window in which the statusbar will be updated.
300 * @message: The string to be displayed. This is copied.
301 * @vs_type: The part of the statusbar to be updated.
303 * This updates any part of the statusbar with the new string.
304 * It handles calling from the main thread or any background thread
305 * ATM this mostly used from background threads - as from the main thread
306 * one may use the vik_statusbar_set_message() directly.
308 void vik_window_statusbar_update ( VikWindow *vw, const gchar* message, vik_statusbar_type_t vs_type )
310 GThread *thread = vik_window_get_thread ( vw );
315 statusbar_idle_data *sid = g_malloc ( sizeof (statusbar_idle_data) );
316 sid->vs = vw->viking_vs;
317 sid->vs_type = vs_type;
318 sid->message = g_strdup ( message );
320 if ( g_thread_self() == thread ) {
321 g_idle_add ( (GSourceFunc) statusbar_idle_update, sid );
324 // From a background thread
325 gdk_threads_add_idle ( (GSourceFunc) statusbar_idle_update, sid );
329 // Actual signal handlers
330 static void destroy_window ( GtkWidget *widget,
333 if ( ! --window_count ) {
334 g_free ( last_folder_files_uri );
335 g_free ( last_folder_images_uri );
340 #define VIK_SETTINGS_WIN_SIDEPANEL "window_sidepanel"
341 #define VIK_SETTINGS_WIN_STATUSBAR "window_statusbar"
342 #define VIK_SETTINGS_WIN_TOOLBAR "window_toolbar"
343 // Menubar setting to off is never auto saved in case it's accidentally turned off
344 // It's not so obvious so to recover the menu visibility.
345 // Thus this value is for setting manually via editting the settings file directly
346 #define VIK_SETTINGS_WIN_MENUBAR "window_menubar"
348 VikWindow *vik_window_new_window ()
350 if ( window_count < MAX_WINDOWS )
352 VikWindow *vw = window_new ();
354 g_signal_connect (G_OBJECT (vw), "destroy",
355 G_CALLBACK (destroy_window), NULL);
356 g_signal_connect (G_OBJECT (vw), "newwindow",
357 G_CALLBACK (vik_window_new_window), NULL);
358 g_signal_connect (G_OBJECT (vw), "openwindow",
359 G_CALLBACK (open_window), NULL);
361 gtk_widget_show_all ( GTK_WIDGET(vw) );
363 if ( a_vik_get_restore_window_state() ) {
364 // These settings are applied after the show all as these options hide widgets
366 if ( a_settings_get_boolean ( VIK_SETTINGS_WIN_SIDEPANEL, &sidepanel ) )
368 gtk_widget_hide ( GTK_WIDGET(vw->viking_vlp) );
369 GtkWidget *check_box = gtk_ui_manager_get_widget ( vw->uim, "/ui/MainMenu/View/SetShow/ViewSidePanel" );
370 gtk_check_menu_item_set_active ( GTK_CHECK_MENU_ITEM(check_box), FALSE );
374 if ( a_settings_get_boolean ( VIK_SETTINGS_WIN_STATUSBAR, &statusbar ) )
376 gtk_widget_hide ( GTK_WIDGET(vw->viking_vs) );
377 GtkWidget *check_box = gtk_ui_manager_get_widget ( vw->uim, "/ui/MainMenu/View/SetShow/ViewStatusBar" );
378 gtk_check_menu_item_set_active ( GTK_CHECK_MENU_ITEM(check_box), FALSE );
382 if ( a_settings_get_boolean ( VIK_SETTINGS_WIN_TOOLBAR, &toolbar ) )
384 gtk_widget_hide ( toolbar_get_widget (vw->viking_vtb) );
385 GtkWidget *check_box = gtk_ui_manager_get_widget ( vw->uim, "/ui/MainMenu/View/SetShow/ViewToolBar" );
386 gtk_check_menu_item_set_active ( GTK_CHECK_MENU_ITEM(check_box), FALSE );
390 if ( a_settings_get_boolean ( VIK_SETTINGS_WIN_MENUBAR, &menubar ) )
392 gtk_widget_hide ( gtk_ui_manager_get_widget ( vw->uim, "/ui/MainMenu" ) );
393 GtkWidget *check_box = gtk_ui_manager_get_widget ( vw->uim, "/ui/MainMenu/View/SetShow/ViewMainMenu" );
394 gtk_check_menu_item_set_active ( GTK_CHECK_MENU_ITEM(check_box), FALSE );
405 * determine_location_thread:
406 * @vw: The window that will get updated
407 * @threaddata: Data used by our background thread mechanism
409 * Use the features in vikgoto to determine where we are
410 * Then set up the viewport:
411 * 1. To goto the location
412 * 2. Set an appropriate level zoom for the location type
413 * 3. Some statusbar message feedback
415 static int determine_location_thread ( VikWindow *vw, gpointer threaddata )
419 gint ans = a_vik_goto_where_am_i ( vw->viking_vvp, &ll, &name );
421 int result = a_background_thread_progress ( threaddata, 1.0 );
423 vik_window_statusbar_update ( vw, _("Location lookup aborted"), VIK_STATUSBAR_INFO );
424 return -1; /* Abort thread */
432 // Position found with city precision - so zoom out more
435 else if ( ans == 3 ) {
436 // Position found via country name search - so zoom wayyyy out
440 vik_viewport_set_zoom ( vw->viking_vvp, zoom );
441 vik_viewport_set_center_latlon ( vw->viking_vvp, &ll, FALSE );
443 gchar *message = g_strdup_printf ( _("Location found: %s"), name );
444 vik_window_statusbar_update ( vw, message, VIK_STATUSBAR_INFO );
448 // Signal to redraw from the background
449 vik_layers_panel_emit_update ( vw->viking_vlp );
452 vik_window_statusbar_update ( vw, _("Unable to determine location"), VIK_STATUSBAR_INFO );
458 * Steps to be taken once initial loading has completed
460 void vik_window_new_window_finish ( VikWindow *vw )
462 // Don't add a map if we've loaded a Viking file already
466 // Maybe add a default map layer
467 if ( a_vik_get_add_default_map_layer () ) {
468 VikMapsLayer *vml = VIK_MAPS_LAYER ( vik_layer_create(VIK_LAYER_MAPS, vw->viking_vvp, FALSE) );
469 vik_layer_rename ( VIK_LAYER(vml), _("Default Map") );
470 vik_aggregate_layer_add_layer ( vik_layers_panel_get_top_layer(vw->viking_vlp), VIK_LAYER(vml), TRUE );
475 // If not loaded any file, maybe try the location lookup
476 if ( vw->loaded_type == LOAD_TYPE_READ_FAILURE ) {
477 if ( a_vik_get_startup_method ( ) == VIK_STARTUP_METHOD_AUTO_LOCATION ) {
479 vik_statusbar_set_message ( vw->viking_vs, VIK_STATUSBAR_INFO, _("Trying to determine location...") );
481 a_background_thread ( BACKGROUND_POOL_REMOTE,
483 _("Determining location"),
484 (vik_thr_func) determine_location_thread,
493 static void open_window ( VikWindow *vw, GSList *files )
497 gboolean change_fn = (g_slist_length(files) == 1); /* only change fn if one file */
498 GSList *cur_file = files;
500 // Only open a new window if a viking file
501 gchar *file_name = cur_file->data;
502 if (vw->filename && check_file_magic_vik ( file_name ) ) {
503 VikWindow *newvw = vik_window_new_window ();
505 vik_window_open_file ( newvw, file_name, TRUE );
508 vik_window_open_file ( vw, file_name, change_fn );
511 cur_file = g_slist_next (cur_file);
513 g_slist_free (files);
517 void vik_window_selected_layer(VikWindow *vw, VikLayer *vl)
519 int i, j, tool_count;
520 VikLayerInterface *layer_interface;
522 if (!vw->action_group) return;
524 for (i=0; i<VIK_LAYER_NUM_TYPES; i++) {
526 layer_interface = vik_layer_get_interface(i);
527 tool_count = layer_interface->tools_count;
529 for (j = 0; j < tool_count; j++) {
530 action = gtk_action_group_get_action(vw->action_group,
531 layer_interface->tools[j].radioActionEntry.name);
532 g_object_set(action, "sensitive", i == vl->type, NULL);
533 toolbar_action_set_sensitive ( vw->viking_vtb, vik_layer_get_interface(i)->tools[j].radioActionEntry.name, i == vl->type );
538 static void window_finalize ( GObject *gob )
540 VikWindow *vw = VIK_WINDOW(gob);
541 g_return_if_fail ( vw != NULL );
543 a_background_remove_window ( vw );
545 window_list = g_slist_remove ( window_list, vw );
547 gdk_cursor_unref ( vw->busy_cursor );
549 for (tt = 0; tt < vw->vt->n_tools; tt++ )
550 if ( vw->vt->tools[tt].ti.destroy )
551 vw->vt->tools[tt].ti.destroy ( vw->vt->tools[tt].state );
552 g_free ( vw->vt->tools );
555 vik_toolbar_finalize ( vw->viking_vtb );
557 G_OBJECT_CLASS(parent_class)->finalize(gob);
561 static void vik_window_class_init ( VikWindowClass *klass )
564 GObjectClass *object_class;
566 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);
567 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);
569 object_class = G_OBJECT_CLASS (klass);
571 object_class->finalize = window_finalize;
573 parent_class = g_type_class_peek_parent (klass);
577 static void zoom_changed (GtkMenuShell *menushell,
580 VikWindow *vw = VIK_WINDOW (user_data);
582 GtkWidget *aw = gtk_menu_get_active ( GTK_MENU (menushell) );
583 gint active = GPOINTER_TO_INT(g_object_get_data ( G_OBJECT (aw), "position" ));
585 gdouble zoom_request = pow (2, active-5 );
587 // But has it really changed?
588 gdouble current_zoom = vik_viewport_get_zoom ( vw->viking_vvp );
589 if ( current_zoom != 0.0 && zoom_request != current_zoom ) {
590 vik_viewport_set_zoom ( vw->viking_vvp, zoom_request );
591 // Force drawing update
597 * @mpp: The initial zoom level
599 static GtkWidget *create_zoom_menu_all_levels ( gdouble mpp )
601 GtkWidget *menu = gtk_menu_new ();
602 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" };
605 for (i = 0 ; i < G_N_ELEMENTS(itemLabels) ; i++)
607 GtkWidget *item = gtk_menu_item_new_with_label (itemLabels[i]);
608 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
609 gtk_widget_show (item);
610 g_object_set_data (G_OBJECT (item), "position", GINT_TO_POINTER(i));
613 gint active = 5 + round ( log (mpp) / log (2) );
614 // Ensure value derived from mpp is in bounds of the menu
615 if ( active >= G_N_ELEMENTS(itemLabels) )
616 active = G_N_ELEMENTS(itemLabels) - 1;
619 gtk_menu_set_active ( GTK_MENU(menu), active );
624 static GtkWidget *create_zoom_combo_all_levels ()
626 GtkWidget *combo = vik_combo_box_text_new();
627 vik_combo_box_text_append ( combo, "0.25");
628 vik_combo_box_text_append ( combo, "0.5");
629 vik_combo_box_text_append ( combo, "1");
630 vik_combo_box_text_append ( combo, "2");
631 vik_combo_box_text_append ( combo, "4");
632 vik_combo_box_text_append ( combo, "8");
633 vik_combo_box_text_append ( combo, "16");
634 vik_combo_box_text_append ( combo, "32");
635 vik_combo_box_text_append ( combo, "64");
636 vik_combo_box_text_append ( combo, "128");
637 vik_combo_box_text_append ( combo, "256");
638 vik_combo_box_text_append ( combo, "512");
639 vik_combo_box_text_append ( combo, "1024");
640 vik_combo_box_text_append ( combo, "2048");
641 vik_combo_box_text_append ( combo, "4096");
642 vik_combo_box_text_append ( combo, "8192");
643 vik_combo_box_text_append ( combo, "16384");
644 vik_combo_box_text_append ( combo, "32768");
646 gtk_widget_set_tooltip_text (combo, _("Select zoom level"));
650 static gint zoom_popup_handler (GtkWidget *widget)
654 g_return_val_if_fail (widget != NULL, FALSE);
655 g_return_val_if_fail (GTK_IS_MENU (widget), FALSE);
657 /* The "widget" is the menu that was supplied when
658 * g_signal_connect_swapped() was called.
660 menu = GTK_MENU (widget);
662 gtk_menu_popup (menu, NULL, NULL, NULL, NULL,
663 1, gtk_get_current_event_time());
671 static void drag_data_received_cb ( GtkWidget *widget,
672 GdkDragContext *context,
675 GtkSelectionData *selection_data,
680 gboolean success = FALSE;
682 if ( (selection_data != NULL) && (gtk_selection_data_get_length(selection_data) > 0) ) {
683 switch (target_type) {
685 gchar *str = (gchar*)gtk_selection_data_get_data(selection_data);
686 g_debug ("drag received string:%s \n", str);
688 // Convert string into GSList of individual entries for use with our open signal
689 gchar **entries = g_strsplit(str, "\r\n", 0);
690 GSList *filenames = NULL;
691 gint entry_runner = 0;
692 gchar *entry = entries[entry_runner];
694 if ( g_strcmp0 ( entry, "" ) ) {
695 // Drag+Drop gives URIs. And so in particular, %20 in place of spaces in filenames
696 // thus need to convert the text into a plain string
697 gchar *filename = g_filename_from_uri ( entry, NULL, NULL );
699 filenames = g_slist_append ( filenames, filename );
702 entry = entries[entry_runner];
706 g_signal_emit ( G_OBJECT(VIK_WINDOW_FROM_WIDGET(widget)), window_signals[VW_OPENWINDOW_SIGNAL], 0, filenames );
707 // NB: GSList & contents are freed by main.open_window
716 gtk_drag_finish ( context, success, FALSE, time );
719 static void toolbar_tool_cb ( GtkAction *old, GtkAction *current, gpointer gp )
721 VikWindow *vw = (VikWindow*)gp;
722 GtkAction *action = gtk_action_group_get_action ( vw->action_group, gtk_action_get_name(current) );
724 gtk_action_activate ( action );
727 static void toolbar_reload_cb ( GtkActionGroup *grp, gpointer gp )
729 VikWindow *vw = (VikWindow*)gp;
730 center_changed_cb ( vw );
733 #define VIK_SETTINGS_WIN_MAX "window_maximized"
734 #define VIK_SETTINGS_WIN_FULLSCREEN "window_fullscreen"
735 #define VIK_SETTINGS_WIN_WIDTH "window_width"
736 #define VIK_SETTINGS_WIN_HEIGHT "window_height"
737 #define VIK_SETTINGS_WIN_PANE_POSITION "window_horizontal_pane_position"
738 #define VIK_SETTINGS_WIN_SAVE_IMAGE_WIDTH "window_save_image_width"
739 #define VIK_SETTINGS_WIN_SAVE_IMAGE_HEIGHT "window_save_image_height"
740 #define VIK_SETTINGS_WIN_SAVE_IMAGE_PNG "window_save_image_as_png"
741 #define VIK_SETTINGS_WIN_COPY_CENTRE_FULL_FORMAT "window_copy_centre_full_format"
743 #define VIKING_ACCELERATOR_KEY_FILE "keys.rc"
745 static void vik_window_init ( VikWindow *vw )
747 vw->action_group = NULL;
749 vw->viking_vvp = vik_viewport_new();
750 vw->viking_vlp = vik_layers_panel_new();
751 vik_layers_panel_set_viewport ( vw->viking_vlp, vw->viking_vvp );
752 vw->viking_vs = vik_statusbar_new();
754 vw->vt = toolbox_create(vw);
755 vw->viking_vtb = vik_toolbar_new ();
756 window_create_ui(vw);
757 window_set_filename (vw, NULL);
759 vw->busy_cursor = gdk_cursor_new ( GDK_WATCH );
762 vw->loaded_type = LOAD_TYPE_READ_FAILURE; //AKA none
763 vw->modified = FALSE;
764 vw->only_updating_coord_mode_ui = FALSE;
766 vw->select_move = FALSE;
767 vw->pan_move = FALSE;
768 vw->pan_x = vw->pan_y = -1;
769 vw->single_click_pending = FALSE;
771 gint draw_image_width;
772 if ( a_settings_get_integer ( VIK_SETTINGS_WIN_SAVE_IMAGE_WIDTH, &draw_image_width ) )
773 vw->draw_image_width = draw_image_width;
775 vw->draw_image_width = DRAW_IMAGE_DEFAULT_WIDTH;
776 gint draw_image_height;
777 if ( a_settings_get_integer ( VIK_SETTINGS_WIN_SAVE_IMAGE_HEIGHT, &draw_image_height ) )
778 vw->draw_image_height = draw_image_height;
780 vw->draw_image_height = DRAW_IMAGE_DEFAULT_HEIGHT;
781 gboolean draw_image_save_as_png;
782 if ( a_settings_get_boolean ( VIK_SETTINGS_WIN_SAVE_IMAGE_PNG, &draw_image_save_as_png ) )
783 vw->draw_image_save_as_png = draw_image_save_as_png;
785 vw->draw_image_save_as_png = DRAW_IMAGE_DEFAULT_SAVE_AS_PNG;
787 vw->main_vbox = gtk_vbox_new(FALSE, 1);
788 gtk_container_add (GTK_CONTAINER (vw), vw->main_vbox);
789 vw->menu_hbox = gtk_hbox_new(FALSE, 1);
790 GtkWidget *menu_bar = gtk_ui_manager_get_widget (vw->uim, "/MainMenu");
791 gtk_box_pack_start (GTK_BOX(vw->menu_hbox), menu_bar, FALSE, TRUE, 0);
792 gtk_box_pack_start (GTK_BOX(vw->main_vbox), vw->menu_hbox, FALSE, TRUE, 0);
794 toolbar_init(vw->viking_vtb,
800 (gpointer)vw); // This auto packs toolbar into the vbox
801 // Must be performed post toolbar init
803 for (i=0; i<VIK_LAYER_NUM_TYPES; i++) {
804 for ( j = 0; j < vik_layer_get_interface(i)->tools_count; j++ ) {
805 toolbar_action_set_sensitive ( vw->viking_vtb, vik_layer_get_interface(i)->tools[j].radioActionEntry.name, FALSE );
809 vik_ext_tool_datasources_add_menu_items ( vw, vw->uim );
811 GtkWidget * zoom_levels = gtk_ui_manager_get_widget (vw->uim, "/MainMenu/View/SetZoom");
812 GtkWidget * zoom_levels_menu = create_zoom_menu_all_levels ( vik_viewport_get_zoom(vw->viking_vvp) );
813 gtk_menu_item_set_submenu (GTK_MENU_ITEM (zoom_levels), zoom_levels_menu);
814 g_signal_connect ( G_OBJECT(zoom_levels_menu), "selection-done", G_CALLBACK(zoom_changed), vw);
815 g_signal_connect_swapped ( G_OBJECT(vw->viking_vs), "clicked", G_CALLBACK(zoom_popup_handler), zoom_levels_menu );
817 g_signal_connect (G_OBJECT (vw), "delete_event", G_CALLBACK (delete_event), NULL);
820 g_signal_connect_swapped (G_OBJECT(vw->viking_vvp), "updated_center", G_CALLBACK(center_changed_cb), vw);
822 g_signal_connect_swapped (G_OBJECT(vw->viking_vvp), "expose_event", G_CALLBACK(draw_sync), vw);
823 g_signal_connect_swapped (G_OBJECT(vw->viking_vvp), "configure_event", G_CALLBACK(window_configure_event), vw);
824 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 );
825 g_signal_connect_swapped (G_OBJECT(vw->viking_vvp), "scroll_event", G_CALLBACK(draw_scroll), vw);
826 g_signal_connect_swapped (G_OBJECT(vw->viking_vvp), "button_press_event", G_CALLBACK(draw_click), vw);
827 g_signal_connect_swapped (G_OBJECT(vw->viking_vvp), "button_release_event", G_CALLBACK(draw_release), vw);
828 g_signal_connect_swapped (G_OBJECT(vw->viking_vvp), "motion_notify_event", G_CALLBACK(draw_mouse_motion), vw);
830 g_signal_connect_swapped (G_OBJECT(vw->viking_vlp), "update", G_CALLBACK(draw_update), vw);
831 g_signal_connect_swapped (G_OBJECT(vw->viking_vlp), "delete_layer", G_CALLBACK(vik_window_clear_highlight), vw);
833 // Allow key presses to be processed anywhere
834 g_signal_connect_swapped (G_OBJECT (vw), "key_press_event", G_CALLBACK (key_press_event), vw);
836 // Set initial button sensitivity
837 center_changed_cb ( vw );
839 vw->hpaned = gtk_hpaned_new ();
840 gtk_paned_pack1 ( GTK_PANED(vw->hpaned), GTK_WIDGET (vw->viking_vlp), FALSE, TRUE );
841 gtk_paned_pack2 ( GTK_PANED(vw->hpaned), GTK_WIDGET (vw->viking_vvp), TRUE, TRUE );
843 /* This packs the button into the window (a gtk container). */
844 gtk_box_pack_start (GTK_BOX(vw->main_vbox), vw->hpaned, TRUE, TRUE, 0);
846 gtk_box_pack_end (GTK_BOX(vw->main_vbox), GTK_WIDGET(vw->viking_vs), FALSE, TRUE, 0);
848 a_background_add_window ( vw );
850 window_list = g_slist_prepend ( window_list, vw);
852 gint height = VIKING_WINDOW_HEIGHT;
853 gint width = VIKING_WINDOW_WIDTH;
855 if ( a_vik_get_restore_window_state() ) {
856 if ( a_settings_get_integer ( VIK_SETTINGS_WIN_HEIGHT, &height ) ) {
857 // Enforce a basic minimum size
862 // No setting - so use default
863 height = VIKING_WINDOW_HEIGHT;
865 if ( a_settings_get_integer ( VIK_SETTINGS_WIN_WIDTH, &width ) ) {
866 // Enforce a basic minimum size
871 // No setting - so use default
872 width = VIKING_WINDOW_WIDTH;
875 if ( a_settings_get_boolean ( VIK_SETTINGS_WIN_MAX, &maxed ) )
877 gtk_window_maximize ( GTK_WINDOW(vw) );
880 if ( a_settings_get_boolean ( VIK_SETTINGS_WIN_FULLSCREEN, &full ) ) {
882 vw->show_full_screen = TRUE;
883 gtk_window_fullscreen ( GTK_WINDOW(vw) );
884 GtkWidget *check_box = gtk_ui_manager_get_widget ( vw->uim, "/ui/MainMenu/View/FullScreen" );
886 gtk_check_menu_item_set_active ( GTK_CHECK_MENU_ITEM(check_box), TRUE );
890 gint position = -1; // Let GTK determine default positioning
891 if ( !a_settings_get_integer ( VIK_SETTINGS_WIN_PANE_POSITION, &position ) ) {
894 gtk_paned_set_position ( GTK_PANED(vw->hpaned), position );
897 gtk_window_set_default_size ( GTK_WINDOW(vw), width, height );
899 vw->show_side_panel = TRUE;
900 vw->show_statusbar = TRUE;
901 vw->show_toolbar = TRUE;
902 vw->show_main_menu = TRUE;
904 // Only accept Drag and Drop of files onto the viewport
905 gtk_drag_dest_set ( GTK_WIDGET(vw->viking_vvp), GTK_DEST_DEFAULT_ALL, NULL, 0, GDK_ACTION_COPY );
906 gtk_drag_dest_add_uri_targets ( GTK_WIDGET(vw->viking_vvp) );
907 g_signal_connect ( GTK_WIDGET(vw->viking_vvp), "drag-data-received", G_CALLBACK(drag_data_received_cb), NULL );
909 // Store the thread value so comparisons can be made to determine the gdk update method
910 // Hopefully we are storing the main thread value here :)
911 // [ATM any window initialization is always be performed by the main thread]
912 vw->thread = g_thread_self();
914 // Set the default tool + mode
915 gtk_action_activate ( gtk_action_group_get_action ( vw->action_group, "Pan" ) );
916 gtk_action_activate ( gtk_action_group_get_action ( vw->action_group, "ModeMercator" ) );
918 gchar *accel_file_name = g_build_filename ( a_get_viking_dir(), VIKING_ACCELERATOR_KEY_FILE, NULL );
919 gtk_accel_map_load ( accel_file_name );
920 g_free ( accel_file_name );
923 static VikWindow *window_new ()
925 return VIK_WINDOW ( g_object_new ( VIK_WINDOW_TYPE, NULL ) );
929 * Update the displayed map
930 * Only update the top most visible map layer
931 * ATM this assumes (as per defaults) the top most map has full alpha setting
932 * such that other other maps even though they may be active will not be seen
933 * It's more complicated to work out which maps are actually visible due to alpha settings
934 * and overkill for this simple refresh method.
936 static void simple_map_update ( VikWindow *vw, gboolean only_new )
938 // Find the most relevent single map layer to operate on
939 VikLayer *vl = vik_aggregate_layer_get_top_visible_layer_of_type (vik_layers_panel_get_top_layer(vw->viking_vlp), VIK_LAYER_MAPS);
941 vik_maps_layer_download ( VIK_MAPS_LAYER(vl), vw->viking_vvp, only_new );
945 * This is the global key press handler
946 * Global shortcuts are available at any time and hence are not restricted to when a certain tool is enabled
948 static gboolean key_press_event( VikWindow *vw, GdkEventKey *event, gpointer data )
950 // The keys handled here are not in the menuing system for a couple of reasons:
951 // . Keeps the menu size compact (alebit at expense of discoverably)
952 // . Allows differing key bindings to perform the same actions
954 // First decide if key events are related to the maps layer
955 gboolean map_download = FALSE;
956 gboolean map_download_only_new = TRUE; // Only new or reload
958 GdkModifierType modifiers = gtk_accelerator_get_default_mod_mask();
960 // Standard 'Refresh' keys: F5 or Ctrl+r
961 // Note 'F5' is actually handled via draw_refresh_cb() later on
962 // (not 'R' it's 'r' notice the case difference!!)
963 if ( event->keyval == GDK_r && (event->state & modifiers) == GDK_CONTROL_MASK ) {
965 map_download_only_new = TRUE;
967 // Full cache reload with Ctrl+F5 or Ctrl+Shift+r [This is not in the menu system]
968 // Note the use of uppercase R here since shift key has been pressed
969 else if ( (event->keyval == GDK_F5 && (event->state & modifiers) == GDK_CONTROL_MASK ) ||
970 ( event->keyval == GDK_R && (event->state & modifiers) == (GDK_CONTROL_MASK + GDK_SHIFT_MASK) ) ) {
972 map_download_only_new = FALSE;
974 // Standard Ctrl+KP+ / Ctrl+KP- to zoom in/out respectively
975 else if ( event->keyval == GDK_KEY_KP_Add && (event->state & modifiers) == GDK_CONTROL_MASK ) {
976 vik_viewport_zoom_in ( vw->viking_vvp );
978 return TRUE; // handled keypress
980 else if ( event->keyval == GDK_KEY_KP_Subtract && (event->state & modifiers) == GDK_CONTROL_MASK ) {
981 vik_viewport_zoom_out ( vw->viking_vvp );
983 return TRUE; // handled keypress
986 if ( map_download ) {
987 simple_map_update ( vw, map_download_only_new );
988 return TRUE; // handled keypress
991 VikLayer *vl = vik_layers_panel_get_selected ( vw->viking_vlp );
992 if (vl && vw->vt->active_tool != -1 && vw->vt->tools[vw->vt->active_tool].ti.key_press ) {
993 gint ltype = vw->vt->tools[vw->vt->active_tool].layer_type;
994 if ( vl && ltype == vl->type )
995 return vw->vt->tools[vw->vt->active_tool].ti.key_press(vl, event, vw->vt->tools[vw->vt->active_tool].state);
998 // Ensure called only on window tools (i.e. not on any of the Layer tools since the layer is NULL)
999 if ( vw->current_tool < TOOL_LAYER ) {
1000 // No layer - but enable window tool keypress processing - these should be able to handle a NULL layer
1001 if ( vw->vt->tools[vw->vt->active_tool].ti.key_press ) {
1002 return vw->vt->tools[vw->vt->active_tool].ti.key_press ( vl, event, vw->vt->tools[vw->vt->active_tool].state );
1006 /* Restore Main Menu via Escape key if the user has hidden it */
1007 /* This key is more likely to be used as they may not remember the function key */
1008 if ( event->keyval == GDK_Escape ) {
1009 GtkWidget *check_box = gtk_ui_manager_get_widget ( vw->uim, "/ui/MainMenu/View/SetShow/ViewMainMenu" );
1011 gboolean state = gtk_check_menu_item_get_active ( GTK_CHECK_MENU_ITEM(check_box) );
1013 gtk_widget_show ( gtk_ui_manager_get_widget ( vw->uim, "/ui/MainMenu" ) );
1014 gtk_check_menu_item_set_active ( GTK_CHECK_MENU_ITEM(check_box), TRUE );
1015 return TRUE; /* handled keypress */
1020 return FALSE; /* don't handle the keypress */
1023 static gboolean delete_event( VikWindow *vw )
1025 #ifdef VIKING_PROMPT_IF_MODIFIED
1032 dia = GTK_DIALOG ( gtk_message_dialog_new ( GTK_WINDOW(vw), GTK_DIALOG_DESTROY_WITH_PARENT, GTK_MESSAGE_QUESTION, GTK_BUTTONS_NONE,
1033 _("Do you want to save the changes you made to the document \"%s\"?\n"
1035 "Your changes will be lost if you don't save them."),
1036 window_get_filename ( vw ) ) );
1037 gtk_dialog_add_buttons ( dia, _("Don't Save"), GTK_RESPONSE_NO, GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, GTK_STOCK_SAVE, GTK_RESPONSE_YES, NULL );
1038 switch ( gtk_dialog_run ( dia ) )
1040 case GTK_RESPONSE_NO: gtk_widget_destroy ( GTK_WIDGET(dia) ); return FALSE;
1041 case GTK_RESPONSE_CANCEL: gtk_widget_destroy ( GTK_WIDGET(dia) ); return TRUE;
1042 default: gtk_widget_destroy ( GTK_WIDGET(dia) ); return ! save_file(NULL, vw);
1046 if ( window_count == 1 ) {
1047 // On the final window close - save latest state - if it's wanted...
1048 if ( a_vik_get_restore_window_state() ) {
1049 gint state = gdk_window_get_state ( GTK_WIDGET(vw)->window );
1050 gboolean state_max = state & GDK_WINDOW_STATE_MAXIMIZED;
1051 a_settings_set_boolean ( VIK_SETTINGS_WIN_MAX, state_max );
1053 gboolean state_fullscreen = state & GDK_WINDOW_STATE_FULLSCREEN;
1054 a_settings_set_boolean ( VIK_SETTINGS_WIN_FULLSCREEN, state_fullscreen );
1056 a_settings_set_boolean ( VIK_SETTINGS_WIN_SIDEPANEL, GTK_WIDGET_VISIBLE (GTK_WIDGET(vw->viking_vlp)) );
1058 a_settings_set_boolean ( VIK_SETTINGS_WIN_STATUSBAR, GTK_WIDGET_VISIBLE (GTK_WIDGET(vw->viking_vs)) );
1060 a_settings_set_boolean ( VIK_SETTINGS_WIN_TOOLBAR, GTK_WIDGET_VISIBLE (toolbar_get_widget(vw->viking_vtb)) );
1062 // If supersized - no need to save the enlarged width+height values
1063 if ( ! (state_fullscreen || state_max) ) {
1065 gtk_window_get_size ( GTK_WINDOW (vw), &width, &height );
1066 a_settings_set_integer ( VIK_SETTINGS_WIN_WIDTH, width );
1067 a_settings_set_integer ( VIK_SETTINGS_WIN_HEIGHT, height );
1070 a_settings_set_integer ( VIK_SETTINGS_WIN_PANE_POSITION, gtk_paned_get_position (GTK_PANED(vw->hpaned)) );
1073 a_settings_set_integer ( VIK_SETTINGS_WIN_SAVE_IMAGE_WIDTH, vw->draw_image_width );
1074 a_settings_set_integer ( VIK_SETTINGS_WIN_SAVE_IMAGE_HEIGHT, vw->draw_image_height );
1075 a_settings_set_boolean ( VIK_SETTINGS_WIN_SAVE_IMAGE_PNG, vw->draw_image_save_as_png );
1077 gchar *accel_file_name = g_build_filename ( a_get_viking_dir(), VIKING_ACCELERATOR_KEY_FILE, NULL );
1078 gtk_accel_map_save ( accel_file_name );
1079 g_free ( accel_file_name );
1086 static void newwindow_cb ( GtkAction *a, VikWindow *vw )
1088 g_signal_emit ( G_OBJECT(vw), window_signals[VW_NEWWINDOW_SIGNAL], 0 );
1091 static void draw_update ( VikWindow *vw )
1097 static void draw_sync ( VikWindow *vw )
1099 vik_viewport_sync(vw->viking_vvp);
1104 * Split the status update, as sometimes only need to update the tool part
1105 * also on initialization the zoom related stuff is not ready to be used
1107 static void draw_status_tool ( VikWindow *vw )
1109 if ( vw->current_tool == TOOL_LAYER )
1110 // Use tooltip rather than the internal name as the tooltip is i8n
1111 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 );
1113 vik_statusbar_set_message ( vw->viking_vs, VIK_STATUSBAR_TOOL, _(tool_names[vw->current_tool]) );
1116 static void draw_status ( VikWindow *vw )
1118 static gchar zoom_level[22];
1119 gdouble xmpp = vik_viewport_get_xmpp (vw->viking_vvp);
1120 gdouble ympp = vik_viewport_get_ympp(vw->viking_vvp);
1121 gchar *unit = vik_viewport_get_coord_mode(vw->viking_vvp) == VIK_COORD_UTM ? _("mpp") : _("pixelfact");
1123 g_snprintf ( zoom_level, 22, "%.3f/%.3f %s", xmpp, ympp, unit );
1125 if ( (int)xmpp - xmpp < 0.0 )
1126 g_snprintf ( zoom_level, 22, "%.3f %s", xmpp, unit );
1128 /* xmpp should be a whole number so don't show useless .000 bit */
1129 g_snprintf ( zoom_level, 22, "%d %s", (int)xmpp, unit );
1131 vik_statusbar_set_message ( vw->viking_vs, VIK_STATUSBAR_ZOOM, zoom_level );
1133 draw_status_tool ( vw );
1136 void vik_window_set_redraw_trigger(VikLayer *vl)
1138 VikWindow *vw = VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vl));
1143 static void window_configure_event ( VikWindow *vw )
1145 static int first = 1;
1148 // This is a hack to set the cursor corresponding to the first tool
1149 // FIXME find the correct way to initialize both tool and its cursor
1151 vw->viewport_cursor = (GdkCursor *)toolbox_get_cursor(vw->vt, "Pan");
1152 /* We set cursor, even if it is NULL: it resets to default */
1153 gdk_window_set_cursor ( gtk_widget_get_window(GTK_WIDGET(vw->viking_vvp)), vw->viewport_cursor );
1157 static void draw_redraw ( VikWindow *vw )
1159 VikCoord old_center = vw->trigger_center;
1160 vw->trigger_center = *(vik_viewport_get_center(vw->viking_vvp));
1161 VikLayer *new_trigger = vw->trigger;
1163 VikLayer *old_trigger = VIK_LAYER(vik_viewport_get_trigger(vw->viking_vvp));
1165 if ( ! new_trigger )
1166 ; /* do nothing -- have to redraw everything. */
1167 else if ( (old_trigger != new_trigger) || !vik_coord_equals(&old_center, &vw->trigger_center) || (new_trigger->type == VIK_LAYER_AGGREGATE) )
1168 vik_viewport_set_trigger ( vw->viking_vvp, new_trigger ); /* todo: set to half_drawn mode if new trigger is above old */
1170 vik_viewport_set_half_drawn ( vw->viking_vvp, TRUE );
1173 vik_viewport_clear ( vw->viking_vvp);
1174 // Main layer drawing
1175 vik_layers_panel_draw_all ( vw->viking_vlp );
1176 // Draw highlight (possibly again but ensures it is on top - especially for when tracks overlap)
1177 if ( vik_viewport_get_draw_highlight (vw->viking_vvp) ) {
1178 if ( vw->containing_vtl && (vw->selected_tracks || vw->selected_waypoints ) ) {
1179 vik_trw_layer_draw_highlight_items ( vw->containing_vtl, vw->selected_tracks, vw->selected_waypoints, vw->viking_vvp );
1181 else if ( vw->containing_vtl && (vw->selected_track || vw->selected_waypoint) ) {
1182 vik_trw_layer_draw_highlight_item ( vw->containing_vtl, vw->selected_track, vw->selected_waypoint, vw->viking_vvp );
1184 else if ( vw->selected_vtl ) {
1185 vik_trw_layer_draw_highlight ( vw->selected_vtl, vw->viking_vvp );
1188 // Other viewport decoration items on top if they are enabled/in use
1189 vik_viewport_draw_scale ( vw->viking_vvp );
1190 vik_viewport_draw_copyright ( vw->viking_vvp );
1191 vik_viewport_draw_centermark ( vw->viking_vvp );
1192 vik_viewport_draw_logo ( vw->viking_vvp );
1194 vik_viewport_set_half_drawn ( vw->viking_vvp, FALSE ); /* just in case. */
1197 gboolean draw_buf_done = TRUE;
1199 static gboolean draw_buf(gpointer data)
1201 gpointer *pass_along = data;
1202 gdk_threads_enter();
1203 gdk_draw_drawable (pass_along[0], pass_along[1],
1204 pass_along[2], 0, 0, 0, 0, -1, -1);
1205 draw_buf_done = TRUE;
1206 gdk_threads_leave();
1211 /* Mouse event handlers ************************************************************************/
1213 static void vik_window_pan_click (VikWindow *vw, GdkEventButton *event)
1215 /* set panning origin */
1216 vw->pan_move = FALSE;
1217 vw->pan_x = (gint) event->x;
1218 vw->pan_y = (gint) event->y;
1221 static void draw_click (VikWindow *vw, GdkEventButton *event)
1223 gtk_widget_grab_focus ( GTK_WIDGET(vw->viking_vvp) );
1225 /* middle button pressed. we reserve all middle button and scroll events
1226 * for panning and zooming; tools only get left/right/movement
1228 if ( event->button == 2) {
1229 if ( vw->vt->tools[vw->vt->active_tool].ti.pan_handler )
1230 // Tool still may need to do something (such as disable something)
1231 toolbox_click(vw->vt, event);
1232 vik_window_pan_click ( vw, event );
1235 toolbox_click(vw->vt, event);
1239 static void vik_window_pan_move (VikWindow *vw, GdkEventMotion *event)
1241 if ( vw->pan_x != -1 ) {
1242 vik_viewport_set_center_screen ( vw->viking_vvp, vik_viewport_get_width(vw->viking_vvp)/2 - event->x + vw->pan_x,
1243 vik_viewport_get_height(vw->viking_vvp)/2 - event->y + vw->pan_y );
1244 vw->pan_move = TRUE;
1245 vw->pan_x = event->x;
1246 vw->pan_y = event->y;
1252 * get_location_strings:
1254 * Utility function to get positional strings for the given location
1255 * lat and lon strings will get allocated and so need to be freed after use
1257 static void get_location_strings ( VikWindow *vw, struct UTM utm, gchar **lat, gchar **lon )
1259 if ( vik_viewport_get_drawmode ( vw->viking_vvp ) == VIK_VIEWPORT_DRAWMODE_UTM ) {
1260 // Reuse lat for the first part (Zone + N or S, and lon for the second part (easting and northing) of a UTM format:
1261 // ZONE[N|S] EASTING NORTHING
1262 *lat = g_malloc(4*sizeof(gchar));
1263 // NB zone is stored in a char but is an actual number
1264 g_snprintf (*lat, 4, "%d%c", utm.zone, utm.letter);
1265 *lon = g_malloc(16*sizeof(gchar));
1266 g_snprintf (*lon, 16, "%d %d", (gint)utm.easting, (gint)utm.northing);
1270 a_coords_utm_to_latlon ( &utm, &ll );
1271 a_coords_latlon_to_string ( &ll, lat, lon );
1275 static void draw_mouse_motion (VikWindow *vw, GdkEventMotion *event)
1277 static VikCoord coord;
1278 static struct UTM utm;
1279 #define BUFFER_SIZE 50
1280 static char pointer_buf[BUFFER_SIZE];
1281 gchar *lat = NULL, *lon = NULL;
1284 VikDemInterpol interpol_method;
1286 /* This is a hack, but work far the best, at least for single pointer systems.
1287 * See http://bugzilla.gnome.org/show_bug.cgi?id=587714 for more. */
1289 gdk_window_get_pointer (event->window, &x, &y, NULL);
1293 toolbox_move(vw->vt, event);
1295 vik_viewport_screen_to_coord ( vw->viking_vvp, event->x, event->y, &coord );
1296 vik_coord_to_utm ( &coord, &utm );
1298 get_location_strings ( vw, utm, &lat, &lon );
1300 /* Change interpolate method according to scale */
1301 zoom = vik_viewport_get_zoom(vw->viking_vvp);
1303 interpol_method = VIK_DEM_INTERPOL_NONE;
1304 else if (zoom >= 1.0)
1305 interpol_method = VIK_DEM_INTERPOL_SIMPLE;
1307 interpol_method = VIK_DEM_INTERPOL_BEST;
1308 if ((alt = a_dems_get_elev_by_coord(&coord, interpol_method)) != VIK_DEM_INVALID_ELEVATION) {
1309 if ( a_vik_get_units_height () == VIK_UNITS_HEIGHT_METRES )
1310 g_snprintf ( pointer_buf, BUFFER_SIZE, _("%s %s %dm"), lat, lon, alt );
1312 g_snprintf ( pointer_buf, BUFFER_SIZE, _("%s %s %dft"), lat, lon, (int)VIK_METERS_TO_FEET(alt) );
1315 g_snprintf ( pointer_buf, BUFFER_SIZE, _("%s %s"), lat, lon );
1320 vik_statusbar_set_message ( vw->viking_vs, VIK_STATUSBAR_POSITION, pointer_buf );
1322 vik_window_pan_move ( vw, event );
1324 /* This is recommended by the GTK+ documentation, but does not work properly.
1325 * Use deprecated way until GTK+ gets a solution for correct motion hint handling:
1326 * http://bugzilla.gnome.org/show_bug.cgi?id=587714
1328 /* gdk_event_request_motions ( event ); */
1332 * Action the single click after a small timeout
1333 * If a double click has occurred then this will do nothing
1335 static gboolean vik_window_pan_timeout (VikWindow *vw)
1337 if ( ! vw->single_click_pending ) {
1338 // Double click happened, so don't do anything
1342 /* set panning origin */
1343 vw->pan_move = FALSE;
1344 vw->single_click_pending = FALSE;
1345 vik_viewport_set_center_screen ( vw->viking_vvp, vw->delayed_pan_x, vw->delayed_pan_y );
1348 // Really turn off the pan moving!!
1349 vw->pan_x = vw->pan_y = -1;
1353 static void vik_window_pan_release ( VikWindow *vw, GdkEventButton *event )
1355 gboolean do_draw = TRUE;
1357 if ( vw->pan_move == FALSE ) {
1358 vw->single_click_pending = !vw->single_click_pending;
1360 if ( vw->single_click_pending ) {
1361 // Store offset to use
1362 vw->delayed_pan_x = vw->pan_x;
1363 vw->delayed_pan_y = vw->pan_y;
1364 // Get double click time
1365 GtkSettings *gs = gtk_widget_get_settings ( GTK_WIDGET(vw) );
1366 GValue dct = { 0 }; // = G_VALUE_INIT; // GLIB 2.30+ only
1367 g_value_init ( &dct, G_TYPE_INT );
1368 g_object_get_property ( G_OBJECT(gs), "gtk-double-click-time", &dct );
1369 // Give chance for a double click to occur
1370 gint timer = g_value_get_int ( &dct ) + 50;
1371 g_timeout_add ( timer, (GSourceFunc)vik_window_pan_timeout, vw );
1375 vik_viewport_set_center_screen ( vw->viking_vvp, vw->pan_x, vw->pan_y );
1379 vik_viewport_set_center_screen ( vw->viking_vvp, vik_viewport_get_width(vw->viking_vvp)/2 - event->x + vw->pan_x,
1380 vik_viewport_get_height(vw->viking_vvp)/2 - event->y + vw->pan_y );
1383 vw->pan_move = FALSE;
1384 vw->pan_x = vw->pan_y = -1;
1389 static void draw_release ( VikWindow *vw, GdkEventButton *event )
1391 gtk_widget_grab_focus ( GTK_WIDGET(vw->viking_vvp) );
1393 if ( event->button == 2 ) { /* move / pan */
1394 if ( vw->vt->tools[vw->vt->active_tool].ti.pan_handler )
1395 // Tool still may need to do something (such as reenable something)
1396 toolbox_release(vw->vt, event);
1397 vik_window_pan_release ( vw, event );
1400 toolbox_release(vw->vt, event);
1404 static void draw_scroll (VikWindow *vw, GdkEventScroll *event)
1406 guint modifiers = event->state & (GDK_SHIFT_MASK | GDK_CONTROL_MASK);
1407 if ( modifiers == GDK_CONTROL_MASK ) {
1408 /* control == pan up & down */
1409 if ( event->direction == GDK_SCROLL_UP )
1410 vik_viewport_set_center_screen ( vw->viking_vvp, vik_viewport_get_width(vw->viking_vvp)/2, vik_viewport_get_height(vw->viking_vvp)/3 );
1412 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 );
1413 } else if ( modifiers == GDK_SHIFT_MASK ) {
1414 /* shift == pan left & right */
1415 if ( event->direction == GDK_SCROLL_UP )
1416 vik_viewport_set_center_screen ( vw->viking_vvp, vik_viewport_get_width(vw->viking_vvp)/3, vik_viewport_get_height(vw->viking_vvp)/2 );
1418 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 );
1419 } else if ( modifiers == (GDK_CONTROL_MASK | GDK_SHIFT_MASK) ) {
1420 // This zoom is on the center position
1421 if ( event->direction == GDK_SCROLL_UP )
1422 vik_viewport_zoom_in (vw->viking_vvp);
1424 vik_viewport_zoom_out (vw->viking_vvp);
1426 /* make sure mouse is still over the same point on the map when we zoom */
1429 gint center_x = vik_viewport_get_width ( vw->viking_vvp ) / 2;
1430 gint center_y = vik_viewport_get_height ( vw->viking_vvp ) / 2;
1431 vik_viewport_screen_to_coord ( vw->viking_vvp, event->x, event->y, &coord );
1432 if ( event->direction == GDK_SCROLL_UP )
1433 vik_viewport_zoom_in (vw->viking_vvp);
1435 vik_viewport_zoom_out(vw->viking_vvp);
1436 vik_viewport_coord_to_screen ( vw->viking_vvp, &coord, &x, &y );
1437 vik_viewport_set_center_screen ( vw->viking_vvp, center_x + (x - event->x),
1438 center_y + (y - event->y) );
1446 /********************************************************************************
1448 ********************************************************************************/
1449 static void draw_ruler(VikViewport *vvp, GdkDrawable *d, GdkGC *gc, gint x1, gint y1, gint x2, gint y2, gdouble distance)
1453 GdkGC *labgc = vik_viewport_new_gc ( vvp, "#cccccc", 1);
1454 GdkGC *thickgc = gdk_gc_new(d);
1456 gdouble len = sqrt((x1-x2)*(x1-x2) + (y1-y2)*(y1-y2));
1457 gdouble dx = (x2-x1)/len*10;
1458 gdouble dy = (y2-y1)/len*10;
1459 gdouble c = cos(DEG2RAD(15.0));
1460 gdouble s = sin(DEG2RAD(15.0));
1462 gdouble baseangle = 0;
1465 /* draw line with arrow ends */
1467 gint tmp_x1=x1, tmp_y1=y1, tmp_x2=x2, tmp_y2=y2;
1468 a_viewport_clip_line(&tmp_x1, &tmp_y1, &tmp_x2, &tmp_y2);
1469 gdk_draw_line(d, gc, tmp_x1, tmp_y1, tmp_x2, tmp_y2);
1472 a_viewport_clip_line(&x1, &y1, &x2, &y2);
1473 gdk_draw_line(d, gc, x1, y1, x2, y2);
1475 gdk_draw_line(d, gc, x1 - dy, y1 + dx, x1 + dy, y1 - dx);
1476 gdk_draw_line(d, gc, x2 - dy, y2 + dx, x2 + dy, y2 - dx);
1477 gdk_draw_line(d, gc, x2, y2, x2 - (dx * c + dy * s), y2 - (dy * c - dx * s));
1478 gdk_draw_line(d, gc, x2, y2, x2 - (dx * c - dy * s), y2 - (dy * c + dx * s));
1479 gdk_draw_line(d, gc, x1, y1, x1 + (dx * c + dy * s), y1 + (dy * c - dx * s));
1480 gdk_draw_line(d, gc, x1, y1, x1 + (dx * c - dy * s), y1 + (dy * c + dx * s));
1486 vik_viewport_compute_bearing ( vvp, x1, y1, x2, y2, &angle, &baseangle );
1490 gdk_gc_copy(thickgc, gc);
1491 gdk_gc_set_line_attributes(thickgc, CW, GDK_LINE_SOLID, GDK_CAP_BUTT, GDK_JOIN_MITER);
1492 gdk_color_parse("#2255cc", &color);
1493 gdk_gc_set_rgb_fg_color(thickgc, &color);
1495 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);
1498 gdk_gc_copy(thickgc, gc);
1499 gdk_gc_set_line_attributes(thickgc, 2, GDK_LINE_SOLID, GDK_CAP_BUTT, GDK_JOIN_MITER);
1500 for (i=0; i<180; i++) {
1501 c = cos(DEG2RAD(i)*2 + baseangle);
1502 s = sin(DEG2RAD(i)*2 + baseangle);
1505 gdk_draw_line (d, gc, x1 + CR*c, y1 + CR*s, x1 + (CR+CW)*c, y1 + (CR+CW)*s);
1507 gdouble ticksize = 2*CW;
1508 gdk_draw_line (d, thickgc, x1 + (CR-CW)*c, y1 + (CR-CW)*s, x1 + (CR+ticksize)*c, y1 + (CR+ticksize)*s);
1512 gdk_draw_arc (d, gc, FALSE, x1-CR, y1-CR, 2*CR, 2*CR, 0, 64*360);
1513 gdk_draw_arc (d, gc, FALSE, x1-CR-CW, y1-CR-CW, 2*(CR+CW), 2*(CR+CW), 0, 64*360);
1514 gdk_draw_arc (d, gc, FALSE, x1-CR+CW, y1-CR+CW, 2*(CR-CW), 2*(CR-CW), 0, 64*360);
1515 c = (CR+CW*2)*cos(baseangle);
1516 s = (CR+CW*2)*sin(baseangle);
1517 gdk_draw_line (d, gc, x1-c, y1-s, x1+c, y1+s);
1518 gdk_draw_line (d, gc, x1+s, y1-c, x1-s, y1+c);
1521 #define LABEL(x, y, w, h) { \
1522 gdk_draw_rectangle(d, labgc, TRUE, (x)-2, (y)-1, (w)+4, (h)+1); \
1523 gdk_draw_rectangle(d, gc, FALSE, (x)-2, (y)-1, (w)+4, (h)+1); \
1524 gdk_draw_layout(d, gc, (x), (y), pl); }
1526 gint wd, hd, xd, yd;
1527 gint wb, hb, xb, yb;
1529 pl = gtk_widget_create_pango_layout (GTK_WIDGET(vvp), NULL);
1530 pango_layout_set_font_description (pl, gtk_widget_get_style(GTK_WIDGET(vvp))->font_desc);
1531 pango_layout_set_text(pl, "N", -1);
1532 gdk_draw_layout(d, gc, x1-5, y1-CR-3*CW-8, pl);
1534 /* draw label with distance */
1535 vik_units_distance_t dist_units = a_vik_get_units_distance ();
1536 switch (dist_units) {
1537 case VIK_UNITS_DISTANCE_KILOMETRES:
1538 if (distance >= 1000 && distance < 100000) {
1539 g_sprintf(str, "%3.2f km", distance/1000.0);
1540 } else if (distance < 1000) {
1541 g_sprintf(str, "%d m", (int)distance);
1543 g_sprintf(str, "%d km", (int)distance/1000);
1546 case VIK_UNITS_DISTANCE_MILES:
1547 if (distance >= VIK_MILES_TO_METERS(1) && distance < VIK_MILES_TO_METERS(100)) {
1548 g_sprintf(str, "%3.2f miles", VIK_METERS_TO_MILES(distance));
1549 } else if (distance < VIK_MILES_TO_METERS(1)) {
1550 g_sprintf(str, "%d yards", (int)(distance*1.0936133));
1552 g_sprintf(str, "%d miles", (int)VIK_METERS_TO_MILES(distance));
1555 case VIK_UNITS_DISTANCE_NAUTICAL_MILES:
1556 if (distance >= VIK_NAUTICAL_MILES_TO_METERS(1) && distance < VIK_NAUTICAL_MILES_TO_METERS(100)) {
1557 g_sprintf(str, "%3.2f NM", VIK_METERS_TO_NAUTICAL_MILES(distance));
1558 } else if (distance < VIK_NAUTICAL_MILES_TO_METERS(1)) {
1559 g_sprintf(str, "%d yards", (int)(distance*1.0936133));
1561 g_sprintf(str, "%d NM", (int)VIK_METERS_TO_NAUTICAL_MILES(distance));
1565 g_critical("Houston, we've had a problem. distance=%d", dist_units);
1568 pango_layout_set_text(pl, str, -1);
1570 pango_layout_get_pixel_size ( pl, &wd, &hd );
1572 xd = (x1+x2)/2 + dy;
1573 yd = (y1+y2)/2 - hd/2 - dx;
1575 xd = (x1+x2)/2 - dy;
1576 yd = (y1+y2)/2 - hd/2 + dx;
1579 if ( xd < -5 || yd < -5 || xd > vik_viewport_get_width(vvp)+5 || yd > vik_viewport_get_height(vvp)+5 ) {
1584 LABEL(xd, yd, wd, hd);
1586 /* draw label with bearing */
1587 g_sprintf(str, "%3.1f°", RAD2DEG(angle));
1588 pango_layout_set_text(pl, str, -1);
1589 pango_layout_get_pixel_size ( pl, &wb, &hb );
1590 xb = x1 + CR*cos(angle-M_PI_2);
1591 yb = y1 + CR*sin(angle-M_PI_2);
1593 if ( xb < -5 || yb < -5 || xb > vik_viewport_get_width(vvp)+5 || yb > vik_viewport_get_height(vvp)+5 ) {
1599 GdkRectangle r1 = {xd-2, yd-1, wd+4, hd+1}, r2 = {xb-2, yb-1, wb+4, hb+1};
1600 if (gdk_rectangle_intersect(&r1, &r2, &r2)) {
1604 LABEL(xb, yb, wb, hb);
1608 g_object_unref ( G_OBJECT ( pl ) );
1609 g_object_unref ( G_OBJECT ( labgc ) );
1610 g_object_unref ( G_OBJECT ( thickgc ) );
1616 gboolean has_oldcoord;
1618 } ruler_tool_state_t;
1620 static gpointer ruler_create (VikWindow *vw, VikViewport *vvp)
1622 ruler_tool_state_t *s = g_new(ruler_tool_state_t, 1);
1625 s->has_oldcoord = FALSE;
1629 static void ruler_destroy (ruler_tool_state_t *s)
1634 static VikLayerToolFuncStatus ruler_click (VikLayer *vl, GdkEventButton *event, ruler_tool_state_t *s)
1639 if ( event->button == 1 ) {
1640 gchar *lat=NULL, *lon=NULL;
1641 vik_viewport_screen_to_coord ( s->vvp, (gint) event->x, (gint) event->y, &coord );
1642 vik_coord_to_latlon ( &coord, &ll );
1643 a_coords_latlon_to_string ( &ll, &lat, &lon );
1644 if ( s->has_oldcoord ) {
1645 vik_units_distance_t dist_units = a_vik_get_units_distance ();
1646 switch (dist_units) {
1647 case VIK_UNITS_DISTANCE_KILOMETRES:
1648 temp = g_strdup_printf ( "%s %s DIFF %f meters", lat, lon, vik_coord_diff( &coord, &(s->oldcoord) ) );
1650 case VIK_UNITS_DISTANCE_MILES:
1651 temp = g_strdup_printf ( "%s %s DIFF %f miles", lat, lon, VIK_METERS_TO_MILES(vik_coord_diff( &coord, &(s->oldcoord) )) );
1653 case VIK_UNITS_DISTANCE_NAUTICAL_MILES:
1654 temp = g_strdup_printf ( "%s %s DIFF %f NM", lat, lon, VIK_METERS_TO_NAUTICAL_MILES(vik_coord_diff( &coord, &(s->oldcoord) )) );
1657 temp = g_strdup_printf ("Just to keep the compiler happy");
1658 g_critical("Houston, we've had a problem. distance=%d", dist_units);
1661 s->has_oldcoord = FALSE;
1664 temp = g_strdup_printf ( "%s %s", lat, lon );
1665 s->has_oldcoord = TRUE;
1668 vik_statusbar_set_message ( s->vw->viking_vs, VIK_STATUSBAR_INFO, temp );
1671 s->oldcoord = coord;
1674 vik_viewport_set_center_screen ( s->vvp, (gint) event->x, (gint) event->y );
1675 draw_update ( s->vw );
1677 return VIK_LAYER_TOOL_ACK;
1680 static VikLayerToolFuncStatus ruler_move (VikLayer *vl, GdkEventMotion *event, ruler_tool_state_t *s)
1682 VikViewport *vvp = s->vvp;
1683 VikWindow *vw = s->vw;
1689 if ( s->has_oldcoord ) {
1690 int oldx, oldy, w1, h1, w2, h2;
1691 static GdkPixmap *buf = NULL;
1692 gchar *lat=NULL, *lon=NULL;
1693 w1 = vik_viewport_get_width(vvp);
1694 h1 = vik_viewport_get_height(vvp);
1696 buf = gdk_pixmap_new ( gtk_widget_get_window(GTK_WIDGET(vvp)), w1, h1, -1 );
1698 gdk_drawable_get_size(buf, &w2, &h2);
1699 if (w1 != w2 || h1 != h2) {
1700 g_object_unref ( G_OBJECT ( buf ) );
1701 buf = gdk_pixmap_new ( gtk_widget_get_window(GTK_WIDGET(vvp)), w1, h1, -1 );
1704 vik_viewport_screen_to_coord ( vvp, (gint) event->x, (gint) event->y, &coord );
1705 vik_coord_to_latlon ( &coord, &ll );
1706 vik_viewport_coord_to_screen ( vvp, &s->oldcoord, &oldx, &oldy );
1708 gdk_draw_drawable (buf, gtk_widget_get_style(GTK_WIDGET(vvp))->black_gc,
1709 vik_viewport_get_pixmap(vvp), 0, 0, 0, 0, -1, -1);
1710 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)) );
1711 if (draw_buf_done) {
1712 static gpointer pass_along[3];
1713 pass_along[0] = gtk_widget_get_window(GTK_WIDGET(vvp));
1714 pass_along[1] = gtk_widget_get_style(GTK_WIDGET(vvp))->black_gc;
1715 pass_along[2] = buf;
1716 g_idle_add_full (G_PRIORITY_HIGH_IDLE + 10, draw_buf, pass_along, NULL);
1717 draw_buf_done = FALSE;
1719 a_coords_latlon_to_string(&ll, &lat, &lon);
1720 vik_units_distance_t dist_units = a_vik_get_units_distance ();
1721 switch (dist_units) {
1722 case VIK_UNITS_DISTANCE_KILOMETRES:
1723 temp = g_strdup_printf ( "%s %s DIFF %f meters", lat, lon, vik_coord_diff( &coord, &(s->oldcoord) ) );
1725 case VIK_UNITS_DISTANCE_MILES:
1726 temp = g_strdup_printf ( "%s %s DIFF %f miles", lat, lon, VIK_METERS_TO_MILES (vik_coord_diff( &coord, &(s->oldcoord) )) );
1728 case VIK_UNITS_DISTANCE_NAUTICAL_MILES:
1729 temp = g_strdup_printf ( "%s %s DIFF %f NM", lat, lon, VIK_METERS_TO_NAUTICAL_MILES (vik_coord_diff( &coord, &(s->oldcoord) )) );
1732 temp = g_strdup_printf ("Just to keep the compiler happy");
1733 g_critical("Houston, we've had a problem. distance=%d", dist_units);
1735 vik_statusbar_set_message ( vw->viking_vs, VIK_STATUSBAR_INFO, temp );
1738 return VIK_LAYER_TOOL_ACK;
1741 static VikLayerToolFuncStatus ruler_release (VikLayer *vl, GdkEventButton *event, ruler_tool_state_t *s)
1743 return VIK_LAYER_TOOL_ACK;
1746 static void ruler_deactivate (VikLayer *vl, ruler_tool_state_t *s)
1748 draw_update ( s->vw );
1751 static gboolean ruler_key_press (VikLayer *vl, GdkEventKey *event, ruler_tool_state_t *s)
1753 if (event->keyval == GDK_Escape) {
1754 s->has_oldcoord = FALSE;
1755 ruler_deactivate ( vl, s );
1758 // Regardless of whether we used it, return false so other GTK things may use it
1762 static VikToolInterface ruler_tool =
1763 // NB Ctrl+Shift+R is used for Refresh (deemed more important), so use 'U' instead
1764 { { "Ruler", "vik-icon-ruler", N_("_Ruler"), "<control><shift>U", N_("Ruler Tool"), 2 },
1765 (VikToolConstructorFunc) ruler_create,
1766 (VikToolDestructorFunc) ruler_destroy,
1767 (VikToolActivationFunc) NULL,
1768 (VikToolActivationFunc) ruler_deactivate,
1769 (VikToolMouseFunc) ruler_click,
1770 (VikToolMouseMoveFunc) ruler_move,
1771 (VikToolMouseFunc) ruler_release,
1772 (VikToolKeyFunc) ruler_key_press,
1774 GDK_CURSOR_IS_PIXMAP,
1775 &cursor_ruler_pixbuf,
1777 /*** end ruler code ********************************************************/
1781 /********************************************************************************
1783 ********************************************************************************/
1788 // Track zoom bounds for zoom tool with shift modifier:
1789 gboolean bounds_active;
1792 } zoom_tool_state_t;
1795 * In case the screen size has changed
1797 static void zoomtool_resize_pixmap (zoom_tool_state_t *zts)
1801 // Allocate a drawing area the size of the viewport
1802 w1 = vik_viewport_get_width ( zts->vw->viking_vvp );
1803 h1 = vik_viewport_get_height ( zts->vw->viking_vvp );
1805 if ( !zts->pixmap ) {
1807 zts->pixmap = gdk_pixmap_new ( gtk_widget_get_window(GTK_WIDGET(zts->vw->viking_vvp)), w1, h1, -1 );
1810 gdk_drawable_get_size ( zts->pixmap, &w2, &h2 );
1812 if ( w1 != w2 || h1 != h2 ) {
1813 // Has changed - delete and recreate with new values
1814 g_object_unref ( G_OBJECT ( zts->pixmap ) );
1815 zts->pixmap = gdk_pixmap_new ( gtk_widget_get_window(GTK_WIDGET(zts->vw->viking_vvp)), w1, h1, -1 );
1819 static gpointer zoomtool_create (VikWindow *vw, VikViewport *vvp)
1821 zoom_tool_state_t *zts = g_new(zoom_tool_state_t, 1);
1826 zts->bounds_active = FALSE;
1830 static void zoomtool_destroy ( zoom_tool_state_t *zts)
1833 g_object_unref ( G_OBJECT ( zts->pixmap ) );
1837 static VikLayerToolFuncStatus zoomtool_click (VikLayer *vl, GdkEventButton *event, zoom_tool_state_t *zts)
1839 zts->vw->modified = TRUE;
1840 guint modifiers = event->state & (GDK_SHIFT_MASK | GDK_CONTROL_MASK);
1844 gint center_x = vik_viewport_get_width ( zts->vw->viking_vvp ) / 2;
1845 gint center_y = vik_viewport_get_height ( zts->vw->viking_vvp ) / 2;
1847 gboolean skip_update = FALSE;
1849 zts->bounds_active = FALSE;
1851 if ( modifiers == (GDK_CONTROL_MASK | GDK_SHIFT_MASK) ) {
1852 // This zoom is on the center position
1853 vik_viewport_set_center_screen ( zts->vw->viking_vvp, center_x, center_y );
1854 if ( event->button == 1 )
1855 vik_viewport_zoom_in (zts->vw->viking_vvp);
1856 else if ( event->button == 3 )
1857 vik_viewport_zoom_out (zts->vw->viking_vvp);
1859 else if ( modifiers == GDK_CONTROL_MASK ) {
1860 // This zoom is to recenter on the mouse position
1861 vik_viewport_set_center_screen ( zts->vw->viking_vvp, (gint) event->x, (gint) event->y );
1862 if ( event->button == 1 )
1863 vik_viewport_zoom_in (zts->vw->viking_vvp);
1864 else if ( event->button == 3 )
1865 vik_viewport_zoom_out (zts->vw->viking_vvp);
1867 else if ( modifiers == GDK_SHIFT_MASK ) {
1868 // Get start of new zoom bounds
1869 if ( event->button == 1 ) {
1870 zts->bounds_active = TRUE;
1871 zts->start_x = (gint) event->x;
1872 zts->start_y = (gint) event->y;
1877 /* make sure mouse is still over the same point on the map when we zoom */
1878 vik_viewport_screen_to_coord ( zts->vw->viking_vvp, event->x, event->y, &coord );
1879 if ( event->button == 1 )
1880 vik_viewport_zoom_in (zts->vw->viking_vvp);
1881 else if ( event->button == 3 )
1882 vik_viewport_zoom_out(zts->vw->viking_vvp);
1883 vik_viewport_coord_to_screen ( zts->vw->viking_vvp, &coord, &x, &y );
1884 vik_viewport_set_center_screen ( zts->vw->viking_vvp,
1885 center_x + (x - event->x),
1886 center_y + (y - event->y) );
1890 draw_update ( zts->vw );
1892 return VIK_LAYER_TOOL_ACK;
1895 static VikLayerToolFuncStatus zoomtool_move (VikLayer *vl, GdkEventMotion *event, zoom_tool_state_t *zts)
1897 guint modifiers = event->state & (GDK_SHIFT_MASK | GDK_CONTROL_MASK);
1899 if ( zts->bounds_active && modifiers == GDK_SHIFT_MASK ) {
1900 zoomtool_resize_pixmap ( zts );
1902 // Blank out currently drawn area
1903 gdk_draw_drawable ( zts->pixmap,
1904 gtk_widget_get_style(GTK_WIDGET(zts->vw->viking_vvp))->black_gc,
1905 vik_viewport_get_pixmap(zts->vw->viking_vvp),
1906 0, 0, 0, 0, -1, -1);
1908 // Calculate new box starting point & size in pixels
1909 int xx, yy, width, height;
1910 if ( event->y > zts->start_y ) {
1912 height = event->y-zts->start_y;
1916 height = zts->start_y-event->y;
1918 if ( event->x > zts->start_x ) {
1920 width = event->x-zts->start_x;
1924 width = zts->start_x-event->x;
1928 gdk_draw_rectangle (zts->pixmap, gtk_widget_get_style(GTK_WIDGET(zts->vw->viking_vvp))->black_gc, FALSE, xx, yy, width, height);
1930 // Only actually draw when there's time to do so
1931 if (draw_buf_done) {
1932 static gpointer pass_along[3];
1933 pass_along[0] = gtk_widget_get_window(GTK_WIDGET(zts->vw->viking_vvp));
1934 pass_along[1] = gtk_widget_get_style(GTK_WIDGET(zts->vw->viking_vvp))->black_gc;
1935 pass_along[2] = zts->pixmap;
1936 g_idle_add_full (G_PRIORITY_HIGH_IDLE + 10, draw_buf, pass_along, NULL);
1937 draw_buf_done = FALSE;
1941 zts->bounds_active = FALSE;
1943 return VIK_LAYER_TOOL_ACK;
1946 static VikLayerToolFuncStatus zoomtool_release (VikLayer *vl, GdkEventButton *event, zoom_tool_state_t *zts)
1948 guint modifiers = event->state & (GDK_SHIFT_MASK | GDK_CONTROL_MASK);
1950 // Ensure haven't just released on the exact same position
1951 // i.e. probably haven't moved the mouse at all
1952 if ( zts->bounds_active && modifiers == GDK_SHIFT_MASK &&
1953 ( event->x < zts->start_x-5 || event->x > zts->start_x+5 ) &&
1954 ( event->y < zts->start_y-5 || event->y > zts->start_y+5 ) ) {
1956 VikCoord coord1, coord2;
1957 vik_viewport_screen_to_coord ( zts->vw->viking_vvp, zts->start_x, zts->start_y, &coord1);
1958 vik_viewport_screen_to_coord ( zts->vw->viking_vvp, event->x, event->y, &coord2);
1960 // From the extend of the bounds pick the best zoom level
1961 // c.f. trw_layer_zoom_to_show_latlons()
1962 // Maybe refactor...
1963 struct LatLon ll1, ll2;
1964 vik_coord_to_latlon(&coord1, &ll1);
1965 vik_coord_to_latlon(&coord2, &ll2);
1966 struct LatLon average = { (ll1.lat+ll2.lat)/2,
1967 (ll1.lon+ll2.lon)/2 };
1969 VikCoord new_center;
1970 vik_coord_load_from_latlon ( &new_center, vik_viewport_get_coord_mode ( zts->vw->viking_vvp ), &average );
1971 vik_viewport_set_center_coord ( zts->vw->viking_vvp, &new_center, FALSE );
1973 /* Convert into definite 'smallest' and 'largest' positions */
1974 struct LatLon minmin;
1975 if ( ll1.lat < ll2.lat )
1976 minmin.lat = ll1.lat;
1978 minmin.lat = ll2.lat;
1980 struct LatLon maxmax;
1981 if ( ll1.lon > ll2.lon )
1982 maxmax.lon = ll1.lon;
1984 maxmax.lon = ll2.lon;
1986 /* Always recalculate the 'best' zoom level */
1987 gdouble zoom = VIK_VIEWPORT_MIN_ZOOM;
1988 vik_viewport_set_zoom ( zts->vw->viking_vvp, zoom );
1990 gdouble min_lat, max_lat, min_lon, max_lon;
1991 /* Should only be a maximum of about 18 iterations from min to max zoom levels */
1992 while ( zoom <= VIK_VIEWPORT_MAX_ZOOM ) {
1993 vik_viewport_get_min_max_lat_lon ( zts->vw->viking_vvp, &min_lat, &max_lat, &min_lon, &max_lon );
1994 /* NB I think the logic used in this test to determine if the bounds is within view
1995 fails if track goes across 180 degrees longitude.
1996 Hopefully that situation is not too common...
1997 Mind you viking doesn't really do edge locations to well anyway */
1998 if ( min_lat < minmin.lat &&
1999 max_lat > minmin.lat &&
2000 min_lon < maxmax.lon &&
2001 max_lon > maxmax.lon )
2002 /* Found within zoom level */
2007 vik_viewport_set_zoom ( zts->vw->viking_vvp, zoom );
2011 // When pressing shift and clicking for zoom, then jump three levels
2012 if ( modifiers == GDK_SHIFT_MASK ) {
2013 // Zoom in/out by three if possible
2014 vik_viewport_set_center_screen ( zts->vw->viking_vvp, event->x, event->y );
2015 if ( event->button == 1 ) {
2016 vik_viewport_zoom_in ( zts->vw->viking_vvp );
2017 vik_viewport_zoom_in ( zts->vw->viking_vvp );
2018 vik_viewport_zoom_in ( zts->vw->viking_vvp );
2020 else if ( event->button == 3 ) {
2021 vik_viewport_zoom_out ( zts->vw->viking_vvp );
2022 vik_viewport_zoom_out ( zts->vw->viking_vvp );
2023 vik_viewport_zoom_out ( zts->vw->viking_vvp );
2028 draw_update ( zts->vw );
2031 zts->bounds_active = FALSE;
2033 return VIK_LAYER_TOOL_ACK;
2036 static VikToolInterface zoom_tool =
2037 { { "Zoom", "vik-icon-zoom", N_("_Zoom"), "<control><shift>Z", N_("Zoom Tool"), 1 },
2038 (VikToolConstructorFunc) zoomtool_create,
2039 (VikToolDestructorFunc) zoomtool_destroy,
2040 (VikToolActivationFunc) NULL,
2041 (VikToolActivationFunc) NULL,
2042 (VikToolMouseFunc) zoomtool_click,
2043 (VikToolMouseMoveFunc) zoomtool_move,
2044 (VikToolMouseFunc) zoomtool_release,
2047 GDK_CURSOR_IS_PIXMAP,
2048 &cursor_zoom_pixbuf,
2050 /*** end zoom code ********************************************************/
2052 /********************************************************************************
2054 ********************************************************************************/
2055 static gpointer pantool_create (VikWindow *vw, VikViewport *vvp)
2060 // NB Double clicking means this gets called THREE times!!!
2061 static VikLayerToolFuncStatus pantool_click (VikLayer *vl, GdkEventButton *event, VikWindow *vw)
2063 vw->modified = TRUE;
2065 if ( event->type == GDK_2BUTTON_PRESS ) {
2066 // Zoom in / out on double click
2067 // No need to change the center as that has already occurred in the first click of a double click occurrence
2068 if ( event->button == 1 ) {
2069 guint modifier = event->state & GDK_SHIFT_MASK;
2071 vik_viewport_zoom_out ( vw->viking_vvp );
2073 vik_viewport_zoom_in ( vw->viking_vvp );
2075 else if ( event->button == 3 )
2076 vik_viewport_zoom_out ( vw->viking_vvp );
2081 // Standard pan click
2082 if ( event->button == 1 )
2083 vik_window_pan_click ( vw, event );
2085 return VIK_LAYER_TOOL_ACK;
2088 static VikLayerToolFuncStatus pantool_move (VikLayer *vl, GdkEventMotion *event, VikWindow *vw)
2090 vik_window_pan_move ( vw, event );
2091 return VIK_LAYER_TOOL_ACK;
2094 static VikLayerToolFuncStatus pantool_release (VikLayer *vl, GdkEventButton *event, VikWindow *vw)
2096 if ( event->button == 1 )
2097 vik_window_pan_release ( vw, event );
2098 return VIK_LAYER_TOOL_ACK;
2101 static VikToolInterface pan_tool =
2102 { { "Pan", "vik-icon-pan", N_("_Pan"), "<control><shift>P", N_("Pan Tool"), 0 },
2103 (VikToolConstructorFunc) pantool_create,
2104 (VikToolDestructorFunc) NULL,
2105 (VikToolActivationFunc) NULL,
2106 (VikToolActivationFunc) NULL,
2107 (VikToolMouseFunc) pantool_click,
2108 (VikToolMouseMoveFunc) pantool_move,
2109 (VikToolMouseFunc) pantool_release,
2115 /*** end pan code ********************************************************/
2117 /********************************************************************************
2119 ********************************************************************************/
2120 static gpointer selecttool_create (VikWindow *vw, VikViewport *vvp)
2122 tool_ed_t *t = g_new(tool_ed_t, 1);
2126 t->is_waypoint = FALSE;
2130 static void selecttool_destroy (tool_ed_t *t)
2138 GdkEventButton *event;
2139 tool_ed_t *tool_edit;
2142 static void click_layer_selected (VikLayer *vl, clicker *ck)
2144 /* Do nothing when function call returns true; */
2145 /* i.e. stop on first found item */
2148 if ( vik_layer_get_interface(vl->type)->select_click )
2149 ck->cont = !vik_layer_get_interface(vl->type)->select_click ( vl, ck->event, ck->vvp, ck->tool_edit );
2153 // Hopefully Alt keys by default
2154 #define VIK_MOVE_MODIFIER GDK_MOD1_MASK
2156 // Alt+mouse on Linux desktops tend to be used by the desktop manager
2157 // Thus use an alternate modifier - you may need to set something into this group
2158 #define VIK_MOVE_MODIFIER GDK_MOD5_MASK
2161 static VikLayerToolFuncStatus selecttool_click (VikLayer *vl, GdkEventButton *event, tool_ed_t *t)
2163 t->vw->select_move = FALSE;
2164 /* Only allow selection on primary button */
2165 if ( event->button == 1 ) {
2167 if ( event->state & VIK_MOVE_MODIFIER )
2168 vik_window_pan_click ( t->vw, event );
2170 /* Enable click to apply callback to potentially all track/waypoint layers */
2171 /* Useful as we can find things that aren't necessarily in the currently selected layer */
2172 GList* gl = vik_layers_panel_get_all_layers_of_type ( t->vw->viking_vlp, VIK_LAYER_TRW, FALSE ); // Don't get invisible layers
2175 ck.vvp = t->vw->viking_vvp;
2178 g_list_foreach ( gl, (GFunc) click_layer_selected, &ck );
2181 // If nothing found then deselect & redraw screen if necessary to remove the highlight
2184 VikTreeview *vtv = vik_layers_panel_get_treeview ( t->vw->viking_vlp );
2186 if ( vik_treeview_get_selected_iter ( vtv, &iter ) ) {
2187 // Only clear if selected thing is a TrackWaypoint layer or a sublayer
2188 gint type = vik_treeview_item_get_type ( vtv, &iter );
2189 if ( type == VIK_TREEVIEW_TYPE_SUBLAYER ||
2190 VIK_LAYER(vik_treeview_item_get_pointer ( vtv, &iter ))->type == VIK_LAYER_TRW ) {
2192 vik_treeview_item_unselect ( vtv, &iter );
2193 if ( vik_window_clear_highlight ( t->vw ) )
2194 draw_update ( t->vw );
2199 // Something found - so enable movement
2200 t->vw->select_move = TRUE;
2204 else if ( ( event->button == 3 ) && ( vl && ( vl->type == VIK_LAYER_TRW ) ) ) {
2206 /* Act on currently selected item to show menu */
2207 if ( t->vw->selected_track || t->vw->selected_waypoint )
2208 if ( vik_layer_get_interface(vl->type)->show_viewport_menu )
2209 vik_layer_get_interface(vl->type)->show_viewport_menu ( vl, event, t->vw->viking_vvp );
2212 return VIK_LAYER_TOOL_ACK;
2215 static VikLayerToolFuncStatus selecttool_move (VikLayer *vl, GdkEventMotion *event, tool_ed_t *t)
2217 if ( t->vw->select_move ) {
2218 // Don't care about vl here
2220 if ( vik_layer_get_interface(VIK_LAYER_TRW)->select_move )
2221 vik_layer_get_interface(VIK_LAYER_TRW)->select_move ( vl, event, t->vvp, t );
2225 if ( event->state & VIK_MOVE_MODIFIER )
2226 vik_window_pan_move ( t->vw, event );
2228 return VIK_LAYER_TOOL_ACK;
2231 static VikLayerToolFuncStatus selecttool_release (VikLayer *vl, GdkEventButton *event, tool_ed_t *t)
2233 if ( t->vw->select_move ) {
2234 // Don't care about vl here
2236 if ( vik_layer_get_interface(VIK_LAYER_TRW)->select_release )
2237 vik_layer_get_interface(VIK_LAYER_TRW)->select_release ( (VikLayer*)t->vtl, event, t->vvp, t );
2240 if ( event->button == 1 && (event->state & VIK_MOVE_MODIFIER) )
2241 vik_window_pan_release ( t->vw, event );
2243 // Force pan off incase it was on
2244 t->vw->pan_move = FALSE;
2245 t->vw->pan_x = t->vw->pan_y = -1;
2247 // End of this select movement
2248 t->vw->select_move = FALSE;
2250 return VIK_LAYER_TOOL_ACK;
2253 static VikToolInterface select_tool =
2254 { { "Select", "vik-icon-select", N_("_Select"), "<control><shift>S", N_("Select Tool"), 3 },
2255 (VikToolConstructorFunc) selecttool_create,
2256 (VikToolDestructorFunc) selecttool_destroy,
2257 (VikToolActivationFunc) NULL,
2258 (VikToolActivationFunc) NULL,
2259 (VikToolMouseFunc) selecttool_click,
2260 (VikToolMouseMoveFunc) selecttool_move,
2261 (VikToolMouseFunc) selecttool_release,
2262 (VikToolKeyFunc) NULL,
2267 /*** end select tool code ********************************************************/
2269 static void draw_pan_cb ( GtkAction *a, VikWindow *vw )
2271 // Since the treeview cell editting intercepts standard keyboard handlers, it means we can receive events here
2272 // Thus if currently editting, ensure we don't move the viewport when Ctrl+<arrow> is received
2273 VikLayer *sel = vik_layers_panel_get_selected ( vw->viking_vlp );
2274 if ( sel && vik_treeview_get_editing ( sel->vt ) )
2277 if (!strcmp(gtk_action_get_name(a), "PanNorth")) {
2278 vik_viewport_set_center_screen ( vw->viking_vvp, vik_viewport_get_width(vw->viking_vvp)/2, 0 );
2279 } else if (!strcmp(gtk_action_get_name(a), "PanEast")) {
2280 vik_viewport_set_center_screen ( vw->viking_vvp, vik_viewport_get_width(vw->viking_vvp), vik_viewport_get_height(vw->viking_vvp)/2 );
2281 } else if (!strcmp(gtk_action_get_name(a), "PanSouth")) {
2282 vik_viewport_set_center_screen ( vw->viking_vvp, vik_viewport_get_width(vw->viking_vvp)/2, vik_viewport_get_height(vw->viking_vvp) );
2283 } else if (!strcmp(gtk_action_get_name(a), "PanWest")) {
2284 vik_viewport_set_center_screen ( vw->viking_vvp, 0, vik_viewport_get_height(vw->viking_vvp)/2 );
2289 static void draw_zoom_cb ( GtkAction *a, VikWindow *vw )
2293 if (!strcmp(gtk_action_get_name(a), "ZoomIn")) {
2296 else if (!strcmp(gtk_action_get_name(a), "ZoomOut")) {
2299 else if (!strcmp(gtk_action_get_name(a), "Zoom0.25")) {
2302 else if (!strcmp(gtk_action_get_name(a), "Zoom0.5")) {
2306 gchar *s = (gchar *)gtk_action_get_name(a);
2312 case -3: vik_viewport_zoom_in ( vw->viking_vvp ); break;
2313 case -4: vik_viewport_zoom_out ( vw->viking_vvp ); break;
2314 case -1: vik_viewport_set_zoom ( vw->viking_vvp, 0.5 ); break;
2315 case -2: vik_viewport_set_zoom ( vw->viking_vvp, 0.25 ); break;
2316 default: vik_viewport_set_zoom ( vw->viking_vvp, what );
2321 static void draw_goto_cb ( GtkAction *a, VikWindow *vw )
2323 VikCoord new_center;
2325 if (!strcmp(gtk_action_get_name(a), "GotoLL")) {
2326 struct LatLon ll, llold;
2327 vik_coord_to_latlon ( vik_viewport_get_center ( vw->viking_vvp ), &llold );
2328 if ( a_dialog_goto_latlon ( GTK_WINDOW(vw), &ll, &llold ) )
2329 vik_coord_load_from_latlon ( &new_center, vik_viewport_get_coord_mode(vw->viking_vvp), &ll );
2333 else if (!strcmp(gtk_action_get_name(a), "GotoUTM")) {
2334 struct UTM utm, utmold;
2335 vik_coord_to_utm ( vik_viewport_get_center ( vw->viking_vvp ), &utmold );
2336 if ( a_dialog_goto_utm ( GTK_WINDOW(vw), &utm, &utmold ) )
2337 vik_coord_load_from_utm ( &new_center, vik_viewport_get_coord_mode(vw->viking_vvp), &utm );
2342 g_critical("Houston, we've had a problem.");
2346 vik_viewport_set_center_coord ( vw->viking_vvp, &new_center, TRUE );
2351 * center_changed_cb:
2353 static void center_changed_cb ( VikWindow *vw )
2355 // ATM Keep back always available, so when we pan - we can jump to the last requested position
2357 GtkAction* action_back = gtk_action_group_get_action ( vw->action_group, "GoBack" );
2358 if ( action_back ) {
2359 gtk_action_set_sensitive ( action_back, vik_viewport_back_available(vw->viking_vvp) );
2362 GtkAction* action_forward = gtk_action_group_get_action ( vw->action_group, "GoForward" );
2363 if ( action_forward ) {
2364 gtk_action_set_sensitive ( action_forward, vik_viewport_forward_available(vw->viking_vvp) );
2367 toolbar_action_set_sensitive ( vw->viking_vtb, "GoForward", vik_viewport_forward_available(vw->viking_vvp) );
2371 * draw_goto_back_and_forth:
2373 static void draw_goto_back_and_forth ( GtkAction *a, VikWindow *vw )
2375 gboolean changed = FALSE;
2376 if (!strcmp(gtk_action_get_name(a), "GoBack")) {
2377 changed = vik_viewport_go_back ( vw->viking_vvp );
2379 else if (!strcmp(gtk_action_get_name(a), "GoForward")) {
2380 changed = vik_viewport_go_forward ( vw->viking_vvp );
2386 // Recheck buttons sensitivities, as the center changed signal is not sent on back/forward changes
2387 // (otherwise we would get stuck in an infinite loop!)
2388 center_changed_cb ( vw );
2395 * Refresh maps displayed
2397 static void draw_refresh_cb ( GtkAction *a, VikWindow *vw )
2399 // Only get 'new' maps
2400 simple_map_update ( vw, TRUE );
2403 static void menu_addlayer_cb ( GtkAction *a, VikWindow *vw )
2405 VikLayerTypeEnum type;
2406 for ( type = 0; type < VIK_LAYER_NUM_TYPES; type++ ) {
2407 if (!strcmp(vik_layer_get_interface(type)->name, gtk_action_get_name(a))) {
2408 if ( vik_layers_panel_new_layer ( vw->viking_vlp, type ) ) {
2410 vw->modified = TRUE;
2416 static void menu_copy_layer_cb ( GtkAction *a, VikWindow *vw )
2418 a_clipboard_copy_selected ( vw->viking_vlp );
2421 static void menu_cut_layer_cb ( GtkAction *a, VikWindow *vw )
2423 vik_layers_panel_cut_selected ( vw->viking_vlp );
2424 vw->modified = TRUE;
2427 static void menu_paste_layer_cb ( GtkAction *a, VikWindow *vw )
2429 if ( vik_layers_panel_paste_selected ( vw->viking_vlp ) )
2431 vw->modified = TRUE;
2435 static void menu_properties_cb ( GtkAction *a, VikWindow *vw )
2437 if ( ! vik_layers_panel_properties ( vw->viking_vlp ) )
2438 a_dialog_info_msg ( GTK_WINDOW(vw), _("You must select a layer to show its properties.") );
2441 static void help_help_cb ( GtkAction *a, VikWindow *vw )
2444 ShellExecute(NULL, "open", ""PACKAGE".pdf", NULL, NULL, SW_SHOWNORMAL);
2447 uri = g_strdup_printf("ghelp:%s", PACKAGE);
2448 GError *error = NULL;
2449 gboolean show = gtk_show_uri (NULL, uri, GDK_CURRENT_TIME, &error);
2450 if ( !show && !error )
2451 // No error to show, so unlikely this will get called
2452 a_dialog_error_msg ( GTK_WINDOW(vw), _("The help system is not available.") );
2455 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 );
2456 g_error_free ( error );
2459 #endif /* WINDOWS */
2462 static void toggle_side_panel ( VikWindow *vw )
2464 vw->show_side_panel = !vw->show_side_panel;
2465 if ( vw->show_side_panel )
2466 gtk_widget_show(GTK_WIDGET(vw->viking_vlp));
2468 gtk_widget_hide(GTK_WIDGET(vw->viking_vlp));
2471 static void toggle_full_screen ( VikWindow *vw )
2473 vw->show_full_screen = !vw->show_full_screen;
2474 if ( vw->show_full_screen )
2475 gtk_window_fullscreen ( GTK_WINDOW(vw) );
2477 gtk_window_unfullscreen ( GTK_WINDOW(vw) );
2480 static void toggle_statusbar ( VikWindow *vw )
2482 vw->show_statusbar = !vw->show_statusbar;
2483 if ( vw->show_statusbar )
2484 gtk_widget_show ( GTK_WIDGET(vw->viking_vs) );
2486 gtk_widget_hide ( GTK_WIDGET(vw->viking_vs) );
2489 static void toggle_toolbar ( VikWindow *vw )
2491 vw->show_toolbar = !vw->show_toolbar;
2492 if ( vw->show_toolbar )
2493 gtk_widget_show ( toolbar_get_widget (vw->viking_vtb) );
2495 gtk_widget_hide ( toolbar_get_widget (vw->viking_vtb) );
2498 static void toggle_main_menu ( VikWindow *vw )
2500 vw->show_main_menu = !vw->show_main_menu;
2501 if ( vw->show_main_menu )
2502 gtk_widget_show ( gtk_ui_manager_get_widget ( vw->uim, "/ui/MainMenu" ) );
2504 gtk_widget_hide ( gtk_ui_manager_get_widget ( vw->uim, "/ui/MainMenu" ) );
2507 // Only for 'view' toggle menu widgets ATM.
2508 GtkWidget *get_show_widget_by_name(VikWindow *vw, const gchar *name)
2510 g_return_val_if_fail(name != NULL, NULL);
2512 // ATM only FullScreen is *not* in SetShow path
2514 if ( g_strcmp0 ("FullScreen", name ) )
2515 path = g_strconcat("/ui/MainMenu/View/SetShow/", name, NULL);
2517 path = g_strconcat("/ui/MainMenu/View/", name, NULL);
2519 GtkWidget *widget = gtk_ui_manager_get_widget(vw->uim, path);
2525 static void tb_view_side_panel_cb ( GtkAction *a, VikWindow *vw )
2527 gboolean next_state = !vw->show_side_panel;
2528 GtkWidget *check_box = get_show_widget_by_name ( vw, gtk_action_get_name(a) );
2529 gboolean menu_state = gtk_check_menu_item_get_active ( GTK_CHECK_MENU_ITEM(check_box) );
2530 if ( next_state != menu_state )
2531 gtk_check_menu_item_set_active ( GTK_CHECK_MENU_ITEM(check_box), next_state );
2533 toggle_side_panel ( vw );
2536 static void tb_full_screen_cb ( GtkAction *a, VikWindow *vw )
2538 gboolean next_state = !vw->show_full_screen;
2539 GtkWidget *check_box = get_show_widget_by_name ( vw, gtk_action_get_name(a) );
2540 gboolean menu_state = gtk_check_menu_item_get_active ( GTK_CHECK_MENU_ITEM(check_box) );
2541 if ( next_state != menu_state )
2542 gtk_check_menu_item_set_active ( GTK_CHECK_MENU_ITEM(check_box), next_state );
2544 toggle_full_screen ( vw );
2547 static void tb_view_statusbar_cb ( GtkAction *a, VikWindow *vw )
2549 gboolean next_state = !vw->show_statusbar;
2550 GtkWidget *check_box = get_show_widget_by_name ( vw, gtk_action_get_name(a) );
2551 gboolean menu_state = gtk_check_menu_item_get_active ( GTK_CHECK_MENU_ITEM(check_box) );
2552 if ( next_state != menu_state )
2553 gtk_check_menu_item_set_active ( GTK_CHECK_MENU_ITEM(check_box), next_state );
2555 toggle_statusbar ( vw );
2558 static void tb_view_toolbar_cb ( GtkAction *a, VikWindow *vw )
2560 gboolean next_state = !vw->show_toolbar;
2561 GtkWidget *check_box = get_show_widget_by_name ( vw, gtk_action_get_name(a) );
2562 gboolean menu_state = gtk_check_menu_item_get_active ( GTK_CHECK_MENU_ITEM(check_box) );
2563 if ( next_state != menu_state )
2564 gtk_check_menu_item_set_active ( GTK_CHECK_MENU_ITEM(check_box), next_state );
2566 toggle_toolbar ( vw );
2569 static void tb_view_main_menu_cb ( GtkAction *a, VikWindow *vw )
2571 gboolean next_state = !vw->show_main_menu;
2572 GtkWidget *check_box = get_show_widget_by_name ( vw, gtk_action_get_name(a) );
2573 gboolean menu_state = gtk_check_menu_item_get_active ( GTK_CHECK_MENU_ITEM(check_box) );
2574 if ( next_state != menu_state )
2575 gtk_check_menu_item_set_active ( GTK_CHECK_MENU_ITEM(check_box), next_state );
2577 toggle_main_menu ( vw );
2580 static void tb_set_draw_scale ( GtkAction *a, VikWindow *vw )
2582 gboolean next_state = !vik_viewport_get_draw_scale ( vw->viking_vvp );
2583 GtkWidget *check_box = get_show_widget_by_name ( vw, gtk_action_get_name(a) );
2584 gboolean menu_state = gtk_check_menu_item_get_active ( GTK_CHECK_MENU_ITEM(check_box) );
2585 if ( next_state != menu_state )
2586 gtk_check_menu_item_set_active ( GTK_CHECK_MENU_ITEM(check_box), next_state );
2588 vik_viewport_set_draw_scale ( vw->viking_vvp, next_state );
2593 static void tb_set_draw_centermark ( GtkAction *a, VikWindow *vw )
2595 gboolean next_state = !vik_viewport_get_draw_centermark ( vw->viking_vvp );
2596 GtkWidget *check_box = get_show_widget_by_name ( vw, gtk_action_get_name(a) );
2597 gboolean menu_state = gtk_check_menu_item_get_active ( GTK_CHECK_MENU_ITEM(check_box) );
2598 if ( next_state != menu_state )
2599 gtk_check_menu_item_set_active ( GTK_CHECK_MENU_ITEM(check_box), next_state );
2601 vik_viewport_set_draw_centermark ( vw->viking_vvp, next_state );
2606 static void tb_set_draw_highlight ( GtkAction *a, VikWindow *vw )
2608 gboolean next_state = !vik_viewport_get_draw_highlight ( vw->viking_vvp );
2609 GtkWidget *check_box = get_show_widget_by_name ( vw, gtk_action_get_name(a) );
2610 gboolean menu_state = gtk_check_menu_item_get_active ( GTK_CHECK_MENU_ITEM(check_box) );
2611 if ( next_state != menu_state )
2612 gtk_check_menu_item_set_active ( GTK_CHECK_MENU_ITEM(check_box), next_state );
2614 vik_viewport_set_draw_highlight ( vw->viking_vvp, next_state );
2619 static void help_about_cb ( GtkAction *a, VikWindow *vw )
2621 a_dialog_about(GTK_WINDOW(vw));
2624 static void help_cache_info_cb ( GtkAction *a, VikWindow *vw )
2626 // NB: No i18n as this is just for debug
2627 gint byte_size = a_mapcache_get_size();
2628 gchar *msg_sz = NULL;
2630 #if GLIB_CHECK_VERSION(2,30,0)
2631 msg_sz = g_format_size_full ( byte_size, G_FORMAT_SIZE_LONG_FORMAT );
2633 msg_sz = g_format_size_for_display ( byte_size );
2635 msg = g_strdup_printf ( "Map Cache size is %s with %d items", msg_sz, a_mapcache_get_count());
2636 a_dialog_info_msg_extra ( GTK_WINDOW(vw), "%s", msg );
2641 static void back_forward_info_cb ( GtkAction *a, VikWindow *vw )
2643 vik_viewport_show_centers ( vw->viking_vvp, GTK_WINDOW(vw) );
2646 static void menu_delete_layer_cb ( GtkAction *a, VikWindow *vw )
2648 if ( vik_layers_panel_get_selected ( vw->viking_vlp ) )
2650 vik_layers_panel_delete_selected ( vw->viking_vlp );
2651 vw->modified = TRUE;
2654 a_dialog_info_msg ( GTK_WINDOW(vw), _("You must select a layer to delete.") );
2657 static void full_screen_cb ( GtkAction *a, VikWindow *vw )
2659 gboolean next_state = !vw->show_full_screen;
2660 GtkToggleToolButton *tbutton = (GtkToggleToolButton *)toolbar_get_widget_by_name ( vw->viking_vtb, gtk_action_get_name(a) );
2662 gboolean tb_state = gtk_toggle_tool_button_get_active ( tbutton );
2663 if ( next_state != tb_state )
2664 gtk_toggle_tool_button_set_active ( tbutton, next_state );
2666 toggle_full_screen ( vw );
2669 toggle_full_screen ( vw );
2672 static void view_side_panel_cb ( GtkAction *a, VikWindow *vw )
2674 gboolean next_state = !vw->show_side_panel;
2675 GtkToggleToolButton *tbutton = (GtkToggleToolButton *)toolbar_get_widget_by_name ( vw->viking_vtb, gtk_action_get_name(a) );
2677 gboolean tb_state = gtk_toggle_tool_button_get_active ( tbutton );
2678 if ( next_state != tb_state )
2679 gtk_toggle_tool_button_set_active ( tbutton, next_state );
2681 toggle_side_panel ( vw );
2684 toggle_side_panel ( vw );
2687 static void view_statusbar_cb ( GtkAction *a, VikWindow *vw )
2689 gboolean next_state = !vw->show_statusbar;
2690 GtkToggleToolButton *tbutton = (GtkToggleToolButton *)toolbar_get_widget_by_name ( vw->viking_vtb, gtk_action_get_name(a) );
2692 gboolean tb_state = gtk_toggle_tool_button_get_active ( tbutton );
2693 if ( next_state != tb_state )
2694 gtk_toggle_tool_button_set_active ( tbutton, next_state );
2696 toggle_statusbar ( vw );
2699 toggle_statusbar ( vw );
2702 static void view_toolbar_cb ( GtkAction *a, VikWindow *vw )
2704 gboolean next_state = !vw->show_toolbar;
2705 GtkToggleToolButton *tbutton = (GtkToggleToolButton *)toolbar_get_widget_by_name ( vw->viking_vtb, gtk_action_get_name(a) );
2707 gboolean tb_state = gtk_toggle_tool_button_get_active ( tbutton );
2708 if ( next_state != tb_state )
2709 gtk_toggle_tool_button_set_active ( tbutton, next_state );
2711 toggle_toolbar ( vw );
2714 toggle_toolbar ( vw );
2717 static void view_main_menu_cb ( GtkAction *a, VikWindow *vw )
2719 gboolean next_state = !vw->show_main_menu;
2720 GtkToggleToolButton *tbutton = (GtkToggleToolButton *)toolbar_get_widget_by_name ( vw->viking_vtb, gtk_action_get_name(a) );
2722 gboolean tb_state = gtk_toggle_tool_button_get_active ( tbutton );
2723 if ( next_state != tb_state )
2724 gtk_toggle_tool_button_set_active ( tbutton, next_state );
2726 toggle_main_menu ( vw );
2729 toggle_toolbar ( vw );
2732 /***************************************
2733 ** tool management routines
2735 ***************************************/
2737 static toolbox_tools_t* toolbox_create(VikWindow *vw)
2739 toolbox_tools_t *vt = g_new(toolbox_tools_t, 1);
2742 vt->active_tool = -1;
2747 static void toolbox_add_tool(toolbox_tools_t *vt, VikToolInterface *vti, gint layer_type )
2749 vt->tools = g_renew(toolbox_tool_t, vt->tools, vt->n_tools+1);
2750 vt->tools[vt->n_tools].ti = *vti;
2751 vt->tools[vt->n_tools].layer_type = layer_type;
2753 vt->tools[vt->n_tools].state = vti->create(vt->vw, vt->vw->viking_vvp);
2756 vt->tools[vt->n_tools].state = NULL;
2761 static int toolbox_get_tool(toolbox_tools_t *vt, const gchar *tool_name)
2764 for (i=0; i<vt->n_tools; i++) {
2765 if (!strcmp(tool_name, vt->tools[i].ti.radioActionEntry.name)) {
2772 static void toolbox_activate(toolbox_tools_t *vt, const gchar *tool_name)
2774 int tool = toolbox_get_tool(vt, tool_name);
2775 toolbox_tool_t *t = &vt->tools[tool];
2776 VikLayer *vl = vik_layers_panel_get_selected ( vt->vw->viking_vlp );
2778 if (tool == vt->n_tools) {
2779 g_critical("trying to activate a non-existent tool...");
2782 /* is the tool already active? */
2783 if (vt->active_tool == tool) {
2787 if (vt->active_tool != -1) {
2788 if (vt->tools[vt->active_tool].ti.deactivate) {
2789 vt->tools[vt->active_tool].ti.deactivate(NULL, vt->tools[vt->active_tool].state);
2792 if (t->ti.activate) {
2793 t->ti.activate(vl, t->state);
2795 vt->active_tool = tool;
2798 static const GdkCursor *toolbox_get_cursor(toolbox_tools_t *vt, const gchar *tool_name)
2800 int tool = toolbox_get_tool(vt, tool_name);
2801 toolbox_tool_t *t = &vt->tools[tool];
2802 if (t->ti.cursor == NULL) {
2803 if (t->ti.cursor_type == GDK_CURSOR_IS_PIXMAP && t->ti.cursor_data != NULL) {
2804 GdkPixbuf *cursor_pixbuf = gdk_pixbuf_from_pixdata (t->ti.cursor_data, FALSE, NULL);
2805 /* TODO: settable offeset */
2806 t->ti.cursor = gdk_cursor_new_from_pixbuf ( gdk_display_get_default(), cursor_pixbuf, 3, 3 );
2807 g_object_unref ( G_OBJECT(cursor_pixbuf) );
2809 t->ti.cursor = gdk_cursor_new ( t->ti.cursor_type );
2812 return t->ti.cursor;
2815 static void toolbox_click (toolbox_tools_t *vt, GdkEventButton *event)
2817 VikLayer *vl = vik_layers_panel_get_selected ( vt->vw->viking_vlp );
2818 if (vt->active_tool != -1 && vt->tools[vt->active_tool].ti.click) {
2819 gint ltype = vt->tools[vt->active_tool].layer_type;
2820 if ( ltype == TOOL_LAYER_TYPE_NONE || (vl && ltype == vl->type) )
2821 vt->tools[vt->active_tool].ti.click(vl, event, vt->tools[vt->active_tool].state);
2825 static void toolbox_move (toolbox_tools_t *vt, GdkEventMotion *event)
2827 VikLayer *vl = vik_layers_panel_get_selected ( vt->vw->viking_vlp );
2828 if (vt->active_tool != -1 && vt->tools[vt->active_tool].ti.move) {
2829 gint ltype = vt->tools[vt->active_tool].layer_type;
2830 if ( ltype == TOOL_LAYER_TYPE_NONE || (vl && ltype == vl->type) )
2831 if ( VIK_LAYER_TOOL_ACK_GRAB_FOCUS == vt->tools[vt->active_tool].ti.move(vl, event, vt->tools[vt->active_tool].state) )
2832 gtk_widget_grab_focus ( GTK_WIDGET(vt->vw->viking_vvp) );
2836 static void toolbox_release (toolbox_tools_t *vt, GdkEventButton *event)
2838 VikLayer *vl = vik_layers_panel_get_selected ( vt->vw->viking_vlp );
2839 if (vt->active_tool != -1 && vt->tools[vt->active_tool].ti.release ) {
2840 gint ltype = vt->tools[vt->active_tool].layer_type;
2841 if ( ltype == TOOL_LAYER_TYPE_NONE || (vl && ltype == vl->type) )
2842 vt->tools[vt->active_tool].ti.release(vl, event, vt->tools[vt->active_tool].state);
2845 /** End tool management ************************************/
2847 void vik_window_enable_layer_tool ( VikWindow *vw, gint layer_id, gint tool_id )
2849 gtk_action_activate ( gtk_action_group_get_action ( vw->action_group, vik_layer_get_interface(layer_id)->tools[tool_id].radioActionEntry.name ) );
2852 // Be careful with usage - as it may trigger actions being continually alternately by the menu and toolbar items
2853 // DON'T Use this from menu callback with toggle toolbar items!!
2854 static void toolbar_sync ( VikWindow *vw, const gchar *name, gboolean state )
2856 GtkToggleToolButton *tbutton = (GtkToggleToolButton *)toolbar_get_widget_by_name ( vw->viking_vtb, name );
2858 // Causes toggle signal action to be raised.
2859 gtk_toggle_tool_button_set_active ( tbutton, state );
2863 /* this function gets called whenever a menu is clicked */
2864 // Note old is not used
2865 static void menu_cb ( GtkAction *old, GtkAction *a, VikWindow *vw )
2867 // Ensure Toolbar kept in sync
2868 const gchar *name = gtk_action_get_name(a);
2869 toolbar_sync ( vw, name, TRUE );
2871 /* White Magic, my friends ... White Magic... */
2873 toolbox_activate(vw->vt, name);
2875 vw->viewport_cursor = (GdkCursor *)toolbox_get_cursor(vw->vt, name);
2877 if ( gtk_widget_get_window(GTK_WIDGET(vw->viking_vvp)) )
2878 /* We set cursor, even if it is NULL: it resets to default */
2879 gdk_window_set_cursor ( gtk_widget_get_window(GTK_WIDGET(vw->viking_vvp)), vw->viewport_cursor );
2881 if (!g_strcmp0(name, "Pan")) {
2882 vw->current_tool = TOOL_PAN;
2884 else if (!g_strcmp0(name, "Zoom")) {
2885 vw->current_tool = TOOL_ZOOM;
2887 else if (!g_strcmp0(name, "Ruler")) {
2888 vw->current_tool = TOOL_RULER;
2890 else if (!g_strcmp0(name, "Select")) {
2891 vw->current_tool = TOOL_SELECT;
2894 VikLayerTypeEnum layer_id;
2895 for (layer_id=0; layer_id<VIK_LAYER_NUM_TYPES; layer_id++) {
2896 for ( tool_id = 0; tool_id < vik_layer_get_interface(layer_id)->tools_count; tool_id++ ) {
2897 if (!g_strcmp0(vik_layer_get_interface(layer_id)->tools[tool_id].radioActionEntry.name, name)) {
2898 vw->current_tool = TOOL_LAYER;
2899 vw->tool_layer_id = layer_id;
2900 vw->tool_tool_id = tool_id;
2905 draw_status_tool ( vw );
2908 static void window_set_filename ( VikWindow *vw, const gchar *filename )
2913 g_free ( vw->filename );
2914 if ( filename == NULL )
2916 vw->filename = NULL;
2920 vw->filename = g_strdup(filename);
2923 /* Refresh window's title */
2924 file = window_get_filename ( vw );
2925 title = g_strdup_printf( "%s - Viking", file );
2926 gtk_window_set_title ( GTK_WINDOW(vw), title );
2930 static const gchar *window_get_filename ( VikWindow *vw )
2932 return vw->filename ? a_file_basename ( vw->filename ) : _("Untitled");
2935 GtkWidget *vik_window_get_drawmode_button ( VikWindow *vw, VikViewportDrawMode mode )
2937 GtkWidget *mode_button;
2940 #ifdef VIK_CONFIG_EXPEDIA
2941 case VIK_VIEWPORT_DRAWMODE_EXPEDIA: buttonname = "/ui/MainMenu/View/ModeExpedia"; break;
2943 case VIK_VIEWPORT_DRAWMODE_MERCATOR: buttonname = "/ui/MainMenu/View/ModeMercator"; break;
2944 case VIK_VIEWPORT_DRAWMODE_LATLON: buttonname = "/ui/MainMenu/View/ModeLatLon"; break;
2945 default: buttonname = "/ui/MainMenu/View/ModeUTM";
2947 mode_button = gtk_ui_manager_get_widget ( vw->uim, buttonname );
2948 g_assert ( mode_button );
2953 * vik_window_get_pan_move:
2954 * @vw: some VikWindow
2956 * Retrieves @vw's pan_move.
2958 * Should be removed as soon as possible.
2960 * Returns: @vw's pan_move
2964 gboolean vik_window_get_pan_move ( VikWindow *vw )
2966 return vw->pan_move;
2969 static void on_activate_recent_item (GtkRecentChooser *chooser,
2974 filename = gtk_recent_chooser_get_current_uri (chooser);
2975 if (filename != NULL)
2977 GFile *file = g_file_new_for_uri ( filename );
2978 gchar *path = g_file_get_path ( file );
2979 g_object_unref ( file );
2980 if ( self->filename )
2982 GSList *filenames = NULL;
2983 filenames = g_slist_append ( filenames, path );
2984 g_signal_emit ( G_OBJECT(self), window_signals[VW_OPENWINDOW_SIGNAL], 0, filenames );
2985 // NB: GSList & contents are freed by main.open_window
2988 vik_window_open_file ( self, path, TRUE );
2996 static void setup_recent_files (VikWindow *self)
2998 GtkRecentManager *manager;
2999 GtkRecentFilter *filter;
3000 GtkWidget *menu, *menu_item;
3002 filter = gtk_recent_filter_new ();
3003 /* gtk_recent_filter_add_application (filter, g_get_application_name()); */
3004 gtk_recent_filter_add_group(filter, "viking");
3006 manager = gtk_recent_manager_get_default ();
3007 menu = gtk_recent_chooser_menu_new_for_manager (manager);
3008 gtk_recent_chooser_set_sort_type (GTK_RECENT_CHOOSER (menu), GTK_RECENT_SORT_MRU);
3009 gtk_recent_chooser_add_filter (GTK_RECENT_CHOOSER (menu), filter);
3010 gtk_recent_chooser_set_limit (GTK_RECENT_CHOOSER (menu), a_vik_get_recent_number_files() );
3012 menu_item = gtk_ui_manager_get_widget (self->uim, "/ui/MainMenu/File/OpenRecentFile");
3013 gtk_menu_item_set_submenu (GTK_MENU_ITEM (menu_item), menu);
3015 g_signal_connect (G_OBJECT (menu), "item-activated",
3016 G_CALLBACK (on_activate_recent_item), (gpointer) self);
3022 static void update_recently_used_document (VikWindow *vw, const gchar *filename)
3024 /* Update Recently Used Document framework */
3025 GtkRecentManager *manager = gtk_recent_manager_get_default();
3026 GtkRecentData *recent_data = g_slice_new (GtkRecentData);
3027 gchar *groups[] = {"viking", NULL};
3028 GFile *file = g_file_new_for_commandline_arg(filename);
3029 gchar *uri = g_file_get_uri(file);
3030 gchar *basename = g_path_get_basename(filename);
3031 g_object_unref(file);
3034 recent_data->display_name = basename;
3035 recent_data->description = NULL;
3036 recent_data->mime_type = "text/x-gps-data";
3037 recent_data->app_name = (gchar *) g_get_application_name ();
3038 recent_data->app_exec = g_strjoin (" ", g_get_prgname (), "%f", NULL);
3039 recent_data->groups = groups;
3040 recent_data->is_private = FALSE;
3041 if (!gtk_recent_manager_add_full (manager, uri, recent_data))
3043 gchar *msg = g_strdup_printf (_("Unable to add '%s' to the list of recently used documents"), uri);
3044 vik_statusbar_set_message ( vw->viking_vs, VIK_STATUSBAR_INFO, msg );
3050 g_free (recent_data->app_exec);
3051 g_slice_free (GtkRecentData, recent_data);
3055 * Call this before doing things that may take a long time and otherwise not show any other feedback
3056 * such as loading and saving files
3058 void vik_window_set_busy_cursor ( VikWindow *vw )
3060 gdk_window_set_cursor ( gtk_widget_get_window(GTK_WIDGET(vw)), vw->busy_cursor );
3061 // Viewport has a separate cursor
3062 gdk_window_set_cursor ( gtk_widget_get_window(GTK_WIDGET(vw->viking_vvp)), vw->busy_cursor );
3063 // Ensure cursor updated before doing stuff
3064 while( gtk_events_pending() )
3065 gtk_main_iteration();
3068 void vik_window_clear_busy_cursor ( VikWindow *vw )
3070 gdk_window_set_cursor ( gtk_widget_get_window(GTK_WIDGET(vw)), NULL );
3071 // Restore viewport cursor
3072 gdk_window_set_cursor ( gtk_widget_get_window(GTK_WIDGET(vw->viking_vvp)), vw->viewport_cursor );
3075 void vik_window_open_file ( VikWindow *vw, const gchar *filename, gboolean change_filename )
3077 vik_window_set_busy_cursor ( vw );
3079 // Enable the *new* filename to be accessible by the Layers codez
3080 gchar *original_filename = g_strdup ( vw->filename );
3081 g_free ( vw->filename );
3082 vw->filename = g_strdup ( filename );
3083 gboolean success = FALSE;
3084 gboolean restore_original_filename = FALSE;
3086 vw->loaded_type = a_file_load ( vik_layers_panel_get_top_layer(vw->viking_vlp), vw->viking_vvp, filename );
3087 switch ( vw->loaded_type )
3089 case LOAD_TYPE_READ_FAILURE:
3090 a_dialog_error_msg ( GTK_WINDOW(vw), _("The file you requested could not be opened.") );
3092 case LOAD_TYPE_GPSBABEL_FAILURE:
3093 a_dialog_error_msg ( GTK_WINDOW(vw), _("GPSBabel is required to load files of this type or GPSBabel encountered problems.") );
3095 case LOAD_TYPE_GPX_FAILURE:
3096 a_dialog_error_msg_extra ( GTK_WINDOW(vw), _("Unable to load malformed GPX file %s"), filename );
3098 case LOAD_TYPE_UNSUPPORTED_FAILURE:
3099 a_dialog_error_msg_extra ( GTK_WINDOW(vw), _("Unsupported file type for %s"), filename );
3101 case LOAD_TYPE_VIK_FAILURE_NON_FATAL:
3103 // Since we can process .vik files with issues just show a warning in the status bar
3104 // Not that a user can do much about it... or tells them what this issue is yet...
3105 gchar *msg = g_strdup_printf (_("WARNING: issues encountered loading %s"), a_file_basename (filename) );
3106 vik_statusbar_set_message ( vw->viking_vs, VIK_STATUSBAR_INFO, msg );
3109 // No break, carry on to show any data
3110 case LOAD_TYPE_VIK_SUCCESS:
3112 restore_original_filename = TRUE; // NB Will actually get inverted by the 'success' component below
3113 GtkWidget *mode_button;
3115 if ( change_filename )
3116 window_set_filename ( vw, filename );
3117 mode_button = vik_window_get_drawmode_button ( vw, vik_viewport_get_drawmode ( vw->viking_vvp ) );
3118 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. */
3119 gtk_check_menu_item_set_active ( GTK_CHECK_MENU_ITEM(mode_button), TRUE );
3120 vw->only_updating_coord_mode_ui = FALSE;
3122 vik_layers_panel_change_coord_mode ( vw->viking_vlp, vik_viewport_get_coord_mode ( vw->viking_vvp ) );
3124 // Slightly long winded methods to align loaded viewport settings with the UI
3125 // Since the rewrite for toolbar + menu actions
3126 // there no longer exists a simple way to directly change the UI to a value for toggle settings
3127 // it only supports toggling the existing setting (otherwise get infinite loops in trying to align tb+menu elements)
3128 // Thus get state, compare them, if different then invert viewport setting and (re)sync the setting (via toggling)
3129 gboolean vp_state_scale = vik_viewport_get_draw_scale ( vw->viking_vvp );
3130 gboolean ui_state_scale = gtk_check_menu_item_get_active ( GTK_CHECK_MENU_ITEM(get_show_widget_by_name(vw, "ShowScale")) );
3131 if ( vp_state_scale != ui_state_scale ) {
3132 vik_viewport_set_draw_scale ( vw->viking_vvp, !vp_state_scale );
3133 toggle_draw_scale ( NULL, vw );
3135 gboolean vp_state_centermark = vik_viewport_get_draw_centermark ( vw->viking_vvp );
3136 gboolean ui_state_centermark = gtk_check_menu_item_get_active ( GTK_CHECK_MENU_ITEM(get_show_widget_by_name(vw, "ShowCenterMark")) );
3137 if ( vp_state_centermark != ui_state_centermark ) {
3138 vik_viewport_set_draw_centermark ( vw->viking_vvp, !vp_state_centermark );
3139 toggle_draw_centermark ( NULL, vw );
3141 gboolean vp_state_highlight = vik_viewport_get_draw_highlight ( vw->viking_vvp );
3142 gboolean ui_state_highlight = gtk_check_menu_item_get_active ( GTK_CHECK_MENU_ITEM(get_show_widget_by_name(vw, "ShowHighlight")) );
3143 if ( vp_state_highlight != ui_state_highlight ) {
3144 vik_viewport_set_draw_highlight ( vw->viking_vvp, !vp_state_highlight );
3145 toggle_draw_highlight ( NULL, vw );
3148 // NB No break, carry on to redraw
3149 //case LOAD_TYPE_OTHER_SUCCESS:
3152 // When LOAD_TYPE_OTHER_SUCCESS *only*, this will maintain the existing Viking project
3153 restore_original_filename = ! restore_original_filename;
3154 update_recently_used_document (vw, filename);
3159 if ( ! success || restore_original_filename )
3160 // Load didn't work or want to keep as the existing Viking project, keep using the original name
3161 window_set_filename ( vw, original_filename );
3162 g_free ( original_filename );
3164 vik_window_clear_busy_cursor ( vw );
3167 static void load_file ( GtkAction *a, VikWindow *vw )
3169 GSList *files = NULL;
3170 GSList *cur_file = NULL;
3172 if (!strcmp(gtk_action_get_name(a), "Open")) {
3175 else if (!strcmp(gtk_action_get_name(a), "Append")) {
3179 g_critical("Houston, we've had a problem.");
3183 GtkWidget *dialog = gtk_file_chooser_dialog_new (_("Please select a GPS data file to open. "),
3185 GTK_FILE_CHOOSER_ACTION_OPEN,
3186 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
3187 GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT,
3189 if ( last_folder_files_uri )
3190 gtk_file_chooser_set_current_folder_uri ( GTK_FILE_CHOOSER(dialog), last_folder_files_uri );
3192 GtkFileFilter *filter;
3193 // NB file filters are listed this way for alphabetical ordering
3194 #ifdef VIK_CONFIG_GEOCACHES
3195 filter = gtk_file_filter_new ();
3196 gtk_file_filter_set_name( filter, _("Geocaching") );
3197 gtk_file_filter_add_pattern ( filter, "*.loc" ); // No MIME type available
3198 gtk_file_chooser_add_filter (GTK_FILE_CHOOSER(dialog), filter);
3201 filter = gtk_file_filter_new ();
3202 gtk_file_filter_set_name( filter, _("Google Earth") );
3203 gtk_file_filter_add_mime_type ( filter, "application/vnd.google-earth.kml+xml");
3204 gtk_file_chooser_add_filter (GTK_FILE_CHOOSER(dialog), filter);
3206 filter = gtk_file_filter_new ();
3207 gtk_file_filter_set_name( filter, _("GPX") );
3208 gtk_file_filter_add_pattern ( filter, "*.gpx" ); // No MIME type available
3209 gtk_file_chooser_add_filter (GTK_FILE_CHOOSER(dialog), filter);
3211 filter = gtk_file_filter_new ();
3212 gtk_file_filter_set_name ( filter, _("JPG") );
3213 gtk_file_filter_add_mime_type ( filter, "image/jpeg");
3214 gtk_file_chooser_add_filter (GTK_FILE_CHOOSER(dialog), filter);
3216 filter = gtk_file_filter_new ();
3217 gtk_file_filter_set_name( filter, _("Viking") );
3218 gtk_file_filter_add_pattern ( filter, "*.vik" );
3219 gtk_file_filter_add_pattern ( filter, "*.viking" );
3220 gtk_file_chooser_add_filter (GTK_FILE_CHOOSER(dialog), filter);
3222 // NB could have filters for gpspoint (*.gps,*.gpsoint?) + gpsmapper (*.gsm,*.gpsmapper?)
3223 // However assume this are barely used and thus not worthy of inclusion
3224 // as they'll just make the options too many and have no clear file pattern
3225 // one can always use the all option
3226 filter = gtk_file_filter_new ();
3227 gtk_file_filter_set_name( filter, _("All") );
3228 gtk_file_filter_add_pattern ( filter, "*" );
3229 gtk_file_chooser_add_filter (GTK_FILE_CHOOSER(dialog), filter);
3230 // Default to any file - same as before open filters were added
3231 gtk_file_chooser_set_filter (GTK_FILE_CHOOSER(dialog), filter);
3233 gtk_file_chooser_set_select_multiple ( GTK_FILE_CHOOSER(dialog), TRUE );
3234 gtk_window_set_transient_for ( GTK_WINDOW(dialog), GTK_WINDOW(vw) );
3235 gtk_window_set_destroy_with_parent ( GTK_WINDOW(dialog), TRUE );
3237 if ( gtk_dialog_run ( GTK_DIALOG(dialog) ) == GTK_RESPONSE_ACCEPT )
3239 g_free ( last_folder_files_uri );
3240 last_folder_files_uri = gtk_file_chooser_get_current_folder_uri ( GTK_FILE_CHOOSER(dialog) );
3242 #ifdef VIKING_PROMPT_IF_MODIFIED
3243 if ( (vw->modified || vw->filename) && newwindow )
3245 if ( vw->filename && newwindow )
3247 g_signal_emit ( G_OBJECT(vw), window_signals[VW_OPENWINDOW_SIGNAL], 0, gtk_file_chooser_get_filenames (GTK_FILE_CHOOSER(dialog) ) );
3250 files = gtk_file_chooser_get_filenames (GTK_FILE_CHOOSER(dialog) );
3251 gboolean change_fn = newwindow && (g_slist_length(files)==1); /* only change fn if one file */
3252 gboolean first_vik_file = TRUE;
3254 while ( cur_file ) {
3256 gchar *file_name = cur_file->data;
3257 if ( newwindow && check_file_magic_vik ( file_name ) ) {
3258 // Load first of many .vik files in current window
3259 if ( first_vik_file ) {
3260 vik_window_open_file ( vw, file_name, TRUE );
3261 first_vik_file = FALSE;
3264 // Load each subsequent .vik file in a separate window
3265 VikWindow *newvw = vik_window_new_window ();
3267 vik_window_open_file ( newvw, file_name, TRUE );
3272 vik_window_open_file ( vw, file_name, change_fn );
3275 cur_file = g_slist_next (cur_file);
3277 g_slist_free (files);
3280 gtk_widget_destroy ( dialog );
3283 static gboolean save_file_as ( GtkAction *a, VikWindow *vw )
3285 gboolean rv = FALSE;
3288 GtkWidget *dialog = gtk_file_chooser_dialog_new (_("Save as Viking File."),
3290 GTK_FILE_CHOOSER_ACTION_SAVE,
3291 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
3292 GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT,
3294 if ( last_folder_files_uri )
3295 gtk_file_chooser_set_current_folder_uri ( GTK_FILE_CHOOSER(dialog), last_folder_files_uri );
3297 GtkFileFilter *filter;
3298 filter = gtk_file_filter_new ();
3299 gtk_file_filter_set_name( filter, _("All") );
3300 gtk_file_filter_add_pattern ( filter, "*" );
3301 gtk_file_chooser_add_filter (GTK_FILE_CHOOSER(dialog), filter);
3303 filter = gtk_file_filter_new ();
3304 gtk_file_filter_set_name( filter, _("Viking") );
3305 gtk_file_filter_add_pattern ( filter, "*.vik" );
3306 gtk_file_filter_add_pattern ( filter, "*.viking" );
3307 gtk_file_chooser_add_filter (GTK_FILE_CHOOSER(dialog), filter);
3308 // Default to a Viking file
3309 gtk_file_chooser_set_filter (GTK_FILE_CHOOSER(dialog), filter);
3311 gtk_window_set_transient_for ( GTK_WINDOW(dialog), GTK_WINDOW(vw) );
3312 gtk_window_set_destroy_with_parent ( GTK_WINDOW(dialog), TRUE );
3314 // Auto append / replace extension with '.vik' to the suggested file name as it's going to be a Viking File
3315 gchar* auto_save_name = g_strdup ( window_get_filename ( vw ) );
3316 if ( ! a_file_check_ext ( auto_save_name, ".vik" ) )
3317 auto_save_name = g_strconcat ( auto_save_name, ".vik", NULL );
3319 gtk_file_chooser_set_current_name (GTK_FILE_CHOOSER(dialog), auto_save_name);
3321 while ( gtk_dialog_run ( GTK_DIALOG(dialog) ) == GTK_RESPONSE_ACCEPT )
3323 fn = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER(dialog) );
3324 if ( g_file_test ( fn, G_FILE_TEST_EXISTS ) == FALSE || a_dialog_yes_or_no ( GTK_WINDOW(dialog), _("The file \"%s\" exists, do you wish to overwrite it?"), a_file_basename ( fn ) ) )
3326 window_set_filename ( vw, fn );
3327 rv = window_save ( vw );
3329 vw->modified = FALSE;
3330 g_free ( last_folder_files_uri );
3331 last_folder_files_uri = gtk_file_chooser_get_current_folder_uri ( GTK_FILE_CHOOSER(dialog) );
3336 g_free ( auto_save_name );
3337 gtk_widget_destroy ( dialog );
3341 static gboolean window_save ( VikWindow *vw )
3343 vik_window_set_busy_cursor ( vw );
3344 gboolean success = TRUE;
3346 if ( a_file_save ( vik_layers_panel_get_top_layer ( vw->viking_vlp ), vw->viking_vvp, vw->filename ) )
3348 update_recently_used_document ( vw, vw->filename );
3352 a_dialog_error_msg ( GTK_WINDOW(vw), _("The filename you requested could not be opened for writing.") );
3355 vik_window_clear_busy_cursor ( vw );
3359 static gboolean save_file ( GtkAction *a, VikWindow *vw )
3361 if ( ! vw->filename )
3362 return save_file_as ( NULL, vw );
3365 vw->modified = FALSE;
3366 return window_save ( vw );
3373 * Export all TRW Layers in the list to individual files in the specified directory
3375 * Returns: %TRUE on success
3377 static gboolean export_to ( VikWindow *vw, GList *gl, VikFileType_t vft, const gchar *dir, const gchar *extension )
3379 gboolean success = TRUE;
3381 gint export_count = 0;
3383 vik_window_set_busy_cursor ( vw );
3387 gchar *fn = g_strconcat ( dir, G_DIR_SEPARATOR_S, VIK_LAYER(gl->data)->name, extension, NULL );
3389 // Some protection in attempting to write too many same named files
3390 // As this will get horribly slow...
3391 gboolean safe = FALSE;
3393 while ( ii < 5000 ) {
3394 if ( g_file_test ( fn, G_FILE_TEST_EXISTS ) ) {
3397 fn = g_strdup_printf ( "%s%s%s#%03d%s", dir, G_DIR_SEPARATOR_S, VIK_LAYER(gl->data)->name, ii, extension );
3408 // NB: We allow exporting empty layers
3410 gboolean this_success = a_file_export ( VIK_TRW_LAYER(gl->data), fn, vft, NULL, TRUE );
3412 // Show some progress
3413 if ( this_success ) {
3415 gchar *message = g_strdup_printf ( _("Exporting to file: %s"), fn );
3416 vik_statusbar_set_message ( vw->viking_vs, VIK_STATUSBAR_INFO, message );
3417 while ( gtk_events_pending() )
3418 gtk_main_iteration ();
3422 success = success && this_success;
3426 gl = g_list_next ( gl );
3429 vik_window_clear_busy_cursor ( vw );
3431 // Confirm what happened.
3432 gchar *message = g_strdup_printf ( _("Exported files: %d"), export_count );
3433 vik_statusbar_set_message ( vw->viking_vs, VIK_STATUSBAR_INFO, message );
3439 static void export_to_common ( VikWindow *vw, VikFileType_t vft, const gchar *extension )
3441 GList *gl = vik_layers_panel_get_all_layers_of_type ( vw->viking_vlp, VIK_LAYER_TRW, TRUE );
3444 a_dialog_info_msg ( GTK_WINDOW(vw), _("Nothing to Export!") );
3448 GtkWidget *dialog = gtk_file_chooser_dialog_new ( _("Export to directory"),
3450 GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER,
3452 GTK_RESPONSE_REJECT,
3454 GTK_RESPONSE_ACCEPT,
3456 gtk_window_set_transient_for ( GTK_WINDOW(dialog), GTK_WINDOW(vw) );
3457 gtk_window_set_destroy_with_parent ( GTK_WINDOW(dialog), TRUE );
3458 gtk_window_set_modal ( GTK_WINDOW(dialog), TRUE );
3460 gtk_widget_show_all ( dialog );
3462 if ( gtk_dialog_run ( GTK_DIALOG(dialog) ) == GTK_RESPONSE_ACCEPT ) {
3463 gchar *dir = gtk_file_chooser_get_filename ( GTK_FILE_CHOOSER(dialog) );
3464 gtk_widget_destroy ( dialog );
3466 if ( !export_to ( vw, gl, vft, dir, extension ) )
3467 a_dialog_error_msg ( GTK_WINDOW(vw),_("Could not convert all files") );
3472 gtk_widget_destroy ( dialog );
3477 static void export_to_gpx ( GtkAction *a, VikWindow *vw )
3479 export_to_common ( vw, FILE_TYPE_GPX, ".gpx" );
3482 static void export_to_kml ( GtkAction *a, VikWindow *vw )
3484 export_to_common ( vw, FILE_TYPE_KML, ".kml" );
3487 static void file_properties_cb ( GtkAction *a, VikWindow *vw )
3489 gchar *message = NULL;
3490 if ( vw->filename ) {
3491 if ( g_file_test ( vw->filename, G_FILE_TEST_EXISTS ) ) {
3492 // Get some timestamp information of the file
3494 if ( g_stat ( vw->filename, &stat_buf ) == 0 ) {
3496 strftime ( time_buf, sizeof(time_buf), "%c", gmtime((const time_t *)&stat_buf.st_mtime) );
3498 gint byte_size = stat_buf.st_size;
3499 #if GLIB_CHECK_VERSION(2,30,0)
3500 size = g_format_size_full ( byte_size, G_FORMAT_SIZE_DEFAULT );
3502 size = g_format_size_for_display ( byte_size );
3504 message = g_strdup_printf ( "%s\n\n%s\n\n%s", vw->filename, time_buf, size );
3509 message = g_strdup ( _("File not accessible") );
3512 message = g_strdup ( _("No Viking File") );
3515 a_dialog_info_msg ( GTK_WINDOW(vw), message );
3519 static void my_acquire ( VikWindow *vw, VikDataSourceInterface *datasource )
3521 vik_datasource_mode_t mode = datasource->mode;
3522 if ( mode == VIK_DATASOURCE_AUTO_LAYER_MANAGEMENT )
3523 mode = VIK_DATASOURCE_CREATENEWLAYER;
3524 a_acquire ( vw, vw->viking_vlp, vw->viking_vvp, mode, datasource, NULL, NULL );
3527 static void acquire_from_gps ( GtkAction *a, VikWindow *vw )
3529 my_acquire ( vw, &vik_datasource_gps_interface );
3532 static void acquire_from_file ( GtkAction *a, VikWindow *vw )
3534 my_acquire ( vw, &vik_datasource_file_interface );
3537 static void acquire_from_geojson ( GtkAction *a, VikWindow *vw )
3539 my_acquire ( vw, &vik_datasource_geojson_interface );
3542 static void acquire_from_routing ( GtkAction *a, VikWindow *vw )
3544 my_acquire ( vw, &vik_datasource_routing_interface );
3547 #ifdef VIK_CONFIG_OPENSTREETMAP
3548 static void acquire_from_osm ( GtkAction *a, VikWindow *vw )
3550 my_acquire ( vw, &vik_datasource_osm_interface );
3553 static void acquire_from_my_osm ( GtkAction *a, VikWindow *vw )
3555 my_acquire ( vw, &vik_datasource_osm_my_traces_interface );
3559 #ifdef VIK_CONFIG_GEOCACHES
3560 static void acquire_from_gc ( GtkAction *a, VikWindow *vw )
3562 my_acquire ( vw, &vik_datasource_gc_interface );
3566 #ifdef VIK_CONFIG_GEOTAG
3567 static void acquire_from_geotag ( GtkAction *a, VikWindow *vw )
3569 my_acquire ( vw, &vik_datasource_geotag_interface );
3573 #ifdef VIK_CONFIG_GEONAMES
3574 static void acquire_from_wikipedia ( GtkAction *a, VikWindow *vw )
3576 my_acquire ( vw, &vik_datasource_wikipedia_interface );
3580 static void acquire_from_url ( GtkAction *a, VikWindow *vw )
3582 my_acquire ( vw, &vik_datasource_url_interface );
3585 static void goto_default_location( GtkAction *a, VikWindow *vw)
3588 ll.lat = a_vik_get_default_lat();
3589 ll.lon = a_vik_get_default_long();
3590 vik_viewport_set_center_latlon(vw->viking_vvp, &ll, TRUE);
3591 vik_layers_panel_emit_update(vw->viking_vlp);
3595 static void goto_address( GtkAction *a, VikWindow *vw)
3597 a_vik_goto ( vw, vw->viking_vvp );
3598 vik_layers_panel_emit_update ( vw->viking_vlp );
3601 static void mapcache_flush_cb ( GtkAction *a, VikWindow *vw )
3606 static void menu_copy_centre_cb ( GtkAction *a, VikWindow *vw )
3608 const VikCoord* coord;
3610 gchar *lat = NULL, *lon = NULL;
3612 coord = vik_viewport_get_center ( vw->viking_vvp );
3613 vik_coord_to_utm ( coord, &utm );
3615 gboolean full_format = FALSE;
3616 (void)a_settings_get_boolean ( VIK_SETTINGS_WIN_COPY_CENTRE_FULL_FORMAT, &full_format );
3619 // Bells & Whistles - may include degrees, minutes and second symbols
3620 get_location_strings ( vw, utm, &lat, &lon );
3622 // Simple x.xx y.yy format
3624 a_coords_utm_to_latlon ( &utm, &ll );
3625 lat = g_strdup_printf ( "%.6f", ll.lat );
3626 lon = g_strdup_printf ( "%.6f", ll.lon );
3629 gchar *msg = g_strdup_printf ( "%s %s", lat, lon );
3633 a_clipboard_copy ( VIK_CLIPBOARD_DATA_TEXT, 0, 0, 0, msg, NULL );
3638 static void layer_defaults_cb ( GtkAction *a, VikWindow *vw )
3640 gchar **texts = g_strsplit ( gtk_action_get_name(a), "Layer", 0 );
3643 return; // Internally broken :(
3645 if ( ! a_layer_defaults_show_window ( GTK_WINDOW(vw), texts[1] ) )
3646 a_dialog_info_msg ( GTK_WINDOW(vw), _("This layer has no configurable properties.") );
3647 // NB no update needed
3649 g_strfreev ( texts );
3652 static void preferences_change_update ( VikWindow *vw, gpointer data )
3654 // Want to update all TrackWaypoint layers
3655 GList *layers = vik_layers_panel_get_all_layers_of_type ( vw->viking_vlp, VIK_LAYER_TRW, TRUE );
3661 // Reset the individual waypoints themselves due to the preferences change
3662 VikTrwLayer *vtl = VIK_TRW_LAYER(layers->data);
3663 vik_trw_layer_reset_waypoints ( vtl );
3664 layers = g_list_next ( layers );
3667 g_list_free ( layers );
3672 static void preferences_cb ( GtkAction *a, VikWindow *vw )
3674 gboolean wp_icon_size = a_vik_get_use_large_waypoint_icons();
3676 a_preferences_show_window ( GTK_WINDOW(vw) );
3678 // Has the waypoint size setting changed?
3679 if (wp_icon_size != a_vik_get_use_large_waypoint_icons()) {
3680 // Delete icon indexing 'cache' and so automatically regenerates with the new setting when changed
3681 clear_garmin_icon_syms ();
3683 // Update all windows
3684 g_slist_foreach ( window_list, (GFunc) preferences_change_update, NULL );
3687 // Ensure TZ Lookup initialized
3688 if ( a_vik_get_time_ref_frame() == VIK_TIME_REF_WORLD )
3689 vu_setup_lat_lon_tz_lookup();
3691 toolbar_apply_settings ( vw->viking_vtb, vw->main_vbox, vw->menu_hbox, TRUE );
3694 static void default_location_cb ( GtkAction *a, VikWindow *vw )
3696 /* Simplistic repeat of preference setting
3697 Only the name & type are important for setting the preference via this 'external' way */
3698 VikLayerParam pref_lat[] = {
3699 { VIK_LAYER_NUM_TYPES,
3700 VIKING_PREFERENCES_NAMESPACE "default_latitude",
3701 VIK_LAYER_PARAM_DOUBLE,
3702 VIK_LAYER_GROUP_NONE,
3704 VIK_LAYER_WIDGET_SPINBUTTON,
3713 VikLayerParam pref_lon[] = {
3714 { VIK_LAYER_NUM_TYPES,
3715 VIKING_PREFERENCES_NAMESPACE "default_longitude",
3716 VIK_LAYER_PARAM_DOUBLE,
3717 VIK_LAYER_GROUP_NONE,
3719 VIK_LAYER_WIDGET_SPINBUTTON,
3729 /* Get current center */
3731 vik_coord_to_latlon ( vik_viewport_get_center ( vw->viking_vvp ), &ll );
3733 /* Apply to preferences */
3734 VikLayerParamData vlp_data;
3735 vlp_data.d = ll.lat;
3736 a_preferences_run_setparam (vlp_data, pref_lat);
3737 vlp_data.d = ll.lon;
3738 a_preferences_run_setparam (vlp_data, pref_lon);
3739 /* Remember to save */
3740 a_preferences_save_to_file();
3746 static void clear_cb ( GtkAction *a, VikWindow *vw )
3748 // Do nothing if empty
3749 VikAggregateLayer *top = vik_layers_panel_get_top_layer(vw->viking_vlp);
3750 if ( ! vik_aggregate_layer_is_empty(top) ) {
3751 if ( a_dialog_yes_or_no ( GTK_WINDOW(vw), _("Are you sure you wish to delete all layers?"), NULL ) ) {
3752 vik_layers_panel_clear ( vw->viking_vlp );
3753 window_set_filename ( vw, NULL );
3759 static void window_close ( GtkAction *a, VikWindow *vw )
3761 if ( ! delete_event ( vw ) )
3762 gtk_widget_destroy ( GTK_WIDGET(vw) );
3765 static gboolean save_file_and_exit ( GtkAction *a, VikWindow *vw )
3767 if (save_file( NULL, vw)) {
3768 window_close( NULL, vw);
3775 static void zoom_to_cb ( GtkAction *a, VikWindow *vw )
3777 gdouble xmpp = vik_viewport_get_xmpp ( vw->viking_vvp ), ympp = vik_viewport_get_ympp ( vw->viking_vvp );
3778 if ( a_dialog_custom_zoom ( GTK_WINDOW(vw), &xmpp, &ympp ) )
3780 vik_viewport_set_xmpp ( vw->viking_vvp, xmpp );
3781 vik_viewport_set_ympp ( vw->viking_vvp, ympp );
3786 static void save_image_file ( VikWindow *vw, const gchar *fn, guint w, guint h, gdouble zoom, gboolean save_as_png, gboolean save_kmz )
3788 /* more efficient way: stuff draws directly to pixbuf (fork viewport) */
3789 GdkPixbuf *pixbuf_to_save;
3790 gdouble old_xmpp, old_ympp;
3791 GError *error = NULL;
3793 GtkWidget *msgbox = gtk_message_dialog_new ( GTK_WINDOW(vw),
3794 GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
3797 _("Generating image file...") );
3799 g_signal_connect_swapped (msgbox, "response", G_CALLBACK (gtk_widget_destroy), msgbox);
3800 // Ensure dialog shown
3801 gtk_widget_show_all ( msgbox );
3803 vik_statusbar_set_message ( vw->viking_vs, VIK_STATUSBAR_INFO, _("Generating image file...") );
3804 while ( gtk_events_pending() )
3805 gtk_main_iteration ();
3806 // Despite many efforts & variations, GTK on my Linux system doesn't show the actual msgbox contents :(
3807 // At least the empty box can give a clue something's going on + the statusbar msg...
3808 // Windows version under Wine OK!
3810 /* backup old zoom & set new */
3811 old_xmpp = vik_viewport_get_xmpp ( vw->viking_vvp );
3812 old_ympp = vik_viewport_get_ympp ( vw->viking_vvp );
3813 vik_viewport_set_zoom ( vw->viking_vvp, zoom );
3815 /* reset width and height: */
3816 vik_viewport_configure_manually ( vw->viking_vvp, w, h );
3818 /* draw all layers */
3821 /* save buffer as file. */
3822 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);
3823 if ( !pixbuf_to_save ) {
3824 g_warning("Failed to generate internal pixmap size: %d x %d", w, h);
3825 gtk_message_dialog_set_markup ( GTK_MESSAGE_DIALOG(msgbox), _("Failed to generate internal image.\n\nTry creating a smaller image.") );
3829 int ans = 0; // Default to success
3832 gdouble north, east, south, west;
3833 vik_viewport_get_min_max_lat_lon ( vw->viking_vvp, &south, &north, &west, &east );
3834 ans = kmz_save_file ( pixbuf_to_save, fn, north, east, south, west );
3837 gdk_pixbuf_save ( pixbuf_to_save, fn, save_as_png ? "png" : "jpeg", &error, NULL );
3839 g_warning("Unable to write to file %s: %s", fn, error->message );
3840 g_error_free (error);
3846 gtk_message_dialog_set_markup ( GTK_MESSAGE_DIALOG(msgbox), _("Image file generated.") );
3848 gtk_message_dialog_set_markup ( GTK_MESSAGE_DIALOG(msgbox), _("Failed to generate image file.") );
3850 g_object_unref ( G_OBJECT(pixbuf_to_save) );
3853 vik_statusbar_set_message ( vw->viking_vs, VIK_STATUSBAR_INFO, "" );
3854 gtk_dialog_add_button ( GTK_DIALOG(msgbox), GTK_STOCK_OK, GTK_RESPONSE_OK );
3855 gtk_dialog_run ( GTK_DIALOG(msgbox) ); // Don't care about the result
3857 /* pretend like nothing happened ;) */
3858 vik_viewport_set_xmpp ( vw->viking_vvp, old_xmpp );
3859 vik_viewport_set_ympp ( vw->viking_vvp, old_ympp );
3860 vik_viewport_configure ( vw->viking_vvp );
3864 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 )
3866 gulong size = sizeof(gchar) * (strlen(fn) + 15);
3867 gchar *name_of_file = g_malloc ( size );
3869 struct UTM utm_orig, utm;
3871 /* *** copied from above *** */
3872 GdkPixbuf *pixbuf_to_save;
3873 gdouble old_xmpp, old_ympp;
3874 GError *error = NULL;
3876 /* backup old zoom & set new */
3877 old_xmpp = vik_viewport_get_xmpp ( vw->viking_vvp );
3878 old_ympp = vik_viewport_get_ympp ( vw->viking_vvp );
3879 vik_viewport_set_zoom ( vw->viking_vvp, zoom );
3881 /* reset width and height: do this only once for all images (same size) */
3882 vik_viewport_configure_manually ( vw->viking_vvp, w, h );
3883 /* *** end copy from above *** */
3885 g_assert ( vik_viewport_get_coord_mode ( vw->viking_vvp ) == VIK_COORD_UTM );
3887 if ( g_mkdir(fn,0777) != 0 )
3888 g_warning ( "%s: Failed to create directory %s", __FUNCTION__, fn );
3890 utm_orig = *((const struct UTM *)vik_viewport_get_center ( vw->viking_vvp ));
3892 for ( y = 1; y <= tiles_h; y++ )
3894 for ( x = 1; x <= tiles_w; x++ )
3896 g_snprintf ( name_of_file, size, "%s%cy%d-x%d.%s", fn, G_DIR_SEPARATOR, y, x, save_as_png ? "png" : "jpg" );
3898 if ( tiles_w & 0x1 )
3899 utm.easting += ((gdouble)x - ceil(((gdouble)tiles_w)/2)) * (w*zoom);
3901 utm.easting += ((gdouble)x - (((gdouble)tiles_w)+1)/2) * (w*zoom);
3902 if ( tiles_h & 0x1 ) /* odd */
3903 utm.northing -= ((gdouble)y - ceil(((gdouble)tiles_h)/2)) * (h*zoom);
3905 utm.northing -= ((gdouble)y - (((gdouble)tiles_h)+1)/2) * (h*zoom);
3907 /* move to correct place. */
3908 vik_viewport_set_center_utm ( vw->viking_vvp, &utm, FALSE );
3912 /* save buffer as file. */
3913 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);
3914 gdk_pixbuf_save ( pixbuf_to_save, name_of_file, save_as_png ? "png" : "jpeg", &error, NULL );
3917 gchar *msg = g_strdup_printf (_("Unable to write to file %s: %s"), name_of_file, error->message );
3918 vik_statusbar_set_message ( vw->viking_vs, VIK_STATUSBAR_INFO, msg );
3920 g_error_free (error);
3923 g_object_unref ( G_OBJECT(pixbuf_to_save) );
3927 vik_viewport_set_center_utm ( vw->viking_vvp, &utm_orig, FALSE );
3928 vik_viewport_set_xmpp ( vw->viking_vvp, old_xmpp );
3929 vik_viewport_set_ympp ( vw->viking_vvp, old_ympp );
3930 vik_viewport_configure ( vw->viking_vvp );
3933 g_free ( name_of_file );
3936 static void draw_to_image_file_current_window_cb(GtkWidget* widget,GdkEventButton *event,gpointer *pass_along)
3938 VikWindow *vw = VIK_WINDOW(pass_along[0]);
3939 GtkSpinButton *width_spin = GTK_SPIN_BUTTON(pass_along[1]), *height_spin = GTK_SPIN_BUTTON(pass_along[2]);
3941 gint active = gtk_combo_box_get_active ( GTK_COMBO_BOX(pass_along[3]) );
3942 gdouble zoom = pow (2, active-2 );
3944 gdouble width_min, width_max, height_min, height_max;
3947 gtk_spin_button_get_range ( width_spin, &width_min, &width_max );
3948 gtk_spin_button_get_range ( height_spin, &height_min, &height_max );
3950 /* TODO: support for xzoom and yzoom values */
3951 width = vik_viewport_get_width ( vw->viking_vvp ) * vik_viewport_get_xmpp ( vw->viking_vvp ) / zoom;
3952 height = vik_viewport_get_height ( vw->viking_vvp ) * vik_viewport_get_xmpp ( vw->viking_vvp ) / zoom;
3954 if ( width > width_max || width < width_min || height > height_max || height < height_min )
3955 a_dialog_info_msg ( GTK_WINDOW(vw), _("Viewable region outside allowable pixel size bounds for image. Clipping width/height values.") );
3957 gtk_spin_button_set_value ( width_spin, width );
3958 gtk_spin_button_set_value ( height_spin, height );
3961 static void draw_to_image_file_total_area_cb (GtkSpinButton *spinbutton, gpointer *pass_along)
3963 GtkSpinButton *width_spin = GTK_SPIN_BUTTON(pass_along[1]), *height_spin = GTK_SPIN_BUTTON(pass_along[2]);
3965 gint active = gtk_combo_box_get_active ( GTK_COMBO_BOX(pass_along[3]) );
3966 gdouble zoom = pow (2, active-2 );
3970 w = gtk_spin_button_get_value(width_spin) * zoom;
3971 h = gtk_spin_button_get_value(height_spin) * zoom;
3972 if (pass_along[4]) /* save many images; find TOTAL area covered */
3974 w *= gtk_spin_button_get_value(GTK_SPIN_BUTTON(pass_along[4]));
3975 h *= gtk_spin_button_get_value(GTK_SPIN_BUTTON(pass_along[5]));
3977 vik_units_distance_t dist_units = a_vik_get_units_distance ();
3978 switch (dist_units) {
3979 case VIK_UNITS_DISTANCE_KILOMETRES:
3980 label_text = g_strdup_printf ( _("Total area: %ldm x %ldm (%.3f sq. km)"), (glong)w, (glong)h, (w*h/1000000));
3982 case VIK_UNITS_DISTANCE_MILES:
3983 label_text = g_strdup_printf ( _("Total area: %ldm x %ldm (%.3f sq. miles)"), (glong)w, (glong)h, (w*h/2589988.11));
3985 case VIK_UNITS_DISTANCE_NAUTICAL_MILES:
3986 label_text = g_strdup_printf ( _("Total area: %ldm x %ldm (%.3f sq. NM)"), (glong)w, (glong)h, (w*h/(1852.0*1852.0)));
3989 label_text = g_strdup_printf ("Just to keep the compiler happy");
3990 g_critical("Houston, we've had a problem. distance=%d", dist_units);
3993 gtk_label_set_text(GTK_LABEL(pass_along[6]), label_text);
3994 g_free ( label_text );
3998 VW_GEN_SINGLE_IMAGE,
3999 VW_GEN_DIRECTORY_OF_IMAGES,
4004 * Get an allocated filename (or directory as specified)
4006 static gchar* draw_image_filename ( VikWindow *vw, img_generation_t img_gen )
4009 if ( img_gen != VW_GEN_DIRECTORY_OF_IMAGES )
4012 GtkWidget *dialog = gtk_file_chooser_dialog_new (_("Save Image"),
4014 GTK_FILE_CHOOSER_ACTION_SAVE,
4015 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
4016 GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT,
4018 if ( last_folder_images_uri )
4019 gtk_file_chooser_set_current_folder_uri ( GTK_FILE_CHOOSER(dialog), last_folder_images_uri );
4021 GtkFileChooser *chooser = GTK_FILE_CHOOSER ( dialog );
4023 GtkFileFilter *filter;
4024 filter = gtk_file_filter_new ();
4025 gtk_file_filter_set_name ( filter, _("All") );
4026 gtk_file_filter_add_pattern ( filter, "*" );
4027 gtk_file_chooser_add_filter ( chooser, filter );
4029 if ( img_gen == VW_GEN_KMZ_FILE ) {
4030 filter = gtk_file_filter_new ();
4031 gtk_file_filter_set_name ( filter, _("KMZ") );
4032 gtk_file_filter_add_mime_type ( filter, "vnd.google-earth.kmz");
4033 gtk_file_filter_add_pattern ( filter, "*.kmz" );
4034 gtk_file_chooser_add_filter ( chooser, filter );
4035 gtk_file_chooser_set_filter ( chooser, filter );
4038 filter = gtk_file_filter_new ();
4039 gtk_file_filter_set_name ( filter, _("JPG") );
4040 gtk_file_filter_add_mime_type ( filter, "image/jpeg");
4041 gtk_file_chooser_add_filter ( chooser, filter );
4043 if ( !vw->draw_image_save_as_png )
4044 gtk_file_chooser_set_filter ( chooser, filter );
4046 filter = gtk_file_filter_new ();
4047 gtk_file_filter_set_name ( filter, _("PNG") );
4048 gtk_file_filter_add_mime_type ( filter, "image/png");
4049 gtk_file_chooser_add_filter ( chooser, filter );
4051 if ( vw->draw_image_save_as_png )
4052 gtk_file_chooser_set_filter ( chooser, filter );
4055 gtk_window_set_transient_for ( GTK_WINDOW(dialog), GTK_WINDOW(vw) );
4056 gtk_window_set_destroy_with_parent ( GTK_WINDOW(dialog), TRUE );
4058 if ( gtk_dialog_run ( GTK_DIALOG(dialog) ) == GTK_RESPONSE_ACCEPT ) {
4059 g_free ( last_folder_images_uri );
4060 last_folder_images_uri = gtk_file_chooser_get_current_folder_uri ( GTK_FILE_CHOOSER(dialog) );
4062 fn = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER(dialog) );
4063 if ( g_file_test ( fn, G_FILE_TEST_EXISTS ) )
4064 if ( ! a_dialog_yes_or_no ( GTK_WINDOW(dialog), _("The file \"%s\" exists, do you wish to overwrite it?"), a_file_basename ( fn ) ) )
4067 gtk_widget_destroy ( dialog );
4071 // For some reason this method is only written to work in UTM...
4072 if ( vik_viewport_get_coord_mode(vw->viking_vvp) != VIK_COORD_UTM ) {
4073 a_dialog_error_msg ( GTK_WINDOW(vw), _("You must be in UTM mode to use this feature") );
4077 GtkWidget *dialog = gtk_file_chooser_dialog_new (_("Choose a directory to hold images"),
4079 GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER,
4080 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
4081 GTK_STOCK_OK, GTK_RESPONSE_ACCEPT,
4083 gtk_window_set_transient_for ( GTK_WINDOW(dialog), GTK_WINDOW(vw) );
4084 gtk_window_set_destroy_with_parent ( GTK_WINDOW(dialog), TRUE );
4086 if ( gtk_dialog_run ( GTK_DIALOG(dialog) ) == GTK_RESPONSE_ACCEPT ) {
4087 fn = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER(dialog) );
4089 gtk_widget_destroy ( dialog );
4094 static void draw_to_image_file ( VikWindow *vw, img_generation_t img_gen )
4096 /* todo: default for answers inside VikWindow or static (thruout instance) */
4097 GtkWidget *dialog = gtk_dialog_new_with_buttons ( _("Save to Image File"), GTK_WINDOW(vw),
4098 GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
4100 GTK_RESPONSE_REJECT,
4102 GTK_RESPONSE_ACCEPT,
4104 GtkWidget *width_label, *width_spin, *height_label, *height_spin;
4105 GtkWidget *current_window_button;
4106 gpointer current_window_pass_along[7];
4107 GtkWidget *zoom_label, *zoom_combo;
4108 GtkWidget *total_size_label;
4110 // only used for VW_GEN_DIRECTORY_OF_IMAGES
4111 GtkWidget *tiles_width_spin = NULL, *tiles_height_spin = NULL;
4113 width_label = gtk_label_new ( _("Width (pixels):") );
4114 width_spin = gtk_spin_button_new ( GTK_ADJUSTMENT(gtk_adjustment_new ( vw->draw_image_width, 10, 50000, 10, 100, 0 )), 10, 0 );
4115 height_label = gtk_label_new ( _("Height (pixels):") );
4116 height_spin = gtk_spin_button_new ( GTK_ADJUSTMENT(gtk_adjustment_new ( vw->draw_image_height, 10, 50000, 10, 100, 0 )), 10, 0 );
4118 GtkWidget *win_warning_label = gtk_label_new ( _("WARNING: USING LARGE IMAGES OVER 10000x10000\nMAY CRASH THE PROGRAM!") );
4120 zoom_label = gtk_label_new ( _("Zoom (meters per pixel):") );
4121 /* TODO: separate xzoom and yzoom factors */
4122 zoom_combo = create_zoom_combo_all_levels();
4124 gdouble mpp = vik_viewport_get_xmpp(vw->viking_vvp);
4125 gint active = 2 + round ( log (mpp) / log (2) );
4127 // Can we not hard code size here?
4132 gtk_combo_box_set_active ( GTK_COMBO_BOX(zoom_combo), active );
4134 total_size_label = gtk_label_new ( NULL );
4136 current_window_button = gtk_button_new_with_label ( _("Area in current viewable window") );
4137 current_window_pass_along [0] = vw;
4138 current_window_pass_along [1] = width_spin;
4139 current_window_pass_along [2] = height_spin;
4140 current_window_pass_along [3] = zoom_combo;
4141 current_window_pass_along [4] = NULL; // Only for directory of tiles: width
4142 current_window_pass_along [5] = NULL; // Only for directory of tiles: height
4143 current_window_pass_along [6] = total_size_label;
4144 g_signal_connect ( G_OBJECT(current_window_button), "button_press_event", G_CALLBACK(draw_to_image_file_current_window_cb), current_window_pass_along );
4146 GtkWidget *png_radio = gtk_radio_button_new_with_label ( NULL, _("Save as PNG") );
4147 GtkWidget *jpeg_radio = gtk_radio_button_new_with_label_from_widget ( GTK_RADIO_BUTTON(png_radio), _("Save as JPEG") );
4149 if ( img_gen == VW_GEN_KMZ_FILE ) {
4150 // Don't show image type selection if creating a KMZ (always JPG internally)
4151 // Start with viewable area by default
4152 draw_to_image_file_current_window_cb ( current_window_button, NULL, current_window_pass_along );
4154 gtk_box_pack_start (GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), jpeg_radio, FALSE, FALSE, 0);
4155 gtk_box_pack_start (GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), png_radio, FALSE, FALSE, 0);
4158 if ( ! vw->draw_image_save_as_png )
4159 gtk_toggle_button_set_active ( GTK_TOGGLE_BUTTON(jpeg_radio), TRUE );
4161 gtk_box_pack_start (GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), width_label, FALSE, FALSE, 0);
4162 gtk_box_pack_start (GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), width_spin, FALSE, FALSE, 0);
4163 gtk_box_pack_start (GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), height_label, FALSE, FALSE, 0);
4164 gtk_box_pack_start (GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), height_spin, FALSE, FALSE, 0);
4166 gtk_box_pack_start (GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), win_warning_label, FALSE, FALSE, 0);
4168 gtk_box_pack_start (GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), current_window_button, FALSE, FALSE, 0);
4169 gtk_box_pack_start (GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), zoom_label, FALSE, FALSE, 0);
4170 gtk_box_pack_start (GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), zoom_combo, FALSE, FALSE, 0);
4172 if ( img_gen == VW_GEN_DIRECTORY_OF_IMAGES )
4174 GtkWidget *tiles_width_label, *tiles_height_label;
4176 tiles_width_label = gtk_label_new ( _("East-west image tiles:") );
4177 tiles_width_spin = gtk_spin_button_new ( GTK_ADJUSTMENT(gtk_adjustment_new ( 5, 1, 10, 1, 100, 0 )), 1, 0 );
4178 tiles_height_label = gtk_label_new ( _("North-south image tiles:") );
4179 tiles_height_spin = gtk_spin_button_new ( GTK_ADJUSTMENT(gtk_adjustment_new ( 5, 1, 10, 1, 100, 0 )), 1, 0 );
4180 gtk_box_pack_start (GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), tiles_width_label, FALSE, FALSE, 0);
4181 gtk_box_pack_start (GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), tiles_width_spin, FALSE, FALSE, 0);
4182 gtk_box_pack_start (GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), tiles_height_label, FALSE, FALSE, 0);
4183 gtk_box_pack_start (GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), tiles_height_spin, FALSE, FALSE, 0);
4185 current_window_pass_along [4] = tiles_width_spin;
4186 current_window_pass_along [5] = tiles_height_spin;
4187 g_signal_connect ( G_OBJECT(tiles_width_spin), "value-changed", G_CALLBACK(draw_to_image_file_total_area_cb), current_window_pass_along );
4188 g_signal_connect ( G_OBJECT(tiles_height_spin), "value-changed", G_CALLBACK(draw_to_image_file_total_area_cb), current_window_pass_along );
4190 gtk_box_pack_start (GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), total_size_label, FALSE, FALSE, 0);
4191 g_signal_connect ( G_OBJECT(width_spin), "value-changed", G_CALLBACK(draw_to_image_file_total_area_cb), current_window_pass_along );
4192 g_signal_connect ( G_OBJECT(height_spin), "value-changed", G_CALLBACK(draw_to_image_file_total_area_cb), current_window_pass_along );
4193 g_signal_connect ( G_OBJECT(zoom_combo), "changed", G_CALLBACK(draw_to_image_file_total_area_cb), current_window_pass_along );
4195 draw_to_image_file_total_area_cb ( NULL, current_window_pass_along ); /* set correct size info now */
4197 gtk_dialog_set_default_response ( GTK_DIALOG(dialog), GTK_RESPONSE_ACCEPT );
4199 gtk_widget_show_all ( gtk_dialog_get_content_area(GTK_DIALOG(dialog)) );
4201 if ( gtk_dialog_run ( GTK_DIALOG(dialog) ) == GTK_RESPONSE_ACCEPT )
4203 gtk_widget_hide ( GTK_WIDGET(dialog) );
4205 gchar *fn = draw_image_filename ( vw, img_gen );
4209 gint active_z = gtk_combo_box_get_active ( GTK_COMBO_BOX(zoom_combo) );
4210 gdouble zoom = pow (2, active_z-2 );
4212 if ( img_gen == VW_GEN_SINGLE_IMAGE )
4213 save_image_file ( vw, fn,
4214 vw->draw_image_width = gtk_spin_button_get_value_as_int ( GTK_SPIN_BUTTON(width_spin) ),
4215 vw->draw_image_height = gtk_spin_button_get_value_as_int ( GTK_SPIN_BUTTON(height_spin) ),
4217 vw->draw_image_save_as_png = gtk_toggle_button_get_active ( GTK_TOGGLE_BUTTON(png_radio) ),
4219 else if ( img_gen == VW_GEN_KMZ_FILE ) {
4220 // Remove some viewport overlays as these aren't useful in KMZ file.
4221 gboolean restore_xhair = vik_viewport_get_draw_centermark ( vw->viking_vvp );
4222 if ( restore_xhair )
4223 vik_viewport_set_draw_centermark ( vw->viking_vvp, FALSE );
4224 gboolean restore_scale = vik_viewport_get_draw_scale ( vw->viking_vvp );
4225 if ( restore_scale )
4226 vik_viewport_set_draw_scale ( vw->viking_vvp, FALSE );
4228 save_image_file ( vw,
4230 gtk_spin_button_get_value_as_int ( GTK_SPIN_BUTTON(width_spin) ),
4231 gtk_spin_button_get_value_as_int ( GTK_SPIN_BUTTON(height_spin) ),
4236 if ( restore_xhair )
4237 vik_viewport_set_draw_centermark ( vw->viking_vvp, TRUE );
4238 if ( restore_scale )
4239 vik_viewport_set_draw_scale ( vw->viking_vvp, TRUE );
4240 if ( restore_xhair || restore_scale )
4244 // NB is in UTM mode ATM
4245 save_image_dir ( vw, fn,
4246 vw->draw_image_width = gtk_spin_button_get_value_as_int ( GTK_SPIN_BUTTON(width_spin) ),
4247 vw->draw_image_height = gtk_spin_button_get_value_as_int ( GTK_SPIN_BUTTON(height_spin) ),
4249 vw->draw_image_save_as_png = gtk_toggle_button_get_active ( GTK_TOGGLE_BUTTON(png_radio) ),
4250 gtk_spin_button_get_value ( GTK_SPIN_BUTTON(tiles_width_spin) ),
4251 gtk_spin_button_get_value ( GTK_SPIN_BUTTON(tiles_height_spin) ) );
4256 gtk_widget_destroy ( GTK_WIDGET(dialog) );
4259 static void draw_to_kmz_file_cb ( GtkAction *a, VikWindow *vw )
4261 if ( vik_viewport_get_coord_mode(vw->viking_vvp) == VIK_COORD_UTM ) {
4262 a_dialog_error_msg ( GTK_WINDOW(vw), _("This feature is not available in UTM mode") );
4265 // NB ATM This only generates a KMZ file with the current viewport image - intended mostly for map images [but will include any lines/icons from track & waypoints that are drawn]
4266 // (it does *not* include a full KML dump of every track, waypoint etc...)
4267 draw_to_image_file ( vw, VW_GEN_KMZ_FILE );
4270 static void draw_to_image_file_cb ( GtkAction *a, VikWindow *vw )
4272 draw_to_image_file ( vw, VW_GEN_SINGLE_IMAGE );
4275 static void draw_to_image_dir_cb ( GtkAction *a, VikWindow *vw )
4277 draw_to_image_file ( vw, VW_GEN_DIRECTORY_OF_IMAGES );
4283 static void import_kmz_file_cb ( GtkAction *a, VikWindow *vw )
4285 GtkWidget *dialog = gtk_file_chooser_dialog_new (_("Open File"),
4287 GTK_FILE_CHOOSER_ACTION_OPEN,
4288 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
4289 GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT,
4292 GtkFileFilter *filter;
4293 filter = gtk_file_filter_new ();
4294 gtk_file_filter_set_name ( filter, _("KMZ") );
4295 gtk_file_filter_add_mime_type ( filter, "vnd.google-earth.kmz");
4296 gtk_file_filter_add_pattern ( filter, "*.kmz" );
4297 gtk_file_chooser_add_filter ( GTK_FILE_CHOOSER(dialog), filter );
4298 gtk_file_chooser_set_filter ( GTK_FILE_CHOOSER(dialog), filter );
4300 filter = gtk_file_filter_new ();
4301 gtk_file_filter_set_name( filter, _("All") );
4302 gtk_file_filter_add_pattern ( filter, "*" );
4303 gtk_file_chooser_add_filter ( GTK_FILE_CHOOSER(dialog), filter );
4304 // Default to any file - same as before open filters were added
4305 gtk_file_chooser_set_filter ( GTK_FILE_CHOOSER(dialog), filter );
4307 if ( gtk_dialog_run ( GTK_DIALOG(dialog) ) == GTK_RESPONSE_ACCEPT ) {
4308 gchar *fn = gtk_file_chooser_get_filename ( GTK_FILE_CHOOSER(dialog) );
4309 // TODO convert ans value into readable explaination of failure...
4310 int ans = kmz_open_file ( fn, vw->viking_vvp, vw->viking_vlp );
4312 a_dialog_error_msg_extra ( GTK_WINDOW(vw), _("Unable to import %s."), fn );
4316 gtk_widget_destroy ( dialog );
4319 static void print_cb ( GtkAction *a, VikWindow *vw )
4321 a_print(vw, vw->viking_vvp);
4324 /* really a misnomer: changes coord mode (actual coordinates) AND/OR draw mode (viewport only) */
4325 static void window_change_coord_mode_cb ( GtkAction *old_a, GtkAction *a, VikWindow *vw )
4327 const gchar *name = gtk_action_get_name(a);
4328 GtkToggleToolButton *tbutton = (GtkToggleToolButton *)toolbar_get_widget_by_name ( vw->viking_vtb, name );
4330 gtk_toggle_tool_button_set_active ( tbutton, TRUE );
4332 VikViewportDrawMode drawmode;
4333 if (!g_strcmp0(name, "ModeUTM")) {
4334 drawmode = VIK_VIEWPORT_DRAWMODE_UTM;
4336 else if (!g_strcmp0(name, "ModeLatLon")) {
4337 drawmode = VIK_VIEWPORT_DRAWMODE_LATLON;
4339 else if (!g_strcmp0(name, "ModeExpedia")) {
4340 drawmode = VIK_VIEWPORT_DRAWMODE_EXPEDIA;
4342 else if (!g_strcmp0(name, "ModeMercator")) {
4343 drawmode = VIK_VIEWPORT_DRAWMODE_MERCATOR;
4346 g_critical("Houston, we've had a problem.");
4350 if ( !vw->only_updating_coord_mode_ui )
4352 VikViewportDrawMode olddrawmode = vik_viewport_get_drawmode ( vw->viking_vvp );
4353 if ( olddrawmode != drawmode )
4355 /* this takes care of coord mode too */
4356 vik_viewport_set_drawmode ( vw->viking_vvp, drawmode );
4357 if ( drawmode == VIK_VIEWPORT_DRAWMODE_UTM ) {
4358 vik_layers_panel_change_coord_mode ( vw->viking_vlp, VIK_COORD_UTM );
4359 } else if ( olddrawmode == VIK_VIEWPORT_DRAWMODE_UTM ) {
4360 vik_layers_panel_change_coord_mode ( vw->viking_vlp, VIK_COORD_LATLON );
4367 static void toggle_draw_scale ( GtkAction *a, VikWindow *vw )
4369 gboolean state = !vik_viewport_get_draw_scale ( vw->viking_vvp );
4370 GtkWidget *check_box = gtk_ui_manager_get_widget ( vw->uim, "/ui/MainMenu/View/SetShow/ShowScale" );
4373 gtk_check_menu_item_set_active ( GTK_CHECK_MENU_ITEM(check_box), state );
4374 vik_viewport_set_draw_scale ( vw->viking_vvp, state );
4378 static void toggle_draw_centermark ( GtkAction *a, VikWindow *vw )
4380 gboolean state = !vik_viewport_get_draw_centermark ( vw->viking_vvp );
4381 GtkWidget *check_box = gtk_ui_manager_get_widget ( vw->uim, "/ui/MainMenu/View/SetShow/ShowCenterMark" );
4384 gtk_check_menu_item_set_active ( GTK_CHECK_MENU_ITEM(check_box), state );
4385 vik_viewport_set_draw_centermark ( vw->viking_vvp, state );
4389 static void toggle_draw_highlight ( GtkAction *a, VikWindow *vw )
4391 gboolean state = !vik_viewport_get_draw_highlight ( vw->viking_vvp );
4392 GtkWidget *check_box = gtk_ui_manager_get_widget ( vw->uim, "/ui/MainMenu/View/SetShow/ShowHighlight" );
4395 gtk_check_menu_item_set_active ( GTK_CHECK_MENU_ITEM(check_box), state );
4396 vik_viewport_set_draw_highlight ( vw->viking_vvp, state );
4400 static void set_bg_color ( GtkAction *a, VikWindow *vw )
4402 GtkWidget *colorsd = gtk_color_selection_dialog_new ( _("Choose a background color") );
4403 GdkColor *color = vik_viewport_get_background_gdkcolor ( vw->viking_vvp );
4404 gtk_color_selection_set_previous_color ( GTK_COLOR_SELECTION(gtk_color_selection_dialog_get_color_selection(GTK_COLOR_SELECTION_DIALOG(colorsd))), color );
4405 gtk_color_selection_set_current_color ( GTK_COLOR_SELECTION(gtk_color_selection_dialog_get_color_selection(GTK_COLOR_SELECTION_DIALOG(colorsd))), color );
4406 if ( gtk_dialog_run ( GTK_DIALOG(colorsd) ) == GTK_RESPONSE_OK )
4408 gtk_color_selection_get_current_color ( GTK_COLOR_SELECTION(gtk_color_selection_dialog_get_color_selection(GTK_COLOR_SELECTION_DIALOG(colorsd))), color );
4409 vik_viewport_set_background_gdkcolor ( vw->viking_vvp, color );
4413 gtk_widget_destroy ( colorsd );
4416 static void set_highlight_color ( GtkAction *a, VikWindow *vw )
4418 GtkWidget *colorsd = gtk_color_selection_dialog_new ( _("Choose a track highlight color") );
4419 GdkColor *color = vik_viewport_get_highlight_gdkcolor ( vw->viking_vvp );
4420 gtk_color_selection_set_previous_color ( GTK_COLOR_SELECTION(gtk_color_selection_dialog_get_color_selection(GTK_COLOR_SELECTION_DIALOG(colorsd))), color );
4421 gtk_color_selection_set_current_color ( GTK_COLOR_SELECTION(gtk_color_selection_dialog_get_color_selection(GTK_COLOR_SELECTION_DIALOG(colorsd))), color );
4422 if ( gtk_dialog_run ( GTK_DIALOG(colorsd) ) == GTK_RESPONSE_OK )
4424 gtk_color_selection_get_current_color ( GTK_COLOR_SELECTION(gtk_color_selection_dialog_get_color_selection(GTK_COLOR_SELECTION_DIALOG(colorsd))), color );
4425 vik_viewport_set_highlight_gdkcolor ( vw->viking_vvp, color );
4429 gtk_widget_destroy ( colorsd );
4433 /***********************************************************************************************
4435 ***********************************************************************************************/
4437 static GtkActionEntry entries[] = {
4438 { "File", NULL, N_("_File"), 0, 0, 0 },
4439 { "Edit", NULL, N_("_Edit"), 0, 0, 0 },
4440 { "View", NULL, N_("_View"), 0, 0, 0 },
4441 { "SetShow", NULL, N_("_Show"), 0, 0, 0 },
4442 { "SetZoom", NULL, N_("_Zoom"), 0, 0, 0 },
4443 { "SetPan", NULL, N_("_Pan"), 0, 0, 0 },
4444 { "Layers", NULL, N_("_Layers"), 0, 0, 0 },
4445 { "Tools", NULL, N_("_Tools"), 0, 0, 0 },
4446 { "Exttools", NULL, N_("_Webtools"), 0, 0, 0 },
4447 { "Help", NULL, N_("_Help"), 0, 0, 0 },
4449 { "New", GTK_STOCK_NEW, N_("_New"), "<control>N", N_("New file"), (GCallback)newwindow_cb },
4450 { "Open", GTK_STOCK_OPEN, N_("_Open..."), "<control>O", N_("Open a file"), (GCallback)load_file },
4451 { "OpenRecentFile", NULL, N_("Open _Recent File"), NULL, NULL, (GCallback)NULL },
4452 { "Append", GTK_STOCK_ADD, N_("Append _File..."), NULL, N_("Append data from a different file"), (GCallback)load_file },
4453 { "Export", GTK_STOCK_CONVERT, N_("_Export All"), NULL, N_("Export All TrackWaypoint Layers"), (GCallback)NULL },
4454 { "ExportGPX", NULL, N_("_GPX..."), NULL, N_("Export as GPX"), (GCallback)export_to_gpx },
4455 { "Acquire", GTK_STOCK_GO_DOWN, N_("A_cquire"), NULL, NULL, (GCallback)NULL },
4456 { "AcquireRouting", NULL, N_("_Directions..."), NULL, N_("Get driving directions"), (GCallback)acquire_from_routing },
4457 #ifdef VIK_CONFIG_OPENSTREETMAP
4458 { "AcquireOSM", NULL, N_("_OSM Traces..."), NULL, N_("Get traces from OpenStreetMap"), (GCallback)acquire_from_osm },
4459 { "AcquireMyOSM", NULL, N_("_My OSM Traces..."), NULL, N_("Get Your Own Traces from OpenStreetMap"), (GCallback)acquire_from_my_osm },
4461 #ifdef VIK_CONFIG_GEOCACHES
4462 { "AcquireGC", NULL, N_("Geo_caches..."), NULL, N_("Get Geocaches from geocaching.com"), (GCallback)acquire_from_gc },
4464 #ifdef VIK_CONFIG_GEOTAG
4465 { "AcquireGeotag", NULL, N_("From Geotagged _Images..."), NULL, N_("Create waypoints from geotagged images"), (GCallback)acquire_from_geotag },
4467 { "AcquireURL", NULL, N_("From _URL..."), NULL, N_("Get a file from a URL"), (GCallback)acquire_from_url },
4468 #ifdef VIK_CONFIG_GEONAMES
4469 { "AcquireWikipedia", NULL, N_("From _Wikipedia Waypoints"), NULL, N_("Create waypoints from Wikipedia items in the current view"), (GCallback)acquire_from_wikipedia },
4471 { "Save", GTK_STOCK_SAVE, N_("_Save"), "<control>S", N_("Save the file"), (GCallback)save_file },
4472 { "SaveAs", GTK_STOCK_SAVE_AS, N_("Save _As..."), NULL, N_("Save the file under different name"), (GCallback)save_file_as },
4473 { "FileProperties", NULL, N_("Properties..."), NULL, N_("File Properties"), (GCallback)file_properties_cb },
4475 { "ImportKMZ", GTK_STOCK_CONVERT, N_("Import KMZ _Map File..."), NULL, N_("Import a KMZ file"), (GCallback)import_kmz_file_cb },
4476 { "GenKMZ", GTK_STOCK_DND, N_("Generate _KMZ Map File..."), NULL, N_("Generate a KMZ file with an overlay of the current view"), (GCallback)draw_to_kmz_file_cb },
4478 { "GenImg", GTK_STOCK_FILE, N_("_Generate Image File..."), NULL, N_("Save a snapshot of the workspace into a file"), (GCallback)draw_to_image_file_cb },
4479 { "GenImgDir", GTK_STOCK_DND_MULTIPLE, N_("Generate _Directory of Images..."), NULL, N_("Generate _Directory of Images"), (GCallback)draw_to_image_dir_cb },
4480 { "Print", GTK_STOCK_PRINT, N_("_Print..."), NULL, N_("Print maps"), (GCallback)print_cb },
4481 { "Exit", GTK_STOCK_QUIT, N_("E_xit"), "<control>W", N_("Exit the program"), (GCallback)window_close },
4482 { "SaveExit", GTK_STOCK_QUIT, N_("Save and Exit"), NULL, N_("Save and Exit the program"), (GCallback)save_file_and_exit },
4484 { "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 },
4485 { "GoForward", GTK_STOCK_GO_FORWARD, N_("Go to the _Next Location"), NULL, N_("Go to the next location"), (GCallback)draw_goto_back_and_forth },
4486 { "GotoDefaultLocation", GTK_STOCK_HOME, N_("Go to the _Default Location"), NULL, N_("Go to the default location"), (GCallback)goto_default_location },
4487 { "GotoSearch", GTK_STOCK_JUMP_TO, N_("Go to _Location..."), NULL, N_("Go to address/place using text search"), (GCallback)goto_address },
4488 { "GotoLL", GTK_STOCK_JUMP_TO, N_("_Go to Lat/Lon..."), NULL, N_("Go to arbitrary lat/lon coordinate"), (GCallback)draw_goto_cb },
4489 { "GotoUTM", GTK_STOCK_JUMP_TO, N_("Go to UTM..."), NULL, N_("Go to arbitrary UTM coordinate"), (GCallback)draw_goto_cb },
4490 { "Refresh", GTK_STOCK_REFRESH, N_("_Refresh"), "F5", N_("Refresh any maps displayed"), (GCallback)draw_refresh_cb },
4491 { "SetHLColor",GTK_STOCK_SELECT_COLOR, N_("Set _Highlight Color..."), NULL, N_("Set Highlight Color"), (GCallback)set_highlight_color },
4492 { "SetBGColor",GTK_STOCK_SELECT_COLOR, N_("Set Bac_kground Color..."), NULL, N_("Set Background Color"), (GCallback)set_bg_color },
4493 { "ZoomIn", GTK_STOCK_ZOOM_IN, N_("Zoom _In"), "<control>plus", N_("Zoom In"), (GCallback)draw_zoom_cb },
4494 { "ZoomOut", GTK_STOCK_ZOOM_OUT, N_("Zoom _Out"), "<control>minus", N_("Zoom Out"), (GCallback)draw_zoom_cb },
4495 { "ZoomTo", GTK_STOCK_ZOOM_FIT, N_("Zoom _To..."), "<control>Z", N_("Zoom To"), (GCallback)zoom_to_cb },
4496 { "PanNorth", NULL, N_("Pan _North"), "<control>Up", NULL, (GCallback)draw_pan_cb },
4497 { "PanEast", NULL, N_("Pan _East"), "<control>Right", NULL, (GCallback)draw_pan_cb },
4498 { "PanSouth", NULL, N_("Pan _South"), "<control>Down", NULL, (GCallback)draw_pan_cb },
4499 { "PanWest", NULL, N_("Pan _West"), "<control>Left", NULL, (GCallback)draw_pan_cb },
4500 { "BGJobs", GTK_STOCK_EXECUTE, N_("Background _Jobs"), NULL, N_("Background Jobs"), (GCallback)a_background_show_window },
4502 { "Cut", GTK_STOCK_CUT, N_("Cu_t"), NULL, N_("Cut selected layer"), (GCallback)menu_cut_layer_cb },
4503 { "Copy", GTK_STOCK_COPY, N_("_Copy"), NULL, N_("Copy selected layer"), (GCallback)menu_copy_layer_cb },
4504 { "Paste", GTK_STOCK_PASTE, N_("_Paste"), NULL, N_("Paste layer into selected container layer or otherwise above selected layer"), (GCallback)menu_paste_layer_cb },
4505 { "Delete", GTK_STOCK_DELETE, N_("_Delete"), NULL, N_("Remove selected layer"), (GCallback)menu_delete_layer_cb },
4506 { "DeleteAll", NULL, N_("Delete All"), NULL, NULL, (GCallback)clear_cb },
4507 { "CopyCentre",NULL, N_("Copy Centre _Location"), "<control>h", NULL, (GCallback)menu_copy_centre_cb },
4508 { "MapCacheFlush",NULL, N_("_Flush Map Cache"), NULL, NULL, (GCallback)mapcache_flush_cb },
4509 { "SetDefaultLocation", GTK_STOCK_GO_FORWARD, N_("_Set the Default Location"), NULL, N_("Set the Default Location to the current position"),(GCallback)default_location_cb },
4510 { "Preferences",GTK_STOCK_PREFERENCES, N_("_Preferences"), NULL, N_("Program Preferences"), (GCallback)preferences_cb },
4511 { "LayerDefaults",GTK_STOCK_PROPERTIES, N_("_Layer Defaults"), NULL, NULL, NULL },
4512 { "Properties",GTK_STOCK_PROPERTIES, N_("_Properties"), NULL, N_("Layer Properties"), (GCallback)menu_properties_cb },
4514 { "HelpEntry", GTK_STOCK_HELP, N_("_Help"), "F1", N_("Help"), (GCallback)help_help_cb },
4515 { "About", GTK_STOCK_ABOUT, N_("_About"), NULL, N_("About"), (GCallback)help_about_cb },
4518 static GtkActionEntry debug_entries[] = {
4519 { "MapCacheInfo", NULL, "_Map Cache Info", NULL, NULL, (GCallback)help_cache_info_cb },
4520 { "BackForwardInfo", NULL, "_Back/Forward Info", NULL, NULL, (GCallback)back_forward_info_cb },
4523 static GtkActionEntry entries_gpsbabel[] = {
4524 { "ExportKML", NULL, N_("_KML..."), NULL, N_("Export as KML"), (GCallback)export_to_kml },
4525 { "AcquireGPS", NULL, N_("From _GPS..."), NULL, N_("Transfer data from a GPS device"), (GCallback)acquire_from_gps },
4526 { "AcquireGPSBabel", NULL, N_("Import File With GPS_Babel..."), NULL, N_("Import file via GPSBabel converter"), (GCallback)acquire_from_file },
4529 static GtkActionEntry entries_geojson[] = {
4530 { "AcquireGeoJSON", NULL, N_("Import Geo_JSON File..."), NULL, N_("Import GeoJSON file"), (GCallback)acquire_from_geojson },
4534 static GtkRadioActionEntry mode_entries[] = {
4535 { "ModeUTM", NULL, N_("_UTM Mode"), "<control>u", NULL, VIK_VIEWPORT_DRAWMODE_UTM },
4536 { "ModeExpedia", NULL, N_("_Expedia Mode"), "<control>e", NULL, VIK_VIEWPORT_DRAWMODE_EXPEDIA },
4537 { "ModeMercator", NULL, N_("_Mercator Mode"), "<control>m", NULL, VIK_VIEWPORT_DRAWMODE_MERCATOR },
4538 { "ModeLatLon", NULL, N_("Lat_/Lon Mode"), "<control>l", NULL, VIK_VIEWPORT_DRAWMODE_LATLON },
4541 static GtkToggleActionEntry toggle_entries[] = {
4542 { "ShowScale", NULL, N_("Show _Scale"), "<shift>F5", N_("Show Scale"), (GCallback)toggle_draw_scale, TRUE },
4543 { "ShowCenterMark", NULL, N_("Show _Center Mark"), "F6", N_("Show Center Mark"), (GCallback)toggle_draw_centermark, TRUE },
4544 { "ShowHighlight", GTK_STOCK_UNDERLINE, N_("Show _Highlight"), "F7", N_("Show Highlight"), (GCallback)toggle_draw_highlight, TRUE },
4545 { "FullScreen", GTK_STOCK_FULLSCREEN, N_("_Full Screen"), "F11", N_("Activate full screen mode"), (GCallback)full_screen_cb, FALSE },
4546 { "ViewSidePanel", GTK_STOCK_INDEX, N_("Show Side _Panel"), "F9", N_("Show Side Panel"), (GCallback)view_side_panel_cb, TRUE },
4547 { "ViewStatusBar", NULL, N_("Show Status_bar"), "F12", N_("Show Statusbar"), (GCallback)view_statusbar_cb, TRUE },
4548 { "ViewToolbar", NULL, N_("Show _Toolbar"), "F3", N_("Show Toolbar"), (GCallback)view_toolbar_cb, TRUE },
4549 { "ViewMainMenu", NULL, N_("Show _Menu"), "F4", N_("Show Menu"), (GCallback)view_main_menu_cb, TRUE },
4552 // This must match the toggle entries order above
4553 static gpointer toggle_entries_toolbar_cb[] = {
4554 (GCallback)tb_set_draw_scale,
4555 (GCallback)tb_set_draw_centermark,
4556 (GCallback)tb_set_draw_highlight,
4557 (GCallback)tb_full_screen_cb,
4558 (GCallback)tb_view_side_panel_cb,
4559 (GCallback)tb_view_statusbar_cb,
4560 (GCallback)tb_view_toolbar_cb,
4561 (GCallback)tb_view_main_menu_cb,
4564 #include "menu.xml.h"
4565 static void window_create_ui( VikWindow *window )
4568 GtkActionGroup *action_group;
4569 GtkAccelGroup *accel_group;
4572 GtkIconFactory *icon_factory;
4573 GtkIconSet *icon_set;
4574 GtkRadioActionEntry *tools = NULL, *radio;
4577 uim = gtk_ui_manager_new ();
4580 toolbox_add_tool(window->vt, &ruler_tool, TOOL_LAYER_TYPE_NONE);
4581 toolbox_add_tool(window->vt, &zoom_tool, TOOL_LAYER_TYPE_NONE);
4582 toolbox_add_tool(window->vt, &pan_tool, TOOL_LAYER_TYPE_NONE);
4583 toolbox_add_tool(window->vt, &select_tool, TOOL_LAYER_TYPE_NONE);
4585 toolbar_action_tool_entry_register ( window->viking_vtb, &pan_tool.radioActionEntry );
4586 toolbar_action_tool_entry_register ( window->viking_vtb, &zoom_tool.radioActionEntry );
4587 toolbar_action_tool_entry_register ( window->viking_vtb, &ruler_tool.radioActionEntry );
4588 toolbar_action_tool_entry_register ( window->viking_vtb, &select_tool.radioActionEntry );
4591 if (!(mid = gtk_ui_manager_add_ui_from_string (uim, menu_xml, -1, &error))) {
4592 g_error_free (error);
4596 action_group = gtk_action_group_new ("MenuActions");
4597 gtk_action_group_set_translation_domain(action_group, PACKAGE_NAME);
4598 gtk_action_group_add_actions (action_group, entries, G_N_ELEMENTS (entries), window);
4599 gtk_action_group_add_toggle_actions (action_group, toggle_entries, G_N_ELEMENTS (toggle_entries), window);
4600 gtk_action_group_add_radio_actions (action_group, mode_entries, G_N_ELEMENTS (mode_entries), 4, (GCallback)window_change_coord_mode_cb, window);
4602 if ( gtk_ui_manager_add_ui_from_string ( uim,
4603 "<ui><menubar name='MainMenu'><menu action='Help'>"
4604 "<menuitem action='MapCacheInfo'/>"
4605 "<menuitem action='BackForwardInfo'/>"
4606 "</menu></menubar></ui>",
4608 gtk_action_group_add_actions (action_group, debug_entries, G_N_ELEMENTS (debug_entries), window);
4612 for ( i=0; i < G_N_ELEMENTS (entries); i++ ) {
4613 if ( entries[i].callback )
4614 toolbar_action_entry_register ( window->viking_vtb, &entries[i] );
4617 if ( G_N_ELEMENTS (toggle_entries) != G_N_ELEMENTS (toggle_entries_toolbar_cb) ) {
4618 g_print ( "Broken entries definitions\n" );
4621 for ( i=0; i < G_N_ELEMENTS (toggle_entries); i++ ) {
4622 if ( toggle_entries_toolbar_cb[i] )
4623 toolbar_action_toggle_entry_register ( window->viking_vtb, &toggle_entries[i], toggle_entries_toolbar_cb[i] );
4626 for ( i=0; i < G_N_ELEMENTS (mode_entries); i++ ) {
4627 toolbar_action_mode_entry_register ( window->viking_vtb, &mode_entries[i] );
4630 // Use this to see if GPSBabel is available:
4631 if ( a_babel_available () ) {
4632 // If going to add more entries then might be worth creating a menu_gpsbabel.xml.h file
4633 if ( gtk_ui_manager_add_ui_from_string ( uim,
4635 "<menubar name='MainMenu'>" \
4636 "<menu action='File'><menu action='Export'><menuitem action='ExportKML'/></menu></menu>" \
4637 "<menu action='File'><menu action='Acquire'><menuitem action='AcquireGPS'/></menu></menu>" \
4638 "<menu action='File'><menu action='Acquire'><menuitem action='AcquireGPSBabel'/></menu></menu>" \
4642 gtk_action_group_add_actions ( action_group, entries_gpsbabel, G_N_ELEMENTS (entries_gpsbabel), window );
4645 // GeoJSON import capability
4646 if ( g_find_program_in_path ( a_geojson_program_import() ) ) {
4647 if ( gtk_ui_manager_add_ui_from_string ( uim,
4648 "<ui><menubar name='MainMenu'><menu action='File'><menu action='Acquire'><menuitem action='AcquireGeoJSON'/></menu></menu></menubar></ui>",
4650 gtk_action_group_add_actions ( action_group, entries_geojson, G_N_ELEMENTS (entries_geojson), window );
4653 icon_factory = gtk_icon_factory_new ();
4654 gtk_icon_factory_add_default (icon_factory);
4656 register_vik_icons(icon_factory);
4658 // Copy the tool RadioActionEntries out of the main Window structure into an extending array 'tools'
4659 // so that it can be applied to the UI in one action group add function call below
4661 for (i=0; i<window->vt->n_tools; i++) {
4662 tools = g_renew(GtkRadioActionEntry, tools, ntools+1);
4663 radio = &tools[ntools];
4665 *radio = window->vt->tools[i].ti.radioActionEntry;
4666 radio->value = ntools;
4669 for (i=0; i<VIK_LAYER_NUM_TYPES; i++) {
4670 GtkActionEntry action;
4671 gtk_ui_manager_add_ui(uim, mid, "/ui/MainMenu/Layers/",
4672 vik_layer_get_interface(i)->name,
4673 vik_layer_get_interface(i)->name,
4674 GTK_UI_MANAGER_MENUITEM, FALSE);
4676 icon_set = gtk_icon_set_new_from_pixbuf (gdk_pixbuf_from_pixdata (vik_layer_get_interface(i)->icon, FALSE, NULL ));
4677 gtk_icon_factory_add (icon_factory, vik_layer_get_interface(i)->name, icon_set);
4678 gtk_icon_set_unref (icon_set);
4680 action.name = vik_layer_get_interface(i)->name;
4681 action.stock_id = vik_layer_get_interface(i)->name;
4682 action.label = g_strdup_printf( _("New _%s Layer"), vik_layer_get_interface(i)->name);
4683 action.accelerator = vik_layer_get_interface(i)->accelerator;
4684 action.tooltip = NULL;
4685 action.callback = (GCallback)menu_addlayer_cb;
4686 gtk_action_group_add_actions(action_group, &action, 1, window);
4688 g_free ( (gchar*)action.label );
4690 if ( vik_layer_get_interface(i)->tools_count ) {
4691 gtk_ui_manager_add_ui(uim, mid, "/ui/MainMenu/Tools/", vik_layer_get_interface(i)->name, NULL, GTK_UI_MANAGER_SEPARATOR, FALSE);
4694 // Further tool copying for to apply to the UI, also apply menu UI setup
4695 for ( j = 0; j < vik_layer_get_interface(i)->tools_count; j++ ) {
4696 tools = g_renew(GtkRadioActionEntry, tools, ntools+1);
4697 radio = &tools[ntools];
4700 gtk_ui_manager_add_ui(uim, mid, "/ui/MainMenu/Tools",
4701 vik_layer_get_interface(i)->tools[j].radioActionEntry.label,
4702 vik_layer_get_interface(i)->tools[j].radioActionEntry.name,
4703 GTK_UI_MANAGER_MENUITEM, FALSE);
4705 toolbox_add_tool(window->vt, &(vik_layer_get_interface(i)->tools[j]), i);
4706 toolbar_action_tool_entry_register ( window->viking_vtb, &(vik_layer_get_interface(i)->tools[j].radioActionEntry) );
4708 *radio = vik_layer_get_interface(i)->tools[j].radioActionEntry;
4709 // Overwrite with actual number to use
4710 radio->value = ntools;
4713 GtkActionEntry action_dl;
4714 gchar *layername = g_strdup_printf ( "Layer%s", vik_layer_get_interface(i)->fixed_layer_name );
4715 gtk_ui_manager_add_ui(uim, mid, "/ui/MainMenu/Edit/LayerDefaults",
4716 vik_layer_get_interface(i)->name,
4718 GTK_UI_MANAGER_MENUITEM, FALSE);
4721 // For default layers use action names of the form 'Layer<LayerName>'
4722 // This is to avoid clashing with just the layer name used above for the tool actions
4723 action_dl.name = g_strconcat("Layer", vik_layer_get_interface(i)->fixed_layer_name, NULL);
4724 action_dl.stock_id = NULL;
4725 action_dl.label = g_strconcat("_", vik_layer_get_interface(i)->name, "...", NULL); // Prepend marker for keyboard accelerator
4726 action_dl.accelerator = NULL;
4727 action_dl.tooltip = NULL;
4728 action_dl.callback = (GCallback)layer_defaults_cb;
4729 gtk_action_group_add_actions(action_group, &action_dl, 1, window);
4730 g_free ( (gchar*)action_dl.name );
4731 g_free ( (gchar*)action_dl.label );
4733 g_object_unref (icon_factory);
4735 gtk_action_group_add_radio_actions(action_group, tools, ntools, 0, (GCallback)menu_cb, window);
4738 gtk_ui_manager_insert_action_group (uim, action_group, 0);
4740 for (i=0; i<VIK_LAYER_NUM_TYPES; i++) {
4741 for ( j = 0; j < vik_layer_get_interface(i)->tools_count; j++ ) {
4742 GtkAction *action = gtk_action_group_get_action(action_group,
4743 vik_layer_get_interface(i)->tools[j].radioActionEntry.name);
4744 g_object_set(action, "sensitive", FALSE, NULL);
4748 // This is done last so we don't need to track the value of mid anymore
4749 vik_ext_tools_add_action_items ( window, window->uim, action_group, mid );
4751 window->action_group = action_group;
4753 accel_group = gtk_ui_manager_get_accel_group (uim);
4754 gtk_window_add_accel_group (GTK_WINDOW (window), accel_group);
4755 gtk_ui_manager_ensure_update (uim);
4757 setup_recent_files(window);
4761 // TODO - add method to add tool icons defined from outside this file
4762 // and remove the reverse dependency on icon definition from this file
4764 const GdkPixdata *data;
4767 { &mover_22_pixbuf, "vik-icon-pan" },
4768 { &zoom_18_pixbuf, "vik-icon-zoom" },
4769 { &ruler_18_pixbuf, "vik-icon-ruler" },
4770 { &select_18_pixbuf, "vik-icon-select" },
4771 { &vik_new_route_18_pixbuf, "vik-icon-Create Route" },
4772 { &route_finder_18_pixbuf, "vik-icon-Route Finder" },
4773 { &demdl_18_pixbuf, "vik-icon-DEM Download" },
4774 { &showpic_18_pixbuf, "vik-icon-Show Picture" },
4775 { &addtr_18_pixbuf, "vik-icon-Create Track" },
4776 { &edtr_18_pixbuf, "vik-icon-Edit Trackpoint" },
4777 { &addwp_18_pixbuf, "vik-icon-Create Waypoint" },
4778 { &edwp_18_pixbuf, "vik-icon-Edit Waypoint" },
4779 { &geozoom_18_pixbuf, "vik-icon-Georef Zoom Tool" },
4780 { &geomove_18_pixbuf, "vik-icon-Georef Move Map" },
4781 { &mapdl_18_pixbuf, "vik-icon-Maps Download" },
4784 static gint n_stock_icons = G_N_ELEMENTS (stock_icons);
4787 register_vik_icons (GtkIconFactory *icon_factory)
4789 GtkIconSet *icon_set;
4792 for (i = 0; i < n_stock_icons; i++) {
4793 icon_set = gtk_icon_set_new_from_pixbuf (gdk_pixbuf_from_pixdata (
4794 stock_icons[i].data, FALSE, NULL ));
4795 gtk_icon_factory_add (icon_factory, stock_icons[i].stock_id, icon_set);
4796 gtk_icon_set_unref (icon_set);
4800 gpointer vik_window_get_selected_trw_layer ( VikWindow *vw )
4802 return vw->selected_vtl;
4805 void vik_window_set_selected_trw_layer ( VikWindow *vw, gpointer vtl )
4807 vw->selected_vtl = vtl;
4808 vw->containing_vtl = vtl;
4810 vw->selected_track = NULL;
4811 vw->selected_tracks = NULL;
4812 vw->selected_waypoint = NULL;
4813 vw->selected_waypoints = NULL;
4814 // Set highlight thickness
4815 vik_viewport_set_highlight_thickness ( vw->viking_vvp, vik_trw_layer_get_property_tracks_line_thickness (vw->containing_vtl) );
4818 GHashTable *vik_window_get_selected_tracks ( VikWindow *vw )
4820 return vw->selected_tracks;
4823 void vik_window_set_selected_tracks ( VikWindow *vw, GHashTable *ght, gpointer vtl )
4825 vw->selected_tracks = ght;
4826 vw->containing_vtl = vtl;
4828 vw->selected_vtl = NULL;
4829 vw->selected_track = NULL;
4830 vw->selected_waypoint = NULL;
4831 vw->selected_waypoints = NULL;
4832 // Set highlight thickness
4833 vik_viewport_set_highlight_thickness ( vw->viking_vvp, vik_trw_layer_get_property_tracks_line_thickness (vw->containing_vtl) );
4836 gpointer vik_window_get_selected_track ( VikWindow *vw )
4838 return vw->selected_track;
4841 void vik_window_set_selected_track ( VikWindow *vw, gpointer *vt, gpointer vtl )
4843 vw->selected_track = vt;
4844 vw->containing_vtl = vtl;
4846 vw->selected_vtl = NULL;
4847 vw->selected_tracks = NULL;
4848 vw->selected_waypoint = NULL;
4849 vw->selected_waypoints = NULL;
4850 // Set highlight thickness
4851 vik_viewport_set_highlight_thickness ( vw->viking_vvp, vik_trw_layer_get_property_tracks_line_thickness (vw->containing_vtl) );
4854 GHashTable *vik_window_get_selected_waypoints ( VikWindow *vw )
4856 return vw->selected_waypoints;
4859 void vik_window_set_selected_waypoints ( VikWindow *vw, GHashTable *ght, gpointer vtl )
4861 vw->selected_waypoints = ght;
4862 vw->containing_vtl = vtl;
4864 vw->selected_vtl = NULL;
4865 vw->selected_track = NULL;
4866 vw->selected_tracks = NULL;
4867 vw->selected_waypoint = NULL;
4870 gpointer vik_window_get_selected_waypoint ( VikWindow *vw )
4872 return vw->selected_waypoint;
4875 void vik_window_set_selected_waypoint ( VikWindow *vw, gpointer *vwp, gpointer vtl )
4877 vw->selected_waypoint = vwp;
4878 vw->containing_vtl = vtl;
4880 vw->selected_vtl = NULL;
4881 vw->selected_track = NULL;
4882 vw->selected_tracks = NULL;
4883 vw->selected_waypoints = NULL;
4886 gboolean vik_window_clear_highlight ( VikWindow *vw )
4888 gboolean need_redraw = FALSE;
4889 if ( vw->selected_vtl != NULL ) {
4890 vw->selected_vtl = NULL;
4893 if ( vw->selected_track != NULL ) {
4894 vw->selected_track = NULL;
4897 if ( vw->selected_tracks != NULL ) {
4898 vw->selected_tracks = NULL;
4901 if ( vw->selected_waypoint != NULL ) {
4902 vw->selected_waypoint = NULL;
4905 if ( vw->selected_waypoints != NULL ) {
4906 vw->selected_waypoints = NULL;
4913 * May return NULL if the window no longer exists
4915 GThread *vik_window_get_thread ( VikWindow *vw )