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"
36 #include "preferences.h"
37 #include "viklayer_defaults.h"
38 #include "icons/icons.h"
39 #include "vikexttools.h"
40 #include "vikexttool_datasources.h"
41 #include "garminsymbols.h"
42 #include "vikmapslayer.h"
43 #include "geonamessearch.h"
56 #include <glib/gstdio.h>
57 #include <glib/gprintf.h>
58 #include <glib/gi18n.h>
60 #include <gdk/gdkkeysyms.h>
62 // This seems rather arbitary, quite large and pointless
63 // I mean, if you have a thousand windows open;
64 // why not be allowed to open a thousand more...
65 #define MAX_WINDOWS 1024
66 static guint window_count = 0;
67 static GSList *window_list = NULL;
69 #define VIKING_WINDOW_WIDTH 1000
70 #define VIKING_WINDOW_HEIGHT 800
71 #define DRAW_IMAGE_DEFAULT_WIDTH 1280
72 #define DRAW_IMAGE_DEFAULT_HEIGHT 1024
73 #define DRAW_IMAGE_DEFAULT_SAVE_AS_PNG TRUE
75 static void window_finalize ( GObject *gob );
76 static GObjectClass *parent_class;
78 static void window_set_filename ( VikWindow *vw, const gchar *filename );
79 static const gchar *window_get_filename ( VikWindow *vw );
81 static VikWindow *window_new ();
83 static void draw_update ( VikWindow *vw );
85 static void newwindow_cb ( GtkAction *a, VikWindow *vw );
88 static void open_window ( VikWindow *vw, GSList *files );
89 static void destroy_window ( GtkWidget *widget,
94 static gboolean delete_event( VikWindow *vw );
96 static gboolean key_press_event( VikWindow *vw, GdkEventKey *event, gpointer data );
98 static void center_changed_cb ( VikWindow *vw );
99 static void window_configure_event ( VikWindow *vw );
100 static void draw_sync ( VikWindow *vw );
101 static void draw_redraw ( VikWindow *vw );
102 static void draw_scroll ( VikWindow *vw, GdkEventScroll *event );
103 static void draw_click ( VikWindow *vw, GdkEventButton *event );
104 static void draw_release ( VikWindow *vw, GdkEventButton *event );
105 static void draw_mouse_motion ( VikWindow *vw, GdkEventMotion *event );
106 static void draw_zoom_cb ( GtkAction *a, VikWindow *vw );
107 static void draw_goto_cb ( GtkAction *a, VikWindow *vw );
108 static void draw_refresh_cb ( GtkAction *a, VikWindow *vw );
110 static void draw_status ( VikWindow *vw );
112 /* End Drawing Functions */
114 static void menu_addlayer_cb ( GtkAction *a, VikWindow *vw );
115 static void menu_properties_cb ( GtkAction *a, VikWindow *vw );
116 static void menu_delete_layer_cb ( GtkAction *a, VikWindow *vw );
118 /* tool management */
124 #define TOOL_LAYER_TYPE_NONE -1
129 toolbox_tool_t *tools;
133 static void menu_tool_cb ( GtkAction *old, GtkAction *a, VikWindow *vw );
134 static toolbox_tools_t* toolbox_create(VikWindow *vw);
135 static void toolbox_add_tool(toolbox_tools_t *vt, VikToolInterface *vti, gint layer_type );
136 static int toolbox_get_tool(toolbox_tools_t *vt, const gchar *tool_name);
137 static void toolbox_activate(toolbox_tools_t *vt, const gchar *tool_name);
138 static const GdkCursor *toolbox_get_cursor(toolbox_tools_t *vt, const gchar *tool_name);
139 static void toolbox_click (toolbox_tools_t *vt, GdkEventButton *event);
140 static void toolbox_move (toolbox_tools_t *vt, GdkEventMotion *event);
141 static void toolbox_release (toolbox_tools_t *vt, GdkEventButton *event);
145 static void window_create_ui( VikWindow *window );
146 static void register_vik_icons (GtkIconFactory *icon_factory);
149 static void load_file ( GtkAction *a, VikWindow *vw );
150 static gboolean save_file_as ( GtkAction *a, VikWindow *vw );
151 static gboolean save_file ( GtkAction *a, VikWindow *vw );
152 static gboolean save_file_and_exit ( GtkAction *a, VikWindow *vw );
153 static gboolean window_save ( VikWindow *vw );
157 VikViewport *viking_vvp;
158 VikLayersPanel *viking_vlp;
159 VikStatusbar *viking_vs;
163 GdkCursor *busy_cursor;
164 GdkCursor *viewport_cursor; // only a reference
166 /* tool management state */
169 guint16 tool_layer_id;
170 guint16 tool_tool_id;
172 GtkActionGroup *action_group;
176 gint delayed_pan_x, delayed_pan_y; // Temporary storage
177 gboolean single_click_pending;
179 guint draw_image_width, draw_image_height;
180 gboolean draw_image_save_as_png;
184 VikLoadType_t loaded_type;
186 GtkWidget *open_dia, *save_dia;
187 GtkWidget *save_img_dia, *save_img_dir_dia;
189 gboolean only_updating_coord_mode_ui; /* hack for a bug in GTK */
193 /* half-drawn update */
195 VikCoord trigger_center;
197 /* Store at this level for highlighted selection drawing since it applies to the viewport and the layers panel */
198 /* Only one of these items can be selected at the same time */
199 gpointer selected_vtl; /* notionally VikTrwLayer */
200 GHashTable *selected_tracks;
201 gpointer selected_track; /* notionally VikTrack */
202 GHashTable *selected_waypoints;
203 gpointer selected_waypoint; /* notionally VikWaypoint */
204 /* only use for individual track or waypoint */
205 /* For track(s) & waypoint(s) it is the layer they are in - this helps refering to the individual item easier */
206 gpointer containing_vtl; /* notionally VikTrwLayer */
220 VW_OPENWINDOW_SIGNAL,
224 static guint window_signals[VW_LAST_SIGNAL] = { 0 };
226 // TODO get rid of this as this is unnecessary duplication...
227 static gchar *tool_names[NUMBER_OF_TOOLS] = { N_("Pan"), N_("Zoom"), N_("Ruler"), N_("Select") };
229 G_DEFINE_TYPE (VikWindow, vik_window, GTK_TYPE_WINDOW)
231 VikViewport * vik_window_viewport(VikWindow *vw)
233 return(vw->viking_vvp);
236 VikLayersPanel * vik_window_layers_panel(VikWindow *vw)
238 return(vw->viking_vlp);
242 * Returns the statusbar for the window
244 VikStatusbar * vik_window_get_statusbar ( VikWindow *vw )
246 return vw->viking_vs;
250 * Returns the 'project' filename
252 const gchar *vik_window_get_filename (VikWindow *vw)
259 vik_statusbar_type_t vs_type;
260 gchar* message; // Always make a copy of this data
261 } statusbar_idle_data;
264 * For the actual statusbar update!
266 static gboolean statusbar_idle_update ( statusbar_idle_data *sid )
268 vik_statusbar_set_message ( sid->vs, sid->vs_type, sid->message );
269 g_free ( sid->message );
275 * vik_window_statusbar_update:
276 * @vw: The main window in which the statusbar will be updated.
277 * @message: The string to be displayed. This is copied.
278 * @vs_type: The part of the statusbar to be updated.
280 * This updates any part of the statusbar with the new string.
281 * It handles calling from the main thread or any background thread
282 * ATM this mostly used from background threads - as from the main thread
283 * one may use the vik_statusbar_set_message() directly.
285 void vik_window_statusbar_update ( VikWindow *vw, const gchar* message, vik_statusbar_type_t vs_type )
287 statusbar_idle_data *sid = g_malloc ( sizeof (statusbar_idle_data) );
288 sid->vs = vw->viking_vs;
289 sid->vs_type = vs_type;
290 sid->message = g_strdup ( message );
292 if ( g_thread_self() == vik_window_get_thread ( vw ) ) {
293 g_idle_add ( (GSourceFunc) statusbar_idle_update, sid );
296 // From a background thread
297 gdk_threads_add_idle ( (GSourceFunc) statusbar_idle_update, sid );
301 // Actual signal handlers
302 static void destroy_window ( GtkWidget *widget,
305 if ( ! --window_count )
309 #define VIK_SETTINGS_WIN_SIDEPANEL "window_sidepanel"
310 #define VIK_SETTINGS_WIN_STATUSBAR "window_statusbar"
311 #define VIK_SETTINGS_WIN_TOOLBAR "window_toolbar"
312 // Menubar setting to off is never auto saved in case it's accidentally turned off
313 // It's not so obvious so to recover the menu visibility.
314 // Thus this value is for setting manually via editting the settings file directly
315 #define VIK_SETTINGS_WIN_MENUBAR "window_menubar"
317 VikWindow *vik_window_new_window ()
319 if ( window_count < MAX_WINDOWS )
321 VikWindow *vw = window_new ();
323 g_signal_connect (G_OBJECT (vw), "destroy",
324 G_CALLBACK (destroy_window), NULL);
325 g_signal_connect (G_OBJECT (vw), "newwindow",
326 G_CALLBACK (vik_window_new_window), NULL);
327 g_signal_connect (G_OBJECT (vw), "openwindow",
328 G_CALLBACK (open_window), NULL);
330 gtk_widget_show_all ( GTK_WIDGET(vw) );
332 if ( a_vik_get_restore_window_state() ) {
333 // These settings are applied after the show all as these options hide widgets
335 if ( a_settings_get_boolean ( VIK_SETTINGS_WIN_SIDEPANEL, &sidepanel ) )
337 gtk_widget_hide ( GTK_WIDGET(vw->viking_vlp) );
338 GtkWidget *check_box = gtk_ui_manager_get_widget ( vw->uim, "/ui/MainMenu/View/SetShow/ViewSidePanel" );
339 gtk_check_menu_item_set_active ( GTK_CHECK_MENU_ITEM(check_box), FALSE );
343 if ( a_settings_get_boolean ( VIK_SETTINGS_WIN_STATUSBAR, &statusbar ) )
345 gtk_widget_hide ( GTK_WIDGET(vw->viking_vs) );
346 GtkWidget *check_box = gtk_ui_manager_get_widget ( vw->uim, "/ui/MainMenu/View/SetShow/ViewStatusBar" );
347 gtk_check_menu_item_set_active ( GTK_CHECK_MENU_ITEM(check_box), FALSE );
351 if ( a_settings_get_boolean ( VIK_SETTINGS_WIN_TOOLBAR, &toolbar ) )
353 gtk_widget_hide ( GTK_WIDGET(vw->toolbar) );
354 GtkWidget *check_box = gtk_ui_manager_get_widget ( vw->uim, "/ui/MainMenu/View/SetShow/ViewToolBar" );
355 gtk_check_menu_item_set_active ( GTK_CHECK_MENU_ITEM(check_box), FALSE );
359 if ( a_settings_get_boolean ( VIK_SETTINGS_WIN_MENUBAR, &menubar ) )
361 gtk_widget_hide ( gtk_ui_manager_get_widget ( vw->uim, "/ui/MainMenu" ) );
362 GtkWidget *check_box = gtk_ui_manager_get_widget ( vw->uim, "/ui/MainMenu/View/SetShow/ViewMainMenu" );
363 gtk_check_menu_item_set_active ( GTK_CHECK_MENU_ITEM(check_box), FALSE );
374 * determine_location_thread:
375 * @vw: The window that will get updated
376 * @threaddata: Data used by our background thread mechanism
378 * Use the features in vikgoto to determine where we are
379 * Then set up the viewport:
380 * 1. To goto the location
381 * 2. Set an appropriate level zoom for the location type
382 * 3. Some statusbar message feedback
384 static int determine_location_thread ( VikWindow *vw, gpointer threaddata )
388 gint ans = a_vik_goto_where_am_i ( vw->viking_vvp, &ll, &name );
390 int result = a_background_thread_progress ( threaddata, 1.0 );
392 vik_window_statusbar_update ( vw, _("Location lookup aborted"), VIK_STATUSBAR_INFO );
393 return -1; /* Abort thread */
401 // Position found with city precision - so zoom out more
404 else if ( ans == 3 ) {
405 // Position found via country name search - so zoom wayyyy out
409 vik_viewport_set_zoom ( vw->viking_vvp, zoom );
410 vik_viewport_set_center_latlon ( vw->viking_vvp, &ll, FALSE );
412 gchar *message = g_strdup_printf ( _("Location found: %s"), name );
413 vik_window_statusbar_update ( vw, message, VIK_STATUSBAR_INFO );
417 // Signal to redraw from the background
418 vik_layers_panel_emit_update ( vw->viking_vlp );
421 vik_window_statusbar_update ( vw, _("Unable to determine location"), VIK_STATUSBAR_INFO );
427 * Steps to be taken once initial loading has completed
429 void vik_window_new_window_finish ( VikWindow *vw )
431 // Don't add a map if we've loaded a Viking file already
435 if ( a_vik_get_startup_method ( ) == VIK_STARTUP_METHOD_SPECIFIED_FILE ) {
436 vik_window_open_file ( vw, a_vik_get_startup_file(), TRUE );
441 // Maybe add a default map layer
442 if ( a_vik_get_add_default_map_layer () ) {
443 VikMapsLayer *vml = VIK_MAPS_LAYER ( vik_layer_create(VIK_LAYER_MAPS, vw->viking_vvp, FALSE) );
444 vik_layer_rename ( VIK_LAYER(vml), _("Default Map") );
445 vik_aggregate_layer_add_layer ( vik_layers_panel_get_top_layer(vw->viking_vlp), VIK_LAYER(vml), TRUE );
450 // If not loaded any file, maybe try the location lookup
451 if ( vw->loaded_type == LOAD_TYPE_READ_FAILURE ) {
452 if ( a_vik_get_startup_method ( ) == VIK_STARTUP_METHOD_AUTO_LOCATION ) {
454 vik_statusbar_set_message ( vw->viking_vs, VIK_STATUSBAR_INFO, _("Trying to determine location...") );
456 a_background_thread ( GTK_WINDOW(vw),
457 _("Determining location"),
458 (vik_thr_func) determine_location_thread,
467 static void open_window ( VikWindow *vw, GSList *files )
469 gboolean change_fn = (g_slist_length(files) == 1); /* only change fn if one file */
470 GSList *cur_file = files;
472 // Only open a new window if a viking file
473 gchar *file_name = cur_file->data;
474 if (vw != NULL && vw->filename && check_file_magic_vik ( file_name ) ) {
475 VikWindow *newvw = vik_window_new_window ();
477 vik_window_open_file ( newvw, file_name, TRUE );
480 vik_window_open_file ( vw, file_name, change_fn );
483 cur_file = g_slist_next (cur_file);
485 g_slist_free (files);
489 void vik_window_selected_layer(VikWindow *vw, VikLayer *vl)
491 int i, j, tool_count;
492 VikLayerInterface *layer_interface;
494 if (!vw->action_group) return;
496 for (i=0; i<VIK_LAYER_NUM_TYPES; i++) {
498 layer_interface = vik_layer_get_interface(i);
499 tool_count = layer_interface->tools_count;
501 for (j = 0; j < tool_count; j++) {
502 action = gtk_action_group_get_action(vw->action_group,
503 layer_interface->tools[j].radioActionEntry.name);
504 g_object_set(action, "sensitive", i == vl->type, NULL);
509 static void window_finalize ( GObject *gob )
511 VikWindow *vw = VIK_WINDOW(gob);
512 g_return_if_fail ( vw != NULL );
514 a_background_remove_window ( vw );
516 window_list = g_slist_remove ( window_list, vw );
518 gdk_cursor_unref ( vw->busy_cursor );
520 for (tt = 0; tt < vw->vt->n_tools; tt++ )
521 if ( vw->vt->tools[tt].ti.destroy )
522 vw->vt->tools[tt].ti.destroy ( vw->vt->tools[tt].state );
523 g_free ( vw->vt->tools );
526 G_OBJECT_CLASS(parent_class)->finalize(gob);
530 static void vik_window_class_init ( VikWindowClass *klass )
533 GObjectClass *object_class;
535 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);
536 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);
538 object_class = G_OBJECT_CLASS (klass);
540 object_class->finalize = window_finalize;
542 parent_class = g_type_class_peek_parent (klass);
546 static void zoom_changed (GtkMenuShell *menushell,
549 VikWindow *vw = VIK_WINDOW (user_data);
551 GtkWidget *aw = gtk_menu_get_active ( GTK_MENU (menushell) );
552 gint active = GPOINTER_TO_INT(g_object_get_data ( G_OBJECT (aw), "position" ));
554 gdouble zoom_request = pow (2, active-2 );
556 // But has it really changed?
557 gdouble current_zoom = vik_viewport_get_zoom ( vw->viking_vvp );
558 if ( current_zoom != 0.0 && zoom_request != current_zoom ) {
559 vik_viewport_set_zoom ( vw->viking_vvp, zoom_request );
560 // Force drawing update
566 * @mpp: The initial zoom level
568 static GtkWidget *create_zoom_menu_all_levels ( gdouble mpp )
570 GtkWidget *menu = gtk_menu_new ();
571 char *itemLabels[] = { "0.25", "0.5", "1", "2", "4", "8", "16", "32", "64", "128", "256", "512", "1024", "2048", "4096", "8192", "16384", "32768" };
574 for (i = 0 ; i < G_N_ELEMENTS(itemLabels) ; i++)
576 GtkWidget *item = gtk_menu_item_new_with_label (itemLabels[i]);
577 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
578 gtk_widget_show (item);
579 g_object_set_data (G_OBJECT (item), "position", GINT_TO_POINTER(i));
582 gint active = 2 + round ( log (mpp) / log (2) );
583 // Ensure value derived from mpp is in bounds of the menu
584 if ( active >= G_N_ELEMENTS(itemLabels) )
585 active = G_N_ELEMENTS(itemLabels) - 1;
588 gtk_menu_set_active ( GTK_MENU(menu), active );
593 static GtkWidget *create_zoom_combo_all_levels ()
595 GtkWidget *combo = vik_combo_box_text_new();
596 vik_combo_box_text_append ( combo, "0.25");
597 vik_combo_box_text_append ( combo, "0.5");
598 vik_combo_box_text_append ( combo, "1");
599 vik_combo_box_text_append ( combo, "2");
600 vik_combo_box_text_append ( combo, "4");
601 vik_combo_box_text_append ( combo, "8");
602 vik_combo_box_text_append ( combo, "16");
603 vik_combo_box_text_append ( combo, "32");
604 vik_combo_box_text_append ( combo, "64");
605 vik_combo_box_text_append ( combo, "128");
606 vik_combo_box_text_append ( combo, "256");
607 vik_combo_box_text_append ( combo, "512");
608 vik_combo_box_text_append ( combo, "1024");
609 vik_combo_box_text_append ( combo, "2048");
610 vik_combo_box_text_append ( combo, "4096");
611 vik_combo_box_text_append ( combo, "8192");
612 vik_combo_box_text_append ( combo, "16384");
613 vik_combo_box_text_append ( combo, "32768");
615 gtk_widget_set_tooltip_text (combo, _("Select zoom level"));
619 static gint zoom_popup_handler (GtkWidget *widget)
623 g_return_val_if_fail (widget != NULL, FALSE);
624 g_return_val_if_fail (GTK_IS_MENU (widget), FALSE);
626 /* The "widget" is the menu that was supplied when
627 * g_signal_connect_swapped() was called.
629 menu = GTK_MENU (widget);
631 gtk_menu_popup (menu, NULL, NULL, NULL, NULL,
632 1, gtk_get_current_event_time());
640 static void drag_data_received_cb ( GtkWidget *widget,
641 GdkDragContext *context,
644 GtkSelectionData *selection_data,
649 gboolean success = FALSE;
651 if ( (selection_data != NULL) && (gtk_selection_data_get_length(selection_data) > 0) ) {
652 switch (target_type) {
654 gchar *str = (gchar*)gtk_selection_data_get_data(selection_data);
655 g_debug ("drag received string:%s \n", str);
657 // Convert string into GSList of individual entries for use with our open signal
658 gchar **entries = g_strsplit(str, "\r\n", 0);
659 GSList *filenames = NULL;
660 gint entry_runner = 0;
661 gchar *entry = entries[entry_runner];
663 if ( g_strcmp0 ( entry, "" ) ) {
664 // Drag+Drop gives URIs. And so in particular, %20 in place of spaces in filenames
665 // thus need to convert the text into a plain string
666 gchar *filename = g_filename_from_uri ( entry, NULL, NULL );
668 filenames = g_slist_append ( filenames, filename );
671 entry = entries[entry_runner];
675 g_signal_emit ( G_OBJECT(VIK_WINDOW_FROM_WIDGET(widget)), window_signals[VW_OPENWINDOW_SIGNAL], 0, filenames );
676 // NB: GSList & contents are freed by main.open_window
685 gtk_drag_finish ( context, success, FALSE, time );
688 #define VIK_SETTINGS_WIN_MAX "window_maximized"
689 #define VIK_SETTINGS_WIN_FULLSCREEN "window_fullscreen"
690 #define VIK_SETTINGS_WIN_WIDTH "window_width"
691 #define VIK_SETTINGS_WIN_HEIGHT "window_height"
692 #define VIK_SETTINGS_WIN_SAVE_IMAGE_WIDTH "window_save_image_width"
693 #define VIK_SETTINGS_WIN_SAVE_IMAGE_HEIGHT "window_save_image_height"
694 #define VIK_SETTINGS_WIN_SAVE_IMAGE_PNG "window_save_image_as_png"
696 static void vik_window_init ( VikWindow *vw )
698 GtkWidget *main_vbox;
701 vw->action_group = NULL;
703 vw->viking_vvp = vik_viewport_new();
704 vw->viking_vlp = vik_layers_panel_new();
705 vik_layers_panel_set_viewport ( vw->viking_vlp, vw->viking_vvp );
706 vw->viking_vs = vik_statusbar_new();
708 vw->vt = toolbox_create(vw);
709 window_create_ui(vw);
710 window_set_filename (vw, NULL);
711 vw->toolbar = GTK_TOOLBAR(gtk_ui_manager_get_widget (vw->uim, "/MainToolbar"));
713 vw->busy_cursor = gdk_cursor_new ( GDK_WATCH );
715 // Set the default tool
716 gtk_action_activate ( gtk_action_group_get_action ( vw->action_group, "Pan" ) );
719 vw->loaded_type = LOAD_TYPE_READ_FAILURE; //AKA none
720 vw->modified = FALSE;
721 vw->only_updating_coord_mode_ui = FALSE;
723 vw->pan_move = FALSE;
724 vw->pan_x = vw->pan_y = -1;
725 vw->single_click_pending = FALSE;
727 gint draw_image_width;
728 if ( a_settings_get_integer ( VIK_SETTINGS_WIN_SAVE_IMAGE_WIDTH, &draw_image_width ) )
729 vw->draw_image_width = draw_image_width;
731 vw->draw_image_width = DRAW_IMAGE_DEFAULT_WIDTH;
732 gint draw_image_height;
733 if ( a_settings_get_integer ( VIK_SETTINGS_WIN_SAVE_IMAGE_HEIGHT, &draw_image_height ) )
734 vw->draw_image_height = draw_image_height;
736 vw->draw_image_height = DRAW_IMAGE_DEFAULT_HEIGHT;
737 gboolean draw_image_save_as_png;
738 if ( a_settings_get_boolean ( VIK_SETTINGS_WIN_SAVE_IMAGE_PNG, &draw_image_save_as_png ) )
739 vw->draw_image_save_as_png = draw_image_save_as_png;
741 vw->draw_image_save_as_png = DRAW_IMAGE_DEFAULT_SAVE_AS_PNG;
743 main_vbox = gtk_vbox_new(FALSE, 1);
744 gtk_container_add (GTK_CONTAINER (vw), main_vbox);
746 gtk_box_pack_start (GTK_BOX(main_vbox), gtk_ui_manager_get_widget (vw->uim, "/MainMenu"), FALSE, TRUE, 0);
747 gtk_box_pack_start (GTK_BOX(main_vbox), GTK_WIDGET(vw->toolbar), FALSE, TRUE, 0);
748 gtk_toolbar_set_icon_size (vw->toolbar, GTK_ICON_SIZE_SMALL_TOOLBAR);
749 gtk_toolbar_set_style (vw->toolbar, GTK_TOOLBAR_ICONS);
751 vik_ext_tool_datasources_add_menu_items ( vw, vw->uim );
753 GtkWidget * zoom_levels = gtk_ui_manager_get_widget (vw->uim, "/MainMenu/View/SetZoom");
754 GtkWidget * zoom_levels_menu = create_zoom_menu_all_levels ( vik_viewport_get_zoom(vw->viking_vvp) );
755 gtk_menu_item_set_submenu (GTK_MENU_ITEM (zoom_levels), zoom_levels_menu);
756 g_signal_connect ( G_OBJECT(zoom_levels_menu), "selection-done", G_CALLBACK(zoom_changed), vw);
757 g_signal_connect_swapped ( G_OBJECT(vw->viking_vs), "clicked", G_CALLBACK(zoom_popup_handler), zoom_levels_menu );
759 g_signal_connect (G_OBJECT (vw), "delete_event", G_CALLBACK (delete_event), NULL);
762 g_signal_connect_swapped (G_OBJECT(vw->viking_vvp), "updated_center", G_CALLBACK(center_changed_cb), vw);
764 g_signal_connect_swapped (G_OBJECT(vw->viking_vvp), "expose_event", G_CALLBACK(draw_sync), vw);
765 g_signal_connect_swapped (G_OBJECT(vw->viking_vvp), "configure_event", G_CALLBACK(window_configure_event), vw);
766 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 );
767 g_signal_connect_swapped (G_OBJECT(vw->viking_vvp), "scroll_event", G_CALLBACK(draw_scroll), vw);
768 g_signal_connect_swapped (G_OBJECT(vw->viking_vvp), "button_press_event", G_CALLBACK(draw_click), vw);
769 g_signal_connect_swapped (G_OBJECT(vw->viking_vvp), "button_release_event", G_CALLBACK(draw_release), vw);
770 g_signal_connect_swapped (G_OBJECT(vw->viking_vvp), "motion_notify_event", G_CALLBACK(draw_mouse_motion), vw);
772 g_signal_connect_swapped (G_OBJECT(vw->viking_vlp), "update", G_CALLBACK(draw_update), vw);
773 g_signal_connect_swapped (G_OBJECT(vw->viking_vlp), "delete_layer", G_CALLBACK(vik_window_clear_highlight), vw);
775 // Allow key presses to be processed anywhere
776 g_signal_connect_swapped (G_OBJECT (vw), "key_press_event", G_CALLBACK (key_press_event), vw);
778 // Set initial button sensitivity
779 center_changed_cb ( vw );
781 hpaned = gtk_hpaned_new ();
782 gtk_paned_pack1 ( GTK_PANED(hpaned), GTK_WIDGET (vw->viking_vlp), FALSE, FALSE );
783 gtk_paned_pack2 ( GTK_PANED(hpaned), GTK_WIDGET (vw->viking_vvp), TRUE, TRUE );
785 /* This packs the button into the window (a gtk container). */
786 gtk_box_pack_start (GTK_BOX(main_vbox), hpaned, TRUE, TRUE, 0);
788 gtk_box_pack_end (GTK_BOX(main_vbox), GTK_WIDGET(vw->viking_vs), FALSE, TRUE, 0);
790 a_background_add_window ( vw );
792 window_list = g_slist_prepend ( window_list, vw);
794 gint height = VIKING_WINDOW_HEIGHT;
795 gint width = VIKING_WINDOW_WIDTH;
797 if ( a_vik_get_restore_window_state() ) {
798 if ( a_settings_get_integer ( VIK_SETTINGS_WIN_HEIGHT, &height ) ) {
799 // Enforce a basic minimum size
804 // No setting - so use default
805 height = VIKING_WINDOW_HEIGHT;
807 if ( a_settings_get_integer ( VIK_SETTINGS_WIN_WIDTH, &width ) ) {
808 // Enforce a basic minimum size
813 // No setting - so use default
814 width = VIKING_WINDOW_WIDTH;
817 if ( a_settings_get_boolean ( VIK_SETTINGS_WIN_MAX, &maxed ) )
819 gtk_window_maximize ( GTK_WINDOW(vw) );
822 if ( a_settings_get_boolean ( VIK_SETTINGS_WIN_FULLSCREEN, &full ) ) {
824 gtk_window_fullscreen ( GTK_WINDOW(vw) );
825 GtkWidget *check_box = gtk_ui_manager_get_widget ( vw->uim, "/ui/MainMenu/View/FullScreen" );
826 gtk_check_menu_item_set_active ( GTK_CHECK_MENU_ITEM(check_box), TRUE );
831 gtk_window_set_default_size ( GTK_WINDOW(vw), width, height );
835 vw->save_img_dia = NULL;
836 vw->save_img_dir_dia = NULL;
838 // Only accept Drag and Drop of files onto the viewport
839 gtk_drag_dest_set ( GTK_WIDGET(vw->viking_vvp), GTK_DEST_DEFAULT_ALL, NULL, 0, GDK_ACTION_COPY );
840 gtk_drag_dest_add_uri_targets ( GTK_WIDGET(vw->viking_vvp) );
841 g_signal_connect ( GTK_WIDGET(vw->viking_vvp), "drag-data-received", G_CALLBACK(drag_data_received_cb), NULL );
843 // Store the thread value so comparisons can be made to determine the gdk update method
844 // Hopefully we are storing the main thread value here :)
845 // [ATM any window initialization is always be performed by the main thread]
846 vw->thread = g_thread_self();
849 static VikWindow *window_new ()
851 return VIK_WINDOW ( g_object_new ( VIK_WINDOW_TYPE, NULL ) );
855 * Update the displayed map
856 * Only update the top most visible map layer
857 * ATM this assumes (as per defaults) the top most map has full alpha setting
858 * such that other other maps even though they may be active will not be seen
859 * It's more complicated to work out which maps are actually visible due to alpha settings
860 * and overkill for this simple refresh method.
862 static void simple_map_update ( VikWindow *vw, gboolean only_new )
864 // Find the most relevent single map layer to operate on
865 VikLayer *vl = vik_aggregate_layer_get_top_visible_layer_of_type (vik_layers_panel_get_top_layer(vw->viking_vlp), VIK_LAYER_MAPS);
867 vik_maps_layer_download ( VIK_MAPS_LAYER(vl), vw->viking_vvp, only_new );
871 * This is the global key press handler
872 * Global shortcuts are available at any time and hence are not restricted to when a certain tool is enabled
874 static gboolean key_press_event( VikWindow *vw, GdkEventKey *event, gpointer data )
876 // The keys handled here are not in the menuing system for a couple of reasons:
877 // . Keeps the menu size compact (alebit at expense of discoverably)
878 // . Allows differing key bindings to perform the same actions
880 // First decide if key events are related to the maps layer
881 gboolean map_download = FALSE;
882 gboolean map_download_only_new = TRUE; // Only new or reload
884 GdkModifierType modifiers = gtk_accelerator_get_default_mod_mask();
886 // Standard 'Refresh' keys: F5 or Ctrl+r
887 // Note 'F5' is actually handled via draw_refresh_cb() later on
888 // (not 'R' it's 'r' notice the case difference!!)
889 if ( event->keyval == GDK_r && (event->state & modifiers) == GDK_CONTROL_MASK ) {
891 map_download_only_new = TRUE;
893 // Full cache reload with Ctrl+F5 or Ctrl+Shift+r [This is not in the menu system]
894 // Note the use of uppercase R here since shift key has been pressed
895 else if ( (event->keyval == GDK_F5 && (event->state & modifiers) == GDK_CONTROL_MASK ) ||
896 ( event->keyval == GDK_R && (event->state & modifiers) == (GDK_CONTROL_MASK + GDK_SHIFT_MASK) ) ) {
898 map_download_only_new = FALSE;
901 if ( map_download ) {
902 simple_map_update ( vw, map_download_only_new );
905 VikLayer *vl = vik_layers_panel_get_selected ( vw->viking_vlp );
906 if (vl && vw->vt->active_tool != -1 && vw->vt->tools[vw->vt->active_tool].ti.key_press ) {
907 gint ltype = vw->vt->tools[vw->vt->active_tool].layer_type;
908 if ( vl && ltype == vl->type )
909 return vw->vt->tools[vw->vt->active_tool].ti.key_press(vl, event, vw->vt->tools[vw->vt->active_tool].state);
912 // Ensure called only on window tools (i.e. not on any of the Layer tools since the layer is NULL)
913 if ( vw->current_tool < TOOL_LAYER ) {
914 // No layer - but enable window tool keypress processing - these should be able to handle a NULL layer
915 if ( vw->vt->tools[vw->vt->active_tool].ti.key_press ) {
916 return vw->vt->tools[vw->vt->active_tool].ti.key_press ( vl, event, vw->vt->tools[vw->vt->active_tool].state );
920 /* Restore Main Menu via Escape key if the user has hidden it */
921 /* This key is more likely to be used as they may not remember the function key */
922 if ( event->keyval == GDK_Escape ) {
923 GtkWidget *check_box = gtk_ui_manager_get_widget ( vw->uim, "/ui/MainMenu/View/SetShow/ViewMainMenu" );
925 gboolean state = gtk_check_menu_item_get_active ( GTK_CHECK_MENU_ITEM(check_box) );
927 gtk_widget_show ( gtk_ui_manager_get_widget ( vw->uim, "/ui/MainMenu" ) );
928 gtk_check_menu_item_set_active ( GTK_CHECK_MENU_ITEM(check_box), TRUE );
929 return TRUE; /* handled keypress */
934 return FALSE; /* don't handle the keypress */
937 static gboolean delete_event( VikWindow *vw )
939 #ifdef VIKING_PROMPT_IF_MODIFIED
946 dia = GTK_DIALOG ( gtk_message_dialog_new ( GTK_WINDOW(vw), GTK_DIALOG_DESTROY_WITH_PARENT, GTK_MESSAGE_QUESTION, GTK_BUTTONS_NONE,
947 _("Do you want to save the changes you made to the document \"%s\"?\n"
949 "Your changes will be lost if you don't save them."),
950 window_get_filename ( vw ) ) );
951 gtk_dialog_add_buttons ( dia, _("Don't Save"), GTK_RESPONSE_NO, GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, GTK_STOCK_SAVE, GTK_RESPONSE_YES, NULL );
952 switch ( gtk_dialog_run ( dia ) )
954 case GTK_RESPONSE_NO: gtk_widget_destroy ( GTK_WIDGET(dia) ); return FALSE;
955 case GTK_RESPONSE_CANCEL: gtk_widget_destroy ( GTK_WIDGET(dia) ); return TRUE;
956 default: gtk_widget_destroy ( GTK_WIDGET(dia) ); return ! save_file(NULL, vw);
960 if ( window_count == 1 ) {
961 // On the final window close - save latest state - if it's wanted...
962 if ( a_vik_get_restore_window_state() ) {
963 gint state = gdk_window_get_state ( GTK_WIDGET(vw)->window );
964 gboolean state_max = state & GDK_WINDOW_STATE_MAXIMIZED;
965 a_settings_set_boolean ( VIK_SETTINGS_WIN_MAX, state_max );
967 gboolean state_fullscreen = state & GDK_WINDOW_STATE_FULLSCREEN;
968 a_settings_set_boolean ( VIK_SETTINGS_WIN_FULLSCREEN, state_fullscreen );
970 a_settings_set_boolean ( VIK_SETTINGS_WIN_SIDEPANEL, GTK_WIDGET_VISIBLE (GTK_WIDGET(vw->viking_vlp)) );
972 a_settings_set_boolean ( VIK_SETTINGS_WIN_STATUSBAR, GTK_WIDGET_VISIBLE (GTK_WIDGET(vw->viking_vs)) );
974 a_settings_set_boolean ( VIK_SETTINGS_WIN_TOOLBAR, GTK_WIDGET_VISIBLE (GTK_WIDGET(vw->toolbar)) );
976 // If supersized - no need to save the enlarged width+height values
977 if ( ! (state_fullscreen || state_max) ) {
979 gtk_window_get_size ( GTK_WINDOW (vw), &width, &height );
980 a_settings_set_integer ( VIK_SETTINGS_WIN_WIDTH, width );
981 a_settings_set_integer ( VIK_SETTINGS_WIN_HEIGHT, height );
985 a_settings_set_integer ( VIK_SETTINGS_WIN_SAVE_IMAGE_WIDTH, vw->draw_image_width );
986 a_settings_set_integer ( VIK_SETTINGS_WIN_SAVE_IMAGE_HEIGHT, vw->draw_image_height );
987 a_settings_set_boolean ( VIK_SETTINGS_WIN_SAVE_IMAGE_PNG, vw->draw_image_save_as_png );
994 static void newwindow_cb ( GtkAction *a, VikWindow *vw )
996 g_signal_emit ( G_OBJECT(vw), window_signals[VW_NEWWINDOW_SIGNAL], 0 );
999 static void draw_update ( VikWindow *vw )
1005 static void draw_sync ( VikWindow *vw )
1007 vik_viewport_sync(vw->viking_vvp);
1012 * Split the status update, as sometimes only need to update the tool part
1013 * also on initialization the zoom related stuff is not ready to be used
1015 static void draw_status_tool ( VikWindow *vw )
1017 if ( vw->current_tool == TOOL_LAYER )
1018 // Use tooltip rather than the internal name as the tooltip is i8n
1019 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 );
1021 vik_statusbar_set_message ( vw->viking_vs, VIK_STATUSBAR_TOOL, _(tool_names[vw->current_tool]) );
1024 static void draw_status ( VikWindow *vw )
1026 static gchar zoom_level[22];
1027 gdouble xmpp = vik_viewport_get_xmpp (vw->viking_vvp);
1028 gdouble ympp = vik_viewport_get_ympp(vw->viking_vvp);
1029 gchar *unit = vik_viewport_get_coord_mode(vw->viking_vvp) == VIK_COORD_UTM ? _("mpp") : _("pixelfact");
1031 g_snprintf ( zoom_level, 22, "%.3f/%.3f %s", xmpp, ympp, unit );
1033 if ( (int)xmpp - xmpp < 0.0 )
1034 g_snprintf ( zoom_level, 22, "%.3f %s", xmpp, unit );
1036 /* xmpp should be a whole number so don't show useless .000 bit */
1037 g_snprintf ( zoom_level, 22, "%d %s", (int)xmpp, unit );
1039 vik_statusbar_set_message ( vw->viking_vs, VIK_STATUSBAR_ZOOM, zoom_level );
1041 draw_status_tool ( vw );
1044 void vik_window_set_redraw_trigger(VikLayer *vl)
1046 VikWindow *vw = VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vl));
1051 static void window_configure_event ( VikWindow *vw )
1053 static int first = 1;
1056 // This is a hack to set the cursor corresponding to the first tool
1057 // FIXME find the correct way to initialize both tool and its cursor
1059 vw->viewport_cursor = (GdkCursor *)toolbox_get_cursor(vw->vt, "Pan");
1060 /* We set cursor, even if it is NULL: it resets to default */
1061 gdk_window_set_cursor ( gtk_widget_get_window(GTK_WIDGET(vw->viking_vvp)), vw->viewport_cursor );
1065 static void draw_redraw ( VikWindow *vw )
1067 VikCoord old_center = vw->trigger_center;
1068 vw->trigger_center = *(vik_viewport_get_center(vw->viking_vvp));
1069 VikLayer *new_trigger = vw->trigger;
1071 VikLayer *old_trigger = VIK_LAYER(vik_viewport_get_trigger(vw->viking_vvp));
1073 if ( ! new_trigger )
1074 ; /* do nothing -- have to redraw everything. */
1075 else if ( (old_trigger != new_trigger) || !vik_coord_equals(&old_center, &vw->trigger_center) || (new_trigger->type == VIK_LAYER_AGGREGATE) )
1076 vik_viewport_set_trigger ( vw->viking_vvp, new_trigger ); /* todo: set to half_drawn mode if new trigger is above old */
1078 vik_viewport_set_half_drawn ( vw->viking_vvp, TRUE );
1081 vik_viewport_clear ( vw->viking_vvp);
1082 // Main layer drawing
1083 vik_layers_panel_draw_all ( vw->viking_vlp );
1084 // Draw highlight (possibly again but ensures it is on top - especially for when tracks overlap)
1085 if ( vik_viewport_get_draw_highlight (vw->viking_vvp) ) {
1086 if ( vw->containing_vtl && (vw->selected_tracks || vw->selected_waypoints ) ) {
1087 vik_trw_layer_draw_highlight_items ( vw->containing_vtl, vw->selected_tracks, vw->selected_waypoints, vw->viking_vvp );
1089 else if ( vw->containing_vtl && (vw->selected_track || vw->selected_waypoint) ) {
1090 vik_trw_layer_draw_highlight_item ( vw->containing_vtl, vw->selected_track, vw->selected_waypoint, vw->viking_vvp );
1092 else if ( vw->selected_vtl ) {
1093 vik_trw_layer_draw_highlight ( vw->selected_vtl, vw->viking_vvp );
1096 // Other viewport decoration items on top if they are enabled/in use
1097 vik_viewport_draw_scale ( vw->viking_vvp );
1098 vik_viewport_draw_copyright ( vw->viking_vvp );
1099 vik_viewport_draw_centermark ( vw->viking_vvp );
1100 vik_viewport_draw_logo ( vw->viking_vvp );
1102 vik_viewport_set_half_drawn ( vw->viking_vvp, FALSE ); /* just in case. */
1105 gboolean draw_buf_done = TRUE;
1107 static gboolean draw_buf(gpointer data)
1109 gpointer *pass_along = data;
1110 gdk_threads_enter();
1111 gdk_draw_drawable (pass_along[0], pass_along[1],
1112 pass_along[2], 0, 0, 0, 0, -1, -1);
1113 draw_buf_done = TRUE;
1114 gdk_threads_leave();
1119 /* Mouse event handlers ************************************************************************/
1121 static void vik_window_pan_click (VikWindow *vw, GdkEventButton *event)
1123 /* set panning origin */
1124 vw->pan_move = FALSE;
1125 vw->pan_x = (gint) event->x;
1126 vw->pan_y = (gint) event->y;
1129 static void draw_click (VikWindow *vw, GdkEventButton *event)
1131 gtk_widget_grab_focus ( GTK_WIDGET(vw->viking_vvp) );
1133 /* middle button pressed. we reserve all middle button and scroll events
1134 * for panning and zooming; tools only get left/right/movement
1136 if ( event->button == 2) {
1137 if ( vw->vt->tools[vw->vt->active_tool].ti.pan_handler )
1138 // Tool still may need to do something (such as disable something)
1139 toolbox_click(vw->vt, event);
1140 vik_window_pan_click ( vw, event );
1143 toolbox_click(vw->vt, event);
1147 static void vik_window_pan_move (VikWindow *vw, GdkEventMotion *event)
1149 if ( vw->pan_x != -1 ) {
1150 vik_viewport_set_center_screen ( vw->viking_vvp, vik_viewport_get_width(vw->viking_vvp)/2 - event->x + vw->pan_x,
1151 vik_viewport_get_height(vw->viking_vvp)/2 - event->y + vw->pan_y );
1152 vw->pan_move = TRUE;
1153 vw->pan_x = event->x;
1154 vw->pan_y = event->y;
1159 static void draw_mouse_motion (VikWindow *vw, GdkEventMotion *event)
1161 static VikCoord coord;
1162 static struct UTM utm;
1163 static struct LatLon ll;
1164 #define BUFFER_SIZE 50
1165 static char pointer_buf[BUFFER_SIZE];
1166 gchar *lat = NULL, *lon = NULL;
1169 VikDemInterpol interpol_method;
1171 /* This is a hack, but work far the best, at least for single pointer systems.
1172 * See http://bugzilla.gnome.org/show_bug.cgi?id=587714 for more. */
1174 gdk_window_get_pointer (event->window, &x, &y, NULL);
1178 toolbox_move(vw->vt, event);
1180 vik_viewport_screen_to_coord ( vw->viking_vvp, event->x, event->y, &coord );
1181 vik_coord_to_utm ( &coord, &utm );
1183 if ( vik_viewport_get_drawmode ( vw->viking_vvp ) == VIK_VIEWPORT_DRAWMODE_UTM ) {
1184 // Reuse lat for the first part (Zone + N or S, and lon for the second part (easting and northing) of a UTM format:
1185 // ZONE[N|S] EASTING NORTHING
1186 lat = g_malloc(4*sizeof(gchar));
1187 // NB zone is stored in a char but is an actual number
1188 g_snprintf (lat, 4, "%d%c", utm.zone, utm.letter);
1189 lon = g_malloc(16*sizeof(gchar));
1190 g_snprintf (lon, 16, "%d %d", (gint)utm.easting, (gint)utm.northing);
1193 a_coords_utm_to_latlon ( &utm, &ll );
1194 a_coords_latlon_to_string ( &ll, &lat, &lon );
1197 /* Change interpolate method according to scale */
1198 zoom = vik_viewport_get_zoom(vw->viking_vvp);
1200 interpol_method = VIK_DEM_INTERPOL_NONE;
1201 else if (zoom >= 1.0)
1202 interpol_method = VIK_DEM_INTERPOL_SIMPLE;
1204 interpol_method = VIK_DEM_INTERPOL_BEST;
1205 if ((alt = a_dems_get_elev_by_coord(&coord, interpol_method)) != VIK_DEM_INVALID_ELEVATION) {
1206 if ( a_vik_get_units_height () == VIK_UNITS_HEIGHT_METRES )
1207 g_snprintf ( pointer_buf, BUFFER_SIZE, _("%s %s %dm"), lat, lon, alt );
1209 g_snprintf ( pointer_buf, BUFFER_SIZE, _("%s %s %dft"), lat, lon, (int)VIK_METERS_TO_FEET(alt) );
1212 g_snprintf ( pointer_buf, BUFFER_SIZE, _("%s %s"), lat, lon );
1217 vik_statusbar_set_message ( vw->viking_vs, VIK_STATUSBAR_POSITION, pointer_buf );
1219 vik_window_pan_move ( vw, event );
1221 /* This is recommended by the GTK+ documentation, but does not work properly.
1222 * Use deprecated way until GTK+ gets a solution for correct motion hint handling:
1223 * http://bugzilla.gnome.org/show_bug.cgi?id=587714
1225 /* gdk_event_request_motions ( event ); */
1229 * Action the single click after a small timeout
1230 * If a double click has occurred then this will do nothing
1232 static gboolean vik_window_pan_timeout (VikWindow *vw)
1234 if ( ! vw->single_click_pending ) {
1235 // Double click happened, so don't do anything
1239 /* set panning origin */
1240 vw->pan_move = FALSE;
1241 vw->single_click_pending = FALSE;
1242 vik_viewport_set_center_screen ( vw->viking_vvp, vw->delayed_pan_x, vw->delayed_pan_y );
1245 // Really turn off the pan moving!!
1246 vw->pan_x = vw->pan_y = -1;
1250 static void vik_window_pan_release ( VikWindow *vw, GdkEventButton *event )
1252 gboolean do_draw = TRUE;
1254 if ( vw->pan_move == FALSE ) {
1255 vw->single_click_pending = !vw->single_click_pending;
1257 if ( vw->single_click_pending ) {
1258 // Store offset to use
1259 vw->delayed_pan_x = vw->pan_x;
1260 vw->delayed_pan_y = vw->pan_y;
1261 // Get double click time
1262 GtkSettings *gs = gtk_widget_get_settings ( GTK_WIDGET(vw) );
1263 GValue dct = G_VALUE_INIT;
1264 g_value_init ( &dct, G_TYPE_INT );
1265 g_object_get_property ( G_OBJECT(gs), "gtk-double-click-time", &dct );
1266 // Give chance for a double click to occur
1267 gint timer = g_value_get_int ( &dct ) + 50;
1268 g_timeout_add ( timer, (GSourceFunc)vik_window_pan_timeout, vw );
1272 vik_viewport_set_center_screen ( vw->viking_vvp, vw->pan_x, vw->pan_y );
1276 vik_viewport_set_center_screen ( vw->viking_vvp, vik_viewport_get_width(vw->viking_vvp)/2 - event->x + vw->pan_x,
1277 vik_viewport_get_height(vw->viking_vvp)/2 - event->y + vw->pan_y );
1280 vw->pan_move = FALSE;
1281 vw->pan_x = vw->pan_y = -1;
1286 static void draw_release ( VikWindow *vw, GdkEventButton *event )
1288 gtk_widget_grab_focus ( GTK_WIDGET(vw->viking_vvp) );
1290 if ( event->button == 2 ) { /* move / pan */
1291 if ( vw->vt->tools[vw->vt->active_tool].ti.pan_handler )
1292 // Tool still may need to do something (such as reenable something)
1293 toolbox_release(vw->vt, event);
1294 vik_window_pan_release ( vw, event );
1297 toolbox_release(vw->vt, event);
1301 static void draw_scroll (VikWindow *vw, GdkEventScroll *event)
1303 guint modifiers = event->state & (GDK_SHIFT_MASK | GDK_CONTROL_MASK);
1304 if ( modifiers == GDK_CONTROL_MASK ) {
1305 /* control == pan up & down */
1306 if ( event->direction == GDK_SCROLL_UP )
1307 vik_viewport_set_center_screen ( vw->viking_vvp, vik_viewport_get_width(vw->viking_vvp)/2, vik_viewport_get_height(vw->viking_vvp)/3 );
1309 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 );
1310 } else if ( modifiers == GDK_SHIFT_MASK ) {
1311 /* shift == pan left & right */
1312 if ( event->direction == GDK_SCROLL_UP )
1313 vik_viewport_set_center_screen ( vw->viking_vvp, vik_viewport_get_width(vw->viking_vvp)/3, vik_viewport_get_height(vw->viking_vvp)/2 );
1315 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 );
1316 } else if ( modifiers == (GDK_CONTROL_MASK | GDK_SHIFT_MASK) ) {
1317 // This zoom is on the center position
1318 if ( event->direction == GDK_SCROLL_UP )
1319 vik_viewport_zoom_in (vw->viking_vvp);
1321 vik_viewport_zoom_out (vw->viking_vvp);
1323 /* make sure mouse is still over the same point on the map when we zoom */
1326 gint center_x = vik_viewport_get_width ( vw->viking_vvp ) / 2;
1327 gint center_y = vik_viewport_get_height ( vw->viking_vvp ) / 2;
1328 vik_viewport_screen_to_coord ( vw->viking_vvp, event->x, event->y, &coord );
1329 if ( event->direction == GDK_SCROLL_UP )
1330 vik_viewport_zoom_in (vw->viking_vvp);
1332 vik_viewport_zoom_out(vw->viking_vvp);
1333 vik_viewport_coord_to_screen ( vw->viking_vvp, &coord, &x, &y );
1334 vik_viewport_set_center_screen ( vw->viking_vvp, center_x + (x - event->x),
1335 center_y + (y - event->y) );
1343 /********************************************************************************
1345 ********************************************************************************/
1346 static void draw_ruler(VikViewport *vvp, GdkDrawable *d, GdkGC *gc, gint x1, gint y1, gint x2, gint y2, gdouble distance)
1350 GdkGC *labgc = vik_viewport_new_gc ( vvp, "#cccccc", 1);
1351 GdkGC *thickgc = gdk_gc_new(d);
1353 gdouble len = sqrt((x1-x2)*(x1-x2) + (y1-y2)*(y1-y2));
1354 gdouble dx = (x2-x1)/len*10;
1355 gdouble dy = (y2-y1)/len*10;
1356 gdouble c = cos(DEG2RAD(15.0));
1357 gdouble s = sin(DEG2RAD(15.0));
1359 gdouble baseangle = 0;
1362 /* draw line with arrow ends */
1364 gint tmp_x1=x1, tmp_y1=y1, tmp_x2=x2, tmp_y2=y2;
1365 a_viewport_clip_line(&tmp_x1, &tmp_y1, &tmp_x2, &tmp_y2);
1366 gdk_draw_line(d, gc, tmp_x1, tmp_y1, tmp_x2, tmp_y2);
1369 a_viewport_clip_line(&x1, &y1, &x2, &y2);
1370 gdk_draw_line(d, gc, x1, y1, x2, y2);
1372 gdk_draw_line(d, gc, x1 - dy, y1 + dx, x1 + dy, y1 - dx);
1373 gdk_draw_line(d, gc, x2 - dy, y2 + dx, x2 + dy, y2 - dx);
1374 gdk_draw_line(d, gc, x2, y2, x2 - (dx * c + dy * s), y2 - (dy * c - dx * s));
1375 gdk_draw_line(d, gc, x2, y2, x2 - (dx * c - dy * s), y2 - (dy * c + dx * s));
1376 gdk_draw_line(d, gc, x1, y1, x1 + (dx * c + dy * s), y1 + (dy * c - dx * s));
1377 gdk_draw_line(d, gc, x1, y1, x1 + (dx * c - dy * s), y1 + (dy * c + dx * s));
1383 vik_viewport_compute_bearing ( vvp, x1, y1, x2, y2, &angle, &baseangle );
1387 gdk_gc_copy(thickgc, gc);
1388 gdk_gc_set_line_attributes(thickgc, CW, GDK_LINE_SOLID, GDK_CAP_BUTT, GDK_JOIN_MITER);
1389 gdk_color_parse("#2255cc", &color);
1390 gdk_gc_set_rgb_fg_color(thickgc, &color);
1392 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);
1395 gdk_gc_copy(thickgc, gc);
1396 gdk_gc_set_line_attributes(thickgc, 2, GDK_LINE_SOLID, GDK_CAP_BUTT, GDK_JOIN_MITER);
1397 for (i=0; i<180; i++) {
1398 c = cos(DEG2RAD(i)*2 + baseangle);
1399 s = sin(DEG2RAD(i)*2 + baseangle);
1402 gdk_draw_line (d, gc, x1 + CR*c, y1 + CR*s, x1 + (CR+CW)*c, y1 + (CR+CW)*s);
1404 gdouble ticksize = 2*CW;
1405 gdk_draw_line (d, thickgc, x1 + (CR-CW)*c, y1 + (CR-CW)*s, x1 + (CR+ticksize)*c, y1 + (CR+ticksize)*s);
1409 gdk_draw_arc (d, gc, FALSE, x1-CR, y1-CR, 2*CR, 2*CR, 0, 64*360);
1410 gdk_draw_arc (d, gc, FALSE, x1-CR-CW, y1-CR-CW, 2*(CR+CW), 2*(CR+CW), 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 c = (CR+CW*2)*cos(baseangle);
1413 s = (CR+CW*2)*sin(baseangle);
1414 gdk_draw_line (d, gc, x1-c, y1-s, x1+c, y1+s);
1415 gdk_draw_line (d, gc, x1+s, y1-c, x1-s, y1+c);
1418 #define LABEL(x, y, w, h) { \
1419 gdk_draw_rectangle(d, labgc, TRUE, (x)-2, (y)-1, (w)+4, (h)+1); \
1420 gdk_draw_rectangle(d, gc, FALSE, (x)-2, (y)-1, (w)+4, (h)+1); \
1421 gdk_draw_layout(d, gc, (x), (y), pl); }
1423 gint wd, hd, xd, yd;
1424 gint wb, hb, xb, yb;
1426 pl = gtk_widget_create_pango_layout (GTK_WIDGET(vvp), NULL);
1427 pango_layout_set_font_description (pl, gtk_widget_get_style(GTK_WIDGET(vvp))->font_desc);
1428 pango_layout_set_text(pl, "N", -1);
1429 gdk_draw_layout(d, gc, x1-5, y1-CR-3*CW-8, pl);
1431 /* draw label with distance */
1432 vik_units_distance_t dist_units = a_vik_get_units_distance ();
1433 switch (dist_units) {
1434 case VIK_UNITS_DISTANCE_KILOMETRES:
1435 if (distance >= 1000 && distance < 100000) {
1436 g_sprintf(str, "%3.2f km", distance/1000.0);
1437 } else if (distance < 1000) {
1438 g_sprintf(str, "%d m", (int)distance);
1440 g_sprintf(str, "%d km", (int)distance/1000);
1443 case VIK_UNITS_DISTANCE_MILES:
1444 if (distance >= VIK_MILES_TO_METERS(1) && distance < VIK_MILES_TO_METERS(100)) {
1445 g_sprintf(str, "%3.2f miles", VIK_METERS_TO_MILES(distance));
1446 } else if (distance < VIK_MILES_TO_METERS(1)) {
1447 g_sprintf(str, "%d yards", (int)(distance*1.0936133));
1449 g_sprintf(str, "%d miles", (int)VIK_METERS_TO_MILES(distance));
1453 g_critical("Houston, we've had a problem. distance=%d", dist_units);
1456 pango_layout_set_text(pl, str, -1);
1458 pango_layout_get_pixel_size ( pl, &wd, &hd );
1460 xd = (x1+x2)/2 + dy;
1461 yd = (y1+y2)/2 - hd/2 - dx;
1463 xd = (x1+x2)/2 - dy;
1464 yd = (y1+y2)/2 - hd/2 + dx;
1467 if ( xd < -5 || yd < -5 || xd > vik_viewport_get_width(vvp)+5 || yd > vik_viewport_get_height(vvp)+5 ) {
1472 LABEL(xd, yd, wd, hd);
1474 /* draw label with bearing */
1475 g_sprintf(str, "%3.1f°", RAD2DEG(angle));
1476 pango_layout_set_text(pl, str, -1);
1477 pango_layout_get_pixel_size ( pl, &wb, &hb );
1478 xb = x1 + CR*cos(angle-M_PI_2);
1479 yb = y1 + CR*sin(angle-M_PI_2);
1481 if ( xb < -5 || yb < -5 || xb > vik_viewport_get_width(vvp)+5 || yb > vik_viewport_get_height(vvp)+5 ) {
1487 GdkRectangle r1 = {xd-2, yd-1, wd+4, hd+1}, r2 = {xb-2, yb-1, wb+4, hb+1};
1488 if (gdk_rectangle_intersect(&r1, &r2, &r2)) {
1492 LABEL(xb, yb, wb, hb);
1496 g_object_unref ( G_OBJECT ( pl ) );
1497 g_object_unref ( G_OBJECT ( labgc ) );
1498 g_object_unref ( G_OBJECT ( thickgc ) );
1504 gboolean has_oldcoord;
1506 } ruler_tool_state_t;
1508 static gpointer ruler_create (VikWindow *vw, VikViewport *vvp)
1510 ruler_tool_state_t *s = g_new(ruler_tool_state_t, 1);
1513 s->has_oldcoord = FALSE;
1517 static void ruler_destroy (ruler_tool_state_t *s)
1522 static VikLayerToolFuncStatus ruler_click (VikLayer *vl, GdkEventButton *event, ruler_tool_state_t *s)
1527 if ( event->button == 1 ) {
1528 gchar *lat=NULL, *lon=NULL;
1529 vik_viewport_screen_to_coord ( s->vvp, (gint) event->x, (gint) event->y, &coord );
1530 vik_coord_to_latlon ( &coord, &ll );
1531 a_coords_latlon_to_string ( &ll, &lat, &lon );
1532 if ( s->has_oldcoord ) {
1533 vik_units_distance_t dist_units = a_vik_get_units_distance ();
1534 switch (dist_units) {
1535 case VIK_UNITS_DISTANCE_KILOMETRES:
1536 temp = g_strdup_printf ( "%s %s DIFF %f meters", lat, lon, vik_coord_diff( &coord, &(s->oldcoord) ) );
1538 case VIK_UNITS_DISTANCE_MILES:
1539 temp = g_strdup_printf ( "%s %s DIFF %f miles", lat, lon, VIK_METERS_TO_MILES(vik_coord_diff( &coord, &(s->oldcoord) )) );
1542 temp = g_strdup_printf ("Just to keep the compiler happy");
1543 g_critical("Houston, we've had a problem. distance=%d", dist_units);
1546 s->has_oldcoord = FALSE;
1549 temp = g_strdup_printf ( "%s %s", lat, lon );
1550 s->has_oldcoord = TRUE;
1553 vik_statusbar_set_message ( s->vw->viking_vs, VIK_STATUSBAR_INFO, temp );
1556 s->oldcoord = coord;
1559 vik_viewport_set_center_screen ( s->vvp, (gint) event->x, (gint) event->y );
1560 draw_update ( s->vw );
1562 return VIK_LAYER_TOOL_ACK;
1565 static VikLayerToolFuncStatus ruler_move (VikLayer *vl, GdkEventMotion *event, ruler_tool_state_t *s)
1567 VikViewport *vvp = s->vvp;
1568 VikWindow *vw = s->vw;
1574 if ( s->has_oldcoord ) {
1575 int oldx, oldy, w1, h1, w2, h2;
1576 static GdkPixmap *buf = NULL;
1577 gchar *lat=NULL, *lon=NULL;
1578 w1 = vik_viewport_get_width(vvp);
1579 h1 = vik_viewport_get_height(vvp);
1581 buf = gdk_pixmap_new ( gtk_widget_get_window(GTK_WIDGET(vvp)), w1, h1, -1 );
1583 gdk_drawable_get_size(buf, &w2, &h2);
1584 if (w1 != w2 || h1 != h2) {
1585 g_object_unref ( G_OBJECT ( buf ) );
1586 buf = gdk_pixmap_new ( gtk_widget_get_window(GTK_WIDGET(vvp)), w1, h1, -1 );
1589 vik_viewport_screen_to_coord ( vvp, (gint) event->x, (gint) event->y, &coord );
1590 vik_coord_to_latlon ( &coord, &ll );
1591 vik_viewport_coord_to_screen ( vvp, &s->oldcoord, &oldx, &oldy );
1593 gdk_draw_drawable (buf, gtk_widget_get_style(GTK_WIDGET(vvp))->black_gc,
1594 vik_viewport_get_pixmap(vvp), 0, 0, 0, 0, -1, -1);
1595 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)) );
1596 if (draw_buf_done) {
1597 static gpointer pass_along[3];
1598 pass_along[0] = gtk_widget_get_window(GTK_WIDGET(vvp));
1599 pass_along[1] = gtk_widget_get_style(GTK_WIDGET(vvp))->black_gc;
1600 pass_along[2] = buf;
1601 g_idle_add_full (G_PRIORITY_HIGH_IDLE + 10, draw_buf, pass_along, NULL);
1602 draw_buf_done = FALSE;
1604 a_coords_latlon_to_string(&ll, &lat, &lon);
1605 vik_units_distance_t dist_units = a_vik_get_units_distance ();
1606 switch (dist_units) {
1607 case VIK_UNITS_DISTANCE_KILOMETRES:
1608 temp = g_strdup_printf ( "%s %s DIFF %f meters", lat, lon, vik_coord_diff( &coord, &(s->oldcoord) ) );
1610 case VIK_UNITS_DISTANCE_MILES:
1611 temp = g_strdup_printf ( "%s %s DIFF %f miles", lat, lon, VIK_METERS_TO_MILES (vik_coord_diff( &coord, &(s->oldcoord) )) );
1614 temp = g_strdup_printf ("Just to keep the compiler happy");
1615 g_critical("Houston, we've had a problem. distance=%d", dist_units);
1617 vik_statusbar_set_message ( vw->viking_vs, VIK_STATUSBAR_INFO, temp );
1620 return VIK_LAYER_TOOL_ACK;
1623 static VikLayerToolFuncStatus ruler_release (VikLayer *vl, GdkEventButton *event, ruler_tool_state_t *s)
1625 return VIK_LAYER_TOOL_ACK;
1628 static void ruler_deactivate (VikLayer *vl, ruler_tool_state_t *s)
1630 draw_update ( s->vw );
1633 static gboolean ruler_key_press (VikLayer *vl, GdkEventKey *event, ruler_tool_state_t *s)
1635 if (event->keyval == GDK_Escape) {
1636 s->has_oldcoord = FALSE;
1637 ruler_deactivate ( vl, s );
1640 // Regardless of whether we used it, return false so other GTK things may use it
1644 static VikToolInterface ruler_tool =
1645 // NB Ctrl+Shift+R is used for Refresh (deemed more important), so use 'U' instead
1646 { { "Ruler", "vik-icon-ruler", N_("_Ruler"), "<control><shift>U", N_("Ruler Tool"), 2 },
1647 (VikToolConstructorFunc) ruler_create,
1648 (VikToolDestructorFunc) ruler_destroy,
1649 (VikToolActivationFunc) NULL,
1650 (VikToolActivationFunc) ruler_deactivate,
1651 (VikToolMouseFunc) ruler_click,
1652 (VikToolMouseMoveFunc) ruler_move,
1653 (VikToolMouseFunc) ruler_release,
1654 (VikToolKeyFunc) ruler_key_press,
1656 GDK_CURSOR_IS_PIXMAP,
1657 &cursor_ruler_pixbuf };
1658 /*** end ruler code ********************************************************/
1662 /********************************************************************************
1664 ********************************************************************************/
1669 // Track zoom bounds for zoom tool with shift modifier:
1670 gboolean bounds_active;
1673 } zoom_tool_state_t;
1676 * In case the screen size has changed
1678 static void zoomtool_resize_pixmap (zoom_tool_state_t *zts)
1682 // Allocate a drawing area the size of the viewport
1683 w1 = vik_viewport_get_width ( zts->vw->viking_vvp );
1684 h1 = vik_viewport_get_height ( zts->vw->viking_vvp );
1686 if ( !zts->pixmap ) {
1688 zts->pixmap = gdk_pixmap_new ( gtk_widget_get_window(GTK_WIDGET(zts->vw->viking_vvp)), w1, h1, -1 );
1691 gdk_drawable_get_size ( zts->pixmap, &w2, &h2 );
1693 if ( w1 != w2 || h1 != h2 ) {
1694 // Has changed - delete and recreate with new values
1695 g_object_unref ( G_OBJECT ( zts->pixmap ) );
1696 zts->pixmap = gdk_pixmap_new ( gtk_widget_get_window(GTK_WIDGET(zts->vw->viking_vvp)), w1, h1, -1 );
1700 static gpointer zoomtool_create (VikWindow *vw, VikViewport *vvp)
1702 zoom_tool_state_t *zts = g_new(zoom_tool_state_t, 1);
1707 zts->bounds_active = FALSE;
1711 static void zoomtool_destroy ( zoom_tool_state_t *zts)
1714 g_object_unref ( G_OBJECT ( zts->pixmap ) );
1718 static VikLayerToolFuncStatus zoomtool_click (VikLayer *vl, GdkEventButton *event, zoom_tool_state_t *zts)
1720 zts->vw->modified = TRUE;
1721 guint modifiers = event->state & (GDK_SHIFT_MASK | GDK_CONTROL_MASK);
1725 gint center_x = vik_viewport_get_width ( zts->vw->viking_vvp ) / 2;
1726 gint center_y = vik_viewport_get_height ( zts->vw->viking_vvp ) / 2;
1728 gboolean skip_update = FALSE;
1730 zts->bounds_active = FALSE;
1732 if ( modifiers == (GDK_CONTROL_MASK | GDK_SHIFT_MASK) ) {
1733 // This zoom is on the center position
1734 vik_viewport_set_center_screen ( zts->vw->viking_vvp, center_x, center_y );
1735 if ( event->button == 1 )
1736 vik_viewport_zoom_in (zts->vw->viking_vvp);
1737 else if ( event->button == 3 )
1738 vik_viewport_zoom_out (zts->vw->viking_vvp);
1740 else if ( modifiers == GDK_CONTROL_MASK ) {
1741 // This zoom is to recenter on the mouse position
1742 vik_viewport_set_center_screen ( zts->vw->viking_vvp, (gint) event->x, (gint) event->y );
1743 if ( event->button == 1 )
1744 vik_viewport_zoom_in (zts->vw->viking_vvp);
1745 else if ( event->button == 3 )
1746 vik_viewport_zoom_out (zts->vw->viking_vvp);
1748 else if ( modifiers == GDK_SHIFT_MASK ) {
1749 // Get start of new zoom bounds
1750 if ( event->button == 1 ) {
1751 zts->bounds_active = TRUE;
1752 zts->start_x = (gint) event->x;
1753 zts->start_y = (gint) event->y;
1758 /* make sure mouse is still over the same point on the map when we zoom */
1759 vik_viewport_screen_to_coord ( zts->vw->viking_vvp, event->x, event->y, &coord );
1760 if ( event->button == 1 )
1761 vik_viewport_zoom_in (zts->vw->viking_vvp);
1762 else if ( event->button == 3 )
1763 vik_viewport_zoom_out(zts->vw->viking_vvp);
1764 vik_viewport_coord_to_screen ( zts->vw->viking_vvp, &coord, &x, &y );
1765 vik_viewport_set_center_screen ( zts->vw->viking_vvp,
1766 center_x + (x - event->x),
1767 center_y + (y - event->y) );
1771 draw_update ( zts->vw );
1773 return VIK_LAYER_TOOL_ACK;
1776 static VikLayerToolFuncStatus zoomtool_move (VikLayer *vl, GdkEventMotion *event, zoom_tool_state_t *zts)
1778 guint modifiers = event->state & (GDK_SHIFT_MASK | GDK_CONTROL_MASK);
1780 if ( zts->bounds_active && modifiers == GDK_SHIFT_MASK ) {
1781 zoomtool_resize_pixmap ( zts );
1783 // Blank out currently drawn area
1784 gdk_draw_drawable ( zts->pixmap,
1785 gtk_widget_get_style(GTK_WIDGET(zts->vw->viking_vvp))->black_gc,
1786 vik_viewport_get_pixmap(zts->vw->viking_vvp),
1787 0, 0, 0, 0, -1, -1);
1789 // Calculate new box starting point & size in pixels
1790 int xx, yy, width, height;
1791 if ( event->y > zts->start_y ) {
1793 height = event->y-zts->start_y;
1797 height = zts->start_y-event->y;
1799 if ( event->x > zts->start_x ) {
1801 width = event->x-zts->start_x;
1805 width = zts->start_x-event->x;
1809 gdk_draw_rectangle (zts->pixmap, gtk_widget_get_style(GTK_WIDGET(zts->vw->viking_vvp))->black_gc, FALSE, xx, yy, width, height);
1811 // Only actually draw when there's time to do so
1812 if (draw_buf_done) {
1813 static gpointer pass_along[3];
1814 pass_along[0] = gtk_widget_get_window(GTK_WIDGET(zts->vw->viking_vvp));
1815 pass_along[1] = gtk_widget_get_style(GTK_WIDGET(zts->vw->viking_vvp))->black_gc;
1816 pass_along[2] = zts->pixmap;
1817 g_idle_add_full (G_PRIORITY_HIGH_IDLE + 10, draw_buf, pass_along, NULL);
1818 draw_buf_done = FALSE;
1822 zts->bounds_active = FALSE;
1824 return VIK_LAYER_TOOL_ACK;
1827 static VikLayerToolFuncStatus zoomtool_release (VikLayer *vl, GdkEventButton *event, zoom_tool_state_t *zts)
1829 guint modifiers = event->state & (GDK_SHIFT_MASK | GDK_CONTROL_MASK);
1831 // Ensure haven't just released on the exact same position
1832 // i.e. probably haven't moved the mouse at all
1833 if ( zts->bounds_active && modifiers == GDK_SHIFT_MASK &&
1834 ( event->x < zts->start_x-5 || event->x > zts->start_x+5 ) &&
1835 ( event->y < zts->start_y-5 || event->y > zts->start_y+5 ) ) {
1837 VikCoord coord1, coord2;
1838 vik_viewport_screen_to_coord ( zts->vw->viking_vvp, zts->start_x, zts->start_y, &coord1);
1839 vik_viewport_screen_to_coord ( zts->vw->viking_vvp, event->x, event->y, &coord2);
1841 // From the extend of the bounds pick the best zoom level
1842 // c.f. trw_layer_zoom_to_show_latlons()
1843 // Maybe refactor...
1844 struct LatLon ll1, ll2;
1845 vik_coord_to_latlon(&coord1, &ll1);
1846 vik_coord_to_latlon(&coord2, &ll2);
1847 struct LatLon average = { (ll1.lat+ll2.lat)/2,
1848 (ll1.lon+ll2.lon)/2 };
1850 VikCoord new_center;
1851 vik_coord_load_from_latlon ( &new_center, vik_viewport_get_coord_mode ( zts->vw->viking_vvp ), &average );
1852 vik_viewport_set_center_coord ( zts->vw->viking_vvp, &new_center, FALSE );
1854 /* Convert into definite 'smallest' and 'largest' positions */
1855 struct LatLon minmin;
1856 if ( ll1.lat < ll2.lat )
1857 minmin.lat = ll1.lat;
1859 minmin.lat = ll2.lat;
1861 struct LatLon maxmax;
1862 if ( ll1.lon > ll2.lon )
1863 maxmax.lon = ll1.lon;
1865 maxmax.lon = ll2.lon;
1867 /* Always recalculate the 'best' zoom level */
1868 gdouble zoom = VIK_VIEWPORT_MIN_ZOOM;
1869 vik_viewport_set_zoom ( zts->vw->viking_vvp, zoom );
1871 gdouble min_lat, max_lat, min_lon, max_lon;
1872 /* Should only be a maximum of about 18 iterations from min to max zoom levels */
1873 while ( zoom <= VIK_VIEWPORT_MAX_ZOOM ) {
1874 vik_viewport_get_min_max_lat_lon ( zts->vw->viking_vvp, &min_lat, &max_lat, &min_lon, &max_lon );
1875 /* NB I think the logic used in this test to determine if the bounds is within view
1876 fails if track goes across 180 degrees longitude.
1877 Hopefully that situation is not too common...
1878 Mind you viking doesn't really do edge locations to well anyway */
1879 if ( min_lat < minmin.lat &&
1880 max_lat > minmin.lat &&
1881 min_lon < maxmax.lon &&
1882 max_lon > maxmax.lon )
1883 /* Found within zoom level */
1888 vik_viewport_set_zoom ( zts->vw->viking_vvp, zoom );
1892 // When pressing shift and clicking for zoom, then jump three levels
1893 if ( modifiers == GDK_SHIFT_MASK ) {
1894 // Zoom in/out by three if possible
1895 vik_viewport_set_center_screen ( zts->vw->viking_vvp, event->x, event->y );
1896 if ( event->button == 1 ) {
1897 vik_viewport_zoom_in ( zts->vw->viking_vvp );
1898 vik_viewport_zoom_in ( zts->vw->viking_vvp );
1899 vik_viewport_zoom_in ( zts->vw->viking_vvp );
1901 else if ( event->button == 3 ) {
1902 vik_viewport_zoom_out ( zts->vw->viking_vvp );
1903 vik_viewport_zoom_out ( zts->vw->viking_vvp );
1904 vik_viewport_zoom_out ( zts->vw->viking_vvp );
1909 draw_update ( zts->vw );
1912 zts->bounds_active = FALSE;
1914 return VIK_LAYER_TOOL_ACK;
1917 static VikToolInterface zoom_tool =
1918 { { "Zoom", "vik-icon-zoom", N_("_Zoom"), "<control><shift>Z", N_("Zoom Tool"), 1 },
1919 (VikToolConstructorFunc) zoomtool_create,
1920 (VikToolDestructorFunc) zoomtool_destroy,
1921 (VikToolActivationFunc) NULL,
1922 (VikToolActivationFunc) NULL,
1923 (VikToolMouseFunc) zoomtool_click,
1924 (VikToolMouseMoveFunc) zoomtool_move,
1925 (VikToolMouseFunc) zoomtool_release,
1928 GDK_CURSOR_IS_PIXMAP,
1929 &cursor_zoom_pixbuf };
1930 /*** end zoom code ********************************************************/
1932 /********************************************************************************
1934 ********************************************************************************/
1935 static gpointer pantool_create (VikWindow *vw, VikViewport *vvp)
1940 // NB Double clicking means this gets called THREE times!!!
1941 static VikLayerToolFuncStatus pantool_click (VikLayer *vl, GdkEventButton *event, VikWindow *vw)
1943 vw->modified = TRUE;
1945 if ( event->type == GDK_2BUTTON_PRESS ) {
1946 // Zoom in / out on double click
1947 // No need to change the center as that has already occurred in the first click of a double click occurrence
1948 if ( event->button == 1 ) {
1949 guint modifier = event->state & GDK_SHIFT_MASK;
1951 vik_viewport_zoom_out ( vw->viking_vvp );
1953 vik_viewport_zoom_in ( vw->viking_vvp );
1955 else if ( event->button == 3 )
1956 vik_viewport_zoom_out ( vw->viking_vvp );
1961 // Standard pan click
1962 if ( event->button == 1 )
1963 vik_window_pan_click ( vw, event );
1965 return VIK_LAYER_TOOL_ACK;
1968 static VikLayerToolFuncStatus pantool_move (VikLayer *vl, GdkEventMotion *event, VikWindow *vw)
1970 vik_window_pan_move ( vw, event );
1971 return VIK_LAYER_TOOL_ACK;
1974 static VikLayerToolFuncStatus pantool_release (VikLayer *vl, GdkEventButton *event, VikWindow *vw)
1976 if ( event->button == 1 )
1977 vik_window_pan_release ( vw, event );
1978 return VIK_LAYER_TOOL_ACK;
1981 static VikToolInterface pan_tool =
1982 { { "Pan", "vik-icon-pan", N_("_Pan"), "<control><shift>P", N_("Pan Tool"), 0 },
1983 (VikToolConstructorFunc) pantool_create,
1984 (VikToolDestructorFunc) NULL,
1985 (VikToolActivationFunc) NULL,
1986 (VikToolActivationFunc) NULL,
1987 (VikToolMouseFunc) pantool_click,
1988 (VikToolMouseMoveFunc) pantool_move,
1989 (VikToolMouseFunc) pantool_release,
1993 /*** end pan code ********************************************************/
1995 /********************************************************************************
1997 ********************************************************************************/
1998 static gpointer selecttool_create (VikWindow *vw, VikViewport *vvp)
2000 tool_ed_t *t = g_new(tool_ed_t, 1);
2004 t->is_waypoint = FALSE;
2008 static void selecttool_destroy (tool_ed_t *t)
2016 GdkEventButton *event;
2017 tool_ed_t *tool_edit;
2020 static void click_layer_selected (VikLayer *vl, clicker *ck)
2022 /* Do nothing when function call returns true; */
2023 /* i.e. stop on first found item */
2026 if ( vik_layer_get_interface(vl->type)->select_click )
2027 ck->cont = !vik_layer_get_interface(vl->type)->select_click ( vl, ck->event, ck->vvp, ck->tool_edit );
2030 static VikLayerToolFuncStatus selecttool_click (VikLayer *vl, GdkEventButton *event, tool_ed_t *t)
2032 /* Only allow selection on primary button */
2033 if ( event->button == 1 ) {
2034 /* Enable click to apply callback to potentially all track/waypoint layers */
2035 /* Useful as we can find things that aren't necessarily in the currently selected layer */
2036 GList* gl = vik_layers_panel_get_all_layers_of_type ( t->vw->viking_vlp, VIK_LAYER_TRW, FALSE ); // Don't get invisible layers
2039 ck.vvp = t->vw->viking_vvp;
2042 g_list_foreach ( gl, (GFunc) click_layer_selected, &ck );
2045 // If nothing found then deselect & redraw screen if necessary to remove the highlight
2048 VikTreeview *vtv = vik_layers_panel_get_treeview ( t->vw->viking_vlp );
2050 if ( vik_treeview_get_selected_iter ( vtv, &iter ) ) {
2051 // Only clear if selected thing is a TrackWaypoint layer or a sublayer
2052 gint type = vik_treeview_item_get_type ( vtv, &iter );
2053 if ( type == VIK_TREEVIEW_TYPE_SUBLAYER ||
2054 VIK_LAYER(vik_treeview_item_get_pointer ( vtv, &iter ))->type == VIK_LAYER_TRW ) {
2056 vik_treeview_item_unselect ( vtv, &iter );
2057 if ( vik_window_clear_highlight ( t->vw ) )
2058 draw_update ( t->vw );
2063 else if ( ( event->button == 3 ) && ( vl && ( vl->type == VIK_LAYER_TRW ) ) ) {
2065 /* Act on currently selected item to show menu */
2066 if ( t->vw->selected_track || t->vw->selected_waypoint )
2067 if ( vik_layer_get_interface(vl->type)->show_viewport_menu )
2068 vik_layer_get_interface(vl->type)->show_viewport_menu ( vl, event, t->vw->viking_vvp );
2071 return VIK_LAYER_TOOL_ACK;
2074 static VikLayerToolFuncStatus selecttool_move (VikLayer *vl, GdkEventButton *event, tool_ed_t *t)
2076 /* Only allow selection on primary button */
2077 if ( event->button == 1 ) {
2078 // Don't care about vl here
2080 if ( vik_layer_get_interface(VIK_LAYER_TRW)->select_move )
2081 vik_layer_get_interface(VIK_LAYER_TRW)->select_move ( vl, event, t->vvp, t );
2083 return VIK_LAYER_TOOL_ACK;
2086 static VikLayerToolFuncStatus selecttool_release (VikLayer *vl, GdkEventButton *event, tool_ed_t *t)
2088 /* Only allow selection on primary button */
2089 if ( event->button == 1 ) {
2090 // Don't care about vl here
2092 if ( vik_layer_get_interface(VIK_LAYER_TRW)->select_release )
2093 vik_layer_get_interface(VIK_LAYER_TRW)->select_release ( (VikLayer*)t->vtl, event, t->vvp, t );
2095 return VIK_LAYER_TOOL_ACK;
2098 static VikToolInterface select_tool =
2099 { { "Select", "vik-icon-select", N_("_Select"), "<control><shift>S", N_("Select Tool"), 3 },
2100 (VikToolConstructorFunc) selecttool_create,
2101 (VikToolDestructorFunc) selecttool_destroy,
2102 (VikToolActivationFunc) NULL,
2103 (VikToolActivationFunc) NULL,
2104 (VikToolMouseFunc) selecttool_click,
2105 (VikToolMouseMoveFunc) selecttool_move,
2106 (VikToolMouseFunc) selecttool_release,
2107 (VikToolKeyFunc) NULL,
2112 /*** end select tool code ********************************************************/
2114 static void draw_pan_cb ( GtkAction *a, VikWindow *vw )
2116 // Since the treeview cell editting intercepts standard keyboard handlers, it means we can receive events here
2117 // Thus if currently editting, ensure we don't move the viewport when Ctrl+<arrow> is received
2118 VikLayer *sel = vik_layers_panel_get_selected ( vw->viking_vlp );
2119 if ( sel && vik_treeview_get_editing ( sel->vt ) )
2122 if (!strcmp(gtk_action_get_name(a), "PanNorth")) {
2123 vik_viewport_set_center_screen ( vw->viking_vvp, vik_viewport_get_width(vw->viking_vvp)/2, 0 );
2124 } else if (!strcmp(gtk_action_get_name(a), "PanEast")) {
2125 vik_viewport_set_center_screen ( vw->viking_vvp, vik_viewport_get_width(vw->viking_vvp), vik_viewport_get_height(vw->viking_vvp)/2 );
2126 } else if (!strcmp(gtk_action_get_name(a), "PanSouth")) {
2127 vik_viewport_set_center_screen ( vw->viking_vvp, vik_viewport_get_width(vw->viking_vvp)/2, vik_viewport_get_height(vw->viking_vvp) );
2128 } else if (!strcmp(gtk_action_get_name(a), "PanWest")) {
2129 vik_viewport_set_center_screen ( vw->viking_vvp, 0, vik_viewport_get_height(vw->viking_vvp)/2 );
2134 static void full_screen_cb ( GtkAction *a, VikWindow *vw )
2136 GtkWidget *check_box = gtk_ui_manager_get_widget ( vw->uim, "/ui/MainMenu/View/FullScreen" );
2137 g_assert(check_box);
2138 gboolean state = gtk_check_menu_item_get_active ( GTK_CHECK_MENU_ITEM(check_box));
2140 gtk_window_fullscreen ( GTK_WINDOW(vw) );
2142 gtk_window_unfullscreen ( GTK_WINDOW(vw) );
2145 static void draw_zoom_cb ( GtkAction *a, VikWindow *vw )
2149 if (!strcmp(gtk_action_get_name(a), "ZoomIn")) {
2152 else if (!strcmp(gtk_action_get_name(a), "ZoomOut")) {
2155 else if (!strcmp(gtk_action_get_name(a), "Zoom0.25")) {
2158 else if (!strcmp(gtk_action_get_name(a), "Zoom0.5")) {
2162 gchar *s = (gchar *)gtk_action_get_name(a);
2168 case -3: vik_viewport_zoom_in ( vw->viking_vvp ); break;
2169 case -4: vik_viewport_zoom_out ( vw->viking_vvp ); break;
2170 case -1: vik_viewport_set_zoom ( vw->viking_vvp, 0.5 ); break;
2171 case -2: vik_viewport_set_zoom ( vw->viking_vvp, 0.25 ); break;
2172 default: vik_viewport_set_zoom ( vw->viking_vvp, what );
2177 static void draw_goto_cb ( GtkAction *a, VikWindow *vw )
2179 VikCoord new_center;
2181 if (!strcmp(gtk_action_get_name(a), "GotoLL")) {
2182 struct LatLon ll, llold;
2183 vik_coord_to_latlon ( vik_viewport_get_center ( vw->viking_vvp ), &llold );
2184 if ( a_dialog_goto_latlon ( GTK_WINDOW(vw), &ll, &llold ) )
2185 vik_coord_load_from_latlon ( &new_center, vik_viewport_get_coord_mode(vw->viking_vvp), &ll );
2189 else if (!strcmp(gtk_action_get_name(a), "GotoUTM")) {
2190 struct UTM utm, utmold;
2191 vik_coord_to_utm ( vik_viewport_get_center ( vw->viking_vvp ), &utmold );
2192 if ( a_dialog_goto_utm ( GTK_WINDOW(vw), &utm, &utmold ) )
2193 vik_coord_load_from_utm ( &new_center, vik_viewport_get_coord_mode(vw->viking_vvp), &utm );
2198 g_critical("Houston, we've had a problem.");
2202 vik_viewport_set_center_coord ( vw->viking_vvp, &new_center, TRUE );
2207 * center_changed_cb:
2209 static void center_changed_cb ( VikWindow *vw )
2211 // ATM Keep back always available, so when we pan - we can jump to the last requested position
2213 GtkAction* action_back = gtk_action_group_get_action ( vw->action_group, "GoBack" );
2214 if ( action_back ) {
2215 gtk_action_set_sensitive ( action_back, vik_viewport_back_available(vw->viking_vvp) );
2218 GtkAction* action_forward = gtk_action_group_get_action ( vw->action_group, "GoForward" );
2219 if ( action_forward ) {
2220 gtk_action_set_sensitive ( action_forward, vik_viewport_forward_available(vw->viking_vvp) );
2225 * draw_goto_back_and_forth:
2227 static void draw_goto_back_and_forth ( GtkAction *a, VikWindow *vw )
2229 gboolean changed = FALSE;
2230 if (!strcmp(gtk_action_get_name(a), "GoBack")) {
2231 changed = vik_viewport_go_back ( vw->viking_vvp );
2233 else if (!strcmp(gtk_action_get_name(a), "GoForward")) {
2234 changed = vik_viewport_go_forward ( vw->viking_vvp );
2240 // Recheck buttons sensitivities, as the center changed signal is not sent on back/forward changes
2241 // (otherwise we would get stuck in an infinite loop!)
2242 center_changed_cb ( vw );
2249 * Refresh maps displayed
2251 static void draw_refresh_cb ( GtkAction *a, VikWindow *vw )
2253 // Only get 'new' maps
2254 simple_map_update ( vw, TRUE );
2257 static void menu_addlayer_cb ( GtkAction *a, VikWindow *vw )
2259 VikLayerTypeEnum type;
2260 for ( type = 0; type < VIK_LAYER_NUM_TYPES; type++ ) {
2261 if (!strcmp(vik_layer_get_interface(type)->name, gtk_action_get_name(a))) {
2262 if ( vik_layers_panel_new_layer ( vw->viking_vlp, type ) ) {
2264 vw->modified = TRUE;
2270 static void menu_copy_layer_cb ( GtkAction *a, VikWindow *vw )
2272 a_clipboard_copy_selected ( vw->viking_vlp );
2275 static void menu_cut_layer_cb ( GtkAction *a, VikWindow *vw )
2277 vik_layers_panel_cut_selected ( vw->viking_vlp );
2278 vw->modified = TRUE;
2281 static void menu_paste_layer_cb ( GtkAction *a, VikWindow *vw )
2283 if ( vik_layers_panel_paste_selected ( vw->viking_vlp ) )
2285 vw->modified = TRUE;
2289 static void menu_properties_cb ( GtkAction *a, VikWindow *vw )
2291 if ( ! vik_layers_panel_properties ( vw->viking_vlp ) )
2292 a_dialog_info_msg ( GTK_WINDOW(vw), _("You must select a layer to show its properties.") );
2295 static void help_help_cb ( GtkAction *a, VikWindow *vw )
2298 ShellExecute(NULL, "open", ""PACKAGE".pdf", NULL, NULL, SW_SHOWNORMAL);
2301 uri = g_strdup_printf("ghelp:%s", PACKAGE);
2302 GError *error = NULL;
2303 gboolean show = gtk_show_uri (NULL, uri, GDK_CURRENT_TIME, &error);
2304 if ( !show && !error )
2305 // No error to show, so unlikely this will get called
2306 a_dialog_error_msg ( GTK_WINDOW(vw), _("The help system is not available.") );
2309 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 );
2310 g_error_free ( error );
2313 #endif /* WINDOWS */
2316 static void help_about_cb ( GtkAction *a, VikWindow *vw )
2318 a_dialog_about(GTK_WINDOW(vw));
2321 static void menu_delete_layer_cb ( GtkAction *a, VikWindow *vw )
2323 if ( vik_layers_panel_get_selected ( vw->viking_vlp ) )
2325 vik_layers_panel_delete_selected ( vw->viking_vlp );
2326 vw->modified = TRUE;
2329 a_dialog_info_msg ( GTK_WINDOW(vw), _("You must select a layer to delete.") );
2332 static void view_side_panel_cb ( GtkAction *a, VikWindow *vw )
2334 GtkWidget *check_box = gtk_ui_manager_get_widget ( vw->uim, "/ui/MainMenu/View/SetShow/ViewSidePanel" );
2335 g_assert(check_box);
2336 gboolean state = gtk_check_menu_item_get_active ( GTK_CHECK_MENU_ITEM(check_box));
2338 gtk_widget_show(GTK_WIDGET(vw->viking_vlp));
2340 gtk_widget_hide(GTK_WIDGET(vw->viking_vlp));
2343 static void view_statusbar_cb ( GtkAction *a, VikWindow *vw )
2345 GtkWidget *check_box = gtk_ui_manager_get_widget ( vw->uim, "/ui/MainMenu/View/SetShow/ViewStatusBar" );
2348 gboolean state = gtk_check_menu_item_get_active ( GTK_CHECK_MENU_ITEM(check_box) );
2350 gtk_widget_show ( GTK_WIDGET(vw->viking_vs) );
2352 gtk_widget_hide ( GTK_WIDGET(vw->viking_vs) );
2355 static void view_toolbar_cb ( GtkAction *a, VikWindow *vw )
2357 GtkWidget *check_box = gtk_ui_manager_get_widget ( vw->uim, "/ui/MainMenu/View/SetShow/ViewToolbar" );
2360 gboolean state = gtk_check_menu_item_get_active ( GTK_CHECK_MENU_ITEM(check_box) );
2362 gtk_widget_show ( GTK_WIDGET(vw->toolbar) );
2364 gtk_widget_hide ( GTK_WIDGET(vw->toolbar) );
2367 static void view_main_menu_cb ( GtkAction *a, VikWindow *vw )
2369 GtkWidget *check_box = gtk_ui_manager_get_widget ( vw->uim, "/ui/MainMenu/View/SetShow/ViewMainMenu" );
2372 gboolean state = gtk_check_menu_item_get_active ( GTK_CHECK_MENU_ITEM(check_box) );
2374 gtk_widget_hide ( gtk_ui_manager_get_widget ( vw->uim, "/ui/MainMenu" ) );
2376 gtk_widget_show ( gtk_ui_manager_get_widget ( vw->uim, "/ui/MainMenu" ) );
2379 /***************************************
2380 ** tool management routines
2382 ***************************************/
2384 static toolbox_tools_t* toolbox_create(VikWindow *vw)
2386 toolbox_tools_t *vt = g_new(toolbox_tools_t, 1);
2389 vt->active_tool = -1;
2394 static void toolbox_add_tool(toolbox_tools_t *vt, VikToolInterface *vti, gint layer_type )
2396 vt->tools = g_renew(toolbox_tool_t, vt->tools, vt->n_tools+1);
2397 vt->tools[vt->n_tools].ti = *vti;
2398 vt->tools[vt->n_tools].layer_type = layer_type;
2400 vt->tools[vt->n_tools].state = vti->create(vt->vw, vt->vw->viking_vvp);
2403 vt->tools[vt->n_tools].state = NULL;
2408 static int toolbox_get_tool(toolbox_tools_t *vt, const gchar *tool_name)
2411 for (i=0; i<vt->n_tools; i++) {
2412 if (!strcmp(tool_name, vt->tools[i].ti.radioActionEntry.name)) {
2419 static void toolbox_activate(toolbox_tools_t *vt, const gchar *tool_name)
2421 int tool = toolbox_get_tool(vt, tool_name);
2422 toolbox_tool_t *t = &vt->tools[tool];
2423 VikLayer *vl = vik_layers_panel_get_selected ( vt->vw->viking_vlp );
2425 if (tool == vt->n_tools) {
2426 g_critical("trying to activate a non-existent tool...");
2429 /* is the tool already active? */
2430 if (vt->active_tool == tool) {
2434 if (vt->active_tool != -1) {
2435 if (vt->tools[vt->active_tool].ti.deactivate) {
2436 vt->tools[vt->active_tool].ti.deactivate(NULL, vt->tools[vt->active_tool].state);
2439 if (t->ti.activate) {
2440 t->ti.activate(vl, t->state);
2442 vt->active_tool = tool;
2445 static const GdkCursor *toolbox_get_cursor(toolbox_tools_t *vt, const gchar *tool_name)
2447 int tool = toolbox_get_tool(vt, tool_name);
2448 toolbox_tool_t *t = &vt->tools[tool];
2449 if (t->ti.cursor == NULL) {
2450 if (t->ti.cursor_type == GDK_CURSOR_IS_PIXMAP && t->ti.cursor_data != NULL) {
2451 GError *cursor_load_err = NULL;
2452 GdkPixbuf *cursor_pixbuf = gdk_pixbuf_from_pixdata (t->ti.cursor_data, FALSE, &cursor_load_err);
2453 /* TODO: settable offeset */
2454 t->ti.cursor = gdk_cursor_new_from_pixbuf ( gdk_display_get_default(), cursor_pixbuf, 3, 3 );
2455 g_object_unref ( G_OBJECT(cursor_pixbuf) );
2457 t->ti.cursor = gdk_cursor_new ( t->ti.cursor_type );
2460 return t->ti.cursor;
2463 static void toolbox_click (toolbox_tools_t *vt, GdkEventButton *event)
2465 VikLayer *vl = vik_layers_panel_get_selected ( vt->vw->viking_vlp );
2466 if (vt->active_tool != -1 && vt->tools[vt->active_tool].ti.click) {
2467 gint ltype = vt->tools[vt->active_tool].layer_type;
2468 if ( ltype == TOOL_LAYER_TYPE_NONE || (vl && ltype == vl->type) )
2469 vt->tools[vt->active_tool].ti.click(vl, event, vt->tools[vt->active_tool].state);
2473 static void toolbox_move (toolbox_tools_t *vt, GdkEventMotion *event)
2475 VikLayer *vl = vik_layers_panel_get_selected ( vt->vw->viking_vlp );
2476 if (vt->active_tool != -1 && vt->tools[vt->active_tool].ti.move) {
2477 gint ltype = vt->tools[vt->active_tool].layer_type;
2478 if ( ltype == TOOL_LAYER_TYPE_NONE || (vl && ltype == vl->type) )
2479 if ( VIK_LAYER_TOOL_ACK_GRAB_FOCUS == vt->tools[vt->active_tool].ti.move(vl, event, vt->tools[vt->active_tool].state) )
2480 gtk_widget_grab_focus ( GTK_WIDGET(vt->vw->viking_vvp) );
2484 static void toolbox_release (toolbox_tools_t *vt, GdkEventButton *event)
2486 VikLayer *vl = vik_layers_panel_get_selected ( vt->vw->viking_vlp );
2487 if (vt->active_tool != -1 && vt->tools[vt->active_tool].ti.release ) {
2488 gint ltype = vt->tools[vt->active_tool].layer_type;
2489 if ( ltype == TOOL_LAYER_TYPE_NONE || (vl && ltype == vl->type) )
2490 vt->tools[vt->active_tool].ti.release(vl, event, vt->tools[vt->active_tool].state);
2493 /** End tool management ************************************/
2495 void vik_window_enable_layer_tool ( VikWindow *vw, gint layer_id, gint tool_id )
2497 gtk_action_activate ( gtk_action_group_get_action ( vw->action_group, vik_layer_get_interface(layer_id)->tools[tool_id].radioActionEntry.name ) );
2500 /* this function gets called whenever a toolbar tool is clicked */
2501 static void menu_tool_cb ( GtkAction *old, GtkAction *a, VikWindow *vw )
2503 /* White Magic, my friends ... White Magic... */
2505 toolbox_activate(vw->vt, gtk_action_get_name(a));
2507 vw->viewport_cursor = (GdkCursor *)toolbox_get_cursor(vw->vt, gtk_action_get_name(a));
2509 if ( gtk_widget_get_window(GTK_WIDGET(vw->viking_vvp)) )
2510 /* We set cursor, even if it is NULL: it resets to default */
2511 gdk_window_set_cursor ( gtk_widget_get_window(GTK_WIDGET(vw->viking_vvp)), vw->viewport_cursor );
2513 if (!strcmp(gtk_action_get_name(a), "Pan")) {
2514 vw->current_tool = TOOL_PAN;
2516 else if (!strcmp(gtk_action_get_name(a), "Zoom")) {
2517 vw->current_tool = TOOL_ZOOM;
2519 else if (!strcmp(gtk_action_get_name(a), "Ruler")) {
2520 vw->current_tool = TOOL_RULER;
2522 else if (!strcmp(gtk_action_get_name(a), "Select")) {
2523 vw->current_tool = TOOL_SELECT;
2526 VikLayerTypeEnum layer_id;
2527 for (layer_id=0; layer_id<VIK_LAYER_NUM_TYPES; layer_id++) {
2528 for ( tool_id = 0; tool_id < vik_layer_get_interface(layer_id)->tools_count; tool_id++ ) {
2529 if (!strcmp(vik_layer_get_interface(layer_id)->tools[tool_id].radioActionEntry.name, gtk_action_get_name(a))) {
2530 vw->current_tool = TOOL_LAYER;
2531 vw->tool_layer_id = layer_id;
2532 vw->tool_tool_id = tool_id;
2537 draw_status_tool ( vw );
2540 static void window_set_filename ( VikWindow *vw, const gchar *filename )
2545 g_free ( vw->filename );
2546 if ( filename == NULL )
2548 vw->filename = NULL;
2552 vw->filename = g_strdup(filename);
2555 /* Refresh window's title */
2556 file = window_get_filename ( vw );
2557 title = g_strdup_printf( "%s - Viking", file );
2558 gtk_window_set_title ( GTK_WINDOW(vw), title );
2562 static const gchar *window_get_filename ( VikWindow *vw )
2564 return vw->filename ? a_file_basename ( vw->filename ) : _("Untitled");
2567 GtkWidget *vik_window_get_drawmode_button ( VikWindow *vw, VikViewportDrawMode mode )
2569 GtkWidget *mode_button;
2572 #ifdef VIK_CONFIG_EXPEDIA
2573 case VIK_VIEWPORT_DRAWMODE_EXPEDIA: buttonname = "/ui/MainMenu/View/ModeExpedia"; break;
2575 case VIK_VIEWPORT_DRAWMODE_MERCATOR: buttonname = "/ui/MainMenu/View/ModeMercator"; break;
2576 case VIK_VIEWPORT_DRAWMODE_LATLON: buttonname = "/ui/MainMenu/View/ModeLatLon"; break;
2577 default: buttonname = "/ui/MainMenu/View/ModeUTM";
2579 mode_button = gtk_ui_manager_get_widget ( vw->uim, buttonname );
2580 g_assert ( mode_button );
2585 * vik_window_get_pan_move:
2586 * @vw: some VikWindow
2588 * Retrieves @vw's pan_move.
2590 * Should be removed as soon as possible.
2592 * Returns: @vw's pan_move
2596 gboolean vik_window_get_pan_move ( VikWindow *vw )
2598 return vw->pan_move;
2601 static void on_activate_recent_item (GtkRecentChooser *chooser,
2606 filename = gtk_recent_chooser_get_current_uri (chooser);
2607 if (filename != NULL)
2609 GFile *file = g_file_new_for_uri ( filename );
2610 gchar *path = g_file_get_path ( file );
2611 g_object_unref ( file );
2612 if ( self->filename )
2614 GSList *filenames = NULL;
2615 filenames = g_slist_append ( filenames, path );
2616 g_signal_emit ( G_OBJECT(self), window_signals[VW_OPENWINDOW_SIGNAL], 0, filenames );
2617 // NB: GSList & contents are freed by main.open_window
2620 vik_window_open_file ( self, path, TRUE );
2628 static void setup_recent_files (VikWindow *self)
2630 GtkRecentManager *manager;
2631 GtkRecentFilter *filter;
2632 GtkWidget *menu, *menu_item;
2634 filter = gtk_recent_filter_new ();
2635 /* gtk_recent_filter_add_application (filter, g_get_application_name()); */
2636 gtk_recent_filter_add_group(filter, "viking");
2638 manager = gtk_recent_manager_get_default ();
2639 menu = gtk_recent_chooser_menu_new_for_manager (manager);
2640 gtk_recent_chooser_set_sort_type (GTK_RECENT_CHOOSER (menu), GTK_RECENT_SORT_MRU);
2641 gtk_recent_chooser_add_filter (GTK_RECENT_CHOOSER (menu), filter);
2643 menu_item = gtk_ui_manager_get_widget (self->uim, "/ui/MainMenu/File/OpenRecentFile");
2644 gtk_menu_item_set_submenu (GTK_MENU_ITEM (menu_item), menu);
2646 g_signal_connect (G_OBJECT (menu), "item-activated",
2647 G_CALLBACK (on_activate_recent_item), (gpointer) self);
2650 static void update_recently_used_document(const gchar *filename)
2652 /* Update Recently Used Document framework */
2653 GtkRecentManager *manager = gtk_recent_manager_get_default();
2654 GtkRecentData *recent_data = g_slice_new (GtkRecentData);
2655 gchar *groups[] = {"viking", NULL};
2656 GFile *file = g_file_new_for_commandline_arg(filename);
2657 gchar *uri = g_file_get_uri(file);
2658 gchar *basename = g_path_get_basename(filename);
2659 g_object_unref(file);
2662 recent_data->display_name = basename;
2663 recent_data->description = NULL;
2664 recent_data->mime_type = "text/x-gps-data";
2665 recent_data->app_name = (gchar *) g_get_application_name ();
2666 recent_data->app_exec = g_strjoin (" ", g_get_prgname (), "%f", NULL);
2667 recent_data->groups = groups;
2668 recent_data->is_private = FALSE;
2669 if (!gtk_recent_manager_add_full (manager, uri, recent_data))
2671 g_warning (_("Unable to add '%s' to the list of recently used documents"), uri);
2676 g_free (recent_data->app_exec);
2677 g_slice_free (GtkRecentData, recent_data);
2681 * Call this before doing things that may take a long time and otherwise not show any other feedback
2682 * such as loading and saving files
2684 void vik_window_set_busy_cursor ( VikWindow *vw )
2686 gdk_window_set_cursor ( gtk_widget_get_window(GTK_WIDGET(vw)), vw->busy_cursor );
2687 // Viewport has a separate cursor
2688 gdk_window_set_cursor ( gtk_widget_get_window(GTK_WIDGET(vw->viking_vvp)), vw->busy_cursor );
2689 // Ensure cursor updated before doing stuff
2690 while( gtk_events_pending() )
2691 gtk_main_iteration();
2694 void vik_window_clear_busy_cursor ( VikWindow *vw )
2696 gdk_window_set_cursor ( gtk_widget_get_window(GTK_WIDGET(vw)), NULL );
2697 // Restore viewport cursor
2698 gdk_window_set_cursor ( gtk_widget_get_window(GTK_WIDGET(vw->viking_vvp)), vw->viewport_cursor );
2701 void vik_window_open_file ( VikWindow *vw, const gchar *filename, gboolean change_filename )
2703 vik_window_set_busy_cursor ( vw );
2705 // Enable the *new* filename to be accessible by the Layers codez
2706 gchar *original_filename = g_strdup ( vw->filename );
2707 g_free ( vw->filename );
2708 vw->filename = g_strdup ( filename );
2709 gboolean success = FALSE;
2710 gboolean restore_original_filename = FALSE;
2712 vw->loaded_type = a_file_load ( vik_layers_panel_get_top_layer(vw->viking_vlp), vw->viking_vvp, filename );
2713 switch ( vw->loaded_type )
2715 case LOAD_TYPE_READ_FAILURE:
2716 a_dialog_error_msg ( GTK_WINDOW(vw), _("The file you requested could not be opened.") );
2718 case LOAD_TYPE_GPSBABEL_FAILURE:
2719 a_dialog_error_msg ( GTK_WINDOW(vw), _("GPSBabel is required to load files of this type or GPSBabel encountered problems.") );
2721 case LOAD_TYPE_GPX_FAILURE:
2722 a_dialog_error_msg_extra ( GTK_WINDOW(vw), _("Unable to load malformed GPX file %s"), filename );
2724 case LOAD_TYPE_UNSUPPORTED_FAILURE:
2725 a_dialog_error_msg_extra ( GTK_WINDOW(vw), _("Unsupported file type for %s"), filename );
2727 case LOAD_TYPE_VIK_FAILURE_NON_FATAL:
2729 // Since we can process .vik files with issues just show a warning in the status bar
2730 // Not that a user can do much about it... or tells them what this issue is yet...
2731 gchar *msg = g_strdup_printf (_("WARNING: issues encountered loading %s"), a_file_basename (filename) );
2732 vik_statusbar_set_message ( vw->viking_vs, VIK_STATUSBAR_INFO, msg );
2735 // No break, carry on to show any data
2736 case LOAD_TYPE_VIK_SUCCESS:
2738 restore_original_filename = TRUE; // NB Will actually get inverted by the 'success' component below
2739 GtkWidget *mode_button;
2741 if ( change_filename )
2742 window_set_filename ( vw, filename );
2743 mode_button = vik_window_get_drawmode_button ( vw, vik_viewport_get_drawmode ( vw->viking_vvp ) );
2744 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. */
2745 gtk_check_menu_item_set_active ( GTK_CHECK_MENU_ITEM(mode_button), TRUE );
2746 vw->only_updating_coord_mode_ui = FALSE;
2748 vik_layers_panel_change_coord_mode ( vw->viking_vlp, vik_viewport_get_coord_mode ( vw->viking_vvp ) );
2750 mode_button = gtk_ui_manager_get_widget ( vw->uim, "/ui/MainMenu/View/SetShow/ShowScale" );
2751 g_assert ( mode_button );
2752 gtk_check_menu_item_set_active ( GTK_CHECK_MENU_ITEM(mode_button),vik_viewport_get_draw_scale(vw->viking_vvp) );
2754 mode_button = gtk_ui_manager_get_widget ( vw->uim, "/ui/MainMenu/View/SetShow/ShowCenterMark" );
2755 g_assert ( mode_button );
2756 gtk_check_menu_item_set_active ( GTK_CHECK_MENU_ITEM(mode_button),vik_viewport_get_draw_centermark(vw->viking_vvp) );
2758 mode_button = gtk_ui_manager_get_widget ( vw->uim, "/ui/MainMenu/View/SetShow/ShowHighlight" );
2759 g_assert ( mode_button );
2760 gtk_check_menu_item_set_active ( GTK_CHECK_MENU_ITEM(mode_button),vik_viewport_get_draw_highlight (vw->viking_vvp) );
2762 // NB No break, carry on to redraw
2763 //case LOAD_TYPE_OTHER_SUCCESS:
2766 // When LOAD_TYPE_OTHER_SUCCESS *only*, this will maintain the existing Viking project
2767 restore_original_filename = ! restore_original_filename;
2768 update_recently_used_document(filename);
2773 if ( ! success || restore_original_filename )
2774 // Load didn't work or want to keep as the existing Viking project, keep using the original name
2775 window_set_filename ( vw, original_filename );
2776 g_free ( original_filename );
2778 vik_window_clear_busy_cursor ( vw );
2781 static void load_file ( GtkAction *a, VikWindow *vw )
2783 GSList *files = NULL;
2784 GSList *cur_file = NULL;
2786 if (!strcmp(gtk_action_get_name(a), "Open")) {
2789 else if (!strcmp(gtk_action_get_name(a), "Append")) {
2793 g_critical("Houston, we've had a problem.");
2797 if ( ! vw->open_dia )
2799 vw->open_dia = gtk_file_chooser_dialog_new (_("Please select a GPS data file to open. "),
2801 GTK_FILE_CHOOSER_ACTION_OPEN,
2802 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
2803 GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT,
2805 gchar *cwd = g_get_current_dir();
2807 gtk_file_chooser_set_current_folder ( GTK_FILE_CHOOSER(vw->open_dia), cwd );
2811 GtkFileFilter *filter;
2812 // NB file filters are listed this way for alphabetical ordering
2813 #ifdef VIK_CONFIG_GEOCACHES
2814 filter = gtk_file_filter_new ();
2815 gtk_file_filter_set_name( filter, _("Geocaching") );
2816 gtk_file_filter_add_pattern ( filter, "*.loc" ); // No MIME type available
2817 gtk_file_chooser_add_filter (GTK_FILE_CHOOSER(vw->open_dia), filter);
2820 filter = gtk_file_filter_new ();
2821 gtk_file_filter_set_name( filter, _("Google Earth") );
2822 gtk_file_filter_add_mime_type ( filter, "application/vnd.google-earth.kml+xml");
2823 gtk_file_chooser_add_filter (GTK_FILE_CHOOSER(vw->open_dia), filter);
2825 filter = gtk_file_filter_new ();
2826 gtk_file_filter_set_name( filter, _("GPX") );
2827 gtk_file_filter_add_pattern ( filter, "*.gpx" ); // No MIME type available
2828 gtk_file_chooser_add_filter (GTK_FILE_CHOOSER(vw->open_dia), filter);
2830 filter = gtk_file_filter_new ();
2831 gtk_file_filter_set_name ( filter, _("JPG") );
2832 gtk_file_filter_add_mime_type ( filter, "image/jpeg");
2833 gtk_file_chooser_add_filter (GTK_FILE_CHOOSER(vw->open_dia), filter);
2835 filter = gtk_file_filter_new ();
2836 gtk_file_filter_set_name( filter, _("Viking") );
2837 gtk_file_filter_add_pattern ( filter, "*.vik" );
2838 gtk_file_filter_add_pattern ( filter, "*.viking" );
2839 gtk_file_chooser_add_filter (GTK_FILE_CHOOSER(vw->open_dia), filter);
2841 // NB could have filters for gpspoint (*.gps,*.gpsoint?) + gpsmapper (*.gsm,*.gpsmapper?)
2842 // However assume this are barely used and thus not worthy of inclusion
2843 // as they'll just make the options too many and have no clear file pattern
2844 // one can always use the all option
2845 filter = gtk_file_filter_new ();
2846 gtk_file_filter_set_name( filter, _("All") );
2847 gtk_file_filter_add_pattern ( filter, "*" );
2848 gtk_file_chooser_add_filter (GTK_FILE_CHOOSER(vw->open_dia), filter);
2849 // Default to any file - same as before open filters were added
2850 gtk_file_chooser_set_filter (GTK_FILE_CHOOSER(vw->open_dia), filter);
2852 gtk_file_chooser_set_select_multiple ( GTK_FILE_CHOOSER(vw->open_dia), TRUE );
2853 gtk_window_set_transient_for ( GTK_WINDOW(vw->open_dia), GTK_WINDOW(vw) );
2854 gtk_window_set_destroy_with_parent ( GTK_WINDOW(vw->open_dia), TRUE );
2856 if ( gtk_dialog_run ( GTK_DIALOG(vw->open_dia) ) == GTK_RESPONSE_ACCEPT )
2858 gtk_widget_hide ( vw->open_dia );
2859 #ifdef VIKING_PROMPT_IF_MODIFIED
2860 if ( (vw->modified || vw->filename) && newwindow )
2862 if ( vw->filename && newwindow )
2864 g_signal_emit ( G_OBJECT(vw), window_signals[VW_OPENWINDOW_SIGNAL], 0, gtk_file_chooser_get_filenames (GTK_FILE_CHOOSER(vw->open_dia) ) );
2866 files = gtk_file_chooser_get_filenames (GTK_FILE_CHOOSER(vw->open_dia) );
2867 gboolean change_fn = newwindow && (g_slist_length(files)==1); /* only change fn if one file */
2868 gboolean first_vik_file = TRUE;
2870 while ( cur_file ) {
2872 gchar *file_name = cur_file->data;
2873 if ( newwindow && check_file_magic_vik ( file_name ) ) {
2874 // Load first of many .vik files in current window
2875 if ( first_vik_file ) {
2876 vik_window_open_file ( vw, file_name, TRUE );
2877 first_vik_file = FALSE;
2880 // Load each subsequent .vik file in a separate window
2881 VikWindow *newvw = vik_window_new_window ();
2883 vik_window_open_file ( newvw, file_name, TRUE );
2888 vik_window_open_file ( vw, file_name, change_fn );
2891 cur_file = g_slist_next (cur_file);
2893 g_slist_free (files);
2897 gtk_widget_hide ( vw->open_dia );
2900 static gboolean save_file_as ( GtkAction *a, VikWindow *vw )
2902 gboolean rv = FALSE;
2904 if ( ! vw->save_dia )
2906 vw->save_dia = gtk_file_chooser_dialog_new (_("Save as Viking File."),
2908 GTK_FILE_CHOOSER_ACTION_SAVE,
2909 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
2910 GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT,
2912 gchar *cwd = g_get_current_dir();
2914 gtk_file_chooser_set_current_folder ( GTK_FILE_CHOOSER(vw->save_dia), cwd );
2918 GtkFileFilter *filter;
2919 filter = gtk_file_filter_new ();
2920 gtk_file_filter_set_name( filter, _("All") );
2921 gtk_file_filter_add_pattern ( filter, "*" );
2922 gtk_file_chooser_add_filter (GTK_FILE_CHOOSER(vw->save_dia), filter);
2924 filter = gtk_file_filter_new ();
2925 gtk_file_filter_set_name( filter, _("Viking") );
2926 gtk_file_filter_add_pattern ( filter, "*.vik" );
2927 gtk_file_filter_add_pattern ( filter, "*.viking" );
2928 gtk_file_chooser_add_filter (GTK_FILE_CHOOSER(vw->save_dia), filter);
2929 // Default to a Viking file
2930 gtk_file_chooser_set_filter (GTK_FILE_CHOOSER(vw->save_dia), filter);
2932 gtk_window_set_transient_for ( GTK_WINDOW(vw->save_dia), GTK_WINDOW(vw) );
2933 gtk_window_set_destroy_with_parent ( GTK_WINDOW(vw->save_dia), TRUE );
2935 // Auto append / replace extension with '.vik' to the suggested file name as it's going to be a Viking File
2936 gchar* auto_save_name = g_strdup ( window_get_filename ( vw ) );
2937 if ( ! a_file_check_ext ( auto_save_name, ".vik" ) )
2938 auto_save_name = g_strconcat ( auto_save_name, ".vik", NULL );
2940 gtk_file_chooser_set_current_name (GTK_FILE_CHOOSER(vw->save_dia), auto_save_name);
2942 while ( gtk_dialog_run ( GTK_DIALOG(vw->save_dia) ) == GTK_RESPONSE_ACCEPT )
2944 fn = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER(vw->save_dia) );
2945 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 ) ) )
2947 window_set_filename ( vw, fn );
2948 rv = window_save ( vw );
2949 vw->modified = FALSE;
2953 g_free ( auto_save_name );
2954 gtk_widget_hide ( vw->save_dia );
2958 static gboolean window_save ( VikWindow *vw )
2960 vik_window_set_busy_cursor ( vw );
2961 gboolean success = TRUE;
2963 if ( a_file_save ( vik_layers_panel_get_top_layer ( vw->viking_vlp ), vw->viking_vvp, vw->filename ) )
2965 update_recently_used_document ( vw->filename );
2969 a_dialog_error_msg ( GTK_WINDOW(vw), _("The filename you requested could not be opened for writing.") );
2972 vik_window_clear_busy_cursor ( vw );
2976 static gboolean save_file ( GtkAction *a, VikWindow *vw )
2978 if ( ! vw->filename )
2979 return save_file_as ( NULL, vw );
2982 vw->modified = FALSE;
2983 return window_save ( vw );
2990 * Export all TRW Layers in the list to individual files in the specified directory
2992 * Returns: %TRUE on success
2994 static gboolean export_to ( VikWindow *vw, GList *gl, VikFileType_t vft, const gchar *dir, const gchar *extension )
2996 gboolean success = TRUE;
2998 gint export_count = 0;
3000 vik_window_set_busy_cursor ( vw );
3004 gchar *fn = g_strconcat ( dir, G_DIR_SEPARATOR_S, VIK_LAYER(gl->data)->name, extension, NULL );
3006 // Some protection in attempting to write too many same named files
3007 // As this will get horribly slow...
3008 gboolean safe = FALSE;
3010 while ( ii < 5000 ) {
3011 if ( g_file_test ( fn, G_FILE_TEST_EXISTS ) ) {
3014 fn = g_strdup_printf ( "%s%s%s#%03d%s", dir, G_DIR_SEPARATOR_S, VIK_LAYER(gl->data)->name, ii, extension );
3025 // NB: We allow exporting empty layers
3027 gboolean this_success = a_file_export ( VIK_TRW_LAYER(gl->data), fn, vft, NULL, TRUE );
3029 // Show some progress
3030 if ( this_success ) {
3032 gchar *message = g_strdup_printf ( _("Exporting to file: %s"), fn );
3033 vik_statusbar_set_message ( vw->viking_vs, VIK_STATUSBAR_INFO, message );
3034 while ( gtk_events_pending() )
3035 gtk_main_iteration ();
3039 success = success && this_success;
3043 gl = g_list_next ( gl );
3046 vik_window_clear_busy_cursor ( vw );
3048 // Confirm what happened.
3049 gchar *message = g_strdup_printf ( _("Exported files: %d"), export_count );
3050 vik_statusbar_set_message ( vw->viking_vs, VIK_STATUSBAR_INFO, message );
3056 static void export_to_common ( VikWindow *vw, VikFileType_t vft, const gchar *extension )
3058 GList *gl = vik_layers_panel_get_all_layers_of_type ( vw->viking_vlp, VIK_LAYER_TRW, TRUE );
3061 a_dialog_info_msg ( GTK_WINDOW(vw), _("Nothing to Export!") );
3065 GtkWidget *dialog = gtk_file_chooser_dialog_new ( _("Export to directory"),
3067 GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER,
3069 GTK_RESPONSE_REJECT,
3071 GTK_RESPONSE_ACCEPT,
3073 gtk_window_set_transient_for ( GTK_WINDOW(dialog), GTK_WINDOW(vw) );
3074 gtk_window_set_destroy_with_parent ( GTK_WINDOW(dialog), TRUE );
3075 gtk_window_set_modal ( GTK_WINDOW(dialog), TRUE );
3077 gtk_widget_show_all ( dialog );
3079 if ( gtk_dialog_run ( GTK_DIALOG(dialog) ) == GTK_RESPONSE_ACCEPT ) {
3080 gchar *dir = gtk_file_chooser_get_filename ( GTK_FILE_CHOOSER(dialog) );
3081 gtk_widget_destroy ( dialog );
3083 if ( !export_to ( vw, gl, vft, dir, extension ) )
3084 a_dialog_error_msg ( GTK_WINDOW(vw),_("Could not convert all files") );
3089 gtk_widget_destroy ( dialog );
3094 static void export_to_gpx ( GtkAction *a, VikWindow *vw )
3096 export_to_common ( vw, FILE_TYPE_GPX, ".gpx" );
3099 static void export_to_kml ( GtkAction *a, VikWindow *vw )
3101 export_to_common ( vw, FILE_TYPE_KML, ".kml" );
3104 #if !GLIB_CHECK_VERSION(2,26,0)
3105 typedef struct stat GStatBuf;
3108 static void file_properties_cb ( GtkAction *a, VikWindow *vw )
3110 gchar *message = NULL;
3111 if ( vw->filename ) {
3112 if ( g_file_test ( vw->filename, G_FILE_TEST_EXISTS ) ) {
3113 // Get some timestamp information of the file
3115 if ( g_stat ( vw->filename, &stat_buf ) == 0 ) {
3117 strftime ( time_buf, sizeof(time_buf), "%c", gmtime((const time_t *)&stat_buf.st_mtime) );
3119 gint byte_size = stat_buf.st_size;
3120 // See http://en.wikipedia.org/wiki/Megabyte (and Kilobyte)
3121 // hence using 1000 rather than 1024
3122 // so get output as per 'ls' or the Gtk file open dialog
3123 if ( byte_size < 1000 )
3124 size = g_strdup_printf ( _("%d bytes"), byte_size );
3125 else if ( byte_size < 1000*1000 )
3126 size = g_strdup_printf ( _("%3.1f kB"), (gdouble)byte_size / 1000 );
3128 size = g_strdup_printf ( _("%3.1f MB"), (gdouble)byte_size / (1000*1000) );
3129 message = g_strdup_printf ( _("%s\n\n%s\n\n%s"), vw->filename, time_buf, size );
3134 message = g_strdup ( _("File not accessible") );
3137 message = g_strdup ( _("No Viking File") );
3140 a_dialog_info_msg ( GTK_WINDOW(vw), message );
3144 static void acquire_from_gps ( GtkAction *a, VikWindow *vw )
3146 // Via the file menu, acquiring from a GPS makes a new layer
3147 // this has always been the way (not entirely sure if this was the real intention!)
3148 // thus maintain the behaviour ATM.
3149 // Hence explicit setting here (as the value may be changed elsewhere)
3150 vik_datasource_gps_interface.mode = VIK_DATASOURCE_CREATENEWLAYER;
3151 a_acquire(vw, vw->viking_vlp, vw->viking_vvp, &vik_datasource_gps_interface, NULL, NULL );
3154 static void acquire_from_file ( GtkAction *a, VikWindow *vw )
3156 a_acquire(vw, vw->viking_vlp, vw->viking_vvp, &vik_datasource_file_interface, NULL, NULL );
3159 static void acquire_from_routing ( GtkAction *a, VikWindow *vw )
3161 a_acquire(vw, vw->viking_vlp, vw->viking_vvp, &vik_datasource_routing_interface, NULL, NULL );
3164 #ifdef VIK_CONFIG_OPENSTREETMAP
3165 static void acquire_from_osm ( GtkAction *a, VikWindow *vw )
3167 a_acquire(vw, vw->viking_vlp, vw->viking_vvp, &vik_datasource_osm_interface, NULL, NULL );
3170 static void acquire_from_my_osm ( GtkAction *a, VikWindow *vw )
3172 a_acquire(vw, vw->viking_vlp, vw->viking_vvp, &vik_datasource_osm_my_traces_interface, NULL, NULL );
3176 #ifdef VIK_CONFIG_GEOCACHES
3177 static void acquire_from_gc ( GtkAction *a, VikWindow *vw )
3179 a_acquire(vw, vw->viking_vlp, vw->viking_vvp, &vik_datasource_gc_interface, NULL, NULL );
3183 #ifdef VIK_CONFIG_GEOTAG
3184 static void acquire_from_geotag ( GtkAction *a, VikWindow *vw )
3186 vik_datasource_geotag_interface.mode = VIK_DATASOURCE_CREATENEWLAYER;
3187 a_acquire(vw, vw->viking_vlp, vw->viking_vvp, &vik_datasource_geotag_interface, NULL, NULL );
3191 #ifdef VIK_CONFIG_GEONAMES
3192 static void acquire_from_wikipedia ( GtkAction *a, VikWindow *vw )
3194 a_acquire(vw, vw->viking_vlp, vw->viking_vvp, &vik_datasource_wikipedia_interface, NULL, NULL );
3198 static void acquire_from_url ( GtkAction *a, VikWindow *vw )
3200 vik_datasource_url_interface.mode = VIK_DATASOURCE_CREATENEWLAYER;
3201 a_acquire(vw, vw->viking_vlp, vw->viking_vvp, &vik_datasource_url_interface, NULL, NULL );
3204 static void goto_default_location( GtkAction *a, VikWindow *vw)
3207 ll.lat = a_vik_get_default_lat();
3208 ll.lon = a_vik_get_default_long();
3209 vik_viewport_set_center_latlon(vw->viking_vvp, &ll, TRUE);
3210 vik_layers_panel_emit_update(vw->viking_vlp);
3214 static void goto_address( GtkAction *a, VikWindow *vw)
3216 a_vik_goto ( vw, vw->viking_vvp );
3217 vik_layers_panel_emit_update ( vw->viking_vlp );
3220 static void mapcache_flush_cb ( GtkAction *a, VikWindow *vw )
3225 static void layer_defaults_cb ( GtkAction *a, VikWindow *vw )
3227 gchar **texts = g_strsplit ( gtk_action_get_name(a), "Layer", 0 );
3230 return; // Internally broken :(
3232 if ( ! a_layer_defaults_show_window ( GTK_WINDOW(vw), texts[1] ) )
3233 a_dialog_info_msg ( GTK_WINDOW(vw), _("This layer has no configurable properties.") );
3234 // NB no update needed
3236 g_strfreev ( texts );
3239 static void preferences_change_update ( VikWindow *vw, gpointer data )
3241 // Want to update all TrackWaypoint layers
3242 GList *layers = vik_layers_panel_get_all_layers_of_type ( vw->viking_vlp, VIK_LAYER_TRW, TRUE );
3248 // Reset the individual waypoints themselves due to the preferences change
3249 VikTrwLayer *vtl = VIK_TRW_LAYER(layers->data);
3250 vik_trw_layer_reset_waypoints ( vtl );
3251 layers = g_list_next ( layers );
3254 g_list_free ( layers );
3259 static void preferences_cb ( GtkAction *a, VikWindow *vw )
3261 gboolean wp_icon_size = a_vik_get_use_large_waypoint_icons();
3263 a_preferences_show_window ( GTK_WINDOW(vw) );
3265 // Has the waypoint size setting changed?
3266 if (wp_icon_size != a_vik_get_use_large_waypoint_icons()) {
3267 // Delete icon indexing 'cache' and so automatically regenerates with the new setting when changed
3268 clear_garmin_icon_syms ();
3270 // Update all windows
3271 g_slist_foreach ( window_list, (GFunc) preferences_change_update, NULL );
3275 static void default_location_cb ( GtkAction *a, VikWindow *vw )
3277 /* Simplistic repeat of preference setting
3278 Only the name & type are important for setting the preference via this 'external' way */
3279 VikLayerParam pref_lat[] = {
3280 { VIK_LAYER_NUM_TYPES,
3281 VIKING_PREFERENCES_NAMESPACE "default_latitude",
3282 VIK_LAYER_PARAM_DOUBLE,
3285 VIK_LAYER_WIDGET_SPINBUTTON,
3293 VikLayerParam pref_lon[] = {
3294 { VIK_LAYER_NUM_TYPES,
3295 VIKING_PREFERENCES_NAMESPACE "default_longitude",
3296 VIK_LAYER_PARAM_DOUBLE,
3299 VIK_LAYER_WIDGET_SPINBUTTON,
3308 /* Get current center */
3310 vik_coord_to_latlon ( vik_viewport_get_center ( vw->viking_vvp ), &ll );
3312 /* Apply to preferences */
3313 VikLayerParamData vlp_data;
3314 vlp_data.d = ll.lat;
3315 a_preferences_run_setparam (vlp_data, pref_lat);
3316 vlp_data.d = ll.lon;
3317 a_preferences_run_setparam (vlp_data, pref_lon);
3318 /* Remember to save */
3319 a_preferences_save_to_file();
3322 static void clear_cb ( GtkAction *a, VikWindow *vw )
3324 vik_layers_panel_clear ( vw->viking_vlp );
3325 window_set_filename ( vw, NULL );
3329 static void window_close ( GtkAction *a, VikWindow *vw )
3331 if ( ! delete_event ( vw ) )
3332 gtk_widget_destroy ( GTK_WIDGET(vw) );
3335 static gboolean save_file_and_exit ( GtkAction *a, VikWindow *vw )
3337 if (save_file( NULL, vw)) {
3338 window_close( NULL, vw);
3345 static void zoom_to_cb ( GtkAction *a, VikWindow *vw )
3347 gdouble xmpp = vik_viewport_get_xmpp ( vw->viking_vvp ), ympp = vik_viewport_get_ympp ( vw->viking_vvp );
3348 if ( a_dialog_custom_zoom ( GTK_WINDOW(vw), &xmpp, &ympp ) )
3350 vik_viewport_set_xmpp ( vw->viking_vvp, xmpp );
3351 vik_viewport_set_ympp ( vw->viking_vvp, ympp );
3356 static void save_image_file ( VikWindow *vw, const gchar *fn, guint w, guint h, gdouble zoom, gboolean save_as_png )
3358 /* more efficient way: stuff draws directly to pixbuf (fork viewport) */
3359 GdkPixbuf *pixbuf_to_save;
3360 gdouble old_xmpp, old_ympp;
3361 GError *error = NULL;
3363 GtkWidget *msgbox = gtk_message_dialog_new ( GTK_WINDOW(vw),
3364 GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
3367 _("Generating image file...") );
3369 g_signal_connect_swapped (msgbox, "response", G_CALLBACK (gtk_widget_destroy), msgbox);
3370 // Ensure dialog shown
3371 gtk_widget_show_all ( msgbox );
3373 vik_statusbar_set_message ( vw->viking_vs, VIK_STATUSBAR_INFO, _("Generating image file...") );
3374 while ( gtk_events_pending() )
3375 gtk_main_iteration ();
3376 // Despite many efforts & variations, GTK on my Linux system doesn't show the actual msgbox contents :(
3377 // At least the empty box can give a clue something's going on + the statusbar msg...
3378 // Windows version under Wine OK!
3380 /* backup old zoom & set new */
3381 old_xmpp = vik_viewport_get_xmpp ( vw->viking_vvp );
3382 old_ympp = vik_viewport_get_ympp ( vw->viking_vvp );
3383 vik_viewport_set_zoom ( vw->viking_vvp, zoom );
3385 /* reset width and height: */
3386 vik_viewport_configure_manually ( vw->viking_vvp, w, h );
3388 /* draw all layers */
3391 /* save buffer as file. */
3392 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);
3393 if ( !pixbuf_to_save ) {
3394 g_warning("Failed to generate internal pixmap size: %d x %d", w, h);
3395 gtk_message_dialog_set_markup ( GTK_MESSAGE_DIALOG(msgbox), _("Failed to generate internal image.\n\nTry creating a smaller image.") );
3399 gdk_pixbuf_save ( pixbuf_to_save, fn, save_as_png ? "png" : "jpeg", &error, NULL );
3402 g_warning("Unable to write to file %s: %s", fn, error->message );
3403 gtk_message_dialog_set_markup ( GTK_MESSAGE_DIALOG(msgbox), _("Failed to generate image file.") );
3404 g_error_free (error);
3408 gtk_message_dialog_set_markup ( GTK_MESSAGE_DIALOG(msgbox), _("Image file generated.") );
3410 g_object_unref ( G_OBJECT(pixbuf_to_save) );
3413 vik_statusbar_set_message ( vw->viking_vs, VIK_STATUSBAR_INFO, "" );
3414 gtk_dialog_add_button ( GTK_DIALOG(msgbox), GTK_STOCK_OK, GTK_RESPONSE_OK );
3415 gtk_dialog_run ( GTK_DIALOG(msgbox) ); // Don't care about the result
3417 /* pretend like nothing happened ;) */
3418 vik_viewport_set_xmpp ( vw->viking_vvp, old_xmpp );
3419 vik_viewport_set_ympp ( vw->viking_vvp, old_ympp );
3420 vik_viewport_configure ( vw->viking_vvp );
3424 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 )
3426 gulong size = sizeof(gchar) * (strlen(fn) + 15);
3427 gchar *name_of_file = g_malloc ( size );
3429 struct UTM utm_orig, utm;
3431 /* *** copied from above *** */
3432 GdkPixbuf *pixbuf_to_save;
3433 gdouble old_xmpp, old_ympp;
3434 GError *error = NULL;
3436 /* backup old zoom & set new */
3437 old_xmpp = vik_viewport_get_xmpp ( vw->viking_vvp );
3438 old_ympp = vik_viewport_get_ympp ( vw->viking_vvp );
3439 vik_viewport_set_zoom ( vw->viking_vvp, zoom );
3441 /* reset width and height: do this only once for all images (same size) */
3442 vik_viewport_configure_manually ( vw->viking_vvp, w, h );
3443 /* *** end copy from above *** */
3445 g_assert ( vik_viewport_get_coord_mode ( vw->viking_vvp ) == VIK_COORD_UTM );
3449 utm_orig = *((const struct UTM *)vik_viewport_get_center ( vw->viking_vvp ));
3451 for ( y = 1; y <= tiles_h; y++ )
3453 for ( x = 1; x <= tiles_w; x++ )
3455 g_snprintf ( name_of_file, size, "%s%cy%d-x%d.%s", fn, G_DIR_SEPARATOR, y, x, save_as_png ? "png" : "jpg" );
3457 if ( tiles_w & 0x1 )
3458 utm.easting += ((gdouble)x - ceil(((gdouble)tiles_w)/2)) * (w*zoom);
3460 utm.easting += ((gdouble)x - (((gdouble)tiles_w)+1)/2) * (w*zoom);
3461 if ( tiles_h & 0x1 ) /* odd */
3462 utm.northing -= ((gdouble)y - ceil(((gdouble)tiles_h)/2)) * (h*zoom);
3464 utm.northing -= ((gdouble)y - (((gdouble)tiles_h)+1)/2) * (h*zoom);
3466 /* move to correct place. */
3467 vik_viewport_set_center_utm ( vw->viking_vvp, &utm, FALSE );
3471 /* save buffer as file. */
3472 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);
3473 gdk_pixbuf_save ( pixbuf_to_save, name_of_file, save_as_png ? "png" : "jpeg", &error, NULL );
3476 g_warning("Unable to write to file %s: %s", name_of_file, error->message );
3477 g_error_free (error);
3480 g_object_unref ( G_OBJECT(pixbuf_to_save) );
3484 vik_viewport_set_center_utm ( vw->viking_vvp, &utm_orig, FALSE );
3485 vik_viewport_set_xmpp ( vw->viking_vvp, old_xmpp );
3486 vik_viewport_set_ympp ( vw->viking_vvp, old_ympp );
3487 vik_viewport_configure ( vw->viking_vvp );
3490 g_free ( name_of_file );
3493 static void draw_to_image_file_current_window_cb(GtkWidget* widget,GdkEventButton *event,gpointer *pass_along)
3495 VikWindow *vw = VIK_WINDOW(pass_along[0]);
3496 GtkSpinButton *width_spin = GTK_SPIN_BUTTON(pass_along[1]), *height_spin = GTK_SPIN_BUTTON(pass_along[2]);
3498 gint active = gtk_combo_box_get_active ( GTK_COMBO_BOX(pass_along[3]) );
3499 gdouble zoom = pow (2, active-2 );
3501 gdouble width_min, width_max, height_min, height_max;
3504 gtk_spin_button_get_range ( width_spin, &width_min, &width_max );
3505 gtk_spin_button_get_range ( height_spin, &height_min, &height_max );
3507 /* TODO: support for xzoom and yzoom values */
3508 width = vik_viewport_get_width ( vw->viking_vvp ) * vik_viewport_get_xmpp ( vw->viking_vvp ) / zoom;
3509 height = vik_viewport_get_height ( vw->viking_vvp ) * vik_viewport_get_xmpp ( vw->viking_vvp ) / zoom;
3511 if ( width > width_max || width < width_min || height > height_max || height < height_min )
3512 a_dialog_info_msg ( GTK_WINDOW(vw), _("Viewable region outside allowable pixel size bounds for image. Clipping width/height values.") );
3514 gtk_spin_button_set_value ( width_spin, width );
3515 gtk_spin_button_set_value ( height_spin, height );
3518 static void draw_to_image_file_total_area_cb (GtkSpinButton *spinbutton, gpointer *pass_along)
3520 GtkSpinButton *width_spin = GTK_SPIN_BUTTON(pass_along[1]), *height_spin = GTK_SPIN_BUTTON(pass_along[2]);
3522 gint active = gtk_combo_box_get_active ( GTK_COMBO_BOX(pass_along[3]) );
3523 gdouble zoom = pow (2, active-2 );
3527 w = gtk_spin_button_get_value(width_spin) * zoom;
3528 h = gtk_spin_button_get_value(height_spin) * zoom;
3529 if (pass_along[4]) /* save many images; find TOTAL area covered */
3531 w *= gtk_spin_button_get_value(GTK_SPIN_BUTTON(pass_along[4]));
3532 h *= gtk_spin_button_get_value(GTK_SPIN_BUTTON(pass_along[5]));
3534 vik_units_distance_t dist_units = a_vik_get_units_distance ();
3535 switch (dist_units) {
3536 case VIK_UNITS_DISTANCE_KILOMETRES:
3537 label_text = g_strdup_printf ( _("Total area: %ldm x %ldm (%.3f sq. km)"), (glong)w, (glong)h, (w*h/1000000));
3539 case VIK_UNITS_DISTANCE_MILES:
3540 label_text = g_strdup_printf ( _("Total area: %ldm x %ldm (%.3f sq. miles)"), (glong)w, (glong)h, (w*h/2589988.11));
3543 label_text = g_strdup_printf ("Just to keep the compiler happy");
3544 g_critical("Houston, we've had a problem. distance=%d", dist_units);
3547 gtk_label_set_text(GTK_LABEL(pass_along[6]), label_text);
3548 g_free ( label_text );
3552 * Get an allocated filename (or directory as specified)
3554 static gchar* draw_image_filename ( VikWindow *vw, gboolean one_image_only )
3557 if ( one_image_only )
3560 if (!vw->save_img_dia) {
3561 vw->save_img_dia = gtk_file_chooser_dialog_new (_("Save Image"),
3563 GTK_FILE_CHOOSER_ACTION_SAVE,
3564 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
3565 GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT,
3568 gchar *cwd = g_get_current_dir();
3570 gtk_file_chooser_set_current_folder ( GTK_FILE_CHOOSER(vw->save_img_dia), cwd );
3574 GtkFileChooser *chooser = GTK_FILE_CHOOSER ( vw->save_img_dia );
3576 GtkFileFilter *filter;
3577 filter = gtk_file_filter_new ();
3578 gtk_file_filter_set_name ( filter, _("All") );
3579 gtk_file_filter_add_pattern ( filter, "*" );
3580 gtk_file_chooser_add_filter ( chooser, filter );
3582 filter = gtk_file_filter_new ();
3583 gtk_file_filter_set_name ( filter, _("JPG") );
3584 gtk_file_filter_add_mime_type ( filter, "image/jpeg");
3585 gtk_file_chooser_add_filter ( chooser, filter );
3587 if ( !vw->draw_image_save_as_png )
3588 gtk_file_chooser_set_filter ( chooser, filter );
3590 filter = gtk_file_filter_new ();
3591 gtk_file_filter_set_name ( filter, _("PNG") );
3592 gtk_file_filter_add_mime_type ( filter, "image/png");
3593 gtk_file_chooser_add_filter ( chooser, filter );
3595 if ( vw->draw_image_save_as_png )
3596 gtk_file_chooser_set_filter ( chooser, filter );
3598 gtk_window_set_transient_for ( GTK_WINDOW(vw->save_img_dia), GTK_WINDOW(vw) );
3599 gtk_window_set_destroy_with_parent ( GTK_WINDOW(vw->save_img_dia), TRUE );
3602 if ( gtk_dialog_run ( GTK_DIALOG(vw->save_img_dia) ) == GTK_RESPONSE_ACCEPT ) {
3603 fn = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER(vw->save_img_dia) );
3604 if ( g_file_test ( fn, G_FILE_TEST_EXISTS ) )
3605 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 ) ) )
3608 gtk_widget_hide ( vw->save_img_dia );
3612 // For some reason this method is only written to work in UTM...
3613 if ( vik_viewport_get_coord_mode(vw->viking_vvp) != VIK_COORD_UTM ) {
3614 a_dialog_error_msg ( GTK_WINDOW(vw), _("You must be in UTM mode to use this feature") );
3618 if (!vw->save_img_dir_dia) {
3619 vw->save_img_dir_dia = gtk_file_chooser_dialog_new (_("Choose a directory to hold images"),
3621 GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER,
3622 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
3623 GTK_STOCK_OK, GTK_RESPONSE_ACCEPT,
3625 gtk_window_set_transient_for ( GTK_WINDOW(vw->save_img_dir_dia), GTK_WINDOW(vw) );
3626 gtk_window_set_destroy_with_parent ( GTK_WINDOW(vw->save_img_dir_dia), TRUE );
3629 if ( gtk_dialog_run ( GTK_DIALOG(vw->save_img_dir_dia) ) == GTK_RESPONSE_ACCEPT ) {
3630 fn = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER(vw->save_img_dir_dia) );
3632 gtk_widget_hide ( vw->save_img_dir_dia );
3637 static void draw_to_image_file ( VikWindow *vw, gboolean one_image_only )
3639 /* todo: default for answers inside VikWindow or static (thruout instance) */
3640 GtkWidget *dialog = gtk_dialog_new_with_buttons ( _("Save to Image File"), GTK_WINDOW(vw),
3641 GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
3643 GTK_RESPONSE_REJECT,
3645 GTK_RESPONSE_ACCEPT,
3647 GtkWidget *width_label, *width_spin, *height_label, *height_spin;
3648 GtkWidget *png_radio, *jpeg_radio;
3649 GtkWidget *current_window_button;
3650 gpointer current_window_pass_along[7];
3651 GtkWidget *zoom_label, *zoom_combo;
3652 GtkWidget *total_size_label;
3654 /* only used if (!one_image_only) */
3655 GtkWidget *tiles_width_spin = NULL, *tiles_height_spin = NULL;
3657 width_label = gtk_label_new ( _("Width (pixels):") );
3658 width_spin = gtk_spin_button_new ( GTK_ADJUSTMENT(gtk_adjustment_new ( vw->draw_image_width, 10, 50000, 10, 100, 0 )), 10, 0 );
3659 height_label = gtk_label_new ( _("Height (pixels):") );
3660 height_spin = gtk_spin_button_new ( GTK_ADJUSTMENT(gtk_adjustment_new ( vw->draw_image_height, 10, 50000, 10, 100, 0 )), 10, 0 );
3662 GtkWidget *win_warning_label = gtk_label_new ( _("WARNING: USING LARGE IMAGES OVER 10000x10000\nMAY CRASH THE PROGRAM!") );
3664 zoom_label = gtk_label_new ( _("Zoom (meters per pixel):") );
3665 /* TODO: separate xzoom and yzoom factors */
3666 zoom_combo = create_zoom_combo_all_levels();
3668 gdouble mpp = vik_viewport_get_xmpp(vw->viking_vvp);
3669 gint active = 2 + round ( log (mpp) / log (2) );
3671 // Can we not hard code size here?
3676 gtk_combo_box_set_active ( GTK_COMBO_BOX(zoom_combo), active );
3678 total_size_label = gtk_label_new ( NULL );
3680 current_window_button = gtk_button_new_with_label ( _("Area in current viewable window") );
3681 current_window_pass_along [0] = vw;
3682 current_window_pass_along [1] = width_spin;
3683 current_window_pass_along [2] = height_spin;
3684 current_window_pass_along [3] = zoom_combo;
3685 current_window_pass_along [4] = NULL; /* used for one_image_only != 1 */
3686 current_window_pass_along [5] = NULL;
3687 current_window_pass_along [6] = total_size_label;
3688 g_signal_connect ( G_OBJECT(current_window_button), "button_press_event", G_CALLBACK(draw_to_image_file_current_window_cb), current_window_pass_along );
3690 png_radio = gtk_radio_button_new_with_label ( NULL, _("Save as PNG") );
3691 jpeg_radio = gtk_radio_button_new_with_label_from_widget ( GTK_RADIO_BUTTON(png_radio), _("Save as JPEG") );
3693 gtk_box_pack_start (GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), png_radio, FALSE, FALSE, 0);
3694 gtk_box_pack_start (GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), jpeg_radio, FALSE, FALSE, 0);
3696 if ( ! vw->draw_image_save_as_png )
3697 gtk_toggle_button_set_active ( GTK_TOGGLE_BUTTON(jpeg_radio), TRUE );
3699 gtk_box_pack_start (GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), width_label, FALSE, FALSE, 0);
3700 gtk_box_pack_start (GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), width_spin, FALSE, FALSE, 0);
3701 gtk_box_pack_start (GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), height_label, FALSE, FALSE, 0);
3702 gtk_box_pack_start (GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), height_spin, FALSE, FALSE, 0);
3704 gtk_box_pack_start (GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), win_warning_label, FALSE, FALSE, 0);
3706 gtk_box_pack_start (GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), current_window_button, FALSE, FALSE, 0);
3707 gtk_box_pack_start (GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), zoom_label, FALSE, FALSE, 0);
3708 gtk_box_pack_start (GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), zoom_combo, FALSE, FALSE, 0);
3710 if ( ! one_image_only )
3712 GtkWidget *tiles_width_label, *tiles_height_label;
3714 tiles_width_label = gtk_label_new ( _("East-west image tiles:") );
3715 tiles_width_spin = gtk_spin_button_new ( GTK_ADJUSTMENT(gtk_adjustment_new ( 5, 1, 10, 1, 100, 0 )), 1, 0 );
3716 tiles_height_label = gtk_label_new ( _("North-south image tiles:") );
3717 tiles_height_spin = gtk_spin_button_new ( GTK_ADJUSTMENT(gtk_adjustment_new ( 5, 1, 10, 1, 100, 0 )), 1, 0 );
3718 gtk_box_pack_start (GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), tiles_width_label, FALSE, FALSE, 0);
3719 gtk_box_pack_start (GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), tiles_width_spin, FALSE, FALSE, 0);
3720 gtk_box_pack_start (GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), tiles_height_label, FALSE, FALSE, 0);
3721 gtk_box_pack_start (GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), tiles_height_spin, FALSE, FALSE, 0);
3723 current_window_pass_along [4] = tiles_width_spin;
3724 current_window_pass_along [5] = tiles_height_spin;
3725 g_signal_connect ( G_OBJECT(tiles_width_spin), "value-changed", G_CALLBACK(draw_to_image_file_total_area_cb), current_window_pass_along );
3726 g_signal_connect ( G_OBJECT(tiles_height_spin), "value-changed", G_CALLBACK(draw_to_image_file_total_area_cb), current_window_pass_along );
3728 gtk_box_pack_start (GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), total_size_label, FALSE, FALSE, 0);
3729 g_signal_connect ( G_OBJECT(width_spin), "value-changed", G_CALLBACK(draw_to_image_file_total_area_cb), current_window_pass_along );
3730 g_signal_connect ( G_OBJECT(height_spin), "value-changed", G_CALLBACK(draw_to_image_file_total_area_cb), current_window_pass_along );
3731 g_signal_connect ( G_OBJECT(zoom_combo), "changed", G_CALLBACK(draw_to_image_file_total_area_cb), current_window_pass_along );
3733 draw_to_image_file_total_area_cb ( NULL, current_window_pass_along ); /* set correct size info now */
3735 gtk_dialog_set_default_response ( GTK_DIALOG(dialog), GTK_RESPONSE_ACCEPT );
3737 gtk_widget_show_all ( gtk_dialog_get_content_area(GTK_DIALOG(dialog)) );
3739 if ( gtk_dialog_run ( GTK_DIALOG(dialog) ) == GTK_RESPONSE_ACCEPT )
3741 gtk_widget_hide ( GTK_WIDGET(dialog) );
3743 gchar *fn = draw_image_filename ( vw, one_image_only );
3747 gint active = gtk_combo_box_get_active ( GTK_COMBO_BOX(zoom_combo) );
3748 gdouble zoom = pow (2, active-2 );
3750 if ( one_image_only )
3751 save_image_file ( vw, fn,
3752 vw->draw_image_width = gtk_spin_button_get_value_as_int ( GTK_SPIN_BUTTON(width_spin) ),
3753 vw->draw_image_height = gtk_spin_button_get_value_as_int ( GTK_SPIN_BUTTON(height_spin) ),
3755 vw->draw_image_save_as_png = gtk_toggle_button_get_active ( GTK_TOGGLE_BUTTON(png_radio) ) );
3757 // NB is in UTM mode ATM
3758 save_image_dir ( vw, fn,
3759 vw->draw_image_width = gtk_spin_button_get_value_as_int ( GTK_SPIN_BUTTON(width_spin) ),
3760 vw->draw_image_height = gtk_spin_button_get_value_as_int ( GTK_SPIN_BUTTON(height_spin) ),
3762 vw->draw_image_save_as_png = gtk_toggle_button_get_active ( GTK_TOGGLE_BUTTON(png_radio) ),
3763 gtk_spin_button_get_value ( GTK_SPIN_BUTTON(tiles_width_spin) ),
3764 gtk_spin_button_get_value ( GTK_SPIN_BUTTON(tiles_height_spin) ) );
3769 gtk_widget_destroy ( GTK_WIDGET(dialog) );
3773 static void draw_to_image_file_cb ( GtkAction *a, VikWindow *vw )
3775 draw_to_image_file ( vw, TRUE );
3778 static void draw_to_image_dir_cb ( GtkAction *a, VikWindow *vw )
3780 draw_to_image_file ( vw, FALSE );
3783 static void print_cb ( GtkAction *a, VikWindow *vw )
3785 a_print(vw, vw->viking_vvp);
3788 /* really a misnomer: changes coord mode (actual coordinates) AND/OR draw mode (viewport only) */
3789 static void window_change_coord_mode_cb ( GtkAction *old_a, GtkAction *a, VikWindow *vw )
3791 VikViewportDrawMode drawmode;
3792 if (!strcmp(gtk_action_get_name(a), "ModeUTM")) {
3793 drawmode = VIK_VIEWPORT_DRAWMODE_UTM;
3795 else if (!strcmp(gtk_action_get_name(a), "ModeLatLon")) {
3796 drawmode = VIK_VIEWPORT_DRAWMODE_LATLON;
3798 else if (!strcmp(gtk_action_get_name(a), "ModeExpedia")) {
3799 drawmode = VIK_VIEWPORT_DRAWMODE_EXPEDIA;
3801 else if (!strcmp(gtk_action_get_name(a), "ModeMercator")) {
3802 drawmode = VIK_VIEWPORT_DRAWMODE_MERCATOR;
3805 g_critical("Houston, we've had a problem.");
3809 if ( !vw->only_updating_coord_mode_ui )
3811 VikViewportDrawMode olddrawmode = vik_viewport_get_drawmode ( vw->viking_vvp );
3812 if ( olddrawmode != drawmode )
3814 /* this takes care of coord mode too */
3815 vik_viewport_set_drawmode ( vw->viking_vvp, drawmode );
3816 if ( drawmode == VIK_VIEWPORT_DRAWMODE_UTM ) {
3817 vik_layers_panel_change_coord_mode ( vw->viking_vlp, VIK_COORD_UTM );
3818 } else if ( olddrawmode == VIK_VIEWPORT_DRAWMODE_UTM ) {
3819 vik_layers_panel_change_coord_mode ( vw->viking_vlp, VIK_COORD_LATLON );
3826 static void set_draw_scale ( GtkAction *a, VikWindow *vw )
3828 GtkWidget *check_box = gtk_ui_manager_get_widget ( vw->uim, "/ui/MainMenu/View/SetShow/ShowScale" );
3829 g_assert(check_box);
3830 gboolean state = gtk_check_menu_item_get_active ( GTK_CHECK_MENU_ITEM(check_box));
3831 vik_viewport_set_draw_scale ( vw->viking_vvp, state );
3835 static void set_draw_centermark ( GtkAction *a, VikWindow *vw )
3837 GtkWidget *check_box = gtk_ui_manager_get_widget ( vw->uim, "/ui/MainMenu/View/SetShow/ShowCenterMark" );
3838 g_assert(check_box);
3839 gboolean state = gtk_check_menu_item_get_active ( GTK_CHECK_MENU_ITEM(check_box));
3840 vik_viewport_set_draw_centermark ( vw->viking_vvp, state );
3844 static void set_draw_highlight ( GtkAction *a, VikWindow *vw )
3846 GtkWidget *check_box = gtk_ui_manager_get_widget ( vw->uim, "/ui/MainMenu/View/SetShow/ShowHighlight" );
3847 g_assert(check_box);
3848 gboolean state = gtk_check_menu_item_get_active ( GTK_CHECK_MENU_ITEM(check_box));
3849 vik_viewport_set_draw_highlight ( vw->viking_vvp, state );
3853 static void set_bg_color ( GtkAction *a, VikWindow *vw )
3855 GtkWidget *colorsd = gtk_color_selection_dialog_new ( _("Choose a background color") );
3856 GdkColor *color = vik_viewport_get_background_gdkcolor ( vw->viking_vvp );
3857 gtk_color_selection_set_previous_color ( GTK_COLOR_SELECTION(gtk_color_selection_dialog_get_color_selection(GTK_COLOR_SELECTION_DIALOG(colorsd))), color );
3858 gtk_color_selection_set_current_color ( GTK_COLOR_SELECTION(gtk_color_selection_dialog_get_color_selection(GTK_COLOR_SELECTION_DIALOG(colorsd))), color );
3859 if ( gtk_dialog_run ( GTK_DIALOG(colorsd) ) == GTK_RESPONSE_OK )
3861 gtk_color_selection_get_current_color ( GTK_COLOR_SELECTION(gtk_color_selection_dialog_get_color_selection(GTK_COLOR_SELECTION_DIALOG(colorsd))), color );
3862 vik_viewport_set_background_gdkcolor ( vw->viking_vvp, color );
3866 gtk_widget_destroy ( colorsd );
3869 static void set_highlight_color ( GtkAction *a, VikWindow *vw )
3871 GtkWidget *colorsd = gtk_color_selection_dialog_new ( _("Choose a track highlight color") );
3872 GdkColor *color = vik_viewport_get_highlight_gdkcolor ( vw->viking_vvp );
3873 gtk_color_selection_set_previous_color ( GTK_COLOR_SELECTION(gtk_color_selection_dialog_get_color_selection(GTK_COLOR_SELECTION_DIALOG(colorsd))), color );
3874 gtk_color_selection_set_current_color ( GTK_COLOR_SELECTION(gtk_color_selection_dialog_get_color_selection(GTK_COLOR_SELECTION_DIALOG(colorsd))), color );
3875 if ( gtk_dialog_run ( GTK_DIALOG(colorsd) ) == GTK_RESPONSE_OK )
3877 gtk_color_selection_get_current_color ( GTK_COLOR_SELECTION(gtk_color_selection_dialog_get_color_selection(GTK_COLOR_SELECTION_DIALOG(colorsd))), color );
3878 vik_viewport_set_highlight_gdkcolor ( vw->viking_vvp, color );
3882 gtk_widget_destroy ( colorsd );
3887 /***********************************************************************************************
3889 ***********************************************************************************************/
3891 static GtkActionEntry entries[] = {
3892 { "File", NULL, N_("_File"), 0, 0, 0 },
3893 { "Edit", NULL, N_("_Edit"), 0, 0, 0 },
3894 { "View", NULL, N_("_View"), 0, 0, 0 },
3895 { "SetShow", NULL, N_("_Show"), 0, 0, 0 },
3896 { "SetZoom", NULL, N_("_Zoom"), 0, 0, 0 },
3897 { "SetPan", NULL, N_("_Pan"), 0, 0, 0 },
3898 { "Layers", NULL, N_("_Layers"), 0, 0, 0 },
3899 { "Tools", NULL, N_("_Tools"), 0, 0, 0 },
3900 { "Exttools", NULL, N_("_Webtools"), 0, 0, 0 },
3901 { "Help", NULL, N_("_Help"), 0, 0, 0 },
3903 { "New", GTK_STOCK_NEW, N_("_New"), "<control>N", N_("New file"), (GCallback)newwindow_cb },
3904 { "Open", GTK_STOCK_OPEN, N_("_Open..."), "<control>O", N_("Open a file"), (GCallback)load_file },
3905 { "OpenRecentFile", NULL, N_("Open _Recent File"), NULL, NULL, (GCallback)NULL },
3906 { "Append", GTK_STOCK_ADD, N_("Append _File..."), NULL, N_("Append data from a different file"), (GCallback)load_file },
3907 { "Export", GTK_STOCK_CONVERT, N_("_Export All"), NULL, N_("Export All TrackWaypoint Layers"), (GCallback)NULL },
3908 { "ExportGPX", NULL, N_("_GPX..."), NULL, N_("Export as GPX"), (GCallback)export_to_gpx },
3909 { "Acquire", GTK_STOCK_GO_DOWN, N_("A_cquire"), NULL, NULL, (GCallback)NULL },
3910 { "AcquireGPS", NULL, N_("From _GPS..."), NULL, N_("Transfer data from a GPS device"), (GCallback)acquire_from_gps },
3911 { "AcquireGPSBabel", NULL, N_("Import File With GPS_Babel..."), NULL, N_("Import file via GPSBabel converter"), (GCallback)acquire_from_file },
3912 { "AcquireRouting", NULL, N_("_Directions..."), NULL, N_("Get driving directions"), (GCallback)acquire_from_routing },
3913 #ifdef VIK_CONFIG_OPENSTREETMAP
3914 { "AcquireOSM", NULL, N_("_OSM Traces..."), NULL, N_("Get traces from OpenStreetMap"), (GCallback)acquire_from_osm },
3915 { "AcquireMyOSM", NULL, N_("_My OSM Traces..."), NULL, N_("Get Your Own Traces from OpenStreetMap"), (GCallback)acquire_from_my_osm },
3917 #ifdef VIK_CONFIG_GEOCACHES
3918 { "AcquireGC", NULL, N_("Geo_caches..."), NULL, N_("Get Geocaches from geocaching.com"), (GCallback)acquire_from_gc },
3920 #ifdef VIK_CONFIG_GEOTAG
3921 { "AcquireGeotag", NULL, N_("From Geotagged _Images..."), NULL, N_("Create waypoints from geotagged images"), (GCallback)acquire_from_geotag },
3923 { "AcquireURL", NULL, N_("From _URL..."), NULL, N_("Get a file from a URL"), (GCallback)acquire_from_url },
3924 #ifdef VIK_CONFIG_GEONAMES
3925 { "AcquireWikipedia", NULL, N_("From _Wikipedia Waypoints"), NULL, N_("Create waypoints from Wikipedia items in the current view"), (GCallback)acquire_from_wikipedia },
3927 { "Save", GTK_STOCK_SAVE, N_("_Save"), "<control>S", N_("Save the file"), (GCallback)save_file },
3928 { "SaveAs", GTK_STOCK_SAVE_AS, N_("Save _As..."), NULL, N_("Save the file under different name"), (GCallback)save_file_as },
3929 { "FileProperties", NULL, N_("Properties..."), NULL, N_("File Properties"), (GCallback)file_properties_cb },
3930 { "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 },
3931 { "GenImgDir", GTK_STOCK_DND_MULTIPLE, N_("Generate _Directory of Images..."), NULL, N_("FIXME:IMGDIR"), (GCallback)draw_to_image_dir_cb },
3932 { "Print", GTK_STOCK_PRINT, N_("_Print..."), NULL, N_("Print maps"), (GCallback)print_cb },
3933 { "Exit", GTK_STOCK_QUIT, N_("E_xit"), "<control>W", N_("Exit the program"), (GCallback)window_close },
3934 { "SaveExit", GTK_STOCK_QUIT, N_("Save and Exit"), NULL, N_("Save and Exit the program"), (GCallback)save_file_and_exit },
3936 { "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 },
3937 { "GoForward", GTK_STOCK_GO_FORWARD, N_("Go to the _Next Location"), NULL, N_("Go to the next location"), (GCallback)draw_goto_back_and_forth },
3938 { "GotoDefaultLocation", GTK_STOCK_HOME, N_("Go to the _Default Location"), NULL, N_("Go to the default location"), (GCallback)goto_default_location },
3939 { "GotoSearch", GTK_STOCK_JUMP_TO, N_("Go to _Location..."), NULL, N_("Go to address/place using text search"), (GCallback)goto_address },
3940 { "GotoLL", GTK_STOCK_JUMP_TO, N_("_Go to Lat/Lon..."), NULL, N_("Go to arbitrary lat/lon coordinate"), (GCallback)draw_goto_cb },
3941 { "GotoUTM", GTK_STOCK_JUMP_TO, N_("Go to UTM..."), NULL, N_("Go to arbitrary UTM coordinate"), (GCallback)draw_goto_cb },
3942 { "Refresh", GTK_STOCK_REFRESH, N_("_Refresh"), "F5", N_("Refresh any maps displayed"), (GCallback)draw_refresh_cb },
3943 { "SetHLColor",GTK_STOCK_SELECT_COLOR, N_("Set _Highlight Color..."), NULL, NULL, (GCallback)set_highlight_color },
3944 { "SetBGColor",GTK_STOCK_SELECT_COLOR, N_("Set Bac_kground Color..."), NULL, NULL, (GCallback)set_bg_color },
3945 { "ZoomIn", GTK_STOCK_ZOOM_IN, N_("Zoom _In"), "<control>plus", NULL, (GCallback)draw_zoom_cb },
3946 { "ZoomOut", GTK_STOCK_ZOOM_OUT, N_("Zoom _Out"), "<control>minus", NULL, (GCallback)draw_zoom_cb },
3947 { "ZoomTo", GTK_STOCK_ZOOM_FIT, N_("Zoom _To..."), "<control>Z", NULL, (GCallback)zoom_to_cb },
3948 { "PanNorth", NULL, N_("Pan _North"), "<control>Up", NULL, (GCallback)draw_pan_cb },
3949 { "PanEast", NULL, N_("Pan _East"), "<control>Right", NULL, (GCallback)draw_pan_cb },
3950 { "PanSouth", NULL, N_("Pan _South"), "<control>Down", NULL, (GCallback)draw_pan_cb },
3951 { "PanWest", NULL, N_("Pan _West"), "<control>Left", NULL, (GCallback)draw_pan_cb },
3952 { "BGJobs", GTK_STOCK_EXECUTE, N_("Background _Jobs"), NULL, NULL, (GCallback)a_background_show_window },
3954 { "Cut", GTK_STOCK_CUT, N_("Cu_t"), NULL, NULL, (GCallback)menu_cut_layer_cb },
3955 { "Copy", GTK_STOCK_COPY, N_("_Copy"), NULL, NULL, (GCallback)menu_copy_layer_cb },
3956 { "Paste", GTK_STOCK_PASTE, N_("_Paste"), NULL, NULL, (GCallback)menu_paste_layer_cb },
3957 { "Delete", GTK_STOCK_DELETE, N_("_Delete"), NULL, NULL, (GCallback)menu_delete_layer_cb },
3958 { "DeleteAll", NULL, N_("Delete All"), NULL, NULL, (GCallback)clear_cb },
3959 { "MapCacheFlush",NULL, N_("_Flush Map Cache"), NULL, NULL, (GCallback)mapcache_flush_cb },
3960 { "SetDefaultLocation", GTK_STOCK_GO_FORWARD, N_("_Set the Default Location"), NULL, N_("Set the Default Location to the current position"),(GCallback)default_location_cb },
3961 { "Preferences",GTK_STOCK_PREFERENCES, N_("_Preferences"), NULL, NULL, (GCallback)preferences_cb },
3962 { "LayerDefaults",GTK_STOCK_PROPERTIES, N_("_Layer Defaults"), NULL, NULL, NULL },
3963 { "Properties",GTK_STOCK_PROPERTIES, N_("_Properties"), NULL, NULL, (GCallback)menu_properties_cb },
3965 { "HelpEntry", GTK_STOCK_HELP, N_("_Help"), "F1", NULL, (GCallback)help_help_cb },
3966 { "About", GTK_STOCK_ABOUT, N_("_About"), NULL, NULL, (GCallback)help_about_cb },
3969 static GtkActionEntry entries_gpsbabel[] = {
3970 { "ExportKML", NULL, N_("_KML..."), NULL, N_("Export as KML"), (GCallback)export_to_kml },
3974 /* FIXME use VIEWPORT_DRAWMODE values */
3975 static GtkRadioActionEntry mode_entries[] = {
3976 { "ModeUTM", NULL, N_("_UTM Mode"), "<control>u", NULL, 0 },
3977 { "ModeExpedia", NULL, N_("_Expedia Mode"), "<control>e", NULL, 1 },
3978 { "ModeMercator", NULL, N_("_Mercator Mode"), "<control>m", NULL, 4 },
3979 { "ModeLatLon", NULL, N_("Lat_/Lon Mode"), "<control>l", NULL, 5 },
3982 static GtkToggleActionEntry toggle_entries[] = {
3983 { "ShowScale", NULL, N_("Show _Scale"), "<shift>F5", N_("Show Scale"), (GCallback)set_draw_scale, TRUE },
3984 { "ShowCenterMark", NULL, N_("Show _Center Mark"), "F6", N_("Show Center Mark"), (GCallback)set_draw_centermark, TRUE },
3985 { "ShowHighlight", GTK_STOCK_UNDERLINE, N_("Show _Highlight"), "F7", N_("Show Highlight"), (GCallback)set_draw_highlight, TRUE },
3986 { "FullScreen", GTK_STOCK_FULLSCREEN, N_("_Full Screen"), "F11", N_("Activate full screen mode"), (GCallback)full_screen_cb, FALSE },
3987 { "ViewSidePanel", GTK_STOCK_INDEX, N_("Show Side _Panel"), "F9", N_("Show Side Panel"), (GCallback)view_side_panel_cb, TRUE },
3988 { "ViewStatusBar", NULL, N_("Show Status_bar"), "F12", N_("Show Statusbar"), (GCallback)view_statusbar_cb, TRUE },
3989 { "ViewToolbar", NULL, N_("Show _Toolbar"), "F3", N_("Show Toolbar"), (GCallback)view_toolbar_cb, TRUE },
3990 { "ViewMainMenu", NULL, N_("Show _Menu"), "F4", N_("Show Menu"), (GCallback)view_main_menu_cb, TRUE },
3993 #include "menu.xml.h"
3994 static void window_create_ui( VikWindow *window )
3997 GtkActionGroup *action_group;
3998 GtkAccelGroup *accel_group;
4001 GtkIconFactory *icon_factory;
4002 GtkIconSet *icon_set;
4003 GtkRadioActionEntry *tools = NULL, *radio;
4006 uim = gtk_ui_manager_new ();
4009 toolbox_add_tool(window->vt, &ruler_tool, TOOL_LAYER_TYPE_NONE);
4010 toolbox_add_tool(window->vt, &zoom_tool, TOOL_LAYER_TYPE_NONE);
4011 toolbox_add_tool(window->vt, &pan_tool, TOOL_LAYER_TYPE_NONE);
4012 toolbox_add_tool(window->vt, &select_tool, TOOL_LAYER_TYPE_NONE);
4015 if (!(mid = gtk_ui_manager_add_ui_from_string (uim, menu_xml, -1, &error))) {
4016 g_error_free (error);
4020 action_group = gtk_action_group_new ("MenuActions");
4021 gtk_action_group_set_translation_domain(action_group, PACKAGE_NAME);
4022 gtk_action_group_add_actions (action_group, entries, G_N_ELEMENTS (entries), window);
4023 gtk_action_group_add_toggle_actions (action_group, toggle_entries, G_N_ELEMENTS (toggle_entries), window);
4024 gtk_action_group_add_radio_actions (action_group, mode_entries, G_N_ELEMENTS (mode_entries), 4, (GCallback)window_change_coord_mode_cb, window);
4026 // Use this to see if GPSBabel is available:
4027 if ( a_babel_available () ) {
4028 // If going to add more entries then might be worth creating a menu_gpsbabel.xml.h file
4029 if ( gtk_ui_manager_add_ui_from_string ( uim,
4030 "<ui><menubar name='MainMenu'><menu action='File'><menu action='Export'><menuitem action='ExportKML'/></menu></menu></menubar></ui>",
4032 gtk_action_group_add_actions ( action_group, entries_gpsbabel, G_N_ELEMENTS (entries_gpsbabel), window );
4035 icon_factory = gtk_icon_factory_new ();
4036 gtk_icon_factory_add_default (icon_factory);
4038 register_vik_icons(icon_factory);
4040 // Copy the tool RadioActionEntries out of the main Window structure into an extending array 'tools'
4041 // so that it can be applied to the UI in one action group add function call below
4043 for (i=0; i<window->vt->n_tools; i++) {
4044 tools = g_renew(GtkRadioActionEntry, tools, ntools+1);
4045 radio = &tools[ntools];
4047 *radio = window->vt->tools[i].ti.radioActionEntry;
4048 radio->value = ntools;
4051 for (i=0; i<VIK_LAYER_NUM_TYPES; i++) {
4052 GtkActionEntry action;
4053 gtk_ui_manager_add_ui(uim, mid, "/ui/MainMenu/Layers/",
4054 vik_layer_get_interface(i)->name,
4055 vik_layer_get_interface(i)->name,
4056 GTK_UI_MANAGER_MENUITEM, FALSE);
4058 icon_set = gtk_icon_set_new_from_pixbuf (gdk_pixbuf_from_pixdata (vik_layer_get_interface(i)->icon, FALSE, NULL ));
4059 gtk_icon_factory_add (icon_factory, vik_layer_get_interface(i)->name, icon_set);
4060 gtk_icon_set_unref (icon_set);
4062 action.name = vik_layer_get_interface(i)->name;
4063 action.stock_id = vik_layer_get_interface(i)->name;
4064 action.label = g_strdup_printf( _("New _%s Layer"), vik_layer_get_interface(i)->name);
4065 action.accelerator = vik_layer_get_interface(i)->accelerator;
4066 action.tooltip = NULL;
4067 action.callback = (GCallback)menu_addlayer_cb;
4068 gtk_action_group_add_actions(action_group, &action, 1, window);
4070 g_free ( (gchar*)action.label );
4072 if ( vik_layer_get_interface(i)->tools_count ) {
4073 gtk_ui_manager_add_ui(uim, mid, "/ui/MainMenu/Tools/", vik_layer_get_interface(i)->name, NULL, GTK_UI_MANAGER_SEPARATOR, FALSE);
4074 gtk_ui_manager_add_ui(uim, mid, "/ui/MainToolbar/ToolItems/", vik_layer_get_interface(i)->name, NULL, GTK_UI_MANAGER_SEPARATOR, FALSE);
4077 // Further tool copying for to apply to the UI, also apply menu UI setup
4078 for ( j = 0; j < vik_layer_get_interface(i)->tools_count; j++ ) {
4079 tools = g_renew(GtkRadioActionEntry, tools, ntools+1);
4080 radio = &tools[ntools];
4083 gtk_ui_manager_add_ui(uim, mid, "/ui/MainMenu/Tools",
4084 vik_layer_get_interface(i)->tools[j].radioActionEntry.label,
4085 vik_layer_get_interface(i)->tools[j].radioActionEntry.name,
4086 GTK_UI_MANAGER_MENUITEM, FALSE);
4087 gtk_ui_manager_add_ui(uim, mid, "/ui/MainToolbar/ToolItems",
4088 vik_layer_get_interface(i)->tools[j].radioActionEntry.label,
4089 vik_layer_get_interface(i)->tools[j].radioActionEntry.name,
4090 GTK_UI_MANAGER_TOOLITEM, FALSE);
4092 toolbox_add_tool(window->vt, &(vik_layer_get_interface(i)->tools[j]), i);
4094 *radio = vik_layer_get_interface(i)->tools[j].radioActionEntry;
4095 // Overwrite with actual number to use
4096 radio->value = ntools;
4099 GtkActionEntry action_dl;
4100 gchar *layername = g_strdup_printf ( "Layer%s", vik_layer_get_interface(i)->fixed_layer_name );
4101 gtk_ui_manager_add_ui(uim, mid, "/ui/MainMenu/Edit/LayerDefaults",
4102 vik_layer_get_interface(i)->name,
4104 GTK_UI_MANAGER_MENUITEM, FALSE);
4107 // For default layers use action names of the form 'Layer<LayerName>'
4108 // This is to avoid clashing with just the layer name used above for the tool actions
4109 action_dl.name = g_strconcat("Layer", vik_layer_get_interface(i)->fixed_layer_name, NULL);
4110 action_dl.stock_id = NULL;
4111 action_dl.label = g_strconcat("_", vik_layer_get_interface(i)->name, "...", NULL); // Prepend marker for keyboard accelerator
4112 action_dl.accelerator = NULL;
4113 action_dl.tooltip = NULL;
4114 action_dl.callback = (GCallback)layer_defaults_cb;
4115 gtk_action_group_add_actions(action_group, &action_dl, 1, window);
4116 g_free ( (gchar*)action_dl.name );
4117 g_free ( (gchar*)action_dl.label );
4119 g_object_unref (icon_factory);
4121 gtk_action_group_add_radio_actions(action_group, tools, ntools, 0, (GCallback)menu_tool_cb, window);
4124 gtk_ui_manager_insert_action_group (uim, action_group, 0);
4126 for (i=0; i<VIK_LAYER_NUM_TYPES; i++) {
4127 for ( j = 0; j < vik_layer_get_interface(i)->tools_count; j++ ) {
4128 GtkAction *action = gtk_action_group_get_action(action_group,
4129 vik_layer_get_interface(i)->tools[j].radioActionEntry.name);
4130 g_object_set(action, "sensitive", FALSE, NULL);
4134 // This is done last so we don't need to track the value of mid anymore
4135 vik_ext_tools_add_action_items ( window, window->uim, action_group, mid );
4137 window->action_group = action_group;
4139 accel_group = gtk_ui_manager_get_accel_group (uim);
4140 gtk_window_add_accel_group (GTK_WINDOW (window), accel_group);
4141 gtk_ui_manager_ensure_update (uim);
4143 setup_recent_files(window);
4147 // TODO - add method to add tool icons defined from outside this file
4148 // and remove the reverse dependency on icon definition from this file
4150 const GdkPixdata *data;
4153 { &mover_22_pixbuf, "vik-icon-pan" },
4154 { &zoom_18_pixbuf, "vik-icon-zoom" },
4155 { &ruler_18_pixbuf, "vik-icon-ruler" },
4156 { &select_18_pixbuf, "vik-icon-select" },
4157 { &vik_new_route_18_pixbuf, "vik-icon-Create Route" },
4158 { &route_finder_18_pixbuf, "vik-icon-Route Finder" },
4159 { &demdl_18_pixbuf, "vik-icon-DEM Download" },
4160 { &showpic_18_pixbuf, "vik-icon-Show Picture" },
4161 { &addtr_18_pixbuf, "vik-icon-Create Track" },
4162 { &edtr_18_pixbuf, "vik-icon-Edit Trackpoint" },
4163 { &addwp_18_pixbuf, "vik-icon-Create Waypoint" },
4164 { &edwp_18_pixbuf, "vik-icon-Edit Waypoint" },
4165 { &geozoom_18_pixbuf, "vik-icon-Georef Zoom Tool" },
4166 { &geomove_18_pixbuf, "vik-icon-Georef Move Map" },
4167 { &mapdl_18_pixbuf, "vik-icon-Maps Download" },
4170 static gint n_stock_icons = G_N_ELEMENTS (stock_icons);
4173 register_vik_icons (GtkIconFactory *icon_factory)
4175 GtkIconSet *icon_set;
4178 for (i = 0; i < n_stock_icons; i++) {
4179 icon_set = gtk_icon_set_new_from_pixbuf (gdk_pixbuf_from_pixdata (
4180 stock_icons[i].data, FALSE, NULL ));
4181 gtk_icon_factory_add (icon_factory, stock_icons[i].stock_id, icon_set);
4182 gtk_icon_set_unref (icon_set);
4186 gpointer vik_window_get_selected_trw_layer ( VikWindow *vw )
4188 return vw->selected_vtl;
4191 void vik_window_set_selected_trw_layer ( VikWindow *vw, gpointer vtl )
4193 vw->selected_vtl = vtl;
4194 vw->containing_vtl = vtl;
4196 vw->selected_track = NULL;
4197 vw->selected_tracks = NULL;
4198 vw->selected_waypoint = NULL;
4199 vw->selected_waypoints = NULL;
4200 // Set highlight thickness
4201 vik_viewport_set_highlight_thickness ( vw->viking_vvp, vik_trw_layer_get_property_tracks_line_thickness (vw->containing_vtl) );
4204 GHashTable *vik_window_get_selected_tracks ( VikWindow *vw )
4206 return vw->selected_tracks;
4209 void vik_window_set_selected_tracks ( VikWindow *vw, GHashTable *ght, gpointer vtl )
4211 vw->selected_tracks = ght;
4212 vw->containing_vtl = vtl;
4214 vw->selected_vtl = NULL;
4215 vw->selected_track = NULL;
4216 vw->selected_waypoint = NULL;
4217 vw->selected_waypoints = NULL;
4218 // Set highlight thickness
4219 vik_viewport_set_highlight_thickness ( vw->viking_vvp, vik_trw_layer_get_property_tracks_line_thickness (vw->containing_vtl) );
4222 gpointer vik_window_get_selected_track ( VikWindow *vw )
4224 return vw->selected_track;
4227 void vik_window_set_selected_track ( VikWindow *vw, gpointer *vt, gpointer vtl )
4229 vw->selected_track = vt;
4230 vw->containing_vtl = vtl;
4232 vw->selected_vtl = NULL;
4233 vw->selected_tracks = NULL;
4234 vw->selected_waypoint = NULL;
4235 vw->selected_waypoints = NULL;
4236 // Set highlight thickness
4237 vik_viewport_set_highlight_thickness ( vw->viking_vvp, vik_trw_layer_get_property_tracks_line_thickness (vw->containing_vtl) );
4240 GHashTable *vik_window_get_selected_waypoints ( VikWindow *vw )
4242 return vw->selected_waypoints;
4245 void vik_window_set_selected_waypoints ( VikWindow *vw, GHashTable *ght, gpointer vtl )
4247 vw->selected_waypoints = ght;
4248 vw->containing_vtl = vtl;
4250 vw->selected_vtl = NULL;
4251 vw->selected_track = NULL;
4252 vw->selected_tracks = NULL;
4253 vw->selected_waypoint = NULL;
4256 gpointer vik_window_get_selected_waypoint ( VikWindow *vw )
4258 return vw->selected_waypoint;
4261 void vik_window_set_selected_waypoint ( VikWindow *vw, gpointer *vwp, gpointer vtl )
4263 vw->selected_waypoint = vwp;
4264 vw->containing_vtl = vtl;
4266 vw->selected_vtl = NULL;
4267 vw->selected_track = NULL;
4268 vw->selected_tracks = NULL;
4269 vw->selected_waypoints = NULL;
4272 gboolean vik_window_clear_highlight ( VikWindow *vw )
4274 gboolean need_redraw = FALSE;
4275 if ( vw->selected_vtl != NULL ) {
4276 vw->selected_vtl = NULL;
4279 if ( vw->selected_track != NULL ) {
4280 vw->selected_track = NULL;
4283 if ( vw->selected_tracks != NULL ) {
4284 vw->selected_tracks = NULL;
4287 if ( vw->selected_waypoint != NULL ) {
4288 vw->selected_waypoint = NULL;
4291 if ( vw->selected_waypoints != NULL ) {
4292 vw->selected_waypoints = NULL;
4298 GThread *vik_window_get_thread ( VikWindow *vw )