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-2013, 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"
38 #include "viklayer_defaults.h"
39 #include "icons/icons.h"
40 #include "vikexttools.h"
41 #include "vikexttool_datasources.h"
42 #include "garminsymbols.h"
43 #include "vikmapslayer.h"
44 #include "geonamessearch.h"
57 #include <glib/gstdio.h>
58 #include <glib/gprintf.h>
59 #include <glib/gi18n.h>
61 #include <gdk/gdkkeysyms.h>
63 // This seems rather arbitary, quite large and pointless
64 // I mean, if you have a thousand windows open;
65 // why not be allowed to open a thousand more...
66 #define MAX_WINDOWS 1024
67 static guint window_count = 0;
68 static GSList *window_list = NULL;
70 #define VIKING_WINDOW_WIDTH 1000
71 #define VIKING_WINDOW_HEIGHT 800
72 #define DRAW_IMAGE_DEFAULT_WIDTH 1280
73 #define DRAW_IMAGE_DEFAULT_HEIGHT 1024
74 #define DRAW_IMAGE_DEFAULT_SAVE_AS_PNG TRUE
76 static void window_finalize ( GObject *gob );
77 static GObjectClass *parent_class;
79 static void window_set_filename ( VikWindow *vw, const gchar *filename );
80 static const gchar *window_get_filename ( VikWindow *vw );
82 static VikWindow *window_new ();
84 static void draw_update ( VikWindow *vw );
86 static void newwindow_cb ( GtkAction *a, VikWindow *vw );
89 static void open_window ( VikWindow *vw, GSList *files );
90 static void destroy_window ( GtkWidget *widget,
95 static gboolean delete_event( VikWindow *vw );
97 static gboolean key_press_event( VikWindow *vw, GdkEventKey *event, gpointer data );
99 static void center_changed_cb ( VikWindow *vw );
100 static void window_configure_event ( VikWindow *vw );
101 static void draw_sync ( VikWindow *vw );
102 static void draw_redraw ( VikWindow *vw );
103 static void draw_scroll ( VikWindow *vw, GdkEventScroll *event );
104 static void draw_click ( VikWindow *vw, GdkEventButton *event );
105 static void draw_release ( VikWindow *vw, GdkEventButton *event );
106 static void draw_mouse_motion ( VikWindow *vw, GdkEventMotion *event );
107 static void draw_zoom_cb ( GtkAction *a, VikWindow *vw );
108 static void draw_goto_cb ( GtkAction *a, VikWindow *vw );
109 static void draw_refresh_cb ( GtkAction *a, VikWindow *vw );
111 static void draw_status ( VikWindow *vw );
113 /* End Drawing Functions */
115 static void menu_addlayer_cb ( GtkAction *a, VikWindow *vw );
116 static void menu_properties_cb ( GtkAction *a, VikWindow *vw );
117 static void menu_delete_layer_cb ( GtkAction *a, VikWindow *vw );
119 /* tool management */
125 #define TOOL_LAYER_TYPE_NONE -1
130 toolbox_tool_t *tools;
134 static void menu_tool_cb ( GtkAction *old, GtkAction *a, VikWindow *vw );
135 static toolbox_tools_t* toolbox_create(VikWindow *vw);
136 static void toolbox_add_tool(toolbox_tools_t *vt, VikToolInterface *vti, gint layer_type );
137 static int toolbox_get_tool(toolbox_tools_t *vt, const gchar *tool_name);
138 static void toolbox_activate(toolbox_tools_t *vt, const gchar *tool_name);
139 static const GdkCursor *toolbox_get_cursor(toolbox_tools_t *vt, const gchar *tool_name);
140 static void toolbox_click (toolbox_tools_t *vt, GdkEventButton *event);
141 static void toolbox_move (toolbox_tools_t *vt, GdkEventMotion *event);
142 static void toolbox_release (toolbox_tools_t *vt, GdkEventButton *event);
146 static void window_create_ui( VikWindow *window );
147 static void register_vik_icons (GtkIconFactory *icon_factory);
150 static void load_file ( GtkAction *a, VikWindow *vw );
151 static gboolean save_file_as ( GtkAction *a, VikWindow *vw );
152 static gboolean save_file ( GtkAction *a, VikWindow *vw );
153 static gboolean save_file_and_exit ( GtkAction *a, VikWindow *vw );
154 static gboolean window_save ( VikWindow *vw );
158 VikViewport *viking_vvp;
159 VikLayersPanel *viking_vlp;
160 VikStatusbar *viking_vs;
164 GdkCursor *busy_cursor;
165 GdkCursor *viewport_cursor; // only a reference
167 /* tool management state */
170 guint16 tool_layer_id;
171 guint16 tool_tool_id;
173 GtkActionGroup *action_group;
177 gint delayed_pan_x, delayed_pan_y; // Temporary storage
178 gboolean single_click_pending;
180 guint draw_image_width, draw_image_height;
181 gboolean draw_image_save_as_png;
185 VikLoadType_t loaded_type;
187 GtkWidget *open_dia, *save_dia;
188 GtkWidget *save_img_dia, *save_img_dir_dia;
190 gboolean only_updating_coord_mode_ui; /* hack for a bug in GTK */
194 /* half-drawn update */
196 VikCoord trigger_center;
198 /* Store at this level for highlighted selection drawing since it applies to the viewport and the layers panel */
199 /* Only one of these items can be selected at the same time */
200 gpointer selected_vtl; /* notionally VikTrwLayer */
201 GHashTable *selected_tracks;
202 gpointer selected_track; /* notionally VikTrack */
203 GHashTable *selected_waypoints;
204 gpointer selected_waypoint; /* notionally VikWaypoint */
205 /* only use for individual track or waypoint */
206 /* For track(s) & waypoint(s) it is the layer they are in - this helps refering to the individual item easier */
207 gpointer containing_vtl; /* notionally VikTrwLayer */
221 VW_OPENWINDOW_SIGNAL,
225 static guint window_signals[VW_LAST_SIGNAL] = { 0 };
227 // TODO get rid of this as this is unnecessary duplication...
228 static gchar *tool_names[NUMBER_OF_TOOLS] = { N_("Pan"), N_("Zoom"), N_("Ruler"), N_("Select") };
230 G_DEFINE_TYPE (VikWindow, vik_window, GTK_TYPE_WINDOW)
232 VikViewport * vik_window_viewport(VikWindow *vw)
234 return(vw->viking_vvp);
237 VikLayersPanel * vik_window_layers_panel(VikWindow *vw)
239 return(vw->viking_vlp);
243 * Returns the statusbar for the window
245 VikStatusbar * vik_window_get_statusbar ( VikWindow *vw )
247 return vw->viking_vs;
251 * Returns the 'project' filename
253 const gchar *vik_window_get_filename (VikWindow *vw)
260 vik_statusbar_type_t vs_type;
261 gchar* message; // Always make a copy of this data
262 } statusbar_idle_data;
265 * For the actual statusbar update!
267 static gboolean statusbar_idle_update ( statusbar_idle_data *sid )
269 vik_statusbar_set_message ( sid->vs, sid->vs_type, sid->message );
270 g_free ( sid->message );
276 * vik_window_statusbar_update:
277 * @vw: The main window in which the statusbar will be updated.
278 * @message: The string to be displayed. This is copied.
279 * @vs_type: The part of the statusbar to be updated.
281 * This updates any part of the statusbar with the new string.
282 * It handles calling from the main thread or any background thread
283 * ATM this mostly used from background threads - as from the main thread
284 * one may use the vik_statusbar_set_message() directly.
286 void vik_window_statusbar_update ( VikWindow *vw, const gchar* message, vik_statusbar_type_t vs_type )
288 statusbar_idle_data *sid = g_malloc ( sizeof (statusbar_idle_data) );
289 sid->vs = vw->viking_vs;
290 sid->vs_type = vs_type;
291 sid->message = g_strdup ( message );
293 if ( g_thread_self() == vik_window_get_thread ( vw ) ) {
294 g_idle_add ( (GSourceFunc) statusbar_idle_update, sid );
297 // From a background thread
298 gdk_threads_add_idle ( (GSourceFunc) statusbar_idle_update, sid );
302 // Actual signal handlers
303 static void destroy_window ( GtkWidget *widget,
306 if ( ! --window_count )
310 #define VIK_SETTINGS_WIN_SIDEPANEL "window_sidepanel"
311 #define VIK_SETTINGS_WIN_STATUSBAR "window_statusbar"
312 #define VIK_SETTINGS_WIN_TOOLBAR "window_toolbar"
313 // Menubar setting to off is never auto saved in case it's accidentally turned off
314 // It's not so obvious so to recover the menu visibility.
315 // Thus this value is for setting manually via editting the settings file directly
316 #define VIK_SETTINGS_WIN_MENUBAR "window_menubar"
318 VikWindow *vik_window_new_window ()
320 if ( window_count < MAX_WINDOWS )
322 VikWindow *vw = window_new ();
324 g_signal_connect (G_OBJECT (vw), "destroy",
325 G_CALLBACK (destroy_window), NULL);
326 g_signal_connect (G_OBJECT (vw), "newwindow",
327 G_CALLBACK (vik_window_new_window), NULL);
328 g_signal_connect (G_OBJECT (vw), "openwindow",
329 G_CALLBACK (open_window), NULL);
331 gtk_widget_show_all ( GTK_WIDGET(vw) );
333 if ( a_vik_get_restore_window_state() ) {
334 // These settings are applied after the show all as these options hide widgets
336 if ( a_settings_get_boolean ( VIK_SETTINGS_WIN_SIDEPANEL, &sidepanel ) )
338 gtk_widget_hide ( GTK_WIDGET(vw->viking_vlp) );
339 GtkWidget *check_box = gtk_ui_manager_get_widget ( vw->uim, "/ui/MainMenu/View/SetShow/ViewSidePanel" );
340 gtk_check_menu_item_set_active ( GTK_CHECK_MENU_ITEM(check_box), FALSE );
344 if ( a_settings_get_boolean ( VIK_SETTINGS_WIN_STATUSBAR, &statusbar ) )
346 gtk_widget_hide ( GTK_WIDGET(vw->viking_vs) );
347 GtkWidget *check_box = gtk_ui_manager_get_widget ( vw->uim, "/ui/MainMenu/View/SetShow/ViewStatusBar" );
348 gtk_check_menu_item_set_active ( GTK_CHECK_MENU_ITEM(check_box), FALSE );
352 if ( a_settings_get_boolean ( VIK_SETTINGS_WIN_TOOLBAR, &toolbar ) )
354 gtk_widget_hide ( GTK_WIDGET(vw->toolbar) );
355 GtkWidget *check_box = gtk_ui_manager_get_widget ( vw->uim, "/ui/MainMenu/View/SetShow/ViewToolBar" );
356 gtk_check_menu_item_set_active ( GTK_CHECK_MENU_ITEM(check_box), FALSE );
360 if ( a_settings_get_boolean ( VIK_SETTINGS_WIN_MENUBAR, &menubar ) )
362 gtk_widget_hide ( gtk_ui_manager_get_widget ( vw->uim, "/ui/MainMenu" ) );
363 GtkWidget *check_box = gtk_ui_manager_get_widget ( vw->uim, "/ui/MainMenu/View/SetShow/ViewMainMenu" );
364 gtk_check_menu_item_set_active ( GTK_CHECK_MENU_ITEM(check_box), FALSE );
375 * determine_location_thread:
376 * @vw: The window that will get updated
377 * @threaddata: Data used by our background thread mechanism
379 * Use the features in vikgoto to determine where we are
380 * Then set up the viewport:
381 * 1. To goto the location
382 * 2. Set an appropriate level zoom for the location type
383 * 3. Some statusbar message feedback
385 static int determine_location_thread ( VikWindow *vw, gpointer threaddata )
389 gint ans = a_vik_goto_where_am_i ( vw->viking_vvp, &ll, &name );
391 int result = a_background_thread_progress ( threaddata, 1.0 );
393 vik_window_statusbar_update ( vw, _("Location lookup aborted"), VIK_STATUSBAR_INFO );
394 return -1; /* Abort thread */
402 // Position found with city precision - so zoom out more
405 else if ( ans == 3 ) {
406 // Position found via country name search - so zoom wayyyy out
410 vik_viewport_set_zoom ( vw->viking_vvp, zoom );
411 vik_viewport_set_center_latlon ( vw->viking_vvp, &ll, FALSE );
413 gchar *message = g_strdup_printf ( _("Location found: %s"), name );
414 vik_window_statusbar_update ( vw, message, VIK_STATUSBAR_INFO );
418 // Signal to redraw from the background
419 vik_layers_panel_emit_update ( vw->viking_vlp );
422 vik_window_statusbar_update ( vw, _("Unable to determine location"), VIK_STATUSBAR_INFO );
428 * Steps to be taken once initial loading has completed
430 void vik_window_new_window_finish ( VikWindow *vw )
432 // Don't add a map if we've loaded a Viking file already
436 if ( a_vik_get_startup_method ( ) == VIK_STARTUP_METHOD_SPECIFIED_FILE ) {
437 vik_window_open_file ( vw, a_vik_get_startup_file(), TRUE );
442 // Maybe add a default map layer
443 if ( a_vik_get_add_default_map_layer () ) {
444 VikMapsLayer *vml = VIK_MAPS_LAYER ( vik_layer_create(VIK_LAYER_MAPS, vw->viking_vvp, FALSE) );
445 vik_layer_rename ( VIK_LAYER(vml), _("Default Map") );
446 vik_aggregate_layer_add_layer ( vik_layers_panel_get_top_layer(vw->viking_vlp), VIK_LAYER(vml), TRUE );
451 // If not loaded any file, maybe try the location lookup
452 if ( vw->loaded_type == LOAD_TYPE_READ_FAILURE ) {
453 if ( a_vik_get_startup_method ( ) == VIK_STARTUP_METHOD_AUTO_LOCATION ) {
455 vik_statusbar_set_message ( vw->viking_vs, VIK_STATUSBAR_INFO, _("Trying to determine location...") );
457 a_background_thread ( GTK_WINDOW(vw),
458 _("Determining location"),
459 (vik_thr_func) determine_location_thread,
468 static void open_window ( VikWindow *vw, GSList *files )
470 gboolean change_fn = (g_slist_length(files) == 1); /* only change fn if one file */
471 GSList *cur_file = files;
473 // Only open a new window if a viking file
474 gchar *file_name = cur_file->data;
475 if (vw != NULL && vw->filename && check_file_magic_vik ( file_name ) ) {
476 VikWindow *newvw = vik_window_new_window ();
478 vik_window_open_file ( newvw, file_name, TRUE );
481 vik_window_open_file ( vw, file_name, change_fn );
484 cur_file = g_slist_next (cur_file);
486 g_slist_free (files);
490 void vik_window_selected_layer(VikWindow *vw, VikLayer *vl)
492 int i, j, tool_count;
493 VikLayerInterface *layer_interface;
495 if (!vw->action_group) return;
497 for (i=0; i<VIK_LAYER_NUM_TYPES; i++) {
499 layer_interface = vik_layer_get_interface(i);
500 tool_count = layer_interface->tools_count;
502 for (j = 0; j < tool_count; j++) {
503 action = gtk_action_group_get_action(vw->action_group,
504 layer_interface->tools[j].radioActionEntry.name);
505 g_object_set(action, "sensitive", i == vl->type, NULL);
510 static void window_finalize ( GObject *gob )
512 VikWindow *vw = VIK_WINDOW(gob);
513 g_return_if_fail ( vw != NULL );
515 a_background_remove_window ( vw );
517 window_list = g_slist_remove ( window_list, vw );
519 gdk_cursor_unref ( vw->busy_cursor );
521 for (tt = 0; tt < vw->vt->n_tools; tt++ )
522 if ( vw->vt->tools[tt].ti.destroy )
523 vw->vt->tools[tt].ti.destroy ( vw->vt->tools[tt].state );
524 g_free ( vw->vt->tools );
527 G_OBJECT_CLASS(parent_class)->finalize(gob);
531 static void vik_window_class_init ( VikWindowClass *klass )
534 GObjectClass *object_class;
536 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);
537 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);
539 object_class = G_OBJECT_CLASS (klass);
541 object_class->finalize = window_finalize;
543 parent_class = g_type_class_peek_parent (klass);
547 static void zoom_changed (GtkMenuShell *menushell,
550 VikWindow *vw = VIK_WINDOW (user_data);
552 GtkWidget *aw = gtk_menu_get_active ( GTK_MENU (menushell) );
553 gint active = GPOINTER_TO_INT(g_object_get_data ( G_OBJECT (aw), "position" ));
555 gdouble zoom_request = pow (2, active-2 );
557 // But has it really changed?
558 gdouble current_zoom = vik_viewport_get_zoom ( vw->viking_vvp );
559 if ( current_zoom != 0.0 && zoom_request != current_zoom ) {
560 vik_viewport_set_zoom ( vw->viking_vvp, zoom_request );
561 // Force drawing update
567 * @mpp: The initial zoom level
569 static GtkWidget *create_zoom_menu_all_levels ( gdouble mpp )
571 GtkWidget *menu = gtk_menu_new ();
572 char *itemLabels[] = { "0.25", "0.5", "1", "2", "4", "8", "16", "32", "64", "128", "256", "512", "1024", "2048", "4096", "8192", "16384", "32768" };
575 for (i = 0 ; i < G_N_ELEMENTS(itemLabels) ; i++)
577 GtkWidget *item = gtk_menu_item_new_with_label (itemLabels[i]);
578 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
579 gtk_widget_show (item);
580 g_object_set_data (G_OBJECT (item), "position", GINT_TO_POINTER(i));
583 gint active = 2 + round ( log (mpp) / log (2) );
584 // Ensure value derived from mpp is in bounds of the menu
585 if ( active >= G_N_ELEMENTS(itemLabels) )
586 active = G_N_ELEMENTS(itemLabels) - 1;
589 gtk_menu_set_active ( GTK_MENU(menu), active );
594 static GtkWidget *create_zoom_combo_all_levels ()
596 GtkWidget *combo = vik_combo_box_text_new();
597 vik_combo_box_text_append ( combo, "0.25");
598 vik_combo_box_text_append ( combo, "0.5");
599 vik_combo_box_text_append ( combo, "1");
600 vik_combo_box_text_append ( combo, "2");
601 vik_combo_box_text_append ( combo, "4");
602 vik_combo_box_text_append ( combo, "8");
603 vik_combo_box_text_append ( combo, "16");
604 vik_combo_box_text_append ( combo, "32");
605 vik_combo_box_text_append ( combo, "64");
606 vik_combo_box_text_append ( combo, "128");
607 vik_combo_box_text_append ( combo, "256");
608 vik_combo_box_text_append ( combo, "512");
609 vik_combo_box_text_append ( combo, "1024");
610 vik_combo_box_text_append ( combo, "2048");
611 vik_combo_box_text_append ( combo, "4096");
612 vik_combo_box_text_append ( combo, "8192");
613 vik_combo_box_text_append ( combo, "16384");
614 vik_combo_box_text_append ( combo, "32768");
616 gtk_widget_set_tooltip_text (combo, _("Select zoom level"));
620 static gint zoom_popup_handler (GtkWidget *widget)
624 g_return_val_if_fail (widget != NULL, FALSE);
625 g_return_val_if_fail (GTK_IS_MENU (widget), FALSE);
627 /* The "widget" is the menu that was supplied when
628 * g_signal_connect_swapped() was called.
630 menu = GTK_MENU (widget);
632 gtk_menu_popup (menu, NULL, NULL, NULL, NULL,
633 1, gtk_get_current_event_time());
641 static void drag_data_received_cb ( GtkWidget *widget,
642 GdkDragContext *context,
645 GtkSelectionData *selection_data,
650 gboolean success = FALSE;
652 if ( (selection_data != NULL) && (gtk_selection_data_get_length(selection_data) > 0) ) {
653 switch (target_type) {
655 gchar *str = (gchar*)gtk_selection_data_get_data(selection_data);
656 g_debug ("drag received string:%s \n", str);
658 // Convert string into GSList of individual entries for use with our open signal
659 gchar **entries = g_strsplit(str, "\r\n", 0);
660 GSList *filenames = NULL;
661 gint entry_runner = 0;
662 gchar *entry = entries[entry_runner];
664 if ( g_strcmp0 ( entry, "" ) ) {
665 // Drag+Drop gives URIs. And so in particular, %20 in place of spaces in filenames
666 // thus need to convert the text into a plain string
667 gchar *filename = g_filename_from_uri ( entry, NULL, NULL );
669 filenames = g_slist_append ( filenames, filename );
672 entry = entries[entry_runner];
676 g_signal_emit ( G_OBJECT(VIK_WINDOW_FROM_WIDGET(widget)), window_signals[VW_OPENWINDOW_SIGNAL], 0, filenames );
677 // NB: GSList & contents are freed by main.open_window
686 gtk_drag_finish ( context, success, FALSE, time );
689 #define VIK_SETTINGS_WIN_MAX "window_maximized"
690 #define VIK_SETTINGS_WIN_FULLSCREEN "window_fullscreen"
691 #define VIK_SETTINGS_WIN_WIDTH "window_width"
692 #define VIK_SETTINGS_WIN_HEIGHT "window_height"
693 #define VIK_SETTINGS_WIN_SAVE_IMAGE_WIDTH "window_save_image_width"
694 #define VIK_SETTINGS_WIN_SAVE_IMAGE_HEIGHT "window_save_image_height"
695 #define VIK_SETTINGS_WIN_SAVE_IMAGE_PNG "window_save_image_as_png"
697 static void vik_window_init ( VikWindow *vw )
699 GtkWidget *main_vbox;
702 vw->action_group = NULL;
704 vw->viking_vvp = vik_viewport_new();
705 vw->viking_vlp = vik_layers_panel_new();
706 vik_layers_panel_set_viewport ( vw->viking_vlp, vw->viking_vvp );
707 vw->viking_vs = vik_statusbar_new();
709 vw->vt = toolbox_create(vw);
710 window_create_ui(vw);
711 window_set_filename (vw, NULL);
712 vw->toolbar = GTK_TOOLBAR(gtk_ui_manager_get_widget (vw->uim, "/MainToolbar"));
714 vw->busy_cursor = gdk_cursor_new ( GDK_WATCH );
716 // Set the default tool
717 gtk_action_activate ( gtk_action_group_get_action ( vw->action_group, "Pan" ) );
720 vw->loaded_type = LOAD_TYPE_READ_FAILURE; //AKA none
721 vw->modified = FALSE;
722 vw->only_updating_coord_mode_ui = FALSE;
724 vw->pan_move = FALSE;
725 vw->pan_x = vw->pan_y = -1;
726 vw->single_click_pending = FALSE;
728 gint draw_image_width;
729 if ( a_settings_get_integer ( VIK_SETTINGS_WIN_SAVE_IMAGE_WIDTH, &draw_image_width ) )
730 vw->draw_image_width = draw_image_width;
732 vw->draw_image_width = DRAW_IMAGE_DEFAULT_WIDTH;
733 gint draw_image_height;
734 if ( a_settings_get_integer ( VIK_SETTINGS_WIN_SAVE_IMAGE_HEIGHT, &draw_image_height ) )
735 vw->draw_image_height = draw_image_height;
737 vw->draw_image_height = DRAW_IMAGE_DEFAULT_HEIGHT;
738 gboolean draw_image_save_as_png;
739 if ( a_settings_get_boolean ( VIK_SETTINGS_WIN_SAVE_IMAGE_PNG, &draw_image_save_as_png ) )
740 vw->draw_image_save_as_png = draw_image_save_as_png;
742 vw->draw_image_save_as_png = DRAW_IMAGE_DEFAULT_SAVE_AS_PNG;
744 main_vbox = gtk_vbox_new(FALSE, 1);
745 gtk_container_add (GTK_CONTAINER (vw), main_vbox);
747 gtk_box_pack_start (GTK_BOX(main_vbox), gtk_ui_manager_get_widget (vw->uim, "/MainMenu"), FALSE, TRUE, 0);
748 gtk_box_pack_start (GTK_BOX(main_vbox), GTK_WIDGET(vw->toolbar), FALSE, TRUE, 0);
749 gtk_toolbar_set_icon_size (vw->toolbar, GTK_ICON_SIZE_SMALL_TOOLBAR);
750 gtk_toolbar_set_style (vw->toolbar, GTK_TOOLBAR_ICONS);
752 vik_ext_tool_datasources_add_menu_items ( vw, vw->uim );
754 GtkWidget * zoom_levels = gtk_ui_manager_get_widget (vw->uim, "/MainMenu/View/SetZoom");
755 GtkWidget * zoom_levels_menu = create_zoom_menu_all_levels ( vik_viewport_get_zoom(vw->viking_vvp) );
756 gtk_menu_item_set_submenu (GTK_MENU_ITEM (zoom_levels), zoom_levels_menu);
757 g_signal_connect ( G_OBJECT(zoom_levels_menu), "selection-done", G_CALLBACK(zoom_changed), vw);
758 g_signal_connect_swapped ( G_OBJECT(vw->viking_vs), "clicked", G_CALLBACK(zoom_popup_handler), zoom_levels_menu );
760 g_signal_connect (G_OBJECT (vw), "delete_event", G_CALLBACK (delete_event), NULL);
763 g_signal_connect_swapped (G_OBJECT(vw->viking_vvp), "updated_center", G_CALLBACK(center_changed_cb), vw);
765 g_signal_connect_swapped (G_OBJECT(vw->viking_vvp), "expose_event", G_CALLBACK(draw_sync), vw);
766 g_signal_connect_swapped (G_OBJECT(vw->viking_vvp), "configure_event", G_CALLBACK(window_configure_event), vw);
767 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 );
768 g_signal_connect_swapped (G_OBJECT(vw->viking_vvp), "scroll_event", G_CALLBACK(draw_scroll), vw);
769 g_signal_connect_swapped (G_OBJECT(vw->viking_vvp), "button_press_event", G_CALLBACK(draw_click), vw);
770 g_signal_connect_swapped (G_OBJECT(vw->viking_vvp), "button_release_event", G_CALLBACK(draw_release), vw);
771 g_signal_connect_swapped (G_OBJECT(vw->viking_vvp), "motion_notify_event", G_CALLBACK(draw_mouse_motion), vw);
773 g_signal_connect_swapped (G_OBJECT(vw->viking_vlp), "update", G_CALLBACK(draw_update), vw);
774 g_signal_connect_swapped (G_OBJECT(vw->viking_vlp), "delete_layer", G_CALLBACK(vik_window_clear_highlight), vw);
776 // Allow key presses to be processed anywhere
777 g_signal_connect_swapped (G_OBJECT (vw), "key_press_event", G_CALLBACK (key_press_event), vw);
779 // Set initial button sensitivity
780 center_changed_cb ( vw );
782 hpaned = gtk_hpaned_new ();
783 gtk_paned_pack1 ( GTK_PANED(hpaned), GTK_WIDGET (vw->viking_vlp), FALSE, FALSE );
784 gtk_paned_pack2 ( GTK_PANED(hpaned), GTK_WIDGET (vw->viking_vvp), TRUE, TRUE );
786 /* This packs the button into the window (a gtk container). */
787 gtk_box_pack_start (GTK_BOX(main_vbox), hpaned, TRUE, TRUE, 0);
789 gtk_box_pack_end (GTK_BOX(main_vbox), GTK_WIDGET(vw->viking_vs), FALSE, TRUE, 0);
791 a_background_add_window ( vw );
793 window_list = g_slist_prepend ( window_list, vw);
795 gint height = VIKING_WINDOW_HEIGHT;
796 gint width = VIKING_WINDOW_WIDTH;
798 if ( a_vik_get_restore_window_state() ) {
799 if ( a_settings_get_integer ( VIK_SETTINGS_WIN_HEIGHT, &height ) ) {
800 // Enforce a basic minimum size
805 // No setting - so use default
806 height = VIKING_WINDOW_HEIGHT;
808 if ( a_settings_get_integer ( VIK_SETTINGS_WIN_WIDTH, &width ) ) {
809 // Enforce a basic minimum size
814 // No setting - so use default
815 width = VIKING_WINDOW_WIDTH;
818 if ( a_settings_get_boolean ( VIK_SETTINGS_WIN_MAX, &maxed ) )
820 gtk_window_maximize ( GTK_WINDOW(vw) );
823 if ( a_settings_get_boolean ( VIK_SETTINGS_WIN_FULLSCREEN, &full ) ) {
825 gtk_window_fullscreen ( GTK_WINDOW(vw) );
826 GtkWidget *check_box = gtk_ui_manager_get_widget ( vw->uim, "/ui/MainMenu/View/FullScreen" );
827 gtk_check_menu_item_set_active ( GTK_CHECK_MENU_ITEM(check_box), TRUE );
832 gtk_window_set_default_size ( GTK_WINDOW(vw), width, height );
836 vw->save_img_dia = NULL;
837 vw->save_img_dir_dia = NULL;
839 // Only accept Drag and Drop of files onto the viewport
840 gtk_drag_dest_set ( GTK_WIDGET(vw->viking_vvp), GTK_DEST_DEFAULT_ALL, NULL, 0, GDK_ACTION_COPY );
841 gtk_drag_dest_add_uri_targets ( GTK_WIDGET(vw->viking_vvp) );
842 g_signal_connect ( GTK_WIDGET(vw->viking_vvp), "drag-data-received", G_CALLBACK(drag_data_received_cb), NULL );
844 // Store the thread value so comparisons can be made to determine the gdk update method
845 // Hopefully we are storing the main thread value here :)
846 // [ATM any window initialization is always be performed by the main thread]
847 vw->thread = g_thread_self();
850 static VikWindow *window_new ()
852 return VIK_WINDOW ( g_object_new ( VIK_WINDOW_TYPE, NULL ) );
856 * Update the displayed map
857 * Only update the top most visible map layer
858 * ATM this assumes (as per defaults) the top most map has full alpha setting
859 * such that other other maps even though they may be active will not be seen
860 * It's more complicated to work out which maps are actually visible due to alpha settings
861 * and overkill for this simple refresh method.
863 static void simple_map_update ( VikWindow *vw, gboolean only_new )
865 // Find the most relevent single map layer to operate on
866 VikLayer *vl = vik_aggregate_layer_get_top_visible_layer_of_type (vik_layers_panel_get_top_layer(vw->viking_vlp), VIK_LAYER_MAPS);
868 vik_maps_layer_download ( VIK_MAPS_LAYER(vl), vw->viking_vvp, only_new );
872 * This is the global key press handler
873 * Global shortcuts are available at any time and hence are not restricted to when a certain tool is enabled
875 static gboolean key_press_event( VikWindow *vw, GdkEventKey *event, gpointer data )
877 // The keys handled here are not in the menuing system for a couple of reasons:
878 // . Keeps the menu size compact (alebit at expense of discoverably)
879 // . Allows differing key bindings to perform the same actions
881 // First decide if key events are related to the maps layer
882 gboolean map_download = FALSE;
883 gboolean map_download_only_new = TRUE; // Only new or reload
885 GdkModifierType modifiers = gtk_accelerator_get_default_mod_mask();
887 // Standard 'Refresh' keys: F5 or Ctrl+r
888 // Note 'F5' is actually handled via draw_refresh_cb() later on
889 // (not 'R' it's 'r' notice the case difference!!)
890 if ( event->keyval == GDK_r && (event->state & modifiers) == GDK_CONTROL_MASK ) {
892 map_download_only_new = TRUE;
894 // Full cache reload with Ctrl+F5 or Ctrl+Shift+r [This is not in the menu system]
895 // Note the use of uppercase R here since shift key has been pressed
896 else if ( (event->keyval == GDK_F5 && (event->state & modifiers) == GDK_CONTROL_MASK ) ||
897 ( event->keyval == GDK_R && (event->state & modifiers) == (GDK_CONTROL_MASK + GDK_SHIFT_MASK) ) ) {
899 map_download_only_new = FALSE;
902 if ( map_download ) {
903 simple_map_update ( vw, map_download_only_new );
906 VikLayer *vl = vik_layers_panel_get_selected ( vw->viking_vlp );
907 if (vl && vw->vt->active_tool != -1 && vw->vt->tools[vw->vt->active_tool].ti.key_press ) {
908 gint ltype = vw->vt->tools[vw->vt->active_tool].layer_type;
909 if ( vl && ltype == vl->type )
910 return vw->vt->tools[vw->vt->active_tool].ti.key_press(vl, event, vw->vt->tools[vw->vt->active_tool].state);
913 // Ensure called only on window tools (i.e. not on any of the Layer tools since the layer is NULL)
914 if ( vw->current_tool < TOOL_LAYER ) {
915 // No layer - but enable window tool keypress processing - these should be able to handle a NULL layer
916 if ( vw->vt->tools[vw->vt->active_tool].ti.key_press ) {
917 return vw->vt->tools[vw->vt->active_tool].ti.key_press ( vl, event, vw->vt->tools[vw->vt->active_tool].state );
921 /* Restore Main Menu via Escape key if the user has hidden it */
922 /* This key is more likely to be used as they may not remember the function key */
923 if ( event->keyval == GDK_Escape ) {
924 GtkWidget *check_box = gtk_ui_manager_get_widget ( vw->uim, "/ui/MainMenu/View/SetShow/ViewMainMenu" );
926 gboolean state = gtk_check_menu_item_get_active ( GTK_CHECK_MENU_ITEM(check_box) );
928 gtk_widget_show ( gtk_ui_manager_get_widget ( vw->uim, "/ui/MainMenu" ) );
929 gtk_check_menu_item_set_active ( GTK_CHECK_MENU_ITEM(check_box), TRUE );
930 return TRUE; /* handled keypress */
935 return FALSE; /* don't handle the keypress */
938 static gboolean delete_event( VikWindow *vw )
940 #ifdef VIKING_PROMPT_IF_MODIFIED
947 dia = GTK_DIALOG ( gtk_message_dialog_new ( GTK_WINDOW(vw), GTK_DIALOG_DESTROY_WITH_PARENT, GTK_MESSAGE_QUESTION, GTK_BUTTONS_NONE,
948 _("Do you want to save the changes you made to the document \"%s\"?\n"
950 "Your changes will be lost if you don't save them."),
951 window_get_filename ( vw ) ) );
952 gtk_dialog_add_buttons ( dia, _("Don't Save"), GTK_RESPONSE_NO, GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, GTK_STOCK_SAVE, GTK_RESPONSE_YES, NULL );
953 switch ( gtk_dialog_run ( dia ) )
955 case GTK_RESPONSE_NO: gtk_widget_destroy ( GTK_WIDGET(dia) ); return FALSE;
956 case GTK_RESPONSE_CANCEL: gtk_widget_destroy ( GTK_WIDGET(dia) ); return TRUE;
957 default: gtk_widget_destroy ( GTK_WIDGET(dia) ); return ! save_file(NULL, vw);
961 if ( window_count == 1 ) {
962 // On the final window close - save latest state - if it's wanted...
963 if ( a_vik_get_restore_window_state() ) {
964 gint state = gdk_window_get_state ( GTK_WIDGET(vw)->window );
965 gboolean state_max = state & GDK_WINDOW_STATE_MAXIMIZED;
966 a_settings_set_boolean ( VIK_SETTINGS_WIN_MAX, state_max );
968 gboolean state_fullscreen = state & GDK_WINDOW_STATE_FULLSCREEN;
969 a_settings_set_boolean ( VIK_SETTINGS_WIN_FULLSCREEN, state_fullscreen );
971 a_settings_set_boolean ( VIK_SETTINGS_WIN_SIDEPANEL, GTK_WIDGET_VISIBLE (GTK_WIDGET(vw->viking_vlp)) );
973 a_settings_set_boolean ( VIK_SETTINGS_WIN_STATUSBAR, GTK_WIDGET_VISIBLE (GTK_WIDGET(vw->viking_vs)) );
975 a_settings_set_boolean ( VIK_SETTINGS_WIN_TOOLBAR, GTK_WIDGET_VISIBLE (GTK_WIDGET(vw->toolbar)) );
977 // If supersized - no need to save the enlarged width+height values
978 if ( ! (state_fullscreen || state_max) ) {
980 gtk_window_get_size ( GTK_WINDOW (vw), &width, &height );
981 a_settings_set_integer ( VIK_SETTINGS_WIN_WIDTH, width );
982 a_settings_set_integer ( VIK_SETTINGS_WIN_HEIGHT, height );
986 a_settings_set_integer ( VIK_SETTINGS_WIN_SAVE_IMAGE_WIDTH, vw->draw_image_width );
987 a_settings_set_integer ( VIK_SETTINGS_WIN_SAVE_IMAGE_HEIGHT, vw->draw_image_height );
988 a_settings_set_boolean ( VIK_SETTINGS_WIN_SAVE_IMAGE_PNG, vw->draw_image_save_as_png );
995 static void newwindow_cb ( GtkAction *a, VikWindow *vw )
997 g_signal_emit ( G_OBJECT(vw), window_signals[VW_NEWWINDOW_SIGNAL], 0 );
1000 static void draw_update ( VikWindow *vw )
1006 static void draw_sync ( VikWindow *vw )
1008 vik_viewport_sync(vw->viking_vvp);
1013 * Split the status update, as sometimes only need to update the tool part
1014 * also on initialization the zoom related stuff is not ready to be used
1016 static void draw_status_tool ( VikWindow *vw )
1018 if ( vw->current_tool == TOOL_LAYER )
1019 // Use tooltip rather than the internal name as the tooltip is i8n
1020 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 );
1022 vik_statusbar_set_message ( vw->viking_vs, VIK_STATUSBAR_TOOL, _(tool_names[vw->current_tool]) );
1025 static void draw_status ( VikWindow *vw )
1027 static gchar zoom_level[22];
1028 gdouble xmpp = vik_viewport_get_xmpp (vw->viking_vvp);
1029 gdouble ympp = vik_viewport_get_ympp(vw->viking_vvp);
1030 gchar *unit = vik_viewport_get_coord_mode(vw->viking_vvp) == VIK_COORD_UTM ? _("mpp") : _("pixelfact");
1032 g_snprintf ( zoom_level, 22, "%.3f/%.3f %s", xmpp, ympp, unit );
1034 if ( (int)xmpp - xmpp < 0.0 )
1035 g_snprintf ( zoom_level, 22, "%.3f %s", xmpp, unit );
1037 /* xmpp should be a whole number so don't show useless .000 bit */
1038 g_snprintf ( zoom_level, 22, "%d %s", (int)xmpp, unit );
1040 vik_statusbar_set_message ( vw->viking_vs, VIK_STATUSBAR_ZOOM, zoom_level );
1042 draw_status_tool ( vw );
1045 void vik_window_set_redraw_trigger(VikLayer *vl)
1047 VikWindow *vw = VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vl));
1052 static void window_configure_event ( VikWindow *vw )
1054 static int first = 1;
1057 // This is a hack to set the cursor corresponding to the first tool
1058 // FIXME find the correct way to initialize both tool and its cursor
1060 vw->viewport_cursor = (GdkCursor *)toolbox_get_cursor(vw->vt, "Pan");
1061 /* We set cursor, even if it is NULL: it resets to default */
1062 gdk_window_set_cursor ( gtk_widget_get_window(GTK_WIDGET(vw->viking_vvp)), vw->viewport_cursor );
1066 static void draw_redraw ( VikWindow *vw )
1068 VikCoord old_center = vw->trigger_center;
1069 vw->trigger_center = *(vik_viewport_get_center(vw->viking_vvp));
1070 VikLayer *new_trigger = vw->trigger;
1072 VikLayer *old_trigger = VIK_LAYER(vik_viewport_get_trigger(vw->viking_vvp));
1074 if ( ! new_trigger )
1075 ; /* do nothing -- have to redraw everything. */
1076 else if ( (old_trigger != new_trigger) || !vik_coord_equals(&old_center, &vw->trigger_center) || (new_trigger->type == VIK_LAYER_AGGREGATE) )
1077 vik_viewport_set_trigger ( vw->viking_vvp, new_trigger ); /* todo: set to half_drawn mode if new trigger is above old */
1079 vik_viewport_set_half_drawn ( vw->viking_vvp, TRUE );
1082 vik_viewport_clear ( vw->viking_vvp);
1083 // Main layer drawing
1084 vik_layers_panel_draw_all ( vw->viking_vlp );
1085 // Draw highlight (possibly again but ensures it is on top - especially for when tracks overlap)
1086 if ( vik_viewport_get_draw_highlight (vw->viking_vvp) ) {
1087 if ( vw->containing_vtl && (vw->selected_tracks || vw->selected_waypoints ) ) {
1088 vik_trw_layer_draw_highlight_items ( vw->containing_vtl, vw->selected_tracks, vw->selected_waypoints, vw->viking_vvp );
1090 else if ( vw->containing_vtl && (vw->selected_track || vw->selected_waypoint) ) {
1091 vik_trw_layer_draw_highlight_item ( vw->containing_vtl, vw->selected_track, vw->selected_waypoint, vw->viking_vvp );
1093 else if ( vw->selected_vtl ) {
1094 vik_trw_layer_draw_highlight ( vw->selected_vtl, vw->viking_vvp );
1097 // Other viewport decoration items on top if they are enabled/in use
1098 vik_viewport_draw_scale ( vw->viking_vvp );
1099 vik_viewport_draw_copyright ( vw->viking_vvp );
1100 vik_viewport_draw_centermark ( vw->viking_vvp );
1101 vik_viewport_draw_logo ( vw->viking_vvp );
1103 vik_viewport_set_half_drawn ( vw->viking_vvp, FALSE ); /* just in case. */
1106 gboolean draw_buf_done = TRUE;
1108 static gboolean draw_buf(gpointer data)
1110 gpointer *pass_along = data;
1111 gdk_threads_enter();
1112 gdk_draw_drawable (pass_along[0], pass_along[1],
1113 pass_along[2], 0, 0, 0, 0, -1, -1);
1114 draw_buf_done = TRUE;
1115 gdk_threads_leave();
1120 /* Mouse event handlers ************************************************************************/
1122 static void vik_window_pan_click (VikWindow *vw, GdkEventButton *event)
1124 /* set panning origin */
1125 vw->pan_move = FALSE;
1126 vw->pan_x = (gint) event->x;
1127 vw->pan_y = (gint) event->y;
1130 static void draw_click (VikWindow *vw, GdkEventButton *event)
1132 gtk_widget_grab_focus ( GTK_WIDGET(vw->viking_vvp) );
1134 /* middle button pressed. we reserve all middle button and scroll events
1135 * for panning and zooming; tools only get left/right/movement
1137 if ( event->button == 2) {
1138 if ( vw->vt->tools[vw->vt->active_tool].ti.pan_handler )
1139 // Tool still may need to do something (such as disable something)
1140 toolbox_click(vw->vt, event);
1141 vik_window_pan_click ( vw, event );
1144 toolbox_click(vw->vt, event);
1148 static void vik_window_pan_move (VikWindow *vw, GdkEventMotion *event)
1150 if ( vw->pan_x != -1 ) {
1151 vik_viewport_set_center_screen ( vw->viking_vvp, vik_viewport_get_width(vw->viking_vvp)/2 - event->x + vw->pan_x,
1152 vik_viewport_get_height(vw->viking_vvp)/2 - event->y + vw->pan_y );
1153 vw->pan_move = TRUE;
1154 vw->pan_x = event->x;
1155 vw->pan_y = event->y;
1160 static void draw_mouse_motion (VikWindow *vw, GdkEventMotion *event)
1162 static VikCoord coord;
1163 static struct UTM utm;
1164 static struct LatLon ll;
1165 #define BUFFER_SIZE 50
1166 static char pointer_buf[BUFFER_SIZE];
1167 gchar *lat = NULL, *lon = NULL;
1170 VikDemInterpol interpol_method;
1172 /* This is a hack, but work far the best, at least for single pointer systems.
1173 * See http://bugzilla.gnome.org/show_bug.cgi?id=587714 for more. */
1175 gdk_window_get_pointer (event->window, &x, &y, NULL);
1179 toolbox_move(vw->vt, event);
1181 vik_viewport_screen_to_coord ( vw->viking_vvp, event->x, event->y, &coord );
1182 vik_coord_to_utm ( &coord, &utm );
1184 if ( vik_viewport_get_drawmode ( vw->viking_vvp ) == VIK_VIEWPORT_DRAWMODE_UTM ) {
1185 // Reuse lat for the first part (Zone + N or S, and lon for the second part (easting and northing) of a UTM format:
1186 // ZONE[N|S] EASTING NORTHING
1187 lat = g_malloc(4*sizeof(gchar));
1188 // NB zone is stored in a char but is an actual number
1189 g_snprintf (lat, 4, "%d%c", utm.zone, utm.letter);
1190 lon = g_malloc(16*sizeof(gchar));
1191 g_snprintf (lon, 16, "%d %d", (gint)utm.easting, (gint)utm.northing);
1194 a_coords_utm_to_latlon ( &utm, &ll );
1195 a_coords_latlon_to_string ( &ll, &lat, &lon );
1198 /* Change interpolate method according to scale */
1199 zoom = vik_viewport_get_zoom(vw->viking_vvp);
1201 interpol_method = VIK_DEM_INTERPOL_NONE;
1202 else if (zoom >= 1.0)
1203 interpol_method = VIK_DEM_INTERPOL_SIMPLE;
1205 interpol_method = VIK_DEM_INTERPOL_BEST;
1206 if ((alt = a_dems_get_elev_by_coord(&coord, interpol_method)) != VIK_DEM_INVALID_ELEVATION) {
1207 if ( a_vik_get_units_height () == VIK_UNITS_HEIGHT_METRES )
1208 g_snprintf ( pointer_buf, BUFFER_SIZE, _("%s %s %dm"), lat, lon, alt );
1210 g_snprintf ( pointer_buf, BUFFER_SIZE, _("%s %s %dft"), lat, lon, (int)VIK_METERS_TO_FEET(alt) );
1213 g_snprintf ( pointer_buf, BUFFER_SIZE, _("%s %s"), lat, lon );
1218 vik_statusbar_set_message ( vw->viking_vs, VIK_STATUSBAR_POSITION, pointer_buf );
1220 vik_window_pan_move ( vw, event );
1222 /* This is recommended by the GTK+ documentation, but does not work properly.
1223 * Use deprecated way until GTK+ gets a solution for correct motion hint handling:
1224 * http://bugzilla.gnome.org/show_bug.cgi?id=587714
1226 /* gdk_event_request_motions ( event ); */
1230 * Action the single click after a small timeout
1231 * If a double click has occurred then this will do nothing
1233 static gboolean vik_window_pan_timeout (VikWindow *vw)
1235 if ( ! vw->single_click_pending ) {
1236 // Double click happened, so don't do anything
1240 /* set panning origin */
1241 vw->pan_move = FALSE;
1242 vw->single_click_pending = FALSE;
1243 vik_viewport_set_center_screen ( vw->viking_vvp, vw->delayed_pan_x, vw->delayed_pan_y );
1246 // Really turn off the pan moving!!
1247 vw->pan_x = vw->pan_y = -1;
1251 static void vik_window_pan_release ( VikWindow *vw, GdkEventButton *event )
1253 gboolean do_draw = TRUE;
1255 if ( vw->pan_move == FALSE ) {
1256 vw->single_click_pending = !vw->single_click_pending;
1258 if ( vw->single_click_pending ) {
1259 // Store offset to use
1260 vw->delayed_pan_x = vw->pan_x;
1261 vw->delayed_pan_y = vw->pan_y;
1262 // Get double click time
1263 GtkSettings *gs = gtk_widget_get_settings ( GTK_WIDGET(vw) );
1264 GValue dct = { 0 }; // = G_VALUE_INIT; // GLIB 2.30+ only
1265 g_value_init ( &dct, G_TYPE_INT );
1266 g_object_get_property ( G_OBJECT(gs), "gtk-double-click-time", &dct );
1267 // Give chance for a double click to occur
1268 gint timer = g_value_get_int ( &dct ) + 50;
1269 g_timeout_add ( timer, (GSourceFunc)vik_window_pan_timeout, vw );
1273 vik_viewport_set_center_screen ( vw->viking_vvp, vw->pan_x, vw->pan_y );
1277 vik_viewport_set_center_screen ( vw->viking_vvp, vik_viewport_get_width(vw->viking_vvp)/2 - event->x + vw->pan_x,
1278 vik_viewport_get_height(vw->viking_vvp)/2 - event->y + vw->pan_y );
1281 vw->pan_move = FALSE;
1282 vw->pan_x = vw->pan_y = -1;
1287 static void draw_release ( VikWindow *vw, GdkEventButton *event )
1289 gtk_widget_grab_focus ( GTK_WIDGET(vw->viking_vvp) );
1291 if ( event->button == 2 ) { /* move / pan */
1292 if ( vw->vt->tools[vw->vt->active_tool].ti.pan_handler )
1293 // Tool still may need to do something (such as reenable something)
1294 toolbox_release(vw->vt, event);
1295 vik_window_pan_release ( vw, event );
1298 toolbox_release(vw->vt, event);
1302 static void draw_scroll (VikWindow *vw, GdkEventScroll *event)
1304 guint modifiers = event->state & (GDK_SHIFT_MASK | GDK_CONTROL_MASK);
1305 if ( modifiers == GDK_CONTROL_MASK ) {
1306 /* control == pan up & down */
1307 if ( event->direction == GDK_SCROLL_UP )
1308 vik_viewport_set_center_screen ( vw->viking_vvp, vik_viewport_get_width(vw->viking_vvp)/2, vik_viewport_get_height(vw->viking_vvp)/3 );
1310 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 );
1311 } else if ( modifiers == GDK_SHIFT_MASK ) {
1312 /* shift == pan left & right */
1313 if ( event->direction == GDK_SCROLL_UP )
1314 vik_viewport_set_center_screen ( vw->viking_vvp, vik_viewport_get_width(vw->viking_vvp)/3, vik_viewport_get_height(vw->viking_vvp)/2 );
1316 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 );
1317 } else if ( modifiers == (GDK_CONTROL_MASK | GDK_SHIFT_MASK) ) {
1318 // This zoom is on the center position
1319 if ( event->direction == GDK_SCROLL_UP )
1320 vik_viewport_zoom_in (vw->viking_vvp);
1322 vik_viewport_zoom_out (vw->viking_vvp);
1324 /* make sure mouse is still over the same point on the map when we zoom */
1327 gint center_x = vik_viewport_get_width ( vw->viking_vvp ) / 2;
1328 gint center_y = vik_viewport_get_height ( vw->viking_vvp ) / 2;
1329 vik_viewport_screen_to_coord ( vw->viking_vvp, event->x, event->y, &coord );
1330 if ( event->direction == GDK_SCROLL_UP )
1331 vik_viewport_zoom_in (vw->viking_vvp);
1333 vik_viewport_zoom_out(vw->viking_vvp);
1334 vik_viewport_coord_to_screen ( vw->viking_vvp, &coord, &x, &y );
1335 vik_viewport_set_center_screen ( vw->viking_vvp, center_x + (x - event->x),
1336 center_y + (y - event->y) );
1344 /********************************************************************************
1346 ********************************************************************************/
1347 static void draw_ruler(VikViewport *vvp, GdkDrawable *d, GdkGC *gc, gint x1, gint y1, gint x2, gint y2, gdouble distance)
1351 GdkGC *labgc = vik_viewport_new_gc ( vvp, "#cccccc", 1);
1352 GdkGC *thickgc = gdk_gc_new(d);
1354 gdouble len = sqrt((x1-x2)*(x1-x2) + (y1-y2)*(y1-y2));
1355 gdouble dx = (x2-x1)/len*10;
1356 gdouble dy = (y2-y1)/len*10;
1357 gdouble c = cos(DEG2RAD(15.0));
1358 gdouble s = sin(DEG2RAD(15.0));
1360 gdouble baseangle = 0;
1363 /* draw line with arrow ends */
1365 gint tmp_x1=x1, tmp_y1=y1, tmp_x2=x2, tmp_y2=y2;
1366 a_viewport_clip_line(&tmp_x1, &tmp_y1, &tmp_x2, &tmp_y2);
1367 gdk_draw_line(d, gc, tmp_x1, tmp_y1, tmp_x2, tmp_y2);
1370 a_viewport_clip_line(&x1, &y1, &x2, &y2);
1371 gdk_draw_line(d, gc, x1, y1, x2, y2);
1373 gdk_draw_line(d, gc, x1 - dy, y1 + dx, x1 + dy, y1 - dx);
1374 gdk_draw_line(d, gc, x2 - dy, y2 + dx, x2 + dy, y2 - dx);
1375 gdk_draw_line(d, gc, x2, y2, x2 - (dx * c + dy * s), y2 - (dy * c - dx * s));
1376 gdk_draw_line(d, gc, x2, y2, x2 - (dx * c - dy * s), y2 - (dy * c + dx * s));
1377 gdk_draw_line(d, gc, x1, y1, x1 + (dx * c + dy * s), y1 + (dy * c - dx * s));
1378 gdk_draw_line(d, gc, x1, y1, x1 + (dx * c - dy * s), y1 + (dy * c + dx * s));
1384 vik_viewport_compute_bearing ( vvp, x1, y1, x2, y2, &angle, &baseangle );
1388 gdk_gc_copy(thickgc, gc);
1389 gdk_gc_set_line_attributes(thickgc, CW, GDK_LINE_SOLID, GDK_CAP_BUTT, GDK_JOIN_MITER);
1390 gdk_color_parse("#2255cc", &color);
1391 gdk_gc_set_rgb_fg_color(thickgc, &color);
1393 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);
1396 gdk_gc_copy(thickgc, gc);
1397 gdk_gc_set_line_attributes(thickgc, 2, GDK_LINE_SOLID, GDK_CAP_BUTT, GDK_JOIN_MITER);
1398 for (i=0; i<180; i++) {
1399 c = cos(DEG2RAD(i)*2 + baseangle);
1400 s = sin(DEG2RAD(i)*2 + baseangle);
1403 gdk_draw_line (d, gc, x1 + CR*c, y1 + CR*s, x1 + (CR+CW)*c, y1 + (CR+CW)*s);
1405 gdouble ticksize = 2*CW;
1406 gdk_draw_line (d, thickgc, x1 + (CR-CW)*c, y1 + (CR-CW)*s, x1 + (CR+ticksize)*c, y1 + (CR+ticksize)*s);
1410 gdk_draw_arc (d, gc, FALSE, x1-CR, y1-CR, 2*CR, 2*CR, 0, 64*360);
1411 gdk_draw_arc (d, gc, FALSE, x1-CR-CW, y1-CR-CW, 2*(CR+CW), 2*(CR+CW), 0, 64*360);
1412 gdk_draw_arc (d, gc, FALSE, x1-CR+CW, y1-CR+CW, 2*(CR-CW), 2*(CR-CW), 0, 64*360);
1413 c = (CR+CW*2)*cos(baseangle);
1414 s = (CR+CW*2)*sin(baseangle);
1415 gdk_draw_line (d, gc, x1-c, y1-s, x1+c, y1+s);
1416 gdk_draw_line (d, gc, x1+s, y1-c, x1-s, y1+c);
1419 #define LABEL(x, y, w, h) { \
1420 gdk_draw_rectangle(d, labgc, TRUE, (x)-2, (y)-1, (w)+4, (h)+1); \
1421 gdk_draw_rectangle(d, gc, FALSE, (x)-2, (y)-1, (w)+4, (h)+1); \
1422 gdk_draw_layout(d, gc, (x), (y), pl); }
1424 gint wd, hd, xd, yd;
1425 gint wb, hb, xb, yb;
1427 pl = gtk_widget_create_pango_layout (GTK_WIDGET(vvp), NULL);
1428 pango_layout_set_font_description (pl, gtk_widget_get_style(GTK_WIDGET(vvp))->font_desc);
1429 pango_layout_set_text(pl, "N", -1);
1430 gdk_draw_layout(d, gc, x1-5, y1-CR-3*CW-8, pl);
1432 /* draw label with distance */
1433 vik_units_distance_t dist_units = a_vik_get_units_distance ();
1434 switch (dist_units) {
1435 case VIK_UNITS_DISTANCE_KILOMETRES:
1436 if (distance >= 1000 && distance < 100000) {
1437 g_sprintf(str, "%3.2f km", distance/1000.0);
1438 } else if (distance < 1000) {
1439 g_sprintf(str, "%d m", (int)distance);
1441 g_sprintf(str, "%d km", (int)distance/1000);
1444 case VIK_UNITS_DISTANCE_MILES:
1445 if (distance >= VIK_MILES_TO_METERS(1) && distance < VIK_MILES_TO_METERS(100)) {
1446 g_sprintf(str, "%3.2f miles", VIK_METERS_TO_MILES(distance));
1447 } else if (distance < VIK_MILES_TO_METERS(1)) {
1448 g_sprintf(str, "%d yards", (int)(distance*1.0936133));
1450 g_sprintf(str, "%d miles", (int)VIK_METERS_TO_MILES(distance));
1454 g_critical("Houston, we've had a problem. distance=%d", dist_units);
1457 pango_layout_set_text(pl, str, -1);
1459 pango_layout_get_pixel_size ( pl, &wd, &hd );
1461 xd = (x1+x2)/2 + dy;
1462 yd = (y1+y2)/2 - hd/2 - dx;
1464 xd = (x1+x2)/2 - dy;
1465 yd = (y1+y2)/2 - hd/2 + dx;
1468 if ( xd < -5 || yd < -5 || xd > vik_viewport_get_width(vvp)+5 || yd > vik_viewport_get_height(vvp)+5 ) {
1473 LABEL(xd, yd, wd, hd);
1475 /* draw label with bearing */
1476 g_sprintf(str, "%3.1f°", RAD2DEG(angle));
1477 pango_layout_set_text(pl, str, -1);
1478 pango_layout_get_pixel_size ( pl, &wb, &hb );
1479 xb = x1 + CR*cos(angle-M_PI_2);
1480 yb = y1 + CR*sin(angle-M_PI_2);
1482 if ( xb < -5 || yb < -5 || xb > vik_viewport_get_width(vvp)+5 || yb > vik_viewport_get_height(vvp)+5 ) {
1488 GdkRectangle r1 = {xd-2, yd-1, wd+4, hd+1}, r2 = {xb-2, yb-1, wb+4, hb+1};
1489 if (gdk_rectangle_intersect(&r1, &r2, &r2)) {
1493 LABEL(xb, yb, wb, hb);
1497 g_object_unref ( G_OBJECT ( pl ) );
1498 g_object_unref ( G_OBJECT ( labgc ) );
1499 g_object_unref ( G_OBJECT ( thickgc ) );
1505 gboolean has_oldcoord;
1507 } ruler_tool_state_t;
1509 static gpointer ruler_create (VikWindow *vw, VikViewport *vvp)
1511 ruler_tool_state_t *s = g_new(ruler_tool_state_t, 1);
1514 s->has_oldcoord = FALSE;
1518 static void ruler_destroy (ruler_tool_state_t *s)
1523 static VikLayerToolFuncStatus ruler_click (VikLayer *vl, GdkEventButton *event, ruler_tool_state_t *s)
1528 if ( event->button == 1 ) {
1529 gchar *lat=NULL, *lon=NULL;
1530 vik_viewport_screen_to_coord ( s->vvp, (gint) event->x, (gint) event->y, &coord );
1531 vik_coord_to_latlon ( &coord, &ll );
1532 a_coords_latlon_to_string ( &ll, &lat, &lon );
1533 if ( s->has_oldcoord ) {
1534 vik_units_distance_t dist_units = a_vik_get_units_distance ();
1535 switch (dist_units) {
1536 case VIK_UNITS_DISTANCE_KILOMETRES:
1537 temp = g_strdup_printf ( "%s %s DIFF %f meters", lat, lon, vik_coord_diff( &coord, &(s->oldcoord) ) );
1539 case VIK_UNITS_DISTANCE_MILES:
1540 temp = g_strdup_printf ( "%s %s DIFF %f miles", lat, lon, VIK_METERS_TO_MILES(vik_coord_diff( &coord, &(s->oldcoord) )) );
1543 temp = g_strdup_printf ("Just to keep the compiler happy");
1544 g_critical("Houston, we've had a problem. distance=%d", dist_units);
1547 s->has_oldcoord = FALSE;
1550 temp = g_strdup_printf ( "%s %s", lat, lon );
1551 s->has_oldcoord = TRUE;
1554 vik_statusbar_set_message ( s->vw->viking_vs, VIK_STATUSBAR_INFO, temp );
1557 s->oldcoord = coord;
1560 vik_viewport_set_center_screen ( s->vvp, (gint) event->x, (gint) event->y );
1561 draw_update ( s->vw );
1563 return VIK_LAYER_TOOL_ACK;
1566 static VikLayerToolFuncStatus ruler_move (VikLayer *vl, GdkEventMotion *event, ruler_tool_state_t *s)
1568 VikViewport *vvp = s->vvp;
1569 VikWindow *vw = s->vw;
1575 if ( s->has_oldcoord ) {
1576 int oldx, oldy, w1, h1, w2, h2;
1577 static GdkPixmap *buf = NULL;
1578 gchar *lat=NULL, *lon=NULL;
1579 w1 = vik_viewport_get_width(vvp);
1580 h1 = vik_viewport_get_height(vvp);
1582 buf = gdk_pixmap_new ( gtk_widget_get_window(GTK_WIDGET(vvp)), w1, h1, -1 );
1584 gdk_drawable_get_size(buf, &w2, &h2);
1585 if (w1 != w2 || h1 != h2) {
1586 g_object_unref ( G_OBJECT ( buf ) );
1587 buf = gdk_pixmap_new ( gtk_widget_get_window(GTK_WIDGET(vvp)), w1, h1, -1 );
1590 vik_viewport_screen_to_coord ( vvp, (gint) event->x, (gint) event->y, &coord );
1591 vik_coord_to_latlon ( &coord, &ll );
1592 vik_viewport_coord_to_screen ( vvp, &s->oldcoord, &oldx, &oldy );
1594 gdk_draw_drawable (buf, gtk_widget_get_style(GTK_WIDGET(vvp))->black_gc,
1595 vik_viewport_get_pixmap(vvp), 0, 0, 0, 0, -1, -1);
1596 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)) );
1597 if (draw_buf_done) {
1598 static gpointer pass_along[3];
1599 pass_along[0] = gtk_widget_get_window(GTK_WIDGET(vvp));
1600 pass_along[1] = gtk_widget_get_style(GTK_WIDGET(vvp))->black_gc;
1601 pass_along[2] = buf;
1602 g_idle_add_full (G_PRIORITY_HIGH_IDLE + 10, draw_buf, pass_along, NULL);
1603 draw_buf_done = FALSE;
1605 a_coords_latlon_to_string(&ll, &lat, &lon);
1606 vik_units_distance_t dist_units = a_vik_get_units_distance ();
1607 switch (dist_units) {
1608 case VIK_UNITS_DISTANCE_KILOMETRES:
1609 temp = g_strdup_printf ( "%s %s DIFF %f meters", lat, lon, vik_coord_diff( &coord, &(s->oldcoord) ) );
1611 case VIK_UNITS_DISTANCE_MILES:
1612 temp = g_strdup_printf ( "%s %s DIFF %f miles", lat, lon, VIK_METERS_TO_MILES (vik_coord_diff( &coord, &(s->oldcoord) )) );
1615 temp = g_strdup_printf ("Just to keep the compiler happy");
1616 g_critical("Houston, we've had a problem. distance=%d", dist_units);
1618 vik_statusbar_set_message ( vw->viking_vs, VIK_STATUSBAR_INFO, temp );
1621 return VIK_LAYER_TOOL_ACK;
1624 static VikLayerToolFuncStatus ruler_release (VikLayer *vl, GdkEventButton *event, ruler_tool_state_t *s)
1626 return VIK_LAYER_TOOL_ACK;
1629 static void ruler_deactivate (VikLayer *vl, ruler_tool_state_t *s)
1631 draw_update ( s->vw );
1634 static gboolean ruler_key_press (VikLayer *vl, GdkEventKey *event, ruler_tool_state_t *s)
1636 if (event->keyval == GDK_Escape) {
1637 s->has_oldcoord = FALSE;
1638 ruler_deactivate ( vl, s );
1641 // Regardless of whether we used it, return false so other GTK things may use it
1645 static VikToolInterface ruler_tool =
1646 // NB Ctrl+Shift+R is used for Refresh (deemed more important), so use 'U' instead
1647 { { "Ruler", "vik-icon-ruler", N_("_Ruler"), "<control><shift>U", N_("Ruler Tool"), 2 },
1648 (VikToolConstructorFunc) ruler_create,
1649 (VikToolDestructorFunc) ruler_destroy,
1650 (VikToolActivationFunc) NULL,
1651 (VikToolActivationFunc) ruler_deactivate,
1652 (VikToolMouseFunc) ruler_click,
1653 (VikToolMouseMoveFunc) ruler_move,
1654 (VikToolMouseFunc) ruler_release,
1655 (VikToolKeyFunc) ruler_key_press,
1657 GDK_CURSOR_IS_PIXMAP,
1658 &cursor_ruler_pixbuf,
1660 /*** end ruler code ********************************************************/
1664 /********************************************************************************
1666 ********************************************************************************/
1671 // Track zoom bounds for zoom tool with shift modifier:
1672 gboolean bounds_active;
1675 } zoom_tool_state_t;
1678 * In case the screen size has changed
1680 static void zoomtool_resize_pixmap (zoom_tool_state_t *zts)
1684 // Allocate a drawing area the size of the viewport
1685 w1 = vik_viewport_get_width ( zts->vw->viking_vvp );
1686 h1 = vik_viewport_get_height ( zts->vw->viking_vvp );
1688 if ( !zts->pixmap ) {
1690 zts->pixmap = gdk_pixmap_new ( gtk_widget_get_window(GTK_WIDGET(zts->vw->viking_vvp)), w1, h1, -1 );
1693 gdk_drawable_get_size ( zts->pixmap, &w2, &h2 );
1695 if ( w1 != w2 || h1 != h2 ) {
1696 // Has changed - delete and recreate with new values
1697 g_object_unref ( G_OBJECT ( zts->pixmap ) );
1698 zts->pixmap = gdk_pixmap_new ( gtk_widget_get_window(GTK_WIDGET(zts->vw->viking_vvp)), w1, h1, -1 );
1702 static gpointer zoomtool_create (VikWindow *vw, VikViewport *vvp)
1704 zoom_tool_state_t *zts = g_new(zoom_tool_state_t, 1);
1709 zts->bounds_active = FALSE;
1713 static void zoomtool_destroy ( zoom_tool_state_t *zts)
1716 g_object_unref ( G_OBJECT ( zts->pixmap ) );
1720 static VikLayerToolFuncStatus zoomtool_click (VikLayer *vl, GdkEventButton *event, zoom_tool_state_t *zts)
1722 zts->vw->modified = TRUE;
1723 guint modifiers = event->state & (GDK_SHIFT_MASK | GDK_CONTROL_MASK);
1727 gint center_x = vik_viewport_get_width ( zts->vw->viking_vvp ) / 2;
1728 gint center_y = vik_viewport_get_height ( zts->vw->viking_vvp ) / 2;
1730 gboolean skip_update = FALSE;
1732 zts->bounds_active = FALSE;
1734 if ( modifiers == (GDK_CONTROL_MASK | GDK_SHIFT_MASK) ) {
1735 // This zoom is on the center position
1736 vik_viewport_set_center_screen ( zts->vw->viking_vvp, center_x, center_y );
1737 if ( event->button == 1 )
1738 vik_viewport_zoom_in (zts->vw->viking_vvp);
1739 else if ( event->button == 3 )
1740 vik_viewport_zoom_out (zts->vw->viking_vvp);
1742 else if ( modifiers == GDK_CONTROL_MASK ) {
1743 // This zoom is to recenter on the mouse position
1744 vik_viewport_set_center_screen ( zts->vw->viking_vvp, (gint) event->x, (gint) event->y );
1745 if ( event->button == 1 )
1746 vik_viewport_zoom_in (zts->vw->viking_vvp);
1747 else if ( event->button == 3 )
1748 vik_viewport_zoom_out (zts->vw->viking_vvp);
1750 else if ( modifiers == GDK_SHIFT_MASK ) {
1751 // Get start of new zoom bounds
1752 if ( event->button == 1 ) {
1753 zts->bounds_active = TRUE;
1754 zts->start_x = (gint) event->x;
1755 zts->start_y = (gint) event->y;
1760 /* make sure mouse is still over the same point on the map when we zoom */
1761 vik_viewport_screen_to_coord ( zts->vw->viking_vvp, event->x, event->y, &coord );
1762 if ( event->button == 1 )
1763 vik_viewport_zoom_in (zts->vw->viking_vvp);
1764 else if ( event->button == 3 )
1765 vik_viewport_zoom_out(zts->vw->viking_vvp);
1766 vik_viewport_coord_to_screen ( zts->vw->viking_vvp, &coord, &x, &y );
1767 vik_viewport_set_center_screen ( zts->vw->viking_vvp,
1768 center_x + (x - event->x),
1769 center_y + (y - event->y) );
1773 draw_update ( zts->vw );
1775 return VIK_LAYER_TOOL_ACK;
1778 static VikLayerToolFuncStatus zoomtool_move (VikLayer *vl, GdkEventMotion *event, zoom_tool_state_t *zts)
1780 guint modifiers = event->state & (GDK_SHIFT_MASK | GDK_CONTROL_MASK);
1782 if ( zts->bounds_active && modifiers == GDK_SHIFT_MASK ) {
1783 zoomtool_resize_pixmap ( zts );
1785 // Blank out currently drawn area
1786 gdk_draw_drawable ( zts->pixmap,
1787 gtk_widget_get_style(GTK_WIDGET(zts->vw->viking_vvp))->black_gc,
1788 vik_viewport_get_pixmap(zts->vw->viking_vvp),
1789 0, 0, 0, 0, -1, -1);
1791 // Calculate new box starting point & size in pixels
1792 int xx, yy, width, height;
1793 if ( event->y > zts->start_y ) {
1795 height = event->y-zts->start_y;
1799 height = zts->start_y-event->y;
1801 if ( event->x > zts->start_x ) {
1803 width = event->x-zts->start_x;
1807 width = zts->start_x-event->x;
1811 gdk_draw_rectangle (zts->pixmap, gtk_widget_get_style(GTK_WIDGET(zts->vw->viking_vvp))->black_gc, FALSE, xx, yy, width, height);
1813 // Only actually draw when there's time to do so
1814 if (draw_buf_done) {
1815 static gpointer pass_along[3];
1816 pass_along[0] = gtk_widget_get_window(GTK_WIDGET(zts->vw->viking_vvp));
1817 pass_along[1] = gtk_widget_get_style(GTK_WIDGET(zts->vw->viking_vvp))->black_gc;
1818 pass_along[2] = zts->pixmap;
1819 g_idle_add_full (G_PRIORITY_HIGH_IDLE + 10, draw_buf, pass_along, NULL);
1820 draw_buf_done = FALSE;
1824 zts->bounds_active = FALSE;
1826 return VIK_LAYER_TOOL_ACK;
1829 static VikLayerToolFuncStatus zoomtool_release (VikLayer *vl, GdkEventButton *event, zoom_tool_state_t *zts)
1831 guint modifiers = event->state & (GDK_SHIFT_MASK | GDK_CONTROL_MASK);
1833 // Ensure haven't just released on the exact same position
1834 // i.e. probably haven't moved the mouse at all
1835 if ( zts->bounds_active && modifiers == GDK_SHIFT_MASK &&
1836 ( event->x < zts->start_x-5 || event->x > zts->start_x+5 ) &&
1837 ( event->y < zts->start_y-5 || event->y > zts->start_y+5 ) ) {
1839 VikCoord coord1, coord2;
1840 vik_viewport_screen_to_coord ( zts->vw->viking_vvp, zts->start_x, zts->start_y, &coord1);
1841 vik_viewport_screen_to_coord ( zts->vw->viking_vvp, event->x, event->y, &coord2);
1843 // From the extend of the bounds pick the best zoom level
1844 // c.f. trw_layer_zoom_to_show_latlons()
1845 // Maybe refactor...
1846 struct LatLon ll1, ll2;
1847 vik_coord_to_latlon(&coord1, &ll1);
1848 vik_coord_to_latlon(&coord2, &ll2);
1849 struct LatLon average = { (ll1.lat+ll2.lat)/2,
1850 (ll1.lon+ll2.lon)/2 };
1852 VikCoord new_center;
1853 vik_coord_load_from_latlon ( &new_center, vik_viewport_get_coord_mode ( zts->vw->viking_vvp ), &average );
1854 vik_viewport_set_center_coord ( zts->vw->viking_vvp, &new_center, FALSE );
1856 /* Convert into definite 'smallest' and 'largest' positions */
1857 struct LatLon minmin;
1858 if ( ll1.lat < ll2.lat )
1859 minmin.lat = ll1.lat;
1861 minmin.lat = ll2.lat;
1863 struct LatLon maxmax;
1864 if ( ll1.lon > ll2.lon )
1865 maxmax.lon = ll1.lon;
1867 maxmax.lon = ll2.lon;
1869 /* Always recalculate the 'best' zoom level */
1870 gdouble zoom = VIK_VIEWPORT_MIN_ZOOM;
1871 vik_viewport_set_zoom ( zts->vw->viking_vvp, zoom );
1873 gdouble min_lat, max_lat, min_lon, max_lon;
1874 /* Should only be a maximum of about 18 iterations from min to max zoom levels */
1875 while ( zoom <= VIK_VIEWPORT_MAX_ZOOM ) {
1876 vik_viewport_get_min_max_lat_lon ( zts->vw->viking_vvp, &min_lat, &max_lat, &min_lon, &max_lon );
1877 /* NB I think the logic used in this test to determine if the bounds is within view
1878 fails if track goes across 180 degrees longitude.
1879 Hopefully that situation is not too common...
1880 Mind you viking doesn't really do edge locations to well anyway */
1881 if ( min_lat < minmin.lat &&
1882 max_lat > minmin.lat &&
1883 min_lon < maxmax.lon &&
1884 max_lon > maxmax.lon )
1885 /* Found within zoom level */
1890 vik_viewport_set_zoom ( zts->vw->viking_vvp, zoom );
1894 // When pressing shift and clicking for zoom, then jump three levels
1895 if ( modifiers == GDK_SHIFT_MASK ) {
1896 // Zoom in/out by three if possible
1897 vik_viewport_set_center_screen ( zts->vw->viking_vvp, event->x, event->y );
1898 if ( event->button == 1 ) {
1899 vik_viewport_zoom_in ( zts->vw->viking_vvp );
1900 vik_viewport_zoom_in ( zts->vw->viking_vvp );
1901 vik_viewport_zoom_in ( zts->vw->viking_vvp );
1903 else if ( event->button == 3 ) {
1904 vik_viewport_zoom_out ( zts->vw->viking_vvp );
1905 vik_viewport_zoom_out ( zts->vw->viking_vvp );
1906 vik_viewport_zoom_out ( zts->vw->viking_vvp );
1911 draw_update ( zts->vw );
1914 zts->bounds_active = FALSE;
1916 return VIK_LAYER_TOOL_ACK;
1919 static VikToolInterface zoom_tool =
1920 { { "Zoom", "vik-icon-zoom", N_("_Zoom"), "<control><shift>Z", N_("Zoom Tool"), 1 },
1921 (VikToolConstructorFunc) zoomtool_create,
1922 (VikToolDestructorFunc) zoomtool_destroy,
1923 (VikToolActivationFunc) NULL,
1924 (VikToolActivationFunc) NULL,
1925 (VikToolMouseFunc) zoomtool_click,
1926 (VikToolMouseMoveFunc) zoomtool_move,
1927 (VikToolMouseFunc) zoomtool_release,
1930 GDK_CURSOR_IS_PIXMAP,
1931 &cursor_zoom_pixbuf,
1933 /*** end zoom code ********************************************************/
1935 /********************************************************************************
1937 ********************************************************************************/
1938 static gpointer pantool_create (VikWindow *vw, VikViewport *vvp)
1943 // NB Double clicking means this gets called THREE times!!!
1944 static VikLayerToolFuncStatus pantool_click (VikLayer *vl, GdkEventButton *event, VikWindow *vw)
1946 vw->modified = TRUE;
1948 if ( event->type == GDK_2BUTTON_PRESS ) {
1949 // Zoom in / out on double click
1950 // No need to change the center as that has already occurred in the first click of a double click occurrence
1951 if ( event->button == 1 ) {
1952 guint modifier = event->state & GDK_SHIFT_MASK;
1954 vik_viewport_zoom_out ( vw->viking_vvp );
1956 vik_viewport_zoom_in ( vw->viking_vvp );
1958 else if ( event->button == 3 )
1959 vik_viewport_zoom_out ( vw->viking_vvp );
1964 // Standard pan click
1965 if ( event->button == 1 )
1966 vik_window_pan_click ( vw, event );
1968 return VIK_LAYER_TOOL_ACK;
1971 static VikLayerToolFuncStatus pantool_move (VikLayer *vl, GdkEventMotion *event, VikWindow *vw)
1973 vik_window_pan_move ( vw, event );
1974 return VIK_LAYER_TOOL_ACK;
1977 static VikLayerToolFuncStatus pantool_release (VikLayer *vl, GdkEventButton *event, VikWindow *vw)
1979 if ( event->button == 1 )
1980 vik_window_pan_release ( vw, event );
1981 return VIK_LAYER_TOOL_ACK;
1984 static VikToolInterface pan_tool =
1985 { { "Pan", "vik-icon-pan", N_("_Pan"), "<control><shift>P", N_("Pan Tool"), 0 },
1986 (VikToolConstructorFunc) pantool_create,
1987 (VikToolDestructorFunc) NULL,
1988 (VikToolActivationFunc) NULL,
1989 (VikToolActivationFunc) NULL,
1990 (VikToolMouseFunc) pantool_click,
1991 (VikToolMouseMoveFunc) pantool_move,
1992 (VikToolMouseFunc) pantool_release,
1998 /*** end pan code ********************************************************/
2000 /********************************************************************************
2002 ********************************************************************************/
2003 static gpointer selecttool_create (VikWindow *vw, VikViewport *vvp)
2005 tool_ed_t *t = g_new(tool_ed_t, 1);
2009 t->is_waypoint = FALSE;
2013 static void selecttool_destroy (tool_ed_t *t)
2021 GdkEventButton *event;
2022 tool_ed_t *tool_edit;
2025 static void click_layer_selected (VikLayer *vl, clicker *ck)
2027 /* Do nothing when function call returns true; */
2028 /* i.e. stop on first found item */
2031 if ( vik_layer_get_interface(vl->type)->select_click )
2032 ck->cont = !vik_layer_get_interface(vl->type)->select_click ( vl, ck->event, ck->vvp, ck->tool_edit );
2035 static VikLayerToolFuncStatus selecttool_click (VikLayer *vl, GdkEventButton *event, tool_ed_t *t)
2037 /* Only allow selection on primary button */
2038 if ( event->button == 1 ) {
2039 /* Enable click to apply callback to potentially all track/waypoint layers */
2040 /* Useful as we can find things that aren't necessarily in the currently selected layer */
2041 GList* gl = vik_layers_panel_get_all_layers_of_type ( t->vw->viking_vlp, VIK_LAYER_TRW, FALSE ); // Don't get invisible layers
2044 ck.vvp = t->vw->viking_vvp;
2047 g_list_foreach ( gl, (GFunc) click_layer_selected, &ck );
2050 // If nothing found then deselect & redraw screen if necessary to remove the highlight
2053 VikTreeview *vtv = vik_layers_panel_get_treeview ( t->vw->viking_vlp );
2055 if ( vik_treeview_get_selected_iter ( vtv, &iter ) ) {
2056 // Only clear if selected thing is a TrackWaypoint layer or a sublayer
2057 gint type = vik_treeview_item_get_type ( vtv, &iter );
2058 if ( type == VIK_TREEVIEW_TYPE_SUBLAYER ||
2059 VIK_LAYER(vik_treeview_item_get_pointer ( vtv, &iter ))->type == VIK_LAYER_TRW ) {
2061 vik_treeview_item_unselect ( vtv, &iter );
2062 if ( vik_window_clear_highlight ( t->vw ) )
2063 draw_update ( t->vw );
2068 else if ( ( event->button == 3 ) && ( vl && ( vl->type == VIK_LAYER_TRW ) ) ) {
2070 /* Act on currently selected item to show menu */
2071 if ( t->vw->selected_track || t->vw->selected_waypoint )
2072 if ( vik_layer_get_interface(vl->type)->show_viewport_menu )
2073 vik_layer_get_interface(vl->type)->show_viewport_menu ( vl, event, t->vw->viking_vvp );
2076 return VIK_LAYER_TOOL_ACK;
2079 static VikLayerToolFuncStatus selecttool_move (VikLayer *vl, GdkEventButton *event, tool_ed_t *t)
2081 /* Only allow selection on primary button */
2082 if ( event->button == 1 ) {
2083 // Don't care about vl here
2085 if ( vik_layer_get_interface(VIK_LAYER_TRW)->select_move )
2086 vik_layer_get_interface(VIK_LAYER_TRW)->select_move ( vl, event, t->vvp, t );
2088 return VIK_LAYER_TOOL_ACK;
2091 static VikLayerToolFuncStatus selecttool_release (VikLayer *vl, GdkEventButton *event, tool_ed_t *t)
2093 /* Only allow selection on primary button */
2094 if ( event->button == 1 ) {
2095 // Don't care about vl here
2097 if ( vik_layer_get_interface(VIK_LAYER_TRW)->select_release )
2098 vik_layer_get_interface(VIK_LAYER_TRW)->select_release ( (VikLayer*)t->vtl, event, t->vvp, t );
2100 return VIK_LAYER_TOOL_ACK;
2103 static VikToolInterface select_tool =
2104 { { "Select", "vik-icon-select", N_("_Select"), "<control><shift>S", N_("Select Tool"), 3 },
2105 (VikToolConstructorFunc) selecttool_create,
2106 (VikToolDestructorFunc) selecttool_destroy,
2107 (VikToolActivationFunc) NULL,
2108 (VikToolActivationFunc) NULL,
2109 (VikToolMouseFunc) selecttool_click,
2110 (VikToolMouseMoveFunc) selecttool_move,
2111 (VikToolMouseFunc) selecttool_release,
2112 (VikToolKeyFunc) NULL,
2117 /*** end select tool code ********************************************************/
2119 static void draw_pan_cb ( GtkAction *a, VikWindow *vw )
2121 // Since the treeview cell editting intercepts standard keyboard handlers, it means we can receive events here
2122 // Thus if currently editting, ensure we don't move the viewport when Ctrl+<arrow> is received
2123 VikLayer *sel = vik_layers_panel_get_selected ( vw->viking_vlp );
2124 if ( sel && vik_treeview_get_editing ( sel->vt ) )
2127 if (!strcmp(gtk_action_get_name(a), "PanNorth")) {
2128 vik_viewport_set_center_screen ( vw->viking_vvp, vik_viewport_get_width(vw->viking_vvp)/2, 0 );
2129 } else if (!strcmp(gtk_action_get_name(a), "PanEast")) {
2130 vik_viewport_set_center_screen ( vw->viking_vvp, vik_viewport_get_width(vw->viking_vvp), vik_viewport_get_height(vw->viking_vvp)/2 );
2131 } else if (!strcmp(gtk_action_get_name(a), "PanSouth")) {
2132 vik_viewport_set_center_screen ( vw->viking_vvp, vik_viewport_get_width(vw->viking_vvp)/2, vik_viewport_get_height(vw->viking_vvp) );
2133 } else if (!strcmp(gtk_action_get_name(a), "PanWest")) {
2134 vik_viewport_set_center_screen ( vw->viking_vvp, 0, vik_viewport_get_height(vw->viking_vvp)/2 );
2139 static void full_screen_cb ( GtkAction *a, VikWindow *vw )
2141 GtkWidget *check_box = gtk_ui_manager_get_widget ( vw->uim, "/ui/MainMenu/View/FullScreen" );
2142 g_assert(check_box);
2143 gboolean state = gtk_check_menu_item_get_active ( GTK_CHECK_MENU_ITEM(check_box));
2145 gtk_window_fullscreen ( GTK_WINDOW(vw) );
2147 gtk_window_unfullscreen ( GTK_WINDOW(vw) );
2150 static void draw_zoom_cb ( GtkAction *a, VikWindow *vw )
2154 if (!strcmp(gtk_action_get_name(a), "ZoomIn")) {
2157 else if (!strcmp(gtk_action_get_name(a), "ZoomOut")) {
2160 else if (!strcmp(gtk_action_get_name(a), "Zoom0.25")) {
2163 else if (!strcmp(gtk_action_get_name(a), "Zoom0.5")) {
2167 gchar *s = (gchar *)gtk_action_get_name(a);
2173 case -3: vik_viewport_zoom_in ( vw->viking_vvp ); break;
2174 case -4: vik_viewport_zoom_out ( vw->viking_vvp ); break;
2175 case -1: vik_viewport_set_zoom ( vw->viking_vvp, 0.5 ); break;
2176 case -2: vik_viewport_set_zoom ( vw->viking_vvp, 0.25 ); break;
2177 default: vik_viewport_set_zoom ( vw->viking_vvp, what );
2182 static void draw_goto_cb ( GtkAction *a, VikWindow *vw )
2184 VikCoord new_center;
2186 if (!strcmp(gtk_action_get_name(a), "GotoLL")) {
2187 struct LatLon ll, llold;
2188 vik_coord_to_latlon ( vik_viewport_get_center ( vw->viking_vvp ), &llold );
2189 if ( a_dialog_goto_latlon ( GTK_WINDOW(vw), &ll, &llold ) )
2190 vik_coord_load_from_latlon ( &new_center, vik_viewport_get_coord_mode(vw->viking_vvp), &ll );
2194 else if (!strcmp(gtk_action_get_name(a), "GotoUTM")) {
2195 struct UTM utm, utmold;
2196 vik_coord_to_utm ( vik_viewport_get_center ( vw->viking_vvp ), &utmold );
2197 if ( a_dialog_goto_utm ( GTK_WINDOW(vw), &utm, &utmold ) )
2198 vik_coord_load_from_utm ( &new_center, vik_viewport_get_coord_mode(vw->viking_vvp), &utm );
2203 g_critical("Houston, we've had a problem.");
2207 vik_viewport_set_center_coord ( vw->viking_vvp, &new_center, TRUE );
2212 * center_changed_cb:
2214 static void center_changed_cb ( VikWindow *vw )
2216 // ATM Keep back always available, so when we pan - we can jump to the last requested position
2218 GtkAction* action_back = gtk_action_group_get_action ( vw->action_group, "GoBack" );
2219 if ( action_back ) {
2220 gtk_action_set_sensitive ( action_back, vik_viewport_back_available(vw->viking_vvp) );
2223 GtkAction* action_forward = gtk_action_group_get_action ( vw->action_group, "GoForward" );
2224 if ( action_forward ) {
2225 gtk_action_set_sensitive ( action_forward, vik_viewport_forward_available(vw->viking_vvp) );
2230 * draw_goto_back_and_forth:
2232 static void draw_goto_back_and_forth ( GtkAction *a, VikWindow *vw )
2234 gboolean changed = FALSE;
2235 if (!strcmp(gtk_action_get_name(a), "GoBack")) {
2236 changed = vik_viewport_go_back ( vw->viking_vvp );
2238 else if (!strcmp(gtk_action_get_name(a), "GoForward")) {
2239 changed = vik_viewport_go_forward ( vw->viking_vvp );
2245 // Recheck buttons sensitivities, as the center changed signal is not sent on back/forward changes
2246 // (otherwise we would get stuck in an infinite loop!)
2247 center_changed_cb ( vw );
2254 * Refresh maps displayed
2256 static void draw_refresh_cb ( GtkAction *a, VikWindow *vw )
2258 // Only get 'new' maps
2259 simple_map_update ( vw, TRUE );
2262 static void menu_addlayer_cb ( GtkAction *a, VikWindow *vw )
2264 VikLayerTypeEnum type;
2265 for ( type = 0; type < VIK_LAYER_NUM_TYPES; type++ ) {
2266 if (!strcmp(vik_layer_get_interface(type)->name, gtk_action_get_name(a))) {
2267 if ( vik_layers_panel_new_layer ( vw->viking_vlp, type ) ) {
2269 vw->modified = TRUE;
2275 static void menu_copy_layer_cb ( GtkAction *a, VikWindow *vw )
2277 a_clipboard_copy_selected ( vw->viking_vlp );
2280 static void menu_cut_layer_cb ( GtkAction *a, VikWindow *vw )
2282 vik_layers_panel_cut_selected ( vw->viking_vlp );
2283 vw->modified = TRUE;
2286 static void menu_paste_layer_cb ( GtkAction *a, VikWindow *vw )
2288 if ( vik_layers_panel_paste_selected ( vw->viking_vlp ) )
2290 vw->modified = TRUE;
2294 static void menu_properties_cb ( GtkAction *a, VikWindow *vw )
2296 if ( ! vik_layers_panel_properties ( vw->viking_vlp ) )
2297 a_dialog_info_msg ( GTK_WINDOW(vw), _("You must select a layer to show its properties.") );
2300 static void help_help_cb ( GtkAction *a, VikWindow *vw )
2303 ShellExecute(NULL, "open", ""PACKAGE".pdf", NULL, NULL, SW_SHOWNORMAL);
2306 uri = g_strdup_printf("ghelp:%s", PACKAGE);
2307 GError *error = NULL;
2308 gboolean show = gtk_show_uri (NULL, uri, GDK_CURRENT_TIME, &error);
2309 if ( !show && !error )
2310 // No error to show, so unlikely this will get called
2311 a_dialog_error_msg ( GTK_WINDOW(vw), _("The help system is not available.") );
2314 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 );
2315 g_error_free ( error );
2318 #endif /* WINDOWS */
2321 static void help_about_cb ( GtkAction *a, VikWindow *vw )
2323 a_dialog_about(GTK_WINDOW(vw));
2326 static void menu_delete_layer_cb ( GtkAction *a, VikWindow *vw )
2328 if ( vik_layers_panel_get_selected ( vw->viking_vlp ) )
2330 vik_layers_panel_delete_selected ( vw->viking_vlp );
2331 vw->modified = TRUE;
2334 a_dialog_info_msg ( GTK_WINDOW(vw), _("You must select a layer to delete.") );
2337 static void view_side_panel_cb ( GtkAction *a, VikWindow *vw )
2339 GtkWidget *check_box = gtk_ui_manager_get_widget ( vw->uim, "/ui/MainMenu/View/SetShow/ViewSidePanel" );
2340 g_assert(check_box);
2341 gboolean state = gtk_check_menu_item_get_active ( GTK_CHECK_MENU_ITEM(check_box));
2343 gtk_widget_show(GTK_WIDGET(vw->viking_vlp));
2345 gtk_widget_hide(GTK_WIDGET(vw->viking_vlp));
2348 static void view_statusbar_cb ( GtkAction *a, VikWindow *vw )
2350 GtkWidget *check_box = gtk_ui_manager_get_widget ( vw->uim, "/ui/MainMenu/View/SetShow/ViewStatusBar" );
2353 gboolean state = gtk_check_menu_item_get_active ( GTK_CHECK_MENU_ITEM(check_box) );
2355 gtk_widget_show ( GTK_WIDGET(vw->viking_vs) );
2357 gtk_widget_hide ( GTK_WIDGET(vw->viking_vs) );
2360 static void view_toolbar_cb ( GtkAction *a, VikWindow *vw )
2362 GtkWidget *check_box = gtk_ui_manager_get_widget ( vw->uim, "/ui/MainMenu/View/SetShow/ViewToolbar" );
2365 gboolean state = gtk_check_menu_item_get_active ( GTK_CHECK_MENU_ITEM(check_box) );
2367 gtk_widget_show ( GTK_WIDGET(vw->toolbar) );
2369 gtk_widget_hide ( GTK_WIDGET(vw->toolbar) );
2372 static void view_main_menu_cb ( GtkAction *a, VikWindow *vw )
2374 GtkWidget *check_box = gtk_ui_manager_get_widget ( vw->uim, "/ui/MainMenu/View/SetShow/ViewMainMenu" );
2377 gboolean state = gtk_check_menu_item_get_active ( GTK_CHECK_MENU_ITEM(check_box) );
2379 gtk_widget_hide ( gtk_ui_manager_get_widget ( vw->uim, "/ui/MainMenu" ) );
2381 gtk_widget_show ( gtk_ui_manager_get_widget ( vw->uim, "/ui/MainMenu" ) );
2384 /***************************************
2385 ** tool management routines
2387 ***************************************/
2389 static toolbox_tools_t* toolbox_create(VikWindow *vw)
2391 toolbox_tools_t *vt = g_new(toolbox_tools_t, 1);
2394 vt->active_tool = -1;
2399 static void toolbox_add_tool(toolbox_tools_t *vt, VikToolInterface *vti, gint layer_type )
2401 vt->tools = g_renew(toolbox_tool_t, vt->tools, vt->n_tools+1);
2402 vt->tools[vt->n_tools].ti = *vti;
2403 vt->tools[vt->n_tools].layer_type = layer_type;
2405 vt->tools[vt->n_tools].state = vti->create(vt->vw, vt->vw->viking_vvp);
2408 vt->tools[vt->n_tools].state = NULL;
2413 static int toolbox_get_tool(toolbox_tools_t *vt, const gchar *tool_name)
2416 for (i=0; i<vt->n_tools; i++) {
2417 if (!strcmp(tool_name, vt->tools[i].ti.radioActionEntry.name)) {
2424 static void toolbox_activate(toolbox_tools_t *vt, const gchar *tool_name)
2426 int tool = toolbox_get_tool(vt, tool_name);
2427 toolbox_tool_t *t = &vt->tools[tool];
2428 VikLayer *vl = vik_layers_panel_get_selected ( vt->vw->viking_vlp );
2430 if (tool == vt->n_tools) {
2431 g_critical("trying to activate a non-existent tool...");
2434 /* is the tool already active? */
2435 if (vt->active_tool == tool) {
2439 if (vt->active_tool != -1) {
2440 if (vt->tools[vt->active_tool].ti.deactivate) {
2441 vt->tools[vt->active_tool].ti.deactivate(NULL, vt->tools[vt->active_tool].state);
2444 if (t->ti.activate) {
2445 t->ti.activate(vl, t->state);
2447 vt->active_tool = tool;
2450 static const GdkCursor *toolbox_get_cursor(toolbox_tools_t *vt, const gchar *tool_name)
2452 int tool = toolbox_get_tool(vt, tool_name);
2453 toolbox_tool_t *t = &vt->tools[tool];
2454 if (t->ti.cursor == NULL) {
2455 if (t->ti.cursor_type == GDK_CURSOR_IS_PIXMAP && t->ti.cursor_data != NULL) {
2456 GError *cursor_load_err = NULL;
2457 GdkPixbuf *cursor_pixbuf = gdk_pixbuf_from_pixdata (t->ti.cursor_data, FALSE, &cursor_load_err);
2458 /* TODO: settable offeset */
2459 t->ti.cursor = gdk_cursor_new_from_pixbuf ( gdk_display_get_default(), cursor_pixbuf, 3, 3 );
2460 g_object_unref ( G_OBJECT(cursor_pixbuf) );
2462 t->ti.cursor = gdk_cursor_new ( t->ti.cursor_type );
2465 return t->ti.cursor;
2468 static void toolbox_click (toolbox_tools_t *vt, GdkEventButton *event)
2470 VikLayer *vl = vik_layers_panel_get_selected ( vt->vw->viking_vlp );
2471 if (vt->active_tool != -1 && vt->tools[vt->active_tool].ti.click) {
2472 gint ltype = vt->tools[vt->active_tool].layer_type;
2473 if ( ltype == TOOL_LAYER_TYPE_NONE || (vl && ltype == vl->type) )
2474 vt->tools[vt->active_tool].ti.click(vl, event, vt->tools[vt->active_tool].state);
2478 static void toolbox_move (toolbox_tools_t *vt, GdkEventMotion *event)
2480 VikLayer *vl = vik_layers_panel_get_selected ( vt->vw->viking_vlp );
2481 if (vt->active_tool != -1 && vt->tools[vt->active_tool].ti.move) {
2482 gint ltype = vt->tools[vt->active_tool].layer_type;
2483 if ( ltype == TOOL_LAYER_TYPE_NONE || (vl && ltype == vl->type) )
2484 if ( VIK_LAYER_TOOL_ACK_GRAB_FOCUS == vt->tools[vt->active_tool].ti.move(vl, event, vt->tools[vt->active_tool].state) )
2485 gtk_widget_grab_focus ( GTK_WIDGET(vt->vw->viking_vvp) );
2489 static void toolbox_release (toolbox_tools_t *vt, GdkEventButton *event)
2491 VikLayer *vl = vik_layers_panel_get_selected ( vt->vw->viking_vlp );
2492 if (vt->active_tool != -1 && vt->tools[vt->active_tool].ti.release ) {
2493 gint ltype = vt->tools[vt->active_tool].layer_type;
2494 if ( ltype == TOOL_LAYER_TYPE_NONE || (vl && ltype == vl->type) )
2495 vt->tools[vt->active_tool].ti.release(vl, event, vt->tools[vt->active_tool].state);
2498 /** End tool management ************************************/
2500 void vik_window_enable_layer_tool ( VikWindow *vw, gint layer_id, gint tool_id )
2502 gtk_action_activate ( gtk_action_group_get_action ( vw->action_group, vik_layer_get_interface(layer_id)->tools[tool_id].radioActionEntry.name ) );
2505 /* this function gets called whenever a toolbar tool is clicked */
2506 static void menu_tool_cb ( GtkAction *old, GtkAction *a, VikWindow *vw )
2508 /* White Magic, my friends ... White Magic... */
2510 toolbox_activate(vw->vt, gtk_action_get_name(a));
2512 vw->viewport_cursor = (GdkCursor *)toolbox_get_cursor(vw->vt, gtk_action_get_name(a));
2514 if ( gtk_widget_get_window(GTK_WIDGET(vw->viking_vvp)) )
2515 /* We set cursor, even if it is NULL: it resets to default */
2516 gdk_window_set_cursor ( gtk_widget_get_window(GTK_WIDGET(vw->viking_vvp)), vw->viewport_cursor );
2518 if (!strcmp(gtk_action_get_name(a), "Pan")) {
2519 vw->current_tool = TOOL_PAN;
2521 else if (!strcmp(gtk_action_get_name(a), "Zoom")) {
2522 vw->current_tool = TOOL_ZOOM;
2524 else if (!strcmp(gtk_action_get_name(a), "Ruler")) {
2525 vw->current_tool = TOOL_RULER;
2527 else if (!strcmp(gtk_action_get_name(a), "Select")) {
2528 vw->current_tool = TOOL_SELECT;
2531 VikLayerTypeEnum layer_id;
2532 for (layer_id=0; layer_id<VIK_LAYER_NUM_TYPES; layer_id++) {
2533 for ( tool_id = 0; tool_id < vik_layer_get_interface(layer_id)->tools_count; tool_id++ ) {
2534 if (!strcmp(vik_layer_get_interface(layer_id)->tools[tool_id].radioActionEntry.name, gtk_action_get_name(a))) {
2535 vw->current_tool = TOOL_LAYER;
2536 vw->tool_layer_id = layer_id;
2537 vw->tool_tool_id = tool_id;
2542 draw_status_tool ( vw );
2545 static void window_set_filename ( VikWindow *vw, const gchar *filename )
2550 g_free ( vw->filename );
2551 if ( filename == NULL )
2553 vw->filename = NULL;
2557 vw->filename = g_strdup(filename);
2560 /* Refresh window's title */
2561 file = window_get_filename ( vw );
2562 title = g_strdup_printf( "%s - Viking", file );
2563 gtk_window_set_title ( GTK_WINDOW(vw), title );
2567 static const gchar *window_get_filename ( VikWindow *vw )
2569 return vw->filename ? a_file_basename ( vw->filename ) : _("Untitled");
2572 GtkWidget *vik_window_get_drawmode_button ( VikWindow *vw, VikViewportDrawMode mode )
2574 GtkWidget *mode_button;
2577 #ifdef VIK_CONFIG_EXPEDIA
2578 case VIK_VIEWPORT_DRAWMODE_EXPEDIA: buttonname = "/ui/MainMenu/View/ModeExpedia"; break;
2580 case VIK_VIEWPORT_DRAWMODE_MERCATOR: buttonname = "/ui/MainMenu/View/ModeMercator"; break;
2581 case VIK_VIEWPORT_DRAWMODE_LATLON: buttonname = "/ui/MainMenu/View/ModeLatLon"; break;
2582 default: buttonname = "/ui/MainMenu/View/ModeUTM";
2584 mode_button = gtk_ui_manager_get_widget ( vw->uim, buttonname );
2585 g_assert ( mode_button );
2590 * vik_window_get_pan_move:
2591 * @vw: some VikWindow
2593 * Retrieves @vw's pan_move.
2595 * Should be removed as soon as possible.
2597 * Returns: @vw's pan_move
2601 gboolean vik_window_get_pan_move ( VikWindow *vw )
2603 return vw->pan_move;
2606 static void on_activate_recent_item (GtkRecentChooser *chooser,
2611 filename = gtk_recent_chooser_get_current_uri (chooser);
2612 if (filename != NULL)
2614 GFile *file = g_file_new_for_uri ( filename );
2615 gchar *path = g_file_get_path ( file );
2616 g_object_unref ( file );
2617 if ( self->filename )
2619 GSList *filenames = NULL;
2620 filenames = g_slist_append ( filenames, path );
2621 g_signal_emit ( G_OBJECT(self), window_signals[VW_OPENWINDOW_SIGNAL], 0, filenames );
2622 // NB: GSList & contents are freed by main.open_window
2625 vik_window_open_file ( self, path, TRUE );
2633 static void setup_recent_files (VikWindow *self)
2635 GtkRecentManager *manager;
2636 GtkRecentFilter *filter;
2637 GtkWidget *menu, *menu_item;
2639 filter = gtk_recent_filter_new ();
2640 /* gtk_recent_filter_add_application (filter, g_get_application_name()); */
2641 gtk_recent_filter_add_group(filter, "viking");
2643 manager = gtk_recent_manager_get_default ();
2644 menu = gtk_recent_chooser_menu_new_for_manager (manager);
2645 gtk_recent_chooser_set_sort_type (GTK_RECENT_CHOOSER (menu), GTK_RECENT_SORT_MRU);
2646 gtk_recent_chooser_add_filter (GTK_RECENT_CHOOSER (menu), filter);
2647 gtk_recent_chooser_set_limit (GTK_RECENT_CHOOSER (menu), a_vik_get_recent_number_files() );
2649 menu_item = gtk_ui_manager_get_widget (self->uim, "/ui/MainMenu/File/OpenRecentFile");
2650 gtk_menu_item_set_submenu (GTK_MENU_ITEM (menu_item), menu);
2652 g_signal_connect (G_OBJECT (menu), "item-activated",
2653 G_CALLBACK (on_activate_recent_item), (gpointer) self);
2659 static void update_recently_used_document (VikWindow *vw, const gchar *filename)
2661 /* Update Recently Used Document framework */
2662 GtkRecentManager *manager = gtk_recent_manager_get_default();
2663 GtkRecentData *recent_data = g_slice_new (GtkRecentData);
2664 gchar *groups[] = {"viking", NULL};
2665 GFile *file = g_file_new_for_commandline_arg(filename);
2666 gchar *uri = g_file_get_uri(file);
2667 gchar *basename = g_path_get_basename(filename);
2668 g_object_unref(file);
2671 recent_data->display_name = basename;
2672 recent_data->description = NULL;
2673 recent_data->mime_type = "text/x-gps-data";
2674 recent_data->app_name = (gchar *) g_get_application_name ();
2675 recent_data->app_exec = g_strjoin (" ", g_get_prgname (), "%f", NULL);
2676 recent_data->groups = groups;
2677 recent_data->is_private = FALSE;
2678 if (!gtk_recent_manager_add_full (manager, uri, recent_data))
2680 gchar *msg = g_strdup_printf (_("Unable to add '%s' to the list of recently used documents"), uri);
2681 vik_statusbar_set_message ( vw->viking_vs, VIK_STATUSBAR_INFO, msg );
2687 g_free (recent_data->app_exec);
2688 g_slice_free (GtkRecentData, recent_data);
2692 * Call this before doing things that may take a long time and otherwise not show any other feedback
2693 * such as loading and saving files
2695 void vik_window_set_busy_cursor ( VikWindow *vw )
2697 gdk_window_set_cursor ( gtk_widget_get_window(GTK_WIDGET(vw)), vw->busy_cursor );
2698 // Viewport has a separate cursor
2699 gdk_window_set_cursor ( gtk_widget_get_window(GTK_WIDGET(vw->viking_vvp)), vw->busy_cursor );
2700 // Ensure cursor updated before doing stuff
2701 while( gtk_events_pending() )
2702 gtk_main_iteration();
2705 void vik_window_clear_busy_cursor ( VikWindow *vw )
2707 gdk_window_set_cursor ( gtk_widget_get_window(GTK_WIDGET(vw)), NULL );
2708 // Restore viewport cursor
2709 gdk_window_set_cursor ( gtk_widget_get_window(GTK_WIDGET(vw->viking_vvp)), vw->viewport_cursor );
2712 void vik_window_open_file ( VikWindow *vw, const gchar *filename, gboolean change_filename )
2714 vik_window_set_busy_cursor ( vw );
2716 // Enable the *new* filename to be accessible by the Layers codez
2717 gchar *original_filename = g_strdup ( vw->filename );
2718 g_free ( vw->filename );
2719 vw->filename = g_strdup ( filename );
2720 gboolean success = FALSE;
2721 gboolean restore_original_filename = FALSE;
2723 vw->loaded_type = a_file_load ( vik_layers_panel_get_top_layer(vw->viking_vlp), vw->viking_vvp, filename );
2724 switch ( vw->loaded_type )
2726 case LOAD_TYPE_READ_FAILURE:
2727 a_dialog_error_msg ( GTK_WINDOW(vw), _("The file you requested could not be opened.") );
2729 case LOAD_TYPE_GPSBABEL_FAILURE:
2730 a_dialog_error_msg ( GTK_WINDOW(vw), _("GPSBabel is required to load files of this type or GPSBabel encountered problems.") );
2732 case LOAD_TYPE_GPX_FAILURE:
2733 a_dialog_error_msg_extra ( GTK_WINDOW(vw), _("Unable to load malformed GPX file %s"), filename );
2735 case LOAD_TYPE_UNSUPPORTED_FAILURE:
2736 a_dialog_error_msg_extra ( GTK_WINDOW(vw), _("Unsupported file type for %s"), filename );
2738 case LOAD_TYPE_VIK_FAILURE_NON_FATAL:
2740 // Since we can process .vik files with issues just show a warning in the status bar
2741 // Not that a user can do much about it... or tells them what this issue is yet...
2742 gchar *msg = g_strdup_printf (_("WARNING: issues encountered loading %s"), a_file_basename (filename) );
2743 vik_statusbar_set_message ( vw->viking_vs, VIK_STATUSBAR_INFO, msg );
2746 // No break, carry on to show any data
2747 case LOAD_TYPE_VIK_SUCCESS:
2749 restore_original_filename = TRUE; // NB Will actually get inverted by the 'success' component below
2750 GtkWidget *mode_button;
2752 if ( change_filename )
2753 window_set_filename ( vw, filename );
2754 mode_button = vik_window_get_drawmode_button ( vw, vik_viewport_get_drawmode ( vw->viking_vvp ) );
2755 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. */
2756 gtk_check_menu_item_set_active ( GTK_CHECK_MENU_ITEM(mode_button), TRUE );
2757 vw->only_updating_coord_mode_ui = FALSE;
2759 vik_layers_panel_change_coord_mode ( vw->viking_vlp, vik_viewport_get_coord_mode ( vw->viking_vvp ) );
2761 mode_button = gtk_ui_manager_get_widget ( vw->uim, "/ui/MainMenu/View/SetShow/ShowScale" );
2762 g_assert ( mode_button );
2763 gtk_check_menu_item_set_active ( GTK_CHECK_MENU_ITEM(mode_button),vik_viewport_get_draw_scale(vw->viking_vvp) );
2765 mode_button = gtk_ui_manager_get_widget ( vw->uim, "/ui/MainMenu/View/SetShow/ShowCenterMark" );
2766 g_assert ( mode_button );
2767 gtk_check_menu_item_set_active ( GTK_CHECK_MENU_ITEM(mode_button),vik_viewport_get_draw_centermark(vw->viking_vvp) );
2769 mode_button = gtk_ui_manager_get_widget ( vw->uim, "/ui/MainMenu/View/SetShow/ShowHighlight" );
2770 g_assert ( mode_button );
2771 gtk_check_menu_item_set_active ( GTK_CHECK_MENU_ITEM(mode_button),vik_viewport_get_draw_highlight (vw->viking_vvp) );
2773 // NB No break, carry on to redraw
2774 //case LOAD_TYPE_OTHER_SUCCESS:
2777 // When LOAD_TYPE_OTHER_SUCCESS *only*, this will maintain the existing Viking project
2778 restore_original_filename = ! restore_original_filename;
2779 update_recently_used_document (vw, filename);
2784 if ( ! success || restore_original_filename )
2785 // Load didn't work or want to keep as the existing Viking project, keep using the original name
2786 window_set_filename ( vw, original_filename );
2787 g_free ( original_filename );
2789 vik_window_clear_busy_cursor ( vw );
2792 static void load_file ( GtkAction *a, VikWindow *vw )
2794 GSList *files = NULL;
2795 GSList *cur_file = NULL;
2797 if (!strcmp(gtk_action_get_name(a), "Open")) {
2800 else if (!strcmp(gtk_action_get_name(a), "Append")) {
2804 g_critical("Houston, we've had a problem.");
2808 if ( ! vw->open_dia )
2810 vw->open_dia = gtk_file_chooser_dialog_new (_("Please select a GPS data file to open. "),
2812 GTK_FILE_CHOOSER_ACTION_OPEN,
2813 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
2814 GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT,
2816 gchar *cwd = g_get_current_dir();
2818 gtk_file_chooser_set_current_folder ( GTK_FILE_CHOOSER(vw->open_dia), cwd );
2822 GtkFileFilter *filter;
2823 // NB file filters are listed this way for alphabetical ordering
2824 #ifdef VIK_CONFIG_GEOCACHES
2825 filter = gtk_file_filter_new ();
2826 gtk_file_filter_set_name( filter, _("Geocaching") );
2827 gtk_file_filter_add_pattern ( filter, "*.loc" ); // No MIME type available
2828 gtk_file_chooser_add_filter (GTK_FILE_CHOOSER(vw->open_dia), filter);
2831 filter = gtk_file_filter_new ();
2832 gtk_file_filter_set_name( filter, _("Google Earth") );
2833 gtk_file_filter_add_mime_type ( filter, "application/vnd.google-earth.kml+xml");
2834 gtk_file_chooser_add_filter (GTK_FILE_CHOOSER(vw->open_dia), filter);
2836 filter = gtk_file_filter_new ();
2837 gtk_file_filter_set_name( filter, _("GPX") );
2838 gtk_file_filter_add_pattern ( filter, "*.gpx" ); // No MIME type available
2839 gtk_file_chooser_add_filter (GTK_FILE_CHOOSER(vw->open_dia), filter);
2841 filter = gtk_file_filter_new ();
2842 gtk_file_filter_set_name ( filter, _("JPG") );
2843 gtk_file_filter_add_mime_type ( filter, "image/jpeg");
2844 gtk_file_chooser_add_filter (GTK_FILE_CHOOSER(vw->open_dia), filter);
2846 filter = gtk_file_filter_new ();
2847 gtk_file_filter_set_name( filter, _("Viking") );
2848 gtk_file_filter_add_pattern ( filter, "*.vik" );
2849 gtk_file_filter_add_pattern ( filter, "*.viking" );
2850 gtk_file_chooser_add_filter (GTK_FILE_CHOOSER(vw->open_dia), filter);
2852 // NB could have filters for gpspoint (*.gps,*.gpsoint?) + gpsmapper (*.gsm,*.gpsmapper?)
2853 // However assume this are barely used and thus not worthy of inclusion
2854 // as they'll just make the options too many and have no clear file pattern
2855 // one can always use the all option
2856 filter = gtk_file_filter_new ();
2857 gtk_file_filter_set_name( filter, _("All") );
2858 gtk_file_filter_add_pattern ( filter, "*" );
2859 gtk_file_chooser_add_filter (GTK_FILE_CHOOSER(vw->open_dia), filter);
2860 // Default to any file - same as before open filters were added
2861 gtk_file_chooser_set_filter (GTK_FILE_CHOOSER(vw->open_dia), filter);
2863 gtk_file_chooser_set_select_multiple ( GTK_FILE_CHOOSER(vw->open_dia), TRUE );
2864 gtk_window_set_transient_for ( GTK_WINDOW(vw->open_dia), GTK_WINDOW(vw) );
2865 gtk_window_set_destroy_with_parent ( GTK_WINDOW(vw->open_dia), TRUE );
2867 if ( gtk_dialog_run ( GTK_DIALOG(vw->open_dia) ) == GTK_RESPONSE_ACCEPT )
2869 gtk_widget_hide ( vw->open_dia );
2870 #ifdef VIKING_PROMPT_IF_MODIFIED
2871 if ( (vw->modified || vw->filename) && newwindow )
2873 if ( vw->filename && newwindow )
2875 g_signal_emit ( G_OBJECT(vw), window_signals[VW_OPENWINDOW_SIGNAL], 0, gtk_file_chooser_get_filenames (GTK_FILE_CHOOSER(vw->open_dia) ) );
2877 files = gtk_file_chooser_get_filenames (GTK_FILE_CHOOSER(vw->open_dia) );
2878 gboolean change_fn = newwindow && (g_slist_length(files)==1); /* only change fn if one file */
2879 gboolean first_vik_file = TRUE;
2881 while ( cur_file ) {
2883 gchar *file_name = cur_file->data;
2884 if ( newwindow && check_file_magic_vik ( file_name ) ) {
2885 // Load first of many .vik files in current window
2886 if ( first_vik_file ) {
2887 vik_window_open_file ( vw, file_name, TRUE );
2888 first_vik_file = FALSE;
2891 // Load each subsequent .vik file in a separate window
2892 VikWindow *newvw = vik_window_new_window ();
2894 vik_window_open_file ( newvw, file_name, TRUE );
2899 vik_window_open_file ( vw, file_name, change_fn );
2902 cur_file = g_slist_next (cur_file);
2904 g_slist_free (files);
2908 gtk_widget_hide ( vw->open_dia );
2911 static gboolean save_file_as ( GtkAction *a, VikWindow *vw )
2913 gboolean rv = FALSE;
2915 if ( ! vw->save_dia )
2917 vw->save_dia = gtk_file_chooser_dialog_new (_("Save as Viking File."),
2919 GTK_FILE_CHOOSER_ACTION_SAVE,
2920 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
2921 GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT,
2923 gchar *cwd = g_get_current_dir();
2925 gtk_file_chooser_set_current_folder ( GTK_FILE_CHOOSER(vw->save_dia), cwd );
2929 GtkFileFilter *filter;
2930 filter = gtk_file_filter_new ();
2931 gtk_file_filter_set_name( filter, _("All") );
2932 gtk_file_filter_add_pattern ( filter, "*" );
2933 gtk_file_chooser_add_filter (GTK_FILE_CHOOSER(vw->save_dia), filter);
2935 filter = gtk_file_filter_new ();
2936 gtk_file_filter_set_name( filter, _("Viking") );
2937 gtk_file_filter_add_pattern ( filter, "*.vik" );
2938 gtk_file_filter_add_pattern ( filter, "*.viking" );
2939 gtk_file_chooser_add_filter (GTK_FILE_CHOOSER(vw->save_dia), filter);
2940 // Default to a Viking file
2941 gtk_file_chooser_set_filter (GTK_FILE_CHOOSER(vw->save_dia), filter);
2943 gtk_window_set_transient_for ( GTK_WINDOW(vw->save_dia), GTK_WINDOW(vw) );
2944 gtk_window_set_destroy_with_parent ( GTK_WINDOW(vw->save_dia), TRUE );
2946 // Auto append / replace extension with '.vik' to the suggested file name as it's going to be a Viking File
2947 gchar* auto_save_name = g_strdup ( window_get_filename ( vw ) );
2948 if ( ! a_file_check_ext ( auto_save_name, ".vik" ) )
2949 auto_save_name = g_strconcat ( auto_save_name, ".vik", NULL );
2951 gtk_file_chooser_set_current_name (GTK_FILE_CHOOSER(vw->save_dia), auto_save_name);
2953 while ( gtk_dialog_run ( GTK_DIALOG(vw->save_dia) ) == GTK_RESPONSE_ACCEPT )
2955 fn = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER(vw->save_dia) );
2956 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 ) ) )
2958 window_set_filename ( vw, fn );
2959 rv = window_save ( vw );
2960 vw->modified = FALSE;
2964 g_free ( auto_save_name );
2965 gtk_widget_hide ( vw->save_dia );
2969 static gboolean window_save ( VikWindow *vw )
2971 vik_window_set_busy_cursor ( vw );
2972 gboolean success = TRUE;
2974 if ( a_file_save ( vik_layers_panel_get_top_layer ( vw->viking_vlp ), vw->viking_vvp, vw->filename ) )
2976 update_recently_used_document ( vw, vw->filename );
2980 a_dialog_error_msg ( GTK_WINDOW(vw), _("The filename you requested could not be opened for writing.") );
2983 vik_window_clear_busy_cursor ( vw );
2987 static gboolean save_file ( GtkAction *a, VikWindow *vw )
2989 if ( ! vw->filename )
2990 return save_file_as ( NULL, vw );
2993 vw->modified = FALSE;
2994 return window_save ( vw );
3001 * Export all TRW Layers in the list to individual files in the specified directory
3003 * Returns: %TRUE on success
3005 static gboolean export_to ( VikWindow *vw, GList *gl, VikFileType_t vft, const gchar *dir, const gchar *extension )
3007 gboolean success = TRUE;
3009 gint export_count = 0;
3011 vik_window_set_busy_cursor ( vw );
3015 gchar *fn = g_strconcat ( dir, G_DIR_SEPARATOR_S, VIK_LAYER(gl->data)->name, extension, NULL );
3017 // Some protection in attempting to write too many same named files
3018 // As this will get horribly slow...
3019 gboolean safe = FALSE;
3021 while ( ii < 5000 ) {
3022 if ( g_file_test ( fn, G_FILE_TEST_EXISTS ) ) {
3025 fn = g_strdup_printf ( "%s%s%s#%03d%s", dir, G_DIR_SEPARATOR_S, VIK_LAYER(gl->data)->name, ii, extension );
3036 // NB: We allow exporting empty layers
3038 gboolean this_success = a_file_export ( VIK_TRW_LAYER(gl->data), fn, vft, NULL, TRUE );
3040 // Show some progress
3041 if ( this_success ) {
3043 gchar *message = g_strdup_printf ( _("Exporting to file: %s"), fn );
3044 vik_statusbar_set_message ( vw->viking_vs, VIK_STATUSBAR_INFO, message );
3045 while ( gtk_events_pending() )
3046 gtk_main_iteration ();
3050 success = success && this_success;
3054 gl = g_list_next ( gl );
3057 vik_window_clear_busy_cursor ( vw );
3059 // Confirm what happened.
3060 gchar *message = g_strdup_printf ( _("Exported files: %d"), export_count );
3061 vik_statusbar_set_message ( vw->viking_vs, VIK_STATUSBAR_INFO, message );
3067 static void export_to_common ( VikWindow *vw, VikFileType_t vft, const gchar *extension )
3069 GList *gl = vik_layers_panel_get_all_layers_of_type ( vw->viking_vlp, VIK_LAYER_TRW, TRUE );
3072 a_dialog_info_msg ( GTK_WINDOW(vw), _("Nothing to Export!") );
3076 GtkWidget *dialog = gtk_file_chooser_dialog_new ( _("Export to directory"),
3078 GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER,
3080 GTK_RESPONSE_REJECT,
3082 GTK_RESPONSE_ACCEPT,
3084 gtk_window_set_transient_for ( GTK_WINDOW(dialog), GTK_WINDOW(vw) );
3085 gtk_window_set_destroy_with_parent ( GTK_WINDOW(dialog), TRUE );
3086 gtk_window_set_modal ( GTK_WINDOW(dialog), TRUE );
3088 gtk_widget_show_all ( dialog );
3090 if ( gtk_dialog_run ( GTK_DIALOG(dialog) ) == GTK_RESPONSE_ACCEPT ) {
3091 gchar *dir = gtk_file_chooser_get_filename ( GTK_FILE_CHOOSER(dialog) );
3092 gtk_widget_destroy ( dialog );
3094 if ( !export_to ( vw, gl, vft, dir, extension ) )
3095 a_dialog_error_msg ( GTK_WINDOW(vw),_("Could not convert all files") );
3100 gtk_widget_destroy ( dialog );
3105 static void export_to_gpx ( GtkAction *a, VikWindow *vw )
3107 export_to_common ( vw, FILE_TYPE_GPX, ".gpx" );
3110 static void export_to_kml ( GtkAction *a, VikWindow *vw )
3112 export_to_common ( vw, FILE_TYPE_KML, ".kml" );
3115 #if !GLIB_CHECK_VERSION(2,26,0)
3116 typedef struct stat GStatBuf;
3119 static void file_properties_cb ( GtkAction *a, VikWindow *vw )
3121 gchar *message = NULL;
3122 if ( vw->filename ) {
3123 if ( g_file_test ( vw->filename, G_FILE_TEST_EXISTS ) ) {
3124 // Get some timestamp information of the file
3126 if ( g_stat ( vw->filename, &stat_buf ) == 0 ) {
3128 strftime ( time_buf, sizeof(time_buf), "%c", gmtime((const time_t *)&stat_buf.st_mtime) );
3130 gint byte_size = stat_buf.st_size;
3131 // See http://en.wikipedia.org/wiki/Megabyte (and Kilobyte)
3132 // hence using 1000 rather than 1024
3133 // so get output as per 'ls' or the Gtk file open dialog
3134 if ( byte_size < 1000 )
3135 size = g_strdup_printf ( _("%d bytes"), byte_size );
3136 else if ( byte_size < 1000*1000 )
3137 size = g_strdup_printf ( _("%3.1f kB"), (gdouble)byte_size / 1000 );
3139 size = g_strdup_printf ( _("%3.1f MB"), (gdouble)byte_size / (1000*1000) );
3140 message = g_strdup_printf ( _("%s\n\n%s\n\n%s"), vw->filename, time_buf, size );
3145 message = g_strdup ( _("File not accessible") );
3148 message = g_strdup ( _("No Viking File") );
3151 a_dialog_info_msg ( GTK_WINDOW(vw), message );
3155 static void my_acquire ( VikWindow *vw, VikDataSourceInterface *datasource )
3157 vik_datasource_mode_t mode = datasource->mode;
3158 if ( mode == VIK_DATASOURCE_AUTO_LAYER_MANAGEMENT )
3159 mode = VIK_DATASOURCE_CREATENEWLAYER;
3160 a_acquire ( vw, vw->viking_vlp, vw->viking_vvp, mode, datasource, NULL, NULL );
3163 static void acquire_from_gps ( GtkAction *a, VikWindow *vw )
3165 my_acquire ( vw, &vik_datasource_gps_interface );
3168 static void acquire_from_file ( GtkAction *a, VikWindow *vw )
3170 my_acquire ( vw, &vik_datasource_file_interface );
3173 static void acquire_from_geojson ( GtkAction *a, VikWindow *vw )
3175 my_acquire ( vw, &vik_datasource_geojson_interface );
3178 static void acquire_from_routing ( GtkAction *a, VikWindow *vw )
3180 my_acquire ( vw, &vik_datasource_routing_interface );
3183 #ifdef VIK_CONFIG_OPENSTREETMAP
3184 static void acquire_from_osm ( GtkAction *a, VikWindow *vw )
3186 my_acquire ( vw, &vik_datasource_osm_interface );
3189 static void acquire_from_my_osm ( GtkAction *a, VikWindow *vw )
3191 my_acquire ( vw, &vik_datasource_osm_my_traces_interface );
3195 #ifdef VIK_CONFIG_GEOCACHES
3196 static void acquire_from_gc ( GtkAction *a, VikWindow *vw )
3198 my_acquire ( vw, &vik_datasource_gc_interface );
3202 #ifdef VIK_CONFIG_GEOTAG
3203 static void acquire_from_geotag ( GtkAction *a, VikWindow *vw )
3205 my_acquire ( vw, &vik_datasource_geotag_interface );
3209 #ifdef VIK_CONFIG_GEONAMES
3210 static void acquire_from_wikipedia ( GtkAction *a, VikWindow *vw )
3212 my_acquire ( vw, &vik_datasource_wikipedia_interface );
3216 static void acquire_from_url ( GtkAction *a, VikWindow *vw )
3218 my_acquire ( vw, &vik_datasource_url_interface );
3221 static void goto_default_location( GtkAction *a, VikWindow *vw)
3224 ll.lat = a_vik_get_default_lat();
3225 ll.lon = a_vik_get_default_long();
3226 vik_viewport_set_center_latlon(vw->viking_vvp, &ll, TRUE);
3227 vik_layers_panel_emit_update(vw->viking_vlp);
3231 static void goto_address( GtkAction *a, VikWindow *vw)
3233 a_vik_goto ( vw, vw->viking_vvp );
3234 vik_layers_panel_emit_update ( vw->viking_vlp );
3237 static void mapcache_flush_cb ( GtkAction *a, VikWindow *vw )
3242 static void layer_defaults_cb ( GtkAction *a, VikWindow *vw )
3244 gchar **texts = g_strsplit ( gtk_action_get_name(a), "Layer", 0 );
3247 return; // Internally broken :(
3249 if ( ! a_layer_defaults_show_window ( GTK_WINDOW(vw), texts[1] ) )
3250 a_dialog_info_msg ( GTK_WINDOW(vw), _("This layer has no configurable properties.") );
3251 // NB no update needed
3253 g_strfreev ( texts );
3256 static void preferences_change_update ( VikWindow *vw, gpointer data )
3258 // Want to update all TrackWaypoint layers
3259 GList *layers = vik_layers_panel_get_all_layers_of_type ( vw->viking_vlp, VIK_LAYER_TRW, TRUE );
3265 // Reset the individual waypoints themselves due to the preferences change
3266 VikTrwLayer *vtl = VIK_TRW_LAYER(layers->data);
3267 vik_trw_layer_reset_waypoints ( vtl );
3268 layers = g_list_next ( layers );
3271 g_list_free ( layers );
3276 static void preferences_cb ( GtkAction *a, VikWindow *vw )
3278 gboolean wp_icon_size = a_vik_get_use_large_waypoint_icons();
3280 a_preferences_show_window ( GTK_WINDOW(vw) );
3282 // Has the waypoint size setting changed?
3283 if (wp_icon_size != a_vik_get_use_large_waypoint_icons()) {
3284 // Delete icon indexing 'cache' and so automatically regenerates with the new setting when changed
3285 clear_garmin_icon_syms ();
3287 // Update all windows
3288 g_slist_foreach ( window_list, (GFunc) preferences_change_update, NULL );
3292 static void default_location_cb ( GtkAction *a, VikWindow *vw )
3294 /* Simplistic repeat of preference setting
3295 Only the name & type are important for setting the preference via this 'external' way */
3296 VikLayerParam pref_lat[] = {
3297 { VIK_LAYER_NUM_TYPES,
3298 VIKING_PREFERENCES_NAMESPACE "default_latitude",
3299 VIK_LAYER_PARAM_DOUBLE,
3302 VIK_LAYER_WIDGET_SPINBUTTON,
3311 VikLayerParam pref_lon[] = {
3312 { VIK_LAYER_NUM_TYPES,
3313 VIKING_PREFERENCES_NAMESPACE "default_longitude",
3314 VIK_LAYER_PARAM_DOUBLE,
3317 VIK_LAYER_WIDGET_SPINBUTTON,
3327 /* Get current center */
3329 vik_coord_to_latlon ( vik_viewport_get_center ( vw->viking_vvp ), &ll );
3331 /* Apply to preferences */
3332 VikLayerParamData vlp_data;
3333 vlp_data.d = ll.lat;
3334 a_preferences_run_setparam (vlp_data, pref_lat);
3335 vlp_data.d = ll.lon;
3336 a_preferences_run_setparam (vlp_data, pref_lon);
3337 /* Remember to save */
3338 a_preferences_save_to_file();
3341 static void clear_cb ( GtkAction *a, VikWindow *vw )
3343 vik_layers_panel_clear ( vw->viking_vlp );
3344 window_set_filename ( vw, NULL );
3348 static void window_close ( GtkAction *a, VikWindow *vw )
3350 if ( ! delete_event ( vw ) )
3351 gtk_widget_destroy ( GTK_WIDGET(vw) );
3354 static gboolean save_file_and_exit ( GtkAction *a, VikWindow *vw )
3356 if (save_file( NULL, vw)) {
3357 window_close( NULL, vw);
3364 static void zoom_to_cb ( GtkAction *a, VikWindow *vw )
3366 gdouble xmpp = vik_viewport_get_xmpp ( vw->viking_vvp ), ympp = vik_viewport_get_ympp ( vw->viking_vvp );
3367 if ( a_dialog_custom_zoom ( GTK_WINDOW(vw), &xmpp, &ympp ) )
3369 vik_viewport_set_xmpp ( vw->viking_vvp, xmpp );
3370 vik_viewport_set_ympp ( vw->viking_vvp, ympp );
3375 static void save_image_file ( VikWindow *vw, const gchar *fn, guint w, guint h, gdouble zoom, gboolean save_as_png )
3377 /* more efficient way: stuff draws directly to pixbuf (fork viewport) */
3378 GdkPixbuf *pixbuf_to_save;
3379 gdouble old_xmpp, old_ympp;
3380 GError *error = NULL;
3382 GtkWidget *msgbox = gtk_message_dialog_new ( GTK_WINDOW(vw),
3383 GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
3386 _("Generating image file...") );
3388 g_signal_connect_swapped (msgbox, "response", G_CALLBACK (gtk_widget_destroy), msgbox);
3389 // Ensure dialog shown
3390 gtk_widget_show_all ( msgbox );
3392 vik_statusbar_set_message ( vw->viking_vs, VIK_STATUSBAR_INFO, _("Generating image file...") );
3393 while ( gtk_events_pending() )
3394 gtk_main_iteration ();
3395 // Despite many efforts & variations, GTK on my Linux system doesn't show the actual msgbox contents :(
3396 // At least the empty box can give a clue something's going on + the statusbar msg...
3397 // Windows version under Wine OK!
3399 /* backup old zoom & set new */
3400 old_xmpp = vik_viewport_get_xmpp ( vw->viking_vvp );
3401 old_ympp = vik_viewport_get_ympp ( vw->viking_vvp );
3402 vik_viewport_set_zoom ( vw->viking_vvp, zoom );
3404 /* reset width and height: */
3405 vik_viewport_configure_manually ( vw->viking_vvp, w, h );
3407 /* draw all layers */
3410 /* save buffer as file. */
3411 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);
3412 if ( !pixbuf_to_save ) {
3413 g_warning("Failed to generate internal pixmap size: %d x %d", w, h);
3414 gtk_message_dialog_set_markup ( GTK_MESSAGE_DIALOG(msgbox), _("Failed to generate internal image.\n\nTry creating a smaller image.") );
3418 gdk_pixbuf_save ( pixbuf_to_save, fn, save_as_png ? "png" : "jpeg", &error, NULL );
3421 g_warning("Unable to write to file %s: %s", fn, error->message );
3422 gtk_message_dialog_set_markup ( GTK_MESSAGE_DIALOG(msgbox), _("Failed to generate image file.") );
3423 g_error_free (error);
3427 gtk_message_dialog_set_markup ( GTK_MESSAGE_DIALOG(msgbox), _("Image file generated.") );
3429 g_object_unref ( G_OBJECT(pixbuf_to_save) );
3432 vik_statusbar_set_message ( vw->viking_vs, VIK_STATUSBAR_INFO, "" );
3433 gtk_dialog_add_button ( GTK_DIALOG(msgbox), GTK_STOCK_OK, GTK_RESPONSE_OK );
3434 gtk_dialog_run ( GTK_DIALOG(msgbox) ); // Don't care about the result
3436 /* pretend like nothing happened ;) */
3437 vik_viewport_set_xmpp ( vw->viking_vvp, old_xmpp );
3438 vik_viewport_set_ympp ( vw->viking_vvp, old_ympp );
3439 vik_viewport_configure ( vw->viking_vvp );
3443 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 )
3445 gulong size = sizeof(gchar) * (strlen(fn) + 15);
3446 gchar *name_of_file = g_malloc ( size );
3448 struct UTM utm_orig, utm;
3450 /* *** copied from above *** */
3451 GdkPixbuf *pixbuf_to_save;
3452 gdouble old_xmpp, old_ympp;
3453 GError *error = NULL;
3455 /* backup old zoom & set new */
3456 old_xmpp = vik_viewport_get_xmpp ( vw->viking_vvp );
3457 old_ympp = vik_viewport_get_ympp ( vw->viking_vvp );
3458 vik_viewport_set_zoom ( vw->viking_vvp, zoom );
3460 /* reset width and height: do this only once for all images (same size) */
3461 vik_viewport_configure_manually ( vw->viking_vvp, w, h );
3462 /* *** end copy from above *** */
3464 g_assert ( vik_viewport_get_coord_mode ( vw->viking_vvp ) == VIK_COORD_UTM );
3468 utm_orig = *((const struct UTM *)vik_viewport_get_center ( vw->viking_vvp ));
3470 for ( y = 1; y <= tiles_h; y++ )
3472 for ( x = 1; x <= tiles_w; x++ )
3474 g_snprintf ( name_of_file, size, "%s%cy%d-x%d.%s", fn, G_DIR_SEPARATOR, y, x, save_as_png ? "png" : "jpg" );
3476 if ( tiles_w & 0x1 )
3477 utm.easting += ((gdouble)x - ceil(((gdouble)tiles_w)/2)) * (w*zoom);
3479 utm.easting += ((gdouble)x - (((gdouble)tiles_w)+1)/2) * (w*zoom);
3480 if ( tiles_h & 0x1 ) /* odd */
3481 utm.northing -= ((gdouble)y - ceil(((gdouble)tiles_h)/2)) * (h*zoom);
3483 utm.northing -= ((gdouble)y - (((gdouble)tiles_h)+1)/2) * (h*zoom);
3485 /* move to correct place. */
3486 vik_viewport_set_center_utm ( vw->viking_vvp, &utm, FALSE );
3490 /* save buffer as file. */
3491 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);
3492 gdk_pixbuf_save ( pixbuf_to_save, name_of_file, save_as_png ? "png" : "jpeg", &error, NULL );
3495 gchar *msg = g_strdup_printf (_("Unable to write to file %s: %s"), name_of_file, error->message );
3496 vik_statusbar_set_message ( vw->viking_vs, VIK_STATUSBAR_INFO, msg );
3498 g_error_free (error);
3501 g_object_unref ( G_OBJECT(pixbuf_to_save) );
3505 vik_viewport_set_center_utm ( vw->viking_vvp, &utm_orig, FALSE );
3506 vik_viewport_set_xmpp ( vw->viking_vvp, old_xmpp );
3507 vik_viewport_set_ympp ( vw->viking_vvp, old_ympp );
3508 vik_viewport_configure ( vw->viking_vvp );
3511 g_free ( name_of_file );
3514 static void draw_to_image_file_current_window_cb(GtkWidget* widget,GdkEventButton *event,gpointer *pass_along)
3516 VikWindow *vw = VIK_WINDOW(pass_along[0]);
3517 GtkSpinButton *width_spin = GTK_SPIN_BUTTON(pass_along[1]), *height_spin = GTK_SPIN_BUTTON(pass_along[2]);
3519 gint active = gtk_combo_box_get_active ( GTK_COMBO_BOX(pass_along[3]) );
3520 gdouble zoom = pow (2, active-2 );
3522 gdouble width_min, width_max, height_min, height_max;
3525 gtk_spin_button_get_range ( width_spin, &width_min, &width_max );
3526 gtk_spin_button_get_range ( height_spin, &height_min, &height_max );
3528 /* TODO: support for xzoom and yzoom values */
3529 width = vik_viewport_get_width ( vw->viking_vvp ) * vik_viewport_get_xmpp ( vw->viking_vvp ) / zoom;
3530 height = vik_viewport_get_height ( vw->viking_vvp ) * vik_viewport_get_xmpp ( vw->viking_vvp ) / zoom;
3532 if ( width > width_max || width < width_min || height > height_max || height < height_min )
3533 a_dialog_info_msg ( GTK_WINDOW(vw), _("Viewable region outside allowable pixel size bounds for image. Clipping width/height values.") );
3535 gtk_spin_button_set_value ( width_spin, width );
3536 gtk_spin_button_set_value ( height_spin, height );
3539 static void draw_to_image_file_total_area_cb (GtkSpinButton *spinbutton, gpointer *pass_along)
3541 GtkSpinButton *width_spin = GTK_SPIN_BUTTON(pass_along[1]), *height_spin = GTK_SPIN_BUTTON(pass_along[2]);
3543 gint active = gtk_combo_box_get_active ( GTK_COMBO_BOX(pass_along[3]) );
3544 gdouble zoom = pow (2, active-2 );
3548 w = gtk_spin_button_get_value(width_spin) * zoom;
3549 h = gtk_spin_button_get_value(height_spin) * zoom;
3550 if (pass_along[4]) /* save many images; find TOTAL area covered */
3552 w *= gtk_spin_button_get_value(GTK_SPIN_BUTTON(pass_along[4]));
3553 h *= gtk_spin_button_get_value(GTK_SPIN_BUTTON(pass_along[5]));
3555 vik_units_distance_t dist_units = a_vik_get_units_distance ();
3556 switch (dist_units) {
3557 case VIK_UNITS_DISTANCE_KILOMETRES:
3558 label_text = g_strdup_printf ( _("Total area: %ldm x %ldm (%.3f sq. km)"), (glong)w, (glong)h, (w*h/1000000));
3560 case VIK_UNITS_DISTANCE_MILES:
3561 label_text = g_strdup_printf ( _("Total area: %ldm x %ldm (%.3f sq. miles)"), (glong)w, (glong)h, (w*h/2589988.11));
3564 label_text = g_strdup_printf ("Just to keep the compiler happy");
3565 g_critical("Houston, we've had a problem. distance=%d", dist_units);
3568 gtk_label_set_text(GTK_LABEL(pass_along[6]), label_text);
3569 g_free ( label_text );
3573 * Get an allocated filename (or directory as specified)
3575 static gchar* draw_image_filename ( VikWindow *vw, gboolean one_image_only )
3578 if ( one_image_only )
3581 if (!vw->save_img_dia) {
3582 vw->save_img_dia = gtk_file_chooser_dialog_new (_("Save Image"),
3584 GTK_FILE_CHOOSER_ACTION_SAVE,
3585 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
3586 GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT,
3589 gchar *cwd = g_get_current_dir();
3591 gtk_file_chooser_set_current_folder ( GTK_FILE_CHOOSER(vw->save_img_dia), cwd );
3595 GtkFileChooser *chooser = GTK_FILE_CHOOSER ( vw->save_img_dia );
3597 GtkFileFilter *filter;
3598 filter = gtk_file_filter_new ();
3599 gtk_file_filter_set_name ( filter, _("All") );
3600 gtk_file_filter_add_pattern ( filter, "*" );
3601 gtk_file_chooser_add_filter ( chooser, filter );
3603 filter = gtk_file_filter_new ();
3604 gtk_file_filter_set_name ( filter, _("JPG") );
3605 gtk_file_filter_add_mime_type ( filter, "image/jpeg");
3606 gtk_file_chooser_add_filter ( chooser, filter );
3608 if ( !vw->draw_image_save_as_png )
3609 gtk_file_chooser_set_filter ( chooser, filter );
3611 filter = gtk_file_filter_new ();
3612 gtk_file_filter_set_name ( filter, _("PNG") );
3613 gtk_file_filter_add_mime_type ( filter, "image/png");
3614 gtk_file_chooser_add_filter ( chooser, filter );
3616 if ( vw->draw_image_save_as_png )
3617 gtk_file_chooser_set_filter ( chooser, filter );
3619 gtk_window_set_transient_for ( GTK_WINDOW(vw->save_img_dia), GTK_WINDOW(vw) );
3620 gtk_window_set_destroy_with_parent ( GTK_WINDOW(vw->save_img_dia), TRUE );
3623 if ( gtk_dialog_run ( GTK_DIALOG(vw->save_img_dia) ) == GTK_RESPONSE_ACCEPT ) {
3624 fn = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER(vw->save_img_dia) );
3625 if ( g_file_test ( fn, G_FILE_TEST_EXISTS ) )
3626 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 ) ) )
3629 gtk_widget_hide ( vw->save_img_dia );
3633 // For some reason this method is only written to work in UTM...
3634 if ( vik_viewport_get_coord_mode(vw->viking_vvp) != VIK_COORD_UTM ) {
3635 a_dialog_error_msg ( GTK_WINDOW(vw), _("You must be in UTM mode to use this feature") );
3639 if (!vw->save_img_dir_dia) {
3640 vw->save_img_dir_dia = gtk_file_chooser_dialog_new (_("Choose a directory to hold images"),
3642 GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER,
3643 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
3644 GTK_STOCK_OK, GTK_RESPONSE_ACCEPT,
3646 gtk_window_set_transient_for ( GTK_WINDOW(vw->save_img_dir_dia), GTK_WINDOW(vw) );
3647 gtk_window_set_destroy_with_parent ( GTK_WINDOW(vw->save_img_dir_dia), TRUE );
3650 if ( gtk_dialog_run ( GTK_DIALOG(vw->save_img_dir_dia) ) == GTK_RESPONSE_ACCEPT ) {
3651 fn = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER(vw->save_img_dir_dia) );
3653 gtk_widget_hide ( vw->save_img_dir_dia );
3658 static void draw_to_image_file ( VikWindow *vw, gboolean one_image_only )
3660 /* todo: default for answers inside VikWindow or static (thruout instance) */
3661 GtkWidget *dialog = gtk_dialog_new_with_buttons ( _("Save to Image File"), GTK_WINDOW(vw),
3662 GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
3664 GTK_RESPONSE_REJECT,
3666 GTK_RESPONSE_ACCEPT,
3668 GtkWidget *width_label, *width_spin, *height_label, *height_spin;
3669 GtkWidget *png_radio, *jpeg_radio;
3670 GtkWidget *current_window_button;
3671 gpointer current_window_pass_along[7];
3672 GtkWidget *zoom_label, *zoom_combo;
3673 GtkWidget *total_size_label;
3675 /* only used if (!one_image_only) */
3676 GtkWidget *tiles_width_spin = NULL, *tiles_height_spin = NULL;
3678 width_label = gtk_label_new ( _("Width (pixels):") );
3679 width_spin = gtk_spin_button_new ( GTK_ADJUSTMENT(gtk_adjustment_new ( vw->draw_image_width, 10, 50000, 10, 100, 0 )), 10, 0 );
3680 height_label = gtk_label_new ( _("Height (pixels):") );
3681 height_spin = gtk_spin_button_new ( GTK_ADJUSTMENT(gtk_adjustment_new ( vw->draw_image_height, 10, 50000, 10, 100, 0 )), 10, 0 );
3683 GtkWidget *win_warning_label = gtk_label_new ( _("WARNING: USING LARGE IMAGES OVER 10000x10000\nMAY CRASH THE PROGRAM!") );
3685 zoom_label = gtk_label_new ( _("Zoom (meters per pixel):") );
3686 /* TODO: separate xzoom and yzoom factors */
3687 zoom_combo = create_zoom_combo_all_levels();
3689 gdouble mpp = vik_viewport_get_xmpp(vw->viking_vvp);
3690 gint active = 2 + round ( log (mpp) / log (2) );
3692 // Can we not hard code size here?
3697 gtk_combo_box_set_active ( GTK_COMBO_BOX(zoom_combo), active );
3699 total_size_label = gtk_label_new ( NULL );
3701 current_window_button = gtk_button_new_with_label ( _("Area in current viewable window") );
3702 current_window_pass_along [0] = vw;
3703 current_window_pass_along [1] = width_spin;
3704 current_window_pass_along [2] = height_spin;
3705 current_window_pass_along [3] = zoom_combo;
3706 current_window_pass_along [4] = NULL; /* used for one_image_only != 1 */
3707 current_window_pass_along [5] = NULL;
3708 current_window_pass_along [6] = total_size_label;
3709 g_signal_connect ( G_OBJECT(current_window_button), "button_press_event", G_CALLBACK(draw_to_image_file_current_window_cb), current_window_pass_along );
3711 png_radio = gtk_radio_button_new_with_label ( NULL, _("Save as PNG") );
3712 jpeg_radio = gtk_radio_button_new_with_label_from_widget ( GTK_RADIO_BUTTON(png_radio), _("Save as JPEG") );
3714 gtk_box_pack_start (GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), png_radio, FALSE, FALSE, 0);
3715 gtk_box_pack_start (GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), jpeg_radio, FALSE, FALSE, 0);
3717 if ( ! vw->draw_image_save_as_png )
3718 gtk_toggle_button_set_active ( GTK_TOGGLE_BUTTON(jpeg_radio), TRUE );
3720 gtk_box_pack_start (GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), width_label, FALSE, FALSE, 0);
3721 gtk_box_pack_start (GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), width_spin, FALSE, FALSE, 0);
3722 gtk_box_pack_start (GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), height_label, FALSE, FALSE, 0);
3723 gtk_box_pack_start (GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), height_spin, FALSE, FALSE, 0);
3725 gtk_box_pack_start (GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), win_warning_label, FALSE, FALSE, 0);
3727 gtk_box_pack_start (GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), current_window_button, FALSE, FALSE, 0);
3728 gtk_box_pack_start (GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), zoom_label, FALSE, FALSE, 0);
3729 gtk_box_pack_start (GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), zoom_combo, FALSE, FALSE, 0);
3731 if ( ! one_image_only )
3733 GtkWidget *tiles_width_label, *tiles_height_label;
3735 tiles_width_label = gtk_label_new ( _("East-west image tiles:") );
3736 tiles_width_spin = gtk_spin_button_new ( GTK_ADJUSTMENT(gtk_adjustment_new ( 5, 1, 10, 1, 100, 0 )), 1, 0 );
3737 tiles_height_label = gtk_label_new ( _("North-south image tiles:") );
3738 tiles_height_spin = gtk_spin_button_new ( GTK_ADJUSTMENT(gtk_adjustment_new ( 5, 1, 10, 1, 100, 0 )), 1, 0 );
3739 gtk_box_pack_start (GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), tiles_width_label, FALSE, FALSE, 0);
3740 gtk_box_pack_start (GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), tiles_width_spin, FALSE, FALSE, 0);
3741 gtk_box_pack_start (GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), tiles_height_label, FALSE, FALSE, 0);
3742 gtk_box_pack_start (GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), tiles_height_spin, FALSE, FALSE, 0);
3744 current_window_pass_along [4] = tiles_width_spin;
3745 current_window_pass_along [5] = tiles_height_spin;
3746 g_signal_connect ( G_OBJECT(tiles_width_spin), "value-changed", G_CALLBACK(draw_to_image_file_total_area_cb), current_window_pass_along );
3747 g_signal_connect ( G_OBJECT(tiles_height_spin), "value-changed", G_CALLBACK(draw_to_image_file_total_area_cb), current_window_pass_along );
3749 gtk_box_pack_start (GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), total_size_label, FALSE, FALSE, 0);
3750 g_signal_connect ( G_OBJECT(width_spin), "value-changed", G_CALLBACK(draw_to_image_file_total_area_cb), current_window_pass_along );
3751 g_signal_connect ( G_OBJECT(height_spin), "value-changed", G_CALLBACK(draw_to_image_file_total_area_cb), current_window_pass_along );
3752 g_signal_connect ( G_OBJECT(zoom_combo), "changed", G_CALLBACK(draw_to_image_file_total_area_cb), current_window_pass_along );
3754 draw_to_image_file_total_area_cb ( NULL, current_window_pass_along ); /* set correct size info now */
3756 gtk_dialog_set_default_response ( GTK_DIALOG(dialog), GTK_RESPONSE_ACCEPT );
3758 gtk_widget_show_all ( gtk_dialog_get_content_area(GTK_DIALOG(dialog)) );
3760 if ( gtk_dialog_run ( GTK_DIALOG(dialog) ) == GTK_RESPONSE_ACCEPT )
3762 gtk_widget_hide ( GTK_WIDGET(dialog) );
3764 gchar *fn = draw_image_filename ( vw, one_image_only );
3768 gint active_z = gtk_combo_box_get_active ( GTK_COMBO_BOX(zoom_combo) );
3769 gdouble zoom = pow (2, active_z-2 );
3771 if ( one_image_only )
3772 save_image_file ( vw, fn,
3773 vw->draw_image_width = gtk_spin_button_get_value_as_int ( GTK_SPIN_BUTTON(width_spin) ),
3774 vw->draw_image_height = gtk_spin_button_get_value_as_int ( GTK_SPIN_BUTTON(height_spin) ),
3776 vw->draw_image_save_as_png = gtk_toggle_button_get_active ( GTK_TOGGLE_BUTTON(png_radio) ) );
3778 // NB is in UTM mode ATM
3779 save_image_dir ( vw, fn,
3780 vw->draw_image_width = gtk_spin_button_get_value_as_int ( GTK_SPIN_BUTTON(width_spin) ),
3781 vw->draw_image_height = gtk_spin_button_get_value_as_int ( GTK_SPIN_BUTTON(height_spin) ),
3783 vw->draw_image_save_as_png = gtk_toggle_button_get_active ( GTK_TOGGLE_BUTTON(png_radio) ),
3784 gtk_spin_button_get_value ( GTK_SPIN_BUTTON(tiles_width_spin) ),
3785 gtk_spin_button_get_value ( GTK_SPIN_BUTTON(tiles_height_spin) ) );
3790 gtk_widget_destroy ( GTK_WIDGET(dialog) );
3794 static void draw_to_image_file_cb ( GtkAction *a, VikWindow *vw )
3796 draw_to_image_file ( vw, TRUE );
3799 static void draw_to_image_dir_cb ( GtkAction *a, VikWindow *vw )
3801 draw_to_image_file ( vw, FALSE );
3804 static void print_cb ( GtkAction *a, VikWindow *vw )
3806 a_print(vw, vw->viking_vvp);
3809 /* really a misnomer: changes coord mode (actual coordinates) AND/OR draw mode (viewport only) */
3810 static void window_change_coord_mode_cb ( GtkAction *old_a, GtkAction *a, VikWindow *vw )
3812 VikViewportDrawMode drawmode;
3813 if (!strcmp(gtk_action_get_name(a), "ModeUTM")) {
3814 drawmode = VIK_VIEWPORT_DRAWMODE_UTM;
3816 else if (!strcmp(gtk_action_get_name(a), "ModeLatLon")) {
3817 drawmode = VIK_VIEWPORT_DRAWMODE_LATLON;
3819 else if (!strcmp(gtk_action_get_name(a), "ModeExpedia")) {
3820 drawmode = VIK_VIEWPORT_DRAWMODE_EXPEDIA;
3822 else if (!strcmp(gtk_action_get_name(a), "ModeMercator")) {
3823 drawmode = VIK_VIEWPORT_DRAWMODE_MERCATOR;
3826 g_critical("Houston, we've had a problem.");
3830 if ( !vw->only_updating_coord_mode_ui )
3832 VikViewportDrawMode olddrawmode = vik_viewport_get_drawmode ( vw->viking_vvp );
3833 if ( olddrawmode != drawmode )
3835 /* this takes care of coord mode too */
3836 vik_viewport_set_drawmode ( vw->viking_vvp, drawmode );
3837 if ( drawmode == VIK_VIEWPORT_DRAWMODE_UTM ) {
3838 vik_layers_panel_change_coord_mode ( vw->viking_vlp, VIK_COORD_UTM );
3839 } else if ( olddrawmode == VIK_VIEWPORT_DRAWMODE_UTM ) {
3840 vik_layers_panel_change_coord_mode ( vw->viking_vlp, VIK_COORD_LATLON );
3847 static void set_draw_scale ( GtkAction *a, VikWindow *vw )
3849 GtkWidget *check_box = gtk_ui_manager_get_widget ( vw->uim, "/ui/MainMenu/View/SetShow/ShowScale" );
3850 g_assert(check_box);
3851 gboolean state = gtk_check_menu_item_get_active ( GTK_CHECK_MENU_ITEM(check_box));
3852 vik_viewport_set_draw_scale ( vw->viking_vvp, state );
3856 static void set_draw_centermark ( GtkAction *a, VikWindow *vw )
3858 GtkWidget *check_box = gtk_ui_manager_get_widget ( vw->uim, "/ui/MainMenu/View/SetShow/ShowCenterMark" );
3859 g_assert(check_box);
3860 gboolean state = gtk_check_menu_item_get_active ( GTK_CHECK_MENU_ITEM(check_box));
3861 vik_viewport_set_draw_centermark ( vw->viking_vvp, state );
3865 static void set_draw_highlight ( GtkAction *a, VikWindow *vw )
3867 GtkWidget *check_box = gtk_ui_manager_get_widget ( vw->uim, "/ui/MainMenu/View/SetShow/ShowHighlight" );
3868 g_assert(check_box);
3869 gboolean state = gtk_check_menu_item_get_active ( GTK_CHECK_MENU_ITEM(check_box));
3870 vik_viewport_set_draw_highlight ( vw->viking_vvp, state );
3874 static void set_bg_color ( GtkAction *a, VikWindow *vw )
3876 GtkWidget *colorsd = gtk_color_selection_dialog_new ( _("Choose a background color") );
3877 GdkColor *color = vik_viewport_get_background_gdkcolor ( vw->viking_vvp );
3878 gtk_color_selection_set_previous_color ( GTK_COLOR_SELECTION(gtk_color_selection_dialog_get_color_selection(GTK_COLOR_SELECTION_DIALOG(colorsd))), color );
3879 gtk_color_selection_set_current_color ( GTK_COLOR_SELECTION(gtk_color_selection_dialog_get_color_selection(GTK_COLOR_SELECTION_DIALOG(colorsd))), color );
3880 if ( gtk_dialog_run ( GTK_DIALOG(colorsd) ) == GTK_RESPONSE_OK )
3882 gtk_color_selection_get_current_color ( GTK_COLOR_SELECTION(gtk_color_selection_dialog_get_color_selection(GTK_COLOR_SELECTION_DIALOG(colorsd))), color );
3883 vik_viewport_set_background_gdkcolor ( vw->viking_vvp, color );
3887 gtk_widget_destroy ( colorsd );
3890 static void set_highlight_color ( GtkAction *a, VikWindow *vw )
3892 GtkWidget *colorsd = gtk_color_selection_dialog_new ( _("Choose a track highlight color") );
3893 GdkColor *color = vik_viewport_get_highlight_gdkcolor ( vw->viking_vvp );
3894 gtk_color_selection_set_previous_color ( GTK_COLOR_SELECTION(gtk_color_selection_dialog_get_color_selection(GTK_COLOR_SELECTION_DIALOG(colorsd))), color );
3895 gtk_color_selection_set_current_color ( GTK_COLOR_SELECTION(gtk_color_selection_dialog_get_color_selection(GTK_COLOR_SELECTION_DIALOG(colorsd))), color );
3896 if ( gtk_dialog_run ( GTK_DIALOG(colorsd) ) == GTK_RESPONSE_OK )
3898 gtk_color_selection_get_current_color ( GTK_COLOR_SELECTION(gtk_color_selection_dialog_get_color_selection(GTK_COLOR_SELECTION_DIALOG(colorsd))), color );
3899 vik_viewport_set_highlight_gdkcolor ( vw->viking_vvp, color );
3903 gtk_widget_destroy ( colorsd );
3908 /***********************************************************************************************
3910 ***********************************************************************************************/
3912 static GtkActionEntry entries[] = {
3913 { "File", NULL, N_("_File"), 0, 0, 0 },
3914 { "Edit", NULL, N_("_Edit"), 0, 0, 0 },
3915 { "View", NULL, N_("_View"), 0, 0, 0 },
3916 { "SetShow", NULL, N_("_Show"), 0, 0, 0 },
3917 { "SetZoom", NULL, N_("_Zoom"), 0, 0, 0 },
3918 { "SetPan", NULL, N_("_Pan"), 0, 0, 0 },
3919 { "Layers", NULL, N_("_Layers"), 0, 0, 0 },
3920 { "Tools", NULL, N_("_Tools"), 0, 0, 0 },
3921 { "Exttools", NULL, N_("_Webtools"), 0, 0, 0 },
3922 { "Help", NULL, N_("_Help"), 0, 0, 0 },
3924 { "New", GTK_STOCK_NEW, N_("_New"), "<control>N", N_("New file"), (GCallback)newwindow_cb },
3925 { "Open", GTK_STOCK_OPEN, N_("_Open..."), "<control>O", N_("Open a file"), (GCallback)load_file },
3926 { "OpenRecentFile", NULL, N_("Open _Recent File"), NULL, NULL, (GCallback)NULL },
3927 { "Append", GTK_STOCK_ADD, N_("Append _File..."), NULL, N_("Append data from a different file"), (GCallback)load_file },
3928 { "Export", GTK_STOCK_CONVERT, N_("_Export All"), NULL, N_("Export All TrackWaypoint Layers"), (GCallback)NULL },
3929 { "ExportGPX", NULL, N_("_GPX..."), NULL, N_("Export as GPX"), (GCallback)export_to_gpx },
3930 { "Acquire", GTK_STOCK_GO_DOWN, N_("A_cquire"), NULL, NULL, (GCallback)NULL },
3931 { "AcquireGPS", NULL, N_("From _GPS..."), NULL, N_("Transfer data from a GPS device"), (GCallback)acquire_from_gps },
3932 { "AcquireGPSBabel", NULL, N_("Import File With GPS_Babel..."), NULL, N_("Import file via GPSBabel converter"), (GCallback)acquire_from_file },
3933 { "AcquireRouting", NULL, N_("_Directions..."), NULL, N_("Get driving directions"), (GCallback)acquire_from_routing },
3934 #ifdef VIK_CONFIG_OPENSTREETMAP
3935 { "AcquireOSM", NULL, N_("_OSM Traces..."), NULL, N_("Get traces from OpenStreetMap"), (GCallback)acquire_from_osm },
3936 { "AcquireMyOSM", NULL, N_("_My OSM Traces..."), NULL, N_("Get Your Own Traces from OpenStreetMap"), (GCallback)acquire_from_my_osm },
3938 #ifdef VIK_CONFIG_GEOCACHES
3939 { "AcquireGC", NULL, N_("Geo_caches..."), NULL, N_("Get Geocaches from geocaching.com"), (GCallback)acquire_from_gc },
3941 #ifdef VIK_CONFIG_GEOTAG
3942 { "AcquireGeotag", NULL, N_("From Geotagged _Images..."), NULL, N_("Create waypoints from geotagged images"), (GCallback)acquire_from_geotag },
3944 { "AcquireURL", NULL, N_("From _URL..."), NULL, N_("Get a file from a URL"), (GCallback)acquire_from_url },
3945 #ifdef VIK_CONFIG_GEONAMES
3946 { "AcquireWikipedia", NULL, N_("From _Wikipedia Waypoints"), NULL, N_("Create waypoints from Wikipedia items in the current view"), (GCallback)acquire_from_wikipedia },
3948 { "Save", GTK_STOCK_SAVE, N_("_Save"), "<control>S", N_("Save the file"), (GCallback)save_file },
3949 { "SaveAs", GTK_STOCK_SAVE_AS, N_("Save _As..."), NULL, N_("Save the file under different name"), (GCallback)save_file_as },
3950 { "FileProperties", NULL, N_("Properties..."), NULL, N_("File Properties"), (GCallback)file_properties_cb },
3951 { "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 },
3952 { "GenImgDir", GTK_STOCK_DND_MULTIPLE, N_("Generate _Directory of Images..."), NULL, N_("FIXME:IMGDIR"), (GCallback)draw_to_image_dir_cb },
3953 { "Print", GTK_STOCK_PRINT, N_("_Print..."), NULL, N_("Print maps"), (GCallback)print_cb },
3954 { "Exit", GTK_STOCK_QUIT, N_("E_xit"), "<control>W", N_("Exit the program"), (GCallback)window_close },
3955 { "SaveExit", GTK_STOCK_QUIT, N_("Save and Exit"), NULL, N_("Save and Exit the program"), (GCallback)save_file_and_exit },
3957 { "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 },
3958 { "GoForward", GTK_STOCK_GO_FORWARD, N_("Go to the _Next Location"), NULL, N_("Go to the next location"), (GCallback)draw_goto_back_and_forth },
3959 { "GotoDefaultLocation", GTK_STOCK_HOME, N_("Go to the _Default Location"), NULL, N_("Go to the default location"), (GCallback)goto_default_location },
3960 { "GotoSearch", GTK_STOCK_JUMP_TO, N_("Go to _Location..."), NULL, N_("Go to address/place using text search"), (GCallback)goto_address },
3961 { "GotoLL", GTK_STOCK_JUMP_TO, N_("_Go to Lat/Lon..."), NULL, N_("Go to arbitrary lat/lon coordinate"), (GCallback)draw_goto_cb },
3962 { "GotoUTM", GTK_STOCK_JUMP_TO, N_("Go to UTM..."), NULL, N_("Go to arbitrary UTM coordinate"), (GCallback)draw_goto_cb },
3963 { "Refresh", GTK_STOCK_REFRESH, N_("_Refresh"), "F5", N_("Refresh any maps displayed"), (GCallback)draw_refresh_cb },
3964 { "SetHLColor",GTK_STOCK_SELECT_COLOR, N_("Set _Highlight Color..."), NULL, NULL, (GCallback)set_highlight_color },
3965 { "SetBGColor",GTK_STOCK_SELECT_COLOR, N_("Set Bac_kground Color..."), NULL, NULL, (GCallback)set_bg_color },
3966 { "ZoomIn", GTK_STOCK_ZOOM_IN, N_("Zoom _In"), "<control>plus", NULL, (GCallback)draw_zoom_cb },
3967 { "ZoomOut", GTK_STOCK_ZOOM_OUT, N_("Zoom _Out"), "<control>minus", NULL, (GCallback)draw_zoom_cb },
3968 { "ZoomTo", GTK_STOCK_ZOOM_FIT, N_("Zoom _To..."), "<control>Z", NULL, (GCallback)zoom_to_cb },
3969 { "PanNorth", NULL, N_("Pan _North"), "<control>Up", NULL, (GCallback)draw_pan_cb },
3970 { "PanEast", NULL, N_("Pan _East"), "<control>Right", NULL, (GCallback)draw_pan_cb },
3971 { "PanSouth", NULL, N_("Pan _South"), "<control>Down", NULL, (GCallback)draw_pan_cb },
3972 { "PanWest", NULL, N_("Pan _West"), "<control>Left", NULL, (GCallback)draw_pan_cb },
3973 { "BGJobs", GTK_STOCK_EXECUTE, N_("Background _Jobs"), NULL, NULL, (GCallback)a_background_show_window },
3975 { "Cut", GTK_STOCK_CUT, N_("Cu_t"), NULL, NULL, (GCallback)menu_cut_layer_cb },
3976 { "Copy", GTK_STOCK_COPY, N_("_Copy"), NULL, NULL, (GCallback)menu_copy_layer_cb },
3977 { "Paste", GTK_STOCK_PASTE, N_("_Paste"), NULL, NULL, (GCallback)menu_paste_layer_cb },
3978 { "Delete", GTK_STOCK_DELETE, N_("_Delete"), NULL, NULL, (GCallback)menu_delete_layer_cb },
3979 { "DeleteAll", NULL, N_("Delete All"), NULL, NULL, (GCallback)clear_cb },
3980 { "MapCacheFlush",NULL, N_("_Flush Map Cache"), NULL, NULL, (GCallback)mapcache_flush_cb },
3981 { "SetDefaultLocation", GTK_STOCK_GO_FORWARD, N_("_Set the Default Location"), NULL, N_("Set the Default Location to the current position"),(GCallback)default_location_cb },
3982 { "Preferences",GTK_STOCK_PREFERENCES, N_("_Preferences"), NULL, NULL, (GCallback)preferences_cb },
3983 { "LayerDefaults",GTK_STOCK_PROPERTIES, N_("_Layer Defaults"), NULL, NULL, NULL },
3984 { "Properties",GTK_STOCK_PROPERTIES, N_("_Properties"), NULL, NULL, (GCallback)menu_properties_cb },
3986 { "HelpEntry", GTK_STOCK_HELP, N_("_Help"), "F1", NULL, (GCallback)help_help_cb },
3987 { "About", GTK_STOCK_ABOUT, N_("_About"), NULL, NULL, (GCallback)help_about_cb },
3990 static GtkActionEntry entries_gpsbabel[] = {
3991 { "ExportKML", NULL, N_("_KML..."), NULL, N_("Export as KML"), (GCallback)export_to_kml },
3994 static GtkActionEntry entries_geojson[] = {
3995 { "AcquireGeoJSON", NULL, N_("Import Geo_JSON File..."), NULL, N_("Import GeoJSON file"), (GCallback)acquire_from_geojson },
3999 /* FIXME use VIEWPORT_DRAWMODE values */
4000 static GtkRadioActionEntry mode_entries[] = {
4001 { "ModeUTM", NULL, N_("_UTM Mode"), "<control>u", NULL, 0 },
4002 { "ModeExpedia", NULL, N_("_Expedia Mode"), "<control>e", NULL, 1 },
4003 { "ModeMercator", NULL, N_("_Mercator Mode"), "<control>m", NULL, 4 },
4004 { "ModeLatLon", NULL, N_("Lat_/Lon Mode"), "<control>l", NULL, 5 },
4007 static GtkToggleActionEntry toggle_entries[] = {
4008 { "ShowScale", NULL, N_("Show _Scale"), "<shift>F5", N_("Show Scale"), (GCallback)set_draw_scale, TRUE },
4009 { "ShowCenterMark", NULL, N_("Show _Center Mark"), "F6", N_("Show Center Mark"), (GCallback)set_draw_centermark, TRUE },
4010 { "ShowHighlight", GTK_STOCK_UNDERLINE, N_("Show _Highlight"), "F7", N_("Show Highlight"), (GCallback)set_draw_highlight, TRUE },
4011 { "FullScreen", GTK_STOCK_FULLSCREEN, N_("_Full Screen"), "F11", N_("Activate full screen mode"), (GCallback)full_screen_cb, FALSE },
4012 { "ViewSidePanel", GTK_STOCK_INDEX, N_("Show Side _Panel"), "F9", N_("Show Side Panel"), (GCallback)view_side_panel_cb, TRUE },
4013 { "ViewStatusBar", NULL, N_("Show Status_bar"), "F12", N_("Show Statusbar"), (GCallback)view_statusbar_cb, TRUE },
4014 { "ViewToolbar", NULL, N_("Show _Toolbar"), "F3", N_("Show Toolbar"), (GCallback)view_toolbar_cb, TRUE },
4015 { "ViewMainMenu", NULL, N_("Show _Menu"), "F4", N_("Show Menu"), (GCallback)view_main_menu_cb, TRUE },
4018 #include "menu.xml.h"
4019 static void window_create_ui( VikWindow *window )
4022 GtkActionGroup *action_group;
4023 GtkAccelGroup *accel_group;
4026 GtkIconFactory *icon_factory;
4027 GtkIconSet *icon_set;
4028 GtkRadioActionEntry *tools = NULL, *radio;
4031 uim = gtk_ui_manager_new ();
4034 toolbox_add_tool(window->vt, &ruler_tool, TOOL_LAYER_TYPE_NONE);
4035 toolbox_add_tool(window->vt, &zoom_tool, TOOL_LAYER_TYPE_NONE);
4036 toolbox_add_tool(window->vt, &pan_tool, TOOL_LAYER_TYPE_NONE);
4037 toolbox_add_tool(window->vt, &select_tool, TOOL_LAYER_TYPE_NONE);
4040 if (!(mid = gtk_ui_manager_add_ui_from_string (uim, menu_xml, -1, &error))) {
4041 g_error_free (error);
4045 action_group = gtk_action_group_new ("MenuActions");
4046 gtk_action_group_set_translation_domain(action_group, PACKAGE_NAME);
4047 gtk_action_group_add_actions (action_group, entries, G_N_ELEMENTS (entries), window);
4048 gtk_action_group_add_toggle_actions (action_group, toggle_entries, G_N_ELEMENTS (toggle_entries), window);
4049 gtk_action_group_add_radio_actions (action_group, mode_entries, G_N_ELEMENTS (mode_entries), 4, (GCallback)window_change_coord_mode_cb, window);
4051 // Use this to see if GPSBabel is available:
4052 if ( a_babel_available () ) {
4053 // If going to add more entries then might be worth creating a menu_gpsbabel.xml.h file
4054 if ( gtk_ui_manager_add_ui_from_string ( uim,
4055 "<ui><menubar name='MainMenu'><menu action='File'><menu action='Export'><menuitem action='ExportKML'/></menu></menu></menubar></ui>",
4057 gtk_action_group_add_actions ( action_group, entries_gpsbabel, G_N_ELEMENTS (entries_gpsbabel), window );
4060 // GeoJSON import capability
4061 if ( g_find_program_in_path ( a_geojson_program_import() ) ) {
4062 if ( gtk_ui_manager_add_ui_from_string ( uim,
4063 "<ui><menubar name='MainMenu'><menu action='File'><menu action='Acquire'><menuitem action='AcquireGeoJSON'/></menu></menu></menubar></ui>",
4065 gtk_action_group_add_actions ( action_group, entries_geojson, G_N_ELEMENTS (entries_geojson), window );
4068 icon_factory = gtk_icon_factory_new ();
4069 gtk_icon_factory_add_default (icon_factory);
4071 register_vik_icons(icon_factory);
4073 // Copy the tool RadioActionEntries out of the main Window structure into an extending array 'tools'
4074 // so that it can be applied to the UI in one action group add function call below
4076 for (i=0; i<window->vt->n_tools; i++) {
4077 tools = g_renew(GtkRadioActionEntry, tools, ntools+1);
4078 radio = &tools[ntools];
4080 *radio = window->vt->tools[i].ti.radioActionEntry;
4081 radio->value = ntools;
4084 for (i=0; i<VIK_LAYER_NUM_TYPES; i++) {
4085 GtkActionEntry action;
4086 gtk_ui_manager_add_ui(uim, mid, "/ui/MainMenu/Layers/",
4087 vik_layer_get_interface(i)->name,
4088 vik_layer_get_interface(i)->name,
4089 GTK_UI_MANAGER_MENUITEM, FALSE);
4091 icon_set = gtk_icon_set_new_from_pixbuf (gdk_pixbuf_from_pixdata (vik_layer_get_interface(i)->icon, FALSE, NULL ));
4092 gtk_icon_factory_add (icon_factory, vik_layer_get_interface(i)->name, icon_set);
4093 gtk_icon_set_unref (icon_set);
4095 action.name = vik_layer_get_interface(i)->name;
4096 action.stock_id = vik_layer_get_interface(i)->name;
4097 action.label = g_strdup_printf( _("New _%s Layer"), vik_layer_get_interface(i)->name);
4098 action.accelerator = vik_layer_get_interface(i)->accelerator;
4099 action.tooltip = NULL;
4100 action.callback = (GCallback)menu_addlayer_cb;
4101 gtk_action_group_add_actions(action_group, &action, 1, window);
4103 g_free ( (gchar*)action.label );
4105 if ( vik_layer_get_interface(i)->tools_count ) {
4106 gtk_ui_manager_add_ui(uim, mid, "/ui/MainMenu/Tools/", vik_layer_get_interface(i)->name, NULL, GTK_UI_MANAGER_SEPARATOR, FALSE);
4107 gtk_ui_manager_add_ui(uim, mid, "/ui/MainToolbar/ToolItems/", vik_layer_get_interface(i)->name, NULL, GTK_UI_MANAGER_SEPARATOR, FALSE);
4110 // Further tool copying for to apply to the UI, also apply menu UI setup
4111 for ( j = 0; j < vik_layer_get_interface(i)->tools_count; j++ ) {
4112 tools = g_renew(GtkRadioActionEntry, tools, ntools+1);
4113 radio = &tools[ntools];
4116 gtk_ui_manager_add_ui(uim, mid, "/ui/MainMenu/Tools",
4117 vik_layer_get_interface(i)->tools[j].radioActionEntry.label,
4118 vik_layer_get_interface(i)->tools[j].radioActionEntry.name,
4119 GTK_UI_MANAGER_MENUITEM, FALSE);
4120 gtk_ui_manager_add_ui(uim, mid, "/ui/MainToolbar/ToolItems",
4121 vik_layer_get_interface(i)->tools[j].radioActionEntry.label,
4122 vik_layer_get_interface(i)->tools[j].radioActionEntry.name,
4123 GTK_UI_MANAGER_TOOLITEM, FALSE);
4125 toolbox_add_tool(window->vt, &(vik_layer_get_interface(i)->tools[j]), i);
4127 *radio = vik_layer_get_interface(i)->tools[j].radioActionEntry;
4128 // Overwrite with actual number to use
4129 radio->value = ntools;
4132 GtkActionEntry action_dl;
4133 gchar *layername = g_strdup_printf ( "Layer%s", vik_layer_get_interface(i)->fixed_layer_name );
4134 gtk_ui_manager_add_ui(uim, mid, "/ui/MainMenu/Edit/LayerDefaults",
4135 vik_layer_get_interface(i)->name,
4137 GTK_UI_MANAGER_MENUITEM, FALSE);
4140 // For default layers use action names of the form 'Layer<LayerName>'
4141 // This is to avoid clashing with just the layer name used above for the tool actions
4142 action_dl.name = g_strconcat("Layer", vik_layer_get_interface(i)->fixed_layer_name, NULL);
4143 action_dl.stock_id = NULL;
4144 action_dl.label = g_strconcat("_", vik_layer_get_interface(i)->name, "...", NULL); // Prepend marker for keyboard accelerator
4145 action_dl.accelerator = NULL;
4146 action_dl.tooltip = NULL;
4147 action_dl.callback = (GCallback)layer_defaults_cb;
4148 gtk_action_group_add_actions(action_group, &action_dl, 1, window);
4149 g_free ( (gchar*)action_dl.name );
4150 g_free ( (gchar*)action_dl.label );
4152 g_object_unref (icon_factory);
4154 gtk_action_group_add_radio_actions(action_group, tools, ntools, 0, (GCallback)menu_tool_cb, window);
4157 gtk_ui_manager_insert_action_group (uim, action_group, 0);
4159 for (i=0; i<VIK_LAYER_NUM_TYPES; i++) {
4160 for ( j = 0; j < vik_layer_get_interface(i)->tools_count; j++ ) {
4161 GtkAction *action = gtk_action_group_get_action(action_group,
4162 vik_layer_get_interface(i)->tools[j].radioActionEntry.name);
4163 g_object_set(action, "sensitive", FALSE, NULL);
4167 // This is done last so we don't need to track the value of mid anymore
4168 vik_ext_tools_add_action_items ( window, window->uim, action_group, mid );
4170 window->action_group = action_group;
4172 accel_group = gtk_ui_manager_get_accel_group (uim);
4173 gtk_window_add_accel_group (GTK_WINDOW (window), accel_group);
4174 gtk_ui_manager_ensure_update (uim);
4176 setup_recent_files(window);
4180 // TODO - add method to add tool icons defined from outside this file
4181 // and remove the reverse dependency on icon definition from this file
4183 const GdkPixdata *data;
4186 { &mover_22_pixbuf, "vik-icon-pan" },
4187 { &zoom_18_pixbuf, "vik-icon-zoom" },
4188 { &ruler_18_pixbuf, "vik-icon-ruler" },
4189 { &select_18_pixbuf, "vik-icon-select" },
4190 { &vik_new_route_18_pixbuf, "vik-icon-Create Route" },
4191 { &route_finder_18_pixbuf, "vik-icon-Route Finder" },
4192 { &demdl_18_pixbuf, "vik-icon-DEM Download" },
4193 { &showpic_18_pixbuf, "vik-icon-Show Picture" },
4194 { &addtr_18_pixbuf, "vik-icon-Create Track" },
4195 { &edtr_18_pixbuf, "vik-icon-Edit Trackpoint" },
4196 { &addwp_18_pixbuf, "vik-icon-Create Waypoint" },
4197 { &edwp_18_pixbuf, "vik-icon-Edit Waypoint" },
4198 { &geozoom_18_pixbuf, "vik-icon-Georef Zoom Tool" },
4199 { &geomove_18_pixbuf, "vik-icon-Georef Move Map" },
4200 { &mapdl_18_pixbuf, "vik-icon-Maps Download" },
4203 static gint n_stock_icons = G_N_ELEMENTS (stock_icons);
4206 register_vik_icons (GtkIconFactory *icon_factory)
4208 GtkIconSet *icon_set;
4211 for (i = 0; i < n_stock_icons; i++) {
4212 icon_set = gtk_icon_set_new_from_pixbuf (gdk_pixbuf_from_pixdata (
4213 stock_icons[i].data, FALSE, NULL ));
4214 gtk_icon_factory_add (icon_factory, stock_icons[i].stock_id, icon_set);
4215 gtk_icon_set_unref (icon_set);
4219 gpointer vik_window_get_selected_trw_layer ( VikWindow *vw )
4221 return vw->selected_vtl;
4224 void vik_window_set_selected_trw_layer ( VikWindow *vw, gpointer vtl )
4226 vw->selected_vtl = vtl;
4227 vw->containing_vtl = vtl;
4229 vw->selected_track = NULL;
4230 vw->selected_tracks = NULL;
4231 vw->selected_waypoint = NULL;
4232 vw->selected_waypoints = NULL;
4233 // Set highlight thickness
4234 vik_viewport_set_highlight_thickness ( vw->viking_vvp, vik_trw_layer_get_property_tracks_line_thickness (vw->containing_vtl) );
4237 GHashTable *vik_window_get_selected_tracks ( VikWindow *vw )
4239 return vw->selected_tracks;
4242 void vik_window_set_selected_tracks ( VikWindow *vw, GHashTable *ght, gpointer vtl )
4244 vw->selected_tracks = ght;
4245 vw->containing_vtl = vtl;
4247 vw->selected_vtl = NULL;
4248 vw->selected_track = NULL;
4249 vw->selected_waypoint = NULL;
4250 vw->selected_waypoints = NULL;
4251 // Set highlight thickness
4252 vik_viewport_set_highlight_thickness ( vw->viking_vvp, vik_trw_layer_get_property_tracks_line_thickness (vw->containing_vtl) );
4255 gpointer vik_window_get_selected_track ( VikWindow *vw )
4257 return vw->selected_track;
4260 void vik_window_set_selected_track ( VikWindow *vw, gpointer *vt, gpointer vtl )
4262 vw->selected_track = vt;
4263 vw->containing_vtl = vtl;
4265 vw->selected_vtl = NULL;
4266 vw->selected_tracks = NULL;
4267 vw->selected_waypoint = NULL;
4268 vw->selected_waypoints = NULL;
4269 // Set highlight thickness
4270 vik_viewport_set_highlight_thickness ( vw->viking_vvp, vik_trw_layer_get_property_tracks_line_thickness (vw->containing_vtl) );
4273 GHashTable *vik_window_get_selected_waypoints ( VikWindow *vw )
4275 return vw->selected_waypoints;
4278 void vik_window_set_selected_waypoints ( VikWindow *vw, GHashTable *ght, gpointer vtl )
4280 vw->selected_waypoints = ght;
4281 vw->containing_vtl = vtl;
4283 vw->selected_vtl = NULL;
4284 vw->selected_track = NULL;
4285 vw->selected_tracks = NULL;
4286 vw->selected_waypoint = NULL;
4289 gpointer vik_window_get_selected_waypoint ( VikWindow *vw )
4291 return vw->selected_waypoint;
4294 void vik_window_set_selected_waypoint ( VikWindow *vw, gpointer *vwp, gpointer vtl )
4296 vw->selected_waypoint = vwp;
4297 vw->containing_vtl = vtl;
4299 vw->selected_vtl = NULL;
4300 vw->selected_track = NULL;
4301 vw->selected_tracks = NULL;
4302 vw->selected_waypoints = NULL;
4305 gboolean vik_window_clear_highlight ( VikWindow *vw )
4307 gboolean need_redraw = FALSE;
4308 if ( vw->selected_vtl != NULL ) {
4309 vw->selected_vtl = NULL;
4312 if ( vw->selected_track != NULL ) {
4313 vw->selected_track = NULL;
4316 if ( vw->selected_tracks != NULL ) {
4317 vw->selected_tracks = NULL;
4320 if ( vw->selected_waypoint != NULL ) {
4321 vw->selected_waypoint = NULL;
4324 if ( vw->selected_waypoints != NULL ) {
4325 vw->selected_waypoints = NULL;
4331 GThread *vik_window_get_thread ( VikWindow *vw )