2 * viking -- GPS Data and Topo Analyzer, Explorer, and Manager
4 * Copyright (C) 2003-2005, Evan Battaglia <gtoevan@gmx.net>
5 * Copyright (C) 2005-2006, Alex Foobarian <foobarian@gmail.com>
6 * Copyright (C) 2012-2014, Rob Norris <rw_norris@hotmail.com>
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
29 #include "background.h"
31 #include "datasources.h"
37 #include "preferences.h"
39 #include "viklayer_defaults.h"
40 #include "icons/icons.h"
41 #include "vikexttools.h"
42 #include "vikexttool_datasources.h"
43 #include "garminsymbols.h"
44 #include "vikmapslayer.h"
45 #include "geonamessearch.h"
60 #include <glib/gstdio.h>
61 #include <glib/gprintf.h>
62 #include <glib/gi18n.h>
64 #include <gdk/gdkkeysyms.h>
66 // This seems rather arbitary, quite large and pointless
67 // I mean, if you have a thousand windows open;
68 // why not be allowed to open a thousand more...
69 #define MAX_WINDOWS 1024
70 static guint window_count = 0;
71 static GSList *window_list = NULL;
73 #define VIKING_WINDOW_WIDTH 1000
74 #define VIKING_WINDOW_HEIGHT 800
75 #define DRAW_IMAGE_DEFAULT_WIDTH 1280
76 #define DRAW_IMAGE_DEFAULT_HEIGHT 1024
77 #define DRAW_IMAGE_DEFAULT_SAVE_AS_PNG TRUE
79 static void window_finalize ( GObject *gob );
80 static GObjectClass *parent_class;
82 static void window_set_filename ( VikWindow *vw, const gchar *filename );
83 static const gchar *window_get_filename ( VikWindow *vw );
85 static VikWindow *window_new ();
87 static void draw_update ( VikWindow *vw );
89 static void newwindow_cb ( GtkAction *a, VikWindow *vw );
92 static void open_window ( VikWindow *vw, GSList *files );
93 static void destroy_window ( GtkWidget *widget,
98 static gboolean delete_event( VikWindow *vw );
100 static gboolean key_press_event( VikWindow *vw, GdkEventKey *event, gpointer data );
102 static void center_changed_cb ( VikWindow *vw );
103 static void window_configure_event ( VikWindow *vw );
104 static void draw_sync ( VikWindow *vw );
105 static void draw_redraw ( VikWindow *vw );
106 static void draw_scroll ( VikWindow *vw, GdkEventScroll *event );
107 static void draw_click ( VikWindow *vw, GdkEventButton *event );
108 static void draw_release ( VikWindow *vw, GdkEventButton *event );
109 static void draw_mouse_motion ( VikWindow *vw, GdkEventMotion *event );
110 static void draw_zoom_cb ( GtkAction *a, VikWindow *vw );
111 static void draw_goto_cb ( GtkAction *a, VikWindow *vw );
112 static void draw_refresh_cb ( GtkAction *a, VikWindow *vw );
114 static void draw_status ( VikWindow *vw );
116 /* End Drawing Functions */
118 static void toggle_draw_scale ( GtkAction *a, VikWindow *vw );
119 static void toggle_draw_centermark ( GtkAction *a, VikWindow *vw );
120 static void toggle_draw_highlight ( GtkAction *a, VikWindow *vw );
122 static void menu_addlayer_cb ( GtkAction *a, VikWindow *vw );
123 static void menu_properties_cb ( GtkAction *a, VikWindow *vw );
124 static void menu_delete_layer_cb ( GtkAction *a, VikWindow *vw );
126 /* tool management */
132 #define TOOL_LAYER_TYPE_NONE -1
137 toolbox_tool_t *tools;
141 static void menu_cb ( GtkAction *old, GtkAction *a, VikWindow *vw );
142 static void window_change_coord_mode_cb ( GtkAction *old, GtkAction *a, VikWindow *vw );
143 static toolbox_tools_t* toolbox_create(VikWindow *vw);
144 static void toolbox_add_tool(toolbox_tools_t *vt, VikToolInterface *vti, gint layer_type );
145 static int toolbox_get_tool(toolbox_tools_t *vt, const gchar *tool_name);
146 static void toolbox_activate(toolbox_tools_t *vt, const gchar *tool_name);
147 static const GdkCursor *toolbox_get_cursor(toolbox_tools_t *vt, const gchar *tool_name);
148 static void toolbox_click (toolbox_tools_t *vt, GdkEventButton *event);
149 static void toolbox_move (toolbox_tools_t *vt, GdkEventMotion *event);
150 static void toolbox_release (toolbox_tools_t *vt, GdkEventButton *event);
154 static void window_create_ui( VikWindow *window );
155 static void register_vik_icons (GtkIconFactory *icon_factory);
158 static void load_file ( GtkAction *a, VikWindow *vw );
159 static gboolean save_file_as ( GtkAction *a, VikWindow *vw );
160 static gboolean save_file ( GtkAction *a, VikWindow *vw );
161 static gboolean save_file_and_exit ( GtkAction *a, VikWindow *vw );
162 static gboolean window_save ( VikWindow *vw );
167 VikViewport *viking_vvp;
168 VikLayersPanel *viking_vlp;
169 VikStatusbar *viking_vs;
170 VikToolbar *viking_vtb;
172 GtkWidget *main_vbox;
173 GtkWidget *menu_hbox;
175 GdkCursor *busy_cursor;
176 GdkCursor *viewport_cursor; // only a reference
178 /* tool management state */
181 guint16 tool_layer_id;
182 guint16 tool_tool_id;
184 GtkActionGroup *action_group;
187 // NB scale, centermark and highlight are in viewport.
188 gboolean show_full_screen;
189 gboolean show_side_panel;
190 gboolean show_statusbar;
191 gboolean show_toolbar;
192 gboolean show_main_menu;
196 gint delayed_pan_x, delayed_pan_y; // Temporary storage
197 gboolean single_click_pending;
199 guint draw_image_width, draw_image_height;
200 gboolean draw_image_save_as_png;
204 VikLoadType_t loaded_type;
206 GtkWidget *open_dia, *save_dia;
207 GtkWidget *save_img_dia, *save_img_dir_dia;
209 gboolean only_updating_coord_mode_ui; /* hack for a bug in GTK */
213 /* half-drawn update */
215 VikCoord trigger_center;
217 /* Store at this level for highlighted selection drawing since it applies to the viewport and the layers panel */
218 /* Only one of these items can be selected at the same time */
219 gpointer selected_vtl; /* notionally VikTrwLayer */
220 GHashTable *selected_tracks;
221 gpointer selected_track; /* notionally VikTrack */
222 GHashTable *selected_waypoints;
223 gpointer selected_waypoint; /* notionally VikWaypoint */
224 /* only use for individual track or waypoint */
225 /* For track(s) & waypoint(s) it is the layer they are in - this helps refering to the individual item easier */
226 gpointer containing_vtl; /* notionally VikTrwLayer */
240 VW_OPENWINDOW_SIGNAL,
244 static guint window_signals[VW_LAST_SIGNAL] = { 0 };
246 // TODO get rid of this as this is unnecessary duplication...
247 static gchar *tool_names[NUMBER_OF_TOOLS] = { N_("Pan"), N_("Zoom"), N_("Ruler"), N_("Select") };
249 G_DEFINE_TYPE (VikWindow, vik_window, GTK_TYPE_WINDOW)
251 VikViewport * vik_window_viewport(VikWindow *vw)
253 return(vw->viking_vvp);
256 VikLayersPanel * vik_window_layers_panel(VikWindow *vw)
258 return(vw->viking_vlp);
262 * Returns the statusbar for the window
264 VikStatusbar * vik_window_get_statusbar ( VikWindow *vw )
266 return vw->viking_vs;
270 * Returns the 'project' filename
272 const gchar *vik_window_get_filename (VikWindow *vw)
279 vik_statusbar_type_t vs_type;
280 gchar* message; // Always make a copy of this data
281 } statusbar_idle_data;
284 * For the actual statusbar update!
286 static gboolean statusbar_idle_update ( statusbar_idle_data *sid )
288 vik_statusbar_set_message ( sid->vs, sid->vs_type, sid->message );
289 g_free ( sid->message );
295 * vik_window_statusbar_update:
296 * @vw: The main window in which the statusbar will be updated.
297 * @message: The string to be displayed. This is copied.
298 * @vs_type: The part of the statusbar to be updated.
300 * This updates any part of the statusbar with the new string.
301 * It handles calling from the main thread or any background thread
302 * ATM this mostly used from background threads - as from the main thread
303 * one may use the vik_statusbar_set_message() directly.
305 void vik_window_statusbar_update ( VikWindow *vw, const gchar* message, vik_statusbar_type_t vs_type )
307 statusbar_idle_data *sid = g_malloc ( sizeof (statusbar_idle_data) );
308 sid->vs = vw->viking_vs;
309 sid->vs_type = vs_type;
310 sid->message = g_strdup ( message );
312 if ( g_thread_self() == vik_window_get_thread ( vw ) ) {
313 g_idle_add ( (GSourceFunc) statusbar_idle_update, sid );
316 // From a background thread
317 gdk_threads_add_idle ( (GSourceFunc) statusbar_idle_update, sid );
321 // Actual signal handlers
322 static void destroy_window ( GtkWidget *widget,
325 if ( ! --window_count )
329 #define VIK_SETTINGS_WIN_SIDEPANEL "window_sidepanel"
330 #define VIK_SETTINGS_WIN_STATUSBAR "window_statusbar"
331 #define VIK_SETTINGS_WIN_TOOLBAR "window_toolbar"
332 // Menubar setting to off is never auto saved in case it's accidentally turned off
333 // It's not so obvious so to recover the menu visibility.
334 // Thus this value is for setting manually via editting the settings file directly
335 #define VIK_SETTINGS_WIN_MENUBAR "window_menubar"
337 VikWindow *vik_window_new_window ()
339 if ( window_count < MAX_WINDOWS )
341 VikWindow *vw = window_new ();
343 g_signal_connect (G_OBJECT (vw), "destroy",
344 G_CALLBACK (destroy_window), NULL);
345 g_signal_connect (G_OBJECT (vw), "newwindow",
346 G_CALLBACK (vik_window_new_window), NULL);
347 g_signal_connect (G_OBJECT (vw), "openwindow",
348 G_CALLBACK (open_window), NULL);
350 gtk_widget_show_all ( GTK_WIDGET(vw) );
352 if ( a_vik_get_restore_window_state() ) {
353 // These settings are applied after the show all as these options hide widgets
355 if ( a_settings_get_boolean ( VIK_SETTINGS_WIN_SIDEPANEL, &sidepanel ) )
357 gtk_widget_hide ( GTK_WIDGET(vw->viking_vlp) );
358 GtkWidget *check_box = gtk_ui_manager_get_widget ( vw->uim, "/ui/MainMenu/View/SetShow/ViewSidePanel" );
359 gtk_check_menu_item_set_active ( GTK_CHECK_MENU_ITEM(check_box), FALSE );
363 if ( a_settings_get_boolean ( VIK_SETTINGS_WIN_STATUSBAR, &statusbar ) )
365 gtk_widget_hide ( GTK_WIDGET(vw->viking_vs) );
366 GtkWidget *check_box = gtk_ui_manager_get_widget ( vw->uim, "/ui/MainMenu/View/SetShow/ViewStatusBar" );
367 gtk_check_menu_item_set_active ( GTK_CHECK_MENU_ITEM(check_box), FALSE );
371 if ( a_settings_get_boolean ( VIK_SETTINGS_WIN_TOOLBAR, &toolbar ) )
373 gtk_widget_hide ( toolbar_get_widget (vw->viking_vtb) );
374 GtkWidget *check_box = gtk_ui_manager_get_widget ( vw->uim, "/ui/MainMenu/View/SetShow/ViewToolBar" );
375 gtk_check_menu_item_set_active ( GTK_CHECK_MENU_ITEM(check_box), FALSE );
379 if ( a_settings_get_boolean ( VIK_SETTINGS_WIN_MENUBAR, &menubar ) )
381 gtk_widget_hide ( gtk_ui_manager_get_widget ( vw->uim, "/ui/MainMenu" ) );
382 GtkWidget *check_box = gtk_ui_manager_get_widget ( vw->uim, "/ui/MainMenu/View/SetShow/ViewMainMenu" );
383 gtk_check_menu_item_set_active ( GTK_CHECK_MENU_ITEM(check_box), FALSE );
394 * determine_location_thread:
395 * @vw: The window that will get updated
396 * @threaddata: Data used by our background thread mechanism
398 * Use the features in vikgoto to determine where we are
399 * Then set up the viewport:
400 * 1. To goto the location
401 * 2. Set an appropriate level zoom for the location type
402 * 3. Some statusbar message feedback
404 static int determine_location_thread ( VikWindow *vw, gpointer threaddata )
408 gint ans = a_vik_goto_where_am_i ( vw->viking_vvp, &ll, &name );
410 int result = a_background_thread_progress ( threaddata, 1.0 );
412 vik_window_statusbar_update ( vw, _("Location lookup aborted"), VIK_STATUSBAR_INFO );
413 return -1; /* Abort thread */
421 // Position found with city precision - so zoom out more
424 else if ( ans == 3 ) {
425 // Position found via country name search - so zoom wayyyy out
429 vik_viewport_set_zoom ( vw->viking_vvp, zoom );
430 vik_viewport_set_center_latlon ( vw->viking_vvp, &ll, FALSE );
432 gchar *message = g_strdup_printf ( _("Location found: %s"), name );
433 vik_window_statusbar_update ( vw, message, VIK_STATUSBAR_INFO );
437 // Signal to redraw from the background
438 vik_layers_panel_emit_update ( vw->viking_vlp );
441 vik_window_statusbar_update ( vw, _("Unable to determine location"), VIK_STATUSBAR_INFO );
447 * Steps to be taken once initial loading has completed
449 void vik_window_new_window_finish ( VikWindow *vw )
451 // Don't add a map if we've loaded a Viking file already
455 if ( a_vik_get_startup_method ( ) == VIK_STARTUP_METHOD_SPECIFIED_FILE ) {
456 vik_window_open_file ( vw, a_vik_get_startup_file(), TRUE );
461 // Maybe add a default map layer
462 if ( a_vik_get_add_default_map_layer () ) {
463 VikMapsLayer *vml = VIK_MAPS_LAYER ( vik_layer_create(VIK_LAYER_MAPS, vw->viking_vvp, FALSE) );
464 vik_layer_rename ( VIK_LAYER(vml), _("Default Map") );
465 vik_aggregate_layer_add_layer ( vik_layers_panel_get_top_layer(vw->viking_vlp), VIK_LAYER(vml), TRUE );
470 // If not loaded any file, maybe try the location lookup
471 if ( vw->loaded_type == LOAD_TYPE_READ_FAILURE ) {
472 if ( a_vik_get_startup_method ( ) == VIK_STARTUP_METHOD_AUTO_LOCATION ) {
474 vik_statusbar_set_message ( vw->viking_vs, VIK_STATUSBAR_INFO, _("Trying to determine location...") );
476 a_background_thread ( GTK_WINDOW(vw),
477 _("Determining location"),
478 (vik_thr_func) determine_location_thread,
487 static void open_window ( VikWindow *vw, GSList *files )
489 gboolean change_fn = (g_slist_length(files) == 1); /* only change fn if one file */
490 GSList *cur_file = files;
492 // Only open a new window if a viking file
493 gchar *file_name = cur_file->data;
494 if (vw != NULL && vw->filename && check_file_magic_vik ( file_name ) ) {
495 VikWindow *newvw = vik_window_new_window ();
497 vik_window_open_file ( newvw, file_name, TRUE );
500 vik_window_open_file ( vw, file_name, change_fn );
503 cur_file = g_slist_next (cur_file);
505 g_slist_free (files);
509 void vik_window_selected_layer(VikWindow *vw, VikLayer *vl)
511 int i, j, tool_count;
512 VikLayerInterface *layer_interface;
514 if (!vw->action_group) return;
516 for (i=0; i<VIK_LAYER_NUM_TYPES; i++) {
518 layer_interface = vik_layer_get_interface(i);
519 tool_count = layer_interface->tools_count;
521 for (j = 0; j < tool_count; j++) {
522 action = gtk_action_group_get_action(vw->action_group,
523 layer_interface->tools[j].radioActionEntry.name);
524 g_object_set(action, "sensitive", i == vl->type, NULL);
525 toolbar_action_set_sensitive ( vw->viking_vtb, vik_layer_get_interface(i)->tools[j].radioActionEntry.name, i == vl->type );
530 static void window_finalize ( GObject *gob )
532 VikWindow *vw = VIK_WINDOW(gob);
533 g_return_if_fail ( vw != NULL );
535 a_background_remove_window ( vw );
537 window_list = g_slist_remove ( window_list, vw );
539 gdk_cursor_unref ( vw->busy_cursor );
541 for (tt = 0; tt < vw->vt->n_tools; tt++ )
542 if ( vw->vt->tools[tt].ti.destroy )
543 vw->vt->tools[tt].ti.destroy ( vw->vt->tools[tt].state );
544 g_free ( vw->vt->tools );
547 vik_toolbar_finalize ( vw->viking_vtb );
549 G_OBJECT_CLASS(parent_class)->finalize(gob);
553 static void vik_window_class_init ( VikWindowClass *klass )
556 GObjectClass *object_class;
558 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);
559 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);
561 object_class = G_OBJECT_CLASS (klass);
563 object_class->finalize = window_finalize;
565 parent_class = g_type_class_peek_parent (klass);
569 static void zoom_changed (GtkMenuShell *menushell,
572 VikWindow *vw = VIK_WINDOW (user_data);
574 GtkWidget *aw = gtk_menu_get_active ( GTK_MENU (menushell) );
575 gint active = GPOINTER_TO_INT(g_object_get_data ( G_OBJECT (aw), "position" ));
577 gdouble zoom_request = pow (2, active-2 );
579 // But has it really changed?
580 gdouble current_zoom = vik_viewport_get_zoom ( vw->viking_vvp );
581 if ( current_zoom != 0.0 && zoom_request != current_zoom ) {
582 vik_viewport_set_zoom ( vw->viking_vvp, zoom_request );
583 // Force drawing update
589 * @mpp: The initial zoom level
591 static GtkWidget *create_zoom_menu_all_levels ( gdouble mpp )
593 GtkWidget *menu = gtk_menu_new ();
594 char *itemLabels[] = { "0.25", "0.5", "1", "2", "4", "8", "16", "32", "64", "128", "256", "512", "1024", "2048", "4096", "8192", "16384", "32768" };
597 for (i = 0 ; i < G_N_ELEMENTS(itemLabels) ; i++)
599 GtkWidget *item = gtk_menu_item_new_with_label (itemLabels[i]);
600 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
601 gtk_widget_show (item);
602 g_object_set_data (G_OBJECT (item), "position", GINT_TO_POINTER(i));
605 gint active = 2 + round ( log (mpp) / log (2) );
606 // Ensure value derived from mpp is in bounds of the menu
607 if ( active >= G_N_ELEMENTS(itemLabels) )
608 active = G_N_ELEMENTS(itemLabels) - 1;
611 gtk_menu_set_active ( GTK_MENU(menu), active );
616 static GtkWidget *create_zoom_combo_all_levels ()
618 GtkWidget *combo = vik_combo_box_text_new();
619 vik_combo_box_text_append ( combo, "0.25");
620 vik_combo_box_text_append ( combo, "0.5");
621 vik_combo_box_text_append ( combo, "1");
622 vik_combo_box_text_append ( combo, "2");
623 vik_combo_box_text_append ( combo, "4");
624 vik_combo_box_text_append ( combo, "8");
625 vik_combo_box_text_append ( combo, "16");
626 vik_combo_box_text_append ( combo, "32");
627 vik_combo_box_text_append ( combo, "64");
628 vik_combo_box_text_append ( combo, "128");
629 vik_combo_box_text_append ( combo, "256");
630 vik_combo_box_text_append ( combo, "512");
631 vik_combo_box_text_append ( combo, "1024");
632 vik_combo_box_text_append ( combo, "2048");
633 vik_combo_box_text_append ( combo, "4096");
634 vik_combo_box_text_append ( combo, "8192");
635 vik_combo_box_text_append ( combo, "16384");
636 vik_combo_box_text_append ( combo, "32768");
638 gtk_widget_set_tooltip_text (combo, _("Select zoom level"));
642 static gint zoom_popup_handler (GtkWidget *widget)
646 g_return_val_if_fail (widget != NULL, FALSE);
647 g_return_val_if_fail (GTK_IS_MENU (widget), FALSE);
649 /* The "widget" is the menu that was supplied when
650 * g_signal_connect_swapped() was called.
652 menu = GTK_MENU (widget);
654 gtk_menu_popup (menu, NULL, NULL, NULL, NULL,
655 1, gtk_get_current_event_time());
663 static void drag_data_received_cb ( GtkWidget *widget,
664 GdkDragContext *context,
667 GtkSelectionData *selection_data,
672 gboolean success = FALSE;
674 if ( (selection_data != NULL) && (gtk_selection_data_get_length(selection_data) > 0) ) {
675 switch (target_type) {
677 gchar *str = (gchar*)gtk_selection_data_get_data(selection_data);
678 g_debug ("drag received string:%s \n", str);
680 // Convert string into GSList of individual entries for use with our open signal
681 gchar **entries = g_strsplit(str, "\r\n", 0);
682 GSList *filenames = NULL;
683 gint entry_runner = 0;
684 gchar *entry = entries[entry_runner];
686 if ( g_strcmp0 ( entry, "" ) ) {
687 // Drag+Drop gives URIs. And so in particular, %20 in place of spaces in filenames
688 // thus need to convert the text into a plain string
689 gchar *filename = g_filename_from_uri ( entry, NULL, NULL );
691 filenames = g_slist_append ( filenames, filename );
694 entry = entries[entry_runner];
698 g_signal_emit ( G_OBJECT(VIK_WINDOW_FROM_WIDGET(widget)), window_signals[VW_OPENWINDOW_SIGNAL], 0, filenames );
699 // NB: GSList & contents are freed by main.open_window
708 gtk_drag_finish ( context, success, FALSE, time );
711 static void toolbar_tool_cb ( GtkAction *old, GtkAction *current, gpointer gp )
713 VikWindow *vw = (VikWindow*)gp;
714 GtkAction *action = gtk_action_group_get_action ( vw->action_group, gtk_action_get_name(current) );
716 gtk_action_activate ( action );
719 static void toolbar_reload_cb ( GtkActionGroup *grp, gpointer gp )
721 VikWindow *vw = (VikWindow*)gp;
722 center_changed_cb ( vw );
725 #define VIK_SETTINGS_WIN_MAX "window_maximized"
726 #define VIK_SETTINGS_WIN_FULLSCREEN "window_fullscreen"
727 #define VIK_SETTINGS_WIN_WIDTH "window_width"
728 #define VIK_SETTINGS_WIN_HEIGHT "window_height"
729 #define VIK_SETTINGS_WIN_PANE_POSITION "window_horizontal_pane_position"
730 #define VIK_SETTINGS_WIN_SAVE_IMAGE_WIDTH "window_save_image_width"
731 #define VIK_SETTINGS_WIN_SAVE_IMAGE_HEIGHT "window_save_image_height"
732 #define VIK_SETTINGS_WIN_SAVE_IMAGE_PNG "window_save_image_as_png"
733 #define VIK_SETTINGS_WIN_COPY_CENTRE_FULL_FORMAT "window_copy_centre_full_format"
735 #define VIKING_ACCELERATOR_KEY_FILE "keys.rc"
737 static void vik_window_init ( VikWindow *vw )
739 vw->action_group = NULL;
741 vw->viking_vvp = vik_viewport_new();
742 vw->viking_vlp = vik_layers_panel_new();
743 vik_layers_panel_set_viewport ( vw->viking_vlp, vw->viking_vvp );
744 vw->viking_vs = vik_statusbar_new();
746 vw->vt = toolbox_create(vw);
747 vw->viking_vtb = vik_toolbar_new ();
748 window_create_ui(vw);
749 window_set_filename (vw, NULL);
751 vw->busy_cursor = gdk_cursor_new ( GDK_WATCH );
754 vw->loaded_type = LOAD_TYPE_READ_FAILURE; //AKA none
755 vw->modified = FALSE;
756 vw->only_updating_coord_mode_ui = FALSE;
758 vw->pan_move = FALSE;
759 vw->pan_x = vw->pan_y = -1;
760 vw->single_click_pending = FALSE;
762 gint draw_image_width;
763 if ( a_settings_get_integer ( VIK_SETTINGS_WIN_SAVE_IMAGE_WIDTH, &draw_image_width ) )
764 vw->draw_image_width = draw_image_width;
766 vw->draw_image_width = DRAW_IMAGE_DEFAULT_WIDTH;
767 gint draw_image_height;
768 if ( a_settings_get_integer ( VIK_SETTINGS_WIN_SAVE_IMAGE_HEIGHT, &draw_image_height ) )
769 vw->draw_image_height = draw_image_height;
771 vw->draw_image_height = DRAW_IMAGE_DEFAULT_HEIGHT;
772 gboolean draw_image_save_as_png;
773 if ( a_settings_get_boolean ( VIK_SETTINGS_WIN_SAVE_IMAGE_PNG, &draw_image_save_as_png ) )
774 vw->draw_image_save_as_png = draw_image_save_as_png;
776 vw->draw_image_save_as_png = DRAW_IMAGE_DEFAULT_SAVE_AS_PNG;
778 vw->main_vbox = gtk_vbox_new(FALSE, 1);
779 gtk_container_add (GTK_CONTAINER (vw), vw->main_vbox);
780 vw->menu_hbox = gtk_hbox_new(FALSE, 1);
781 GtkWidget *menu_bar = gtk_ui_manager_get_widget (vw->uim, "/MainMenu");
782 gtk_box_pack_start (GTK_BOX(vw->menu_hbox), menu_bar, FALSE, TRUE, 0);
783 gtk_box_pack_start (GTK_BOX(vw->main_vbox), vw->menu_hbox, FALSE, TRUE, 0);
785 toolbar_init(vw->viking_vtb,
791 (gpointer)vw); // This auto packs toolbar into the vbox
792 // Must be performed post toolbar init
794 for (i=0; i<VIK_LAYER_NUM_TYPES; i++) {
795 for ( j = 0; j < vik_layer_get_interface(i)->tools_count; j++ ) {
796 toolbar_action_set_sensitive ( vw->viking_vtb, vik_layer_get_interface(i)->tools[j].radioActionEntry.name, FALSE );
800 vik_ext_tool_datasources_add_menu_items ( vw, vw->uim );
802 GtkWidget * zoom_levels = gtk_ui_manager_get_widget (vw->uim, "/MainMenu/View/SetZoom");
803 GtkWidget * zoom_levels_menu = create_zoom_menu_all_levels ( vik_viewport_get_zoom(vw->viking_vvp) );
804 gtk_menu_item_set_submenu (GTK_MENU_ITEM (zoom_levels), zoom_levels_menu);
805 g_signal_connect ( G_OBJECT(zoom_levels_menu), "selection-done", G_CALLBACK(zoom_changed), vw);
806 g_signal_connect_swapped ( G_OBJECT(vw->viking_vs), "clicked", G_CALLBACK(zoom_popup_handler), zoom_levels_menu );
808 g_signal_connect (G_OBJECT (vw), "delete_event", G_CALLBACK (delete_event), NULL);
811 g_signal_connect_swapped (G_OBJECT(vw->viking_vvp), "updated_center", G_CALLBACK(center_changed_cb), vw);
813 g_signal_connect_swapped (G_OBJECT(vw->viking_vvp), "expose_event", G_CALLBACK(draw_sync), vw);
814 g_signal_connect_swapped (G_OBJECT(vw->viking_vvp), "configure_event", G_CALLBACK(window_configure_event), vw);
815 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 );
816 g_signal_connect_swapped (G_OBJECT(vw->viking_vvp), "scroll_event", G_CALLBACK(draw_scroll), vw);
817 g_signal_connect_swapped (G_OBJECT(vw->viking_vvp), "button_press_event", G_CALLBACK(draw_click), vw);
818 g_signal_connect_swapped (G_OBJECT(vw->viking_vvp), "button_release_event", G_CALLBACK(draw_release), vw);
819 g_signal_connect_swapped (G_OBJECT(vw->viking_vvp), "motion_notify_event", G_CALLBACK(draw_mouse_motion), vw);
821 g_signal_connect_swapped (G_OBJECT(vw->viking_vlp), "update", G_CALLBACK(draw_update), vw);
822 g_signal_connect_swapped (G_OBJECT(vw->viking_vlp), "delete_layer", G_CALLBACK(vik_window_clear_highlight), vw);
824 // Allow key presses to be processed anywhere
825 g_signal_connect_swapped (G_OBJECT (vw), "key_press_event", G_CALLBACK (key_press_event), vw);
827 // Set initial button sensitivity
828 center_changed_cb ( vw );
830 vw->hpaned = gtk_hpaned_new ();
831 gtk_paned_pack1 ( GTK_PANED(vw->hpaned), GTK_WIDGET (vw->viking_vlp), FALSE, FALSE );
832 gtk_paned_pack2 ( GTK_PANED(vw->hpaned), GTK_WIDGET (vw->viking_vvp), TRUE, TRUE );
834 /* This packs the button into the window (a gtk container). */
835 gtk_box_pack_start (GTK_BOX(vw->main_vbox), vw->hpaned, TRUE, TRUE, 0);
837 gtk_box_pack_end (GTK_BOX(vw->main_vbox), GTK_WIDGET(vw->viking_vs), FALSE, TRUE, 0);
839 a_background_add_window ( vw );
841 window_list = g_slist_prepend ( window_list, vw);
843 gint height = VIKING_WINDOW_HEIGHT;
844 gint width = VIKING_WINDOW_WIDTH;
846 if ( a_vik_get_restore_window_state() ) {
847 if ( a_settings_get_integer ( VIK_SETTINGS_WIN_HEIGHT, &height ) ) {
848 // Enforce a basic minimum size
853 // No setting - so use default
854 height = VIKING_WINDOW_HEIGHT;
856 if ( a_settings_get_integer ( VIK_SETTINGS_WIN_WIDTH, &width ) ) {
857 // Enforce a basic minimum size
862 // No setting - so use default
863 width = VIKING_WINDOW_WIDTH;
866 if ( a_settings_get_boolean ( VIK_SETTINGS_WIN_MAX, &maxed ) )
868 gtk_window_maximize ( GTK_WINDOW(vw) );
871 if ( a_settings_get_boolean ( VIK_SETTINGS_WIN_FULLSCREEN, &full ) ) {
873 vw->show_full_screen = TRUE;
874 gtk_window_fullscreen ( GTK_WINDOW(vw) );
875 GtkWidget *check_box = gtk_ui_manager_get_widget ( vw->uim, "/ui/MainMenu/View/FullScreen" );
877 gtk_check_menu_item_set_active ( GTK_CHECK_MENU_ITEM(check_box), TRUE );
881 gint position = -1; // Let GTK determine default positioning
882 if ( !a_settings_get_integer ( VIK_SETTINGS_WIN_PANE_POSITION, &position ) ) {
885 gtk_paned_set_position ( GTK_PANED(vw->hpaned), position );
888 gtk_window_set_default_size ( GTK_WINDOW(vw), width, height );
892 vw->save_img_dia = NULL;
893 vw->save_img_dir_dia = NULL;
895 vw->show_side_panel = TRUE;
896 vw->show_statusbar = TRUE;
897 vw->show_toolbar = TRUE;
898 vw->show_main_menu = TRUE;
900 // Only accept Drag and Drop of files onto the viewport
901 gtk_drag_dest_set ( GTK_WIDGET(vw->viking_vvp), GTK_DEST_DEFAULT_ALL, NULL, 0, GDK_ACTION_COPY );
902 gtk_drag_dest_add_uri_targets ( GTK_WIDGET(vw->viking_vvp) );
903 g_signal_connect ( GTK_WIDGET(vw->viking_vvp), "drag-data-received", G_CALLBACK(drag_data_received_cb), NULL );
905 // Store the thread value so comparisons can be made to determine the gdk update method
906 // Hopefully we are storing the main thread value here :)
907 // [ATM any window initialization is always be performed by the main thread]
908 vw->thread = g_thread_self();
910 // Set the default tool + mode
911 gtk_action_activate ( gtk_action_group_get_action ( vw->action_group, "Pan" ) );
912 gtk_action_activate ( gtk_action_group_get_action ( vw->action_group, "ModeMercator" ) );
914 gchar *accel_file_name = g_build_filename ( a_get_viking_dir(), VIKING_ACCELERATOR_KEY_FILE, NULL );
915 gtk_accel_map_load ( accel_file_name );
916 g_free ( accel_file_name );
919 static VikWindow *window_new ()
921 return VIK_WINDOW ( g_object_new ( VIK_WINDOW_TYPE, NULL ) );
925 * Update the displayed map
926 * Only update the top most visible map layer
927 * ATM this assumes (as per defaults) the top most map has full alpha setting
928 * such that other other maps even though they may be active will not be seen
929 * It's more complicated to work out which maps are actually visible due to alpha settings
930 * and overkill for this simple refresh method.
932 static void simple_map_update ( VikWindow *vw, gboolean only_new )
934 // Find the most relevent single map layer to operate on
935 VikLayer *vl = vik_aggregate_layer_get_top_visible_layer_of_type (vik_layers_panel_get_top_layer(vw->viking_vlp), VIK_LAYER_MAPS);
937 vik_maps_layer_download ( VIK_MAPS_LAYER(vl), vw->viking_vvp, only_new );
941 * This is the global key press handler
942 * Global shortcuts are available at any time and hence are not restricted to when a certain tool is enabled
944 static gboolean key_press_event( VikWindow *vw, GdkEventKey *event, gpointer data )
946 // The keys handled here are not in the menuing system for a couple of reasons:
947 // . Keeps the menu size compact (alebit at expense of discoverably)
948 // . Allows differing key bindings to perform the same actions
950 // First decide if key events are related to the maps layer
951 gboolean map_download = FALSE;
952 gboolean map_download_only_new = TRUE; // Only new or reload
954 GdkModifierType modifiers = gtk_accelerator_get_default_mod_mask();
956 // Standard 'Refresh' keys: F5 or Ctrl+r
957 // Note 'F5' is actually handled via draw_refresh_cb() later on
958 // (not 'R' it's 'r' notice the case difference!!)
959 if ( event->keyval == GDK_r && (event->state & modifiers) == GDK_CONTROL_MASK ) {
961 map_download_only_new = TRUE;
963 // Full cache reload with Ctrl+F5 or Ctrl+Shift+r [This is not in the menu system]
964 // Note the use of uppercase R here since shift key has been pressed
965 else if ( (event->keyval == GDK_F5 && (event->state & modifiers) == GDK_CONTROL_MASK ) ||
966 ( event->keyval == GDK_R && (event->state & modifiers) == (GDK_CONTROL_MASK + GDK_SHIFT_MASK) ) ) {
968 map_download_only_new = FALSE;
970 // Standard Ctrl+KP+ / Ctrl+KP- to zoom in/out respectively
971 else if ( event->keyval == GDK_KEY_KP_Add && (event->state & modifiers) == GDK_CONTROL_MASK ) {
972 vik_viewport_zoom_in ( vw->viking_vvp );
974 return TRUE; // handled keypress
976 else if ( event->keyval == GDK_KEY_KP_Subtract && (event->state & modifiers) == GDK_CONTROL_MASK ) {
977 vik_viewport_zoom_out ( vw->viking_vvp );
979 return TRUE; // handled keypress
982 if ( map_download ) {
983 simple_map_update ( vw, map_download_only_new );
984 return TRUE; // handled keypress
987 VikLayer *vl = vik_layers_panel_get_selected ( vw->viking_vlp );
988 if (vl && vw->vt->active_tool != -1 && vw->vt->tools[vw->vt->active_tool].ti.key_press ) {
989 gint ltype = vw->vt->tools[vw->vt->active_tool].layer_type;
990 if ( vl && ltype == vl->type )
991 return vw->vt->tools[vw->vt->active_tool].ti.key_press(vl, event, vw->vt->tools[vw->vt->active_tool].state);
994 // Ensure called only on window tools (i.e. not on any of the Layer tools since the layer is NULL)
995 if ( vw->current_tool < TOOL_LAYER ) {
996 // No layer - but enable window tool keypress processing - these should be able to handle a NULL layer
997 if ( vw->vt->tools[vw->vt->active_tool].ti.key_press ) {
998 return vw->vt->tools[vw->vt->active_tool].ti.key_press ( vl, event, vw->vt->tools[vw->vt->active_tool].state );
1002 /* Restore Main Menu via Escape key if the user has hidden it */
1003 /* This key is more likely to be used as they may not remember the function key */
1004 if ( event->keyval == GDK_Escape ) {
1005 GtkWidget *check_box = gtk_ui_manager_get_widget ( vw->uim, "/ui/MainMenu/View/SetShow/ViewMainMenu" );
1007 gboolean state = gtk_check_menu_item_get_active ( GTK_CHECK_MENU_ITEM(check_box) );
1009 gtk_widget_show ( gtk_ui_manager_get_widget ( vw->uim, "/ui/MainMenu" ) );
1010 gtk_check_menu_item_set_active ( GTK_CHECK_MENU_ITEM(check_box), TRUE );
1011 return TRUE; /* handled keypress */
1016 return FALSE; /* don't handle the keypress */
1019 static gboolean delete_event( VikWindow *vw )
1021 #ifdef VIKING_PROMPT_IF_MODIFIED
1028 dia = GTK_DIALOG ( gtk_message_dialog_new ( GTK_WINDOW(vw), GTK_DIALOG_DESTROY_WITH_PARENT, GTK_MESSAGE_QUESTION, GTK_BUTTONS_NONE,
1029 _("Do you want to save the changes you made to the document \"%s\"?\n"
1031 "Your changes will be lost if you don't save them."),
1032 window_get_filename ( vw ) ) );
1033 gtk_dialog_add_buttons ( dia, _("Don't Save"), GTK_RESPONSE_NO, GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, GTK_STOCK_SAVE, GTK_RESPONSE_YES, NULL );
1034 switch ( gtk_dialog_run ( dia ) )
1036 case GTK_RESPONSE_NO: gtk_widget_destroy ( GTK_WIDGET(dia) ); return FALSE;
1037 case GTK_RESPONSE_CANCEL: gtk_widget_destroy ( GTK_WIDGET(dia) ); return TRUE;
1038 default: gtk_widget_destroy ( GTK_WIDGET(dia) ); return ! save_file(NULL, vw);
1042 if ( window_count == 1 ) {
1043 // On the final window close - save latest state - if it's wanted...
1044 if ( a_vik_get_restore_window_state() ) {
1045 gint state = gdk_window_get_state ( GTK_WIDGET(vw)->window );
1046 gboolean state_max = state & GDK_WINDOW_STATE_MAXIMIZED;
1047 a_settings_set_boolean ( VIK_SETTINGS_WIN_MAX, state_max );
1049 gboolean state_fullscreen = state & GDK_WINDOW_STATE_FULLSCREEN;
1050 a_settings_set_boolean ( VIK_SETTINGS_WIN_FULLSCREEN, state_fullscreen );
1052 a_settings_set_boolean ( VIK_SETTINGS_WIN_SIDEPANEL, GTK_WIDGET_VISIBLE (GTK_WIDGET(vw->viking_vlp)) );
1054 a_settings_set_boolean ( VIK_SETTINGS_WIN_STATUSBAR, GTK_WIDGET_VISIBLE (GTK_WIDGET(vw->viking_vs)) );
1056 a_settings_set_boolean ( VIK_SETTINGS_WIN_TOOLBAR, GTK_WIDGET_VISIBLE (toolbar_get_widget(vw->viking_vtb)) );
1058 // If supersized - no need to save the enlarged width+height values
1059 if ( ! (state_fullscreen || state_max) ) {
1061 gtk_window_get_size ( GTK_WINDOW (vw), &width, &height );
1062 a_settings_set_integer ( VIK_SETTINGS_WIN_WIDTH, width );
1063 a_settings_set_integer ( VIK_SETTINGS_WIN_HEIGHT, height );
1066 a_settings_set_integer ( VIK_SETTINGS_WIN_PANE_POSITION, gtk_paned_get_position (GTK_PANED(vw->hpaned)) );
1069 a_settings_set_integer ( VIK_SETTINGS_WIN_SAVE_IMAGE_WIDTH, vw->draw_image_width );
1070 a_settings_set_integer ( VIK_SETTINGS_WIN_SAVE_IMAGE_HEIGHT, vw->draw_image_height );
1071 a_settings_set_boolean ( VIK_SETTINGS_WIN_SAVE_IMAGE_PNG, vw->draw_image_save_as_png );
1073 gchar *accel_file_name = g_build_filename ( a_get_viking_dir(), VIKING_ACCELERATOR_KEY_FILE, NULL );
1074 gtk_accel_map_save ( accel_file_name );
1075 g_free ( accel_file_name );
1082 static void newwindow_cb ( GtkAction *a, VikWindow *vw )
1084 g_signal_emit ( G_OBJECT(vw), window_signals[VW_NEWWINDOW_SIGNAL], 0 );
1087 static void draw_update ( VikWindow *vw )
1093 static void draw_sync ( VikWindow *vw )
1095 vik_viewport_sync(vw->viking_vvp);
1100 * Split the status update, as sometimes only need to update the tool part
1101 * also on initialization the zoom related stuff is not ready to be used
1103 static void draw_status_tool ( VikWindow *vw )
1105 if ( vw->current_tool == TOOL_LAYER )
1106 // Use tooltip rather than the internal name as the tooltip is i8n
1107 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 );
1109 vik_statusbar_set_message ( vw->viking_vs, VIK_STATUSBAR_TOOL, _(tool_names[vw->current_tool]) );
1112 static void draw_status ( VikWindow *vw )
1114 static gchar zoom_level[22];
1115 gdouble xmpp = vik_viewport_get_xmpp (vw->viking_vvp);
1116 gdouble ympp = vik_viewport_get_ympp(vw->viking_vvp);
1117 gchar *unit = vik_viewport_get_coord_mode(vw->viking_vvp) == VIK_COORD_UTM ? _("mpp") : _("pixelfact");
1119 g_snprintf ( zoom_level, 22, "%.3f/%.3f %s", xmpp, ympp, unit );
1121 if ( (int)xmpp - xmpp < 0.0 )
1122 g_snprintf ( zoom_level, 22, "%.3f %s", xmpp, unit );
1124 /* xmpp should be a whole number so don't show useless .000 bit */
1125 g_snprintf ( zoom_level, 22, "%d %s", (int)xmpp, unit );
1127 vik_statusbar_set_message ( vw->viking_vs, VIK_STATUSBAR_ZOOM, zoom_level );
1129 draw_status_tool ( vw );
1132 void vik_window_set_redraw_trigger(VikLayer *vl)
1134 VikWindow *vw = VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vl));
1139 static void window_configure_event ( VikWindow *vw )
1141 static int first = 1;
1144 // This is a hack to set the cursor corresponding to the first tool
1145 // FIXME find the correct way to initialize both tool and its cursor
1147 vw->viewport_cursor = (GdkCursor *)toolbox_get_cursor(vw->vt, "Pan");
1148 /* We set cursor, even if it is NULL: it resets to default */
1149 gdk_window_set_cursor ( gtk_widget_get_window(GTK_WIDGET(vw->viking_vvp)), vw->viewport_cursor );
1153 static void draw_redraw ( VikWindow *vw )
1155 VikCoord old_center = vw->trigger_center;
1156 vw->trigger_center = *(vik_viewport_get_center(vw->viking_vvp));
1157 VikLayer *new_trigger = vw->trigger;
1159 VikLayer *old_trigger = VIK_LAYER(vik_viewport_get_trigger(vw->viking_vvp));
1161 if ( ! new_trigger )
1162 ; /* do nothing -- have to redraw everything. */
1163 else if ( (old_trigger != new_trigger) || !vik_coord_equals(&old_center, &vw->trigger_center) || (new_trigger->type == VIK_LAYER_AGGREGATE) )
1164 vik_viewport_set_trigger ( vw->viking_vvp, new_trigger ); /* todo: set to half_drawn mode if new trigger is above old */
1166 vik_viewport_set_half_drawn ( vw->viking_vvp, TRUE );
1169 vik_viewport_clear ( vw->viking_vvp);
1170 // Main layer drawing
1171 vik_layers_panel_draw_all ( vw->viking_vlp );
1172 // Draw highlight (possibly again but ensures it is on top - especially for when tracks overlap)
1173 if ( vik_viewport_get_draw_highlight (vw->viking_vvp) ) {
1174 if ( vw->containing_vtl && (vw->selected_tracks || vw->selected_waypoints ) ) {
1175 vik_trw_layer_draw_highlight_items ( vw->containing_vtl, vw->selected_tracks, vw->selected_waypoints, vw->viking_vvp );
1177 else if ( vw->containing_vtl && (vw->selected_track || vw->selected_waypoint) ) {
1178 vik_trw_layer_draw_highlight_item ( vw->containing_vtl, vw->selected_track, vw->selected_waypoint, vw->viking_vvp );
1180 else if ( vw->selected_vtl ) {
1181 vik_trw_layer_draw_highlight ( vw->selected_vtl, vw->viking_vvp );
1184 // Other viewport decoration items on top if they are enabled/in use
1185 vik_viewport_draw_scale ( vw->viking_vvp );
1186 vik_viewport_draw_copyright ( vw->viking_vvp );
1187 vik_viewport_draw_centermark ( vw->viking_vvp );
1188 vik_viewport_draw_logo ( vw->viking_vvp );
1190 vik_viewport_set_half_drawn ( vw->viking_vvp, FALSE ); /* just in case. */
1193 gboolean draw_buf_done = TRUE;
1195 static gboolean draw_buf(gpointer data)
1197 gpointer *pass_along = data;
1198 gdk_threads_enter();
1199 gdk_draw_drawable (pass_along[0], pass_along[1],
1200 pass_along[2], 0, 0, 0, 0, -1, -1);
1201 draw_buf_done = TRUE;
1202 gdk_threads_leave();
1207 /* Mouse event handlers ************************************************************************/
1209 static void vik_window_pan_click (VikWindow *vw, GdkEventButton *event)
1211 /* set panning origin */
1212 vw->pan_move = FALSE;
1213 vw->pan_x = (gint) event->x;
1214 vw->pan_y = (gint) event->y;
1217 static void draw_click (VikWindow *vw, GdkEventButton *event)
1219 gtk_widget_grab_focus ( GTK_WIDGET(vw->viking_vvp) );
1221 /* middle button pressed. we reserve all middle button and scroll events
1222 * for panning and zooming; tools only get left/right/movement
1224 if ( event->button == 2) {
1225 if ( vw->vt->tools[vw->vt->active_tool].ti.pan_handler )
1226 // Tool still may need to do something (such as disable something)
1227 toolbox_click(vw->vt, event);
1228 vik_window_pan_click ( vw, event );
1231 toolbox_click(vw->vt, event);
1235 static void vik_window_pan_move (VikWindow *vw, GdkEventMotion *event)
1237 if ( vw->pan_x != -1 ) {
1238 vik_viewport_set_center_screen ( vw->viking_vvp, vik_viewport_get_width(vw->viking_vvp)/2 - event->x + vw->pan_x,
1239 vik_viewport_get_height(vw->viking_vvp)/2 - event->y + vw->pan_y );
1240 vw->pan_move = TRUE;
1241 vw->pan_x = event->x;
1242 vw->pan_y = event->y;
1248 * get_location_strings:
1250 * Utility function to get positional strings for the given location
1251 * lat and lon strings will get allocated and so need to be freed after use
1253 static void get_location_strings ( VikWindow *vw, struct UTM utm, gchar **lat, gchar **lon )
1255 if ( vik_viewport_get_drawmode ( vw->viking_vvp ) == VIK_VIEWPORT_DRAWMODE_UTM ) {
1256 // Reuse lat for the first part (Zone + N or S, and lon for the second part (easting and northing) of a UTM format:
1257 // ZONE[N|S] EASTING NORTHING
1258 *lat = g_malloc(4*sizeof(gchar));
1259 // NB zone is stored in a char but is an actual number
1260 g_snprintf (*lat, 4, "%d%c", utm.zone, utm.letter);
1261 *lon = g_malloc(16*sizeof(gchar));
1262 g_snprintf (*lon, 16, "%d %d", (gint)utm.easting, (gint)utm.northing);
1266 a_coords_utm_to_latlon ( &utm, &ll );
1267 a_coords_latlon_to_string ( &ll, lat, lon );
1271 static void draw_mouse_motion (VikWindow *vw, GdkEventMotion *event)
1273 static VikCoord coord;
1274 static struct UTM utm;
1275 #define BUFFER_SIZE 50
1276 static char pointer_buf[BUFFER_SIZE];
1277 gchar *lat = NULL, *lon = NULL;
1280 VikDemInterpol interpol_method;
1282 /* This is a hack, but work far the best, at least for single pointer systems.
1283 * See http://bugzilla.gnome.org/show_bug.cgi?id=587714 for more. */
1285 gdk_window_get_pointer (event->window, &x, &y, NULL);
1289 toolbox_move(vw->vt, event);
1291 vik_viewport_screen_to_coord ( vw->viking_vvp, event->x, event->y, &coord );
1292 vik_coord_to_utm ( &coord, &utm );
1294 get_location_strings ( vw, utm, &lat, &lon );
1296 /* Change interpolate method according to scale */
1297 zoom = vik_viewport_get_zoom(vw->viking_vvp);
1299 interpol_method = VIK_DEM_INTERPOL_NONE;
1300 else if (zoom >= 1.0)
1301 interpol_method = VIK_DEM_INTERPOL_SIMPLE;
1303 interpol_method = VIK_DEM_INTERPOL_BEST;
1304 if ((alt = a_dems_get_elev_by_coord(&coord, interpol_method)) != VIK_DEM_INVALID_ELEVATION) {
1305 if ( a_vik_get_units_height () == VIK_UNITS_HEIGHT_METRES )
1306 g_snprintf ( pointer_buf, BUFFER_SIZE, _("%s %s %dm"), lat, lon, alt );
1308 g_snprintf ( pointer_buf, BUFFER_SIZE, _("%s %s %dft"), lat, lon, (int)VIK_METERS_TO_FEET(alt) );
1311 g_snprintf ( pointer_buf, BUFFER_SIZE, _("%s %s"), lat, lon );
1316 vik_statusbar_set_message ( vw->viking_vs, VIK_STATUSBAR_POSITION, pointer_buf );
1318 vik_window_pan_move ( vw, event );
1320 /* This is recommended by the GTK+ documentation, but does not work properly.
1321 * Use deprecated way until GTK+ gets a solution for correct motion hint handling:
1322 * http://bugzilla.gnome.org/show_bug.cgi?id=587714
1324 /* gdk_event_request_motions ( event ); */
1328 * Action the single click after a small timeout
1329 * If a double click has occurred then this will do nothing
1331 static gboolean vik_window_pan_timeout (VikWindow *vw)
1333 if ( ! vw->single_click_pending ) {
1334 // Double click happened, so don't do anything
1338 /* set panning origin */
1339 vw->pan_move = FALSE;
1340 vw->single_click_pending = FALSE;
1341 vik_viewport_set_center_screen ( vw->viking_vvp, vw->delayed_pan_x, vw->delayed_pan_y );
1344 // Really turn off the pan moving!!
1345 vw->pan_x = vw->pan_y = -1;
1349 static void vik_window_pan_release ( VikWindow *vw, GdkEventButton *event )
1351 gboolean do_draw = TRUE;
1353 if ( vw->pan_move == FALSE ) {
1354 vw->single_click_pending = !vw->single_click_pending;
1356 if ( vw->single_click_pending ) {
1357 // Store offset to use
1358 vw->delayed_pan_x = vw->pan_x;
1359 vw->delayed_pan_y = vw->pan_y;
1360 // Get double click time
1361 GtkSettings *gs = gtk_widget_get_settings ( GTK_WIDGET(vw) );
1362 GValue dct = { 0 }; // = G_VALUE_INIT; // GLIB 2.30+ only
1363 g_value_init ( &dct, G_TYPE_INT );
1364 g_object_get_property ( G_OBJECT(gs), "gtk-double-click-time", &dct );
1365 // Give chance for a double click to occur
1366 gint timer = g_value_get_int ( &dct ) + 50;
1367 g_timeout_add ( timer, (GSourceFunc)vik_window_pan_timeout, vw );
1371 vik_viewport_set_center_screen ( vw->viking_vvp, vw->pan_x, vw->pan_y );
1375 vik_viewport_set_center_screen ( vw->viking_vvp, vik_viewport_get_width(vw->viking_vvp)/2 - event->x + vw->pan_x,
1376 vik_viewport_get_height(vw->viking_vvp)/2 - event->y + vw->pan_y );
1379 vw->pan_move = FALSE;
1380 vw->pan_x = vw->pan_y = -1;
1385 static void draw_release ( VikWindow *vw, GdkEventButton *event )
1387 gtk_widget_grab_focus ( GTK_WIDGET(vw->viking_vvp) );
1389 if ( event->button == 2 ) { /* move / pan */
1390 if ( vw->vt->tools[vw->vt->active_tool].ti.pan_handler )
1391 // Tool still may need to do something (such as reenable something)
1392 toolbox_release(vw->vt, event);
1393 vik_window_pan_release ( vw, event );
1396 toolbox_release(vw->vt, event);
1400 static void draw_scroll (VikWindow *vw, GdkEventScroll *event)
1402 guint modifiers = event->state & (GDK_SHIFT_MASK | GDK_CONTROL_MASK);
1403 if ( modifiers == GDK_CONTROL_MASK ) {
1404 /* control == pan up & down */
1405 if ( event->direction == GDK_SCROLL_UP )
1406 vik_viewport_set_center_screen ( vw->viking_vvp, vik_viewport_get_width(vw->viking_vvp)/2, vik_viewport_get_height(vw->viking_vvp)/3 );
1408 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 );
1409 } else if ( modifiers == GDK_SHIFT_MASK ) {
1410 /* shift == pan left & right */
1411 if ( event->direction == GDK_SCROLL_UP )
1412 vik_viewport_set_center_screen ( vw->viking_vvp, vik_viewport_get_width(vw->viking_vvp)/3, vik_viewport_get_height(vw->viking_vvp)/2 );
1414 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 );
1415 } else if ( modifiers == (GDK_CONTROL_MASK | GDK_SHIFT_MASK) ) {
1416 // This zoom is on the center position
1417 if ( event->direction == GDK_SCROLL_UP )
1418 vik_viewport_zoom_in (vw->viking_vvp);
1420 vik_viewport_zoom_out (vw->viking_vvp);
1422 /* make sure mouse is still over the same point on the map when we zoom */
1425 gint center_x = vik_viewport_get_width ( vw->viking_vvp ) / 2;
1426 gint center_y = vik_viewport_get_height ( vw->viking_vvp ) / 2;
1427 vik_viewport_screen_to_coord ( vw->viking_vvp, event->x, event->y, &coord );
1428 if ( event->direction == GDK_SCROLL_UP )
1429 vik_viewport_zoom_in (vw->viking_vvp);
1431 vik_viewport_zoom_out(vw->viking_vvp);
1432 vik_viewport_coord_to_screen ( vw->viking_vvp, &coord, &x, &y );
1433 vik_viewport_set_center_screen ( vw->viking_vvp, center_x + (x - event->x),
1434 center_y + (y - event->y) );
1442 /********************************************************************************
1444 ********************************************************************************/
1445 static void draw_ruler(VikViewport *vvp, GdkDrawable *d, GdkGC *gc, gint x1, gint y1, gint x2, gint y2, gdouble distance)
1449 GdkGC *labgc = vik_viewport_new_gc ( vvp, "#cccccc", 1);
1450 GdkGC *thickgc = gdk_gc_new(d);
1452 gdouble len = sqrt((x1-x2)*(x1-x2) + (y1-y2)*(y1-y2));
1453 gdouble dx = (x2-x1)/len*10;
1454 gdouble dy = (y2-y1)/len*10;
1455 gdouble c = cos(DEG2RAD(15.0));
1456 gdouble s = sin(DEG2RAD(15.0));
1458 gdouble baseangle = 0;
1461 /* draw line with arrow ends */
1463 gint tmp_x1=x1, tmp_y1=y1, tmp_x2=x2, tmp_y2=y2;
1464 a_viewport_clip_line(&tmp_x1, &tmp_y1, &tmp_x2, &tmp_y2);
1465 gdk_draw_line(d, gc, tmp_x1, tmp_y1, tmp_x2, tmp_y2);
1468 a_viewport_clip_line(&x1, &y1, &x2, &y2);
1469 gdk_draw_line(d, gc, x1, y1, x2, y2);
1471 gdk_draw_line(d, gc, x1 - dy, y1 + dx, x1 + dy, y1 - dx);
1472 gdk_draw_line(d, gc, x2 - dy, y2 + dx, x2 + dy, y2 - dx);
1473 gdk_draw_line(d, gc, x2, y2, x2 - (dx * c + dy * s), y2 - (dy * c - dx * s));
1474 gdk_draw_line(d, gc, x2, y2, x2 - (dx * c - dy * s), y2 - (dy * c + dx * s));
1475 gdk_draw_line(d, gc, x1, y1, x1 + (dx * c + dy * s), y1 + (dy * c - dx * s));
1476 gdk_draw_line(d, gc, x1, y1, x1 + (dx * c - dy * s), y1 + (dy * c + dx * s));
1482 vik_viewport_compute_bearing ( vvp, x1, y1, x2, y2, &angle, &baseangle );
1486 gdk_gc_copy(thickgc, gc);
1487 gdk_gc_set_line_attributes(thickgc, CW, GDK_LINE_SOLID, GDK_CAP_BUTT, GDK_JOIN_MITER);
1488 gdk_color_parse("#2255cc", &color);
1489 gdk_gc_set_rgb_fg_color(thickgc, &color);
1491 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);
1494 gdk_gc_copy(thickgc, gc);
1495 gdk_gc_set_line_attributes(thickgc, 2, GDK_LINE_SOLID, GDK_CAP_BUTT, GDK_JOIN_MITER);
1496 for (i=0; i<180; i++) {
1497 c = cos(DEG2RAD(i)*2 + baseangle);
1498 s = sin(DEG2RAD(i)*2 + baseangle);
1501 gdk_draw_line (d, gc, x1 + CR*c, y1 + CR*s, x1 + (CR+CW)*c, y1 + (CR+CW)*s);
1503 gdouble ticksize = 2*CW;
1504 gdk_draw_line (d, thickgc, x1 + (CR-CW)*c, y1 + (CR-CW)*s, x1 + (CR+ticksize)*c, y1 + (CR+ticksize)*s);
1508 gdk_draw_arc (d, gc, FALSE, x1-CR, y1-CR, 2*CR, 2*CR, 0, 64*360);
1509 gdk_draw_arc (d, gc, FALSE, x1-CR-CW, y1-CR-CW, 2*(CR+CW), 2*(CR+CW), 0, 64*360);
1510 gdk_draw_arc (d, gc, FALSE, x1-CR+CW, y1-CR+CW, 2*(CR-CW), 2*(CR-CW), 0, 64*360);
1511 c = (CR+CW*2)*cos(baseangle);
1512 s = (CR+CW*2)*sin(baseangle);
1513 gdk_draw_line (d, gc, x1-c, y1-s, x1+c, y1+s);
1514 gdk_draw_line (d, gc, x1+s, y1-c, x1-s, y1+c);
1517 #define LABEL(x, y, w, h) { \
1518 gdk_draw_rectangle(d, labgc, TRUE, (x)-2, (y)-1, (w)+4, (h)+1); \
1519 gdk_draw_rectangle(d, gc, FALSE, (x)-2, (y)-1, (w)+4, (h)+1); \
1520 gdk_draw_layout(d, gc, (x), (y), pl); }
1522 gint wd, hd, xd, yd;
1523 gint wb, hb, xb, yb;
1525 pl = gtk_widget_create_pango_layout (GTK_WIDGET(vvp), NULL);
1526 pango_layout_set_font_description (pl, gtk_widget_get_style(GTK_WIDGET(vvp))->font_desc);
1527 pango_layout_set_text(pl, "N", -1);
1528 gdk_draw_layout(d, gc, x1-5, y1-CR-3*CW-8, pl);
1530 /* draw label with distance */
1531 vik_units_distance_t dist_units = a_vik_get_units_distance ();
1532 switch (dist_units) {
1533 case VIK_UNITS_DISTANCE_KILOMETRES:
1534 if (distance >= 1000 && distance < 100000) {
1535 g_sprintf(str, "%3.2f km", distance/1000.0);
1536 } else if (distance < 1000) {
1537 g_sprintf(str, "%d m", (int)distance);
1539 g_sprintf(str, "%d km", (int)distance/1000);
1542 case VIK_UNITS_DISTANCE_MILES:
1543 if (distance >= VIK_MILES_TO_METERS(1) && distance < VIK_MILES_TO_METERS(100)) {
1544 g_sprintf(str, "%3.2f miles", VIK_METERS_TO_MILES(distance));
1545 } else if (distance < VIK_MILES_TO_METERS(1)) {
1546 g_sprintf(str, "%d yards", (int)(distance*1.0936133));
1548 g_sprintf(str, "%d miles", (int)VIK_METERS_TO_MILES(distance));
1551 case VIK_UNITS_DISTANCE_NAUTICAL_MILES:
1552 if (distance >= VIK_NAUTICAL_MILES_TO_METERS(1) && distance < VIK_NAUTICAL_MILES_TO_METERS(100)) {
1553 g_sprintf(str, "%3.2f NM", VIK_METERS_TO_NAUTICAL_MILES(distance));
1554 } else if (distance < VIK_NAUTICAL_MILES_TO_METERS(1)) {
1555 g_sprintf(str, "%d yards", (int)(distance*1.0936133));
1557 g_sprintf(str, "%d NM", (int)VIK_METERS_TO_NAUTICAL_MILES(distance));
1561 g_critical("Houston, we've had a problem. distance=%d", dist_units);
1564 pango_layout_set_text(pl, str, -1);
1566 pango_layout_get_pixel_size ( pl, &wd, &hd );
1568 xd = (x1+x2)/2 + dy;
1569 yd = (y1+y2)/2 - hd/2 - dx;
1571 xd = (x1+x2)/2 - dy;
1572 yd = (y1+y2)/2 - hd/2 + dx;
1575 if ( xd < -5 || yd < -5 || xd > vik_viewport_get_width(vvp)+5 || yd > vik_viewport_get_height(vvp)+5 ) {
1580 LABEL(xd, yd, wd, hd);
1582 /* draw label with bearing */
1583 g_sprintf(str, "%3.1f°", RAD2DEG(angle));
1584 pango_layout_set_text(pl, str, -1);
1585 pango_layout_get_pixel_size ( pl, &wb, &hb );
1586 xb = x1 + CR*cos(angle-M_PI_2);
1587 yb = y1 + CR*sin(angle-M_PI_2);
1589 if ( xb < -5 || yb < -5 || xb > vik_viewport_get_width(vvp)+5 || yb > vik_viewport_get_height(vvp)+5 ) {
1595 GdkRectangle r1 = {xd-2, yd-1, wd+4, hd+1}, r2 = {xb-2, yb-1, wb+4, hb+1};
1596 if (gdk_rectangle_intersect(&r1, &r2, &r2)) {
1600 LABEL(xb, yb, wb, hb);
1604 g_object_unref ( G_OBJECT ( pl ) );
1605 g_object_unref ( G_OBJECT ( labgc ) );
1606 g_object_unref ( G_OBJECT ( thickgc ) );
1612 gboolean has_oldcoord;
1614 } ruler_tool_state_t;
1616 static gpointer ruler_create (VikWindow *vw, VikViewport *vvp)
1618 ruler_tool_state_t *s = g_new(ruler_tool_state_t, 1);
1621 s->has_oldcoord = FALSE;
1625 static void ruler_destroy (ruler_tool_state_t *s)
1630 static VikLayerToolFuncStatus ruler_click (VikLayer *vl, GdkEventButton *event, ruler_tool_state_t *s)
1635 if ( event->button == 1 ) {
1636 gchar *lat=NULL, *lon=NULL;
1637 vik_viewport_screen_to_coord ( s->vvp, (gint) event->x, (gint) event->y, &coord );
1638 vik_coord_to_latlon ( &coord, &ll );
1639 a_coords_latlon_to_string ( &ll, &lat, &lon );
1640 if ( s->has_oldcoord ) {
1641 vik_units_distance_t dist_units = a_vik_get_units_distance ();
1642 switch (dist_units) {
1643 case VIK_UNITS_DISTANCE_KILOMETRES:
1644 temp = g_strdup_printf ( "%s %s DIFF %f meters", lat, lon, vik_coord_diff( &coord, &(s->oldcoord) ) );
1646 case VIK_UNITS_DISTANCE_MILES:
1647 temp = g_strdup_printf ( "%s %s DIFF %f miles", lat, lon, VIK_METERS_TO_MILES(vik_coord_diff( &coord, &(s->oldcoord) )) );
1649 case VIK_UNITS_DISTANCE_NAUTICAL_MILES:
1650 temp = g_strdup_printf ( "%s %s DIFF %f NM", lat, lon, VIK_METERS_TO_NAUTICAL_MILES(vik_coord_diff( &coord, &(s->oldcoord) )) );
1653 temp = g_strdup_printf ("Just to keep the compiler happy");
1654 g_critical("Houston, we've had a problem. distance=%d", dist_units);
1657 s->has_oldcoord = FALSE;
1660 temp = g_strdup_printf ( "%s %s", lat, lon );
1661 s->has_oldcoord = TRUE;
1664 vik_statusbar_set_message ( s->vw->viking_vs, VIK_STATUSBAR_INFO, temp );
1667 s->oldcoord = coord;
1670 vik_viewport_set_center_screen ( s->vvp, (gint) event->x, (gint) event->y );
1671 draw_update ( s->vw );
1673 return VIK_LAYER_TOOL_ACK;
1676 static VikLayerToolFuncStatus ruler_move (VikLayer *vl, GdkEventMotion *event, ruler_tool_state_t *s)
1678 VikViewport *vvp = s->vvp;
1679 VikWindow *vw = s->vw;
1685 if ( s->has_oldcoord ) {
1686 int oldx, oldy, w1, h1, w2, h2;
1687 static GdkPixmap *buf = NULL;
1688 gchar *lat=NULL, *lon=NULL;
1689 w1 = vik_viewport_get_width(vvp);
1690 h1 = vik_viewport_get_height(vvp);
1692 buf = gdk_pixmap_new ( gtk_widget_get_window(GTK_WIDGET(vvp)), w1, h1, -1 );
1694 gdk_drawable_get_size(buf, &w2, &h2);
1695 if (w1 != w2 || h1 != h2) {
1696 g_object_unref ( G_OBJECT ( buf ) );
1697 buf = gdk_pixmap_new ( gtk_widget_get_window(GTK_WIDGET(vvp)), w1, h1, -1 );
1700 vik_viewport_screen_to_coord ( vvp, (gint) event->x, (gint) event->y, &coord );
1701 vik_coord_to_latlon ( &coord, &ll );
1702 vik_viewport_coord_to_screen ( vvp, &s->oldcoord, &oldx, &oldy );
1704 gdk_draw_drawable (buf, gtk_widget_get_style(GTK_WIDGET(vvp))->black_gc,
1705 vik_viewport_get_pixmap(vvp), 0, 0, 0, 0, -1, -1);
1706 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)) );
1707 if (draw_buf_done) {
1708 static gpointer pass_along[3];
1709 pass_along[0] = gtk_widget_get_window(GTK_WIDGET(vvp));
1710 pass_along[1] = gtk_widget_get_style(GTK_WIDGET(vvp))->black_gc;
1711 pass_along[2] = buf;
1712 g_idle_add_full (G_PRIORITY_HIGH_IDLE + 10, draw_buf, pass_along, NULL);
1713 draw_buf_done = FALSE;
1715 a_coords_latlon_to_string(&ll, &lat, &lon);
1716 vik_units_distance_t dist_units = a_vik_get_units_distance ();
1717 switch (dist_units) {
1718 case VIK_UNITS_DISTANCE_KILOMETRES:
1719 temp = g_strdup_printf ( "%s %s DIFF %f meters", lat, lon, vik_coord_diff( &coord, &(s->oldcoord) ) );
1721 case VIK_UNITS_DISTANCE_MILES:
1722 temp = g_strdup_printf ( "%s %s DIFF %f miles", lat, lon, VIK_METERS_TO_MILES (vik_coord_diff( &coord, &(s->oldcoord) )) );
1724 case VIK_UNITS_DISTANCE_NAUTICAL_MILES:
1725 temp = g_strdup_printf ( "%s %s DIFF %f NM", lat, lon, VIK_METERS_TO_NAUTICAL_MILES (vik_coord_diff( &coord, &(s->oldcoord) )) );
1728 temp = g_strdup_printf ("Just to keep the compiler happy");
1729 g_critical("Houston, we've had a problem. distance=%d", dist_units);
1731 vik_statusbar_set_message ( vw->viking_vs, VIK_STATUSBAR_INFO, temp );
1734 return VIK_LAYER_TOOL_ACK;
1737 static VikLayerToolFuncStatus ruler_release (VikLayer *vl, GdkEventButton *event, ruler_tool_state_t *s)
1739 return VIK_LAYER_TOOL_ACK;
1742 static void ruler_deactivate (VikLayer *vl, ruler_tool_state_t *s)
1744 draw_update ( s->vw );
1747 static gboolean ruler_key_press (VikLayer *vl, GdkEventKey *event, ruler_tool_state_t *s)
1749 if (event->keyval == GDK_Escape) {
1750 s->has_oldcoord = FALSE;
1751 ruler_deactivate ( vl, s );
1754 // Regardless of whether we used it, return false so other GTK things may use it
1758 static VikToolInterface ruler_tool =
1759 // NB Ctrl+Shift+R is used for Refresh (deemed more important), so use 'U' instead
1760 { { "Ruler", "vik-icon-ruler", N_("_Ruler"), "<control><shift>U", N_("Ruler Tool"), 2 },
1761 (VikToolConstructorFunc) ruler_create,
1762 (VikToolDestructorFunc) ruler_destroy,
1763 (VikToolActivationFunc) NULL,
1764 (VikToolActivationFunc) ruler_deactivate,
1765 (VikToolMouseFunc) ruler_click,
1766 (VikToolMouseMoveFunc) ruler_move,
1767 (VikToolMouseFunc) ruler_release,
1768 (VikToolKeyFunc) ruler_key_press,
1770 GDK_CURSOR_IS_PIXMAP,
1771 &cursor_ruler_pixbuf,
1773 /*** end ruler code ********************************************************/
1777 /********************************************************************************
1779 ********************************************************************************/
1784 // Track zoom bounds for zoom tool with shift modifier:
1785 gboolean bounds_active;
1788 } zoom_tool_state_t;
1791 * In case the screen size has changed
1793 static void zoomtool_resize_pixmap (zoom_tool_state_t *zts)
1797 // Allocate a drawing area the size of the viewport
1798 w1 = vik_viewport_get_width ( zts->vw->viking_vvp );
1799 h1 = vik_viewport_get_height ( zts->vw->viking_vvp );
1801 if ( !zts->pixmap ) {
1803 zts->pixmap = gdk_pixmap_new ( gtk_widget_get_window(GTK_WIDGET(zts->vw->viking_vvp)), w1, h1, -1 );
1806 gdk_drawable_get_size ( zts->pixmap, &w2, &h2 );
1808 if ( w1 != w2 || h1 != h2 ) {
1809 // Has changed - delete and recreate with new values
1810 g_object_unref ( G_OBJECT ( zts->pixmap ) );
1811 zts->pixmap = gdk_pixmap_new ( gtk_widget_get_window(GTK_WIDGET(zts->vw->viking_vvp)), w1, h1, -1 );
1815 static gpointer zoomtool_create (VikWindow *vw, VikViewport *vvp)
1817 zoom_tool_state_t *zts = g_new(zoom_tool_state_t, 1);
1822 zts->bounds_active = FALSE;
1826 static void zoomtool_destroy ( zoom_tool_state_t *zts)
1829 g_object_unref ( G_OBJECT ( zts->pixmap ) );
1833 static VikLayerToolFuncStatus zoomtool_click (VikLayer *vl, GdkEventButton *event, zoom_tool_state_t *zts)
1835 zts->vw->modified = TRUE;
1836 guint modifiers = event->state & (GDK_SHIFT_MASK | GDK_CONTROL_MASK);
1840 gint center_x = vik_viewport_get_width ( zts->vw->viking_vvp ) / 2;
1841 gint center_y = vik_viewport_get_height ( zts->vw->viking_vvp ) / 2;
1843 gboolean skip_update = FALSE;
1845 zts->bounds_active = FALSE;
1847 if ( modifiers == (GDK_CONTROL_MASK | GDK_SHIFT_MASK) ) {
1848 // This zoom is on the center position
1849 vik_viewport_set_center_screen ( zts->vw->viking_vvp, center_x, center_y );
1850 if ( event->button == 1 )
1851 vik_viewport_zoom_in (zts->vw->viking_vvp);
1852 else if ( event->button == 3 )
1853 vik_viewport_zoom_out (zts->vw->viking_vvp);
1855 else if ( modifiers == GDK_CONTROL_MASK ) {
1856 // This zoom is to recenter on the mouse position
1857 vik_viewport_set_center_screen ( zts->vw->viking_vvp, (gint) event->x, (gint) event->y );
1858 if ( event->button == 1 )
1859 vik_viewport_zoom_in (zts->vw->viking_vvp);
1860 else if ( event->button == 3 )
1861 vik_viewport_zoom_out (zts->vw->viking_vvp);
1863 else if ( modifiers == GDK_SHIFT_MASK ) {
1864 // Get start of new zoom bounds
1865 if ( event->button == 1 ) {
1866 zts->bounds_active = TRUE;
1867 zts->start_x = (gint) event->x;
1868 zts->start_y = (gint) event->y;
1873 /* make sure mouse is still over the same point on the map when we zoom */
1874 vik_viewport_screen_to_coord ( zts->vw->viking_vvp, event->x, event->y, &coord );
1875 if ( event->button == 1 )
1876 vik_viewport_zoom_in (zts->vw->viking_vvp);
1877 else if ( event->button == 3 )
1878 vik_viewport_zoom_out(zts->vw->viking_vvp);
1879 vik_viewport_coord_to_screen ( zts->vw->viking_vvp, &coord, &x, &y );
1880 vik_viewport_set_center_screen ( zts->vw->viking_vvp,
1881 center_x + (x - event->x),
1882 center_y + (y - event->y) );
1886 draw_update ( zts->vw );
1888 return VIK_LAYER_TOOL_ACK;
1891 static VikLayerToolFuncStatus zoomtool_move (VikLayer *vl, GdkEventMotion *event, zoom_tool_state_t *zts)
1893 guint modifiers = event->state & (GDK_SHIFT_MASK | GDK_CONTROL_MASK);
1895 if ( zts->bounds_active && modifiers == GDK_SHIFT_MASK ) {
1896 zoomtool_resize_pixmap ( zts );
1898 // Blank out currently drawn area
1899 gdk_draw_drawable ( zts->pixmap,
1900 gtk_widget_get_style(GTK_WIDGET(zts->vw->viking_vvp))->black_gc,
1901 vik_viewport_get_pixmap(zts->vw->viking_vvp),
1902 0, 0, 0, 0, -1, -1);
1904 // Calculate new box starting point & size in pixels
1905 int xx, yy, width, height;
1906 if ( event->y > zts->start_y ) {
1908 height = event->y-zts->start_y;
1912 height = zts->start_y-event->y;
1914 if ( event->x > zts->start_x ) {
1916 width = event->x-zts->start_x;
1920 width = zts->start_x-event->x;
1924 gdk_draw_rectangle (zts->pixmap, gtk_widget_get_style(GTK_WIDGET(zts->vw->viking_vvp))->black_gc, FALSE, xx, yy, width, height);
1926 // Only actually draw when there's time to do so
1927 if (draw_buf_done) {
1928 static gpointer pass_along[3];
1929 pass_along[0] = gtk_widget_get_window(GTK_WIDGET(zts->vw->viking_vvp));
1930 pass_along[1] = gtk_widget_get_style(GTK_WIDGET(zts->vw->viking_vvp))->black_gc;
1931 pass_along[2] = zts->pixmap;
1932 g_idle_add_full (G_PRIORITY_HIGH_IDLE + 10, draw_buf, pass_along, NULL);
1933 draw_buf_done = FALSE;
1937 zts->bounds_active = FALSE;
1939 return VIK_LAYER_TOOL_ACK;
1942 static VikLayerToolFuncStatus zoomtool_release (VikLayer *vl, GdkEventButton *event, zoom_tool_state_t *zts)
1944 guint modifiers = event->state & (GDK_SHIFT_MASK | GDK_CONTROL_MASK);
1946 // Ensure haven't just released on the exact same position
1947 // i.e. probably haven't moved the mouse at all
1948 if ( zts->bounds_active && modifiers == GDK_SHIFT_MASK &&
1949 ( event->x < zts->start_x-5 || event->x > zts->start_x+5 ) &&
1950 ( event->y < zts->start_y-5 || event->y > zts->start_y+5 ) ) {
1952 VikCoord coord1, coord2;
1953 vik_viewport_screen_to_coord ( zts->vw->viking_vvp, zts->start_x, zts->start_y, &coord1);
1954 vik_viewport_screen_to_coord ( zts->vw->viking_vvp, event->x, event->y, &coord2);
1956 // From the extend of the bounds pick the best zoom level
1957 // c.f. trw_layer_zoom_to_show_latlons()
1958 // Maybe refactor...
1959 struct LatLon ll1, ll2;
1960 vik_coord_to_latlon(&coord1, &ll1);
1961 vik_coord_to_latlon(&coord2, &ll2);
1962 struct LatLon average = { (ll1.lat+ll2.lat)/2,
1963 (ll1.lon+ll2.lon)/2 };
1965 VikCoord new_center;
1966 vik_coord_load_from_latlon ( &new_center, vik_viewport_get_coord_mode ( zts->vw->viking_vvp ), &average );
1967 vik_viewport_set_center_coord ( zts->vw->viking_vvp, &new_center, FALSE );
1969 /* Convert into definite 'smallest' and 'largest' positions */
1970 struct LatLon minmin;
1971 if ( ll1.lat < ll2.lat )
1972 minmin.lat = ll1.lat;
1974 minmin.lat = ll2.lat;
1976 struct LatLon maxmax;
1977 if ( ll1.lon > ll2.lon )
1978 maxmax.lon = ll1.lon;
1980 maxmax.lon = ll2.lon;
1982 /* Always recalculate the 'best' zoom level */
1983 gdouble zoom = VIK_VIEWPORT_MIN_ZOOM;
1984 vik_viewport_set_zoom ( zts->vw->viking_vvp, zoom );
1986 gdouble min_lat, max_lat, min_lon, max_lon;
1987 /* Should only be a maximum of about 18 iterations from min to max zoom levels */
1988 while ( zoom <= VIK_VIEWPORT_MAX_ZOOM ) {
1989 vik_viewport_get_min_max_lat_lon ( zts->vw->viking_vvp, &min_lat, &max_lat, &min_lon, &max_lon );
1990 /* NB I think the logic used in this test to determine if the bounds is within view
1991 fails if track goes across 180 degrees longitude.
1992 Hopefully that situation is not too common...
1993 Mind you viking doesn't really do edge locations to well anyway */
1994 if ( min_lat < minmin.lat &&
1995 max_lat > minmin.lat &&
1996 min_lon < maxmax.lon &&
1997 max_lon > maxmax.lon )
1998 /* Found within zoom level */
2003 vik_viewport_set_zoom ( zts->vw->viking_vvp, zoom );
2007 // When pressing shift and clicking for zoom, then jump three levels
2008 if ( modifiers == GDK_SHIFT_MASK ) {
2009 // Zoom in/out by three if possible
2010 vik_viewport_set_center_screen ( zts->vw->viking_vvp, event->x, event->y );
2011 if ( event->button == 1 ) {
2012 vik_viewport_zoom_in ( zts->vw->viking_vvp );
2013 vik_viewport_zoom_in ( zts->vw->viking_vvp );
2014 vik_viewport_zoom_in ( zts->vw->viking_vvp );
2016 else if ( event->button == 3 ) {
2017 vik_viewport_zoom_out ( zts->vw->viking_vvp );
2018 vik_viewport_zoom_out ( zts->vw->viking_vvp );
2019 vik_viewport_zoom_out ( zts->vw->viking_vvp );
2024 draw_update ( zts->vw );
2027 zts->bounds_active = FALSE;
2029 return VIK_LAYER_TOOL_ACK;
2032 static VikToolInterface zoom_tool =
2033 { { "Zoom", "vik-icon-zoom", N_("_Zoom"), "<control><shift>Z", N_("Zoom Tool"), 1 },
2034 (VikToolConstructorFunc) zoomtool_create,
2035 (VikToolDestructorFunc) zoomtool_destroy,
2036 (VikToolActivationFunc) NULL,
2037 (VikToolActivationFunc) NULL,
2038 (VikToolMouseFunc) zoomtool_click,
2039 (VikToolMouseMoveFunc) zoomtool_move,
2040 (VikToolMouseFunc) zoomtool_release,
2043 GDK_CURSOR_IS_PIXMAP,
2044 &cursor_zoom_pixbuf,
2046 /*** end zoom code ********************************************************/
2048 /********************************************************************************
2050 ********************************************************************************/
2051 static gpointer pantool_create (VikWindow *vw, VikViewport *vvp)
2056 // NB Double clicking means this gets called THREE times!!!
2057 static VikLayerToolFuncStatus pantool_click (VikLayer *vl, GdkEventButton *event, VikWindow *vw)
2059 vw->modified = TRUE;
2061 if ( event->type == GDK_2BUTTON_PRESS ) {
2062 // Zoom in / out on double click
2063 // No need to change the center as that has already occurred in the first click of a double click occurrence
2064 if ( event->button == 1 ) {
2065 guint modifier = event->state & GDK_SHIFT_MASK;
2067 vik_viewport_zoom_out ( vw->viking_vvp );
2069 vik_viewport_zoom_in ( vw->viking_vvp );
2071 else if ( event->button == 3 )
2072 vik_viewport_zoom_out ( vw->viking_vvp );
2077 // Standard pan click
2078 if ( event->button == 1 )
2079 vik_window_pan_click ( vw, event );
2081 return VIK_LAYER_TOOL_ACK;
2084 static VikLayerToolFuncStatus pantool_move (VikLayer *vl, GdkEventMotion *event, VikWindow *vw)
2086 vik_window_pan_move ( vw, event );
2087 return VIK_LAYER_TOOL_ACK;
2090 static VikLayerToolFuncStatus pantool_release (VikLayer *vl, GdkEventButton *event, VikWindow *vw)
2092 if ( event->button == 1 )
2093 vik_window_pan_release ( vw, event );
2094 return VIK_LAYER_TOOL_ACK;
2097 static VikToolInterface pan_tool =
2098 { { "Pan", "vik-icon-pan", N_("_Pan"), "<control><shift>P", N_("Pan Tool"), 0 },
2099 (VikToolConstructorFunc) pantool_create,
2100 (VikToolDestructorFunc) NULL,
2101 (VikToolActivationFunc) NULL,
2102 (VikToolActivationFunc) NULL,
2103 (VikToolMouseFunc) pantool_click,
2104 (VikToolMouseMoveFunc) pantool_move,
2105 (VikToolMouseFunc) pantool_release,
2111 /*** end pan code ********************************************************/
2113 /********************************************************************************
2115 ********************************************************************************/
2116 static gpointer selecttool_create (VikWindow *vw, VikViewport *vvp)
2118 tool_ed_t *t = g_new(tool_ed_t, 1);
2122 t->is_waypoint = FALSE;
2126 static void selecttool_destroy (tool_ed_t *t)
2134 GdkEventButton *event;
2135 tool_ed_t *tool_edit;
2138 static void click_layer_selected (VikLayer *vl, clicker *ck)
2140 /* Do nothing when function call returns true; */
2141 /* i.e. stop on first found item */
2144 if ( vik_layer_get_interface(vl->type)->select_click )
2145 ck->cont = !vik_layer_get_interface(vl->type)->select_click ( vl, ck->event, ck->vvp, ck->tool_edit );
2148 static VikLayerToolFuncStatus selecttool_click (VikLayer *vl, GdkEventButton *event, tool_ed_t *t)
2150 /* Only allow selection on primary button */
2151 if ( event->button == 1 ) {
2152 /* Enable click to apply callback to potentially all track/waypoint layers */
2153 /* Useful as we can find things that aren't necessarily in the currently selected layer */
2154 GList* gl = vik_layers_panel_get_all_layers_of_type ( t->vw->viking_vlp, VIK_LAYER_TRW, FALSE ); // Don't get invisible layers
2157 ck.vvp = t->vw->viking_vvp;
2160 g_list_foreach ( gl, (GFunc) click_layer_selected, &ck );
2163 // If nothing found then deselect & redraw screen if necessary to remove the highlight
2166 VikTreeview *vtv = vik_layers_panel_get_treeview ( t->vw->viking_vlp );
2168 if ( vik_treeview_get_selected_iter ( vtv, &iter ) ) {
2169 // Only clear if selected thing is a TrackWaypoint layer or a sublayer
2170 gint type = vik_treeview_item_get_type ( vtv, &iter );
2171 if ( type == VIK_TREEVIEW_TYPE_SUBLAYER ||
2172 VIK_LAYER(vik_treeview_item_get_pointer ( vtv, &iter ))->type == VIK_LAYER_TRW ) {
2174 vik_treeview_item_unselect ( vtv, &iter );
2175 if ( vik_window_clear_highlight ( t->vw ) )
2176 draw_update ( t->vw );
2181 else if ( ( event->button == 3 ) && ( vl && ( vl->type == VIK_LAYER_TRW ) ) ) {
2183 /* Act on currently selected item to show menu */
2184 if ( t->vw->selected_track || t->vw->selected_waypoint )
2185 if ( vik_layer_get_interface(vl->type)->show_viewport_menu )
2186 vik_layer_get_interface(vl->type)->show_viewport_menu ( vl, event, t->vw->viking_vvp );
2189 return VIK_LAYER_TOOL_ACK;
2192 static VikLayerToolFuncStatus selecttool_move (VikLayer *vl, GdkEventButton *event, tool_ed_t *t)
2194 /* Only allow selection on primary button */
2195 if ( event->button == 1 ) {
2196 // Don't care about vl here
2198 if ( vik_layer_get_interface(VIK_LAYER_TRW)->select_move )
2199 vik_layer_get_interface(VIK_LAYER_TRW)->select_move ( vl, event, t->vvp, t );
2201 return VIK_LAYER_TOOL_ACK;
2204 static VikLayerToolFuncStatus selecttool_release (VikLayer *vl, GdkEventButton *event, tool_ed_t *t)
2206 /* Only allow selection on primary button */
2207 if ( event->button == 1 ) {
2208 // Don't care about vl here
2210 if ( vik_layer_get_interface(VIK_LAYER_TRW)->select_release )
2211 vik_layer_get_interface(VIK_LAYER_TRW)->select_release ( (VikLayer*)t->vtl, event, t->vvp, t );
2213 return VIK_LAYER_TOOL_ACK;
2216 static VikToolInterface select_tool =
2217 { { "Select", "vik-icon-select", N_("_Select"), "<control><shift>S", N_("Select Tool"), 3 },
2218 (VikToolConstructorFunc) selecttool_create,
2219 (VikToolDestructorFunc) selecttool_destroy,
2220 (VikToolActivationFunc) NULL,
2221 (VikToolActivationFunc) NULL,
2222 (VikToolMouseFunc) selecttool_click,
2223 (VikToolMouseMoveFunc) selecttool_move,
2224 (VikToolMouseFunc) selecttool_release,
2225 (VikToolKeyFunc) NULL,
2230 /*** end select tool code ********************************************************/
2232 static void draw_pan_cb ( GtkAction *a, VikWindow *vw )
2234 // Since the treeview cell editting intercepts standard keyboard handlers, it means we can receive events here
2235 // Thus if currently editting, ensure we don't move the viewport when Ctrl+<arrow> is received
2236 VikLayer *sel = vik_layers_panel_get_selected ( vw->viking_vlp );
2237 if ( sel && vik_treeview_get_editing ( sel->vt ) )
2240 if (!strcmp(gtk_action_get_name(a), "PanNorth")) {
2241 vik_viewport_set_center_screen ( vw->viking_vvp, vik_viewport_get_width(vw->viking_vvp)/2, 0 );
2242 } else if (!strcmp(gtk_action_get_name(a), "PanEast")) {
2243 vik_viewport_set_center_screen ( vw->viking_vvp, vik_viewport_get_width(vw->viking_vvp), vik_viewport_get_height(vw->viking_vvp)/2 );
2244 } else if (!strcmp(gtk_action_get_name(a), "PanSouth")) {
2245 vik_viewport_set_center_screen ( vw->viking_vvp, vik_viewport_get_width(vw->viking_vvp)/2, vik_viewport_get_height(vw->viking_vvp) );
2246 } else if (!strcmp(gtk_action_get_name(a), "PanWest")) {
2247 vik_viewport_set_center_screen ( vw->viking_vvp, 0, vik_viewport_get_height(vw->viking_vvp)/2 );
2252 static void draw_zoom_cb ( GtkAction *a, VikWindow *vw )
2256 if (!strcmp(gtk_action_get_name(a), "ZoomIn")) {
2259 else if (!strcmp(gtk_action_get_name(a), "ZoomOut")) {
2262 else if (!strcmp(gtk_action_get_name(a), "Zoom0.25")) {
2265 else if (!strcmp(gtk_action_get_name(a), "Zoom0.5")) {
2269 gchar *s = (gchar *)gtk_action_get_name(a);
2275 case -3: vik_viewport_zoom_in ( vw->viking_vvp ); break;
2276 case -4: vik_viewport_zoom_out ( vw->viking_vvp ); break;
2277 case -1: vik_viewport_set_zoom ( vw->viking_vvp, 0.5 ); break;
2278 case -2: vik_viewport_set_zoom ( vw->viking_vvp, 0.25 ); break;
2279 default: vik_viewport_set_zoom ( vw->viking_vvp, what );
2284 static void draw_goto_cb ( GtkAction *a, VikWindow *vw )
2286 VikCoord new_center;
2288 if (!strcmp(gtk_action_get_name(a), "GotoLL")) {
2289 struct LatLon ll, llold;
2290 vik_coord_to_latlon ( vik_viewport_get_center ( vw->viking_vvp ), &llold );
2291 if ( a_dialog_goto_latlon ( GTK_WINDOW(vw), &ll, &llold ) )
2292 vik_coord_load_from_latlon ( &new_center, vik_viewport_get_coord_mode(vw->viking_vvp), &ll );
2296 else if (!strcmp(gtk_action_get_name(a), "GotoUTM")) {
2297 struct UTM utm, utmold;
2298 vik_coord_to_utm ( vik_viewport_get_center ( vw->viking_vvp ), &utmold );
2299 if ( a_dialog_goto_utm ( GTK_WINDOW(vw), &utm, &utmold ) )
2300 vik_coord_load_from_utm ( &new_center, vik_viewport_get_coord_mode(vw->viking_vvp), &utm );
2305 g_critical("Houston, we've had a problem.");
2309 vik_viewport_set_center_coord ( vw->viking_vvp, &new_center, TRUE );
2314 * center_changed_cb:
2316 static void center_changed_cb ( VikWindow *vw )
2318 // ATM Keep back always available, so when we pan - we can jump to the last requested position
2320 GtkAction* action_back = gtk_action_group_get_action ( vw->action_group, "GoBack" );
2321 if ( action_back ) {
2322 gtk_action_set_sensitive ( action_back, vik_viewport_back_available(vw->viking_vvp) );
2325 GtkAction* action_forward = gtk_action_group_get_action ( vw->action_group, "GoForward" );
2326 if ( action_forward ) {
2327 gtk_action_set_sensitive ( action_forward, vik_viewport_forward_available(vw->viking_vvp) );
2330 toolbar_action_set_sensitive ( vw->viking_vtb, "GoForward", vik_viewport_forward_available(vw->viking_vvp) );
2334 * draw_goto_back_and_forth:
2336 static void draw_goto_back_and_forth ( GtkAction *a, VikWindow *vw )
2338 gboolean changed = FALSE;
2339 if (!strcmp(gtk_action_get_name(a), "GoBack")) {
2340 changed = vik_viewport_go_back ( vw->viking_vvp );
2342 else if (!strcmp(gtk_action_get_name(a), "GoForward")) {
2343 changed = vik_viewport_go_forward ( vw->viking_vvp );
2349 // Recheck buttons sensitivities, as the center changed signal is not sent on back/forward changes
2350 // (otherwise we would get stuck in an infinite loop!)
2351 center_changed_cb ( vw );
2358 * Refresh maps displayed
2360 static void draw_refresh_cb ( GtkAction *a, VikWindow *vw )
2362 // Only get 'new' maps
2363 simple_map_update ( vw, TRUE );
2366 static void menu_addlayer_cb ( GtkAction *a, VikWindow *vw )
2368 VikLayerTypeEnum type;
2369 for ( type = 0; type < VIK_LAYER_NUM_TYPES; type++ ) {
2370 if (!strcmp(vik_layer_get_interface(type)->name, gtk_action_get_name(a))) {
2371 if ( vik_layers_panel_new_layer ( vw->viking_vlp, type ) ) {
2373 vw->modified = TRUE;
2379 static void menu_copy_layer_cb ( GtkAction *a, VikWindow *vw )
2381 a_clipboard_copy_selected ( vw->viking_vlp );
2384 static void menu_cut_layer_cb ( GtkAction *a, VikWindow *vw )
2386 vik_layers_panel_cut_selected ( vw->viking_vlp );
2387 vw->modified = TRUE;
2390 static void menu_paste_layer_cb ( GtkAction *a, VikWindow *vw )
2392 if ( vik_layers_panel_paste_selected ( vw->viking_vlp ) )
2394 vw->modified = TRUE;
2398 static void menu_properties_cb ( GtkAction *a, VikWindow *vw )
2400 if ( ! vik_layers_panel_properties ( vw->viking_vlp ) )
2401 a_dialog_info_msg ( GTK_WINDOW(vw), _("You must select a layer to show its properties.") );
2404 static void help_help_cb ( GtkAction *a, VikWindow *vw )
2407 ShellExecute(NULL, "open", ""PACKAGE".pdf", NULL, NULL, SW_SHOWNORMAL);
2410 uri = g_strdup_printf("ghelp:%s", PACKAGE);
2411 GError *error = NULL;
2412 gboolean show = gtk_show_uri (NULL, uri, GDK_CURRENT_TIME, &error);
2413 if ( !show && !error )
2414 // No error to show, so unlikely this will get called
2415 a_dialog_error_msg ( GTK_WINDOW(vw), _("The help system is not available.") );
2418 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 );
2419 g_error_free ( error );
2422 #endif /* WINDOWS */
2425 static void toggle_side_panel ( VikWindow *vw )
2427 vw->show_side_panel = !vw->show_side_panel;
2428 if ( vw->show_side_panel )
2429 gtk_widget_show(GTK_WIDGET(vw->viking_vlp));
2431 gtk_widget_hide(GTK_WIDGET(vw->viking_vlp));
2434 static void toggle_full_screen ( VikWindow *vw )
2436 vw->show_full_screen = !vw->show_full_screen;
2437 if ( vw->show_full_screen )
2438 gtk_window_fullscreen ( GTK_WINDOW(vw) );
2440 gtk_window_unfullscreen ( GTK_WINDOW(vw) );
2443 static void toggle_statusbar ( VikWindow *vw )
2445 vw->show_statusbar = !vw->show_statusbar;
2446 if ( vw->show_statusbar )
2447 gtk_widget_show ( GTK_WIDGET(vw->viking_vs) );
2449 gtk_widget_hide ( GTK_WIDGET(vw->viking_vs) );
2452 static void toggle_toolbar ( VikWindow *vw )
2454 vw->show_toolbar = !vw->show_toolbar;
2455 if ( vw->show_toolbar )
2456 gtk_widget_show ( toolbar_get_widget (vw->viking_vtb) );
2458 gtk_widget_hide ( toolbar_get_widget (vw->viking_vtb) );
2461 static void toggle_main_menu ( VikWindow *vw )
2463 vw->show_main_menu = !vw->show_main_menu;
2464 if ( vw->show_main_menu )
2465 gtk_widget_show ( gtk_ui_manager_get_widget ( vw->uim, "/ui/MainMenu" ) );
2467 gtk_widget_hide ( gtk_ui_manager_get_widget ( vw->uim, "/ui/MainMenu" ) );
2470 // Only for 'view' toggle menu widgets ATM.
2471 GtkWidget *get_show_widget_by_name(VikWindow *vw, const gchar *name)
2473 g_return_val_if_fail(name != NULL, NULL);
2475 // ATM only FullScreen is *not* in SetShow path
2477 if ( g_strcmp0 ("FullScreen", name ) )
2478 path = g_strconcat("/ui/MainMenu/View/SetShow/", name, NULL);
2480 path = g_strconcat("/ui/MainMenu/View/", name, NULL);
2482 GtkWidget *widget = gtk_ui_manager_get_widget(vw->uim, path);
2488 static void tb_view_side_panel_cb ( GtkAction *a, VikWindow *vw )
2490 gboolean next_state = !vw->show_side_panel;
2491 GtkWidget *check_box = get_show_widget_by_name ( vw, gtk_action_get_name(a) );
2492 gboolean menu_state = gtk_check_menu_item_get_active ( GTK_CHECK_MENU_ITEM(check_box) );
2493 if ( next_state != menu_state )
2494 gtk_check_menu_item_set_active ( GTK_CHECK_MENU_ITEM(check_box), next_state );
2496 toggle_side_panel ( vw );
2499 static void tb_full_screen_cb ( GtkAction *a, VikWindow *vw )
2501 gboolean next_state = !vw->show_full_screen;
2502 GtkWidget *check_box = get_show_widget_by_name ( vw, gtk_action_get_name(a) );
2503 gboolean menu_state = gtk_check_menu_item_get_active ( GTK_CHECK_MENU_ITEM(check_box) );
2504 if ( next_state != menu_state )
2505 gtk_check_menu_item_set_active ( GTK_CHECK_MENU_ITEM(check_box), next_state );
2507 toggle_full_screen ( vw );
2510 static void tb_view_statusbar_cb ( GtkAction *a, VikWindow *vw )
2512 gboolean next_state = !vw->show_statusbar;
2513 GtkWidget *check_box = get_show_widget_by_name ( vw, gtk_action_get_name(a) );
2514 gboolean menu_state = gtk_check_menu_item_get_active ( GTK_CHECK_MENU_ITEM(check_box) );
2515 if ( next_state != menu_state )
2516 gtk_check_menu_item_set_active ( GTK_CHECK_MENU_ITEM(check_box), next_state );
2518 toggle_statusbar ( vw );
2521 static void tb_view_toolbar_cb ( GtkAction *a, VikWindow *vw )
2523 gboolean next_state = !vw->show_toolbar;
2524 GtkWidget *check_box = get_show_widget_by_name ( vw, gtk_action_get_name(a) );
2525 gboolean menu_state = gtk_check_menu_item_get_active ( GTK_CHECK_MENU_ITEM(check_box) );
2526 if ( next_state != menu_state )
2527 gtk_check_menu_item_set_active ( GTK_CHECK_MENU_ITEM(check_box), next_state );
2529 toggle_toolbar ( vw );
2532 static void tb_view_main_menu_cb ( GtkAction *a, VikWindow *vw )
2534 gboolean next_state = !vw->show_main_menu;
2535 GtkWidget *check_box = get_show_widget_by_name ( vw, gtk_action_get_name(a) );
2536 gboolean menu_state = gtk_check_menu_item_get_active ( GTK_CHECK_MENU_ITEM(check_box) );
2537 if ( next_state != menu_state )
2538 gtk_check_menu_item_set_active ( GTK_CHECK_MENU_ITEM(check_box), next_state );
2540 toggle_main_menu ( vw );
2543 static void tb_set_draw_scale ( GtkAction *a, VikWindow *vw )
2545 gboolean next_state = !vik_viewport_get_draw_scale ( vw->viking_vvp );
2546 GtkWidget *check_box = get_show_widget_by_name ( vw, gtk_action_get_name(a) );
2547 gboolean menu_state = gtk_check_menu_item_get_active ( GTK_CHECK_MENU_ITEM(check_box) );
2548 if ( next_state != menu_state )
2549 gtk_check_menu_item_set_active ( GTK_CHECK_MENU_ITEM(check_box), next_state );
2551 vik_viewport_set_draw_scale ( vw->viking_vvp, next_state );
2556 static void tb_set_draw_centermark ( GtkAction *a, VikWindow *vw )
2558 gboolean next_state = !vik_viewport_get_draw_centermark ( vw->viking_vvp );
2559 GtkWidget *check_box = get_show_widget_by_name ( vw, gtk_action_get_name(a) );
2560 gboolean menu_state = gtk_check_menu_item_get_active ( GTK_CHECK_MENU_ITEM(check_box) );
2561 if ( next_state != menu_state )
2562 gtk_check_menu_item_set_active ( GTK_CHECK_MENU_ITEM(check_box), next_state );
2564 vik_viewport_set_draw_centermark ( vw->viking_vvp, next_state );
2569 static void tb_set_draw_highlight ( GtkAction *a, VikWindow *vw )
2571 gboolean next_state = !vik_viewport_get_draw_highlight ( vw->viking_vvp );
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 vik_viewport_set_draw_highlight ( vw->viking_vvp, next_state );
2582 static void help_about_cb ( GtkAction *a, VikWindow *vw )
2584 a_dialog_about(GTK_WINDOW(vw));
2587 static void help_cache_info_cb ( GtkAction *a, VikWindow *vw )
2589 // NB: No i18n as this is just for debug
2590 gint byte_size = a_mapcache_get_size();
2591 gchar *msg_sz = NULL;
2593 #if GLIB_CHECK_VERSION(2,30,0)
2594 msg_sz = g_format_size_full ( byte_size, G_FORMAT_SIZE_LONG_FORMAT );
2596 msg_sz = g_format_size_for_display ( byte_size );
2598 msg = g_strdup_printf ( "Map Cache size is %s with %d items", msg_sz, a_mapcache_get_count());
2599 a_dialog_info_msg_extra ( GTK_WINDOW(vw), "%s", msg );
2604 static void back_forward_info_cb ( GtkAction *a, VikWindow *vw )
2606 vik_viewport_show_centers ( vw->viking_vvp, GTK_WINDOW(vw) );
2609 static void menu_delete_layer_cb ( GtkAction *a, VikWindow *vw )
2611 if ( vik_layers_panel_get_selected ( vw->viking_vlp ) )
2613 vik_layers_panel_delete_selected ( vw->viking_vlp );
2614 vw->modified = TRUE;
2617 a_dialog_info_msg ( GTK_WINDOW(vw), _("You must select a layer to delete.") );
2620 static void full_screen_cb ( GtkAction *a, VikWindow *vw )
2622 gboolean next_state = !vw->show_full_screen;
2623 GtkToggleToolButton *tbutton = (GtkToggleToolButton *)toolbar_get_widget_by_name ( vw->viking_vtb, gtk_action_get_name(a) );
2625 gboolean tb_state = gtk_toggle_tool_button_get_active ( tbutton );
2626 if ( next_state != tb_state )
2627 gtk_toggle_tool_button_set_active ( tbutton, next_state );
2629 toggle_full_screen ( vw );
2632 toggle_full_screen ( vw );
2635 static void view_side_panel_cb ( GtkAction *a, VikWindow *vw )
2637 gboolean next_state = !vw->show_side_panel;
2638 GtkToggleToolButton *tbutton = (GtkToggleToolButton *)toolbar_get_widget_by_name ( vw->viking_vtb, gtk_action_get_name(a) );
2640 gboolean tb_state = gtk_toggle_tool_button_get_active ( tbutton );
2641 if ( next_state != tb_state )
2642 gtk_toggle_tool_button_set_active ( tbutton, next_state );
2644 toggle_side_panel ( vw );
2647 toggle_side_panel ( vw );
2650 static void view_statusbar_cb ( GtkAction *a, VikWindow *vw )
2652 gboolean next_state = !vw->show_statusbar;
2653 GtkToggleToolButton *tbutton = (GtkToggleToolButton *)toolbar_get_widget_by_name ( vw->viking_vtb, gtk_action_get_name(a) );
2655 gboolean tb_state = gtk_toggle_tool_button_get_active ( tbutton );
2656 if ( next_state != tb_state )
2657 gtk_toggle_tool_button_set_active ( tbutton, next_state );
2659 toggle_statusbar ( vw );
2662 toggle_statusbar ( vw );
2665 static void view_toolbar_cb ( GtkAction *a, VikWindow *vw )
2667 gboolean next_state = !vw->show_toolbar;
2668 GtkToggleToolButton *tbutton = (GtkToggleToolButton *)toolbar_get_widget_by_name ( vw->viking_vtb, gtk_action_get_name(a) );
2670 gboolean tb_state = gtk_toggle_tool_button_get_active ( tbutton );
2671 if ( next_state != tb_state )
2672 gtk_toggle_tool_button_set_active ( tbutton, next_state );
2674 toggle_toolbar ( vw );
2677 toggle_toolbar ( vw );
2680 static void view_main_menu_cb ( GtkAction *a, VikWindow *vw )
2682 gboolean next_state = !vw->show_main_menu;
2683 GtkToggleToolButton *tbutton = (GtkToggleToolButton *)toolbar_get_widget_by_name ( vw->viking_vtb, gtk_action_get_name(a) );
2685 gboolean tb_state = gtk_toggle_tool_button_get_active ( tbutton );
2686 if ( next_state != tb_state )
2687 gtk_toggle_tool_button_set_active ( tbutton, next_state );
2689 toggle_main_menu ( vw );
2692 toggle_toolbar ( vw );
2695 /***************************************
2696 ** tool management routines
2698 ***************************************/
2700 static toolbox_tools_t* toolbox_create(VikWindow *vw)
2702 toolbox_tools_t *vt = g_new(toolbox_tools_t, 1);
2705 vt->active_tool = -1;
2710 static void toolbox_add_tool(toolbox_tools_t *vt, VikToolInterface *vti, gint layer_type )
2712 vt->tools = g_renew(toolbox_tool_t, vt->tools, vt->n_tools+1);
2713 vt->tools[vt->n_tools].ti = *vti;
2714 vt->tools[vt->n_tools].layer_type = layer_type;
2716 vt->tools[vt->n_tools].state = vti->create(vt->vw, vt->vw->viking_vvp);
2719 vt->tools[vt->n_tools].state = NULL;
2724 static int toolbox_get_tool(toolbox_tools_t *vt, const gchar *tool_name)
2727 for (i=0; i<vt->n_tools; i++) {
2728 if (!strcmp(tool_name, vt->tools[i].ti.radioActionEntry.name)) {
2735 static void toolbox_activate(toolbox_tools_t *vt, const gchar *tool_name)
2737 int tool = toolbox_get_tool(vt, tool_name);
2738 toolbox_tool_t *t = &vt->tools[tool];
2739 VikLayer *vl = vik_layers_panel_get_selected ( vt->vw->viking_vlp );
2741 if (tool == vt->n_tools) {
2742 g_critical("trying to activate a non-existent tool...");
2745 /* is the tool already active? */
2746 if (vt->active_tool == tool) {
2750 if (vt->active_tool != -1) {
2751 if (vt->tools[vt->active_tool].ti.deactivate) {
2752 vt->tools[vt->active_tool].ti.deactivate(NULL, vt->tools[vt->active_tool].state);
2755 if (t->ti.activate) {
2756 t->ti.activate(vl, t->state);
2758 vt->active_tool = tool;
2761 static const GdkCursor *toolbox_get_cursor(toolbox_tools_t *vt, const gchar *tool_name)
2763 int tool = toolbox_get_tool(vt, tool_name);
2764 toolbox_tool_t *t = &vt->tools[tool];
2765 if (t->ti.cursor == NULL) {
2766 if (t->ti.cursor_type == GDK_CURSOR_IS_PIXMAP && t->ti.cursor_data != NULL) {
2767 GError *cursor_load_err = NULL;
2768 GdkPixbuf *cursor_pixbuf = gdk_pixbuf_from_pixdata (t->ti.cursor_data, FALSE, &cursor_load_err);
2769 /* TODO: settable offeset */
2770 t->ti.cursor = gdk_cursor_new_from_pixbuf ( gdk_display_get_default(), cursor_pixbuf, 3, 3 );
2771 g_object_unref ( G_OBJECT(cursor_pixbuf) );
2773 t->ti.cursor = gdk_cursor_new ( t->ti.cursor_type );
2776 return t->ti.cursor;
2779 static void toolbox_click (toolbox_tools_t *vt, GdkEventButton *event)
2781 VikLayer *vl = vik_layers_panel_get_selected ( vt->vw->viking_vlp );
2782 if (vt->active_tool != -1 && vt->tools[vt->active_tool].ti.click) {
2783 gint ltype = vt->tools[vt->active_tool].layer_type;
2784 if ( ltype == TOOL_LAYER_TYPE_NONE || (vl && ltype == vl->type) )
2785 vt->tools[vt->active_tool].ti.click(vl, event, vt->tools[vt->active_tool].state);
2789 static void toolbox_move (toolbox_tools_t *vt, GdkEventMotion *event)
2791 VikLayer *vl = vik_layers_panel_get_selected ( vt->vw->viking_vlp );
2792 if (vt->active_tool != -1 && vt->tools[vt->active_tool].ti.move) {
2793 gint ltype = vt->tools[vt->active_tool].layer_type;
2794 if ( ltype == TOOL_LAYER_TYPE_NONE || (vl && ltype == vl->type) )
2795 if ( VIK_LAYER_TOOL_ACK_GRAB_FOCUS == vt->tools[vt->active_tool].ti.move(vl, event, vt->tools[vt->active_tool].state) )
2796 gtk_widget_grab_focus ( GTK_WIDGET(vt->vw->viking_vvp) );
2800 static void toolbox_release (toolbox_tools_t *vt, GdkEventButton *event)
2802 VikLayer *vl = vik_layers_panel_get_selected ( vt->vw->viking_vlp );
2803 if (vt->active_tool != -1 && vt->tools[vt->active_tool].ti.release ) {
2804 gint ltype = vt->tools[vt->active_tool].layer_type;
2805 if ( ltype == TOOL_LAYER_TYPE_NONE || (vl && ltype == vl->type) )
2806 vt->tools[vt->active_tool].ti.release(vl, event, vt->tools[vt->active_tool].state);
2809 /** End tool management ************************************/
2811 void vik_window_enable_layer_tool ( VikWindow *vw, gint layer_id, gint tool_id )
2813 gtk_action_activate ( gtk_action_group_get_action ( vw->action_group, vik_layer_get_interface(layer_id)->tools[tool_id].radioActionEntry.name ) );
2816 // Be careful with usage - as it may trigger actions being continually alternately by the menu and toolbar items
2817 // DON'T Use this from menu callback with toggle toolbar items!!
2818 static void toolbar_sync ( VikWindow *vw, const gchar *name, gboolean state )
2820 GtkToggleToolButton *tbutton = (GtkToggleToolButton *)toolbar_get_widget_by_name ( vw->viking_vtb, name );
2822 // Causes toggle signal action to be raised.
2823 gtk_toggle_tool_button_set_active ( tbutton, state );
2827 /* this function gets called whenever a menu is clicked */
2828 // Note old is not used
2829 static void menu_cb ( GtkAction *old, GtkAction *a, VikWindow *vw )
2831 // Ensure Toolbar kept in sync
2832 const gchar *name = gtk_action_get_name(a);
2833 toolbar_sync ( vw, name, TRUE );
2835 /* White Magic, my friends ... White Magic... */
2837 toolbox_activate(vw->vt, name);
2839 vw->viewport_cursor = (GdkCursor *)toolbox_get_cursor(vw->vt, name);
2841 if ( gtk_widget_get_window(GTK_WIDGET(vw->viking_vvp)) )
2842 /* We set cursor, even if it is NULL: it resets to default */
2843 gdk_window_set_cursor ( gtk_widget_get_window(GTK_WIDGET(vw->viking_vvp)), vw->viewport_cursor );
2845 if (!g_strcmp0(name, "Pan")) {
2846 vw->current_tool = TOOL_PAN;
2848 else if (!g_strcmp0(name, "Zoom")) {
2849 vw->current_tool = TOOL_ZOOM;
2851 else if (!g_strcmp0(name, "Ruler")) {
2852 vw->current_tool = TOOL_RULER;
2854 else if (!g_strcmp0(name, "Select")) {
2855 vw->current_tool = TOOL_SELECT;
2858 VikLayerTypeEnum layer_id;
2859 for (layer_id=0; layer_id<VIK_LAYER_NUM_TYPES; layer_id++) {
2860 for ( tool_id = 0; tool_id < vik_layer_get_interface(layer_id)->tools_count; tool_id++ ) {
2861 if (!g_strcmp0(vik_layer_get_interface(layer_id)->tools[tool_id].radioActionEntry.name, name)) {
2862 vw->current_tool = TOOL_LAYER;
2863 vw->tool_layer_id = layer_id;
2864 vw->tool_tool_id = tool_id;
2869 draw_status_tool ( vw );
2872 static void window_set_filename ( VikWindow *vw, const gchar *filename )
2877 g_free ( vw->filename );
2878 if ( filename == NULL )
2880 vw->filename = NULL;
2884 vw->filename = g_strdup(filename);
2887 /* Refresh window's title */
2888 file = window_get_filename ( vw );
2889 title = g_strdup_printf( "%s - Viking", file );
2890 gtk_window_set_title ( GTK_WINDOW(vw), title );
2894 static const gchar *window_get_filename ( VikWindow *vw )
2896 return vw->filename ? a_file_basename ( vw->filename ) : _("Untitled");
2899 GtkWidget *vik_window_get_drawmode_button ( VikWindow *vw, VikViewportDrawMode mode )
2901 GtkWidget *mode_button;
2904 #ifdef VIK_CONFIG_EXPEDIA
2905 case VIK_VIEWPORT_DRAWMODE_EXPEDIA: buttonname = "/ui/MainMenu/View/ModeExpedia"; break;
2907 case VIK_VIEWPORT_DRAWMODE_MERCATOR: buttonname = "/ui/MainMenu/View/ModeMercator"; break;
2908 case VIK_VIEWPORT_DRAWMODE_LATLON: buttonname = "/ui/MainMenu/View/ModeLatLon"; break;
2909 default: buttonname = "/ui/MainMenu/View/ModeUTM";
2911 mode_button = gtk_ui_manager_get_widget ( vw->uim, buttonname );
2912 g_assert ( mode_button );
2917 * vik_window_get_pan_move:
2918 * @vw: some VikWindow
2920 * Retrieves @vw's pan_move.
2922 * Should be removed as soon as possible.
2924 * Returns: @vw's pan_move
2928 gboolean vik_window_get_pan_move ( VikWindow *vw )
2930 return vw->pan_move;
2933 static void on_activate_recent_item (GtkRecentChooser *chooser,
2938 filename = gtk_recent_chooser_get_current_uri (chooser);
2939 if (filename != NULL)
2941 GFile *file = g_file_new_for_uri ( filename );
2942 gchar *path = g_file_get_path ( file );
2943 g_object_unref ( file );
2944 if ( self->filename )
2946 GSList *filenames = NULL;
2947 filenames = g_slist_append ( filenames, path );
2948 g_signal_emit ( G_OBJECT(self), window_signals[VW_OPENWINDOW_SIGNAL], 0, filenames );
2949 // NB: GSList & contents are freed by main.open_window
2952 vik_window_open_file ( self, path, TRUE );
2960 static void setup_recent_files (VikWindow *self)
2962 GtkRecentManager *manager;
2963 GtkRecentFilter *filter;
2964 GtkWidget *menu, *menu_item;
2966 filter = gtk_recent_filter_new ();
2967 /* gtk_recent_filter_add_application (filter, g_get_application_name()); */
2968 gtk_recent_filter_add_group(filter, "viking");
2970 manager = gtk_recent_manager_get_default ();
2971 menu = gtk_recent_chooser_menu_new_for_manager (manager);
2972 gtk_recent_chooser_set_sort_type (GTK_RECENT_CHOOSER (menu), GTK_RECENT_SORT_MRU);
2973 gtk_recent_chooser_add_filter (GTK_RECENT_CHOOSER (menu), filter);
2974 gtk_recent_chooser_set_limit (GTK_RECENT_CHOOSER (menu), a_vik_get_recent_number_files() );
2976 menu_item = gtk_ui_manager_get_widget (self->uim, "/ui/MainMenu/File/OpenRecentFile");
2977 gtk_menu_item_set_submenu (GTK_MENU_ITEM (menu_item), menu);
2979 g_signal_connect (G_OBJECT (menu), "item-activated",
2980 G_CALLBACK (on_activate_recent_item), (gpointer) self);
2986 static void update_recently_used_document (VikWindow *vw, const gchar *filename)
2988 /* Update Recently Used Document framework */
2989 GtkRecentManager *manager = gtk_recent_manager_get_default();
2990 GtkRecentData *recent_data = g_slice_new (GtkRecentData);
2991 gchar *groups[] = {"viking", NULL};
2992 GFile *file = g_file_new_for_commandline_arg(filename);
2993 gchar *uri = g_file_get_uri(file);
2994 gchar *basename = g_path_get_basename(filename);
2995 g_object_unref(file);
2998 recent_data->display_name = basename;
2999 recent_data->description = NULL;
3000 recent_data->mime_type = "text/x-gps-data";
3001 recent_data->app_name = (gchar *) g_get_application_name ();
3002 recent_data->app_exec = g_strjoin (" ", g_get_prgname (), "%f", NULL);
3003 recent_data->groups = groups;
3004 recent_data->is_private = FALSE;
3005 if (!gtk_recent_manager_add_full (manager, uri, recent_data))
3007 gchar *msg = g_strdup_printf (_("Unable to add '%s' to the list of recently used documents"), uri);
3008 vik_statusbar_set_message ( vw->viking_vs, VIK_STATUSBAR_INFO, msg );
3014 g_free (recent_data->app_exec);
3015 g_slice_free (GtkRecentData, recent_data);
3019 * Call this before doing things that may take a long time and otherwise not show any other feedback
3020 * such as loading and saving files
3022 void vik_window_set_busy_cursor ( VikWindow *vw )
3024 gdk_window_set_cursor ( gtk_widget_get_window(GTK_WIDGET(vw)), vw->busy_cursor );
3025 // Viewport has a separate cursor
3026 gdk_window_set_cursor ( gtk_widget_get_window(GTK_WIDGET(vw->viking_vvp)), vw->busy_cursor );
3027 // Ensure cursor updated before doing stuff
3028 while( gtk_events_pending() )
3029 gtk_main_iteration();
3032 void vik_window_clear_busy_cursor ( VikWindow *vw )
3034 gdk_window_set_cursor ( gtk_widget_get_window(GTK_WIDGET(vw)), NULL );
3035 // Restore viewport cursor
3036 gdk_window_set_cursor ( gtk_widget_get_window(GTK_WIDGET(vw->viking_vvp)), vw->viewport_cursor );
3039 void vik_window_open_file ( VikWindow *vw, const gchar *filename, gboolean change_filename )
3041 vik_window_set_busy_cursor ( vw );
3043 // Enable the *new* filename to be accessible by the Layers codez
3044 gchar *original_filename = g_strdup ( vw->filename );
3045 g_free ( vw->filename );
3046 vw->filename = g_strdup ( filename );
3047 gboolean success = FALSE;
3048 gboolean restore_original_filename = FALSE;
3050 vw->loaded_type = a_file_load ( vik_layers_panel_get_top_layer(vw->viking_vlp), vw->viking_vvp, filename );
3051 switch ( vw->loaded_type )
3053 case LOAD_TYPE_READ_FAILURE:
3054 a_dialog_error_msg ( GTK_WINDOW(vw), _("The file you requested could not be opened.") );
3056 case LOAD_TYPE_GPSBABEL_FAILURE:
3057 a_dialog_error_msg ( GTK_WINDOW(vw), _("GPSBabel is required to load files of this type or GPSBabel encountered problems.") );
3059 case LOAD_TYPE_GPX_FAILURE:
3060 a_dialog_error_msg_extra ( GTK_WINDOW(vw), _("Unable to load malformed GPX file %s"), filename );
3062 case LOAD_TYPE_UNSUPPORTED_FAILURE:
3063 a_dialog_error_msg_extra ( GTK_WINDOW(vw), _("Unsupported file type for %s"), filename );
3065 case LOAD_TYPE_VIK_FAILURE_NON_FATAL:
3067 // Since we can process .vik files with issues just show a warning in the status bar
3068 // Not that a user can do much about it... or tells them what this issue is yet...
3069 gchar *msg = g_strdup_printf (_("WARNING: issues encountered loading %s"), a_file_basename (filename) );
3070 vik_statusbar_set_message ( vw->viking_vs, VIK_STATUSBAR_INFO, msg );
3073 // No break, carry on to show any data
3074 case LOAD_TYPE_VIK_SUCCESS:
3076 restore_original_filename = TRUE; // NB Will actually get inverted by the 'success' component below
3077 GtkWidget *mode_button;
3079 if ( change_filename )
3080 window_set_filename ( vw, filename );
3081 mode_button = vik_window_get_drawmode_button ( vw, vik_viewport_get_drawmode ( vw->viking_vvp ) );
3082 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. */
3083 gtk_check_menu_item_set_active ( GTK_CHECK_MENU_ITEM(mode_button), TRUE );
3084 vw->only_updating_coord_mode_ui = FALSE;
3086 vik_layers_panel_change_coord_mode ( vw->viking_vlp, vik_viewport_get_coord_mode ( vw->viking_vvp ) );
3088 // Slightly long winded methods to align loaded viewport settings with the UI
3089 // Since the rewrite for toolbar + menu actions
3090 // there no longer exists a simple way to directly change the UI to a value for toggle settings
3091 // it only supports toggling the existing setting (otherwise get infinite loops in trying to align tb+menu elements)
3092 // Thus get state, compare them, if different then invert viewport setting and (re)sync the setting (via toggling)
3093 gboolean vp_state_scale = vik_viewport_get_draw_scale ( vw->viking_vvp );
3094 gboolean ui_state_scale = gtk_check_menu_item_get_active ( GTK_CHECK_MENU_ITEM(get_show_widget_by_name(vw, "ShowScale")) );
3095 if ( vp_state_scale != ui_state_scale ) {
3096 vik_viewport_set_draw_scale ( vw->viking_vvp, !vp_state_scale );
3097 toggle_draw_scale ( NULL, vw );
3099 gboolean vp_state_centermark = vik_viewport_get_draw_centermark ( vw->viking_vvp );
3100 gboolean ui_state_centermark = gtk_check_menu_item_get_active ( GTK_CHECK_MENU_ITEM(get_show_widget_by_name(vw, "ShowCenterMark")) );
3101 if ( vp_state_centermark != ui_state_centermark ) {
3102 vik_viewport_set_draw_centermark ( vw->viking_vvp, !vp_state_centermark );
3103 toggle_draw_centermark ( NULL, vw );
3105 gboolean vp_state_highlight = vik_viewport_get_draw_highlight ( vw->viking_vvp );
3106 gboolean ui_state_highlight = gtk_check_menu_item_get_active ( GTK_CHECK_MENU_ITEM(get_show_widget_by_name(vw, "ShowHighlight")) );
3107 if ( vp_state_highlight != ui_state_highlight ) {
3108 vik_viewport_set_draw_highlight ( vw->viking_vvp, !vp_state_highlight );
3109 toggle_draw_highlight ( NULL, vw );
3112 // NB No break, carry on to redraw
3113 //case LOAD_TYPE_OTHER_SUCCESS:
3116 // When LOAD_TYPE_OTHER_SUCCESS *only*, this will maintain the existing Viking project
3117 restore_original_filename = ! restore_original_filename;
3118 update_recently_used_document (vw, filename);
3123 if ( ! success || restore_original_filename )
3124 // Load didn't work or want to keep as the existing Viking project, keep using the original name
3125 window_set_filename ( vw, original_filename );
3126 g_free ( original_filename );
3128 vik_window_clear_busy_cursor ( vw );
3131 static void load_file ( GtkAction *a, VikWindow *vw )
3133 GSList *files = NULL;
3134 GSList *cur_file = NULL;
3136 if (!strcmp(gtk_action_get_name(a), "Open")) {
3139 else if (!strcmp(gtk_action_get_name(a), "Append")) {
3143 g_critical("Houston, we've had a problem.");
3147 if ( ! vw->open_dia )
3149 vw->open_dia = gtk_file_chooser_dialog_new (_("Please select a GPS data file to open. "),
3151 GTK_FILE_CHOOSER_ACTION_OPEN,
3152 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
3153 GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT,
3155 gchar *cwd = g_get_current_dir();
3157 gtk_file_chooser_set_current_folder ( GTK_FILE_CHOOSER(vw->open_dia), cwd );
3161 GtkFileFilter *filter;
3162 // NB file filters are listed this way for alphabetical ordering
3163 #ifdef VIK_CONFIG_GEOCACHES
3164 filter = gtk_file_filter_new ();
3165 gtk_file_filter_set_name( filter, _("Geocaching") );
3166 gtk_file_filter_add_pattern ( filter, "*.loc" ); // No MIME type available
3167 gtk_file_chooser_add_filter (GTK_FILE_CHOOSER(vw->open_dia), filter);
3170 filter = gtk_file_filter_new ();
3171 gtk_file_filter_set_name( filter, _("Google Earth") );
3172 gtk_file_filter_add_mime_type ( filter, "application/vnd.google-earth.kml+xml");
3173 gtk_file_chooser_add_filter (GTK_FILE_CHOOSER(vw->open_dia), filter);
3175 filter = gtk_file_filter_new ();
3176 gtk_file_filter_set_name( filter, _("GPX") );
3177 gtk_file_filter_add_pattern ( filter, "*.gpx" ); // No MIME type available
3178 gtk_file_chooser_add_filter (GTK_FILE_CHOOSER(vw->open_dia), filter);
3180 filter = gtk_file_filter_new ();
3181 gtk_file_filter_set_name ( filter, _("JPG") );
3182 gtk_file_filter_add_mime_type ( filter, "image/jpeg");
3183 gtk_file_chooser_add_filter (GTK_FILE_CHOOSER(vw->open_dia), filter);
3185 filter = gtk_file_filter_new ();
3186 gtk_file_filter_set_name( filter, _("Viking") );
3187 gtk_file_filter_add_pattern ( filter, "*.vik" );
3188 gtk_file_filter_add_pattern ( filter, "*.viking" );
3189 gtk_file_chooser_add_filter (GTK_FILE_CHOOSER(vw->open_dia), filter);
3191 // NB could have filters for gpspoint (*.gps,*.gpsoint?) + gpsmapper (*.gsm,*.gpsmapper?)
3192 // However assume this are barely used and thus not worthy of inclusion
3193 // as they'll just make the options too many and have no clear file pattern
3194 // one can always use the all option
3195 filter = gtk_file_filter_new ();
3196 gtk_file_filter_set_name( filter, _("All") );
3197 gtk_file_filter_add_pattern ( filter, "*" );
3198 gtk_file_chooser_add_filter (GTK_FILE_CHOOSER(vw->open_dia), filter);
3199 // Default to any file - same as before open filters were added
3200 gtk_file_chooser_set_filter (GTK_FILE_CHOOSER(vw->open_dia), filter);
3202 gtk_file_chooser_set_select_multiple ( GTK_FILE_CHOOSER(vw->open_dia), TRUE );
3203 gtk_window_set_transient_for ( GTK_WINDOW(vw->open_dia), GTK_WINDOW(vw) );
3204 gtk_window_set_destroy_with_parent ( GTK_WINDOW(vw->open_dia), TRUE );
3206 if ( gtk_dialog_run ( GTK_DIALOG(vw->open_dia) ) == GTK_RESPONSE_ACCEPT )
3208 gtk_widget_hide ( vw->open_dia );
3209 #ifdef VIKING_PROMPT_IF_MODIFIED
3210 if ( (vw->modified || vw->filename) && newwindow )
3212 if ( vw->filename && newwindow )
3214 g_signal_emit ( G_OBJECT(vw), window_signals[VW_OPENWINDOW_SIGNAL], 0, gtk_file_chooser_get_filenames (GTK_FILE_CHOOSER(vw->open_dia) ) );
3216 files = gtk_file_chooser_get_filenames (GTK_FILE_CHOOSER(vw->open_dia) );
3217 gboolean change_fn = newwindow && (g_slist_length(files)==1); /* only change fn if one file */
3218 gboolean first_vik_file = TRUE;
3220 while ( cur_file ) {
3222 gchar *file_name = cur_file->data;
3223 if ( newwindow && check_file_magic_vik ( file_name ) ) {
3224 // Load first of many .vik files in current window
3225 if ( first_vik_file ) {
3226 vik_window_open_file ( vw, file_name, TRUE );
3227 first_vik_file = FALSE;
3230 // Load each subsequent .vik file in a separate window
3231 VikWindow *newvw = vik_window_new_window ();
3233 vik_window_open_file ( newvw, file_name, TRUE );
3238 vik_window_open_file ( vw, file_name, change_fn );
3241 cur_file = g_slist_next (cur_file);
3243 g_slist_free (files);
3247 gtk_widget_hide ( vw->open_dia );
3250 static gboolean save_file_as ( GtkAction *a, VikWindow *vw )
3252 gboolean rv = FALSE;
3254 if ( ! vw->save_dia )
3256 vw->save_dia = gtk_file_chooser_dialog_new (_("Save as Viking File."),
3258 GTK_FILE_CHOOSER_ACTION_SAVE,
3259 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
3260 GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT,
3262 gchar *cwd = g_get_current_dir();
3264 gtk_file_chooser_set_current_folder ( GTK_FILE_CHOOSER(vw->save_dia), cwd );
3268 GtkFileFilter *filter;
3269 filter = gtk_file_filter_new ();
3270 gtk_file_filter_set_name( filter, _("All") );
3271 gtk_file_filter_add_pattern ( filter, "*" );
3272 gtk_file_chooser_add_filter (GTK_FILE_CHOOSER(vw->save_dia), filter);
3274 filter = gtk_file_filter_new ();
3275 gtk_file_filter_set_name( filter, _("Viking") );
3276 gtk_file_filter_add_pattern ( filter, "*.vik" );
3277 gtk_file_filter_add_pattern ( filter, "*.viking" );
3278 gtk_file_chooser_add_filter (GTK_FILE_CHOOSER(vw->save_dia), filter);
3279 // Default to a Viking file
3280 gtk_file_chooser_set_filter (GTK_FILE_CHOOSER(vw->save_dia), filter);
3282 gtk_window_set_transient_for ( GTK_WINDOW(vw->save_dia), GTK_WINDOW(vw) );
3283 gtk_window_set_destroy_with_parent ( GTK_WINDOW(vw->save_dia), TRUE );
3285 // Auto append / replace extension with '.vik' to the suggested file name as it's going to be a Viking File
3286 gchar* auto_save_name = g_strdup ( window_get_filename ( vw ) );
3287 if ( ! a_file_check_ext ( auto_save_name, ".vik" ) )
3288 auto_save_name = g_strconcat ( auto_save_name, ".vik", NULL );
3290 gtk_file_chooser_set_current_name (GTK_FILE_CHOOSER(vw->save_dia), auto_save_name);
3292 while ( gtk_dialog_run ( GTK_DIALOG(vw->save_dia) ) == GTK_RESPONSE_ACCEPT )
3294 fn = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER(vw->save_dia) );
3295 if ( g_file_test ( fn, G_FILE_TEST_EXISTS ) == FALSE || a_dialog_yes_or_no ( GTK_WINDOW(vw->save_dia), _("The file \"%s\" exists, do you wish to overwrite it?"), a_file_basename ( fn ) ) )
3297 window_set_filename ( vw, fn );
3298 rv = window_save ( vw );
3299 vw->modified = FALSE;
3303 g_free ( auto_save_name );
3304 gtk_widget_hide ( vw->save_dia );
3308 static gboolean window_save ( VikWindow *vw )
3310 vik_window_set_busy_cursor ( vw );
3311 gboolean success = TRUE;
3313 if ( a_file_save ( vik_layers_panel_get_top_layer ( vw->viking_vlp ), vw->viking_vvp, vw->filename ) )
3315 update_recently_used_document ( vw, vw->filename );
3319 a_dialog_error_msg ( GTK_WINDOW(vw), _("The filename you requested could not be opened for writing.") );
3322 vik_window_clear_busy_cursor ( vw );
3326 static gboolean save_file ( GtkAction *a, VikWindow *vw )
3328 if ( ! vw->filename )
3329 return save_file_as ( NULL, vw );
3332 vw->modified = FALSE;
3333 return window_save ( vw );
3340 * Export all TRW Layers in the list to individual files in the specified directory
3342 * Returns: %TRUE on success
3344 static gboolean export_to ( VikWindow *vw, GList *gl, VikFileType_t vft, const gchar *dir, const gchar *extension )
3346 gboolean success = TRUE;
3348 gint export_count = 0;
3350 vik_window_set_busy_cursor ( vw );
3354 gchar *fn = g_strconcat ( dir, G_DIR_SEPARATOR_S, VIK_LAYER(gl->data)->name, extension, NULL );
3356 // Some protection in attempting to write too many same named files
3357 // As this will get horribly slow...
3358 gboolean safe = FALSE;
3360 while ( ii < 5000 ) {
3361 if ( g_file_test ( fn, G_FILE_TEST_EXISTS ) ) {
3364 fn = g_strdup_printf ( "%s%s%s#%03d%s", dir, G_DIR_SEPARATOR_S, VIK_LAYER(gl->data)->name, ii, extension );
3375 // NB: We allow exporting empty layers
3377 gboolean this_success = a_file_export ( VIK_TRW_LAYER(gl->data), fn, vft, NULL, TRUE );
3379 // Show some progress
3380 if ( this_success ) {
3382 gchar *message = g_strdup_printf ( _("Exporting to file: %s"), fn );
3383 vik_statusbar_set_message ( vw->viking_vs, VIK_STATUSBAR_INFO, message );
3384 while ( gtk_events_pending() )
3385 gtk_main_iteration ();
3389 success = success && this_success;
3393 gl = g_list_next ( gl );
3396 vik_window_clear_busy_cursor ( vw );
3398 // Confirm what happened.
3399 gchar *message = g_strdup_printf ( _("Exported files: %d"), export_count );
3400 vik_statusbar_set_message ( vw->viking_vs, VIK_STATUSBAR_INFO, message );
3406 static void export_to_common ( VikWindow *vw, VikFileType_t vft, const gchar *extension )
3408 GList *gl = vik_layers_panel_get_all_layers_of_type ( vw->viking_vlp, VIK_LAYER_TRW, TRUE );
3411 a_dialog_info_msg ( GTK_WINDOW(vw), _("Nothing to Export!") );
3415 GtkWidget *dialog = gtk_file_chooser_dialog_new ( _("Export to directory"),
3417 GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER,
3419 GTK_RESPONSE_REJECT,
3421 GTK_RESPONSE_ACCEPT,
3423 gtk_window_set_transient_for ( GTK_WINDOW(dialog), GTK_WINDOW(vw) );
3424 gtk_window_set_destroy_with_parent ( GTK_WINDOW(dialog), TRUE );
3425 gtk_window_set_modal ( GTK_WINDOW(dialog), TRUE );
3427 gtk_widget_show_all ( dialog );
3429 if ( gtk_dialog_run ( GTK_DIALOG(dialog) ) == GTK_RESPONSE_ACCEPT ) {
3430 gchar *dir = gtk_file_chooser_get_filename ( GTK_FILE_CHOOSER(dialog) );
3431 gtk_widget_destroy ( dialog );
3433 if ( !export_to ( vw, gl, vft, dir, extension ) )
3434 a_dialog_error_msg ( GTK_WINDOW(vw),_("Could not convert all files") );
3439 gtk_widget_destroy ( dialog );
3444 static void export_to_gpx ( GtkAction *a, VikWindow *vw )
3446 export_to_common ( vw, FILE_TYPE_GPX, ".gpx" );
3449 static void export_to_kml ( GtkAction *a, VikWindow *vw )
3451 export_to_common ( vw, FILE_TYPE_KML, ".kml" );
3454 #if !GLIB_CHECK_VERSION(2,26,0)
3455 typedef struct stat GStatBuf;
3458 static void file_properties_cb ( GtkAction *a, VikWindow *vw )
3460 gchar *message = NULL;
3461 if ( vw->filename ) {
3462 if ( g_file_test ( vw->filename, G_FILE_TEST_EXISTS ) ) {
3463 // Get some timestamp information of the file
3465 if ( g_stat ( vw->filename, &stat_buf ) == 0 ) {
3467 strftime ( time_buf, sizeof(time_buf), "%c", gmtime((const time_t *)&stat_buf.st_mtime) );
3469 gint byte_size = stat_buf.st_size;
3470 #if GLIB_CHECK_VERSION(2,30,0)
3471 size = g_format_size_full ( byte_size, G_FORMAT_SIZE_DEFAULT );
3473 size = g_format_size_for_display ( byte_size );
3475 message = g_strdup_printf ( "%s\n\n%s\n\n%s", vw->filename, time_buf, size );
3480 message = g_strdup ( _("File not accessible") );
3483 message = g_strdup ( _("No Viking File") );
3486 a_dialog_info_msg ( GTK_WINDOW(vw), message );
3490 static void my_acquire ( VikWindow *vw, VikDataSourceInterface *datasource )
3492 vik_datasource_mode_t mode = datasource->mode;
3493 if ( mode == VIK_DATASOURCE_AUTO_LAYER_MANAGEMENT )
3494 mode = VIK_DATASOURCE_CREATENEWLAYER;
3495 a_acquire ( vw, vw->viking_vlp, vw->viking_vvp, mode, datasource, NULL, NULL );
3498 static void acquire_from_gps ( GtkAction *a, VikWindow *vw )
3500 my_acquire ( vw, &vik_datasource_gps_interface );
3503 static void acquire_from_file ( GtkAction *a, VikWindow *vw )
3505 my_acquire ( vw, &vik_datasource_file_interface );
3508 static void acquire_from_geojson ( GtkAction *a, VikWindow *vw )
3510 my_acquire ( vw, &vik_datasource_geojson_interface );
3513 static void acquire_from_routing ( GtkAction *a, VikWindow *vw )
3515 my_acquire ( vw, &vik_datasource_routing_interface );
3518 #ifdef VIK_CONFIG_OPENSTREETMAP
3519 static void acquire_from_osm ( GtkAction *a, VikWindow *vw )
3521 my_acquire ( vw, &vik_datasource_osm_interface );
3524 static void acquire_from_my_osm ( GtkAction *a, VikWindow *vw )
3526 my_acquire ( vw, &vik_datasource_osm_my_traces_interface );
3530 #ifdef VIK_CONFIG_GEOCACHES
3531 static void acquire_from_gc ( GtkAction *a, VikWindow *vw )
3533 my_acquire ( vw, &vik_datasource_gc_interface );
3537 #ifdef VIK_CONFIG_GEOTAG
3538 static void acquire_from_geotag ( GtkAction *a, VikWindow *vw )
3540 my_acquire ( vw, &vik_datasource_geotag_interface );
3544 #ifdef VIK_CONFIG_GEONAMES
3545 static void acquire_from_wikipedia ( GtkAction *a, VikWindow *vw )
3547 my_acquire ( vw, &vik_datasource_wikipedia_interface );
3551 static void acquire_from_url ( GtkAction *a, VikWindow *vw )
3553 my_acquire ( vw, &vik_datasource_url_interface );
3556 static void goto_default_location( GtkAction *a, VikWindow *vw)
3559 ll.lat = a_vik_get_default_lat();
3560 ll.lon = a_vik_get_default_long();
3561 vik_viewport_set_center_latlon(vw->viking_vvp, &ll, TRUE);
3562 vik_layers_panel_emit_update(vw->viking_vlp);
3566 static void goto_address( GtkAction *a, VikWindow *vw)
3568 a_vik_goto ( vw, vw->viking_vvp );
3569 vik_layers_panel_emit_update ( vw->viking_vlp );
3572 static void mapcache_flush_cb ( GtkAction *a, VikWindow *vw )
3577 static void menu_copy_centre_cb ( GtkAction *a, VikWindow *vw )
3579 const VikCoord* coord;
3581 gchar *lat = NULL, *lon = NULL;
3583 coord = vik_viewport_get_center ( vw->viking_vvp );
3584 vik_coord_to_utm ( coord, &utm );
3586 gboolean full_format = FALSE;
3587 a_settings_get_boolean ( VIK_SETTINGS_WIN_COPY_CENTRE_FULL_FORMAT, &full_format );
3590 // Bells & Whistles - may include degrees, minutes and second symbols
3591 get_location_strings ( vw, utm, &lat, &lon );
3593 // Simple x.xx y.yy format
3595 a_coords_utm_to_latlon ( &utm, &ll );
3596 lat = g_strdup_printf ( "%.6f", ll.lat );
3597 lon = g_strdup_printf ( "%.6f", ll.lon );
3600 gchar *msg = g_strdup_printf ( "%s %s", lat, lon );
3604 a_clipboard_copy ( VIK_CLIPBOARD_DATA_TEXT, 0, 0, 0, msg, NULL );
3609 static void layer_defaults_cb ( GtkAction *a, VikWindow *vw )
3611 gchar **texts = g_strsplit ( gtk_action_get_name(a), "Layer", 0 );
3614 return; // Internally broken :(
3616 if ( ! a_layer_defaults_show_window ( GTK_WINDOW(vw), texts[1] ) )
3617 a_dialog_info_msg ( GTK_WINDOW(vw), _("This layer has no configurable properties.") );
3618 // NB no update needed
3620 g_strfreev ( texts );
3623 static void preferences_change_update ( VikWindow *vw, gpointer data )
3625 // Want to update all TrackWaypoint layers
3626 GList *layers = vik_layers_panel_get_all_layers_of_type ( vw->viking_vlp, VIK_LAYER_TRW, TRUE );
3632 // Reset the individual waypoints themselves due to the preferences change
3633 VikTrwLayer *vtl = VIK_TRW_LAYER(layers->data);
3634 vik_trw_layer_reset_waypoints ( vtl );
3635 layers = g_list_next ( layers );
3638 g_list_free ( layers );
3643 static void preferences_cb ( GtkAction *a, VikWindow *vw )
3645 gboolean wp_icon_size = a_vik_get_use_large_waypoint_icons();
3647 a_preferences_show_window ( GTK_WINDOW(vw) );
3649 // Has the waypoint size setting changed?
3650 if (wp_icon_size != a_vik_get_use_large_waypoint_icons()) {
3651 // Delete icon indexing 'cache' and so automatically regenerates with the new setting when changed
3652 clear_garmin_icon_syms ();
3654 // Update all windows
3655 g_slist_foreach ( window_list, (GFunc) preferences_change_update, NULL );
3658 // Ensure TZ Lookup initialized
3659 if ( a_vik_get_time_ref_frame() == VIK_TIME_REF_WORLD )
3660 vu_setup_lat_lon_tz_lookup();
3662 toolbar_apply_settings ( vw->viking_vtb, vw->main_vbox, vw->menu_hbox, TRUE );
3665 static void default_location_cb ( GtkAction *a, VikWindow *vw )
3667 /* Simplistic repeat of preference setting
3668 Only the name & type are important for setting the preference via this 'external' way */
3669 VikLayerParam pref_lat[] = {
3670 { VIK_LAYER_NUM_TYPES,
3671 VIKING_PREFERENCES_NAMESPACE "default_latitude",
3672 VIK_LAYER_PARAM_DOUBLE,
3675 VIK_LAYER_WIDGET_SPINBUTTON,
3684 VikLayerParam pref_lon[] = {
3685 { VIK_LAYER_NUM_TYPES,
3686 VIKING_PREFERENCES_NAMESPACE "default_longitude",
3687 VIK_LAYER_PARAM_DOUBLE,
3690 VIK_LAYER_WIDGET_SPINBUTTON,
3700 /* Get current center */
3702 vik_coord_to_latlon ( vik_viewport_get_center ( vw->viking_vvp ), &ll );
3704 /* Apply to preferences */
3705 VikLayerParamData vlp_data;
3706 vlp_data.d = ll.lat;
3707 a_preferences_run_setparam (vlp_data, pref_lat);
3708 vlp_data.d = ll.lon;
3709 a_preferences_run_setparam (vlp_data, pref_lon);
3710 /* Remember to save */
3711 a_preferences_save_to_file();
3714 static void clear_cb ( GtkAction *a, VikWindow *vw )
3716 vik_layers_panel_clear ( vw->viking_vlp );
3717 window_set_filename ( vw, NULL );
3721 static void window_close ( GtkAction *a, VikWindow *vw )
3723 if ( ! delete_event ( vw ) )
3724 gtk_widget_destroy ( GTK_WIDGET(vw) );
3727 static gboolean save_file_and_exit ( GtkAction *a, VikWindow *vw )
3729 if (save_file( NULL, vw)) {
3730 window_close( NULL, vw);
3737 static void zoom_to_cb ( GtkAction *a, VikWindow *vw )
3739 gdouble xmpp = vik_viewport_get_xmpp ( vw->viking_vvp ), ympp = vik_viewport_get_ympp ( vw->viking_vvp );
3740 if ( a_dialog_custom_zoom ( GTK_WINDOW(vw), &xmpp, &ympp ) )
3742 vik_viewport_set_xmpp ( vw->viking_vvp, xmpp );
3743 vik_viewport_set_ympp ( vw->viking_vvp, ympp );
3748 static void save_image_file ( VikWindow *vw, const gchar *fn, guint w, guint h, gdouble zoom, gboolean save_as_png )
3750 /* more efficient way: stuff draws directly to pixbuf (fork viewport) */
3751 GdkPixbuf *pixbuf_to_save;
3752 gdouble old_xmpp, old_ympp;
3753 GError *error = NULL;
3755 GtkWidget *msgbox = gtk_message_dialog_new ( GTK_WINDOW(vw),
3756 GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
3759 _("Generating image file...") );
3761 g_signal_connect_swapped (msgbox, "response", G_CALLBACK (gtk_widget_destroy), msgbox);
3762 // Ensure dialog shown
3763 gtk_widget_show_all ( msgbox );
3765 vik_statusbar_set_message ( vw->viking_vs, VIK_STATUSBAR_INFO, _("Generating image file...") );
3766 while ( gtk_events_pending() )
3767 gtk_main_iteration ();
3768 // Despite many efforts & variations, GTK on my Linux system doesn't show the actual msgbox contents :(
3769 // At least the empty box can give a clue something's going on + the statusbar msg...
3770 // Windows version under Wine OK!
3772 /* backup old zoom & set new */
3773 old_xmpp = vik_viewport_get_xmpp ( vw->viking_vvp );
3774 old_ympp = vik_viewport_get_ympp ( vw->viking_vvp );
3775 vik_viewport_set_zoom ( vw->viking_vvp, zoom );
3777 /* reset width and height: */
3778 vik_viewport_configure_manually ( vw->viking_vvp, w, h );
3780 /* draw all layers */
3783 /* save buffer as file. */
3784 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);
3785 if ( !pixbuf_to_save ) {
3786 g_warning("Failed to generate internal pixmap size: %d x %d", w, h);
3787 gtk_message_dialog_set_markup ( GTK_MESSAGE_DIALOG(msgbox), _("Failed to generate internal image.\n\nTry creating a smaller image.") );
3791 gdk_pixbuf_save ( pixbuf_to_save, fn, save_as_png ? "png" : "jpeg", &error, NULL );
3794 g_warning("Unable to write to file %s: %s", fn, error->message );
3795 gtk_message_dialog_set_markup ( GTK_MESSAGE_DIALOG(msgbox), _("Failed to generate image file.") );
3796 g_error_free (error);
3800 gtk_message_dialog_set_markup ( GTK_MESSAGE_DIALOG(msgbox), _("Image file generated.") );
3802 g_object_unref ( G_OBJECT(pixbuf_to_save) );
3805 vik_statusbar_set_message ( vw->viking_vs, VIK_STATUSBAR_INFO, "" );
3806 gtk_dialog_add_button ( GTK_DIALOG(msgbox), GTK_STOCK_OK, GTK_RESPONSE_OK );
3807 gtk_dialog_run ( GTK_DIALOG(msgbox) ); // Don't care about the result
3809 /* pretend like nothing happened ;) */
3810 vik_viewport_set_xmpp ( vw->viking_vvp, old_xmpp );
3811 vik_viewport_set_ympp ( vw->viking_vvp, old_ympp );
3812 vik_viewport_configure ( vw->viking_vvp );
3816 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 )
3818 gulong size = sizeof(gchar) * (strlen(fn) + 15);
3819 gchar *name_of_file = g_malloc ( size );
3821 struct UTM utm_orig, utm;
3823 /* *** copied from above *** */
3824 GdkPixbuf *pixbuf_to_save;
3825 gdouble old_xmpp, old_ympp;
3826 GError *error = NULL;
3828 /* backup old zoom & set new */
3829 old_xmpp = vik_viewport_get_xmpp ( vw->viking_vvp );
3830 old_ympp = vik_viewport_get_ympp ( vw->viking_vvp );
3831 vik_viewport_set_zoom ( vw->viking_vvp, zoom );
3833 /* reset width and height: do this only once for all images (same size) */
3834 vik_viewport_configure_manually ( vw->viking_vvp, w, h );
3835 /* *** end copy from above *** */
3837 g_assert ( vik_viewport_get_coord_mode ( vw->viking_vvp ) == VIK_COORD_UTM );
3841 utm_orig = *((const struct UTM *)vik_viewport_get_center ( vw->viking_vvp ));
3843 for ( y = 1; y <= tiles_h; y++ )
3845 for ( x = 1; x <= tiles_w; x++ )
3847 g_snprintf ( name_of_file, size, "%s%cy%d-x%d.%s", fn, G_DIR_SEPARATOR, y, x, save_as_png ? "png" : "jpg" );
3849 if ( tiles_w & 0x1 )
3850 utm.easting += ((gdouble)x - ceil(((gdouble)tiles_w)/2)) * (w*zoom);
3852 utm.easting += ((gdouble)x - (((gdouble)tiles_w)+1)/2) * (w*zoom);
3853 if ( tiles_h & 0x1 ) /* odd */
3854 utm.northing -= ((gdouble)y - ceil(((gdouble)tiles_h)/2)) * (h*zoom);
3856 utm.northing -= ((gdouble)y - (((gdouble)tiles_h)+1)/2) * (h*zoom);
3858 /* move to correct place. */
3859 vik_viewport_set_center_utm ( vw->viking_vvp, &utm, FALSE );
3863 /* save buffer as file. */
3864 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);
3865 gdk_pixbuf_save ( pixbuf_to_save, name_of_file, save_as_png ? "png" : "jpeg", &error, NULL );
3868 gchar *msg = g_strdup_printf (_("Unable to write to file %s: %s"), name_of_file, error->message );
3869 vik_statusbar_set_message ( vw->viking_vs, VIK_STATUSBAR_INFO, msg );
3871 g_error_free (error);
3874 g_object_unref ( G_OBJECT(pixbuf_to_save) );
3878 vik_viewport_set_center_utm ( vw->viking_vvp, &utm_orig, FALSE );
3879 vik_viewport_set_xmpp ( vw->viking_vvp, old_xmpp );
3880 vik_viewport_set_ympp ( vw->viking_vvp, old_ympp );
3881 vik_viewport_configure ( vw->viking_vvp );
3884 g_free ( name_of_file );
3887 static void draw_to_image_file_current_window_cb(GtkWidget* widget,GdkEventButton *event,gpointer *pass_along)
3889 VikWindow *vw = VIK_WINDOW(pass_along[0]);
3890 GtkSpinButton *width_spin = GTK_SPIN_BUTTON(pass_along[1]), *height_spin = GTK_SPIN_BUTTON(pass_along[2]);
3892 gint active = gtk_combo_box_get_active ( GTK_COMBO_BOX(pass_along[3]) );
3893 gdouble zoom = pow (2, active-2 );
3895 gdouble width_min, width_max, height_min, height_max;
3898 gtk_spin_button_get_range ( width_spin, &width_min, &width_max );
3899 gtk_spin_button_get_range ( height_spin, &height_min, &height_max );
3901 /* TODO: support for xzoom and yzoom values */
3902 width = vik_viewport_get_width ( vw->viking_vvp ) * vik_viewport_get_xmpp ( vw->viking_vvp ) / zoom;
3903 height = vik_viewport_get_height ( vw->viking_vvp ) * vik_viewport_get_xmpp ( vw->viking_vvp ) / zoom;
3905 if ( width > width_max || width < width_min || height > height_max || height < height_min )
3906 a_dialog_info_msg ( GTK_WINDOW(vw), _("Viewable region outside allowable pixel size bounds for image. Clipping width/height values.") );
3908 gtk_spin_button_set_value ( width_spin, width );
3909 gtk_spin_button_set_value ( height_spin, height );
3912 static void draw_to_image_file_total_area_cb (GtkSpinButton *spinbutton, gpointer *pass_along)
3914 GtkSpinButton *width_spin = GTK_SPIN_BUTTON(pass_along[1]), *height_spin = GTK_SPIN_BUTTON(pass_along[2]);
3916 gint active = gtk_combo_box_get_active ( GTK_COMBO_BOX(pass_along[3]) );
3917 gdouble zoom = pow (2, active-2 );
3921 w = gtk_spin_button_get_value(width_spin) * zoom;
3922 h = gtk_spin_button_get_value(height_spin) * zoom;
3923 if (pass_along[4]) /* save many images; find TOTAL area covered */
3925 w *= gtk_spin_button_get_value(GTK_SPIN_BUTTON(pass_along[4]));
3926 h *= gtk_spin_button_get_value(GTK_SPIN_BUTTON(pass_along[5]));
3928 vik_units_distance_t dist_units = a_vik_get_units_distance ();
3929 switch (dist_units) {
3930 case VIK_UNITS_DISTANCE_KILOMETRES:
3931 label_text = g_strdup_printf ( _("Total area: %ldm x %ldm (%.3f sq. km)"), (glong)w, (glong)h, (w*h/1000000));
3933 case VIK_UNITS_DISTANCE_MILES:
3934 label_text = g_strdup_printf ( _("Total area: %ldm x %ldm (%.3f sq. miles)"), (glong)w, (glong)h, (w*h/2589988.11));
3936 case VIK_UNITS_DISTANCE_NAUTICAL_MILES:
3937 label_text = g_strdup_printf ( _("Total area: %ldm x %ldm (%.3f sq. NM)"), (glong)w, (glong)h, (w*h/(1852.0*1852.0)));
3940 label_text = g_strdup_printf ("Just to keep the compiler happy");
3941 g_critical("Houston, we've had a problem. distance=%d", dist_units);
3944 gtk_label_set_text(GTK_LABEL(pass_along[6]), label_text);
3945 g_free ( label_text );
3949 * Get an allocated filename (or directory as specified)
3951 static gchar* draw_image_filename ( VikWindow *vw, gboolean one_image_only )
3954 if ( one_image_only )
3957 if (!vw->save_img_dia) {
3958 vw->save_img_dia = gtk_file_chooser_dialog_new (_("Save Image"),
3960 GTK_FILE_CHOOSER_ACTION_SAVE,
3961 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
3962 GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT,
3965 gchar *cwd = g_get_current_dir();
3967 gtk_file_chooser_set_current_folder ( GTK_FILE_CHOOSER(vw->save_img_dia), cwd );
3971 GtkFileChooser *chooser = GTK_FILE_CHOOSER ( vw->save_img_dia );
3973 GtkFileFilter *filter;
3974 filter = gtk_file_filter_new ();
3975 gtk_file_filter_set_name ( filter, _("All") );
3976 gtk_file_filter_add_pattern ( filter, "*" );
3977 gtk_file_chooser_add_filter ( chooser, filter );
3979 filter = gtk_file_filter_new ();
3980 gtk_file_filter_set_name ( filter, _("JPG") );
3981 gtk_file_filter_add_mime_type ( filter, "image/jpeg");
3982 gtk_file_chooser_add_filter ( chooser, filter );
3984 if ( !vw->draw_image_save_as_png )
3985 gtk_file_chooser_set_filter ( chooser, filter );
3987 filter = gtk_file_filter_new ();
3988 gtk_file_filter_set_name ( filter, _("PNG") );
3989 gtk_file_filter_add_mime_type ( filter, "image/png");
3990 gtk_file_chooser_add_filter ( chooser, filter );
3992 if ( vw->draw_image_save_as_png )
3993 gtk_file_chooser_set_filter ( chooser, filter );
3995 gtk_window_set_transient_for ( GTK_WINDOW(vw->save_img_dia), GTK_WINDOW(vw) );
3996 gtk_window_set_destroy_with_parent ( GTK_WINDOW(vw->save_img_dia), TRUE );
3999 if ( gtk_dialog_run ( GTK_DIALOG(vw->save_img_dia) ) == GTK_RESPONSE_ACCEPT ) {
4000 fn = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER(vw->save_img_dia) );
4001 if ( g_file_test ( fn, G_FILE_TEST_EXISTS ) )
4002 if ( ! a_dialog_yes_or_no ( GTK_WINDOW(vw->save_img_dia), _("The file \"%s\" exists, do you wish to overwrite it?"), a_file_basename ( fn ) ) )
4005 gtk_widget_hide ( vw->save_img_dia );
4009 // For some reason this method is only written to work in UTM...
4010 if ( vik_viewport_get_coord_mode(vw->viking_vvp) != VIK_COORD_UTM ) {
4011 a_dialog_error_msg ( GTK_WINDOW(vw), _("You must be in UTM mode to use this feature") );
4015 if (!vw->save_img_dir_dia) {
4016 vw->save_img_dir_dia = gtk_file_chooser_dialog_new (_("Choose a directory to hold images"),
4018 GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER,
4019 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
4020 GTK_STOCK_OK, GTK_RESPONSE_ACCEPT,
4022 gtk_window_set_transient_for ( GTK_WINDOW(vw->save_img_dir_dia), GTK_WINDOW(vw) );
4023 gtk_window_set_destroy_with_parent ( GTK_WINDOW(vw->save_img_dir_dia), TRUE );
4026 if ( gtk_dialog_run ( GTK_DIALOG(vw->save_img_dir_dia) ) == GTK_RESPONSE_ACCEPT ) {
4027 fn = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER(vw->save_img_dir_dia) );
4029 gtk_widget_hide ( vw->save_img_dir_dia );
4034 static void draw_to_image_file ( VikWindow *vw, gboolean one_image_only )
4036 /* todo: default for answers inside VikWindow or static (thruout instance) */
4037 GtkWidget *dialog = gtk_dialog_new_with_buttons ( _("Save to Image File"), GTK_WINDOW(vw),
4038 GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
4040 GTK_RESPONSE_REJECT,
4042 GTK_RESPONSE_ACCEPT,
4044 GtkWidget *width_label, *width_spin, *height_label, *height_spin;
4045 GtkWidget *png_radio, *jpeg_radio;
4046 GtkWidget *current_window_button;
4047 gpointer current_window_pass_along[7];
4048 GtkWidget *zoom_label, *zoom_combo;
4049 GtkWidget *total_size_label;
4051 /* only used if (!one_image_only) */
4052 GtkWidget *tiles_width_spin = NULL, *tiles_height_spin = NULL;
4054 width_label = gtk_label_new ( _("Width (pixels):") );
4055 width_spin = gtk_spin_button_new ( GTK_ADJUSTMENT(gtk_adjustment_new ( vw->draw_image_width, 10, 50000, 10, 100, 0 )), 10, 0 );
4056 height_label = gtk_label_new ( _("Height (pixels):") );
4057 height_spin = gtk_spin_button_new ( GTK_ADJUSTMENT(gtk_adjustment_new ( vw->draw_image_height, 10, 50000, 10, 100, 0 )), 10, 0 );
4059 GtkWidget *win_warning_label = gtk_label_new ( _("WARNING: USING LARGE IMAGES OVER 10000x10000\nMAY CRASH THE PROGRAM!") );
4061 zoom_label = gtk_label_new ( _("Zoom (meters per pixel):") );
4062 /* TODO: separate xzoom and yzoom factors */
4063 zoom_combo = create_zoom_combo_all_levels();
4065 gdouble mpp = vik_viewport_get_xmpp(vw->viking_vvp);
4066 gint active = 2 + round ( log (mpp) / log (2) );
4068 // Can we not hard code size here?
4073 gtk_combo_box_set_active ( GTK_COMBO_BOX(zoom_combo), active );
4075 total_size_label = gtk_label_new ( NULL );
4077 current_window_button = gtk_button_new_with_label ( _("Area in current viewable window") );
4078 current_window_pass_along [0] = vw;
4079 current_window_pass_along [1] = width_spin;
4080 current_window_pass_along [2] = height_spin;
4081 current_window_pass_along [3] = zoom_combo;
4082 current_window_pass_along [4] = NULL; /* used for one_image_only != 1 */
4083 current_window_pass_along [5] = NULL;
4084 current_window_pass_along [6] = total_size_label;
4085 g_signal_connect ( G_OBJECT(current_window_button), "button_press_event", G_CALLBACK(draw_to_image_file_current_window_cb), current_window_pass_along );
4087 png_radio = gtk_radio_button_new_with_label ( NULL, _("Save as PNG") );
4088 jpeg_radio = gtk_radio_button_new_with_label_from_widget ( GTK_RADIO_BUTTON(png_radio), _("Save as JPEG") );
4090 gtk_box_pack_start (GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), png_radio, FALSE, FALSE, 0);
4091 gtk_box_pack_start (GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), jpeg_radio, FALSE, FALSE, 0);
4093 if ( ! vw->draw_image_save_as_png )
4094 gtk_toggle_button_set_active ( GTK_TOGGLE_BUTTON(jpeg_radio), TRUE );
4096 gtk_box_pack_start (GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), width_label, FALSE, FALSE, 0);
4097 gtk_box_pack_start (GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), width_spin, FALSE, FALSE, 0);
4098 gtk_box_pack_start (GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), height_label, FALSE, FALSE, 0);
4099 gtk_box_pack_start (GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), height_spin, FALSE, FALSE, 0);
4101 gtk_box_pack_start (GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), win_warning_label, FALSE, FALSE, 0);
4103 gtk_box_pack_start (GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), current_window_button, FALSE, FALSE, 0);
4104 gtk_box_pack_start (GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), zoom_label, FALSE, FALSE, 0);
4105 gtk_box_pack_start (GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), zoom_combo, FALSE, FALSE, 0);
4107 if ( ! one_image_only )
4109 GtkWidget *tiles_width_label, *tiles_height_label;
4111 tiles_width_label = gtk_label_new ( _("East-west image tiles:") );
4112 tiles_width_spin = gtk_spin_button_new ( GTK_ADJUSTMENT(gtk_adjustment_new ( 5, 1, 10, 1, 100, 0 )), 1, 0 );
4113 tiles_height_label = gtk_label_new ( _("North-south image tiles:") );
4114 tiles_height_spin = gtk_spin_button_new ( GTK_ADJUSTMENT(gtk_adjustment_new ( 5, 1, 10, 1, 100, 0 )), 1, 0 );
4115 gtk_box_pack_start (GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), tiles_width_label, FALSE, FALSE, 0);
4116 gtk_box_pack_start (GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), tiles_width_spin, FALSE, FALSE, 0);
4117 gtk_box_pack_start (GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), tiles_height_label, FALSE, FALSE, 0);
4118 gtk_box_pack_start (GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), tiles_height_spin, FALSE, FALSE, 0);
4120 current_window_pass_along [4] = tiles_width_spin;
4121 current_window_pass_along [5] = tiles_height_spin;
4122 g_signal_connect ( G_OBJECT(tiles_width_spin), "value-changed", G_CALLBACK(draw_to_image_file_total_area_cb), current_window_pass_along );
4123 g_signal_connect ( G_OBJECT(tiles_height_spin), "value-changed", G_CALLBACK(draw_to_image_file_total_area_cb), current_window_pass_along );
4125 gtk_box_pack_start (GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), total_size_label, FALSE, FALSE, 0);
4126 g_signal_connect ( G_OBJECT(width_spin), "value-changed", G_CALLBACK(draw_to_image_file_total_area_cb), current_window_pass_along );
4127 g_signal_connect ( G_OBJECT(height_spin), "value-changed", G_CALLBACK(draw_to_image_file_total_area_cb), current_window_pass_along );
4128 g_signal_connect ( G_OBJECT(zoom_combo), "changed", G_CALLBACK(draw_to_image_file_total_area_cb), current_window_pass_along );
4130 draw_to_image_file_total_area_cb ( NULL, current_window_pass_along ); /* set correct size info now */
4132 gtk_dialog_set_default_response ( GTK_DIALOG(dialog), GTK_RESPONSE_ACCEPT );
4134 gtk_widget_show_all ( gtk_dialog_get_content_area(GTK_DIALOG(dialog)) );
4136 if ( gtk_dialog_run ( GTK_DIALOG(dialog) ) == GTK_RESPONSE_ACCEPT )
4138 gtk_widget_hide ( GTK_WIDGET(dialog) );
4140 gchar *fn = draw_image_filename ( vw, one_image_only );
4144 gint active_z = gtk_combo_box_get_active ( GTK_COMBO_BOX(zoom_combo) );
4145 gdouble zoom = pow (2, active_z-2 );
4147 if ( one_image_only )
4148 save_image_file ( vw, fn,
4149 vw->draw_image_width = gtk_spin_button_get_value_as_int ( GTK_SPIN_BUTTON(width_spin) ),
4150 vw->draw_image_height = gtk_spin_button_get_value_as_int ( GTK_SPIN_BUTTON(height_spin) ),
4152 vw->draw_image_save_as_png = gtk_toggle_button_get_active ( GTK_TOGGLE_BUTTON(png_radio) ) );
4154 // NB is in UTM mode ATM
4155 save_image_dir ( vw, fn,
4156 vw->draw_image_width = gtk_spin_button_get_value_as_int ( GTK_SPIN_BUTTON(width_spin) ),
4157 vw->draw_image_height = gtk_spin_button_get_value_as_int ( GTK_SPIN_BUTTON(height_spin) ),
4159 vw->draw_image_save_as_png = gtk_toggle_button_get_active ( GTK_TOGGLE_BUTTON(png_radio) ),
4160 gtk_spin_button_get_value ( GTK_SPIN_BUTTON(tiles_width_spin) ),
4161 gtk_spin_button_get_value ( GTK_SPIN_BUTTON(tiles_height_spin) ) );
4166 gtk_widget_destroy ( GTK_WIDGET(dialog) );
4170 static void draw_to_image_file_cb ( GtkAction *a, VikWindow *vw )
4172 draw_to_image_file ( vw, TRUE );
4175 static void draw_to_image_dir_cb ( GtkAction *a, VikWindow *vw )
4177 draw_to_image_file ( vw, FALSE );
4180 static void print_cb ( GtkAction *a, VikWindow *vw )
4182 a_print(vw, vw->viking_vvp);
4185 /* really a misnomer: changes coord mode (actual coordinates) AND/OR draw mode (viewport only) */
4186 static void window_change_coord_mode_cb ( GtkAction *old_a, GtkAction *a, VikWindow *vw )
4188 const gchar *name = gtk_action_get_name(a);
4189 GtkToggleToolButton *tbutton = (GtkToggleToolButton *)toolbar_get_widget_by_name ( vw->viking_vtb, name );
4191 gtk_toggle_tool_button_set_active ( tbutton, TRUE );
4193 VikViewportDrawMode drawmode;
4194 if (!g_strcmp0(name, "ModeUTM")) {
4195 drawmode = VIK_VIEWPORT_DRAWMODE_UTM;
4197 else if (!g_strcmp0(name, "ModeLatLon")) {
4198 drawmode = VIK_VIEWPORT_DRAWMODE_LATLON;
4200 else if (!g_strcmp0(name, "ModeExpedia")) {
4201 drawmode = VIK_VIEWPORT_DRAWMODE_EXPEDIA;
4203 else if (!g_strcmp0(name, "ModeMercator")) {
4204 drawmode = VIK_VIEWPORT_DRAWMODE_MERCATOR;
4207 g_critical("Houston, we've had a problem.");
4211 if ( !vw->only_updating_coord_mode_ui )
4213 VikViewportDrawMode olddrawmode = vik_viewport_get_drawmode ( vw->viking_vvp );
4214 if ( olddrawmode != drawmode )
4216 /* this takes care of coord mode too */
4217 vik_viewport_set_drawmode ( vw->viking_vvp, drawmode );
4218 if ( drawmode == VIK_VIEWPORT_DRAWMODE_UTM ) {
4219 vik_layers_panel_change_coord_mode ( vw->viking_vlp, VIK_COORD_UTM );
4220 } else if ( olddrawmode == VIK_VIEWPORT_DRAWMODE_UTM ) {
4221 vik_layers_panel_change_coord_mode ( vw->viking_vlp, VIK_COORD_LATLON );
4228 static void toggle_draw_scale ( GtkAction *a, VikWindow *vw )
4230 gboolean state = !vik_viewport_get_draw_scale ( vw->viking_vvp );
4231 GtkWidget *check_box = gtk_ui_manager_get_widget ( vw->uim, "/ui/MainMenu/View/SetShow/ShowScale" );
4234 gtk_check_menu_item_set_active ( GTK_CHECK_MENU_ITEM(check_box), state );
4235 vik_viewport_set_draw_scale ( vw->viking_vvp, state );
4239 static void toggle_draw_centermark ( GtkAction *a, VikWindow *vw )
4241 gboolean state = !vik_viewport_get_draw_centermark ( vw->viking_vvp );
4242 GtkWidget *check_box = gtk_ui_manager_get_widget ( vw->uim, "/ui/MainMenu/View/SetShow/ShowCenterMark" );
4245 gtk_check_menu_item_set_active ( GTK_CHECK_MENU_ITEM(check_box), state );
4246 vik_viewport_set_draw_centermark ( vw->viking_vvp, state );
4250 static void toggle_draw_highlight ( GtkAction *a, VikWindow *vw )
4252 gboolean state = !vik_viewport_get_draw_highlight ( vw->viking_vvp );
4253 GtkWidget *check_box = gtk_ui_manager_get_widget ( vw->uim, "/ui/MainMenu/View/SetShow/ShowHighlight" );
4256 gtk_check_menu_item_set_active ( GTK_CHECK_MENU_ITEM(check_box), state );
4257 vik_viewport_set_draw_highlight ( vw->viking_vvp, state );
4261 static void set_bg_color ( GtkAction *a, VikWindow *vw )
4263 GtkWidget *colorsd = gtk_color_selection_dialog_new ( _("Choose a background color") );
4264 GdkColor *color = vik_viewport_get_background_gdkcolor ( vw->viking_vvp );
4265 gtk_color_selection_set_previous_color ( GTK_COLOR_SELECTION(gtk_color_selection_dialog_get_color_selection(GTK_COLOR_SELECTION_DIALOG(colorsd))), color );
4266 gtk_color_selection_set_current_color ( GTK_COLOR_SELECTION(gtk_color_selection_dialog_get_color_selection(GTK_COLOR_SELECTION_DIALOG(colorsd))), color );
4267 if ( gtk_dialog_run ( GTK_DIALOG(colorsd) ) == GTK_RESPONSE_OK )
4269 gtk_color_selection_get_current_color ( GTK_COLOR_SELECTION(gtk_color_selection_dialog_get_color_selection(GTK_COLOR_SELECTION_DIALOG(colorsd))), color );
4270 vik_viewport_set_background_gdkcolor ( vw->viking_vvp, color );
4274 gtk_widget_destroy ( colorsd );
4277 static void set_highlight_color ( GtkAction *a, VikWindow *vw )
4279 GtkWidget *colorsd = gtk_color_selection_dialog_new ( _("Choose a track highlight color") );
4280 GdkColor *color = vik_viewport_get_highlight_gdkcolor ( vw->viking_vvp );
4281 gtk_color_selection_set_previous_color ( GTK_COLOR_SELECTION(gtk_color_selection_dialog_get_color_selection(GTK_COLOR_SELECTION_DIALOG(colorsd))), color );
4282 gtk_color_selection_set_current_color ( GTK_COLOR_SELECTION(gtk_color_selection_dialog_get_color_selection(GTK_COLOR_SELECTION_DIALOG(colorsd))), color );
4283 if ( gtk_dialog_run ( GTK_DIALOG(colorsd) ) == GTK_RESPONSE_OK )
4285 gtk_color_selection_get_current_color ( GTK_COLOR_SELECTION(gtk_color_selection_dialog_get_color_selection(GTK_COLOR_SELECTION_DIALOG(colorsd))), color );
4286 vik_viewport_set_highlight_gdkcolor ( vw->viking_vvp, color );
4290 gtk_widget_destroy ( colorsd );
4294 /***********************************************************************************************
4296 ***********************************************************************************************/
4298 static GtkActionEntry entries[] = {
4299 { "File", NULL, N_("_File"), 0, 0, 0 },
4300 { "Edit", NULL, N_("_Edit"), 0, 0, 0 },
4301 { "View", NULL, N_("_View"), 0, 0, 0 },
4302 { "SetShow", NULL, N_("_Show"), 0, 0, 0 },
4303 { "SetZoom", NULL, N_("_Zoom"), 0, 0, 0 },
4304 { "SetPan", NULL, N_("_Pan"), 0, 0, 0 },
4305 { "Layers", NULL, N_("_Layers"), 0, 0, 0 },
4306 { "Tools", NULL, N_("_Tools"), 0, 0, 0 },
4307 { "Exttools", NULL, N_("_Webtools"), 0, 0, 0 },
4308 { "Help", NULL, N_("_Help"), 0, 0, 0 },
4310 { "New", GTK_STOCK_NEW, N_("_New"), "<control>N", N_("New file"), (GCallback)newwindow_cb },
4311 { "Open", GTK_STOCK_OPEN, N_("_Open..."), "<control>O", N_("Open a file"), (GCallback)load_file },
4312 { "OpenRecentFile", NULL, N_("Open _Recent File"), NULL, NULL, (GCallback)NULL },
4313 { "Append", GTK_STOCK_ADD, N_("Append _File..."), NULL, N_("Append data from a different file"), (GCallback)load_file },
4314 { "Export", GTK_STOCK_CONVERT, N_("_Export All"), NULL, N_("Export All TrackWaypoint Layers"), (GCallback)NULL },
4315 { "ExportGPX", NULL, N_("_GPX..."), NULL, N_("Export as GPX"), (GCallback)export_to_gpx },
4316 { "Acquire", GTK_STOCK_GO_DOWN, N_("A_cquire"), NULL, NULL, (GCallback)NULL },
4317 { "AcquireGPS", NULL, N_("From _GPS..."), NULL, N_("Transfer data from a GPS device"), (GCallback)acquire_from_gps },
4318 { "AcquireGPSBabel", NULL, N_("Import File With GPS_Babel..."), NULL, N_("Import file via GPSBabel converter"), (GCallback)acquire_from_file },
4319 { "AcquireRouting", NULL, N_("_Directions..."), NULL, N_("Get driving directions"), (GCallback)acquire_from_routing },
4320 #ifdef VIK_CONFIG_OPENSTREETMAP
4321 { "AcquireOSM", NULL, N_("_OSM Traces..."), NULL, N_("Get traces from OpenStreetMap"), (GCallback)acquire_from_osm },
4322 { "AcquireMyOSM", NULL, N_("_My OSM Traces..."), NULL, N_("Get Your Own Traces from OpenStreetMap"), (GCallback)acquire_from_my_osm },
4324 #ifdef VIK_CONFIG_GEOCACHES
4325 { "AcquireGC", NULL, N_("Geo_caches..."), NULL, N_("Get Geocaches from geocaching.com"), (GCallback)acquire_from_gc },
4327 #ifdef VIK_CONFIG_GEOTAG
4328 { "AcquireGeotag", NULL, N_("From Geotagged _Images..."), NULL, N_("Create waypoints from geotagged images"), (GCallback)acquire_from_geotag },
4330 { "AcquireURL", NULL, N_("From _URL..."), NULL, N_("Get a file from a URL"), (GCallback)acquire_from_url },
4331 #ifdef VIK_CONFIG_GEONAMES
4332 { "AcquireWikipedia", NULL, N_("From _Wikipedia Waypoints"), NULL, N_("Create waypoints from Wikipedia items in the current view"), (GCallback)acquire_from_wikipedia },
4334 { "Save", GTK_STOCK_SAVE, N_("_Save"), "<control>S", N_("Save the file"), (GCallback)save_file },
4335 { "SaveAs", GTK_STOCK_SAVE_AS, N_("Save _As..."), NULL, N_("Save the file under different name"), (GCallback)save_file_as },
4336 { "FileProperties", NULL, N_("Properties..."), NULL, N_("File Properties"), (GCallback)file_properties_cb },
4337 { "GenImg", GTK_STOCK_CLEAR, N_("_Generate Image File..."), NULL, N_("Save a snapshot of the workspace into a file"), (GCallback)draw_to_image_file_cb },
4338 { "GenImgDir", GTK_STOCK_DND_MULTIPLE, N_("Generate _Directory of Images..."), NULL, N_("Generate _Directory of Images"), (GCallback)draw_to_image_dir_cb },
4339 { "Print", GTK_STOCK_PRINT, N_("_Print..."), NULL, N_("Print maps"), (GCallback)print_cb },
4340 { "Exit", GTK_STOCK_QUIT, N_("E_xit"), "<control>W", N_("Exit the program"), (GCallback)window_close },
4341 { "SaveExit", GTK_STOCK_QUIT, N_("Save and Exit"), NULL, N_("Save and Exit the program"), (GCallback)save_file_and_exit },
4343 { "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 },
4344 { "GoForward", GTK_STOCK_GO_FORWARD, N_("Go to the _Next Location"), NULL, N_("Go to the next location"), (GCallback)draw_goto_back_and_forth },
4345 { "GotoDefaultLocation", GTK_STOCK_HOME, N_("Go to the _Default Location"), NULL, N_("Go to the default location"), (GCallback)goto_default_location },
4346 { "GotoSearch", GTK_STOCK_JUMP_TO, N_("Go to _Location..."), NULL, N_("Go to address/place using text search"), (GCallback)goto_address },
4347 { "GotoLL", GTK_STOCK_JUMP_TO, N_("_Go to Lat/Lon..."), NULL, N_("Go to arbitrary lat/lon coordinate"), (GCallback)draw_goto_cb },
4348 { "GotoUTM", GTK_STOCK_JUMP_TO, N_("Go to UTM..."), NULL, N_("Go to arbitrary UTM coordinate"), (GCallback)draw_goto_cb },
4349 { "Refresh", GTK_STOCK_REFRESH, N_("_Refresh"), "F5", N_("Refresh any maps displayed"), (GCallback)draw_refresh_cb },
4350 { "SetHLColor",GTK_STOCK_SELECT_COLOR, N_("Set _Highlight Color..."), NULL, N_("Set Highlight Color"), (GCallback)set_highlight_color },
4351 { "SetBGColor",GTK_STOCK_SELECT_COLOR, N_("Set Bac_kground Color..."), NULL, N_("Set Background Color"), (GCallback)set_bg_color },
4352 { "ZoomIn", GTK_STOCK_ZOOM_IN, N_("Zoom _In"), "<control>plus", N_("Zoom In"), (GCallback)draw_zoom_cb },
4353 { "ZoomOut", GTK_STOCK_ZOOM_OUT, N_("Zoom _Out"), "<control>minus", N_("Zoom Out"), (GCallback)draw_zoom_cb },
4354 { "ZoomTo", GTK_STOCK_ZOOM_FIT, N_("Zoom _To..."), "<control>Z", N_("Zoom To"), (GCallback)zoom_to_cb },
4355 { "PanNorth", NULL, N_("Pan _North"), "<control>Up", NULL, (GCallback)draw_pan_cb },
4356 { "PanEast", NULL, N_("Pan _East"), "<control>Right", NULL, (GCallback)draw_pan_cb },
4357 { "PanSouth", NULL, N_("Pan _South"), "<control>Down", NULL, (GCallback)draw_pan_cb },
4358 { "PanWest", NULL, N_("Pan _West"), "<control>Left", NULL, (GCallback)draw_pan_cb },
4359 { "BGJobs", GTK_STOCK_EXECUTE, N_("Background _Jobs"), NULL, N_("Background Jobs"), (GCallback)a_background_show_window },
4361 { "Cut", GTK_STOCK_CUT, N_("Cu_t"), NULL, N_("Cut selected layer"), (GCallback)menu_cut_layer_cb },
4362 { "Copy", GTK_STOCK_COPY, N_("_Copy"), NULL, N_("Copy selected layer"), (GCallback)menu_copy_layer_cb },
4363 { "Paste", GTK_STOCK_PASTE, N_("_Paste"), NULL, N_("Paste layer into selected container layer or otherwise above selected layer"), (GCallback)menu_paste_layer_cb },
4364 { "Delete", GTK_STOCK_DELETE, N_("_Delete"), NULL, N_("Remove selected layer"), (GCallback)menu_delete_layer_cb },
4365 { "DeleteAll", NULL, N_("Delete All"), NULL, NULL, (GCallback)clear_cb },
4366 { "CopyCentre",NULL, N_("Copy Centre _Location"), "<control>h", NULL, (GCallback)menu_copy_centre_cb },
4367 { "MapCacheFlush",NULL, N_("_Flush Map Cache"), NULL, NULL, (GCallback)mapcache_flush_cb },
4368 { "SetDefaultLocation", GTK_STOCK_GO_FORWARD, N_("_Set the Default Location"), NULL, N_("Set the Default Location to the current position"),(GCallback)default_location_cb },
4369 { "Preferences",GTK_STOCK_PREFERENCES, N_("_Preferences"), NULL, N_("Program Preferences"), (GCallback)preferences_cb },
4370 { "LayerDefaults",GTK_STOCK_PROPERTIES, N_("_Layer Defaults"), NULL, NULL, NULL },
4371 { "Properties",GTK_STOCK_PROPERTIES, N_("_Properties"), NULL, N_("Layer Properties"), (GCallback)menu_properties_cb },
4373 { "HelpEntry", GTK_STOCK_HELP, N_("_Help"), "F1", N_("Help"), (GCallback)help_help_cb },
4374 { "About", GTK_STOCK_ABOUT, N_("_About"), NULL, N_("About"), (GCallback)help_about_cb },
4377 static GtkActionEntry debug_entries[] = {
4378 { "MapCacheInfo", NULL, "_Map Cache Info", NULL, NULL, (GCallback)help_cache_info_cb },
4379 { "BackForwardInfo", NULL, "_Back/Forward Info", NULL, NULL, (GCallback)back_forward_info_cb },
4382 static GtkActionEntry entries_gpsbabel[] = {
4383 { "ExportKML", NULL, N_("_KML..."), NULL, N_("Export as KML"), (GCallback)export_to_kml },
4386 static GtkActionEntry entries_geojson[] = {
4387 { "AcquireGeoJSON", NULL, N_("Import Geo_JSON File..."), NULL, N_("Import GeoJSON file"), (GCallback)acquire_from_geojson },
4391 static GtkRadioActionEntry mode_entries[] = {
4392 { "ModeUTM", NULL, N_("_UTM Mode"), "<control>u", NULL, VIK_VIEWPORT_DRAWMODE_UTM },
4393 { "ModeExpedia", NULL, N_("_Expedia Mode"), "<control>e", NULL, VIK_VIEWPORT_DRAWMODE_EXPEDIA },
4394 { "ModeMercator", NULL, N_("_Mercator Mode"), "<control>m", NULL, VIK_VIEWPORT_DRAWMODE_MERCATOR },
4395 { "ModeLatLon", NULL, N_("Lat_/Lon Mode"), "<control>l", NULL, VIK_VIEWPORT_DRAWMODE_LATLON },
4398 static GtkToggleActionEntry toggle_entries[] = {
4399 { "ShowScale", NULL, N_("Show _Scale"), "<shift>F5", N_("Show Scale"), (GCallback)toggle_draw_scale, TRUE },
4400 { "ShowCenterMark", NULL, N_("Show _Center Mark"), "F6", N_("Show Center Mark"), (GCallback)toggle_draw_centermark, TRUE },
4401 { "ShowHighlight", GTK_STOCK_UNDERLINE, N_("Show _Highlight"), "F7", N_("Show Highlight"), (GCallback)toggle_draw_highlight, TRUE },
4402 { "FullScreen", GTK_STOCK_FULLSCREEN, N_("_Full Screen"), "F11", N_("Activate full screen mode"), (GCallback)full_screen_cb, FALSE },
4403 { "ViewSidePanel", GTK_STOCK_INDEX, N_("Show Side _Panel"), "F9", N_("Show Side Panel"), (GCallback)view_side_panel_cb, TRUE },
4404 { "ViewStatusBar", NULL, N_("Show Status_bar"), "F12", N_("Show Statusbar"), (GCallback)view_statusbar_cb, TRUE },
4405 { "ViewToolbar", NULL, N_("Show _Toolbar"), "F3", N_("Show Toolbar"), (GCallback)view_toolbar_cb, TRUE },
4406 { "ViewMainMenu", NULL, N_("Show _Menu"), "F4", N_("Show Menu"), (GCallback)view_main_menu_cb, TRUE },
4409 // This must match the toggle entries order above
4410 static gpointer toggle_entries_toolbar_cb[] = {
4411 (GCallback)tb_set_draw_scale,
4412 (GCallback)tb_set_draw_centermark,
4413 (GCallback)tb_set_draw_highlight,
4414 (GCallback)tb_full_screen_cb,
4415 (GCallback)tb_view_side_panel_cb,
4416 (GCallback)tb_view_statusbar_cb,
4417 (GCallback)tb_view_toolbar_cb,
4418 (GCallback)tb_view_main_menu_cb,
4421 #include "menu.xml.h"
4422 static void window_create_ui( VikWindow *window )
4425 GtkActionGroup *action_group;
4426 GtkAccelGroup *accel_group;
4429 GtkIconFactory *icon_factory;
4430 GtkIconSet *icon_set;
4431 GtkRadioActionEntry *tools = NULL, *radio;
4434 uim = gtk_ui_manager_new ();
4437 toolbox_add_tool(window->vt, &ruler_tool, TOOL_LAYER_TYPE_NONE);
4438 toolbox_add_tool(window->vt, &zoom_tool, TOOL_LAYER_TYPE_NONE);
4439 toolbox_add_tool(window->vt, &pan_tool, TOOL_LAYER_TYPE_NONE);
4440 toolbox_add_tool(window->vt, &select_tool, TOOL_LAYER_TYPE_NONE);
4442 toolbar_action_tool_entry_register ( window->viking_vtb, &pan_tool.radioActionEntry );
4443 toolbar_action_tool_entry_register ( window->viking_vtb, &zoom_tool.radioActionEntry );
4444 toolbar_action_tool_entry_register ( window->viking_vtb, &ruler_tool.radioActionEntry );
4445 toolbar_action_tool_entry_register ( window->viking_vtb, &select_tool.radioActionEntry );
4448 if (!(mid = gtk_ui_manager_add_ui_from_string (uim, menu_xml, -1, &error))) {
4449 g_error_free (error);
4453 action_group = gtk_action_group_new ("MenuActions");
4454 gtk_action_group_set_translation_domain(action_group, PACKAGE_NAME);
4455 gtk_action_group_add_actions (action_group, entries, G_N_ELEMENTS (entries), window);
4456 gtk_action_group_add_toggle_actions (action_group, toggle_entries, G_N_ELEMENTS (toggle_entries), window);
4457 gtk_action_group_add_radio_actions (action_group, mode_entries, G_N_ELEMENTS (mode_entries), 4, (GCallback)window_change_coord_mode_cb, window);
4459 if ( gtk_ui_manager_add_ui_from_string ( uim,
4460 "<ui><menubar name='MainMenu'><menu action='Help'>"
4461 "<menuitem action='MapCacheInfo'/>"
4462 "<menuitem action='BackForwardInfo'/>"
4463 "</menu></menubar></ui>",
4465 gtk_action_group_add_actions (action_group, debug_entries, G_N_ELEMENTS (debug_entries), window);
4469 for ( i=0; i < G_N_ELEMENTS (entries); i++ ) {
4470 if ( entries[i].callback )
4471 toolbar_action_entry_register ( window->viking_vtb, &entries[i] );
4474 if ( G_N_ELEMENTS (toggle_entries) != G_N_ELEMENTS (toggle_entries_toolbar_cb) ) {
4475 g_print ( "Broken entries definitions\n" );
4478 for ( i=0; i < G_N_ELEMENTS (toggle_entries); i++ ) {
4479 if ( toggle_entries_toolbar_cb[i] )
4480 toolbar_action_toggle_entry_register ( window->viking_vtb, &toggle_entries[i], toggle_entries_toolbar_cb[i] );
4483 for ( i=0; i < G_N_ELEMENTS (mode_entries); i++ ) {
4484 toolbar_action_mode_entry_register ( window->viking_vtb, &mode_entries[i] );
4487 // Use this to see if GPSBabel is available:
4488 if ( a_babel_available () ) {
4489 // If going to add more entries then might be worth creating a menu_gpsbabel.xml.h file
4490 if ( gtk_ui_manager_add_ui_from_string ( uim,
4491 "<ui><menubar name='MainMenu'><menu action='File'><menu action='Export'><menuitem action='ExportKML'/></menu></menu></menubar></ui>",
4493 gtk_action_group_add_actions ( action_group, entries_gpsbabel, G_N_ELEMENTS (entries_gpsbabel), window );
4496 // GeoJSON import capability
4497 if ( g_find_program_in_path ( a_geojson_program_import() ) ) {
4498 if ( gtk_ui_manager_add_ui_from_string ( uim,
4499 "<ui><menubar name='MainMenu'><menu action='File'><menu action='Acquire'><menuitem action='AcquireGeoJSON'/></menu></menu></menubar></ui>",
4501 gtk_action_group_add_actions ( action_group, entries_geojson, G_N_ELEMENTS (entries_geojson), window );
4504 icon_factory = gtk_icon_factory_new ();
4505 gtk_icon_factory_add_default (icon_factory);
4507 register_vik_icons(icon_factory);
4509 // Copy the tool RadioActionEntries out of the main Window structure into an extending array 'tools'
4510 // so that it can be applied to the UI in one action group add function call below
4512 for (i=0; i<window->vt->n_tools; i++) {
4513 tools = g_renew(GtkRadioActionEntry, tools, ntools+1);
4514 radio = &tools[ntools];
4516 *radio = window->vt->tools[i].ti.radioActionEntry;
4517 radio->value = ntools;
4520 for (i=0; i<VIK_LAYER_NUM_TYPES; i++) {
4521 GtkActionEntry action;
4522 gtk_ui_manager_add_ui(uim, mid, "/ui/MainMenu/Layers/",
4523 vik_layer_get_interface(i)->name,
4524 vik_layer_get_interface(i)->name,
4525 GTK_UI_MANAGER_MENUITEM, FALSE);
4527 icon_set = gtk_icon_set_new_from_pixbuf (gdk_pixbuf_from_pixdata (vik_layer_get_interface(i)->icon, FALSE, NULL ));
4528 gtk_icon_factory_add (icon_factory, vik_layer_get_interface(i)->name, icon_set);
4529 gtk_icon_set_unref (icon_set);
4531 action.name = vik_layer_get_interface(i)->name;
4532 action.stock_id = vik_layer_get_interface(i)->name;
4533 action.label = g_strdup_printf( _("New _%s Layer"), vik_layer_get_interface(i)->name);
4534 action.accelerator = vik_layer_get_interface(i)->accelerator;
4535 action.tooltip = NULL;
4536 action.callback = (GCallback)menu_addlayer_cb;
4537 gtk_action_group_add_actions(action_group, &action, 1, window);
4539 g_free ( (gchar*)action.label );
4541 if ( vik_layer_get_interface(i)->tools_count ) {
4542 gtk_ui_manager_add_ui(uim, mid, "/ui/MainMenu/Tools/", vik_layer_get_interface(i)->name, NULL, GTK_UI_MANAGER_SEPARATOR, FALSE);
4545 // Further tool copying for to apply to the UI, also apply menu UI setup
4546 for ( j = 0; j < vik_layer_get_interface(i)->tools_count; j++ ) {
4547 tools = g_renew(GtkRadioActionEntry, tools, ntools+1);
4548 radio = &tools[ntools];
4551 gtk_ui_manager_add_ui(uim, mid, "/ui/MainMenu/Tools",
4552 vik_layer_get_interface(i)->tools[j].radioActionEntry.label,
4553 vik_layer_get_interface(i)->tools[j].radioActionEntry.name,
4554 GTK_UI_MANAGER_MENUITEM, FALSE);
4556 toolbox_add_tool(window->vt, &(vik_layer_get_interface(i)->tools[j]), i);
4557 toolbar_action_tool_entry_register ( window->viking_vtb, &(vik_layer_get_interface(i)->tools[j].radioActionEntry) );
4559 *radio = vik_layer_get_interface(i)->tools[j].radioActionEntry;
4560 // Overwrite with actual number to use
4561 radio->value = ntools;
4564 GtkActionEntry action_dl;
4565 gchar *layername = g_strdup_printf ( "Layer%s", vik_layer_get_interface(i)->fixed_layer_name );
4566 gtk_ui_manager_add_ui(uim, mid, "/ui/MainMenu/Edit/LayerDefaults",
4567 vik_layer_get_interface(i)->name,
4569 GTK_UI_MANAGER_MENUITEM, FALSE);
4572 // For default layers use action names of the form 'Layer<LayerName>'
4573 // This is to avoid clashing with just the layer name used above for the tool actions
4574 action_dl.name = g_strconcat("Layer", vik_layer_get_interface(i)->fixed_layer_name, NULL);
4575 action_dl.stock_id = NULL;
4576 action_dl.label = g_strconcat("_", vik_layer_get_interface(i)->name, "...", NULL); // Prepend marker for keyboard accelerator
4577 action_dl.accelerator = NULL;
4578 action_dl.tooltip = NULL;
4579 action_dl.callback = (GCallback)layer_defaults_cb;
4580 gtk_action_group_add_actions(action_group, &action_dl, 1, window);
4581 g_free ( (gchar*)action_dl.name );
4582 g_free ( (gchar*)action_dl.label );
4584 g_object_unref (icon_factory);
4586 gtk_action_group_add_radio_actions(action_group, tools, ntools, 0, (GCallback)menu_cb, window);
4589 gtk_ui_manager_insert_action_group (uim, action_group, 0);
4591 for (i=0; i<VIK_LAYER_NUM_TYPES; i++) {
4592 for ( j = 0; j < vik_layer_get_interface(i)->tools_count; j++ ) {
4593 GtkAction *action = gtk_action_group_get_action(action_group,
4594 vik_layer_get_interface(i)->tools[j].radioActionEntry.name);
4595 g_object_set(action, "sensitive", FALSE, NULL);
4599 // This is done last so we don't need to track the value of mid anymore
4600 vik_ext_tools_add_action_items ( window, window->uim, action_group, mid );
4602 window->action_group = action_group;
4604 accel_group = gtk_ui_manager_get_accel_group (uim);
4605 gtk_window_add_accel_group (GTK_WINDOW (window), accel_group);
4606 gtk_ui_manager_ensure_update (uim);
4608 setup_recent_files(window);
4612 // TODO - add method to add tool icons defined from outside this file
4613 // and remove the reverse dependency on icon definition from this file
4615 const GdkPixdata *data;
4618 { &mover_22_pixbuf, "vik-icon-pan" },
4619 { &zoom_18_pixbuf, "vik-icon-zoom" },
4620 { &ruler_18_pixbuf, "vik-icon-ruler" },
4621 { &select_18_pixbuf, "vik-icon-select" },
4622 { &vik_new_route_18_pixbuf, "vik-icon-Create Route" },
4623 { &route_finder_18_pixbuf, "vik-icon-Route Finder" },
4624 { &demdl_18_pixbuf, "vik-icon-DEM Download" },
4625 { &showpic_18_pixbuf, "vik-icon-Show Picture" },
4626 { &addtr_18_pixbuf, "vik-icon-Create Track" },
4627 { &edtr_18_pixbuf, "vik-icon-Edit Trackpoint" },
4628 { &addwp_18_pixbuf, "vik-icon-Create Waypoint" },
4629 { &edwp_18_pixbuf, "vik-icon-Edit Waypoint" },
4630 { &geozoom_18_pixbuf, "vik-icon-Georef Zoom Tool" },
4631 { &geomove_18_pixbuf, "vik-icon-Georef Move Map" },
4632 { &mapdl_18_pixbuf, "vik-icon-Maps Download" },
4635 static gint n_stock_icons = G_N_ELEMENTS (stock_icons);
4638 register_vik_icons (GtkIconFactory *icon_factory)
4640 GtkIconSet *icon_set;
4643 for (i = 0; i < n_stock_icons; i++) {
4644 icon_set = gtk_icon_set_new_from_pixbuf (gdk_pixbuf_from_pixdata (
4645 stock_icons[i].data, FALSE, NULL ));
4646 gtk_icon_factory_add (icon_factory, stock_icons[i].stock_id, icon_set);
4647 gtk_icon_set_unref (icon_set);
4651 gpointer vik_window_get_selected_trw_layer ( VikWindow *vw )
4653 return vw->selected_vtl;
4656 void vik_window_set_selected_trw_layer ( VikWindow *vw, gpointer vtl )
4658 vw->selected_vtl = vtl;
4659 vw->containing_vtl = vtl;
4661 vw->selected_track = NULL;
4662 vw->selected_tracks = NULL;
4663 vw->selected_waypoint = NULL;
4664 vw->selected_waypoints = NULL;
4665 // Set highlight thickness
4666 vik_viewport_set_highlight_thickness ( vw->viking_vvp, vik_trw_layer_get_property_tracks_line_thickness (vw->containing_vtl) );
4669 GHashTable *vik_window_get_selected_tracks ( VikWindow *vw )
4671 return vw->selected_tracks;
4674 void vik_window_set_selected_tracks ( VikWindow *vw, GHashTable *ght, gpointer vtl )
4676 vw->selected_tracks = ght;
4677 vw->containing_vtl = vtl;
4679 vw->selected_vtl = NULL;
4680 vw->selected_track = NULL;
4681 vw->selected_waypoint = NULL;
4682 vw->selected_waypoints = NULL;
4683 // Set highlight thickness
4684 vik_viewport_set_highlight_thickness ( vw->viking_vvp, vik_trw_layer_get_property_tracks_line_thickness (vw->containing_vtl) );
4687 gpointer vik_window_get_selected_track ( VikWindow *vw )
4689 return vw->selected_track;
4692 void vik_window_set_selected_track ( VikWindow *vw, gpointer *vt, gpointer vtl )
4694 vw->selected_track = vt;
4695 vw->containing_vtl = vtl;
4697 vw->selected_vtl = NULL;
4698 vw->selected_tracks = NULL;
4699 vw->selected_waypoint = NULL;
4700 vw->selected_waypoints = NULL;
4701 // Set highlight thickness
4702 vik_viewport_set_highlight_thickness ( vw->viking_vvp, vik_trw_layer_get_property_tracks_line_thickness (vw->containing_vtl) );
4705 GHashTable *vik_window_get_selected_waypoints ( VikWindow *vw )
4707 return vw->selected_waypoints;
4710 void vik_window_set_selected_waypoints ( VikWindow *vw, GHashTable *ght, gpointer vtl )
4712 vw->selected_waypoints = ght;
4713 vw->containing_vtl = vtl;
4715 vw->selected_vtl = NULL;
4716 vw->selected_track = NULL;
4717 vw->selected_tracks = NULL;
4718 vw->selected_waypoint = NULL;
4721 gpointer vik_window_get_selected_waypoint ( VikWindow *vw )
4723 return vw->selected_waypoint;
4726 void vik_window_set_selected_waypoint ( VikWindow *vw, gpointer *vwp, gpointer vtl )
4728 vw->selected_waypoint = vwp;
4729 vw->containing_vtl = vtl;
4731 vw->selected_vtl = NULL;
4732 vw->selected_track = NULL;
4733 vw->selected_tracks = NULL;
4734 vw->selected_waypoints = NULL;
4737 gboolean vik_window_clear_highlight ( VikWindow *vw )
4739 gboolean need_redraw = FALSE;
4740 if ( vw->selected_vtl != NULL ) {
4741 vw->selected_vtl = NULL;
4744 if ( vw->selected_track != NULL ) {
4745 vw->selected_track = NULL;
4748 if ( vw->selected_tracks != NULL ) {
4749 vw->selected_tracks = NULL;
4752 if ( vw->selected_waypoint != NULL ) {
4753 vw->selected_waypoint = NULL;
4756 if ( vw->selected_waypoints != NULL ) {
4757 vw->selected_waypoints = NULL;
4763 GThread *vik_window_get_thread ( VikWindow *vw )