2 * viking -- GPS Data and Topo Analyzer, Explorer, and Manager
4 * Copyright (C) 2003-2005, Evan Battaglia <gtoevan@gmx.net>
5 * Copyright (C) 2005-2006, Alex Foobarian <foobarian@gmail.com>
6 * Copyright (C) 2012-2014, Rob Norris <rw_norris@hotmail.com>
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
29 #include "background.h"
31 #include "datasources.h"
37 #include "preferences.h"
39 #include "viklayer_defaults.h"
40 #include "icons/icons.h"
41 #include "vikexttools.h"
42 #include "vikexttool_datasources.h"
43 #include "garminsymbols.h"
44 #include "vikmapslayer.h"
45 #include "geonamessearch.h"
60 #include <glib/gstdio.h>
61 #include <glib/gprintf.h>
62 #include <glib/gi18n.h>
64 #include <gdk/gdkkeysyms.h>
66 // This seems rather arbitary, quite large and pointless
67 // I mean, if you have a thousand windows open;
68 // why not be allowed to open a thousand more...
69 #define MAX_WINDOWS 1024
70 static guint window_count = 0;
71 static GSList *window_list = NULL;
73 #define VIKING_WINDOW_WIDTH 1000
74 #define VIKING_WINDOW_HEIGHT 800
75 #define DRAW_IMAGE_DEFAULT_WIDTH 1280
76 #define DRAW_IMAGE_DEFAULT_HEIGHT 1024
77 #define DRAW_IMAGE_DEFAULT_SAVE_AS_PNG TRUE
79 static void window_finalize ( GObject *gob );
80 static GObjectClass *parent_class;
82 static void window_set_filename ( VikWindow *vw, const gchar *filename );
83 static const gchar *window_get_filename ( VikWindow *vw );
85 static VikWindow *window_new ();
87 static void draw_update ( VikWindow *vw );
89 static void newwindow_cb ( GtkAction *a, VikWindow *vw );
92 static void open_window ( VikWindow *vw, GSList *files );
93 static void destroy_window ( GtkWidget *widget,
98 static gboolean delete_event( VikWindow *vw );
100 static gboolean key_press_event( VikWindow *vw, GdkEventKey *event, gpointer data );
102 static void center_changed_cb ( VikWindow *vw );
103 static void window_configure_event ( VikWindow *vw );
104 static void draw_sync ( VikWindow *vw );
105 static void draw_redraw ( VikWindow *vw );
106 static void draw_scroll ( VikWindow *vw, GdkEventScroll *event );
107 static void draw_click ( VikWindow *vw, GdkEventButton *event );
108 static void draw_release ( VikWindow *vw, GdkEventButton *event );
109 static void draw_mouse_motion ( VikWindow *vw, GdkEventMotion *event );
110 static void draw_zoom_cb ( GtkAction *a, VikWindow *vw );
111 static void draw_goto_cb ( GtkAction *a, VikWindow *vw );
112 static void draw_refresh_cb ( GtkAction *a, VikWindow *vw );
114 static void draw_status ( VikWindow *vw );
116 /* End Drawing Functions */
118 static void toggle_draw_scale ( GtkAction *a, VikWindow *vw );
119 static void toggle_draw_centermark ( GtkAction *a, VikWindow *vw );
120 static void toggle_draw_highlight ( GtkAction *a, VikWindow *vw );
122 static void menu_addlayer_cb ( GtkAction *a, VikWindow *vw );
123 static void menu_properties_cb ( GtkAction *a, VikWindow *vw );
124 static void menu_delete_layer_cb ( GtkAction *a, VikWindow *vw );
126 /* tool management */
132 #define TOOL_LAYER_TYPE_NONE -1
137 toolbox_tool_t *tools;
141 static void menu_cb ( GtkAction *old, GtkAction *a, VikWindow *vw );
142 static void window_change_coord_mode_cb ( GtkAction *old, GtkAction *a, VikWindow *vw );
143 static toolbox_tools_t* toolbox_create(VikWindow *vw);
144 static void toolbox_add_tool(toolbox_tools_t *vt, VikToolInterface *vti, gint layer_type );
145 static int toolbox_get_tool(toolbox_tools_t *vt, const gchar *tool_name);
146 static void toolbox_activate(toolbox_tools_t *vt, const gchar *tool_name);
147 static const GdkCursor *toolbox_get_cursor(toolbox_tools_t *vt, const gchar *tool_name);
148 static void toolbox_click (toolbox_tools_t *vt, GdkEventButton *event);
149 static void toolbox_move (toolbox_tools_t *vt, GdkEventMotion *event);
150 static void toolbox_release (toolbox_tools_t *vt, GdkEventButton *event);
154 static void window_create_ui( VikWindow *window );
155 static void register_vik_icons (GtkIconFactory *icon_factory);
158 static void load_file ( GtkAction *a, VikWindow *vw );
159 static gboolean save_file_as ( GtkAction *a, VikWindow *vw );
160 static gboolean save_file ( GtkAction *a, VikWindow *vw );
161 static gboolean save_file_and_exit ( GtkAction *a, VikWindow *vw );
162 static gboolean window_save ( VikWindow *vw );
167 VikViewport *viking_vvp;
168 VikLayersPanel *viking_vlp;
169 VikStatusbar *viking_vs;
170 VikToolbar *viking_vtb;
172 GtkWidget *main_vbox;
173 GtkWidget *menu_hbox;
175 GdkCursor *busy_cursor;
176 GdkCursor *viewport_cursor; // only a reference
178 /* tool management state */
181 guint16 tool_layer_id;
182 guint16 tool_tool_id;
184 GtkActionGroup *action_group;
187 // NB scale, centermark and highlight are in viewport.
188 gboolean show_full_screen;
189 gboolean show_side_panel;
190 gboolean show_statusbar;
191 gboolean show_toolbar;
192 gboolean show_main_menu;
194 gboolean select_move;
197 gint delayed_pan_x, delayed_pan_y; // Temporary storage
198 gboolean single_click_pending;
200 guint draw_image_width, draw_image_height;
201 gboolean draw_image_save_as_png;
205 VikLoadType_t loaded_type;
207 GtkWidget *open_dia, *save_dia;
208 GtkWidget *save_img_dia, *save_img_dir_dia;
210 gboolean only_updating_coord_mode_ui; /* hack for a bug in GTK */
214 /* half-drawn update */
216 VikCoord trigger_center;
218 /* Store at this level for highlighted selection drawing since it applies to the viewport and the layers panel */
219 /* Only one of these items can be selected at the same time */
220 gpointer selected_vtl; /* notionally VikTrwLayer */
221 GHashTable *selected_tracks;
222 gpointer selected_track; /* notionally VikTrack */
223 GHashTable *selected_waypoints;
224 gpointer selected_waypoint; /* notionally VikWaypoint */
225 /* only use for individual track or waypoint */
226 /* For track(s) & waypoint(s) it is the layer they are in - this helps refering to the individual item easier */
227 gpointer containing_vtl; /* notionally VikTrwLayer */
241 VW_OPENWINDOW_SIGNAL,
245 static guint window_signals[VW_LAST_SIGNAL] = { 0 };
247 // TODO get rid of this as this is unnecessary duplication...
248 static gchar *tool_names[NUMBER_OF_TOOLS] = { N_("Pan"), N_("Zoom"), N_("Ruler"), N_("Select") };
250 G_DEFINE_TYPE (VikWindow, vik_window, GTK_TYPE_WINDOW)
252 VikViewport * vik_window_viewport(VikWindow *vw)
254 return(vw->viking_vvp);
257 VikLayersPanel * vik_window_layers_panel(VikWindow *vw)
259 return(vw->viking_vlp);
263 * Returns the statusbar for the window
265 VikStatusbar * vik_window_get_statusbar ( VikWindow *vw )
267 return vw->viking_vs;
271 * Returns the 'project' filename
273 const gchar *vik_window_get_filename (VikWindow *vw)
280 vik_statusbar_type_t vs_type;
281 gchar* message; // Always make a copy of this data
282 } statusbar_idle_data;
285 * For the actual statusbar update!
287 static gboolean statusbar_idle_update ( statusbar_idle_data *sid )
289 vik_statusbar_set_message ( sid->vs, sid->vs_type, sid->message );
290 g_free ( sid->message );
296 * vik_window_statusbar_update:
297 * @vw: The main window in which the statusbar will be updated.
298 * @message: The string to be displayed. This is copied.
299 * @vs_type: The part of the statusbar to be updated.
301 * This updates any part of the statusbar with the new string.
302 * It handles calling from the main thread or any background thread
303 * ATM this mostly used from background threads - as from the main thread
304 * one may use the vik_statusbar_set_message() directly.
306 void vik_window_statusbar_update ( VikWindow *vw, const gchar* message, vik_statusbar_type_t vs_type )
308 statusbar_idle_data *sid = g_malloc ( sizeof (statusbar_idle_data) );
309 sid->vs = vw->viking_vs;
310 sid->vs_type = vs_type;
311 sid->message = g_strdup ( message );
313 if ( g_thread_self() == vik_window_get_thread ( vw ) ) {
314 g_idle_add ( (GSourceFunc) statusbar_idle_update, sid );
317 // From a background thread
318 gdk_threads_add_idle ( (GSourceFunc) statusbar_idle_update, sid );
322 // Actual signal handlers
323 static void destroy_window ( GtkWidget *widget,
326 if ( ! --window_count )
330 #define VIK_SETTINGS_WIN_SIDEPANEL "window_sidepanel"
331 #define VIK_SETTINGS_WIN_STATUSBAR "window_statusbar"
332 #define VIK_SETTINGS_WIN_TOOLBAR "window_toolbar"
333 // Menubar setting to off is never auto saved in case it's accidentally turned off
334 // It's not so obvious so to recover the menu visibility.
335 // Thus this value is for setting manually via editting the settings file directly
336 #define VIK_SETTINGS_WIN_MENUBAR "window_menubar"
338 VikWindow *vik_window_new_window ()
340 if ( window_count < MAX_WINDOWS )
342 VikWindow *vw = window_new ();
344 g_signal_connect (G_OBJECT (vw), "destroy",
345 G_CALLBACK (destroy_window), NULL);
346 g_signal_connect (G_OBJECT (vw), "newwindow",
347 G_CALLBACK (vik_window_new_window), NULL);
348 g_signal_connect (G_OBJECT (vw), "openwindow",
349 G_CALLBACK (open_window), NULL);
351 gtk_widget_show_all ( GTK_WIDGET(vw) );
353 if ( a_vik_get_restore_window_state() ) {
354 // These settings are applied after the show all as these options hide widgets
356 if ( a_settings_get_boolean ( VIK_SETTINGS_WIN_SIDEPANEL, &sidepanel ) )
358 gtk_widget_hide ( GTK_WIDGET(vw->viking_vlp) );
359 GtkWidget *check_box = gtk_ui_manager_get_widget ( vw->uim, "/ui/MainMenu/View/SetShow/ViewSidePanel" );
360 gtk_check_menu_item_set_active ( GTK_CHECK_MENU_ITEM(check_box), FALSE );
364 if ( a_settings_get_boolean ( VIK_SETTINGS_WIN_STATUSBAR, &statusbar ) )
366 gtk_widget_hide ( GTK_WIDGET(vw->viking_vs) );
367 GtkWidget *check_box = gtk_ui_manager_get_widget ( vw->uim, "/ui/MainMenu/View/SetShow/ViewStatusBar" );
368 gtk_check_menu_item_set_active ( GTK_CHECK_MENU_ITEM(check_box), FALSE );
372 if ( a_settings_get_boolean ( VIK_SETTINGS_WIN_TOOLBAR, &toolbar ) )
374 gtk_widget_hide ( toolbar_get_widget (vw->viking_vtb) );
375 GtkWidget *check_box = gtk_ui_manager_get_widget ( vw->uim, "/ui/MainMenu/View/SetShow/ViewToolBar" );
376 gtk_check_menu_item_set_active ( GTK_CHECK_MENU_ITEM(check_box), FALSE );
380 if ( a_settings_get_boolean ( VIK_SETTINGS_WIN_MENUBAR, &menubar ) )
382 gtk_widget_hide ( gtk_ui_manager_get_widget ( vw->uim, "/ui/MainMenu" ) );
383 GtkWidget *check_box = gtk_ui_manager_get_widget ( vw->uim, "/ui/MainMenu/View/SetShow/ViewMainMenu" );
384 gtk_check_menu_item_set_active ( GTK_CHECK_MENU_ITEM(check_box), FALSE );
395 * determine_location_thread:
396 * @vw: The window that will get updated
397 * @threaddata: Data used by our background thread mechanism
399 * Use the features in vikgoto to determine where we are
400 * Then set up the viewport:
401 * 1. To goto the location
402 * 2. Set an appropriate level zoom for the location type
403 * 3. Some statusbar message feedback
405 static int determine_location_thread ( VikWindow *vw, gpointer threaddata )
409 gint ans = a_vik_goto_where_am_i ( vw->viking_vvp, &ll, &name );
411 int result = a_background_thread_progress ( threaddata, 1.0 );
413 vik_window_statusbar_update ( vw, _("Location lookup aborted"), VIK_STATUSBAR_INFO );
414 return -1; /* Abort thread */
422 // Position found with city precision - so zoom out more
425 else if ( ans == 3 ) {
426 // Position found via country name search - so zoom wayyyy out
430 vik_viewport_set_zoom ( vw->viking_vvp, zoom );
431 vik_viewport_set_center_latlon ( vw->viking_vvp, &ll, FALSE );
433 gchar *message = g_strdup_printf ( _("Location found: %s"), name );
434 vik_window_statusbar_update ( vw, message, VIK_STATUSBAR_INFO );
438 // Signal to redraw from the background
439 vik_layers_panel_emit_update ( vw->viking_vlp );
442 vik_window_statusbar_update ( vw, _("Unable to determine location"), VIK_STATUSBAR_INFO );
448 * Steps to be taken once initial loading has completed
450 void vik_window_new_window_finish ( VikWindow *vw )
452 // Don't add a map if we've loaded a Viking file already
456 if ( a_vik_get_startup_method ( ) == VIK_STARTUP_METHOD_SPECIFIED_FILE ) {
457 vik_window_open_file ( vw, a_vik_get_startup_file(), TRUE );
462 // Maybe add a default map layer
463 if ( a_vik_get_add_default_map_layer () ) {
464 VikMapsLayer *vml = VIK_MAPS_LAYER ( vik_layer_create(VIK_LAYER_MAPS, vw->viking_vvp, FALSE) );
465 vik_layer_rename ( VIK_LAYER(vml), _("Default Map") );
466 vik_aggregate_layer_add_layer ( vik_layers_panel_get_top_layer(vw->viking_vlp), VIK_LAYER(vml), TRUE );
471 // If not loaded any file, maybe try the location lookup
472 if ( vw->loaded_type == LOAD_TYPE_READ_FAILURE ) {
473 if ( a_vik_get_startup_method ( ) == VIK_STARTUP_METHOD_AUTO_LOCATION ) {
475 vik_statusbar_set_message ( vw->viking_vs, VIK_STATUSBAR_INFO, _("Trying to determine location...") );
477 a_background_thread ( GTK_WINDOW(vw),
478 _("Determining location"),
479 (vik_thr_func) determine_location_thread,
488 static void open_window ( VikWindow *vw, GSList *files )
490 gboolean change_fn = (g_slist_length(files) == 1); /* only change fn if one file */
491 GSList *cur_file = files;
493 // Only open a new window if a viking file
494 gchar *file_name = cur_file->data;
495 if (vw != NULL && vw->filename && check_file_magic_vik ( file_name ) ) {
496 VikWindow *newvw = vik_window_new_window ();
498 vik_window_open_file ( newvw, file_name, TRUE );
501 vik_window_open_file ( vw, file_name, change_fn );
504 cur_file = g_slist_next (cur_file);
506 g_slist_free (files);
510 void vik_window_selected_layer(VikWindow *vw, VikLayer *vl)
512 int i, j, tool_count;
513 VikLayerInterface *layer_interface;
515 if (!vw->action_group) return;
517 for (i=0; i<VIK_LAYER_NUM_TYPES; i++) {
519 layer_interface = vik_layer_get_interface(i);
520 tool_count = layer_interface->tools_count;
522 for (j = 0; j < tool_count; j++) {
523 action = gtk_action_group_get_action(vw->action_group,
524 layer_interface->tools[j].radioActionEntry.name);
525 g_object_set(action, "sensitive", i == vl->type, NULL);
526 toolbar_action_set_sensitive ( vw->viking_vtb, vik_layer_get_interface(i)->tools[j].radioActionEntry.name, i == vl->type );
531 static void window_finalize ( GObject *gob )
533 VikWindow *vw = VIK_WINDOW(gob);
534 g_return_if_fail ( vw != NULL );
536 a_background_remove_window ( vw );
538 window_list = g_slist_remove ( window_list, vw );
540 gdk_cursor_unref ( vw->busy_cursor );
542 for (tt = 0; tt < vw->vt->n_tools; tt++ )
543 if ( vw->vt->tools[tt].ti.destroy )
544 vw->vt->tools[tt].ti.destroy ( vw->vt->tools[tt].state );
545 g_free ( vw->vt->tools );
548 vik_toolbar_finalize ( vw->viking_vtb );
550 G_OBJECT_CLASS(parent_class)->finalize(gob);
554 static void vik_window_class_init ( VikWindowClass *klass )
557 GObjectClass *object_class;
559 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);
560 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);
562 object_class = G_OBJECT_CLASS (klass);
564 object_class->finalize = window_finalize;
566 parent_class = g_type_class_peek_parent (klass);
570 static void zoom_changed (GtkMenuShell *menushell,
573 VikWindow *vw = VIK_WINDOW (user_data);
575 GtkWidget *aw = gtk_menu_get_active ( GTK_MENU (menushell) );
576 gint active = GPOINTER_TO_INT(g_object_get_data ( G_OBJECT (aw), "position" ));
578 gdouble zoom_request = pow (2, active-2 );
580 // But has it really changed?
581 gdouble current_zoom = vik_viewport_get_zoom ( vw->viking_vvp );
582 if ( current_zoom != 0.0 && zoom_request != current_zoom ) {
583 vik_viewport_set_zoom ( vw->viking_vvp, zoom_request );
584 // Force drawing update
590 * @mpp: The initial zoom level
592 static GtkWidget *create_zoom_menu_all_levels ( gdouble mpp )
594 GtkWidget *menu = gtk_menu_new ();
595 char *itemLabels[] = { "0.25", "0.5", "1", "2", "4", "8", "16", "32", "64", "128", "256", "512", "1024", "2048", "4096", "8192", "16384", "32768" };
598 for (i = 0 ; i < G_N_ELEMENTS(itemLabels) ; i++)
600 GtkWidget *item = gtk_menu_item_new_with_label (itemLabels[i]);
601 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
602 gtk_widget_show (item);
603 g_object_set_data (G_OBJECT (item), "position", GINT_TO_POINTER(i));
606 gint active = 2 + round ( log (mpp) / log (2) );
607 // Ensure value derived from mpp is in bounds of the menu
608 if ( active >= G_N_ELEMENTS(itemLabels) )
609 active = G_N_ELEMENTS(itemLabels) - 1;
612 gtk_menu_set_active ( GTK_MENU(menu), active );
617 static GtkWidget *create_zoom_combo_all_levels ()
619 GtkWidget *combo = vik_combo_box_text_new();
620 vik_combo_box_text_append ( combo, "0.25");
621 vik_combo_box_text_append ( combo, "0.5");
622 vik_combo_box_text_append ( combo, "1");
623 vik_combo_box_text_append ( combo, "2");
624 vik_combo_box_text_append ( combo, "4");
625 vik_combo_box_text_append ( combo, "8");
626 vik_combo_box_text_append ( combo, "16");
627 vik_combo_box_text_append ( combo, "32");
628 vik_combo_box_text_append ( combo, "64");
629 vik_combo_box_text_append ( combo, "128");
630 vik_combo_box_text_append ( combo, "256");
631 vik_combo_box_text_append ( combo, "512");
632 vik_combo_box_text_append ( combo, "1024");
633 vik_combo_box_text_append ( combo, "2048");
634 vik_combo_box_text_append ( combo, "4096");
635 vik_combo_box_text_append ( combo, "8192");
636 vik_combo_box_text_append ( combo, "16384");
637 vik_combo_box_text_append ( combo, "32768");
639 gtk_widget_set_tooltip_text (combo, _("Select zoom level"));
643 static gint zoom_popup_handler (GtkWidget *widget)
647 g_return_val_if_fail (widget != NULL, FALSE);
648 g_return_val_if_fail (GTK_IS_MENU (widget), FALSE);
650 /* The "widget" is the menu that was supplied when
651 * g_signal_connect_swapped() was called.
653 menu = GTK_MENU (widget);
655 gtk_menu_popup (menu, NULL, NULL, NULL, NULL,
656 1, gtk_get_current_event_time());
664 static void drag_data_received_cb ( GtkWidget *widget,
665 GdkDragContext *context,
668 GtkSelectionData *selection_data,
673 gboolean success = FALSE;
675 if ( (selection_data != NULL) && (gtk_selection_data_get_length(selection_data) > 0) ) {
676 switch (target_type) {
678 gchar *str = (gchar*)gtk_selection_data_get_data(selection_data);
679 g_debug ("drag received string:%s \n", str);
681 // Convert string into GSList of individual entries for use with our open signal
682 gchar **entries = g_strsplit(str, "\r\n", 0);
683 GSList *filenames = NULL;
684 gint entry_runner = 0;
685 gchar *entry = entries[entry_runner];
687 if ( g_strcmp0 ( entry, "" ) ) {
688 // Drag+Drop gives URIs. And so in particular, %20 in place of spaces in filenames
689 // thus need to convert the text into a plain string
690 gchar *filename = g_filename_from_uri ( entry, NULL, NULL );
692 filenames = g_slist_append ( filenames, filename );
695 entry = entries[entry_runner];
699 g_signal_emit ( G_OBJECT(VIK_WINDOW_FROM_WIDGET(widget)), window_signals[VW_OPENWINDOW_SIGNAL], 0, filenames );
700 // NB: GSList & contents are freed by main.open_window
709 gtk_drag_finish ( context, success, FALSE, time );
712 static void toolbar_tool_cb ( GtkAction *old, GtkAction *current, gpointer gp )
714 VikWindow *vw = (VikWindow*)gp;
715 GtkAction *action = gtk_action_group_get_action ( vw->action_group, gtk_action_get_name(current) );
717 gtk_action_activate ( action );
720 static void toolbar_reload_cb ( GtkActionGroup *grp, gpointer gp )
722 VikWindow *vw = (VikWindow*)gp;
723 center_changed_cb ( vw );
726 #define VIK_SETTINGS_WIN_MAX "window_maximized"
727 #define VIK_SETTINGS_WIN_FULLSCREEN "window_fullscreen"
728 #define VIK_SETTINGS_WIN_WIDTH "window_width"
729 #define VIK_SETTINGS_WIN_HEIGHT "window_height"
730 #define VIK_SETTINGS_WIN_PANE_POSITION "window_horizontal_pane_position"
731 #define VIK_SETTINGS_WIN_SAVE_IMAGE_WIDTH "window_save_image_width"
732 #define VIK_SETTINGS_WIN_SAVE_IMAGE_HEIGHT "window_save_image_height"
733 #define VIK_SETTINGS_WIN_SAVE_IMAGE_PNG "window_save_image_as_png"
734 #define VIK_SETTINGS_WIN_COPY_CENTRE_FULL_FORMAT "window_copy_centre_full_format"
736 #define VIKING_ACCELERATOR_KEY_FILE "keys.rc"
738 static void vik_window_init ( VikWindow *vw )
740 vw->action_group = NULL;
742 vw->viking_vvp = vik_viewport_new();
743 vw->viking_vlp = vik_layers_panel_new();
744 vik_layers_panel_set_viewport ( vw->viking_vlp, vw->viking_vvp );
745 vw->viking_vs = vik_statusbar_new();
747 vw->vt = toolbox_create(vw);
748 vw->viking_vtb = vik_toolbar_new ();
749 window_create_ui(vw);
750 window_set_filename (vw, NULL);
752 vw->busy_cursor = gdk_cursor_new ( GDK_WATCH );
755 vw->loaded_type = LOAD_TYPE_READ_FAILURE; //AKA none
756 vw->modified = FALSE;
757 vw->only_updating_coord_mode_ui = FALSE;
759 vw->select_move = FALSE;
760 vw->pan_move = FALSE;
761 vw->pan_x = vw->pan_y = -1;
762 vw->single_click_pending = FALSE;
764 gint draw_image_width;
765 if ( a_settings_get_integer ( VIK_SETTINGS_WIN_SAVE_IMAGE_WIDTH, &draw_image_width ) )
766 vw->draw_image_width = draw_image_width;
768 vw->draw_image_width = DRAW_IMAGE_DEFAULT_WIDTH;
769 gint draw_image_height;
770 if ( a_settings_get_integer ( VIK_SETTINGS_WIN_SAVE_IMAGE_HEIGHT, &draw_image_height ) )
771 vw->draw_image_height = draw_image_height;
773 vw->draw_image_height = DRAW_IMAGE_DEFAULT_HEIGHT;
774 gboolean draw_image_save_as_png;
775 if ( a_settings_get_boolean ( VIK_SETTINGS_WIN_SAVE_IMAGE_PNG, &draw_image_save_as_png ) )
776 vw->draw_image_save_as_png = draw_image_save_as_png;
778 vw->draw_image_save_as_png = DRAW_IMAGE_DEFAULT_SAVE_AS_PNG;
780 vw->main_vbox = gtk_vbox_new(FALSE, 1);
781 gtk_container_add (GTK_CONTAINER (vw), vw->main_vbox);
782 vw->menu_hbox = gtk_hbox_new(FALSE, 1);
783 GtkWidget *menu_bar = gtk_ui_manager_get_widget (vw->uim, "/MainMenu");
784 gtk_box_pack_start (GTK_BOX(vw->menu_hbox), menu_bar, FALSE, TRUE, 0);
785 gtk_box_pack_start (GTK_BOX(vw->main_vbox), vw->menu_hbox, FALSE, TRUE, 0);
787 toolbar_init(vw->viking_vtb,
793 (gpointer)vw); // This auto packs toolbar into the vbox
794 // Must be performed post toolbar init
796 for (i=0; i<VIK_LAYER_NUM_TYPES; i++) {
797 for ( j = 0; j < vik_layer_get_interface(i)->tools_count; j++ ) {
798 toolbar_action_set_sensitive ( vw->viking_vtb, vik_layer_get_interface(i)->tools[j].radioActionEntry.name, FALSE );
802 vik_ext_tool_datasources_add_menu_items ( vw, vw->uim );
804 GtkWidget * zoom_levels = gtk_ui_manager_get_widget (vw->uim, "/MainMenu/View/SetZoom");
805 GtkWidget * zoom_levels_menu = create_zoom_menu_all_levels ( vik_viewport_get_zoom(vw->viking_vvp) );
806 gtk_menu_item_set_submenu (GTK_MENU_ITEM (zoom_levels), zoom_levels_menu);
807 g_signal_connect ( G_OBJECT(zoom_levels_menu), "selection-done", G_CALLBACK(zoom_changed), vw);
808 g_signal_connect_swapped ( G_OBJECT(vw->viking_vs), "clicked", G_CALLBACK(zoom_popup_handler), zoom_levels_menu );
810 g_signal_connect (G_OBJECT (vw), "delete_event", G_CALLBACK (delete_event), NULL);
813 g_signal_connect_swapped (G_OBJECT(vw->viking_vvp), "updated_center", G_CALLBACK(center_changed_cb), vw);
815 g_signal_connect_swapped (G_OBJECT(vw->viking_vvp), "expose_event", G_CALLBACK(draw_sync), vw);
816 g_signal_connect_swapped (G_OBJECT(vw->viking_vvp), "configure_event", G_CALLBACK(window_configure_event), vw);
817 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 );
818 g_signal_connect_swapped (G_OBJECT(vw->viking_vvp), "scroll_event", G_CALLBACK(draw_scroll), vw);
819 g_signal_connect_swapped (G_OBJECT(vw->viking_vvp), "button_press_event", G_CALLBACK(draw_click), vw);
820 g_signal_connect_swapped (G_OBJECT(vw->viking_vvp), "button_release_event", G_CALLBACK(draw_release), vw);
821 g_signal_connect_swapped (G_OBJECT(vw->viking_vvp), "motion_notify_event", G_CALLBACK(draw_mouse_motion), vw);
823 g_signal_connect_swapped (G_OBJECT(vw->viking_vlp), "update", G_CALLBACK(draw_update), vw);
824 g_signal_connect_swapped (G_OBJECT(vw->viking_vlp), "delete_layer", G_CALLBACK(vik_window_clear_highlight), vw);
826 // Allow key presses to be processed anywhere
827 g_signal_connect_swapped (G_OBJECT (vw), "key_press_event", G_CALLBACK (key_press_event), vw);
829 // Set initial button sensitivity
830 center_changed_cb ( vw );
832 vw->hpaned = gtk_hpaned_new ();
833 gtk_paned_pack1 ( GTK_PANED(vw->hpaned), GTK_WIDGET (vw->viking_vlp), FALSE, FALSE );
834 gtk_paned_pack2 ( GTK_PANED(vw->hpaned), GTK_WIDGET (vw->viking_vvp), TRUE, TRUE );
836 /* This packs the button into the window (a gtk container). */
837 gtk_box_pack_start (GTK_BOX(vw->main_vbox), vw->hpaned, TRUE, TRUE, 0);
839 gtk_box_pack_end (GTK_BOX(vw->main_vbox), GTK_WIDGET(vw->viking_vs), FALSE, TRUE, 0);
841 a_background_add_window ( vw );
843 window_list = g_slist_prepend ( window_list, vw);
845 gint height = VIKING_WINDOW_HEIGHT;
846 gint width = VIKING_WINDOW_WIDTH;
848 if ( a_vik_get_restore_window_state() ) {
849 if ( a_settings_get_integer ( VIK_SETTINGS_WIN_HEIGHT, &height ) ) {
850 // Enforce a basic minimum size
855 // No setting - so use default
856 height = VIKING_WINDOW_HEIGHT;
858 if ( a_settings_get_integer ( VIK_SETTINGS_WIN_WIDTH, &width ) ) {
859 // Enforce a basic minimum size
864 // No setting - so use default
865 width = VIKING_WINDOW_WIDTH;
868 if ( a_settings_get_boolean ( VIK_SETTINGS_WIN_MAX, &maxed ) )
870 gtk_window_maximize ( GTK_WINDOW(vw) );
873 if ( a_settings_get_boolean ( VIK_SETTINGS_WIN_FULLSCREEN, &full ) ) {
875 vw->show_full_screen = TRUE;
876 gtk_window_fullscreen ( GTK_WINDOW(vw) );
877 GtkWidget *check_box = gtk_ui_manager_get_widget ( vw->uim, "/ui/MainMenu/View/FullScreen" );
879 gtk_check_menu_item_set_active ( GTK_CHECK_MENU_ITEM(check_box), TRUE );
883 gint position = -1; // Let GTK determine default positioning
884 if ( !a_settings_get_integer ( VIK_SETTINGS_WIN_PANE_POSITION, &position ) ) {
887 gtk_paned_set_position ( GTK_PANED(vw->hpaned), position );
890 gtk_window_set_default_size ( GTK_WINDOW(vw), width, height );
894 vw->save_img_dia = NULL;
895 vw->save_img_dir_dia = NULL;
897 vw->show_side_panel = TRUE;
898 vw->show_statusbar = TRUE;
899 vw->show_toolbar = TRUE;
900 vw->show_main_menu = TRUE;
902 // Only accept Drag and Drop of files onto the viewport
903 gtk_drag_dest_set ( GTK_WIDGET(vw->viking_vvp), GTK_DEST_DEFAULT_ALL, NULL, 0, GDK_ACTION_COPY );
904 gtk_drag_dest_add_uri_targets ( GTK_WIDGET(vw->viking_vvp) );
905 g_signal_connect ( GTK_WIDGET(vw->viking_vvp), "drag-data-received", G_CALLBACK(drag_data_received_cb), NULL );
907 // Store the thread value so comparisons can be made to determine the gdk update method
908 // Hopefully we are storing the main thread value here :)
909 // [ATM any window initialization is always be performed by the main thread]
910 vw->thread = g_thread_self();
912 // Set the default tool + mode
913 gtk_action_activate ( gtk_action_group_get_action ( vw->action_group, "Pan" ) );
914 gtk_action_activate ( gtk_action_group_get_action ( vw->action_group, "ModeMercator" ) );
916 gchar *accel_file_name = g_build_filename ( a_get_viking_dir(), VIKING_ACCELERATOR_KEY_FILE, NULL );
917 gtk_accel_map_load ( accel_file_name );
918 g_free ( accel_file_name );
921 static VikWindow *window_new ()
923 return VIK_WINDOW ( g_object_new ( VIK_WINDOW_TYPE, NULL ) );
927 * Update the displayed map
928 * Only update the top most visible map layer
929 * ATM this assumes (as per defaults) the top most map has full alpha setting
930 * such that other other maps even though they may be active will not be seen
931 * It's more complicated to work out which maps are actually visible due to alpha settings
932 * and overkill for this simple refresh method.
934 static void simple_map_update ( VikWindow *vw, gboolean only_new )
936 // Find the most relevent single map layer to operate on
937 VikLayer *vl = vik_aggregate_layer_get_top_visible_layer_of_type (vik_layers_panel_get_top_layer(vw->viking_vlp), VIK_LAYER_MAPS);
939 vik_maps_layer_download ( VIK_MAPS_LAYER(vl), vw->viking_vvp, only_new );
943 * This is the global key press handler
944 * Global shortcuts are available at any time and hence are not restricted to when a certain tool is enabled
946 static gboolean key_press_event( VikWindow *vw, GdkEventKey *event, gpointer data )
948 // The keys handled here are not in the menuing system for a couple of reasons:
949 // . Keeps the menu size compact (alebit at expense of discoverably)
950 // . Allows differing key bindings to perform the same actions
952 // First decide if key events are related to the maps layer
953 gboolean map_download = FALSE;
954 gboolean map_download_only_new = TRUE; // Only new or reload
956 GdkModifierType modifiers = gtk_accelerator_get_default_mod_mask();
958 // Standard 'Refresh' keys: F5 or Ctrl+r
959 // Note 'F5' is actually handled via draw_refresh_cb() later on
960 // (not 'R' it's 'r' notice the case difference!!)
961 if ( event->keyval == GDK_r && (event->state & modifiers) == GDK_CONTROL_MASK ) {
963 map_download_only_new = TRUE;
965 // Full cache reload with Ctrl+F5 or Ctrl+Shift+r [This is not in the menu system]
966 // Note the use of uppercase R here since shift key has been pressed
967 else if ( (event->keyval == GDK_F5 && (event->state & modifiers) == GDK_CONTROL_MASK ) ||
968 ( event->keyval == GDK_R && (event->state & modifiers) == (GDK_CONTROL_MASK + GDK_SHIFT_MASK) ) ) {
970 map_download_only_new = FALSE;
972 // Standard Ctrl+KP+ / Ctrl+KP- to zoom in/out respectively
973 else if ( event->keyval == GDK_KEY_KP_Add && (event->state & modifiers) == GDK_CONTROL_MASK ) {
974 vik_viewport_zoom_in ( vw->viking_vvp );
976 return TRUE; // handled keypress
978 else if ( event->keyval == GDK_KEY_KP_Subtract && (event->state & modifiers) == GDK_CONTROL_MASK ) {
979 vik_viewport_zoom_out ( vw->viking_vvp );
981 return TRUE; // handled keypress
984 if ( map_download ) {
985 simple_map_update ( vw, map_download_only_new );
986 return TRUE; // handled keypress
989 VikLayer *vl = vik_layers_panel_get_selected ( vw->viking_vlp );
990 if (vl && vw->vt->active_tool != -1 && vw->vt->tools[vw->vt->active_tool].ti.key_press ) {
991 gint ltype = vw->vt->tools[vw->vt->active_tool].layer_type;
992 if ( vl && ltype == vl->type )
993 return vw->vt->tools[vw->vt->active_tool].ti.key_press(vl, event, vw->vt->tools[vw->vt->active_tool].state);
996 // Ensure called only on window tools (i.e. not on any of the Layer tools since the layer is NULL)
997 if ( vw->current_tool < TOOL_LAYER ) {
998 // No layer - but enable window tool keypress processing - these should be able to handle a NULL layer
999 if ( vw->vt->tools[vw->vt->active_tool].ti.key_press ) {
1000 return vw->vt->tools[vw->vt->active_tool].ti.key_press ( vl, event, vw->vt->tools[vw->vt->active_tool].state );
1004 /* Restore Main Menu via Escape key if the user has hidden it */
1005 /* This key is more likely to be used as they may not remember the function key */
1006 if ( event->keyval == GDK_Escape ) {
1007 GtkWidget *check_box = gtk_ui_manager_get_widget ( vw->uim, "/ui/MainMenu/View/SetShow/ViewMainMenu" );
1009 gboolean state = gtk_check_menu_item_get_active ( GTK_CHECK_MENU_ITEM(check_box) );
1011 gtk_widget_show ( gtk_ui_manager_get_widget ( vw->uim, "/ui/MainMenu" ) );
1012 gtk_check_menu_item_set_active ( GTK_CHECK_MENU_ITEM(check_box), TRUE );
1013 return TRUE; /* handled keypress */
1018 return FALSE; /* don't handle the keypress */
1021 static gboolean delete_event( VikWindow *vw )
1023 #ifdef VIKING_PROMPT_IF_MODIFIED
1030 dia = GTK_DIALOG ( gtk_message_dialog_new ( GTK_WINDOW(vw), GTK_DIALOG_DESTROY_WITH_PARENT, GTK_MESSAGE_QUESTION, GTK_BUTTONS_NONE,
1031 _("Do you want to save the changes you made to the document \"%s\"?\n"
1033 "Your changes will be lost if you don't save them."),
1034 window_get_filename ( vw ) ) );
1035 gtk_dialog_add_buttons ( dia, _("Don't Save"), GTK_RESPONSE_NO, GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, GTK_STOCK_SAVE, GTK_RESPONSE_YES, NULL );
1036 switch ( gtk_dialog_run ( dia ) )
1038 case GTK_RESPONSE_NO: gtk_widget_destroy ( GTK_WIDGET(dia) ); return FALSE;
1039 case GTK_RESPONSE_CANCEL: gtk_widget_destroy ( GTK_WIDGET(dia) ); return TRUE;
1040 default: gtk_widget_destroy ( GTK_WIDGET(dia) ); return ! save_file(NULL, vw);
1044 if ( window_count == 1 ) {
1045 // On the final window close - save latest state - if it's wanted...
1046 if ( a_vik_get_restore_window_state() ) {
1047 gint state = gdk_window_get_state ( GTK_WIDGET(vw)->window );
1048 gboolean state_max = state & GDK_WINDOW_STATE_MAXIMIZED;
1049 a_settings_set_boolean ( VIK_SETTINGS_WIN_MAX, state_max );
1051 gboolean state_fullscreen = state & GDK_WINDOW_STATE_FULLSCREEN;
1052 a_settings_set_boolean ( VIK_SETTINGS_WIN_FULLSCREEN, state_fullscreen );
1054 a_settings_set_boolean ( VIK_SETTINGS_WIN_SIDEPANEL, GTK_WIDGET_VISIBLE (GTK_WIDGET(vw->viking_vlp)) );
1056 a_settings_set_boolean ( VIK_SETTINGS_WIN_STATUSBAR, GTK_WIDGET_VISIBLE (GTK_WIDGET(vw->viking_vs)) );
1058 a_settings_set_boolean ( VIK_SETTINGS_WIN_TOOLBAR, GTK_WIDGET_VISIBLE (toolbar_get_widget(vw->viking_vtb)) );
1060 // If supersized - no need to save the enlarged width+height values
1061 if ( ! (state_fullscreen || state_max) ) {
1063 gtk_window_get_size ( GTK_WINDOW (vw), &width, &height );
1064 a_settings_set_integer ( VIK_SETTINGS_WIN_WIDTH, width );
1065 a_settings_set_integer ( VIK_SETTINGS_WIN_HEIGHT, height );
1068 a_settings_set_integer ( VIK_SETTINGS_WIN_PANE_POSITION, gtk_paned_get_position (GTK_PANED(vw->hpaned)) );
1071 a_settings_set_integer ( VIK_SETTINGS_WIN_SAVE_IMAGE_WIDTH, vw->draw_image_width );
1072 a_settings_set_integer ( VIK_SETTINGS_WIN_SAVE_IMAGE_HEIGHT, vw->draw_image_height );
1073 a_settings_set_boolean ( VIK_SETTINGS_WIN_SAVE_IMAGE_PNG, vw->draw_image_save_as_png );
1075 gchar *accel_file_name = g_build_filename ( a_get_viking_dir(), VIKING_ACCELERATOR_KEY_FILE, NULL );
1076 gtk_accel_map_save ( accel_file_name );
1077 g_free ( accel_file_name );
1084 static void newwindow_cb ( GtkAction *a, VikWindow *vw )
1086 g_signal_emit ( G_OBJECT(vw), window_signals[VW_NEWWINDOW_SIGNAL], 0 );
1089 static void draw_update ( VikWindow *vw )
1095 static void draw_sync ( VikWindow *vw )
1097 vik_viewport_sync(vw->viking_vvp);
1102 * Split the status update, as sometimes only need to update the tool part
1103 * also on initialization the zoom related stuff is not ready to be used
1105 static void draw_status_tool ( VikWindow *vw )
1107 if ( vw->current_tool == TOOL_LAYER )
1108 // Use tooltip rather than the internal name as the tooltip is i8n
1109 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 );
1111 vik_statusbar_set_message ( vw->viking_vs, VIK_STATUSBAR_TOOL, _(tool_names[vw->current_tool]) );
1114 static void draw_status ( VikWindow *vw )
1116 static gchar zoom_level[22];
1117 gdouble xmpp = vik_viewport_get_xmpp (vw->viking_vvp);
1118 gdouble ympp = vik_viewport_get_ympp(vw->viking_vvp);
1119 gchar *unit = vik_viewport_get_coord_mode(vw->viking_vvp) == VIK_COORD_UTM ? _("mpp") : _("pixelfact");
1121 g_snprintf ( zoom_level, 22, "%.3f/%.3f %s", xmpp, ympp, unit );
1123 if ( (int)xmpp - xmpp < 0.0 )
1124 g_snprintf ( zoom_level, 22, "%.3f %s", xmpp, unit );
1126 /* xmpp should be a whole number so don't show useless .000 bit */
1127 g_snprintf ( zoom_level, 22, "%d %s", (int)xmpp, unit );
1129 vik_statusbar_set_message ( vw->viking_vs, VIK_STATUSBAR_ZOOM, zoom_level );
1131 draw_status_tool ( vw );
1134 void vik_window_set_redraw_trigger(VikLayer *vl)
1136 VikWindow *vw = VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vl));
1141 static void window_configure_event ( VikWindow *vw )
1143 static int first = 1;
1146 // This is a hack to set the cursor corresponding to the first tool
1147 // FIXME find the correct way to initialize both tool and its cursor
1149 vw->viewport_cursor = (GdkCursor *)toolbox_get_cursor(vw->vt, "Pan");
1150 /* We set cursor, even if it is NULL: it resets to default */
1151 gdk_window_set_cursor ( gtk_widget_get_window(GTK_WIDGET(vw->viking_vvp)), vw->viewport_cursor );
1155 static void draw_redraw ( VikWindow *vw )
1157 VikCoord old_center = vw->trigger_center;
1158 vw->trigger_center = *(vik_viewport_get_center(vw->viking_vvp));
1159 VikLayer *new_trigger = vw->trigger;
1161 VikLayer *old_trigger = VIK_LAYER(vik_viewport_get_trigger(vw->viking_vvp));
1163 if ( ! new_trigger )
1164 ; /* do nothing -- have to redraw everything. */
1165 else if ( (old_trigger != new_trigger) || !vik_coord_equals(&old_center, &vw->trigger_center) || (new_trigger->type == VIK_LAYER_AGGREGATE) )
1166 vik_viewport_set_trigger ( vw->viking_vvp, new_trigger ); /* todo: set to half_drawn mode if new trigger is above old */
1168 vik_viewport_set_half_drawn ( vw->viking_vvp, TRUE );
1171 vik_viewport_clear ( vw->viking_vvp);
1172 // Main layer drawing
1173 vik_layers_panel_draw_all ( vw->viking_vlp );
1174 // Draw highlight (possibly again but ensures it is on top - especially for when tracks overlap)
1175 if ( vik_viewport_get_draw_highlight (vw->viking_vvp) ) {
1176 if ( vw->containing_vtl && (vw->selected_tracks || vw->selected_waypoints ) ) {
1177 vik_trw_layer_draw_highlight_items ( vw->containing_vtl, vw->selected_tracks, vw->selected_waypoints, vw->viking_vvp );
1179 else if ( vw->containing_vtl && (vw->selected_track || vw->selected_waypoint) ) {
1180 vik_trw_layer_draw_highlight_item ( vw->containing_vtl, vw->selected_track, vw->selected_waypoint, vw->viking_vvp );
1182 else if ( vw->selected_vtl ) {
1183 vik_trw_layer_draw_highlight ( vw->selected_vtl, vw->viking_vvp );
1186 // Other viewport decoration items on top if they are enabled/in use
1187 vik_viewport_draw_scale ( vw->viking_vvp );
1188 vik_viewport_draw_copyright ( vw->viking_vvp );
1189 vik_viewport_draw_centermark ( vw->viking_vvp );
1190 vik_viewport_draw_logo ( vw->viking_vvp );
1192 vik_viewport_set_half_drawn ( vw->viking_vvp, FALSE ); /* just in case. */
1195 gboolean draw_buf_done = TRUE;
1197 static gboolean draw_buf(gpointer data)
1199 gpointer *pass_along = data;
1200 gdk_threads_enter();
1201 gdk_draw_drawable (pass_along[0], pass_along[1],
1202 pass_along[2], 0, 0, 0, 0, -1, -1);
1203 draw_buf_done = TRUE;
1204 gdk_threads_leave();
1209 /* Mouse event handlers ************************************************************************/
1211 static void vik_window_pan_click (VikWindow *vw, GdkEventButton *event)
1213 /* set panning origin */
1214 vw->pan_move = FALSE;
1215 vw->pan_x = (gint) event->x;
1216 vw->pan_y = (gint) event->y;
1219 static void draw_click (VikWindow *vw, GdkEventButton *event)
1221 gtk_widget_grab_focus ( GTK_WIDGET(vw->viking_vvp) );
1223 /* middle button pressed. we reserve all middle button and scroll events
1224 * for panning and zooming; tools only get left/right/movement
1226 if ( event->button == 2) {
1227 if ( vw->vt->tools[vw->vt->active_tool].ti.pan_handler )
1228 // Tool still may need to do something (such as disable something)
1229 toolbox_click(vw->vt, event);
1230 vik_window_pan_click ( vw, event );
1233 toolbox_click(vw->vt, event);
1237 static void vik_window_pan_move (VikWindow *vw, GdkEventMotion *event)
1239 if ( vw->pan_x != -1 ) {
1240 vik_viewport_set_center_screen ( vw->viking_vvp, vik_viewport_get_width(vw->viking_vvp)/2 - event->x + vw->pan_x,
1241 vik_viewport_get_height(vw->viking_vvp)/2 - event->y + vw->pan_y );
1242 vw->pan_move = TRUE;
1243 vw->pan_x = event->x;
1244 vw->pan_y = event->y;
1250 * get_location_strings:
1252 * Utility function to get positional strings for the given location
1253 * lat and lon strings will get allocated and so need to be freed after use
1255 static void get_location_strings ( VikWindow *vw, struct UTM utm, gchar **lat, gchar **lon )
1257 if ( vik_viewport_get_drawmode ( vw->viking_vvp ) == VIK_VIEWPORT_DRAWMODE_UTM ) {
1258 // Reuse lat for the first part (Zone + N or S, and lon for the second part (easting and northing) of a UTM format:
1259 // ZONE[N|S] EASTING NORTHING
1260 *lat = g_malloc(4*sizeof(gchar));
1261 // NB zone is stored in a char but is an actual number
1262 g_snprintf (*lat, 4, "%d%c", utm.zone, utm.letter);
1263 *lon = g_malloc(16*sizeof(gchar));
1264 g_snprintf (*lon, 16, "%d %d", (gint)utm.easting, (gint)utm.northing);
1268 a_coords_utm_to_latlon ( &utm, &ll );
1269 a_coords_latlon_to_string ( &ll, lat, lon );
1273 static void draw_mouse_motion (VikWindow *vw, GdkEventMotion *event)
1275 static VikCoord coord;
1276 static struct UTM utm;
1277 #define BUFFER_SIZE 50
1278 static char pointer_buf[BUFFER_SIZE];
1279 gchar *lat = NULL, *lon = NULL;
1282 VikDemInterpol interpol_method;
1284 /* This is a hack, but work far the best, at least for single pointer systems.
1285 * See http://bugzilla.gnome.org/show_bug.cgi?id=587714 for more. */
1287 gdk_window_get_pointer (event->window, &x, &y, NULL);
1291 toolbox_move(vw->vt, event);
1293 vik_viewport_screen_to_coord ( vw->viking_vvp, event->x, event->y, &coord );
1294 vik_coord_to_utm ( &coord, &utm );
1296 get_location_strings ( vw, utm, &lat, &lon );
1298 /* Change interpolate method according to scale */
1299 zoom = vik_viewport_get_zoom(vw->viking_vvp);
1301 interpol_method = VIK_DEM_INTERPOL_NONE;
1302 else if (zoom >= 1.0)
1303 interpol_method = VIK_DEM_INTERPOL_SIMPLE;
1305 interpol_method = VIK_DEM_INTERPOL_BEST;
1306 if ((alt = a_dems_get_elev_by_coord(&coord, interpol_method)) != VIK_DEM_INVALID_ELEVATION) {
1307 if ( a_vik_get_units_height () == VIK_UNITS_HEIGHT_METRES )
1308 g_snprintf ( pointer_buf, BUFFER_SIZE, _("%s %s %dm"), lat, lon, alt );
1310 g_snprintf ( pointer_buf, BUFFER_SIZE, _("%s %s %dft"), lat, lon, (int)VIK_METERS_TO_FEET(alt) );
1313 g_snprintf ( pointer_buf, BUFFER_SIZE, _("%s %s"), lat, lon );
1318 vik_statusbar_set_message ( vw->viking_vs, VIK_STATUSBAR_POSITION, pointer_buf );
1320 vik_window_pan_move ( vw, event );
1322 /* This is recommended by the GTK+ documentation, but does not work properly.
1323 * Use deprecated way until GTK+ gets a solution for correct motion hint handling:
1324 * http://bugzilla.gnome.org/show_bug.cgi?id=587714
1326 /* gdk_event_request_motions ( event ); */
1330 * Action the single click after a small timeout
1331 * If a double click has occurred then this will do nothing
1333 static gboolean vik_window_pan_timeout (VikWindow *vw)
1335 if ( ! vw->single_click_pending ) {
1336 // Double click happened, so don't do anything
1340 /* set panning origin */
1341 vw->pan_move = FALSE;
1342 vw->single_click_pending = FALSE;
1343 vik_viewport_set_center_screen ( vw->viking_vvp, vw->delayed_pan_x, vw->delayed_pan_y );
1346 // Really turn off the pan moving!!
1347 vw->pan_x = vw->pan_y = -1;
1351 static void vik_window_pan_release ( VikWindow *vw, GdkEventButton *event )
1353 gboolean do_draw = TRUE;
1355 if ( vw->pan_move == FALSE ) {
1356 vw->single_click_pending = !vw->single_click_pending;
1358 if ( vw->single_click_pending ) {
1359 // Store offset to use
1360 vw->delayed_pan_x = vw->pan_x;
1361 vw->delayed_pan_y = vw->pan_y;
1362 // Get double click time
1363 GtkSettings *gs = gtk_widget_get_settings ( GTK_WIDGET(vw) );
1364 GValue dct = { 0 }; // = G_VALUE_INIT; // GLIB 2.30+ only
1365 g_value_init ( &dct, G_TYPE_INT );
1366 g_object_get_property ( G_OBJECT(gs), "gtk-double-click-time", &dct );
1367 // Give chance for a double click to occur
1368 gint timer = g_value_get_int ( &dct ) + 50;
1369 g_timeout_add ( timer, (GSourceFunc)vik_window_pan_timeout, vw );
1373 vik_viewport_set_center_screen ( vw->viking_vvp, vw->pan_x, vw->pan_y );
1377 vik_viewport_set_center_screen ( vw->viking_vvp, vik_viewport_get_width(vw->viking_vvp)/2 - event->x + vw->pan_x,
1378 vik_viewport_get_height(vw->viking_vvp)/2 - event->y + vw->pan_y );
1381 vw->pan_move = FALSE;
1382 vw->pan_x = vw->pan_y = -1;
1387 static void draw_release ( VikWindow *vw, GdkEventButton *event )
1389 gtk_widget_grab_focus ( GTK_WIDGET(vw->viking_vvp) );
1391 if ( event->button == 2 ) { /* move / pan */
1392 if ( vw->vt->tools[vw->vt->active_tool].ti.pan_handler )
1393 // Tool still may need to do something (such as reenable something)
1394 toolbox_release(vw->vt, event);
1395 vik_window_pan_release ( vw, event );
1398 toolbox_release(vw->vt, event);
1402 static void draw_scroll (VikWindow *vw, GdkEventScroll *event)
1404 guint modifiers = event->state & (GDK_SHIFT_MASK | GDK_CONTROL_MASK);
1405 if ( modifiers == GDK_CONTROL_MASK ) {
1406 /* control == pan up & down */
1407 if ( event->direction == GDK_SCROLL_UP )
1408 vik_viewport_set_center_screen ( vw->viking_vvp, vik_viewport_get_width(vw->viking_vvp)/2, vik_viewport_get_height(vw->viking_vvp)/3 );
1410 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 );
1411 } else if ( modifiers == GDK_SHIFT_MASK ) {
1412 /* shift == pan left & right */
1413 if ( event->direction == GDK_SCROLL_UP )
1414 vik_viewport_set_center_screen ( vw->viking_vvp, vik_viewport_get_width(vw->viking_vvp)/3, vik_viewport_get_height(vw->viking_vvp)/2 );
1416 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 );
1417 } else if ( modifiers == (GDK_CONTROL_MASK | GDK_SHIFT_MASK) ) {
1418 // This zoom is on the center position
1419 if ( event->direction == GDK_SCROLL_UP )
1420 vik_viewport_zoom_in (vw->viking_vvp);
1422 vik_viewport_zoom_out (vw->viking_vvp);
1424 /* make sure mouse is still over the same point on the map when we zoom */
1427 gint center_x = vik_viewport_get_width ( vw->viking_vvp ) / 2;
1428 gint center_y = vik_viewport_get_height ( vw->viking_vvp ) / 2;
1429 vik_viewport_screen_to_coord ( vw->viking_vvp, event->x, event->y, &coord );
1430 if ( event->direction == GDK_SCROLL_UP )
1431 vik_viewport_zoom_in (vw->viking_vvp);
1433 vik_viewport_zoom_out(vw->viking_vvp);
1434 vik_viewport_coord_to_screen ( vw->viking_vvp, &coord, &x, &y );
1435 vik_viewport_set_center_screen ( vw->viking_vvp, center_x + (x - event->x),
1436 center_y + (y - event->y) );
1444 /********************************************************************************
1446 ********************************************************************************/
1447 static void draw_ruler(VikViewport *vvp, GdkDrawable *d, GdkGC *gc, gint x1, gint y1, gint x2, gint y2, gdouble distance)
1451 GdkGC *labgc = vik_viewport_new_gc ( vvp, "#cccccc", 1);
1452 GdkGC *thickgc = gdk_gc_new(d);
1454 gdouble len = sqrt((x1-x2)*(x1-x2) + (y1-y2)*(y1-y2));
1455 gdouble dx = (x2-x1)/len*10;
1456 gdouble dy = (y2-y1)/len*10;
1457 gdouble c = cos(DEG2RAD(15.0));
1458 gdouble s = sin(DEG2RAD(15.0));
1460 gdouble baseangle = 0;
1463 /* draw line with arrow ends */
1465 gint tmp_x1=x1, tmp_y1=y1, tmp_x2=x2, tmp_y2=y2;
1466 a_viewport_clip_line(&tmp_x1, &tmp_y1, &tmp_x2, &tmp_y2);
1467 gdk_draw_line(d, gc, tmp_x1, tmp_y1, tmp_x2, tmp_y2);
1470 a_viewport_clip_line(&x1, &y1, &x2, &y2);
1471 gdk_draw_line(d, gc, x1, y1, x2, y2);
1473 gdk_draw_line(d, gc, x1 - dy, y1 + dx, x1 + dy, y1 - dx);
1474 gdk_draw_line(d, gc, x2 - dy, y2 + dx, x2 + dy, y2 - dx);
1475 gdk_draw_line(d, gc, x2, y2, x2 - (dx * c + dy * s), y2 - (dy * c - dx * s));
1476 gdk_draw_line(d, gc, x2, y2, x2 - (dx * c - dy * s), y2 - (dy * c + dx * s));
1477 gdk_draw_line(d, gc, x1, y1, x1 + (dx * c + dy * s), y1 + (dy * c - dx * s));
1478 gdk_draw_line(d, gc, x1, y1, x1 + (dx * c - dy * s), y1 + (dy * c + dx * s));
1484 vik_viewport_compute_bearing ( vvp, x1, y1, x2, y2, &angle, &baseangle );
1488 gdk_gc_copy(thickgc, gc);
1489 gdk_gc_set_line_attributes(thickgc, CW, GDK_LINE_SOLID, GDK_CAP_BUTT, GDK_JOIN_MITER);
1490 gdk_color_parse("#2255cc", &color);
1491 gdk_gc_set_rgb_fg_color(thickgc, &color);
1493 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);
1496 gdk_gc_copy(thickgc, gc);
1497 gdk_gc_set_line_attributes(thickgc, 2, GDK_LINE_SOLID, GDK_CAP_BUTT, GDK_JOIN_MITER);
1498 for (i=0; i<180; i++) {
1499 c = cos(DEG2RAD(i)*2 + baseangle);
1500 s = sin(DEG2RAD(i)*2 + baseangle);
1503 gdk_draw_line (d, gc, x1 + CR*c, y1 + CR*s, x1 + (CR+CW)*c, y1 + (CR+CW)*s);
1505 gdouble ticksize = 2*CW;
1506 gdk_draw_line (d, thickgc, x1 + (CR-CW)*c, y1 + (CR-CW)*s, x1 + (CR+ticksize)*c, y1 + (CR+ticksize)*s);
1510 gdk_draw_arc (d, gc, FALSE, x1-CR, y1-CR, 2*CR, 2*CR, 0, 64*360);
1511 gdk_draw_arc (d, gc, FALSE, x1-CR-CW, y1-CR-CW, 2*(CR+CW), 2*(CR+CW), 0, 64*360);
1512 gdk_draw_arc (d, gc, FALSE, x1-CR+CW, y1-CR+CW, 2*(CR-CW), 2*(CR-CW), 0, 64*360);
1513 c = (CR+CW*2)*cos(baseangle);
1514 s = (CR+CW*2)*sin(baseangle);
1515 gdk_draw_line (d, gc, x1-c, y1-s, x1+c, y1+s);
1516 gdk_draw_line (d, gc, x1+s, y1-c, x1-s, y1+c);
1519 #define LABEL(x, y, w, h) { \
1520 gdk_draw_rectangle(d, labgc, TRUE, (x)-2, (y)-1, (w)+4, (h)+1); \
1521 gdk_draw_rectangle(d, gc, FALSE, (x)-2, (y)-1, (w)+4, (h)+1); \
1522 gdk_draw_layout(d, gc, (x), (y), pl); }
1524 gint wd, hd, xd, yd;
1525 gint wb, hb, xb, yb;
1527 pl = gtk_widget_create_pango_layout (GTK_WIDGET(vvp), NULL);
1528 pango_layout_set_font_description (pl, gtk_widget_get_style(GTK_WIDGET(vvp))->font_desc);
1529 pango_layout_set_text(pl, "N", -1);
1530 gdk_draw_layout(d, gc, x1-5, y1-CR-3*CW-8, pl);
1532 /* draw label with distance */
1533 vik_units_distance_t dist_units = a_vik_get_units_distance ();
1534 switch (dist_units) {
1535 case VIK_UNITS_DISTANCE_KILOMETRES:
1536 if (distance >= 1000 && distance < 100000) {
1537 g_sprintf(str, "%3.2f km", distance/1000.0);
1538 } else if (distance < 1000) {
1539 g_sprintf(str, "%d m", (int)distance);
1541 g_sprintf(str, "%d km", (int)distance/1000);
1544 case VIK_UNITS_DISTANCE_MILES:
1545 if (distance >= VIK_MILES_TO_METERS(1) && distance < VIK_MILES_TO_METERS(100)) {
1546 g_sprintf(str, "%3.2f miles", VIK_METERS_TO_MILES(distance));
1547 } else if (distance < VIK_MILES_TO_METERS(1)) {
1548 g_sprintf(str, "%d yards", (int)(distance*1.0936133));
1550 g_sprintf(str, "%d miles", (int)VIK_METERS_TO_MILES(distance));
1553 case VIK_UNITS_DISTANCE_NAUTICAL_MILES:
1554 if (distance >= VIK_NAUTICAL_MILES_TO_METERS(1) && distance < VIK_NAUTICAL_MILES_TO_METERS(100)) {
1555 g_sprintf(str, "%3.2f NM", VIK_METERS_TO_NAUTICAL_MILES(distance));
1556 } else if (distance < VIK_NAUTICAL_MILES_TO_METERS(1)) {
1557 g_sprintf(str, "%d yards", (int)(distance*1.0936133));
1559 g_sprintf(str, "%d NM", (int)VIK_METERS_TO_NAUTICAL_MILES(distance));
1563 g_critical("Houston, we've had a problem. distance=%d", dist_units);
1566 pango_layout_set_text(pl, str, -1);
1568 pango_layout_get_pixel_size ( pl, &wd, &hd );
1570 xd = (x1+x2)/2 + dy;
1571 yd = (y1+y2)/2 - hd/2 - dx;
1573 xd = (x1+x2)/2 - dy;
1574 yd = (y1+y2)/2 - hd/2 + dx;
1577 if ( xd < -5 || yd < -5 || xd > vik_viewport_get_width(vvp)+5 || yd > vik_viewport_get_height(vvp)+5 ) {
1582 LABEL(xd, yd, wd, hd);
1584 /* draw label with bearing */
1585 g_sprintf(str, "%3.1f°", RAD2DEG(angle));
1586 pango_layout_set_text(pl, str, -1);
1587 pango_layout_get_pixel_size ( pl, &wb, &hb );
1588 xb = x1 + CR*cos(angle-M_PI_2);
1589 yb = y1 + CR*sin(angle-M_PI_2);
1591 if ( xb < -5 || yb < -5 || xb > vik_viewport_get_width(vvp)+5 || yb > vik_viewport_get_height(vvp)+5 ) {
1597 GdkRectangle r1 = {xd-2, yd-1, wd+4, hd+1}, r2 = {xb-2, yb-1, wb+4, hb+1};
1598 if (gdk_rectangle_intersect(&r1, &r2, &r2)) {
1602 LABEL(xb, yb, wb, hb);
1606 g_object_unref ( G_OBJECT ( pl ) );
1607 g_object_unref ( G_OBJECT ( labgc ) );
1608 g_object_unref ( G_OBJECT ( thickgc ) );
1614 gboolean has_oldcoord;
1616 } ruler_tool_state_t;
1618 static gpointer ruler_create (VikWindow *vw, VikViewport *vvp)
1620 ruler_tool_state_t *s = g_new(ruler_tool_state_t, 1);
1623 s->has_oldcoord = FALSE;
1627 static void ruler_destroy (ruler_tool_state_t *s)
1632 static VikLayerToolFuncStatus ruler_click (VikLayer *vl, GdkEventButton *event, ruler_tool_state_t *s)
1637 if ( event->button == 1 ) {
1638 gchar *lat=NULL, *lon=NULL;
1639 vik_viewport_screen_to_coord ( s->vvp, (gint) event->x, (gint) event->y, &coord );
1640 vik_coord_to_latlon ( &coord, &ll );
1641 a_coords_latlon_to_string ( &ll, &lat, &lon );
1642 if ( s->has_oldcoord ) {
1643 vik_units_distance_t dist_units = a_vik_get_units_distance ();
1644 switch (dist_units) {
1645 case VIK_UNITS_DISTANCE_KILOMETRES:
1646 temp = g_strdup_printf ( "%s %s DIFF %f meters", lat, lon, vik_coord_diff( &coord, &(s->oldcoord) ) );
1648 case VIK_UNITS_DISTANCE_MILES:
1649 temp = g_strdup_printf ( "%s %s DIFF %f miles", lat, lon, VIK_METERS_TO_MILES(vik_coord_diff( &coord, &(s->oldcoord) )) );
1651 case VIK_UNITS_DISTANCE_NAUTICAL_MILES:
1652 temp = g_strdup_printf ( "%s %s DIFF %f NM", lat, lon, VIK_METERS_TO_NAUTICAL_MILES(vik_coord_diff( &coord, &(s->oldcoord) )) );
1655 temp = g_strdup_printf ("Just to keep the compiler happy");
1656 g_critical("Houston, we've had a problem. distance=%d", dist_units);
1659 s->has_oldcoord = FALSE;
1662 temp = g_strdup_printf ( "%s %s", lat, lon );
1663 s->has_oldcoord = TRUE;
1666 vik_statusbar_set_message ( s->vw->viking_vs, VIK_STATUSBAR_INFO, temp );
1669 s->oldcoord = coord;
1672 vik_viewport_set_center_screen ( s->vvp, (gint) event->x, (gint) event->y );
1673 draw_update ( s->vw );
1675 return VIK_LAYER_TOOL_ACK;
1678 static VikLayerToolFuncStatus ruler_move (VikLayer *vl, GdkEventMotion *event, ruler_tool_state_t *s)
1680 VikViewport *vvp = s->vvp;
1681 VikWindow *vw = s->vw;
1687 if ( s->has_oldcoord ) {
1688 int oldx, oldy, w1, h1, w2, h2;
1689 static GdkPixmap *buf = NULL;
1690 gchar *lat=NULL, *lon=NULL;
1691 w1 = vik_viewport_get_width(vvp);
1692 h1 = vik_viewport_get_height(vvp);
1694 buf = gdk_pixmap_new ( gtk_widget_get_window(GTK_WIDGET(vvp)), w1, h1, -1 );
1696 gdk_drawable_get_size(buf, &w2, &h2);
1697 if (w1 != w2 || h1 != h2) {
1698 g_object_unref ( G_OBJECT ( buf ) );
1699 buf = gdk_pixmap_new ( gtk_widget_get_window(GTK_WIDGET(vvp)), w1, h1, -1 );
1702 vik_viewport_screen_to_coord ( vvp, (gint) event->x, (gint) event->y, &coord );
1703 vik_coord_to_latlon ( &coord, &ll );
1704 vik_viewport_coord_to_screen ( vvp, &s->oldcoord, &oldx, &oldy );
1706 gdk_draw_drawable (buf, gtk_widget_get_style(GTK_WIDGET(vvp))->black_gc,
1707 vik_viewport_get_pixmap(vvp), 0, 0, 0, 0, -1, -1);
1708 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)) );
1709 if (draw_buf_done) {
1710 static gpointer pass_along[3];
1711 pass_along[0] = gtk_widget_get_window(GTK_WIDGET(vvp));
1712 pass_along[1] = gtk_widget_get_style(GTK_WIDGET(vvp))->black_gc;
1713 pass_along[2] = buf;
1714 g_idle_add_full (G_PRIORITY_HIGH_IDLE + 10, draw_buf, pass_along, NULL);
1715 draw_buf_done = FALSE;
1717 a_coords_latlon_to_string(&ll, &lat, &lon);
1718 vik_units_distance_t dist_units = a_vik_get_units_distance ();
1719 switch (dist_units) {
1720 case VIK_UNITS_DISTANCE_KILOMETRES:
1721 temp = g_strdup_printf ( "%s %s DIFF %f meters", lat, lon, vik_coord_diff( &coord, &(s->oldcoord) ) );
1723 case VIK_UNITS_DISTANCE_MILES:
1724 temp = g_strdup_printf ( "%s %s DIFF %f miles", lat, lon, VIK_METERS_TO_MILES (vik_coord_diff( &coord, &(s->oldcoord) )) );
1726 case VIK_UNITS_DISTANCE_NAUTICAL_MILES:
1727 temp = g_strdup_printf ( "%s %s DIFF %f NM", lat, lon, VIK_METERS_TO_NAUTICAL_MILES (vik_coord_diff( &coord, &(s->oldcoord) )) );
1730 temp = g_strdup_printf ("Just to keep the compiler happy");
1731 g_critical("Houston, we've had a problem. distance=%d", dist_units);
1733 vik_statusbar_set_message ( vw->viking_vs, VIK_STATUSBAR_INFO, temp );
1736 return VIK_LAYER_TOOL_ACK;
1739 static VikLayerToolFuncStatus ruler_release (VikLayer *vl, GdkEventButton *event, ruler_tool_state_t *s)
1741 return VIK_LAYER_TOOL_ACK;
1744 static void ruler_deactivate (VikLayer *vl, ruler_tool_state_t *s)
1746 draw_update ( s->vw );
1749 static gboolean ruler_key_press (VikLayer *vl, GdkEventKey *event, ruler_tool_state_t *s)
1751 if (event->keyval == GDK_Escape) {
1752 s->has_oldcoord = FALSE;
1753 ruler_deactivate ( vl, s );
1756 // Regardless of whether we used it, return false so other GTK things may use it
1760 static VikToolInterface ruler_tool =
1761 // NB Ctrl+Shift+R is used for Refresh (deemed more important), so use 'U' instead
1762 { { "Ruler", "vik-icon-ruler", N_("_Ruler"), "<control><shift>U", N_("Ruler Tool"), 2 },
1763 (VikToolConstructorFunc) ruler_create,
1764 (VikToolDestructorFunc) ruler_destroy,
1765 (VikToolActivationFunc) NULL,
1766 (VikToolActivationFunc) ruler_deactivate,
1767 (VikToolMouseFunc) ruler_click,
1768 (VikToolMouseMoveFunc) ruler_move,
1769 (VikToolMouseFunc) ruler_release,
1770 (VikToolKeyFunc) ruler_key_press,
1772 GDK_CURSOR_IS_PIXMAP,
1773 &cursor_ruler_pixbuf,
1775 /*** end ruler code ********************************************************/
1779 /********************************************************************************
1781 ********************************************************************************/
1786 // Track zoom bounds for zoom tool with shift modifier:
1787 gboolean bounds_active;
1790 } zoom_tool_state_t;
1793 * In case the screen size has changed
1795 static void zoomtool_resize_pixmap (zoom_tool_state_t *zts)
1799 // Allocate a drawing area the size of the viewport
1800 w1 = vik_viewport_get_width ( zts->vw->viking_vvp );
1801 h1 = vik_viewport_get_height ( zts->vw->viking_vvp );
1803 if ( !zts->pixmap ) {
1805 zts->pixmap = gdk_pixmap_new ( gtk_widget_get_window(GTK_WIDGET(zts->vw->viking_vvp)), w1, h1, -1 );
1808 gdk_drawable_get_size ( zts->pixmap, &w2, &h2 );
1810 if ( w1 != w2 || h1 != h2 ) {
1811 // Has changed - delete and recreate with new values
1812 g_object_unref ( G_OBJECT ( zts->pixmap ) );
1813 zts->pixmap = gdk_pixmap_new ( gtk_widget_get_window(GTK_WIDGET(zts->vw->viking_vvp)), w1, h1, -1 );
1817 static gpointer zoomtool_create (VikWindow *vw, VikViewport *vvp)
1819 zoom_tool_state_t *zts = g_new(zoom_tool_state_t, 1);
1824 zts->bounds_active = FALSE;
1828 static void zoomtool_destroy ( zoom_tool_state_t *zts)
1831 g_object_unref ( G_OBJECT ( zts->pixmap ) );
1835 static VikLayerToolFuncStatus zoomtool_click (VikLayer *vl, GdkEventButton *event, zoom_tool_state_t *zts)
1837 zts->vw->modified = TRUE;
1838 guint modifiers = event->state & (GDK_SHIFT_MASK | GDK_CONTROL_MASK);
1842 gint center_x = vik_viewport_get_width ( zts->vw->viking_vvp ) / 2;
1843 gint center_y = vik_viewport_get_height ( zts->vw->viking_vvp ) / 2;
1845 gboolean skip_update = FALSE;
1847 zts->bounds_active = FALSE;
1849 if ( modifiers == (GDK_CONTROL_MASK | GDK_SHIFT_MASK) ) {
1850 // This zoom is on the center position
1851 vik_viewport_set_center_screen ( zts->vw->viking_vvp, center_x, center_y );
1852 if ( event->button == 1 )
1853 vik_viewport_zoom_in (zts->vw->viking_vvp);
1854 else if ( event->button == 3 )
1855 vik_viewport_zoom_out (zts->vw->viking_vvp);
1857 else if ( modifiers == GDK_CONTROL_MASK ) {
1858 // This zoom is to recenter on the mouse position
1859 vik_viewport_set_center_screen ( zts->vw->viking_vvp, (gint) event->x, (gint) event->y );
1860 if ( event->button == 1 )
1861 vik_viewport_zoom_in (zts->vw->viking_vvp);
1862 else if ( event->button == 3 )
1863 vik_viewport_zoom_out (zts->vw->viking_vvp);
1865 else if ( modifiers == GDK_SHIFT_MASK ) {
1866 // Get start of new zoom bounds
1867 if ( event->button == 1 ) {
1868 zts->bounds_active = TRUE;
1869 zts->start_x = (gint) event->x;
1870 zts->start_y = (gint) event->y;
1875 /* make sure mouse is still over the same point on the map when we zoom */
1876 vik_viewport_screen_to_coord ( zts->vw->viking_vvp, event->x, event->y, &coord );
1877 if ( event->button == 1 )
1878 vik_viewport_zoom_in (zts->vw->viking_vvp);
1879 else if ( event->button == 3 )
1880 vik_viewport_zoom_out(zts->vw->viking_vvp);
1881 vik_viewport_coord_to_screen ( zts->vw->viking_vvp, &coord, &x, &y );
1882 vik_viewport_set_center_screen ( zts->vw->viking_vvp,
1883 center_x + (x - event->x),
1884 center_y + (y - event->y) );
1888 draw_update ( zts->vw );
1890 return VIK_LAYER_TOOL_ACK;
1893 static VikLayerToolFuncStatus zoomtool_move (VikLayer *vl, GdkEventMotion *event, zoom_tool_state_t *zts)
1895 guint modifiers = event->state & (GDK_SHIFT_MASK | GDK_CONTROL_MASK);
1897 if ( zts->bounds_active && modifiers == GDK_SHIFT_MASK ) {
1898 zoomtool_resize_pixmap ( zts );
1900 // Blank out currently drawn area
1901 gdk_draw_drawable ( zts->pixmap,
1902 gtk_widget_get_style(GTK_WIDGET(zts->vw->viking_vvp))->black_gc,
1903 vik_viewport_get_pixmap(zts->vw->viking_vvp),
1904 0, 0, 0, 0, -1, -1);
1906 // Calculate new box starting point & size in pixels
1907 int xx, yy, width, height;
1908 if ( event->y > zts->start_y ) {
1910 height = event->y-zts->start_y;
1914 height = zts->start_y-event->y;
1916 if ( event->x > zts->start_x ) {
1918 width = event->x-zts->start_x;
1922 width = zts->start_x-event->x;
1926 gdk_draw_rectangle (zts->pixmap, gtk_widget_get_style(GTK_WIDGET(zts->vw->viking_vvp))->black_gc, FALSE, xx, yy, width, height);
1928 // Only actually draw when there's time to do so
1929 if (draw_buf_done) {
1930 static gpointer pass_along[3];
1931 pass_along[0] = gtk_widget_get_window(GTK_WIDGET(zts->vw->viking_vvp));
1932 pass_along[1] = gtk_widget_get_style(GTK_WIDGET(zts->vw->viking_vvp))->black_gc;
1933 pass_along[2] = zts->pixmap;
1934 g_idle_add_full (G_PRIORITY_HIGH_IDLE + 10, draw_buf, pass_along, NULL);
1935 draw_buf_done = FALSE;
1939 zts->bounds_active = FALSE;
1941 return VIK_LAYER_TOOL_ACK;
1944 static VikLayerToolFuncStatus zoomtool_release (VikLayer *vl, GdkEventButton *event, zoom_tool_state_t *zts)
1946 guint modifiers = event->state & (GDK_SHIFT_MASK | GDK_CONTROL_MASK);
1948 // Ensure haven't just released on the exact same position
1949 // i.e. probably haven't moved the mouse at all
1950 if ( zts->bounds_active && modifiers == GDK_SHIFT_MASK &&
1951 ( event->x < zts->start_x-5 || event->x > zts->start_x+5 ) &&
1952 ( event->y < zts->start_y-5 || event->y > zts->start_y+5 ) ) {
1954 VikCoord coord1, coord2;
1955 vik_viewport_screen_to_coord ( zts->vw->viking_vvp, zts->start_x, zts->start_y, &coord1);
1956 vik_viewport_screen_to_coord ( zts->vw->viking_vvp, event->x, event->y, &coord2);
1958 // From the extend of the bounds pick the best zoom level
1959 // c.f. trw_layer_zoom_to_show_latlons()
1960 // Maybe refactor...
1961 struct LatLon ll1, ll2;
1962 vik_coord_to_latlon(&coord1, &ll1);
1963 vik_coord_to_latlon(&coord2, &ll2);
1964 struct LatLon average = { (ll1.lat+ll2.lat)/2,
1965 (ll1.lon+ll2.lon)/2 };
1967 VikCoord new_center;
1968 vik_coord_load_from_latlon ( &new_center, vik_viewport_get_coord_mode ( zts->vw->viking_vvp ), &average );
1969 vik_viewport_set_center_coord ( zts->vw->viking_vvp, &new_center, FALSE );
1971 /* Convert into definite 'smallest' and 'largest' positions */
1972 struct LatLon minmin;
1973 if ( ll1.lat < ll2.lat )
1974 minmin.lat = ll1.lat;
1976 minmin.lat = ll2.lat;
1978 struct LatLon maxmax;
1979 if ( ll1.lon > ll2.lon )
1980 maxmax.lon = ll1.lon;
1982 maxmax.lon = ll2.lon;
1984 /* Always recalculate the 'best' zoom level */
1985 gdouble zoom = VIK_VIEWPORT_MIN_ZOOM;
1986 vik_viewport_set_zoom ( zts->vw->viking_vvp, zoom );
1988 gdouble min_lat, max_lat, min_lon, max_lon;
1989 /* Should only be a maximum of about 18 iterations from min to max zoom levels */
1990 while ( zoom <= VIK_VIEWPORT_MAX_ZOOM ) {
1991 vik_viewport_get_min_max_lat_lon ( zts->vw->viking_vvp, &min_lat, &max_lat, &min_lon, &max_lon );
1992 /* NB I think the logic used in this test to determine if the bounds is within view
1993 fails if track goes across 180 degrees longitude.
1994 Hopefully that situation is not too common...
1995 Mind you viking doesn't really do edge locations to well anyway */
1996 if ( min_lat < minmin.lat &&
1997 max_lat > minmin.lat &&
1998 min_lon < maxmax.lon &&
1999 max_lon > maxmax.lon )
2000 /* Found within zoom level */
2005 vik_viewport_set_zoom ( zts->vw->viking_vvp, zoom );
2009 // When pressing shift and clicking for zoom, then jump three levels
2010 if ( modifiers == GDK_SHIFT_MASK ) {
2011 // Zoom in/out by three if possible
2012 vik_viewport_set_center_screen ( zts->vw->viking_vvp, event->x, event->y );
2013 if ( event->button == 1 ) {
2014 vik_viewport_zoom_in ( zts->vw->viking_vvp );
2015 vik_viewport_zoom_in ( zts->vw->viking_vvp );
2016 vik_viewport_zoom_in ( zts->vw->viking_vvp );
2018 else if ( event->button == 3 ) {
2019 vik_viewport_zoom_out ( zts->vw->viking_vvp );
2020 vik_viewport_zoom_out ( zts->vw->viking_vvp );
2021 vik_viewport_zoom_out ( zts->vw->viking_vvp );
2026 draw_update ( zts->vw );
2029 zts->bounds_active = FALSE;
2031 return VIK_LAYER_TOOL_ACK;
2034 static VikToolInterface zoom_tool =
2035 { { "Zoom", "vik-icon-zoom", N_("_Zoom"), "<control><shift>Z", N_("Zoom Tool"), 1 },
2036 (VikToolConstructorFunc) zoomtool_create,
2037 (VikToolDestructorFunc) zoomtool_destroy,
2038 (VikToolActivationFunc) NULL,
2039 (VikToolActivationFunc) NULL,
2040 (VikToolMouseFunc) zoomtool_click,
2041 (VikToolMouseMoveFunc) zoomtool_move,
2042 (VikToolMouseFunc) zoomtool_release,
2045 GDK_CURSOR_IS_PIXMAP,
2046 &cursor_zoom_pixbuf,
2048 /*** end zoom code ********************************************************/
2050 /********************************************************************************
2052 ********************************************************************************/
2053 static gpointer pantool_create (VikWindow *vw, VikViewport *vvp)
2058 // NB Double clicking means this gets called THREE times!!!
2059 static VikLayerToolFuncStatus pantool_click (VikLayer *vl, GdkEventButton *event, VikWindow *vw)
2061 vw->modified = TRUE;
2063 if ( event->type == GDK_2BUTTON_PRESS ) {
2064 // Zoom in / out on double click
2065 // No need to change the center as that has already occurred in the first click of a double click occurrence
2066 if ( event->button == 1 ) {
2067 guint modifier = event->state & GDK_SHIFT_MASK;
2069 vik_viewport_zoom_out ( vw->viking_vvp );
2071 vik_viewport_zoom_in ( vw->viking_vvp );
2073 else if ( event->button == 3 )
2074 vik_viewport_zoom_out ( vw->viking_vvp );
2079 // Standard pan click
2080 if ( event->button == 1 )
2081 vik_window_pan_click ( vw, event );
2083 return VIK_LAYER_TOOL_ACK;
2086 static VikLayerToolFuncStatus pantool_move (VikLayer *vl, GdkEventMotion *event, VikWindow *vw)
2088 vik_window_pan_move ( vw, event );
2089 return VIK_LAYER_TOOL_ACK;
2092 static VikLayerToolFuncStatus pantool_release (VikLayer *vl, GdkEventButton *event, VikWindow *vw)
2094 if ( event->button == 1 )
2095 vik_window_pan_release ( vw, event );
2096 return VIK_LAYER_TOOL_ACK;
2099 static VikToolInterface pan_tool =
2100 { { "Pan", "vik-icon-pan", N_("_Pan"), "<control><shift>P", N_("Pan Tool"), 0 },
2101 (VikToolConstructorFunc) pantool_create,
2102 (VikToolDestructorFunc) NULL,
2103 (VikToolActivationFunc) NULL,
2104 (VikToolActivationFunc) NULL,
2105 (VikToolMouseFunc) pantool_click,
2106 (VikToolMouseMoveFunc) pantool_move,
2107 (VikToolMouseFunc) pantool_release,
2113 /*** end pan code ********************************************************/
2115 /********************************************************************************
2117 ********************************************************************************/
2118 static gpointer selecttool_create (VikWindow *vw, VikViewport *vvp)
2120 tool_ed_t *t = g_new(tool_ed_t, 1);
2124 t->is_waypoint = FALSE;
2128 static void selecttool_destroy (tool_ed_t *t)
2136 GdkEventButton *event;
2137 tool_ed_t *tool_edit;
2140 static void click_layer_selected (VikLayer *vl, clicker *ck)
2142 /* Do nothing when function call returns true; */
2143 /* i.e. stop on first found item */
2146 if ( vik_layer_get_interface(vl->type)->select_click )
2147 ck->cont = !vik_layer_get_interface(vl->type)->select_click ( vl, ck->event, ck->vvp, ck->tool_edit );
2151 // Hopefully Alt keys by default
2152 #define VIK_MOVE_MODIFIER GDK_MOD1_MASK
2154 // Alt+mouse on Linux desktops tend to be used by the desktop manager
2155 // Thus use an alternate modifier - you may need to set something into this group
2156 #define VIK_MOVE_MODIFIER GDK_MOD5_MASK
2159 static VikLayerToolFuncStatus selecttool_click (VikLayer *vl, GdkEventButton *event, tool_ed_t *t)
2161 t->vw->select_move = FALSE;
2162 /* Only allow selection on primary button */
2163 if ( event->button == 1 ) {
2165 if ( event->state & VIK_MOVE_MODIFIER )
2166 vik_window_pan_click ( t->vw, event );
2168 /* Enable click to apply callback to potentially all track/waypoint layers */
2169 /* Useful as we can find things that aren't necessarily in the currently selected layer */
2170 GList* gl = vik_layers_panel_get_all_layers_of_type ( t->vw->viking_vlp, VIK_LAYER_TRW, FALSE ); // Don't get invisible layers
2173 ck.vvp = t->vw->viking_vvp;
2176 g_list_foreach ( gl, (GFunc) click_layer_selected, &ck );
2179 // If nothing found then deselect & redraw screen if necessary to remove the highlight
2182 VikTreeview *vtv = vik_layers_panel_get_treeview ( t->vw->viking_vlp );
2184 if ( vik_treeview_get_selected_iter ( vtv, &iter ) ) {
2185 // Only clear if selected thing is a TrackWaypoint layer or a sublayer
2186 gint type = vik_treeview_item_get_type ( vtv, &iter );
2187 if ( type == VIK_TREEVIEW_TYPE_SUBLAYER ||
2188 VIK_LAYER(vik_treeview_item_get_pointer ( vtv, &iter ))->type == VIK_LAYER_TRW ) {
2190 vik_treeview_item_unselect ( vtv, &iter );
2191 if ( vik_window_clear_highlight ( t->vw ) )
2192 draw_update ( t->vw );
2197 // Something found - so enable movement
2198 t->vw->select_move = TRUE;
2202 else if ( ( event->button == 3 ) && ( vl && ( vl->type == VIK_LAYER_TRW ) ) ) {
2204 /* Act on currently selected item to show menu */
2205 if ( t->vw->selected_track || t->vw->selected_waypoint )
2206 if ( vik_layer_get_interface(vl->type)->show_viewport_menu )
2207 vik_layer_get_interface(vl->type)->show_viewport_menu ( vl, event, t->vw->viking_vvp );
2210 return VIK_LAYER_TOOL_ACK;
2213 static VikLayerToolFuncStatus selecttool_move (VikLayer *vl, GdkEventMotion *event, tool_ed_t *t)
2215 if ( t->vw->select_move ) {
2216 // Don't care about vl here
2218 if ( vik_layer_get_interface(VIK_LAYER_TRW)->select_move )
2219 vik_layer_get_interface(VIK_LAYER_TRW)->select_move ( vl, event, t->vvp, t );
2223 if ( event->state & VIK_MOVE_MODIFIER )
2224 vik_window_pan_move ( t->vw, event );
2226 return VIK_LAYER_TOOL_ACK;
2229 static VikLayerToolFuncStatus selecttool_release (VikLayer *vl, GdkEventButton *event, tool_ed_t *t)
2231 if ( t->vw->select_move ) {
2232 // Don't care about vl here
2234 if ( vik_layer_get_interface(VIK_LAYER_TRW)->select_release )
2235 vik_layer_get_interface(VIK_LAYER_TRW)->select_release ( (VikLayer*)t->vtl, event, t->vvp, t );
2238 if ( event->button == 1 && (event->state & VIK_MOVE_MODIFIER) )
2239 vik_window_pan_release ( t->vw, event );
2241 // Force pan off incase it was on
2242 t->vw->pan_move = FALSE;
2243 t->vw->pan_x = t->vw->pan_y = -1;
2245 // End of this select movement
2246 t->vw->select_move = FALSE;
2248 return VIK_LAYER_TOOL_ACK;
2251 static VikToolInterface select_tool =
2252 { { "Select", "vik-icon-select", N_("_Select"), "<control><shift>S", N_("Select Tool"), 3 },
2253 (VikToolConstructorFunc) selecttool_create,
2254 (VikToolDestructorFunc) selecttool_destroy,
2255 (VikToolActivationFunc) NULL,
2256 (VikToolActivationFunc) NULL,
2257 (VikToolMouseFunc) selecttool_click,
2258 (VikToolMouseMoveFunc) selecttool_move,
2259 (VikToolMouseFunc) selecttool_release,
2260 (VikToolKeyFunc) NULL,
2265 /*** end select tool code ********************************************************/
2267 static void draw_pan_cb ( GtkAction *a, VikWindow *vw )
2269 // Since the treeview cell editting intercepts standard keyboard handlers, it means we can receive events here
2270 // Thus if currently editting, ensure we don't move the viewport when Ctrl+<arrow> is received
2271 VikLayer *sel = vik_layers_panel_get_selected ( vw->viking_vlp );
2272 if ( sel && vik_treeview_get_editing ( sel->vt ) )
2275 if (!strcmp(gtk_action_get_name(a), "PanNorth")) {
2276 vik_viewport_set_center_screen ( vw->viking_vvp, vik_viewport_get_width(vw->viking_vvp)/2, 0 );
2277 } else if (!strcmp(gtk_action_get_name(a), "PanEast")) {
2278 vik_viewport_set_center_screen ( vw->viking_vvp, vik_viewport_get_width(vw->viking_vvp), vik_viewport_get_height(vw->viking_vvp)/2 );
2279 } else if (!strcmp(gtk_action_get_name(a), "PanSouth")) {
2280 vik_viewport_set_center_screen ( vw->viking_vvp, vik_viewport_get_width(vw->viking_vvp)/2, vik_viewport_get_height(vw->viking_vvp) );
2281 } else if (!strcmp(gtk_action_get_name(a), "PanWest")) {
2282 vik_viewport_set_center_screen ( vw->viking_vvp, 0, vik_viewport_get_height(vw->viking_vvp)/2 );
2287 static void draw_zoom_cb ( GtkAction *a, VikWindow *vw )
2291 if (!strcmp(gtk_action_get_name(a), "ZoomIn")) {
2294 else if (!strcmp(gtk_action_get_name(a), "ZoomOut")) {
2297 else if (!strcmp(gtk_action_get_name(a), "Zoom0.25")) {
2300 else if (!strcmp(gtk_action_get_name(a), "Zoom0.5")) {
2304 gchar *s = (gchar *)gtk_action_get_name(a);
2310 case -3: vik_viewport_zoom_in ( vw->viking_vvp ); break;
2311 case -4: vik_viewport_zoom_out ( vw->viking_vvp ); break;
2312 case -1: vik_viewport_set_zoom ( vw->viking_vvp, 0.5 ); break;
2313 case -2: vik_viewport_set_zoom ( vw->viking_vvp, 0.25 ); break;
2314 default: vik_viewport_set_zoom ( vw->viking_vvp, what );
2319 static void draw_goto_cb ( GtkAction *a, VikWindow *vw )
2321 VikCoord new_center;
2323 if (!strcmp(gtk_action_get_name(a), "GotoLL")) {
2324 struct LatLon ll, llold;
2325 vik_coord_to_latlon ( vik_viewport_get_center ( vw->viking_vvp ), &llold );
2326 if ( a_dialog_goto_latlon ( GTK_WINDOW(vw), &ll, &llold ) )
2327 vik_coord_load_from_latlon ( &new_center, vik_viewport_get_coord_mode(vw->viking_vvp), &ll );
2331 else if (!strcmp(gtk_action_get_name(a), "GotoUTM")) {
2332 struct UTM utm, utmold;
2333 vik_coord_to_utm ( vik_viewport_get_center ( vw->viking_vvp ), &utmold );
2334 if ( a_dialog_goto_utm ( GTK_WINDOW(vw), &utm, &utmold ) )
2335 vik_coord_load_from_utm ( &new_center, vik_viewport_get_coord_mode(vw->viking_vvp), &utm );
2340 g_critical("Houston, we've had a problem.");
2344 vik_viewport_set_center_coord ( vw->viking_vvp, &new_center, TRUE );
2349 * center_changed_cb:
2351 static void center_changed_cb ( VikWindow *vw )
2353 // ATM Keep back always available, so when we pan - we can jump to the last requested position
2355 GtkAction* action_back = gtk_action_group_get_action ( vw->action_group, "GoBack" );
2356 if ( action_back ) {
2357 gtk_action_set_sensitive ( action_back, vik_viewport_back_available(vw->viking_vvp) );
2360 GtkAction* action_forward = gtk_action_group_get_action ( vw->action_group, "GoForward" );
2361 if ( action_forward ) {
2362 gtk_action_set_sensitive ( action_forward, vik_viewport_forward_available(vw->viking_vvp) );
2365 toolbar_action_set_sensitive ( vw->viking_vtb, "GoForward", vik_viewport_forward_available(vw->viking_vvp) );
2369 * draw_goto_back_and_forth:
2371 static void draw_goto_back_and_forth ( GtkAction *a, VikWindow *vw )
2373 gboolean changed = FALSE;
2374 if (!strcmp(gtk_action_get_name(a), "GoBack")) {
2375 changed = vik_viewport_go_back ( vw->viking_vvp );
2377 else if (!strcmp(gtk_action_get_name(a), "GoForward")) {
2378 changed = vik_viewport_go_forward ( vw->viking_vvp );
2384 // Recheck buttons sensitivities, as the center changed signal is not sent on back/forward changes
2385 // (otherwise we would get stuck in an infinite loop!)
2386 center_changed_cb ( vw );
2393 * Refresh maps displayed
2395 static void draw_refresh_cb ( GtkAction *a, VikWindow *vw )
2397 // Only get 'new' maps
2398 simple_map_update ( vw, TRUE );
2401 static void menu_addlayer_cb ( GtkAction *a, VikWindow *vw )
2403 VikLayerTypeEnum type;
2404 for ( type = 0; type < VIK_LAYER_NUM_TYPES; type++ ) {
2405 if (!strcmp(vik_layer_get_interface(type)->name, gtk_action_get_name(a))) {
2406 if ( vik_layers_panel_new_layer ( vw->viking_vlp, type ) ) {
2408 vw->modified = TRUE;
2414 static void menu_copy_layer_cb ( GtkAction *a, VikWindow *vw )
2416 a_clipboard_copy_selected ( vw->viking_vlp );
2419 static void menu_cut_layer_cb ( GtkAction *a, VikWindow *vw )
2421 vik_layers_panel_cut_selected ( vw->viking_vlp );
2422 vw->modified = TRUE;
2425 static void menu_paste_layer_cb ( GtkAction *a, VikWindow *vw )
2427 if ( vik_layers_panel_paste_selected ( vw->viking_vlp ) )
2429 vw->modified = TRUE;
2433 static void menu_properties_cb ( GtkAction *a, VikWindow *vw )
2435 if ( ! vik_layers_panel_properties ( vw->viking_vlp ) )
2436 a_dialog_info_msg ( GTK_WINDOW(vw), _("You must select a layer to show its properties.") );
2439 static void help_help_cb ( GtkAction *a, VikWindow *vw )
2442 ShellExecute(NULL, "open", ""PACKAGE".pdf", NULL, NULL, SW_SHOWNORMAL);
2445 uri = g_strdup_printf("ghelp:%s", PACKAGE);
2446 GError *error = NULL;
2447 gboolean show = gtk_show_uri (NULL, uri, GDK_CURRENT_TIME, &error);
2448 if ( !show && !error )
2449 // No error to show, so unlikely this will get called
2450 a_dialog_error_msg ( GTK_WINDOW(vw), _("The help system is not available.") );
2453 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 );
2454 g_error_free ( error );
2457 #endif /* WINDOWS */
2460 static void toggle_side_panel ( VikWindow *vw )
2462 vw->show_side_panel = !vw->show_side_panel;
2463 if ( vw->show_side_panel )
2464 gtk_widget_show(GTK_WIDGET(vw->viking_vlp));
2466 gtk_widget_hide(GTK_WIDGET(vw->viking_vlp));
2469 static void toggle_full_screen ( VikWindow *vw )
2471 vw->show_full_screen = !vw->show_full_screen;
2472 if ( vw->show_full_screen )
2473 gtk_window_fullscreen ( GTK_WINDOW(vw) );
2475 gtk_window_unfullscreen ( GTK_WINDOW(vw) );
2478 static void toggle_statusbar ( VikWindow *vw )
2480 vw->show_statusbar = !vw->show_statusbar;
2481 if ( vw->show_statusbar )
2482 gtk_widget_show ( GTK_WIDGET(vw->viking_vs) );
2484 gtk_widget_hide ( GTK_WIDGET(vw->viking_vs) );
2487 static void toggle_toolbar ( VikWindow *vw )
2489 vw->show_toolbar = !vw->show_toolbar;
2490 if ( vw->show_toolbar )
2491 gtk_widget_show ( toolbar_get_widget (vw->viking_vtb) );
2493 gtk_widget_hide ( toolbar_get_widget (vw->viking_vtb) );
2496 static void toggle_main_menu ( VikWindow *vw )
2498 vw->show_main_menu = !vw->show_main_menu;
2499 if ( vw->show_main_menu )
2500 gtk_widget_show ( gtk_ui_manager_get_widget ( vw->uim, "/ui/MainMenu" ) );
2502 gtk_widget_hide ( gtk_ui_manager_get_widget ( vw->uim, "/ui/MainMenu" ) );
2505 // Only for 'view' toggle menu widgets ATM.
2506 GtkWidget *get_show_widget_by_name(VikWindow *vw, const gchar *name)
2508 g_return_val_if_fail(name != NULL, NULL);
2510 // ATM only FullScreen is *not* in SetShow path
2512 if ( g_strcmp0 ("FullScreen", name ) )
2513 path = g_strconcat("/ui/MainMenu/View/SetShow/", name, NULL);
2515 path = g_strconcat("/ui/MainMenu/View/", name, NULL);
2517 GtkWidget *widget = gtk_ui_manager_get_widget(vw->uim, path);
2523 static void tb_view_side_panel_cb ( GtkAction *a, VikWindow *vw )
2525 gboolean next_state = !vw->show_side_panel;
2526 GtkWidget *check_box = get_show_widget_by_name ( vw, gtk_action_get_name(a) );
2527 gboolean menu_state = gtk_check_menu_item_get_active ( GTK_CHECK_MENU_ITEM(check_box) );
2528 if ( next_state != menu_state )
2529 gtk_check_menu_item_set_active ( GTK_CHECK_MENU_ITEM(check_box), next_state );
2531 toggle_side_panel ( vw );
2534 static void tb_full_screen_cb ( GtkAction *a, VikWindow *vw )
2536 gboolean next_state = !vw->show_full_screen;
2537 GtkWidget *check_box = get_show_widget_by_name ( vw, gtk_action_get_name(a) );
2538 gboolean menu_state = gtk_check_menu_item_get_active ( GTK_CHECK_MENU_ITEM(check_box) );
2539 if ( next_state != menu_state )
2540 gtk_check_menu_item_set_active ( GTK_CHECK_MENU_ITEM(check_box), next_state );
2542 toggle_full_screen ( vw );
2545 static void tb_view_statusbar_cb ( GtkAction *a, VikWindow *vw )
2547 gboolean next_state = !vw->show_statusbar;
2548 GtkWidget *check_box = get_show_widget_by_name ( vw, gtk_action_get_name(a) );
2549 gboolean menu_state = gtk_check_menu_item_get_active ( GTK_CHECK_MENU_ITEM(check_box) );
2550 if ( next_state != menu_state )
2551 gtk_check_menu_item_set_active ( GTK_CHECK_MENU_ITEM(check_box), next_state );
2553 toggle_statusbar ( vw );
2556 static void tb_view_toolbar_cb ( GtkAction *a, VikWindow *vw )
2558 gboolean next_state = !vw->show_toolbar;
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 toggle_toolbar ( vw );
2567 static void tb_view_main_menu_cb ( GtkAction *a, VikWindow *vw )
2569 gboolean next_state = !vw->show_main_menu;
2570 GtkWidget *check_box = get_show_widget_by_name ( vw, gtk_action_get_name(a) );
2571 gboolean menu_state = gtk_check_menu_item_get_active ( GTK_CHECK_MENU_ITEM(check_box) );
2572 if ( next_state != menu_state )
2573 gtk_check_menu_item_set_active ( GTK_CHECK_MENU_ITEM(check_box), next_state );
2575 toggle_main_menu ( vw );
2578 static void tb_set_draw_scale ( GtkAction *a, VikWindow *vw )
2580 gboolean next_state = !vik_viewport_get_draw_scale ( vw->viking_vvp );
2581 GtkWidget *check_box = get_show_widget_by_name ( vw, gtk_action_get_name(a) );
2582 gboolean menu_state = gtk_check_menu_item_get_active ( GTK_CHECK_MENU_ITEM(check_box) );
2583 if ( next_state != menu_state )
2584 gtk_check_menu_item_set_active ( GTK_CHECK_MENU_ITEM(check_box), next_state );
2586 vik_viewport_set_draw_scale ( vw->viking_vvp, next_state );
2591 static void tb_set_draw_centermark ( GtkAction *a, VikWindow *vw )
2593 gboolean next_state = !vik_viewport_get_draw_centermark ( vw->viking_vvp );
2594 GtkWidget *check_box = get_show_widget_by_name ( vw, gtk_action_get_name(a) );
2595 gboolean menu_state = gtk_check_menu_item_get_active ( GTK_CHECK_MENU_ITEM(check_box) );
2596 if ( next_state != menu_state )
2597 gtk_check_menu_item_set_active ( GTK_CHECK_MENU_ITEM(check_box), next_state );
2599 vik_viewport_set_draw_centermark ( vw->viking_vvp, next_state );
2604 static void tb_set_draw_highlight ( GtkAction *a, VikWindow *vw )
2606 gboolean next_state = !vik_viewport_get_draw_highlight ( vw->viking_vvp );
2607 GtkWidget *check_box = get_show_widget_by_name ( vw, gtk_action_get_name(a) );
2608 gboolean menu_state = gtk_check_menu_item_get_active ( GTK_CHECK_MENU_ITEM(check_box) );
2609 if ( next_state != menu_state )
2610 gtk_check_menu_item_set_active ( GTK_CHECK_MENU_ITEM(check_box), next_state );
2612 vik_viewport_set_draw_highlight ( vw->viking_vvp, next_state );
2617 static void help_about_cb ( GtkAction *a, VikWindow *vw )
2619 a_dialog_about(GTK_WINDOW(vw));
2622 static void help_cache_info_cb ( GtkAction *a, VikWindow *vw )
2624 // NB: No i18n as this is just for debug
2625 gint byte_size = a_mapcache_get_size();
2626 gchar *msg_sz = NULL;
2628 #if GLIB_CHECK_VERSION(2,30,0)
2629 msg_sz = g_format_size_full ( byte_size, G_FORMAT_SIZE_LONG_FORMAT );
2631 msg_sz = g_format_size_for_display ( byte_size );
2633 msg = g_strdup_printf ( "Map Cache size is %s with %d items", msg_sz, a_mapcache_get_count());
2634 a_dialog_info_msg_extra ( GTK_WINDOW(vw), "%s", msg );
2639 static void back_forward_info_cb ( GtkAction *a, VikWindow *vw )
2641 vik_viewport_show_centers ( vw->viking_vvp, GTK_WINDOW(vw) );
2644 static void menu_delete_layer_cb ( GtkAction *a, VikWindow *vw )
2646 if ( vik_layers_panel_get_selected ( vw->viking_vlp ) )
2648 vik_layers_panel_delete_selected ( vw->viking_vlp );
2649 vw->modified = TRUE;
2652 a_dialog_info_msg ( GTK_WINDOW(vw), _("You must select a layer to delete.") );
2655 static void full_screen_cb ( GtkAction *a, VikWindow *vw )
2657 gboolean next_state = !vw->show_full_screen;
2658 GtkToggleToolButton *tbutton = (GtkToggleToolButton *)toolbar_get_widget_by_name ( vw->viking_vtb, gtk_action_get_name(a) );
2660 gboolean tb_state = gtk_toggle_tool_button_get_active ( tbutton );
2661 if ( next_state != tb_state )
2662 gtk_toggle_tool_button_set_active ( tbutton, next_state );
2664 toggle_full_screen ( vw );
2667 toggle_full_screen ( vw );
2670 static void view_side_panel_cb ( GtkAction *a, VikWindow *vw )
2672 gboolean next_state = !vw->show_side_panel;
2673 GtkToggleToolButton *tbutton = (GtkToggleToolButton *)toolbar_get_widget_by_name ( vw->viking_vtb, gtk_action_get_name(a) );
2675 gboolean tb_state = gtk_toggle_tool_button_get_active ( tbutton );
2676 if ( next_state != tb_state )
2677 gtk_toggle_tool_button_set_active ( tbutton, next_state );
2679 toggle_side_panel ( vw );
2682 toggle_side_panel ( vw );
2685 static void view_statusbar_cb ( GtkAction *a, VikWindow *vw )
2687 gboolean next_state = !vw->show_statusbar;
2688 GtkToggleToolButton *tbutton = (GtkToggleToolButton *)toolbar_get_widget_by_name ( vw->viking_vtb, gtk_action_get_name(a) );
2690 gboolean tb_state = gtk_toggle_tool_button_get_active ( tbutton );
2691 if ( next_state != tb_state )
2692 gtk_toggle_tool_button_set_active ( tbutton, next_state );
2694 toggle_statusbar ( vw );
2697 toggle_statusbar ( vw );
2700 static void view_toolbar_cb ( GtkAction *a, VikWindow *vw )
2702 gboolean next_state = !vw->show_toolbar;
2703 GtkToggleToolButton *tbutton = (GtkToggleToolButton *)toolbar_get_widget_by_name ( vw->viking_vtb, gtk_action_get_name(a) );
2705 gboolean tb_state = gtk_toggle_tool_button_get_active ( tbutton );
2706 if ( next_state != tb_state )
2707 gtk_toggle_tool_button_set_active ( tbutton, next_state );
2709 toggle_toolbar ( vw );
2712 toggle_toolbar ( vw );
2715 static void view_main_menu_cb ( GtkAction *a, VikWindow *vw )
2717 gboolean next_state = !vw->show_main_menu;
2718 GtkToggleToolButton *tbutton = (GtkToggleToolButton *)toolbar_get_widget_by_name ( vw->viking_vtb, gtk_action_get_name(a) );
2720 gboolean tb_state = gtk_toggle_tool_button_get_active ( tbutton );
2721 if ( next_state != tb_state )
2722 gtk_toggle_tool_button_set_active ( tbutton, next_state );
2724 toggle_main_menu ( vw );
2727 toggle_toolbar ( vw );
2730 /***************************************
2731 ** tool management routines
2733 ***************************************/
2735 static toolbox_tools_t* toolbox_create(VikWindow *vw)
2737 toolbox_tools_t *vt = g_new(toolbox_tools_t, 1);
2740 vt->active_tool = -1;
2745 static void toolbox_add_tool(toolbox_tools_t *vt, VikToolInterface *vti, gint layer_type )
2747 vt->tools = g_renew(toolbox_tool_t, vt->tools, vt->n_tools+1);
2748 vt->tools[vt->n_tools].ti = *vti;
2749 vt->tools[vt->n_tools].layer_type = layer_type;
2751 vt->tools[vt->n_tools].state = vti->create(vt->vw, vt->vw->viking_vvp);
2754 vt->tools[vt->n_tools].state = NULL;
2759 static int toolbox_get_tool(toolbox_tools_t *vt, const gchar *tool_name)
2762 for (i=0; i<vt->n_tools; i++) {
2763 if (!strcmp(tool_name, vt->tools[i].ti.radioActionEntry.name)) {
2770 static void toolbox_activate(toolbox_tools_t *vt, const gchar *tool_name)
2772 int tool = toolbox_get_tool(vt, tool_name);
2773 toolbox_tool_t *t = &vt->tools[tool];
2774 VikLayer *vl = vik_layers_panel_get_selected ( vt->vw->viking_vlp );
2776 if (tool == vt->n_tools) {
2777 g_critical("trying to activate a non-existent tool...");
2780 /* is the tool already active? */
2781 if (vt->active_tool == tool) {
2785 if (vt->active_tool != -1) {
2786 if (vt->tools[vt->active_tool].ti.deactivate) {
2787 vt->tools[vt->active_tool].ti.deactivate(NULL, vt->tools[vt->active_tool].state);
2790 if (t->ti.activate) {
2791 t->ti.activate(vl, t->state);
2793 vt->active_tool = tool;
2796 static const GdkCursor *toolbox_get_cursor(toolbox_tools_t *vt, const gchar *tool_name)
2798 int tool = toolbox_get_tool(vt, tool_name);
2799 toolbox_tool_t *t = &vt->tools[tool];
2800 if (t->ti.cursor == NULL) {
2801 if (t->ti.cursor_type == GDK_CURSOR_IS_PIXMAP && t->ti.cursor_data != NULL) {
2802 GError *cursor_load_err = NULL;
2803 GdkPixbuf *cursor_pixbuf = gdk_pixbuf_from_pixdata (t->ti.cursor_data, FALSE, &cursor_load_err);
2804 /* TODO: settable offeset */
2805 t->ti.cursor = gdk_cursor_new_from_pixbuf ( gdk_display_get_default(), cursor_pixbuf, 3, 3 );
2806 g_object_unref ( G_OBJECT(cursor_pixbuf) );
2808 t->ti.cursor = gdk_cursor_new ( t->ti.cursor_type );
2811 return t->ti.cursor;
2814 static void toolbox_click (toolbox_tools_t *vt, GdkEventButton *event)
2816 VikLayer *vl = vik_layers_panel_get_selected ( vt->vw->viking_vlp );
2817 if (vt->active_tool != -1 && vt->tools[vt->active_tool].ti.click) {
2818 gint ltype = vt->tools[vt->active_tool].layer_type;
2819 if ( ltype == TOOL_LAYER_TYPE_NONE || (vl && ltype == vl->type) )
2820 vt->tools[vt->active_tool].ti.click(vl, event, vt->tools[vt->active_tool].state);
2824 static void toolbox_move (toolbox_tools_t *vt, GdkEventMotion *event)
2826 VikLayer *vl = vik_layers_panel_get_selected ( vt->vw->viking_vlp );
2827 if (vt->active_tool != -1 && vt->tools[vt->active_tool].ti.move) {
2828 gint ltype = vt->tools[vt->active_tool].layer_type;
2829 if ( ltype == TOOL_LAYER_TYPE_NONE || (vl && ltype == vl->type) )
2830 if ( VIK_LAYER_TOOL_ACK_GRAB_FOCUS == vt->tools[vt->active_tool].ti.move(vl, event, vt->tools[vt->active_tool].state) )
2831 gtk_widget_grab_focus ( GTK_WIDGET(vt->vw->viking_vvp) );
2835 static void toolbox_release (toolbox_tools_t *vt, GdkEventButton *event)
2837 VikLayer *vl = vik_layers_panel_get_selected ( vt->vw->viking_vlp );
2838 if (vt->active_tool != -1 && vt->tools[vt->active_tool].ti.release ) {
2839 gint ltype = vt->tools[vt->active_tool].layer_type;
2840 if ( ltype == TOOL_LAYER_TYPE_NONE || (vl && ltype == vl->type) )
2841 vt->tools[vt->active_tool].ti.release(vl, event, vt->tools[vt->active_tool].state);
2844 /** End tool management ************************************/
2846 void vik_window_enable_layer_tool ( VikWindow *vw, gint layer_id, gint tool_id )
2848 gtk_action_activate ( gtk_action_group_get_action ( vw->action_group, vik_layer_get_interface(layer_id)->tools[tool_id].radioActionEntry.name ) );
2851 // Be careful with usage - as it may trigger actions being continually alternately by the menu and toolbar items
2852 // DON'T Use this from menu callback with toggle toolbar items!!
2853 static void toolbar_sync ( VikWindow *vw, const gchar *name, gboolean state )
2855 GtkToggleToolButton *tbutton = (GtkToggleToolButton *)toolbar_get_widget_by_name ( vw->viking_vtb, name );
2857 // Causes toggle signal action to be raised.
2858 gtk_toggle_tool_button_set_active ( tbutton, state );
2862 /* this function gets called whenever a menu is clicked */
2863 // Note old is not used
2864 static void menu_cb ( GtkAction *old, GtkAction *a, VikWindow *vw )
2866 // Ensure Toolbar kept in sync
2867 const gchar *name = gtk_action_get_name(a);
2868 toolbar_sync ( vw, name, TRUE );
2870 /* White Magic, my friends ... White Magic... */
2872 toolbox_activate(vw->vt, name);
2874 vw->viewport_cursor = (GdkCursor *)toolbox_get_cursor(vw->vt, name);
2876 if ( gtk_widget_get_window(GTK_WIDGET(vw->viking_vvp)) )
2877 /* We set cursor, even if it is NULL: it resets to default */
2878 gdk_window_set_cursor ( gtk_widget_get_window(GTK_WIDGET(vw->viking_vvp)), vw->viewport_cursor );
2880 if (!g_strcmp0(name, "Pan")) {
2881 vw->current_tool = TOOL_PAN;
2883 else if (!g_strcmp0(name, "Zoom")) {
2884 vw->current_tool = TOOL_ZOOM;
2886 else if (!g_strcmp0(name, "Ruler")) {
2887 vw->current_tool = TOOL_RULER;
2889 else if (!g_strcmp0(name, "Select")) {
2890 vw->current_tool = TOOL_SELECT;
2893 VikLayerTypeEnum layer_id;
2894 for (layer_id=0; layer_id<VIK_LAYER_NUM_TYPES; layer_id++) {
2895 for ( tool_id = 0; tool_id < vik_layer_get_interface(layer_id)->tools_count; tool_id++ ) {
2896 if (!g_strcmp0(vik_layer_get_interface(layer_id)->tools[tool_id].radioActionEntry.name, name)) {
2897 vw->current_tool = TOOL_LAYER;
2898 vw->tool_layer_id = layer_id;
2899 vw->tool_tool_id = tool_id;
2904 draw_status_tool ( vw );
2907 static void window_set_filename ( VikWindow *vw, const gchar *filename )
2912 g_free ( vw->filename );
2913 if ( filename == NULL )
2915 vw->filename = NULL;
2919 vw->filename = g_strdup(filename);
2922 /* Refresh window's title */
2923 file = window_get_filename ( vw );
2924 title = g_strdup_printf( "%s - Viking", file );
2925 gtk_window_set_title ( GTK_WINDOW(vw), title );
2929 static const gchar *window_get_filename ( VikWindow *vw )
2931 return vw->filename ? a_file_basename ( vw->filename ) : _("Untitled");
2934 GtkWidget *vik_window_get_drawmode_button ( VikWindow *vw, VikViewportDrawMode mode )
2936 GtkWidget *mode_button;
2939 #ifdef VIK_CONFIG_EXPEDIA
2940 case VIK_VIEWPORT_DRAWMODE_EXPEDIA: buttonname = "/ui/MainMenu/View/ModeExpedia"; break;
2942 case VIK_VIEWPORT_DRAWMODE_MERCATOR: buttonname = "/ui/MainMenu/View/ModeMercator"; break;
2943 case VIK_VIEWPORT_DRAWMODE_LATLON: buttonname = "/ui/MainMenu/View/ModeLatLon"; break;
2944 default: buttonname = "/ui/MainMenu/View/ModeUTM";
2946 mode_button = gtk_ui_manager_get_widget ( vw->uim, buttonname );
2947 g_assert ( mode_button );
2952 * vik_window_get_pan_move:
2953 * @vw: some VikWindow
2955 * Retrieves @vw's pan_move.
2957 * Should be removed as soon as possible.
2959 * Returns: @vw's pan_move
2963 gboolean vik_window_get_pan_move ( VikWindow *vw )
2965 return vw->pan_move;
2968 static void on_activate_recent_item (GtkRecentChooser *chooser,
2973 filename = gtk_recent_chooser_get_current_uri (chooser);
2974 if (filename != NULL)
2976 GFile *file = g_file_new_for_uri ( filename );
2977 gchar *path = g_file_get_path ( file );
2978 g_object_unref ( file );
2979 if ( self->filename )
2981 GSList *filenames = NULL;
2982 filenames = g_slist_append ( filenames, path );
2983 g_signal_emit ( G_OBJECT(self), window_signals[VW_OPENWINDOW_SIGNAL], 0, filenames );
2984 // NB: GSList & contents are freed by main.open_window
2987 vik_window_open_file ( self, path, TRUE );
2995 static void setup_recent_files (VikWindow *self)
2997 GtkRecentManager *manager;
2998 GtkRecentFilter *filter;
2999 GtkWidget *menu, *menu_item;
3001 filter = gtk_recent_filter_new ();
3002 /* gtk_recent_filter_add_application (filter, g_get_application_name()); */
3003 gtk_recent_filter_add_group(filter, "viking");
3005 manager = gtk_recent_manager_get_default ();
3006 menu = gtk_recent_chooser_menu_new_for_manager (manager);
3007 gtk_recent_chooser_set_sort_type (GTK_RECENT_CHOOSER (menu), GTK_RECENT_SORT_MRU);
3008 gtk_recent_chooser_add_filter (GTK_RECENT_CHOOSER (menu), filter);
3009 gtk_recent_chooser_set_limit (GTK_RECENT_CHOOSER (menu), a_vik_get_recent_number_files() );
3011 menu_item = gtk_ui_manager_get_widget (self->uim, "/ui/MainMenu/File/OpenRecentFile");
3012 gtk_menu_item_set_submenu (GTK_MENU_ITEM (menu_item), menu);
3014 g_signal_connect (G_OBJECT (menu), "item-activated",
3015 G_CALLBACK (on_activate_recent_item), (gpointer) self);
3021 static void update_recently_used_document (VikWindow *vw, const gchar *filename)
3023 /* Update Recently Used Document framework */
3024 GtkRecentManager *manager = gtk_recent_manager_get_default();
3025 GtkRecentData *recent_data = g_slice_new (GtkRecentData);
3026 gchar *groups[] = {"viking", NULL};
3027 GFile *file = g_file_new_for_commandline_arg(filename);
3028 gchar *uri = g_file_get_uri(file);
3029 gchar *basename = g_path_get_basename(filename);
3030 g_object_unref(file);
3033 recent_data->display_name = basename;
3034 recent_data->description = NULL;
3035 recent_data->mime_type = "text/x-gps-data";
3036 recent_data->app_name = (gchar *) g_get_application_name ();
3037 recent_data->app_exec = g_strjoin (" ", g_get_prgname (), "%f", NULL);
3038 recent_data->groups = groups;
3039 recent_data->is_private = FALSE;
3040 if (!gtk_recent_manager_add_full (manager, uri, recent_data))
3042 gchar *msg = g_strdup_printf (_("Unable to add '%s' to the list of recently used documents"), uri);
3043 vik_statusbar_set_message ( vw->viking_vs, VIK_STATUSBAR_INFO, msg );
3049 g_free (recent_data->app_exec);
3050 g_slice_free (GtkRecentData, recent_data);
3054 * Call this before doing things that may take a long time and otherwise not show any other feedback
3055 * such as loading and saving files
3057 void vik_window_set_busy_cursor ( VikWindow *vw )
3059 gdk_window_set_cursor ( gtk_widget_get_window(GTK_WIDGET(vw)), vw->busy_cursor );
3060 // Viewport has a separate cursor
3061 gdk_window_set_cursor ( gtk_widget_get_window(GTK_WIDGET(vw->viking_vvp)), vw->busy_cursor );
3062 // Ensure cursor updated before doing stuff
3063 while( gtk_events_pending() )
3064 gtk_main_iteration();
3067 void vik_window_clear_busy_cursor ( VikWindow *vw )
3069 gdk_window_set_cursor ( gtk_widget_get_window(GTK_WIDGET(vw)), NULL );
3070 // Restore viewport cursor
3071 gdk_window_set_cursor ( gtk_widget_get_window(GTK_WIDGET(vw->viking_vvp)), vw->viewport_cursor );
3074 void vik_window_open_file ( VikWindow *vw, const gchar *filename, gboolean change_filename )
3076 vik_window_set_busy_cursor ( vw );
3078 // Enable the *new* filename to be accessible by the Layers codez
3079 gchar *original_filename = g_strdup ( vw->filename );
3080 g_free ( vw->filename );
3081 vw->filename = g_strdup ( filename );
3082 gboolean success = FALSE;
3083 gboolean restore_original_filename = FALSE;
3085 vw->loaded_type = a_file_load ( vik_layers_panel_get_top_layer(vw->viking_vlp), vw->viking_vvp, filename );
3086 switch ( vw->loaded_type )
3088 case LOAD_TYPE_READ_FAILURE:
3089 a_dialog_error_msg ( GTK_WINDOW(vw), _("The file you requested could not be opened.") );
3091 case LOAD_TYPE_GPSBABEL_FAILURE:
3092 a_dialog_error_msg ( GTK_WINDOW(vw), _("GPSBabel is required to load files of this type or GPSBabel encountered problems.") );
3094 case LOAD_TYPE_GPX_FAILURE:
3095 a_dialog_error_msg_extra ( GTK_WINDOW(vw), _("Unable to load malformed GPX file %s"), filename );
3097 case LOAD_TYPE_UNSUPPORTED_FAILURE:
3098 a_dialog_error_msg_extra ( GTK_WINDOW(vw), _("Unsupported file type for %s"), filename );
3100 case LOAD_TYPE_VIK_FAILURE_NON_FATAL:
3102 // Since we can process .vik files with issues just show a warning in the status bar
3103 // Not that a user can do much about it... or tells them what this issue is yet...
3104 gchar *msg = g_strdup_printf (_("WARNING: issues encountered loading %s"), a_file_basename (filename) );
3105 vik_statusbar_set_message ( vw->viking_vs, VIK_STATUSBAR_INFO, msg );
3108 // No break, carry on to show any data
3109 case LOAD_TYPE_VIK_SUCCESS:
3111 restore_original_filename = TRUE; // NB Will actually get inverted by the 'success' component below
3112 GtkWidget *mode_button;
3114 if ( change_filename )
3115 window_set_filename ( vw, filename );
3116 mode_button = vik_window_get_drawmode_button ( vw, vik_viewport_get_drawmode ( vw->viking_vvp ) );
3117 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. */
3118 gtk_check_menu_item_set_active ( GTK_CHECK_MENU_ITEM(mode_button), TRUE );
3119 vw->only_updating_coord_mode_ui = FALSE;
3121 vik_layers_panel_change_coord_mode ( vw->viking_vlp, vik_viewport_get_coord_mode ( vw->viking_vvp ) );
3123 // Slightly long winded methods to align loaded viewport settings with the UI
3124 // Since the rewrite for toolbar + menu actions
3125 // there no longer exists a simple way to directly change the UI to a value for toggle settings
3126 // it only supports toggling the existing setting (otherwise get infinite loops in trying to align tb+menu elements)
3127 // Thus get state, compare them, if different then invert viewport setting and (re)sync the setting (via toggling)
3128 gboolean vp_state_scale = vik_viewport_get_draw_scale ( vw->viking_vvp );
3129 gboolean ui_state_scale = gtk_check_menu_item_get_active ( GTK_CHECK_MENU_ITEM(get_show_widget_by_name(vw, "ShowScale")) );
3130 if ( vp_state_scale != ui_state_scale ) {
3131 vik_viewport_set_draw_scale ( vw->viking_vvp, !vp_state_scale );
3132 toggle_draw_scale ( NULL, vw );
3134 gboolean vp_state_centermark = vik_viewport_get_draw_centermark ( vw->viking_vvp );
3135 gboolean ui_state_centermark = gtk_check_menu_item_get_active ( GTK_CHECK_MENU_ITEM(get_show_widget_by_name(vw, "ShowCenterMark")) );
3136 if ( vp_state_centermark != ui_state_centermark ) {
3137 vik_viewport_set_draw_centermark ( vw->viking_vvp, !vp_state_centermark );
3138 toggle_draw_centermark ( NULL, vw );
3140 gboolean vp_state_highlight = vik_viewport_get_draw_highlight ( vw->viking_vvp );
3141 gboolean ui_state_highlight = gtk_check_menu_item_get_active ( GTK_CHECK_MENU_ITEM(get_show_widget_by_name(vw, "ShowHighlight")) );
3142 if ( vp_state_highlight != ui_state_highlight ) {
3143 vik_viewport_set_draw_highlight ( vw->viking_vvp, !vp_state_highlight );
3144 toggle_draw_highlight ( NULL, vw );
3147 // NB No break, carry on to redraw
3148 //case LOAD_TYPE_OTHER_SUCCESS:
3151 // When LOAD_TYPE_OTHER_SUCCESS *only*, this will maintain the existing Viking project
3152 restore_original_filename = ! restore_original_filename;
3153 update_recently_used_document (vw, filename);
3158 if ( ! success || restore_original_filename )
3159 // Load didn't work or want to keep as the existing Viking project, keep using the original name
3160 window_set_filename ( vw, original_filename );
3161 g_free ( original_filename );
3163 vik_window_clear_busy_cursor ( vw );
3166 static void load_file ( GtkAction *a, VikWindow *vw )
3168 GSList *files = NULL;
3169 GSList *cur_file = NULL;
3171 if (!strcmp(gtk_action_get_name(a), "Open")) {
3174 else if (!strcmp(gtk_action_get_name(a), "Append")) {
3178 g_critical("Houston, we've had a problem.");
3182 if ( ! vw->open_dia )
3184 vw->open_dia = gtk_file_chooser_dialog_new (_("Please select a GPS data file to open. "),
3186 GTK_FILE_CHOOSER_ACTION_OPEN,
3187 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
3188 GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT,
3190 gchar *cwd = g_get_current_dir();
3192 gtk_file_chooser_set_current_folder ( GTK_FILE_CHOOSER(vw->open_dia), cwd );
3196 GtkFileFilter *filter;
3197 // NB file filters are listed this way for alphabetical ordering
3198 #ifdef VIK_CONFIG_GEOCACHES
3199 filter = gtk_file_filter_new ();
3200 gtk_file_filter_set_name( filter, _("Geocaching") );
3201 gtk_file_filter_add_pattern ( filter, "*.loc" ); // No MIME type available
3202 gtk_file_chooser_add_filter (GTK_FILE_CHOOSER(vw->open_dia), filter);
3205 filter = gtk_file_filter_new ();
3206 gtk_file_filter_set_name( filter, _("Google Earth") );
3207 gtk_file_filter_add_mime_type ( filter, "application/vnd.google-earth.kml+xml");
3208 gtk_file_chooser_add_filter (GTK_FILE_CHOOSER(vw->open_dia), filter);
3210 filter = gtk_file_filter_new ();
3211 gtk_file_filter_set_name( filter, _("GPX") );
3212 gtk_file_filter_add_pattern ( filter, "*.gpx" ); // No MIME type available
3213 gtk_file_chooser_add_filter (GTK_FILE_CHOOSER(vw->open_dia), filter);
3215 filter = gtk_file_filter_new ();
3216 gtk_file_filter_set_name ( filter, _("JPG") );
3217 gtk_file_filter_add_mime_type ( filter, "image/jpeg");
3218 gtk_file_chooser_add_filter (GTK_FILE_CHOOSER(vw->open_dia), filter);
3220 filter = gtk_file_filter_new ();
3221 gtk_file_filter_set_name( filter, _("Viking") );
3222 gtk_file_filter_add_pattern ( filter, "*.vik" );
3223 gtk_file_filter_add_pattern ( filter, "*.viking" );
3224 gtk_file_chooser_add_filter (GTK_FILE_CHOOSER(vw->open_dia), filter);
3226 // NB could have filters for gpspoint (*.gps,*.gpsoint?) + gpsmapper (*.gsm,*.gpsmapper?)
3227 // However assume this are barely used and thus not worthy of inclusion
3228 // as they'll just make the options too many and have no clear file pattern
3229 // one can always use the all option
3230 filter = gtk_file_filter_new ();
3231 gtk_file_filter_set_name( filter, _("All") );
3232 gtk_file_filter_add_pattern ( filter, "*" );
3233 gtk_file_chooser_add_filter (GTK_FILE_CHOOSER(vw->open_dia), filter);
3234 // Default to any file - same as before open filters were added
3235 gtk_file_chooser_set_filter (GTK_FILE_CHOOSER(vw->open_dia), filter);
3237 gtk_file_chooser_set_select_multiple ( GTK_FILE_CHOOSER(vw->open_dia), TRUE );
3238 gtk_window_set_transient_for ( GTK_WINDOW(vw->open_dia), GTK_WINDOW(vw) );
3239 gtk_window_set_destroy_with_parent ( GTK_WINDOW(vw->open_dia), TRUE );
3241 if ( gtk_dialog_run ( GTK_DIALOG(vw->open_dia) ) == GTK_RESPONSE_ACCEPT )
3243 gtk_widget_hide ( vw->open_dia );
3244 #ifdef VIKING_PROMPT_IF_MODIFIED
3245 if ( (vw->modified || vw->filename) && newwindow )
3247 if ( vw->filename && newwindow )
3249 g_signal_emit ( G_OBJECT(vw), window_signals[VW_OPENWINDOW_SIGNAL], 0, gtk_file_chooser_get_filenames (GTK_FILE_CHOOSER(vw->open_dia) ) );
3251 files = gtk_file_chooser_get_filenames (GTK_FILE_CHOOSER(vw->open_dia) );
3252 gboolean change_fn = newwindow && (g_slist_length(files)==1); /* only change fn if one file */
3253 gboolean first_vik_file = TRUE;
3255 while ( cur_file ) {
3257 gchar *file_name = cur_file->data;
3258 if ( newwindow && check_file_magic_vik ( file_name ) ) {
3259 // Load first of many .vik files in current window
3260 if ( first_vik_file ) {
3261 vik_window_open_file ( vw, file_name, TRUE );
3262 first_vik_file = FALSE;
3265 // Load each subsequent .vik file in a separate window
3266 VikWindow *newvw = vik_window_new_window ();
3268 vik_window_open_file ( newvw, file_name, TRUE );
3273 vik_window_open_file ( vw, file_name, change_fn );
3276 cur_file = g_slist_next (cur_file);
3278 g_slist_free (files);
3282 gtk_widget_hide ( vw->open_dia );
3285 static gboolean save_file_as ( GtkAction *a, VikWindow *vw )
3287 gboolean rv = FALSE;
3289 if ( ! vw->save_dia )
3291 vw->save_dia = gtk_file_chooser_dialog_new (_("Save as Viking File."),
3293 GTK_FILE_CHOOSER_ACTION_SAVE,
3294 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
3295 GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT,
3297 gchar *cwd = g_get_current_dir();
3299 gtk_file_chooser_set_current_folder ( GTK_FILE_CHOOSER(vw->save_dia), cwd );
3303 GtkFileFilter *filter;
3304 filter = gtk_file_filter_new ();
3305 gtk_file_filter_set_name( filter, _("All") );
3306 gtk_file_filter_add_pattern ( filter, "*" );
3307 gtk_file_chooser_add_filter (GTK_FILE_CHOOSER(vw->save_dia), filter);
3309 filter = gtk_file_filter_new ();
3310 gtk_file_filter_set_name( filter, _("Viking") );
3311 gtk_file_filter_add_pattern ( filter, "*.vik" );
3312 gtk_file_filter_add_pattern ( filter, "*.viking" );
3313 gtk_file_chooser_add_filter (GTK_FILE_CHOOSER(vw->save_dia), filter);
3314 // Default to a Viking file
3315 gtk_file_chooser_set_filter (GTK_FILE_CHOOSER(vw->save_dia), filter);
3317 gtk_window_set_transient_for ( GTK_WINDOW(vw->save_dia), GTK_WINDOW(vw) );
3318 gtk_window_set_destroy_with_parent ( GTK_WINDOW(vw->save_dia), TRUE );
3320 // Auto append / replace extension with '.vik' to the suggested file name as it's going to be a Viking File
3321 gchar* auto_save_name = g_strdup ( window_get_filename ( vw ) );
3322 if ( ! a_file_check_ext ( auto_save_name, ".vik" ) )
3323 auto_save_name = g_strconcat ( auto_save_name, ".vik", NULL );
3325 gtk_file_chooser_set_current_name (GTK_FILE_CHOOSER(vw->save_dia), auto_save_name);
3327 while ( gtk_dialog_run ( GTK_DIALOG(vw->save_dia) ) == GTK_RESPONSE_ACCEPT )
3329 fn = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER(vw->save_dia) );
3330 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 ) ) )
3332 window_set_filename ( vw, fn );
3333 rv = window_save ( vw );
3334 vw->modified = FALSE;
3338 g_free ( auto_save_name );
3339 gtk_widget_hide ( vw->save_dia );
3343 static gboolean window_save ( VikWindow *vw )
3345 vik_window_set_busy_cursor ( vw );
3346 gboolean success = TRUE;
3348 if ( a_file_save ( vik_layers_panel_get_top_layer ( vw->viking_vlp ), vw->viking_vvp, vw->filename ) )
3350 update_recently_used_document ( vw, vw->filename );
3354 a_dialog_error_msg ( GTK_WINDOW(vw), _("The filename you requested could not be opened for writing.") );
3357 vik_window_clear_busy_cursor ( vw );
3361 static gboolean save_file ( GtkAction *a, VikWindow *vw )
3363 if ( ! vw->filename )
3364 return save_file_as ( NULL, vw );
3367 vw->modified = FALSE;
3368 return window_save ( vw );
3375 * Export all TRW Layers in the list to individual files in the specified directory
3377 * Returns: %TRUE on success
3379 static gboolean export_to ( VikWindow *vw, GList *gl, VikFileType_t vft, const gchar *dir, const gchar *extension )
3381 gboolean success = TRUE;
3383 gint export_count = 0;
3385 vik_window_set_busy_cursor ( vw );
3389 gchar *fn = g_strconcat ( dir, G_DIR_SEPARATOR_S, VIK_LAYER(gl->data)->name, extension, NULL );
3391 // Some protection in attempting to write too many same named files
3392 // As this will get horribly slow...
3393 gboolean safe = FALSE;
3395 while ( ii < 5000 ) {
3396 if ( g_file_test ( fn, G_FILE_TEST_EXISTS ) ) {
3399 fn = g_strdup_printf ( "%s%s%s#%03d%s", dir, G_DIR_SEPARATOR_S, VIK_LAYER(gl->data)->name, ii, extension );
3410 // NB: We allow exporting empty layers
3412 gboolean this_success = a_file_export ( VIK_TRW_LAYER(gl->data), fn, vft, NULL, TRUE );
3414 // Show some progress
3415 if ( this_success ) {
3417 gchar *message = g_strdup_printf ( _("Exporting to file: %s"), fn );
3418 vik_statusbar_set_message ( vw->viking_vs, VIK_STATUSBAR_INFO, message );
3419 while ( gtk_events_pending() )
3420 gtk_main_iteration ();
3424 success = success && this_success;
3428 gl = g_list_next ( gl );
3431 vik_window_clear_busy_cursor ( vw );
3433 // Confirm what happened.
3434 gchar *message = g_strdup_printf ( _("Exported files: %d"), export_count );
3435 vik_statusbar_set_message ( vw->viking_vs, VIK_STATUSBAR_INFO, message );
3441 static void export_to_common ( VikWindow *vw, VikFileType_t vft, const gchar *extension )
3443 GList *gl = vik_layers_panel_get_all_layers_of_type ( vw->viking_vlp, VIK_LAYER_TRW, TRUE );
3446 a_dialog_info_msg ( GTK_WINDOW(vw), _("Nothing to Export!") );
3450 GtkWidget *dialog = gtk_file_chooser_dialog_new ( _("Export to directory"),
3452 GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER,
3454 GTK_RESPONSE_REJECT,
3456 GTK_RESPONSE_ACCEPT,
3458 gtk_window_set_transient_for ( GTK_WINDOW(dialog), GTK_WINDOW(vw) );
3459 gtk_window_set_destroy_with_parent ( GTK_WINDOW(dialog), TRUE );
3460 gtk_window_set_modal ( GTK_WINDOW(dialog), TRUE );
3462 gtk_widget_show_all ( dialog );
3464 if ( gtk_dialog_run ( GTK_DIALOG(dialog) ) == GTK_RESPONSE_ACCEPT ) {
3465 gchar *dir = gtk_file_chooser_get_filename ( GTK_FILE_CHOOSER(dialog) );
3466 gtk_widget_destroy ( dialog );
3468 if ( !export_to ( vw, gl, vft, dir, extension ) )
3469 a_dialog_error_msg ( GTK_WINDOW(vw),_("Could not convert all files") );
3474 gtk_widget_destroy ( dialog );
3479 static void export_to_gpx ( GtkAction *a, VikWindow *vw )
3481 export_to_common ( vw, FILE_TYPE_GPX, ".gpx" );
3484 static void export_to_kml ( GtkAction *a, VikWindow *vw )
3486 export_to_common ( vw, FILE_TYPE_KML, ".kml" );
3489 #if !GLIB_CHECK_VERSION(2,26,0)
3490 typedef struct stat GStatBuf;
3493 static void file_properties_cb ( GtkAction *a, VikWindow *vw )
3495 gchar *message = NULL;
3496 if ( vw->filename ) {
3497 if ( g_file_test ( vw->filename, G_FILE_TEST_EXISTS ) ) {
3498 // Get some timestamp information of the file
3500 if ( g_stat ( vw->filename, &stat_buf ) == 0 ) {
3502 strftime ( time_buf, sizeof(time_buf), "%c", gmtime((const time_t *)&stat_buf.st_mtime) );
3504 gint byte_size = stat_buf.st_size;
3505 #if GLIB_CHECK_VERSION(2,30,0)
3506 size = g_format_size_full ( byte_size, G_FORMAT_SIZE_DEFAULT );
3508 size = g_format_size_for_display ( byte_size );
3510 message = g_strdup_printf ( "%s\n\n%s\n\n%s", vw->filename, time_buf, size );
3515 message = g_strdup ( _("File not accessible") );
3518 message = g_strdup ( _("No Viking File") );
3521 a_dialog_info_msg ( GTK_WINDOW(vw), message );
3525 static void my_acquire ( VikWindow *vw, VikDataSourceInterface *datasource )
3527 vik_datasource_mode_t mode = datasource->mode;
3528 if ( mode == VIK_DATASOURCE_AUTO_LAYER_MANAGEMENT )
3529 mode = VIK_DATASOURCE_CREATENEWLAYER;
3530 a_acquire ( vw, vw->viking_vlp, vw->viking_vvp, mode, datasource, NULL, NULL );
3533 static void acquire_from_gps ( GtkAction *a, VikWindow *vw )
3535 my_acquire ( vw, &vik_datasource_gps_interface );
3538 static void acquire_from_file ( GtkAction *a, VikWindow *vw )
3540 my_acquire ( vw, &vik_datasource_file_interface );
3543 static void acquire_from_geojson ( GtkAction *a, VikWindow *vw )
3545 my_acquire ( vw, &vik_datasource_geojson_interface );
3548 static void acquire_from_routing ( GtkAction *a, VikWindow *vw )
3550 my_acquire ( vw, &vik_datasource_routing_interface );
3553 #ifdef VIK_CONFIG_OPENSTREETMAP
3554 static void acquire_from_osm ( GtkAction *a, VikWindow *vw )
3556 my_acquire ( vw, &vik_datasource_osm_interface );
3559 static void acquire_from_my_osm ( GtkAction *a, VikWindow *vw )
3561 my_acquire ( vw, &vik_datasource_osm_my_traces_interface );
3565 #ifdef VIK_CONFIG_GEOCACHES
3566 static void acquire_from_gc ( GtkAction *a, VikWindow *vw )
3568 my_acquire ( vw, &vik_datasource_gc_interface );
3572 #ifdef VIK_CONFIG_GEOTAG
3573 static void acquire_from_geotag ( GtkAction *a, VikWindow *vw )
3575 my_acquire ( vw, &vik_datasource_geotag_interface );
3579 #ifdef VIK_CONFIG_GEONAMES
3580 static void acquire_from_wikipedia ( GtkAction *a, VikWindow *vw )
3582 my_acquire ( vw, &vik_datasource_wikipedia_interface );
3586 static void acquire_from_url ( GtkAction *a, VikWindow *vw )
3588 my_acquire ( vw, &vik_datasource_url_interface );
3591 static void goto_default_location( GtkAction *a, VikWindow *vw)
3594 ll.lat = a_vik_get_default_lat();
3595 ll.lon = a_vik_get_default_long();
3596 vik_viewport_set_center_latlon(vw->viking_vvp, &ll, TRUE);
3597 vik_layers_panel_emit_update(vw->viking_vlp);
3601 static void goto_address( GtkAction *a, VikWindow *vw)
3603 a_vik_goto ( vw, vw->viking_vvp );
3604 vik_layers_panel_emit_update ( vw->viking_vlp );
3607 static void mapcache_flush_cb ( GtkAction *a, VikWindow *vw )
3612 static void menu_copy_centre_cb ( GtkAction *a, VikWindow *vw )
3614 const VikCoord* coord;
3616 gchar *lat = NULL, *lon = NULL;
3618 coord = vik_viewport_get_center ( vw->viking_vvp );
3619 vik_coord_to_utm ( coord, &utm );
3621 gboolean full_format = FALSE;
3622 a_settings_get_boolean ( VIK_SETTINGS_WIN_COPY_CENTRE_FULL_FORMAT, &full_format );
3625 // Bells & Whistles - may include degrees, minutes and second symbols
3626 get_location_strings ( vw, utm, &lat, &lon );
3628 // Simple x.xx y.yy format
3630 a_coords_utm_to_latlon ( &utm, &ll );
3631 lat = g_strdup_printf ( "%.6f", ll.lat );
3632 lon = g_strdup_printf ( "%.6f", ll.lon );
3635 gchar *msg = g_strdup_printf ( "%s %s", lat, lon );
3639 a_clipboard_copy ( VIK_CLIPBOARD_DATA_TEXT, 0, 0, 0, msg, NULL );
3644 static void layer_defaults_cb ( GtkAction *a, VikWindow *vw )
3646 gchar **texts = g_strsplit ( gtk_action_get_name(a), "Layer", 0 );
3649 return; // Internally broken :(
3651 if ( ! a_layer_defaults_show_window ( GTK_WINDOW(vw), texts[1] ) )
3652 a_dialog_info_msg ( GTK_WINDOW(vw), _("This layer has no configurable properties.") );
3653 // NB no update needed
3655 g_strfreev ( texts );
3658 static void preferences_change_update ( VikWindow *vw, gpointer data )
3660 // Want to update all TrackWaypoint layers
3661 GList *layers = vik_layers_panel_get_all_layers_of_type ( vw->viking_vlp, VIK_LAYER_TRW, TRUE );
3667 // Reset the individual waypoints themselves due to the preferences change
3668 VikTrwLayer *vtl = VIK_TRW_LAYER(layers->data);
3669 vik_trw_layer_reset_waypoints ( vtl );
3670 layers = g_list_next ( layers );
3673 g_list_free ( layers );
3678 static void preferences_cb ( GtkAction *a, VikWindow *vw )
3680 gboolean wp_icon_size = a_vik_get_use_large_waypoint_icons();
3682 a_preferences_show_window ( GTK_WINDOW(vw) );
3684 // Has the waypoint size setting changed?
3685 if (wp_icon_size != a_vik_get_use_large_waypoint_icons()) {
3686 // Delete icon indexing 'cache' and so automatically regenerates with the new setting when changed
3687 clear_garmin_icon_syms ();
3689 // Update all windows
3690 g_slist_foreach ( window_list, (GFunc) preferences_change_update, NULL );
3693 // Ensure TZ Lookup initialized
3694 if ( a_vik_get_time_ref_frame() == VIK_TIME_REF_WORLD )
3695 vu_setup_lat_lon_tz_lookup();
3697 toolbar_apply_settings ( vw->viking_vtb, vw->main_vbox, vw->menu_hbox, TRUE );
3700 static void default_location_cb ( GtkAction *a, VikWindow *vw )
3702 /* Simplistic repeat of preference setting
3703 Only the name & type are important for setting the preference via this 'external' way */
3704 VikLayerParam pref_lat[] = {
3705 { VIK_LAYER_NUM_TYPES,
3706 VIKING_PREFERENCES_NAMESPACE "default_latitude",
3707 VIK_LAYER_PARAM_DOUBLE,
3710 VIK_LAYER_WIDGET_SPINBUTTON,
3719 VikLayerParam pref_lon[] = {
3720 { VIK_LAYER_NUM_TYPES,
3721 VIKING_PREFERENCES_NAMESPACE "default_longitude",
3722 VIK_LAYER_PARAM_DOUBLE,
3725 VIK_LAYER_WIDGET_SPINBUTTON,
3735 /* Get current center */
3737 vik_coord_to_latlon ( vik_viewport_get_center ( vw->viking_vvp ), &ll );
3739 /* Apply to preferences */
3740 VikLayerParamData vlp_data;
3741 vlp_data.d = ll.lat;
3742 a_preferences_run_setparam (vlp_data, pref_lat);
3743 vlp_data.d = ll.lon;
3744 a_preferences_run_setparam (vlp_data, pref_lon);
3745 /* Remember to save */
3746 a_preferences_save_to_file();
3749 static void clear_cb ( GtkAction *a, VikWindow *vw )
3751 vik_layers_panel_clear ( vw->viking_vlp );
3752 window_set_filename ( vw, NULL );
3756 static void window_close ( GtkAction *a, VikWindow *vw )
3758 if ( ! delete_event ( vw ) )
3759 gtk_widget_destroy ( GTK_WIDGET(vw) );
3762 static gboolean save_file_and_exit ( GtkAction *a, VikWindow *vw )
3764 if (save_file( NULL, vw)) {
3765 window_close( NULL, vw);
3772 static void zoom_to_cb ( GtkAction *a, VikWindow *vw )
3774 gdouble xmpp = vik_viewport_get_xmpp ( vw->viking_vvp ), ympp = vik_viewport_get_ympp ( vw->viking_vvp );
3775 if ( a_dialog_custom_zoom ( GTK_WINDOW(vw), &xmpp, &ympp ) )
3777 vik_viewport_set_xmpp ( vw->viking_vvp, xmpp );
3778 vik_viewport_set_ympp ( vw->viking_vvp, ympp );
3783 static void save_image_file ( VikWindow *vw, const gchar *fn, guint w, guint h, gdouble zoom, gboolean save_as_png )
3785 /* more efficient way: stuff draws directly to pixbuf (fork viewport) */
3786 GdkPixbuf *pixbuf_to_save;
3787 gdouble old_xmpp, old_ympp;
3788 GError *error = NULL;
3790 GtkWidget *msgbox = gtk_message_dialog_new ( GTK_WINDOW(vw),
3791 GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
3794 _("Generating image file...") );
3796 g_signal_connect_swapped (msgbox, "response", G_CALLBACK (gtk_widget_destroy), msgbox);
3797 // Ensure dialog shown
3798 gtk_widget_show_all ( msgbox );
3800 vik_statusbar_set_message ( vw->viking_vs, VIK_STATUSBAR_INFO, _("Generating image file...") );
3801 while ( gtk_events_pending() )
3802 gtk_main_iteration ();
3803 // Despite many efforts & variations, GTK on my Linux system doesn't show the actual msgbox contents :(
3804 // At least the empty box can give a clue something's going on + the statusbar msg...
3805 // Windows version under Wine OK!
3807 /* backup old zoom & set new */
3808 old_xmpp = vik_viewport_get_xmpp ( vw->viking_vvp );
3809 old_ympp = vik_viewport_get_ympp ( vw->viking_vvp );
3810 vik_viewport_set_zoom ( vw->viking_vvp, zoom );
3812 /* reset width and height: */
3813 vik_viewport_configure_manually ( vw->viking_vvp, w, h );
3815 /* draw all layers */
3818 /* save buffer as file. */
3819 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);
3820 if ( !pixbuf_to_save ) {
3821 g_warning("Failed to generate internal pixmap size: %d x %d", w, h);
3822 gtk_message_dialog_set_markup ( GTK_MESSAGE_DIALOG(msgbox), _("Failed to generate internal image.\n\nTry creating a smaller image.") );
3826 gdk_pixbuf_save ( pixbuf_to_save, fn, save_as_png ? "png" : "jpeg", &error, NULL );
3829 g_warning("Unable to write to file %s: %s", fn, error->message );
3830 gtk_message_dialog_set_markup ( GTK_MESSAGE_DIALOG(msgbox), _("Failed to generate image file.") );
3831 g_error_free (error);
3835 gtk_message_dialog_set_markup ( GTK_MESSAGE_DIALOG(msgbox), _("Image file generated.") );
3837 g_object_unref ( G_OBJECT(pixbuf_to_save) );
3840 vik_statusbar_set_message ( vw->viking_vs, VIK_STATUSBAR_INFO, "" );
3841 gtk_dialog_add_button ( GTK_DIALOG(msgbox), GTK_STOCK_OK, GTK_RESPONSE_OK );
3842 gtk_dialog_run ( GTK_DIALOG(msgbox) ); // Don't care about the result
3844 /* pretend like nothing happened ;) */
3845 vik_viewport_set_xmpp ( vw->viking_vvp, old_xmpp );
3846 vik_viewport_set_ympp ( vw->viking_vvp, old_ympp );
3847 vik_viewport_configure ( vw->viking_vvp );
3851 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 )
3853 gulong size = sizeof(gchar) * (strlen(fn) + 15);
3854 gchar *name_of_file = g_malloc ( size );
3856 struct UTM utm_orig, utm;
3858 /* *** copied from above *** */
3859 GdkPixbuf *pixbuf_to_save;
3860 gdouble old_xmpp, old_ympp;
3861 GError *error = NULL;
3863 /* backup old zoom & set new */
3864 old_xmpp = vik_viewport_get_xmpp ( vw->viking_vvp );
3865 old_ympp = vik_viewport_get_ympp ( vw->viking_vvp );
3866 vik_viewport_set_zoom ( vw->viking_vvp, zoom );
3868 /* reset width and height: do this only once for all images (same size) */
3869 vik_viewport_configure_manually ( vw->viking_vvp, w, h );
3870 /* *** end copy from above *** */
3872 g_assert ( vik_viewport_get_coord_mode ( vw->viking_vvp ) == VIK_COORD_UTM );
3876 utm_orig = *((const struct UTM *)vik_viewport_get_center ( vw->viking_vvp ));
3878 for ( y = 1; y <= tiles_h; y++ )
3880 for ( x = 1; x <= tiles_w; x++ )
3882 g_snprintf ( name_of_file, size, "%s%cy%d-x%d.%s", fn, G_DIR_SEPARATOR, y, x, save_as_png ? "png" : "jpg" );
3884 if ( tiles_w & 0x1 )
3885 utm.easting += ((gdouble)x - ceil(((gdouble)tiles_w)/2)) * (w*zoom);
3887 utm.easting += ((gdouble)x - (((gdouble)tiles_w)+1)/2) * (w*zoom);
3888 if ( tiles_h & 0x1 ) /* odd */
3889 utm.northing -= ((gdouble)y - ceil(((gdouble)tiles_h)/2)) * (h*zoom);
3891 utm.northing -= ((gdouble)y - (((gdouble)tiles_h)+1)/2) * (h*zoom);
3893 /* move to correct place. */
3894 vik_viewport_set_center_utm ( vw->viking_vvp, &utm, FALSE );
3898 /* save buffer as file. */
3899 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);
3900 gdk_pixbuf_save ( pixbuf_to_save, name_of_file, save_as_png ? "png" : "jpeg", &error, NULL );
3903 gchar *msg = g_strdup_printf (_("Unable to write to file %s: %s"), name_of_file, error->message );
3904 vik_statusbar_set_message ( vw->viking_vs, VIK_STATUSBAR_INFO, msg );
3906 g_error_free (error);
3909 g_object_unref ( G_OBJECT(pixbuf_to_save) );
3913 vik_viewport_set_center_utm ( vw->viking_vvp, &utm_orig, FALSE );
3914 vik_viewport_set_xmpp ( vw->viking_vvp, old_xmpp );
3915 vik_viewport_set_ympp ( vw->viking_vvp, old_ympp );
3916 vik_viewport_configure ( vw->viking_vvp );
3919 g_free ( name_of_file );
3922 static void draw_to_image_file_current_window_cb(GtkWidget* widget,GdkEventButton *event,gpointer *pass_along)
3924 VikWindow *vw = VIK_WINDOW(pass_along[0]);
3925 GtkSpinButton *width_spin = GTK_SPIN_BUTTON(pass_along[1]), *height_spin = GTK_SPIN_BUTTON(pass_along[2]);
3927 gint active = gtk_combo_box_get_active ( GTK_COMBO_BOX(pass_along[3]) );
3928 gdouble zoom = pow (2, active-2 );
3930 gdouble width_min, width_max, height_min, height_max;
3933 gtk_spin_button_get_range ( width_spin, &width_min, &width_max );
3934 gtk_spin_button_get_range ( height_spin, &height_min, &height_max );
3936 /* TODO: support for xzoom and yzoom values */
3937 width = vik_viewport_get_width ( vw->viking_vvp ) * vik_viewport_get_xmpp ( vw->viking_vvp ) / zoom;
3938 height = vik_viewport_get_height ( vw->viking_vvp ) * vik_viewport_get_xmpp ( vw->viking_vvp ) / zoom;
3940 if ( width > width_max || width < width_min || height > height_max || height < height_min )
3941 a_dialog_info_msg ( GTK_WINDOW(vw), _("Viewable region outside allowable pixel size bounds for image. Clipping width/height values.") );
3943 gtk_spin_button_set_value ( width_spin, width );
3944 gtk_spin_button_set_value ( height_spin, height );
3947 static void draw_to_image_file_total_area_cb (GtkSpinButton *spinbutton, gpointer *pass_along)
3949 GtkSpinButton *width_spin = GTK_SPIN_BUTTON(pass_along[1]), *height_spin = GTK_SPIN_BUTTON(pass_along[2]);
3951 gint active = gtk_combo_box_get_active ( GTK_COMBO_BOX(pass_along[3]) );
3952 gdouble zoom = pow (2, active-2 );
3956 w = gtk_spin_button_get_value(width_spin) * zoom;
3957 h = gtk_spin_button_get_value(height_spin) * zoom;
3958 if (pass_along[4]) /* save many images; find TOTAL area covered */
3960 w *= gtk_spin_button_get_value(GTK_SPIN_BUTTON(pass_along[4]));
3961 h *= gtk_spin_button_get_value(GTK_SPIN_BUTTON(pass_along[5]));
3963 vik_units_distance_t dist_units = a_vik_get_units_distance ();
3964 switch (dist_units) {
3965 case VIK_UNITS_DISTANCE_KILOMETRES:
3966 label_text = g_strdup_printf ( _("Total area: %ldm x %ldm (%.3f sq. km)"), (glong)w, (glong)h, (w*h/1000000));
3968 case VIK_UNITS_DISTANCE_MILES:
3969 label_text = g_strdup_printf ( _("Total area: %ldm x %ldm (%.3f sq. miles)"), (glong)w, (glong)h, (w*h/2589988.11));
3971 case VIK_UNITS_DISTANCE_NAUTICAL_MILES:
3972 label_text = g_strdup_printf ( _("Total area: %ldm x %ldm (%.3f sq. NM)"), (glong)w, (glong)h, (w*h/(1852.0*1852.0)));
3975 label_text = g_strdup_printf ("Just to keep the compiler happy");
3976 g_critical("Houston, we've had a problem. distance=%d", dist_units);
3979 gtk_label_set_text(GTK_LABEL(pass_along[6]), label_text);
3980 g_free ( label_text );
3984 * Get an allocated filename (or directory as specified)
3986 static gchar* draw_image_filename ( VikWindow *vw, gboolean one_image_only )
3989 if ( one_image_only )
3992 if (!vw->save_img_dia) {
3993 vw->save_img_dia = gtk_file_chooser_dialog_new (_("Save Image"),
3995 GTK_FILE_CHOOSER_ACTION_SAVE,
3996 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
3997 GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT,
4000 gchar *cwd = g_get_current_dir();
4002 gtk_file_chooser_set_current_folder ( GTK_FILE_CHOOSER(vw->save_img_dia), cwd );
4006 GtkFileChooser *chooser = GTK_FILE_CHOOSER ( vw->save_img_dia );
4008 GtkFileFilter *filter;
4009 filter = gtk_file_filter_new ();
4010 gtk_file_filter_set_name ( filter, _("All") );
4011 gtk_file_filter_add_pattern ( filter, "*" );
4012 gtk_file_chooser_add_filter ( chooser, filter );
4014 filter = gtk_file_filter_new ();
4015 gtk_file_filter_set_name ( filter, _("JPG") );
4016 gtk_file_filter_add_mime_type ( filter, "image/jpeg");
4017 gtk_file_chooser_add_filter ( chooser, filter );
4019 if ( !vw->draw_image_save_as_png )
4020 gtk_file_chooser_set_filter ( chooser, filter );
4022 filter = gtk_file_filter_new ();
4023 gtk_file_filter_set_name ( filter, _("PNG") );
4024 gtk_file_filter_add_mime_type ( filter, "image/png");
4025 gtk_file_chooser_add_filter ( chooser, filter );
4027 if ( vw->draw_image_save_as_png )
4028 gtk_file_chooser_set_filter ( chooser, filter );
4030 gtk_window_set_transient_for ( GTK_WINDOW(vw->save_img_dia), GTK_WINDOW(vw) );
4031 gtk_window_set_destroy_with_parent ( GTK_WINDOW(vw->save_img_dia), TRUE );
4034 if ( gtk_dialog_run ( GTK_DIALOG(vw->save_img_dia) ) == GTK_RESPONSE_ACCEPT ) {
4035 fn = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER(vw->save_img_dia) );
4036 if ( g_file_test ( fn, G_FILE_TEST_EXISTS ) )
4037 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 ) ) )
4040 gtk_widget_hide ( vw->save_img_dia );
4044 // For some reason this method is only written to work in UTM...
4045 if ( vik_viewport_get_coord_mode(vw->viking_vvp) != VIK_COORD_UTM ) {
4046 a_dialog_error_msg ( GTK_WINDOW(vw), _("You must be in UTM mode to use this feature") );
4050 if (!vw->save_img_dir_dia) {
4051 vw->save_img_dir_dia = gtk_file_chooser_dialog_new (_("Choose a directory to hold images"),
4053 GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER,
4054 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
4055 GTK_STOCK_OK, GTK_RESPONSE_ACCEPT,
4057 gtk_window_set_transient_for ( GTK_WINDOW(vw->save_img_dir_dia), GTK_WINDOW(vw) );
4058 gtk_window_set_destroy_with_parent ( GTK_WINDOW(vw->save_img_dir_dia), TRUE );
4061 if ( gtk_dialog_run ( GTK_DIALOG(vw->save_img_dir_dia) ) == GTK_RESPONSE_ACCEPT ) {
4062 fn = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER(vw->save_img_dir_dia) );
4064 gtk_widget_hide ( vw->save_img_dir_dia );
4069 static void draw_to_image_file ( VikWindow *vw, gboolean one_image_only )
4071 /* todo: default for answers inside VikWindow or static (thruout instance) */
4072 GtkWidget *dialog = gtk_dialog_new_with_buttons ( _("Save to Image File"), GTK_WINDOW(vw),
4073 GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
4075 GTK_RESPONSE_REJECT,
4077 GTK_RESPONSE_ACCEPT,
4079 GtkWidget *width_label, *width_spin, *height_label, *height_spin;
4080 GtkWidget *png_radio, *jpeg_radio;
4081 GtkWidget *current_window_button;
4082 gpointer current_window_pass_along[7];
4083 GtkWidget *zoom_label, *zoom_combo;
4084 GtkWidget *total_size_label;
4086 /* only used if (!one_image_only) */
4087 GtkWidget *tiles_width_spin = NULL, *tiles_height_spin = NULL;
4089 width_label = gtk_label_new ( _("Width (pixels):") );
4090 width_spin = gtk_spin_button_new ( GTK_ADJUSTMENT(gtk_adjustment_new ( vw->draw_image_width, 10, 50000, 10, 100, 0 )), 10, 0 );
4091 height_label = gtk_label_new ( _("Height (pixels):") );
4092 height_spin = gtk_spin_button_new ( GTK_ADJUSTMENT(gtk_adjustment_new ( vw->draw_image_height, 10, 50000, 10, 100, 0 )), 10, 0 );
4094 GtkWidget *win_warning_label = gtk_label_new ( _("WARNING: USING LARGE IMAGES OVER 10000x10000\nMAY CRASH THE PROGRAM!") );
4096 zoom_label = gtk_label_new ( _("Zoom (meters per pixel):") );
4097 /* TODO: separate xzoom and yzoom factors */
4098 zoom_combo = create_zoom_combo_all_levels();
4100 gdouble mpp = vik_viewport_get_xmpp(vw->viking_vvp);
4101 gint active = 2 + round ( log (mpp) / log (2) );
4103 // Can we not hard code size here?
4108 gtk_combo_box_set_active ( GTK_COMBO_BOX(zoom_combo), active );
4110 total_size_label = gtk_label_new ( NULL );
4112 current_window_button = gtk_button_new_with_label ( _("Area in current viewable window") );
4113 current_window_pass_along [0] = vw;
4114 current_window_pass_along [1] = width_spin;
4115 current_window_pass_along [2] = height_spin;
4116 current_window_pass_along [3] = zoom_combo;
4117 current_window_pass_along [4] = NULL; /* used for one_image_only != 1 */
4118 current_window_pass_along [5] = NULL;
4119 current_window_pass_along [6] = total_size_label;
4120 g_signal_connect ( G_OBJECT(current_window_button), "button_press_event", G_CALLBACK(draw_to_image_file_current_window_cb), current_window_pass_along );
4122 png_radio = gtk_radio_button_new_with_label ( NULL, _("Save as PNG") );
4123 jpeg_radio = gtk_radio_button_new_with_label_from_widget ( GTK_RADIO_BUTTON(png_radio), _("Save as JPEG") );
4125 gtk_box_pack_start (GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), png_radio, FALSE, FALSE, 0);
4126 gtk_box_pack_start (GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), jpeg_radio, FALSE, FALSE, 0);
4128 if ( ! vw->draw_image_save_as_png )
4129 gtk_toggle_button_set_active ( GTK_TOGGLE_BUTTON(jpeg_radio), TRUE );
4131 gtk_box_pack_start (GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), width_label, FALSE, FALSE, 0);
4132 gtk_box_pack_start (GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), width_spin, FALSE, FALSE, 0);
4133 gtk_box_pack_start (GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), height_label, FALSE, FALSE, 0);
4134 gtk_box_pack_start (GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), height_spin, FALSE, FALSE, 0);
4136 gtk_box_pack_start (GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), win_warning_label, FALSE, FALSE, 0);
4138 gtk_box_pack_start (GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), current_window_button, FALSE, FALSE, 0);
4139 gtk_box_pack_start (GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), zoom_label, FALSE, FALSE, 0);
4140 gtk_box_pack_start (GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), zoom_combo, FALSE, FALSE, 0);
4142 if ( ! one_image_only )
4144 GtkWidget *tiles_width_label, *tiles_height_label;
4146 tiles_width_label = gtk_label_new ( _("East-west image tiles:") );
4147 tiles_width_spin = gtk_spin_button_new ( GTK_ADJUSTMENT(gtk_adjustment_new ( 5, 1, 10, 1, 100, 0 )), 1, 0 );
4148 tiles_height_label = gtk_label_new ( _("North-south image tiles:") );
4149 tiles_height_spin = gtk_spin_button_new ( GTK_ADJUSTMENT(gtk_adjustment_new ( 5, 1, 10, 1, 100, 0 )), 1, 0 );
4150 gtk_box_pack_start (GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), tiles_width_label, FALSE, FALSE, 0);
4151 gtk_box_pack_start (GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), tiles_width_spin, FALSE, FALSE, 0);
4152 gtk_box_pack_start (GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), tiles_height_label, FALSE, FALSE, 0);
4153 gtk_box_pack_start (GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), tiles_height_spin, FALSE, FALSE, 0);
4155 current_window_pass_along [4] = tiles_width_spin;
4156 current_window_pass_along [5] = tiles_height_spin;
4157 g_signal_connect ( G_OBJECT(tiles_width_spin), "value-changed", G_CALLBACK(draw_to_image_file_total_area_cb), current_window_pass_along );
4158 g_signal_connect ( G_OBJECT(tiles_height_spin), "value-changed", G_CALLBACK(draw_to_image_file_total_area_cb), current_window_pass_along );
4160 gtk_box_pack_start (GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), total_size_label, FALSE, FALSE, 0);
4161 g_signal_connect ( G_OBJECT(width_spin), "value-changed", G_CALLBACK(draw_to_image_file_total_area_cb), current_window_pass_along );
4162 g_signal_connect ( G_OBJECT(height_spin), "value-changed", G_CALLBACK(draw_to_image_file_total_area_cb), current_window_pass_along );
4163 g_signal_connect ( G_OBJECT(zoom_combo), "changed", G_CALLBACK(draw_to_image_file_total_area_cb), current_window_pass_along );
4165 draw_to_image_file_total_area_cb ( NULL, current_window_pass_along ); /* set correct size info now */
4167 gtk_dialog_set_default_response ( GTK_DIALOG(dialog), GTK_RESPONSE_ACCEPT );
4169 gtk_widget_show_all ( gtk_dialog_get_content_area(GTK_DIALOG(dialog)) );
4171 if ( gtk_dialog_run ( GTK_DIALOG(dialog) ) == GTK_RESPONSE_ACCEPT )
4173 gtk_widget_hide ( GTK_WIDGET(dialog) );
4175 gchar *fn = draw_image_filename ( vw, one_image_only );
4179 gint active_z = gtk_combo_box_get_active ( GTK_COMBO_BOX(zoom_combo) );
4180 gdouble zoom = pow (2, active_z-2 );
4182 if ( one_image_only )
4183 save_image_file ( vw, fn,
4184 vw->draw_image_width = gtk_spin_button_get_value_as_int ( GTK_SPIN_BUTTON(width_spin) ),
4185 vw->draw_image_height = gtk_spin_button_get_value_as_int ( GTK_SPIN_BUTTON(height_spin) ),
4187 vw->draw_image_save_as_png = gtk_toggle_button_get_active ( GTK_TOGGLE_BUTTON(png_radio) ) );
4189 // NB is in UTM mode ATM
4190 save_image_dir ( vw, fn,
4191 vw->draw_image_width = gtk_spin_button_get_value_as_int ( GTK_SPIN_BUTTON(width_spin) ),
4192 vw->draw_image_height = gtk_spin_button_get_value_as_int ( GTK_SPIN_BUTTON(height_spin) ),
4194 vw->draw_image_save_as_png = gtk_toggle_button_get_active ( GTK_TOGGLE_BUTTON(png_radio) ),
4195 gtk_spin_button_get_value ( GTK_SPIN_BUTTON(tiles_width_spin) ),
4196 gtk_spin_button_get_value ( GTK_SPIN_BUTTON(tiles_height_spin) ) );
4201 gtk_widget_destroy ( GTK_WIDGET(dialog) );
4205 static void draw_to_image_file_cb ( GtkAction *a, VikWindow *vw )
4207 draw_to_image_file ( vw, TRUE );
4210 static void draw_to_image_dir_cb ( GtkAction *a, VikWindow *vw )
4212 draw_to_image_file ( vw, FALSE );
4215 static void print_cb ( GtkAction *a, VikWindow *vw )
4217 a_print(vw, vw->viking_vvp);
4220 /* really a misnomer: changes coord mode (actual coordinates) AND/OR draw mode (viewport only) */
4221 static void window_change_coord_mode_cb ( GtkAction *old_a, GtkAction *a, VikWindow *vw )
4223 const gchar *name = gtk_action_get_name(a);
4224 GtkToggleToolButton *tbutton = (GtkToggleToolButton *)toolbar_get_widget_by_name ( vw->viking_vtb, name );
4226 gtk_toggle_tool_button_set_active ( tbutton, TRUE );
4228 VikViewportDrawMode drawmode;
4229 if (!g_strcmp0(name, "ModeUTM")) {
4230 drawmode = VIK_VIEWPORT_DRAWMODE_UTM;
4232 else if (!g_strcmp0(name, "ModeLatLon")) {
4233 drawmode = VIK_VIEWPORT_DRAWMODE_LATLON;
4235 else if (!g_strcmp0(name, "ModeExpedia")) {
4236 drawmode = VIK_VIEWPORT_DRAWMODE_EXPEDIA;
4238 else if (!g_strcmp0(name, "ModeMercator")) {
4239 drawmode = VIK_VIEWPORT_DRAWMODE_MERCATOR;
4242 g_critical("Houston, we've had a problem.");
4246 if ( !vw->only_updating_coord_mode_ui )
4248 VikViewportDrawMode olddrawmode = vik_viewport_get_drawmode ( vw->viking_vvp );
4249 if ( olddrawmode != drawmode )
4251 /* this takes care of coord mode too */
4252 vik_viewport_set_drawmode ( vw->viking_vvp, drawmode );
4253 if ( drawmode == VIK_VIEWPORT_DRAWMODE_UTM ) {
4254 vik_layers_panel_change_coord_mode ( vw->viking_vlp, VIK_COORD_UTM );
4255 } else if ( olddrawmode == VIK_VIEWPORT_DRAWMODE_UTM ) {
4256 vik_layers_panel_change_coord_mode ( vw->viking_vlp, VIK_COORD_LATLON );
4263 static void toggle_draw_scale ( GtkAction *a, VikWindow *vw )
4265 gboolean state = !vik_viewport_get_draw_scale ( vw->viking_vvp );
4266 GtkWidget *check_box = gtk_ui_manager_get_widget ( vw->uim, "/ui/MainMenu/View/SetShow/ShowScale" );
4269 gtk_check_menu_item_set_active ( GTK_CHECK_MENU_ITEM(check_box), state );
4270 vik_viewport_set_draw_scale ( vw->viking_vvp, state );
4274 static void toggle_draw_centermark ( GtkAction *a, VikWindow *vw )
4276 gboolean state = !vik_viewport_get_draw_centermark ( vw->viking_vvp );
4277 GtkWidget *check_box = gtk_ui_manager_get_widget ( vw->uim, "/ui/MainMenu/View/SetShow/ShowCenterMark" );
4280 gtk_check_menu_item_set_active ( GTK_CHECK_MENU_ITEM(check_box), state );
4281 vik_viewport_set_draw_centermark ( vw->viking_vvp, state );
4285 static void toggle_draw_highlight ( GtkAction *a, VikWindow *vw )
4287 gboolean state = !vik_viewport_get_draw_highlight ( vw->viking_vvp );
4288 GtkWidget *check_box = gtk_ui_manager_get_widget ( vw->uim, "/ui/MainMenu/View/SetShow/ShowHighlight" );
4291 gtk_check_menu_item_set_active ( GTK_CHECK_MENU_ITEM(check_box), state );
4292 vik_viewport_set_draw_highlight ( vw->viking_vvp, state );
4296 static void set_bg_color ( GtkAction *a, VikWindow *vw )
4298 GtkWidget *colorsd = gtk_color_selection_dialog_new ( _("Choose a background color") );
4299 GdkColor *color = vik_viewport_get_background_gdkcolor ( vw->viking_vvp );
4300 gtk_color_selection_set_previous_color ( GTK_COLOR_SELECTION(gtk_color_selection_dialog_get_color_selection(GTK_COLOR_SELECTION_DIALOG(colorsd))), color );
4301 gtk_color_selection_set_current_color ( GTK_COLOR_SELECTION(gtk_color_selection_dialog_get_color_selection(GTK_COLOR_SELECTION_DIALOG(colorsd))), color );
4302 if ( gtk_dialog_run ( GTK_DIALOG(colorsd) ) == GTK_RESPONSE_OK )
4304 gtk_color_selection_get_current_color ( GTK_COLOR_SELECTION(gtk_color_selection_dialog_get_color_selection(GTK_COLOR_SELECTION_DIALOG(colorsd))), color );
4305 vik_viewport_set_background_gdkcolor ( vw->viking_vvp, color );
4309 gtk_widget_destroy ( colorsd );
4312 static void set_highlight_color ( GtkAction *a, VikWindow *vw )
4314 GtkWidget *colorsd = gtk_color_selection_dialog_new ( _("Choose a track highlight color") );
4315 GdkColor *color = vik_viewport_get_highlight_gdkcolor ( vw->viking_vvp );
4316 gtk_color_selection_set_previous_color ( GTK_COLOR_SELECTION(gtk_color_selection_dialog_get_color_selection(GTK_COLOR_SELECTION_DIALOG(colorsd))), color );
4317 gtk_color_selection_set_current_color ( GTK_COLOR_SELECTION(gtk_color_selection_dialog_get_color_selection(GTK_COLOR_SELECTION_DIALOG(colorsd))), color );
4318 if ( gtk_dialog_run ( GTK_DIALOG(colorsd) ) == GTK_RESPONSE_OK )
4320 gtk_color_selection_get_current_color ( GTK_COLOR_SELECTION(gtk_color_selection_dialog_get_color_selection(GTK_COLOR_SELECTION_DIALOG(colorsd))), color );
4321 vik_viewport_set_highlight_gdkcolor ( vw->viking_vvp, color );
4325 gtk_widget_destroy ( colorsd );
4329 /***********************************************************************************************
4331 ***********************************************************************************************/
4333 static GtkActionEntry entries[] = {
4334 { "File", NULL, N_("_File"), 0, 0, 0 },
4335 { "Edit", NULL, N_("_Edit"), 0, 0, 0 },
4336 { "View", NULL, N_("_View"), 0, 0, 0 },
4337 { "SetShow", NULL, N_("_Show"), 0, 0, 0 },
4338 { "SetZoom", NULL, N_("_Zoom"), 0, 0, 0 },
4339 { "SetPan", NULL, N_("_Pan"), 0, 0, 0 },
4340 { "Layers", NULL, N_("_Layers"), 0, 0, 0 },
4341 { "Tools", NULL, N_("_Tools"), 0, 0, 0 },
4342 { "Exttools", NULL, N_("_Webtools"), 0, 0, 0 },
4343 { "Help", NULL, N_("_Help"), 0, 0, 0 },
4345 { "New", GTK_STOCK_NEW, N_("_New"), "<control>N", N_("New file"), (GCallback)newwindow_cb },
4346 { "Open", GTK_STOCK_OPEN, N_("_Open..."), "<control>O", N_("Open a file"), (GCallback)load_file },
4347 { "OpenRecentFile", NULL, N_("Open _Recent File"), NULL, NULL, (GCallback)NULL },
4348 { "Append", GTK_STOCK_ADD, N_("Append _File..."), NULL, N_("Append data from a different file"), (GCallback)load_file },
4349 { "Export", GTK_STOCK_CONVERT, N_("_Export All"), NULL, N_("Export All TrackWaypoint Layers"), (GCallback)NULL },
4350 { "ExportGPX", NULL, N_("_GPX..."), NULL, N_("Export as GPX"), (GCallback)export_to_gpx },
4351 { "Acquire", GTK_STOCK_GO_DOWN, N_("A_cquire"), NULL, NULL, (GCallback)NULL },
4352 { "AcquireGPS", NULL, N_("From _GPS..."), NULL, N_("Transfer data from a GPS device"), (GCallback)acquire_from_gps },
4353 { "AcquireGPSBabel", NULL, N_("Import File With GPS_Babel..."), NULL, N_("Import file via GPSBabel converter"), (GCallback)acquire_from_file },
4354 { "AcquireRouting", NULL, N_("_Directions..."), NULL, N_("Get driving directions"), (GCallback)acquire_from_routing },
4355 #ifdef VIK_CONFIG_OPENSTREETMAP
4356 { "AcquireOSM", NULL, N_("_OSM Traces..."), NULL, N_("Get traces from OpenStreetMap"), (GCallback)acquire_from_osm },
4357 { "AcquireMyOSM", NULL, N_("_My OSM Traces..."), NULL, N_("Get Your Own Traces from OpenStreetMap"), (GCallback)acquire_from_my_osm },
4359 #ifdef VIK_CONFIG_GEOCACHES
4360 { "AcquireGC", NULL, N_("Geo_caches..."), NULL, N_("Get Geocaches from geocaching.com"), (GCallback)acquire_from_gc },
4362 #ifdef VIK_CONFIG_GEOTAG
4363 { "AcquireGeotag", NULL, N_("From Geotagged _Images..."), NULL, N_("Create waypoints from geotagged images"), (GCallback)acquire_from_geotag },
4365 { "AcquireURL", NULL, N_("From _URL..."), NULL, N_("Get a file from a URL"), (GCallback)acquire_from_url },
4366 #ifdef VIK_CONFIG_GEONAMES
4367 { "AcquireWikipedia", NULL, N_("From _Wikipedia Waypoints"), NULL, N_("Create waypoints from Wikipedia items in the current view"), (GCallback)acquire_from_wikipedia },
4369 { "Save", GTK_STOCK_SAVE, N_("_Save"), "<control>S", N_("Save the file"), (GCallback)save_file },
4370 { "SaveAs", GTK_STOCK_SAVE_AS, N_("Save _As..."), NULL, N_("Save the file under different name"), (GCallback)save_file_as },
4371 { "FileProperties", NULL, N_("Properties..."), NULL, N_("File Properties"), (GCallback)file_properties_cb },
4372 { "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 },
4373 { "GenImgDir", GTK_STOCK_DND_MULTIPLE, N_("Generate _Directory of Images..."), NULL, N_("Generate _Directory of Images"), (GCallback)draw_to_image_dir_cb },
4374 { "Print", GTK_STOCK_PRINT, N_("_Print..."), NULL, N_("Print maps"), (GCallback)print_cb },
4375 { "Exit", GTK_STOCK_QUIT, N_("E_xit"), "<control>W", N_("Exit the program"), (GCallback)window_close },
4376 { "SaveExit", GTK_STOCK_QUIT, N_("Save and Exit"), NULL, N_("Save and Exit the program"), (GCallback)save_file_and_exit },
4378 { "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 },
4379 { "GoForward", GTK_STOCK_GO_FORWARD, N_("Go to the _Next Location"), NULL, N_("Go to the next location"), (GCallback)draw_goto_back_and_forth },
4380 { "GotoDefaultLocation", GTK_STOCK_HOME, N_("Go to the _Default Location"), NULL, N_("Go to the default location"), (GCallback)goto_default_location },
4381 { "GotoSearch", GTK_STOCK_JUMP_TO, N_("Go to _Location..."), NULL, N_("Go to address/place using text search"), (GCallback)goto_address },
4382 { "GotoLL", GTK_STOCK_JUMP_TO, N_("_Go to Lat/Lon..."), NULL, N_("Go to arbitrary lat/lon coordinate"), (GCallback)draw_goto_cb },
4383 { "GotoUTM", GTK_STOCK_JUMP_TO, N_("Go to UTM..."), NULL, N_("Go to arbitrary UTM coordinate"), (GCallback)draw_goto_cb },
4384 { "Refresh", GTK_STOCK_REFRESH, N_("_Refresh"), "F5", N_("Refresh any maps displayed"), (GCallback)draw_refresh_cb },
4385 { "SetHLColor",GTK_STOCK_SELECT_COLOR, N_("Set _Highlight Color..."), NULL, N_("Set Highlight Color"), (GCallback)set_highlight_color },
4386 { "SetBGColor",GTK_STOCK_SELECT_COLOR, N_("Set Bac_kground Color..."), NULL, N_("Set Background Color"), (GCallback)set_bg_color },
4387 { "ZoomIn", GTK_STOCK_ZOOM_IN, N_("Zoom _In"), "<control>plus", N_("Zoom In"), (GCallback)draw_zoom_cb },
4388 { "ZoomOut", GTK_STOCK_ZOOM_OUT, N_("Zoom _Out"), "<control>minus", N_("Zoom Out"), (GCallback)draw_zoom_cb },
4389 { "ZoomTo", GTK_STOCK_ZOOM_FIT, N_("Zoom _To..."), "<control>Z", N_("Zoom To"), (GCallback)zoom_to_cb },
4390 { "PanNorth", NULL, N_("Pan _North"), "<control>Up", NULL, (GCallback)draw_pan_cb },
4391 { "PanEast", NULL, N_("Pan _East"), "<control>Right", NULL, (GCallback)draw_pan_cb },
4392 { "PanSouth", NULL, N_("Pan _South"), "<control>Down", NULL, (GCallback)draw_pan_cb },
4393 { "PanWest", NULL, N_("Pan _West"), "<control>Left", NULL, (GCallback)draw_pan_cb },
4394 { "BGJobs", GTK_STOCK_EXECUTE, N_("Background _Jobs"), NULL, N_("Background Jobs"), (GCallback)a_background_show_window },
4396 { "Cut", GTK_STOCK_CUT, N_("Cu_t"), NULL, N_("Cut selected layer"), (GCallback)menu_cut_layer_cb },
4397 { "Copy", GTK_STOCK_COPY, N_("_Copy"), NULL, N_("Copy selected layer"), (GCallback)menu_copy_layer_cb },
4398 { "Paste", GTK_STOCK_PASTE, N_("_Paste"), NULL, N_("Paste layer into selected container layer or otherwise above selected layer"), (GCallback)menu_paste_layer_cb },
4399 { "Delete", GTK_STOCK_DELETE, N_("_Delete"), NULL, N_("Remove selected layer"), (GCallback)menu_delete_layer_cb },
4400 { "DeleteAll", NULL, N_("Delete All"), NULL, NULL, (GCallback)clear_cb },
4401 { "CopyCentre",NULL, N_("Copy Centre _Location"), "<control>h", NULL, (GCallback)menu_copy_centre_cb },
4402 { "MapCacheFlush",NULL, N_("_Flush Map Cache"), NULL, NULL, (GCallback)mapcache_flush_cb },
4403 { "SetDefaultLocation", GTK_STOCK_GO_FORWARD, N_("_Set the Default Location"), NULL, N_("Set the Default Location to the current position"),(GCallback)default_location_cb },
4404 { "Preferences",GTK_STOCK_PREFERENCES, N_("_Preferences"), NULL, N_("Program Preferences"), (GCallback)preferences_cb },
4405 { "LayerDefaults",GTK_STOCK_PROPERTIES, N_("_Layer Defaults"), NULL, NULL, NULL },
4406 { "Properties",GTK_STOCK_PROPERTIES, N_("_Properties"), NULL, N_("Layer Properties"), (GCallback)menu_properties_cb },
4408 { "HelpEntry", GTK_STOCK_HELP, N_("_Help"), "F1", N_("Help"), (GCallback)help_help_cb },
4409 { "About", GTK_STOCK_ABOUT, N_("_About"), NULL, N_("About"), (GCallback)help_about_cb },
4412 static GtkActionEntry debug_entries[] = {
4413 { "MapCacheInfo", NULL, "_Map Cache Info", NULL, NULL, (GCallback)help_cache_info_cb },
4414 { "BackForwardInfo", NULL, "_Back/Forward Info", NULL, NULL, (GCallback)back_forward_info_cb },
4417 static GtkActionEntry entries_gpsbabel[] = {
4418 { "ExportKML", NULL, N_("_KML..."), NULL, N_("Export as KML"), (GCallback)export_to_kml },
4421 static GtkActionEntry entries_geojson[] = {
4422 { "AcquireGeoJSON", NULL, N_("Import Geo_JSON File..."), NULL, N_("Import GeoJSON file"), (GCallback)acquire_from_geojson },
4426 static GtkRadioActionEntry mode_entries[] = {
4427 { "ModeUTM", NULL, N_("_UTM Mode"), "<control>u", NULL, VIK_VIEWPORT_DRAWMODE_UTM },
4428 { "ModeExpedia", NULL, N_("_Expedia Mode"), "<control>e", NULL, VIK_VIEWPORT_DRAWMODE_EXPEDIA },
4429 { "ModeMercator", NULL, N_("_Mercator Mode"), "<control>m", NULL, VIK_VIEWPORT_DRAWMODE_MERCATOR },
4430 { "ModeLatLon", NULL, N_("Lat_/Lon Mode"), "<control>l", NULL, VIK_VIEWPORT_DRAWMODE_LATLON },
4433 static GtkToggleActionEntry toggle_entries[] = {
4434 { "ShowScale", NULL, N_("Show _Scale"), "<shift>F5", N_("Show Scale"), (GCallback)toggle_draw_scale, TRUE },
4435 { "ShowCenterMark", NULL, N_("Show _Center Mark"), "F6", N_("Show Center Mark"), (GCallback)toggle_draw_centermark, TRUE },
4436 { "ShowHighlight", GTK_STOCK_UNDERLINE, N_("Show _Highlight"), "F7", N_("Show Highlight"), (GCallback)toggle_draw_highlight, TRUE },
4437 { "FullScreen", GTK_STOCK_FULLSCREEN, N_("_Full Screen"), "F11", N_("Activate full screen mode"), (GCallback)full_screen_cb, FALSE },
4438 { "ViewSidePanel", GTK_STOCK_INDEX, N_("Show Side _Panel"), "F9", N_("Show Side Panel"), (GCallback)view_side_panel_cb, TRUE },
4439 { "ViewStatusBar", NULL, N_("Show Status_bar"), "F12", N_("Show Statusbar"), (GCallback)view_statusbar_cb, TRUE },
4440 { "ViewToolbar", NULL, N_("Show _Toolbar"), "F3", N_("Show Toolbar"), (GCallback)view_toolbar_cb, TRUE },
4441 { "ViewMainMenu", NULL, N_("Show _Menu"), "F4", N_("Show Menu"), (GCallback)view_main_menu_cb, TRUE },
4444 // This must match the toggle entries order above
4445 static gpointer toggle_entries_toolbar_cb[] = {
4446 (GCallback)tb_set_draw_scale,
4447 (GCallback)tb_set_draw_centermark,
4448 (GCallback)tb_set_draw_highlight,
4449 (GCallback)tb_full_screen_cb,
4450 (GCallback)tb_view_side_panel_cb,
4451 (GCallback)tb_view_statusbar_cb,
4452 (GCallback)tb_view_toolbar_cb,
4453 (GCallback)tb_view_main_menu_cb,
4456 #include "menu.xml.h"
4457 static void window_create_ui( VikWindow *window )
4460 GtkActionGroup *action_group;
4461 GtkAccelGroup *accel_group;
4464 GtkIconFactory *icon_factory;
4465 GtkIconSet *icon_set;
4466 GtkRadioActionEntry *tools = NULL, *radio;
4469 uim = gtk_ui_manager_new ();
4472 toolbox_add_tool(window->vt, &ruler_tool, TOOL_LAYER_TYPE_NONE);
4473 toolbox_add_tool(window->vt, &zoom_tool, TOOL_LAYER_TYPE_NONE);
4474 toolbox_add_tool(window->vt, &pan_tool, TOOL_LAYER_TYPE_NONE);
4475 toolbox_add_tool(window->vt, &select_tool, TOOL_LAYER_TYPE_NONE);
4477 toolbar_action_tool_entry_register ( window->viking_vtb, &pan_tool.radioActionEntry );
4478 toolbar_action_tool_entry_register ( window->viking_vtb, &zoom_tool.radioActionEntry );
4479 toolbar_action_tool_entry_register ( window->viking_vtb, &ruler_tool.radioActionEntry );
4480 toolbar_action_tool_entry_register ( window->viking_vtb, &select_tool.radioActionEntry );
4483 if (!(mid = gtk_ui_manager_add_ui_from_string (uim, menu_xml, -1, &error))) {
4484 g_error_free (error);
4488 action_group = gtk_action_group_new ("MenuActions");
4489 gtk_action_group_set_translation_domain(action_group, PACKAGE_NAME);
4490 gtk_action_group_add_actions (action_group, entries, G_N_ELEMENTS (entries), window);
4491 gtk_action_group_add_toggle_actions (action_group, toggle_entries, G_N_ELEMENTS (toggle_entries), window);
4492 gtk_action_group_add_radio_actions (action_group, mode_entries, G_N_ELEMENTS (mode_entries), 4, (GCallback)window_change_coord_mode_cb, window);
4494 if ( gtk_ui_manager_add_ui_from_string ( uim,
4495 "<ui><menubar name='MainMenu'><menu action='Help'>"
4496 "<menuitem action='MapCacheInfo'/>"
4497 "<menuitem action='BackForwardInfo'/>"
4498 "</menu></menubar></ui>",
4500 gtk_action_group_add_actions (action_group, debug_entries, G_N_ELEMENTS (debug_entries), window);
4504 for ( i=0; i < G_N_ELEMENTS (entries); i++ ) {
4505 if ( entries[i].callback )
4506 toolbar_action_entry_register ( window->viking_vtb, &entries[i] );
4509 if ( G_N_ELEMENTS (toggle_entries) != G_N_ELEMENTS (toggle_entries_toolbar_cb) ) {
4510 g_print ( "Broken entries definitions\n" );
4513 for ( i=0; i < G_N_ELEMENTS (toggle_entries); i++ ) {
4514 if ( toggle_entries_toolbar_cb[i] )
4515 toolbar_action_toggle_entry_register ( window->viking_vtb, &toggle_entries[i], toggle_entries_toolbar_cb[i] );
4518 for ( i=0; i < G_N_ELEMENTS (mode_entries); i++ ) {
4519 toolbar_action_mode_entry_register ( window->viking_vtb, &mode_entries[i] );
4522 // Use this to see if GPSBabel is available:
4523 if ( a_babel_available () ) {
4524 // If going to add more entries then might be worth creating a menu_gpsbabel.xml.h file
4525 if ( gtk_ui_manager_add_ui_from_string ( uim,
4526 "<ui><menubar name='MainMenu'><menu action='File'><menu action='Export'><menuitem action='ExportKML'/></menu></menu></menubar></ui>",
4528 gtk_action_group_add_actions ( action_group, entries_gpsbabel, G_N_ELEMENTS (entries_gpsbabel), window );
4531 // GeoJSON import capability
4532 if ( g_find_program_in_path ( a_geojson_program_import() ) ) {
4533 if ( gtk_ui_manager_add_ui_from_string ( uim,
4534 "<ui><menubar name='MainMenu'><menu action='File'><menu action='Acquire'><menuitem action='AcquireGeoJSON'/></menu></menu></menubar></ui>",
4536 gtk_action_group_add_actions ( action_group, entries_geojson, G_N_ELEMENTS (entries_geojson), window );
4539 icon_factory = gtk_icon_factory_new ();
4540 gtk_icon_factory_add_default (icon_factory);
4542 register_vik_icons(icon_factory);
4544 // Copy the tool RadioActionEntries out of the main Window structure into an extending array 'tools'
4545 // so that it can be applied to the UI in one action group add function call below
4547 for (i=0; i<window->vt->n_tools; i++) {
4548 tools = g_renew(GtkRadioActionEntry, tools, ntools+1);
4549 radio = &tools[ntools];
4551 *radio = window->vt->tools[i].ti.radioActionEntry;
4552 radio->value = ntools;
4555 for (i=0; i<VIK_LAYER_NUM_TYPES; i++) {
4556 GtkActionEntry action;
4557 gtk_ui_manager_add_ui(uim, mid, "/ui/MainMenu/Layers/",
4558 vik_layer_get_interface(i)->name,
4559 vik_layer_get_interface(i)->name,
4560 GTK_UI_MANAGER_MENUITEM, FALSE);
4562 icon_set = gtk_icon_set_new_from_pixbuf (gdk_pixbuf_from_pixdata (vik_layer_get_interface(i)->icon, FALSE, NULL ));
4563 gtk_icon_factory_add (icon_factory, vik_layer_get_interface(i)->name, icon_set);
4564 gtk_icon_set_unref (icon_set);
4566 action.name = vik_layer_get_interface(i)->name;
4567 action.stock_id = vik_layer_get_interface(i)->name;
4568 action.label = g_strdup_printf( _("New _%s Layer"), vik_layer_get_interface(i)->name);
4569 action.accelerator = vik_layer_get_interface(i)->accelerator;
4570 action.tooltip = NULL;
4571 action.callback = (GCallback)menu_addlayer_cb;
4572 gtk_action_group_add_actions(action_group, &action, 1, window);
4574 g_free ( (gchar*)action.label );
4576 if ( vik_layer_get_interface(i)->tools_count ) {
4577 gtk_ui_manager_add_ui(uim, mid, "/ui/MainMenu/Tools/", vik_layer_get_interface(i)->name, NULL, GTK_UI_MANAGER_SEPARATOR, FALSE);
4580 // Further tool copying for to apply to the UI, also apply menu UI setup
4581 for ( j = 0; j < vik_layer_get_interface(i)->tools_count; j++ ) {
4582 tools = g_renew(GtkRadioActionEntry, tools, ntools+1);
4583 radio = &tools[ntools];
4586 gtk_ui_manager_add_ui(uim, mid, "/ui/MainMenu/Tools",
4587 vik_layer_get_interface(i)->tools[j].radioActionEntry.label,
4588 vik_layer_get_interface(i)->tools[j].radioActionEntry.name,
4589 GTK_UI_MANAGER_MENUITEM, FALSE);
4591 toolbox_add_tool(window->vt, &(vik_layer_get_interface(i)->tools[j]), i);
4592 toolbar_action_tool_entry_register ( window->viking_vtb, &(vik_layer_get_interface(i)->tools[j].radioActionEntry) );
4594 *radio = vik_layer_get_interface(i)->tools[j].radioActionEntry;
4595 // Overwrite with actual number to use
4596 radio->value = ntools;
4599 GtkActionEntry action_dl;
4600 gchar *layername = g_strdup_printf ( "Layer%s", vik_layer_get_interface(i)->fixed_layer_name );
4601 gtk_ui_manager_add_ui(uim, mid, "/ui/MainMenu/Edit/LayerDefaults",
4602 vik_layer_get_interface(i)->name,
4604 GTK_UI_MANAGER_MENUITEM, FALSE);
4607 // For default layers use action names of the form 'Layer<LayerName>'
4608 // This is to avoid clashing with just the layer name used above for the tool actions
4609 action_dl.name = g_strconcat("Layer", vik_layer_get_interface(i)->fixed_layer_name, NULL);
4610 action_dl.stock_id = NULL;
4611 action_dl.label = g_strconcat("_", vik_layer_get_interface(i)->name, "...", NULL); // Prepend marker for keyboard accelerator
4612 action_dl.accelerator = NULL;
4613 action_dl.tooltip = NULL;
4614 action_dl.callback = (GCallback)layer_defaults_cb;
4615 gtk_action_group_add_actions(action_group, &action_dl, 1, window);
4616 g_free ( (gchar*)action_dl.name );
4617 g_free ( (gchar*)action_dl.label );
4619 g_object_unref (icon_factory);
4621 gtk_action_group_add_radio_actions(action_group, tools, ntools, 0, (GCallback)menu_cb, window);
4624 gtk_ui_manager_insert_action_group (uim, action_group, 0);
4626 for (i=0; i<VIK_LAYER_NUM_TYPES; i++) {
4627 for ( j = 0; j < vik_layer_get_interface(i)->tools_count; j++ ) {
4628 GtkAction *action = gtk_action_group_get_action(action_group,
4629 vik_layer_get_interface(i)->tools[j].radioActionEntry.name);
4630 g_object_set(action, "sensitive", FALSE, NULL);
4634 // This is done last so we don't need to track the value of mid anymore
4635 vik_ext_tools_add_action_items ( window, window->uim, action_group, mid );
4637 window->action_group = action_group;
4639 accel_group = gtk_ui_manager_get_accel_group (uim);
4640 gtk_window_add_accel_group (GTK_WINDOW (window), accel_group);
4641 gtk_ui_manager_ensure_update (uim);
4643 setup_recent_files(window);
4647 // TODO - add method to add tool icons defined from outside this file
4648 // and remove the reverse dependency on icon definition from this file
4650 const GdkPixdata *data;
4653 { &mover_22_pixbuf, "vik-icon-pan" },
4654 { &zoom_18_pixbuf, "vik-icon-zoom" },
4655 { &ruler_18_pixbuf, "vik-icon-ruler" },
4656 { &select_18_pixbuf, "vik-icon-select" },
4657 { &vik_new_route_18_pixbuf, "vik-icon-Create Route" },
4658 { &route_finder_18_pixbuf, "vik-icon-Route Finder" },
4659 { &demdl_18_pixbuf, "vik-icon-DEM Download" },
4660 { &showpic_18_pixbuf, "vik-icon-Show Picture" },
4661 { &addtr_18_pixbuf, "vik-icon-Create Track" },
4662 { &edtr_18_pixbuf, "vik-icon-Edit Trackpoint" },
4663 { &addwp_18_pixbuf, "vik-icon-Create Waypoint" },
4664 { &edwp_18_pixbuf, "vik-icon-Edit Waypoint" },
4665 { &geozoom_18_pixbuf, "vik-icon-Georef Zoom Tool" },
4666 { &geomove_18_pixbuf, "vik-icon-Georef Move Map" },
4667 { &mapdl_18_pixbuf, "vik-icon-Maps Download" },
4670 static gint n_stock_icons = G_N_ELEMENTS (stock_icons);
4673 register_vik_icons (GtkIconFactory *icon_factory)
4675 GtkIconSet *icon_set;
4678 for (i = 0; i < n_stock_icons; i++) {
4679 icon_set = gtk_icon_set_new_from_pixbuf (gdk_pixbuf_from_pixdata (
4680 stock_icons[i].data, FALSE, NULL ));
4681 gtk_icon_factory_add (icon_factory, stock_icons[i].stock_id, icon_set);
4682 gtk_icon_set_unref (icon_set);
4686 gpointer vik_window_get_selected_trw_layer ( VikWindow *vw )
4688 return vw->selected_vtl;
4691 void vik_window_set_selected_trw_layer ( VikWindow *vw, gpointer vtl )
4693 vw->selected_vtl = vtl;
4694 vw->containing_vtl = vtl;
4696 vw->selected_track = NULL;
4697 vw->selected_tracks = NULL;
4698 vw->selected_waypoint = NULL;
4699 vw->selected_waypoints = NULL;
4700 // Set highlight thickness
4701 vik_viewport_set_highlight_thickness ( vw->viking_vvp, vik_trw_layer_get_property_tracks_line_thickness (vw->containing_vtl) );
4704 GHashTable *vik_window_get_selected_tracks ( VikWindow *vw )
4706 return vw->selected_tracks;
4709 void vik_window_set_selected_tracks ( VikWindow *vw, GHashTable *ght, gpointer vtl )
4711 vw->selected_tracks = ght;
4712 vw->containing_vtl = vtl;
4714 vw->selected_vtl = NULL;
4715 vw->selected_track = NULL;
4716 vw->selected_waypoint = NULL;
4717 vw->selected_waypoints = NULL;
4718 // Set highlight thickness
4719 vik_viewport_set_highlight_thickness ( vw->viking_vvp, vik_trw_layer_get_property_tracks_line_thickness (vw->containing_vtl) );
4722 gpointer vik_window_get_selected_track ( VikWindow *vw )
4724 return vw->selected_track;
4727 void vik_window_set_selected_track ( VikWindow *vw, gpointer *vt, gpointer vtl )
4729 vw->selected_track = vt;
4730 vw->containing_vtl = vtl;
4732 vw->selected_vtl = NULL;
4733 vw->selected_tracks = NULL;
4734 vw->selected_waypoint = NULL;
4735 vw->selected_waypoints = NULL;
4736 // Set highlight thickness
4737 vik_viewport_set_highlight_thickness ( vw->viking_vvp, vik_trw_layer_get_property_tracks_line_thickness (vw->containing_vtl) );
4740 GHashTable *vik_window_get_selected_waypoints ( VikWindow *vw )
4742 return vw->selected_waypoints;
4745 void vik_window_set_selected_waypoints ( VikWindow *vw, GHashTable *ght, gpointer vtl )
4747 vw->selected_waypoints = ght;
4748 vw->containing_vtl = vtl;
4750 vw->selected_vtl = NULL;
4751 vw->selected_track = NULL;
4752 vw->selected_tracks = NULL;
4753 vw->selected_waypoint = NULL;
4756 gpointer vik_window_get_selected_waypoint ( VikWindow *vw )
4758 return vw->selected_waypoint;
4761 void vik_window_set_selected_waypoint ( VikWindow *vw, gpointer *vwp, gpointer vtl )
4763 vw->selected_waypoint = vwp;
4764 vw->containing_vtl = vtl;
4766 vw->selected_vtl = NULL;
4767 vw->selected_track = NULL;
4768 vw->selected_tracks = NULL;
4769 vw->selected_waypoints = NULL;
4772 gboolean vik_window_clear_highlight ( VikWindow *vw )
4774 gboolean need_redraw = FALSE;
4775 if ( vw->selected_vtl != NULL ) {
4776 vw->selected_vtl = NULL;
4779 if ( vw->selected_track != NULL ) {
4780 vw->selected_track = NULL;
4783 if ( vw->selected_tracks != NULL ) {
4784 vw->selected_tracks = NULL;
4787 if ( vw->selected_waypoint != NULL ) {
4788 vw->selected_waypoint = NULL;
4791 if ( vw->selected_waypoints != NULL ) {
4792 vw->selected_waypoints = NULL;
4798 GThread *vik_window_get_thread ( VikWindow *vw )