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);
2642 gtk_recent_chooser_set_limit (GTK_RECENT_CHOOSER (menu), a_vik_get_recent_number_files() );
2644 menu_item = gtk_ui_manager_get_widget (self->uim, "/ui/MainMenu/File/OpenRecentFile");
2645 gtk_menu_item_set_submenu (GTK_MENU_ITEM (menu_item), menu);
2647 g_signal_connect (G_OBJECT (menu), "item-activated",
2648 G_CALLBACK (on_activate_recent_item), (gpointer) self);
2651 static void update_recently_used_document(const gchar *filename)
2653 /* Update Recently Used Document framework */
2654 GtkRecentManager *manager = gtk_recent_manager_get_default();
2655 GtkRecentData *recent_data = g_slice_new (GtkRecentData);
2656 gchar *groups[] = {"viking", NULL};
2657 GFile *file = g_file_new_for_commandline_arg(filename);
2658 gchar *uri = g_file_get_uri(file);
2659 gchar *basename = g_path_get_basename(filename);
2660 g_object_unref(file);
2663 recent_data->display_name = basename;
2664 recent_data->description = NULL;
2665 recent_data->mime_type = "text/x-gps-data";
2666 recent_data->app_name = (gchar *) g_get_application_name ();
2667 recent_data->app_exec = g_strjoin (" ", g_get_prgname (), "%f", NULL);
2668 recent_data->groups = groups;
2669 recent_data->is_private = FALSE;
2670 if (!gtk_recent_manager_add_full (manager, uri, recent_data))
2672 g_warning (_("Unable to add '%s' to the list of recently used documents"), uri);
2677 g_free (recent_data->app_exec);
2678 g_slice_free (GtkRecentData, recent_data);
2682 * Call this before doing things that may take a long time and otherwise not show any other feedback
2683 * such as loading and saving files
2685 void vik_window_set_busy_cursor ( VikWindow *vw )
2687 gdk_window_set_cursor ( gtk_widget_get_window(GTK_WIDGET(vw)), vw->busy_cursor );
2688 // Viewport has a separate cursor
2689 gdk_window_set_cursor ( gtk_widget_get_window(GTK_WIDGET(vw->viking_vvp)), vw->busy_cursor );
2690 // Ensure cursor updated before doing stuff
2691 while( gtk_events_pending() )
2692 gtk_main_iteration();
2695 void vik_window_clear_busy_cursor ( VikWindow *vw )
2697 gdk_window_set_cursor ( gtk_widget_get_window(GTK_WIDGET(vw)), NULL );
2698 // Restore viewport cursor
2699 gdk_window_set_cursor ( gtk_widget_get_window(GTK_WIDGET(vw->viking_vvp)), vw->viewport_cursor );
2702 void vik_window_open_file ( VikWindow *vw, const gchar *filename, gboolean change_filename )
2704 vik_window_set_busy_cursor ( vw );
2706 // Enable the *new* filename to be accessible by the Layers codez
2707 gchar *original_filename = g_strdup ( vw->filename );
2708 g_free ( vw->filename );
2709 vw->filename = g_strdup ( filename );
2710 gboolean success = FALSE;
2711 gboolean restore_original_filename = FALSE;
2713 vw->loaded_type = a_file_load ( vik_layers_panel_get_top_layer(vw->viking_vlp), vw->viking_vvp, filename );
2714 switch ( vw->loaded_type )
2716 case LOAD_TYPE_READ_FAILURE:
2717 a_dialog_error_msg ( GTK_WINDOW(vw), _("The file you requested could not be opened.") );
2719 case LOAD_TYPE_GPSBABEL_FAILURE:
2720 a_dialog_error_msg ( GTK_WINDOW(vw), _("GPSBabel is required to load files of this type or GPSBabel encountered problems.") );
2722 case LOAD_TYPE_GPX_FAILURE:
2723 a_dialog_error_msg_extra ( GTK_WINDOW(vw), _("Unable to load malformed GPX file %s"), filename );
2725 case LOAD_TYPE_UNSUPPORTED_FAILURE:
2726 a_dialog_error_msg_extra ( GTK_WINDOW(vw), _("Unsupported file type for %s"), filename );
2728 case LOAD_TYPE_VIK_FAILURE_NON_FATAL:
2730 // Since we can process .vik files with issues just show a warning in the status bar
2731 // Not that a user can do much about it... or tells them what this issue is yet...
2732 gchar *msg = g_strdup_printf (_("WARNING: issues encountered loading %s"), a_file_basename (filename) );
2733 vik_statusbar_set_message ( vw->viking_vs, VIK_STATUSBAR_INFO, msg );
2736 // No break, carry on to show any data
2737 case LOAD_TYPE_VIK_SUCCESS:
2739 restore_original_filename = TRUE; // NB Will actually get inverted by the 'success' component below
2740 GtkWidget *mode_button;
2742 if ( change_filename )
2743 window_set_filename ( vw, filename );
2744 mode_button = vik_window_get_drawmode_button ( vw, vik_viewport_get_drawmode ( vw->viking_vvp ) );
2745 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. */
2746 gtk_check_menu_item_set_active ( GTK_CHECK_MENU_ITEM(mode_button), TRUE );
2747 vw->only_updating_coord_mode_ui = FALSE;
2749 vik_layers_panel_change_coord_mode ( vw->viking_vlp, vik_viewport_get_coord_mode ( vw->viking_vvp ) );
2751 mode_button = gtk_ui_manager_get_widget ( vw->uim, "/ui/MainMenu/View/SetShow/ShowScale" );
2752 g_assert ( mode_button );
2753 gtk_check_menu_item_set_active ( GTK_CHECK_MENU_ITEM(mode_button),vik_viewport_get_draw_scale(vw->viking_vvp) );
2755 mode_button = gtk_ui_manager_get_widget ( vw->uim, "/ui/MainMenu/View/SetShow/ShowCenterMark" );
2756 g_assert ( mode_button );
2757 gtk_check_menu_item_set_active ( GTK_CHECK_MENU_ITEM(mode_button),vik_viewport_get_draw_centermark(vw->viking_vvp) );
2759 mode_button = gtk_ui_manager_get_widget ( vw->uim, "/ui/MainMenu/View/SetShow/ShowHighlight" );
2760 g_assert ( mode_button );
2761 gtk_check_menu_item_set_active ( GTK_CHECK_MENU_ITEM(mode_button),vik_viewport_get_draw_highlight (vw->viking_vvp) );
2763 // NB No break, carry on to redraw
2764 //case LOAD_TYPE_OTHER_SUCCESS:
2767 // When LOAD_TYPE_OTHER_SUCCESS *only*, this will maintain the existing Viking project
2768 restore_original_filename = ! restore_original_filename;
2769 update_recently_used_document(filename);
2774 if ( ! success || restore_original_filename )
2775 // Load didn't work or want to keep as the existing Viking project, keep using the original name
2776 window_set_filename ( vw, original_filename );
2777 g_free ( original_filename );
2779 vik_window_clear_busy_cursor ( vw );
2782 static void load_file ( GtkAction *a, VikWindow *vw )
2784 GSList *files = NULL;
2785 GSList *cur_file = NULL;
2787 if (!strcmp(gtk_action_get_name(a), "Open")) {
2790 else if (!strcmp(gtk_action_get_name(a), "Append")) {
2794 g_critical("Houston, we've had a problem.");
2798 if ( ! vw->open_dia )
2800 vw->open_dia = gtk_file_chooser_dialog_new (_("Please select a GPS data file to open. "),
2802 GTK_FILE_CHOOSER_ACTION_OPEN,
2803 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
2804 GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT,
2806 gchar *cwd = g_get_current_dir();
2808 gtk_file_chooser_set_current_folder ( GTK_FILE_CHOOSER(vw->open_dia), cwd );
2812 GtkFileFilter *filter;
2813 // NB file filters are listed this way for alphabetical ordering
2814 #ifdef VIK_CONFIG_GEOCACHES
2815 filter = gtk_file_filter_new ();
2816 gtk_file_filter_set_name( filter, _("Geocaching") );
2817 gtk_file_filter_add_pattern ( filter, "*.loc" ); // No MIME type available
2818 gtk_file_chooser_add_filter (GTK_FILE_CHOOSER(vw->open_dia), filter);
2821 filter = gtk_file_filter_new ();
2822 gtk_file_filter_set_name( filter, _("Google Earth") );
2823 gtk_file_filter_add_mime_type ( filter, "application/vnd.google-earth.kml+xml");
2824 gtk_file_chooser_add_filter (GTK_FILE_CHOOSER(vw->open_dia), filter);
2826 filter = gtk_file_filter_new ();
2827 gtk_file_filter_set_name( filter, _("GPX") );
2828 gtk_file_filter_add_pattern ( filter, "*.gpx" ); // No MIME type available
2829 gtk_file_chooser_add_filter (GTK_FILE_CHOOSER(vw->open_dia), filter);
2831 filter = gtk_file_filter_new ();
2832 gtk_file_filter_set_name ( filter, _("JPG") );
2833 gtk_file_filter_add_mime_type ( filter, "image/jpeg");
2834 gtk_file_chooser_add_filter (GTK_FILE_CHOOSER(vw->open_dia), filter);
2836 filter = gtk_file_filter_new ();
2837 gtk_file_filter_set_name( filter, _("Viking") );
2838 gtk_file_filter_add_pattern ( filter, "*.vik" );
2839 gtk_file_filter_add_pattern ( filter, "*.viking" );
2840 gtk_file_chooser_add_filter (GTK_FILE_CHOOSER(vw->open_dia), filter);
2842 // NB could have filters for gpspoint (*.gps,*.gpsoint?) + gpsmapper (*.gsm,*.gpsmapper?)
2843 // However assume this are barely used and thus not worthy of inclusion
2844 // as they'll just make the options too many and have no clear file pattern
2845 // one can always use the all option
2846 filter = gtk_file_filter_new ();
2847 gtk_file_filter_set_name( filter, _("All") );
2848 gtk_file_filter_add_pattern ( filter, "*" );
2849 gtk_file_chooser_add_filter (GTK_FILE_CHOOSER(vw->open_dia), filter);
2850 // Default to any file - same as before open filters were added
2851 gtk_file_chooser_set_filter (GTK_FILE_CHOOSER(vw->open_dia), filter);
2853 gtk_file_chooser_set_select_multiple ( GTK_FILE_CHOOSER(vw->open_dia), TRUE );
2854 gtk_window_set_transient_for ( GTK_WINDOW(vw->open_dia), GTK_WINDOW(vw) );
2855 gtk_window_set_destroy_with_parent ( GTK_WINDOW(vw->open_dia), TRUE );
2857 if ( gtk_dialog_run ( GTK_DIALOG(vw->open_dia) ) == GTK_RESPONSE_ACCEPT )
2859 gtk_widget_hide ( vw->open_dia );
2860 #ifdef VIKING_PROMPT_IF_MODIFIED
2861 if ( (vw->modified || vw->filename) && newwindow )
2863 if ( vw->filename && newwindow )
2865 g_signal_emit ( G_OBJECT(vw), window_signals[VW_OPENWINDOW_SIGNAL], 0, gtk_file_chooser_get_filenames (GTK_FILE_CHOOSER(vw->open_dia) ) );
2867 files = gtk_file_chooser_get_filenames (GTK_FILE_CHOOSER(vw->open_dia) );
2868 gboolean change_fn = newwindow && (g_slist_length(files)==1); /* only change fn if one file */
2869 gboolean first_vik_file = TRUE;
2871 while ( cur_file ) {
2873 gchar *file_name = cur_file->data;
2874 if ( newwindow && check_file_magic_vik ( file_name ) ) {
2875 // Load first of many .vik files in current window
2876 if ( first_vik_file ) {
2877 vik_window_open_file ( vw, file_name, TRUE );
2878 first_vik_file = FALSE;
2881 // Load each subsequent .vik file in a separate window
2882 VikWindow *newvw = vik_window_new_window ();
2884 vik_window_open_file ( newvw, file_name, TRUE );
2889 vik_window_open_file ( vw, file_name, change_fn );
2892 cur_file = g_slist_next (cur_file);
2894 g_slist_free (files);
2898 gtk_widget_hide ( vw->open_dia );
2901 static gboolean save_file_as ( GtkAction *a, VikWindow *vw )
2903 gboolean rv = FALSE;
2905 if ( ! vw->save_dia )
2907 vw->save_dia = gtk_file_chooser_dialog_new (_("Save as Viking File."),
2909 GTK_FILE_CHOOSER_ACTION_SAVE,
2910 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
2911 GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT,
2913 gchar *cwd = g_get_current_dir();
2915 gtk_file_chooser_set_current_folder ( GTK_FILE_CHOOSER(vw->save_dia), cwd );
2919 GtkFileFilter *filter;
2920 filter = gtk_file_filter_new ();
2921 gtk_file_filter_set_name( filter, _("All") );
2922 gtk_file_filter_add_pattern ( filter, "*" );
2923 gtk_file_chooser_add_filter (GTK_FILE_CHOOSER(vw->save_dia), filter);
2925 filter = gtk_file_filter_new ();
2926 gtk_file_filter_set_name( filter, _("Viking") );
2927 gtk_file_filter_add_pattern ( filter, "*.vik" );
2928 gtk_file_filter_add_pattern ( filter, "*.viking" );
2929 gtk_file_chooser_add_filter (GTK_FILE_CHOOSER(vw->save_dia), filter);
2930 // Default to a Viking file
2931 gtk_file_chooser_set_filter (GTK_FILE_CHOOSER(vw->save_dia), filter);
2933 gtk_window_set_transient_for ( GTK_WINDOW(vw->save_dia), GTK_WINDOW(vw) );
2934 gtk_window_set_destroy_with_parent ( GTK_WINDOW(vw->save_dia), TRUE );
2936 // Auto append / replace extension with '.vik' to the suggested file name as it's going to be a Viking File
2937 gchar* auto_save_name = g_strdup ( window_get_filename ( vw ) );
2938 if ( ! a_file_check_ext ( auto_save_name, ".vik" ) )
2939 auto_save_name = g_strconcat ( auto_save_name, ".vik", NULL );
2941 gtk_file_chooser_set_current_name (GTK_FILE_CHOOSER(vw->save_dia), auto_save_name);
2943 while ( gtk_dialog_run ( GTK_DIALOG(vw->save_dia) ) == GTK_RESPONSE_ACCEPT )
2945 fn = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER(vw->save_dia) );
2946 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 ) ) )
2948 window_set_filename ( vw, fn );
2949 rv = window_save ( vw );
2950 vw->modified = FALSE;
2954 g_free ( auto_save_name );
2955 gtk_widget_hide ( vw->save_dia );
2959 static gboolean window_save ( VikWindow *vw )
2961 vik_window_set_busy_cursor ( vw );
2962 gboolean success = TRUE;
2964 if ( a_file_save ( vik_layers_panel_get_top_layer ( vw->viking_vlp ), vw->viking_vvp, vw->filename ) )
2966 update_recently_used_document ( vw->filename );
2970 a_dialog_error_msg ( GTK_WINDOW(vw), _("The filename you requested could not be opened for writing.") );
2973 vik_window_clear_busy_cursor ( vw );
2977 static gboolean save_file ( GtkAction *a, VikWindow *vw )
2979 if ( ! vw->filename )
2980 return save_file_as ( NULL, vw );
2983 vw->modified = FALSE;
2984 return window_save ( vw );
2991 * Export all TRW Layers in the list to individual files in the specified directory
2993 * Returns: %TRUE on success
2995 static gboolean export_to ( VikWindow *vw, GList *gl, VikFileType_t vft, const gchar *dir, const gchar *extension )
2997 gboolean success = TRUE;
2999 gint export_count = 0;
3001 vik_window_set_busy_cursor ( vw );
3005 gchar *fn = g_strconcat ( dir, G_DIR_SEPARATOR_S, VIK_LAYER(gl->data)->name, extension, NULL );
3007 // Some protection in attempting to write too many same named files
3008 // As this will get horribly slow...
3009 gboolean safe = FALSE;
3011 while ( ii < 5000 ) {
3012 if ( g_file_test ( fn, G_FILE_TEST_EXISTS ) ) {
3015 fn = g_strdup_printf ( "%s%s%s#%03d%s", dir, G_DIR_SEPARATOR_S, VIK_LAYER(gl->data)->name, ii, extension );
3026 // NB: We allow exporting empty layers
3028 gboolean this_success = a_file_export ( VIK_TRW_LAYER(gl->data), fn, vft, NULL, TRUE );
3030 // Show some progress
3031 if ( this_success ) {
3033 gchar *message = g_strdup_printf ( _("Exporting to file: %s"), fn );
3034 vik_statusbar_set_message ( vw->viking_vs, VIK_STATUSBAR_INFO, message );
3035 while ( gtk_events_pending() )
3036 gtk_main_iteration ();
3040 success = success && this_success;
3044 gl = g_list_next ( gl );
3047 vik_window_clear_busy_cursor ( vw );
3049 // Confirm what happened.
3050 gchar *message = g_strdup_printf ( _("Exported files: %d"), export_count );
3051 vik_statusbar_set_message ( vw->viking_vs, VIK_STATUSBAR_INFO, message );
3057 static void export_to_common ( VikWindow *vw, VikFileType_t vft, const gchar *extension )
3059 GList *gl = vik_layers_panel_get_all_layers_of_type ( vw->viking_vlp, VIK_LAYER_TRW, TRUE );
3062 a_dialog_info_msg ( GTK_WINDOW(vw), _("Nothing to Export!") );
3066 GtkWidget *dialog = gtk_file_chooser_dialog_new ( _("Export to directory"),
3068 GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER,
3070 GTK_RESPONSE_REJECT,
3072 GTK_RESPONSE_ACCEPT,
3074 gtk_window_set_transient_for ( GTK_WINDOW(dialog), GTK_WINDOW(vw) );
3075 gtk_window_set_destroy_with_parent ( GTK_WINDOW(dialog), TRUE );
3076 gtk_window_set_modal ( GTK_WINDOW(dialog), TRUE );
3078 gtk_widget_show_all ( dialog );
3080 if ( gtk_dialog_run ( GTK_DIALOG(dialog) ) == GTK_RESPONSE_ACCEPT ) {
3081 gchar *dir = gtk_file_chooser_get_filename ( GTK_FILE_CHOOSER(dialog) );
3082 gtk_widget_destroy ( dialog );
3084 if ( !export_to ( vw, gl, vft, dir, extension ) )
3085 a_dialog_error_msg ( GTK_WINDOW(vw),_("Could not convert all files") );
3090 gtk_widget_destroy ( dialog );
3095 static void export_to_gpx ( GtkAction *a, VikWindow *vw )
3097 export_to_common ( vw, FILE_TYPE_GPX, ".gpx" );
3100 static void export_to_kml ( GtkAction *a, VikWindow *vw )
3102 export_to_common ( vw, FILE_TYPE_KML, ".kml" );
3105 #if !GLIB_CHECK_VERSION(2,26,0)
3106 typedef struct stat GStatBuf;
3109 static void file_properties_cb ( GtkAction *a, VikWindow *vw )
3111 gchar *message = NULL;
3112 if ( vw->filename ) {
3113 if ( g_file_test ( vw->filename, G_FILE_TEST_EXISTS ) ) {
3114 // Get some timestamp information of the file
3116 if ( g_stat ( vw->filename, &stat_buf ) == 0 ) {
3118 strftime ( time_buf, sizeof(time_buf), "%c", gmtime((const time_t *)&stat_buf.st_mtime) );
3120 gint byte_size = stat_buf.st_size;
3121 // See http://en.wikipedia.org/wiki/Megabyte (and Kilobyte)
3122 // hence using 1000 rather than 1024
3123 // so get output as per 'ls' or the Gtk file open dialog
3124 if ( byte_size < 1000 )
3125 size = g_strdup_printf ( _("%d bytes"), byte_size );
3126 else if ( byte_size < 1000*1000 )
3127 size = g_strdup_printf ( _("%3.1f kB"), (gdouble)byte_size / 1000 );
3129 size = g_strdup_printf ( _("%3.1f MB"), (gdouble)byte_size / (1000*1000) );
3130 message = g_strdup_printf ( _("%s\n\n%s\n\n%s"), vw->filename, time_buf, size );
3135 message = g_strdup ( _("File not accessible") );
3138 message = g_strdup ( _("No Viking File") );
3141 a_dialog_info_msg ( GTK_WINDOW(vw), message );
3145 static void acquire_from_gps ( GtkAction *a, VikWindow *vw )
3147 // Via the file menu, acquiring from a GPS makes a new layer
3148 // this has always been the way (not entirely sure if this was the real intention!)
3149 // thus maintain the behaviour ATM.
3150 // Hence explicit setting here (as the value may be changed elsewhere)
3151 vik_datasource_gps_interface.mode = VIK_DATASOURCE_CREATENEWLAYER;
3152 a_acquire(vw, vw->viking_vlp, vw->viking_vvp, &vik_datasource_gps_interface, NULL, NULL );
3155 static void acquire_from_file ( GtkAction *a, VikWindow *vw )
3157 a_acquire(vw, vw->viking_vlp, vw->viking_vvp, &vik_datasource_file_interface, NULL, NULL );
3160 static void acquire_from_routing ( GtkAction *a, VikWindow *vw )
3162 a_acquire(vw, vw->viking_vlp, vw->viking_vvp, &vik_datasource_routing_interface, NULL, NULL );
3165 #ifdef VIK_CONFIG_OPENSTREETMAP
3166 static void acquire_from_osm ( GtkAction *a, VikWindow *vw )
3168 a_acquire(vw, vw->viking_vlp, vw->viking_vvp, &vik_datasource_osm_interface, NULL, NULL );
3171 static void acquire_from_my_osm ( GtkAction *a, VikWindow *vw )
3173 a_acquire(vw, vw->viking_vlp, vw->viking_vvp, &vik_datasource_osm_my_traces_interface, NULL, NULL );
3177 #ifdef VIK_CONFIG_GEOCACHES
3178 static void acquire_from_gc ( GtkAction *a, VikWindow *vw )
3180 a_acquire(vw, vw->viking_vlp, vw->viking_vvp, &vik_datasource_gc_interface, NULL, NULL );
3184 #ifdef VIK_CONFIG_GEOTAG
3185 static void acquire_from_geotag ( GtkAction *a, VikWindow *vw )
3187 vik_datasource_geotag_interface.mode = VIK_DATASOURCE_CREATENEWLAYER;
3188 a_acquire(vw, vw->viking_vlp, vw->viking_vvp, &vik_datasource_geotag_interface, NULL, NULL );
3192 #ifdef VIK_CONFIG_GEONAMES
3193 static void acquire_from_wikipedia ( GtkAction *a, VikWindow *vw )
3195 a_acquire(vw, vw->viking_vlp, vw->viking_vvp, &vik_datasource_wikipedia_interface, NULL, NULL );
3199 static void acquire_from_url ( GtkAction *a, VikWindow *vw )
3201 vik_datasource_url_interface.mode = VIK_DATASOURCE_CREATENEWLAYER;
3202 a_acquire(vw, vw->viking_vlp, vw->viking_vvp, &vik_datasource_url_interface, NULL, NULL );
3205 static void goto_default_location( GtkAction *a, VikWindow *vw)
3208 ll.lat = a_vik_get_default_lat();
3209 ll.lon = a_vik_get_default_long();
3210 vik_viewport_set_center_latlon(vw->viking_vvp, &ll, TRUE);
3211 vik_layers_panel_emit_update(vw->viking_vlp);
3215 static void goto_address( GtkAction *a, VikWindow *vw)
3217 a_vik_goto ( vw, vw->viking_vvp );
3218 vik_layers_panel_emit_update ( vw->viking_vlp );
3221 static void mapcache_flush_cb ( GtkAction *a, VikWindow *vw )
3226 static void layer_defaults_cb ( GtkAction *a, VikWindow *vw )
3228 gchar **texts = g_strsplit ( gtk_action_get_name(a), "Layer", 0 );
3231 return; // Internally broken :(
3233 if ( ! a_layer_defaults_show_window ( GTK_WINDOW(vw), texts[1] ) )
3234 a_dialog_info_msg ( GTK_WINDOW(vw), _("This layer has no configurable properties.") );
3235 // NB no update needed
3237 g_strfreev ( texts );
3240 static void preferences_change_update ( VikWindow *vw, gpointer data )
3242 // Want to update all TrackWaypoint layers
3243 GList *layers = vik_layers_panel_get_all_layers_of_type ( vw->viking_vlp, VIK_LAYER_TRW, TRUE );
3249 // Reset the individual waypoints themselves due to the preferences change
3250 VikTrwLayer *vtl = VIK_TRW_LAYER(layers->data);
3251 vik_trw_layer_reset_waypoints ( vtl );
3252 layers = g_list_next ( layers );
3255 g_list_free ( layers );
3260 static void preferences_cb ( GtkAction *a, VikWindow *vw )
3262 gboolean wp_icon_size = a_vik_get_use_large_waypoint_icons();
3264 a_preferences_show_window ( GTK_WINDOW(vw) );
3266 // Has the waypoint size setting changed?
3267 if (wp_icon_size != a_vik_get_use_large_waypoint_icons()) {
3268 // Delete icon indexing 'cache' and so automatically regenerates with the new setting when changed
3269 clear_garmin_icon_syms ();
3271 // Update all windows
3272 g_slist_foreach ( window_list, (GFunc) preferences_change_update, NULL );
3276 static void default_location_cb ( GtkAction *a, VikWindow *vw )
3278 /* Simplistic repeat of preference setting
3279 Only the name & type are important for setting the preference via this 'external' way */
3280 VikLayerParam pref_lat[] = {
3281 { VIK_LAYER_NUM_TYPES,
3282 VIKING_PREFERENCES_NAMESPACE "default_latitude",
3283 VIK_LAYER_PARAM_DOUBLE,
3286 VIK_LAYER_WIDGET_SPINBUTTON,
3294 VikLayerParam pref_lon[] = {
3295 { VIK_LAYER_NUM_TYPES,
3296 VIKING_PREFERENCES_NAMESPACE "default_longitude",
3297 VIK_LAYER_PARAM_DOUBLE,
3300 VIK_LAYER_WIDGET_SPINBUTTON,
3309 /* Get current center */
3311 vik_coord_to_latlon ( vik_viewport_get_center ( vw->viking_vvp ), &ll );
3313 /* Apply to preferences */
3314 VikLayerParamData vlp_data;
3315 vlp_data.d = ll.lat;
3316 a_preferences_run_setparam (vlp_data, pref_lat);
3317 vlp_data.d = ll.lon;
3318 a_preferences_run_setparam (vlp_data, pref_lon);
3319 /* Remember to save */
3320 a_preferences_save_to_file();
3323 static void clear_cb ( GtkAction *a, VikWindow *vw )
3325 vik_layers_panel_clear ( vw->viking_vlp );
3326 window_set_filename ( vw, NULL );
3330 static void window_close ( GtkAction *a, VikWindow *vw )
3332 if ( ! delete_event ( vw ) )
3333 gtk_widget_destroy ( GTK_WIDGET(vw) );
3336 static gboolean save_file_and_exit ( GtkAction *a, VikWindow *vw )
3338 if (save_file( NULL, vw)) {
3339 window_close( NULL, vw);
3346 static void zoom_to_cb ( GtkAction *a, VikWindow *vw )
3348 gdouble xmpp = vik_viewport_get_xmpp ( vw->viking_vvp ), ympp = vik_viewport_get_ympp ( vw->viking_vvp );
3349 if ( a_dialog_custom_zoom ( GTK_WINDOW(vw), &xmpp, &ympp ) )
3351 vik_viewport_set_xmpp ( vw->viking_vvp, xmpp );
3352 vik_viewport_set_ympp ( vw->viking_vvp, ympp );
3357 static void save_image_file ( VikWindow *vw, const gchar *fn, guint w, guint h, gdouble zoom, gboolean save_as_png )
3359 /* more efficient way: stuff draws directly to pixbuf (fork viewport) */
3360 GdkPixbuf *pixbuf_to_save;
3361 gdouble old_xmpp, old_ympp;
3362 GError *error = NULL;
3364 GtkWidget *msgbox = gtk_message_dialog_new ( GTK_WINDOW(vw),
3365 GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
3368 _("Generating image file...") );
3370 g_signal_connect_swapped (msgbox, "response", G_CALLBACK (gtk_widget_destroy), msgbox);
3371 // Ensure dialog shown
3372 gtk_widget_show_all ( msgbox );
3374 vik_statusbar_set_message ( vw->viking_vs, VIK_STATUSBAR_INFO, _("Generating image file...") );
3375 while ( gtk_events_pending() )
3376 gtk_main_iteration ();
3377 // Despite many efforts & variations, GTK on my Linux system doesn't show the actual msgbox contents :(
3378 // At least the empty box can give a clue something's going on + the statusbar msg...
3379 // Windows version under Wine OK!
3381 /* backup old zoom & set new */
3382 old_xmpp = vik_viewport_get_xmpp ( vw->viking_vvp );
3383 old_ympp = vik_viewport_get_ympp ( vw->viking_vvp );
3384 vik_viewport_set_zoom ( vw->viking_vvp, zoom );
3386 /* reset width and height: */
3387 vik_viewport_configure_manually ( vw->viking_vvp, w, h );
3389 /* draw all layers */
3392 /* save buffer as file. */
3393 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);
3394 if ( !pixbuf_to_save ) {
3395 g_warning("Failed to generate internal pixmap size: %d x %d", w, h);
3396 gtk_message_dialog_set_markup ( GTK_MESSAGE_DIALOG(msgbox), _("Failed to generate internal image.\n\nTry creating a smaller image.") );
3400 gdk_pixbuf_save ( pixbuf_to_save, fn, save_as_png ? "png" : "jpeg", &error, NULL );
3403 g_warning("Unable to write to file %s: %s", fn, error->message );
3404 gtk_message_dialog_set_markup ( GTK_MESSAGE_DIALOG(msgbox), _("Failed to generate image file.") );
3405 g_error_free (error);
3409 gtk_message_dialog_set_markup ( GTK_MESSAGE_DIALOG(msgbox), _("Image file generated.") );
3411 g_object_unref ( G_OBJECT(pixbuf_to_save) );
3414 vik_statusbar_set_message ( vw->viking_vs, VIK_STATUSBAR_INFO, "" );
3415 gtk_dialog_add_button ( GTK_DIALOG(msgbox), GTK_STOCK_OK, GTK_RESPONSE_OK );
3416 gtk_dialog_run ( GTK_DIALOG(msgbox) ); // Don't care about the result
3418 /* pretend like nothing happened ;) */
3419 vik_viewport_set_xmpp ( vw->viking_vvp, old_xmpp );
3420 vik_viewport_set_ympp ( vw->viking_vvp, old_ympp );
3421 vik_viewport_configure ( vw->viking_vvp );
3425 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 )
3427 gulong size = sizeof(gchar) * (strlen(fn) + 15);
3428 gchar *name_of_file = g_malloc ( size );
3430 struct UTM utm_orig, utm;
3432 /* *** copied from above *** */
3433 GdkPixbuf *pixbuf_to_save;
3434 gdouble old_xmpp, old_ympp;
3435 GError *error = NULL;
3437 /* backup old zoom & set new */
3438 old_xmpp = vik_viewport_get_xmpp ( vw->viking_vvp );
3439 old_ympp = vik_viewport_get_ympp ( vw->viking_vvp );
3440 vik_viewport_set_zoom ( vw->viking_vvp, zoom );
3442 /* reset width and height: do this only once for all images (same size) */
3443 vik_viewport_configure_manually ( vw->viking_vvp, w, h );
3444 /* *** end copy from above *** */
3446 g_assert ( vik_viewport_get_coord_mode ( vw->viking_vvp ) == VIK_COORD_UTM );
3450 utm_orig = *((const struct UTM *)vik_viewport_get_center ( vw->viking_vvp ));
3452 for ( y = 1; y <= tiles_h; y++ )
3454 for ( x = 1; x <= tiles_w; x++ )
3456 g_snprintf ( name_of_file, size, "%s%cy%d-x%d.%s", fn, G_DIR_SEPARATOR, y, x, save_as_png ? "png" : "jpg" );
3458 if ( tiles_w & 0x1 )
3459 utm.easting += ((gdouble)x - ceil(((gdouble)tiles_w)/2)) * (w*zoom);
3461 utm.easting += ((gdouble)x - (((gdouble)tiles_w)+1)/2) * (w*zoom);
3462 if ( tiles_h & 0x1 ) /* odd */
3463 utm.northing -= ((gdouble)y - ceil(((gdouble)tiles_h)/2)) * (h*zoom);
3465 utm.northing -= ((gdouble)y - (((gdouble)tiles_h)+1)/2) * (h*zoom);
3467 /* move to correct place. */
3468 vik_viewport_set_center_utm ( vw->viking_vvp, &utm, FALSE );
3472 /* save buffer as file. */
3473 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);
3474 gdk_pixbuf_save ( pixbuf_to_save, name_of_file, save_as_png ? "png" : "jpeg", &error, NULL );
3477 g_warning("Unable to write to file %s: %s", name_of_file, error->message );
3478 g_error_free (error);
3481 g_object_unref ( G_OBJECT(pixbuf_to_save) );
3485 vik_viewport_set_center_utm ( vw->viking_vvp, &utm_orig, FALSE );
3486 vik_viewport_set_xmpp ( vw->viking_vvp, old_xmpp );
3487 vik_viewport_set_ympp ( vw->viking_vvp, old_ympp );
3488 vik_viewport_configure ( vw->viking_vvp );
3491 g_free ( name_of_file );
3494 static void draw_to_image_file_current_window_cb(GtkWidget* widget,GdkEventButton *event,gpointer *pass_along)
3496 VikWindow *vw = VIK_WINDOW(pass_along[0]);
3497 GtkSpinButton *width_spin = GTK_SPIN_BUTTON(pass_along[1]), *height_spin = GTK_SPIN_BUTTON(pass_along[2]);
3499 gint active = gtk_combo_box_get_active ( GTK_COMBO_BOX(pass_along[3]) );
3500 gdouble zoom = pow (2, active-2 );
3502 gdouble width_min, width_max, height_min, height_max;
3505 gtk_spin_button_get_range ( width_spin, &width_min, &width_max );
3506 gtk_spin_button_get_range ( height_spin, &height_min, &height_max );
3508 /* TODO: support for xzoom and yzoom values */
3509 width = vik_viewport_get_width ( vw->viking_vvp ) * vik_viewport_get_xmpp ( vw->viking_vvp ) / zoom;
3510 height = vik_viewport_get_height ( vw->viking_vvp ) * vik_viewport_get_xmpp ( vw->viking_vvp ) / zoom;
3512 if ( width > width_max || width < width_min || height > height_max || height < height_min )
3513 a_dialog_info_msg ( GTK_WINDOW(vw), _("Viewable region outside allowable pixel size bounds for image. Clipping width/height values.") );
3515 gtk_spin_button_set_value ( width_spin, width );
3516 gtk_spin_button_set_value ( height_spin, height );
3519 static void draw_to_image_file_total_area_cb (GtkSpinButton *spinbutton, gpointer *pass_along)
3521 GtkSpinButton *width_spin = GTK_SPIN_BUTTON(pass_along[1]), *height_spin = GTK_SPIN_BUTTON(pass_along[2]);
3523 gint active = gtk_combo_box_get_active ( GTK_COMBO_BOX(pass_along[3]) );
3524 gdouble zoom = pow (2, active-2 );
3528 w = gtk_spin_button_get_value(width_spin) * zoom;
3529 h = gtk_spin_button_get_value(height_spin) * zoom;
3530 if (pass_along[4]) /* save many images; find TOTAL area covered */
3532 w *= gtk_spin_button_get_value(GTK_SPIN_BUTTON(pass_along[4]));
3533 h *= gtk_spin_button_get_value(GTK_SPIN_BUTTON(pass_along[5]));
3535 vik_units_distance_t dist_units = a_vik_get_units_distance ();
3536 switch (dist_units) {
3537 case VIK_UNITS_DISTANCE_KILOMETRES:
3538 label_text = g_strdup_printf ( _("Total area: %ldm x %ldm (%.3f sq. km)"), (glong)w, (glong)h, (w*h/1000000));
3540 case VIK_UNITS_DISTANCE_MILES:
3541 label_text = g_strdup_printf ( _("Total area: %ldm x %ldm (%.3f sq. miles)"), (glong)w, (glong)h, (w*h/2589988.11));
3544 label_text = g_strdup_printf ("Just to keep the compiler happy");
3545 g_critical("Houston, we've had a problem. distance=%d", dist_units);
3548 gtk_label_set_text(GTK_LABEL(pass_along[6]), label_text);
3549 g_free ( label_text );
3553 * Get an allocated filename (or directory as specified)
3555 static gchar* draw_image_filename ( VikWindow *vw, gboolean one_image_only )
3558 if ( one_image_only )
3561 if (!vw->save_img_dia) {
3562 vw->save_img_dia = gtk_file_chooser_dialog_new (_("Save Image"),
3564 GTK_FILE_CHOOSER_ACTION_SAVE,
3565 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
3566 GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT,
3569 gchar *cwd = g_get_current_dir();
3571 gtk_file_chooser_set_current_folder ( GTK_FILE_CHOOSER(vw->save_img_dia), cwd );
3575 GtkFileChooser *chooser = GTK_FILE_CHOOSER ( vw->save_img_dia );
3577 GtkFileFilter *filter;
3578 filter = gtk_file_filter_new ();
3579 gtk_file_filter_set_name ( filter, _("All") );
3580 gtk_file_filter_add_pattern ( filter, "*" );
3581 gtk_file_chooser_add_filter ( chooser, filter );
3583 filter = gtk_file_filter_new ();
3584 gtk_file_filter_set_name ( filter, _("JPG") );
3585 gtk_file_filter_add_mime_type ( filter, "image/jpeg");
3586 gtk_file_chooser_add_filter ( chooser, filter );
3588 if ( !vw->draw_image_save_as_png )
3589 gtk_file_chooser_set_filter ( chooser, filter );
3591 filter = gtk_file_filter_new ();
3592 gtk_file_filter_set_name ( filter, _("PNG") );
3593 gtk_file_filter_add_mime_type ( filter, "image/png");
3594 gtk_file_chooser_add_filter ( chooser, filter );
3596 if ( vw->draw_image_save_as_png )
3597 gtk_file_chooser_set_filter ( chooser, filter );
3599 gtk_window_set_transient_for ( GTK_WINDOW(vw->save_img_dia), GTK_WINDOW(vw) );
3600 gtk_window_set_destroy_with_parent ( GTK_WINDOW(vw->save_img_dia), TRUE );
3603 if ( gtk_dialog_run ( GTK_DIALOG(vw->save_img_dia) ) == GTK_RESPONSE_ACCEPT ) {
3604 fn = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER(vw->save_img_dia) );
3605 if ( g_file_test ( fn, G_FILE_TEST_EXISTS ) )
3606 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 ) ) )
3609 gtk_widget_hide ( vw->save_img_dia );
3613 // For some reason this method is only written to work in UTM...
3614 if ( vik_viewport_get_coord_mode(vw->viking_vvp) != VIK_COORD_UTM ) {
3615 a_dialog_error_msg ( GTK_WINDOW(vw), _("You must be in UTM mode to use this feature") );
3619 if (!vw->save_img_dir_dia) {
3620 vw->save_img_dir_dia = gtk_file_chooser_dialog_new (_("Choose a directory to hold images"),
3622 GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER,
3623 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
3624 GTK_STOCK_OK, GTK_RESPONSE_ACCEPT,
3626 gtk_window_set_transient_for ( GTK_WINDOW(vw->save_img_dir_dia), GTK_WINDOW(vw) );
3627 gtk_window_set_destroy_with_parent ( GTK_WINDOW(vw->save_img_dir_dia), TRUE );
3630 if ( gtk_dialog_run ( GTK_DIALOG(vw->save_img_dir_dia) ) == GTK_RESPONSE_ACCEPT ) {
3631 fn = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER(vw->save_img_dir_dia) );
3633 gtk_widget_hide ( vw->save_img_dir_dia );
3638 static void draw_to_image_file ( VikWindow *vw, gboolean one_image_only )
3640 /* todo: default for answers inside VikWindow or static (thruout instance) */
3641 GtkWidget *dialog = gtk_dialog_new_with_buttons ( _("Save to Image File"), GTK_WINDOW(vw),
3642 GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
3644 GTK_RESPONSE_REJECT,
3646 GTK_RESPONSE_ACCEPT,
3648 GtkWidget *width_label, *width_spin, *height_label, *height_spin;
3649 GtkWidget *png_radio, *jpeg_radio;
3650 GtkWidget *current_window_button;
3651 gpointer current_window_pass_along[7];
3652 GtkWidget *zoom_label, *zoom_combo;
3653 GtkWidget *total_size_label;
3655 /* only used if (!one_image_only) */
3656 GtkWidget *tiles_width_spin = NULL, *tiles_height_spin = NULL;
3658 width_label = gtk_label_new ( _("Width (pixels):") );
3659 width_spin = gtk_spin_button_new ( GTK_ADJUSTMENT(gtk_adjustment_new ( vw->draw_image_width, 10, 50000, 10, 100, 0 )), 10, 0 );
3660 height_label = gtk_label_new ( _("Height (pixels):") );
3661 height_spin = gtk_spin_button_new ( GTK_ADJUSTMENT(gtk_adjustment_new ( vw->draw_image_height, 10, 50000, 10, 100, 0 )), 10, 0 );
3663 GtkWidget *win_warning_label = gtk_label_new ( _("WARNING: USING LARGE IMAGES OVER 10000x10000\nMAY CRASH THE PROGRAM!") );
3665 zoom_label = gtk_label_new ( _("Zoom (meters per pixel):") );
3666 /* TODO: separate xzoom and yzoom factors */
3667 zoom_combo = create_zoom_combo_all_levels();
3669 gdouble mpp = vik_viewport_get_xmpp(vw->viking_vvp);
3670 gint active = 2 + round ( log (mpp) / log (2) );
3672 // Can we not hard code size here?
3677 gtk_combo_box_set_active ( GTK_COMBO_BOX(zoom_combo), active );
3679 total_size_label = gtk_label_new ( NULL );
3681 current_window_button = gtk_button_new_with_label ( _("Area in current viewable window") );
3682 current_window_pass_along [0] = vw;
3683 current_window_pass_along [1] = width_spin;
3684 current_window_pass_along [2] = height_spin;
3685 current_window_pass_along [3] = zoom_combo;
3686 current_window_pass_along [4] = NULL; /* used for one_image_only != 1 */
3687 current_window_pass_along [5] = NULL;
3688 current_window_pass_along [6] = total_size_label;
3689 g_signal_connect ( G_OBJECT(current_window_button), "button_press_event", G_CALLBACK(draw_to_image_file_current_window_cb), current_window_pass_along );
3691 png_radio = gtk_radio_button_new_with_label ( NULL, _("Save as PNG") );
3692 jpeg_radio = gtk_radio_button_new_with_label_from_widget ( GTK_RADIO_BUTTON(png_radio), _("Save as JPEG") );
3694 gtk_box_pack_start (GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), png_radio, FALSE, FALSE, 0);
3695 gtk_box_pack_start (GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), jpeg_radio, FALSE, FALSE, 0);
3697 if ( ! vw->draw_image_save_as_png )
3698 gtk_toggle_button_set_active ( GTK_TOGGLE_BUTTON(jpeg_radio), TRUE );
3700 gtk_box_pack_start (GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), width_label, FALSE, FALSE, 0);
3701 gtk_box_pack_start (GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), width_spin, FALSE, FALSE, 0);
3702 gtk_box_pack_start (GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), height_label, FALSE, FALSE, 0);
3703 gtk_box_pack_start (GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), height_spin, FALSE, FALSE, 0);
3705 gtk_box_pack_start (GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), win_warning_label, FALSE, FALSE, 0);
3707 gtk_box_pack_start (GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), current_window_button, FALSE, FALSE, 0);
3708 gtk_box_pack_start (GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), zoom_label, FALSE, FALSE, 0);
3709 gtk_box_pack_start (GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), zoom_combo, FALSE, FALSE, 0);
3711 if ( ! one_image_only )
3713 GtkWidget *tiles_width_label, *tiles_height_label;
3715 tiles_width_label = gtk_label_new ( _("East-west image tiles:") );
3716 tiles_width_spin = gtk_spin_button_new ( GTK_ADJUSTMENT(gtk_adjustment_new ( 5, 1, 10, 1, 100, 0 )), 1, 0 );
3717 tiles_height_label = gtk_label_new ( _("North-south image tiles:") );
3718 tiles_height_spin = gtk_spin_button_new ( GTK_ADJUSTMENT(gtk_adjustment_new ( 5, 1, 10, 1, 100, 0 )), 1, 0 );
3719 gtk_box_pack_start (GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), tiles_width_label, FALSE, FALSE, 0);
3720 gtk_box_pack_start (GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), tiles_width_spin, FALSE, FALSE, 0);
3721 gtk_box_pack_start (GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), tiles_height_label, FALSE, FALSE, 0);
3722 gtk_box_pack_start (GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), tiles_height_spin, FALSE, FALSE, 0);
3724 current_window_pass_along [4] = tiles_width_spin;
3725 current_window_pass_along [5] = tiles_height_spin;
3726 g_signal_connect ( G_OBJECT(tiles_width_spin), "value-changed", G_CALLBACK(draw_to_image_file_total_area_cb), current_window_pass_along );
3727 g_signal_connect ( G_OBJECT(tiles_height_spin), "value-changed", G_CALLBACK(draw_to_image_file_total_area_cb), current_window_pass_along );
3729 gtk_box_pack_start (GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), total_size_label, FALSE, FALSE, 0);
3730 g_signal_connect ( G_OBJECT(width_spin), "value-changed", G_CALLBACK(draw_to_image_file_total_area_cb), current_window_pass_along );
3731 g_signal_connect ( G_OBJECT(height_spin), "value-changed", G_CALLBACK(draw_to_image_file_total_area_cb), current_window_pass_along );
3732 g_signal_connect ( G_OBJECT(zoom_combo), "changed", G_CALLBACK(draw_to_image_file_total_area_cb), current_window_pass_along );
3734 draw_to_image_file_total_area_cb ( NULL, current_window_pass_along ); /* set correct size info now */
3736 gtk_dialog_set_default_response ( GTK_DIALOG(dialog), GTK_RESPONSE_ACCEPT );
3738 gtk_widget_show_all ( gtk_dialog_get_content_area(GTK_DIALOG(dialog)) );
3740 if ( gtk_dialog_run ( GTK_DIALOG(dialog) ) == GTK_RESPONSE_ACCEPT )
3742 gtk_widget_hide ( GTK_WIDGET(dialog) );
3744 gchar *fn = draw_image_filename ( vw, one_image_only );
3748 gint active = gtk_combo_box_get_active ( GTK_COMBO_BOX(zoom_combo) );
3749 gdouble zoom = pow (2, active-2 );
3751 if ( one_image_only )
3752 save_image_file ( vw, fn,
3753 vw->draw_image_width = gtk_spin_button_get_value_as_int ( GTK_SPIN_BUTTON(width_spin) ),
3754 vw->draw_image_height = gtk_spin_button_get_value_as_int ( GTK_SPIN_BUTTON(height_spin) ),
3756 vw->draw_image_save_as_png = gtk_toggle_button_get_active ( GTK_TOGGLE_BUTTON(png_radio) ) );
3758 // NB is in UTM mode ATM
3759 save_image_dir ( vw, fn,
3760 vw->draw_image_width = gtk_spin_button_get_value_as_int ( GTK_SPIN_BUTTON(width_spin) ),
3761 vw->draw_image_height = gtk_spin_button_get_value_as_int ( GTK_SPIN_BUTTON(height_spin) ),
3763 vw->draw_image_save_as_png = gtk_toggle_button_get_active ( GTK_TOGGLE_BUTTON(png_radio) ),
3764 gtk_spin_button_get_value ( GTK_SPIN_BUTTON(tiles_width_spin) ),
3765 gtk_spin_button_get_value ( GTK_SPIN_BUTTON(tiles_height_spin) ) );
3770 gtk_widget_destroy ( GTK_WIDGET(dialog) );
3774 static void draw_to_image_file_cb ( GtkAction *a, VikWindow *vw )
3776 draw_to_image_file ( vw, TRUE );
3779 static void draw_to_image_dir_cb ( GtkAction *a, VikWindow *vw )
3781 draw_to_image_file ( vw, FALSE );
3784 static void print_cb ( GtkAction *a, VikWindow *vw )
3786 a_print(vw, vw->viking_vvp);
3789 /* really a misnomer: changes coord mode (actual coordinates) AND/OR draw mode (viewport only) */
3790 static void window_change_coord_mode_cb ( GtkAction *old_a, GtkAction *a, VikWindow *vw )
3792 VikViewportDrawMode drawmode;
3793 if (!strcmp(gtk_action_get_name(a), "ModeUTM")) {
3794 drawmode = VIK_VIEWPORT_DRAWMODE_UTM;
3796 else if (!strcmp(gtk_action_get_name(a), "ModeLatLon")) {
3797 drawmode = VIK_VIEWPORT_DRAWMODE_LATLON;
3799 else if (!strcmp(gtk_action_get_name(a), "ModeExpedia")) {
3800 drawmode = VIK_VIEWPORT_DRAWMODE_EXPEDIA;
3802 else if (!strcmp(gtk_action_get_name(a), "ModeMercator")) {
3803 drawmode = VIK_VIEWPORT_DRAWMODE_MERCATOR;
3806 g_critical("Houston, we've had a problem.");
3810 if ( !vw->only_updating_coord_mode_ui )
3812 VikViewportDrawMode olddrawmode = vik_viewport_get_drawmode ( vw->viking_vvp );
3813 if ( olddrawmode != drawmode )
3815 /* this takes care of coord mode too */
3816 vik_viewport_set_drawmode ( vw->viking_vvp, drawmode );
3817 if ( drawmode == VIK_VIEWPORT_DRAWMODE_UTM ) {
3818 vik_layers_panel_change_coord_mode ( vw->viking_vlp, VIK_COORD_UTM );
3819 } else if ( olddrawmode == VIK_VIEWPORT_DRAWMODE_UTM ) {
3820 vik_layers_panel_change_coord_mode ( vw->viking_vlp, VIK_COORD_LATLON );
3827 static void set_draw_scale ( GtkAction *a, VikWindow *vw )
3829 GtkWidget *check_box = gtk_ui_manager_get_widget ( vw->uim, "/ui/MainMenu/View/SetShow/ShowScale" );
3830 g_assert(check_box);
3831 gboolean state = gtk_check_menu_item_get_active ( GTK_CHECK_MENU_ITEM(check_box));
3832 vik_viewport_set_draw_scale ( vw->viking_vvp, state );
3836 static void set_draw_centermark ( GtkAction *a, VikWindow *vw )
3838 GtkWidget *check_box = gtk_ui_manager_get_widget ( vw->uim, "/ui/MainMenu/View/SetShow/ShowCenterMark" );
3839 g_assert(check_box);
3840 gboolean state = gtk_check_menu_item_get_active ( GTK_CHECK_MENU_ITEM(check_box));
3841 vik_viewport_set_draw_centermark ( vw->viking_vvp, state );
3845 static void set_draw_highlight ( GtkAction *a, VikWindow *vw )
3847 GtkWidget *check_box = gtk_ui_manager_get_widget ( vw->uim, "/ui/MainMenu/View/SetShow/ShowHighlight" );
3848 g_assert(check_box);
3849 gboolean state = gtk_check_menu_item_get_active ( GTK_CHECK_MENU_ITEM(check_box));
3850 vik_viewport_set_draw_highlight ( vw->viking_vvp, state );
3854 static void set_bg_color ( GtkAction *a, VikWindow *vw )
3856 GtkWidget *colorsd = gtk_color_selection_dialog_new ( _("Choose a background color") );
3857 GdkColor *color = vik_viewport_get_background_gdkcolor ( vw->viking_vvp );
3858 gtk_color_selection_set_previous_color ( GTK_COLOR_SELECTION(gtk_color_selection_dialog_get_color_selection(GTK_COLOR_SELECTION_DIALOG(colorsd))), color );
3859 gtk_color_selection_set_current_color ( GTK_COLOR_SELECTION(gtk_color_selection_dialog_get_color_selection(GTK_COLOR_SELECTION_DIALOG(colorsd))), color );
3860 if ( gtk_dialog_run ( GTK_DIALOG(colorsd) ) == GTK_RESPONSE_OK )
3862 gtk_color_selection_get_current_color ( GTK_COLOR_SELECTION(gtk_color_selection_dialog_get_color_selection(GTK_COLOR_SELECTION_DIALOG(colorsd))), color );
3863 vik_viewport_set_background_gdkcolor ( vw->viking_vvp, color );
3867 gtk_widget_destroy ( colorsd );
3870 static void set_highlight_color ( GtkAction *a, VikWindow *vw )
3872 GtkWidget *colorsd = gtk_color_selection_dialog_new ( _("Choose a track highlight color") );
3873 GdkColor *color = vik_viewport_get_highlight_gdkcolor ( vw->viking_vvp );
3874 gtk_color_selection_set_previous_color ( GTK_COLOR_SELECTION(gtk_color_selection_dialog_get_color_selection(GTK_COLOR_SELECTION_DIALOG(colorsd))), color );
3875 gtk_color_selection_set_current_color ( GTK_COLOR_SELECTION(gtk_color_selection_dialog_get_color_selection(GTK_COLOR_SELECTION_DIALOG(colorsd))), color );
3876 if ( gtk_dialog_run ( GTK_DIALOG(colorsd) ) == GTK_RESPONSE_OK )
3878 gtk_color_selection_get_current_color ( GTK_COLOR_SELECTION(gtk_color_selection_dialog_get_color_selection(GTK_COLOR_SELECTION_DIALOG(colorsd))), color );
3879 vik_viewport_set_highlight_gdkcolor ( vw->viking_vvp, color );
3883 gtk_widget_destroy ( colorsd );
3888 /***********************************************************************************************
3890 ***********************************************************************************************/
3892 static GtkActionEntry entries[] = {
3893 { "File", NULL, N_("_File"), 0, 0, 0 },
3894 { "Edit", NULL, N_("_Edit"), 0, 0, 0 },
3895 { "View", NULL, N_("_View"), 0, 0, 0 },
3896 { "SetShow", NULL, N_("_Show"), 0, 0, 0 },
3897 { "SetZoom", NULL, N_("_Zoom"), 0, 0, 0 },
3898 { "SetPan", NULL, N_("_Pan"), 0, 0, 0 },
3899 { "Layers", NULL, N_("_Layers"), 0, 0, 0 },
3900 { "Tools", NULL, N_("_Tools"), 0, 0, 0 },
3901 { "Exttools", NULL, N_("_Webtools"), 0, 0, 0 },
3902 { "Help", NULL, N_("_Help"), 0, 0, 0 },
3904 { "New", GTK_STOCK_NEW, N_("_New"), "<control>N", N_("New file"), (GCallback)newwindow_cb },
3905 { "Open", GTK_STOCK_OPEN, N_("_Open..."), "<control>O", N_("Open a file"), (GCallback)load_file },
3906 { "OpenRecentFile", NULL, N_("Open _Recent File"), NULL, NULL, (GCallback)NULL },
3907 { "Append", GTK_STOCK_ADD, N_("Append _File..."), NULL, N_("Append data from a different file"), (GCallback)load_file },
3908 { "Export", GTK_STOCK_CONVERT, N_("_Export All"), NULL, N_("Export All TrackWaypoint Layers"), (GCallback)NULL },
3909 { "ExportGPX", NULL, N_("_GPX..."), NULL, N_("Export as GPX"), (GCallback)export_to_gpx },
3910 { "Acquire", GTK_STOCK_GO_DOWN, N_("A_cquire"), NULL, NULL, (GCallback)NULL },
3911 { "AcquireGPS", NULL, N_("From _GPS..."), NULL, N_("Transfer data from a GPS device"), (GCallback)acquire_from_gps },
3912 { "AcquireGPSBabel", NULL, N_("Import File With GPS_Babel..."), NULL, N_("Import file via GPSBabel converter"), (GCallback)acquire_from_file },
3913 { "AcquireRouting", NULL, N_("_Directions..."), NULL, N_("Get driving directions"), (GCallback)acquire_from_routing },
3914 #ifdef VIK_CONFIG_OPENSTREETMAP
3915 { "AcquireOSM", NULL, N_("_OSM Traces..."), NULL, N_("Get traces from OpenStreetMap"), (GCallback)acquire_from_osm },
3916 { "AcquireMyOSM", NULL, N_("_My OSM Traces..."), NULL, N_("Get Your Own Traces from OpenStreetMap"), (GCallback)acquire_from_my_osm },
3918 #ifdef VIK_CONFIG_GEOCACHES
3919 { "AcquireGC", NULL, N_("Geo_caches..."), NULL, N_("Get Geocaches from geocaching.com"), (GCallback)acquire_from_gc },
3921 #ifdef VIK_CONFIG_GEOTAG
3922 { "AcquireGeotag", NULL, N_("From Geotagged _Images..."), NULL, N_("Create waypoints from geotagged images"), (GCallback)acquire_from_geotag },
3924 { "AcquireURL", NULL, N_("From _URL..."), NULL, N_("Get a file from a URL"), (GCallback)acquire_from_url },
3925 #ifdef VIK_CONFIG_GEONAMES
3926 { "AcquireWikipedia", NULL, N_("From _Wikipedia Waypoints"), NULL, N_("Create waypoints from Wikipedia items in the current view"), (GCallback)acquire_from_wikipedia },
3928 { "Save", GTK_STOCK_SAVE, N_("_Save"), "<control>S", N_("Save the file"), (GCallback)save_file },
3929 { "SaveAs", GTK_STOCK_SAVE_AS, N_("Save _As..."), NULL, N_("Save the file under different name"), (GCallback)save_file_as },
3930 { "FileProperties", NULL, N_("Properties..."), NULL, N_("File Properties"), (GCallback)file_properties_cb },
3931 { "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 },
3932 { "GenImgDir", GTK_STOCK_DND_MULTIPLE, N_("Generate _Directory of Images..."), NULL, N_("FIXME:IMGDIR"), (GCallback)draw_to_image_dir_cb },
3933 { "Print", GTK_STOCK_PRINT, N_("_Print..."), NULL, N_("Print maps"), (GCallback)print_cb },
3934 { "Exit", GTK_STOCK_QUIT, N_("E_xit"), "<control>W", N_("Exit the program"), (GCallback)window_close },
3935 { "SaveExit", GTK_STOCK_QUIT, N_("Save and Exit"), NULL, N_("Save and Exit the program"), (GCallback)save_file_and_exit },
3937 { "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 },
3938 { "GoForward", GTK_STOCK_GO_FORWARD, N_("Go to the _Next Location"), NULL, N_("Go to the next location"), (GCallback)draw_goto_back_and_forth },
3939 { "GotoDefaultLocation", GTK_STOCK_HOME, N_("Go to the _Default Location"), NULL, N_("Go to the default location"), (GCallback)goto_default_location },
3940 { "GotoSearch", GTK_STOCK_JUMP_TO, N_("Go to _Location..."), NULL, N_("Go to address/place using text search"), (GCallback)goto_address },
3941 { "GotoLL", GTK_STOCK_JUMP_TO, N_("_Go to Lat/Lon..."), NULL, N_("Go to arbitrary lat/lon coordinate"), (GCallback)draw_goto_cb },
3942 { "GotoUTM", GTK_STOCK_JUMP_TO, N_("Go to UTM..."), NULL, N_("Go to arbitrary UTM coordinate"), (GCallback)draw_goto_cb },
3943 { "Refresh", GTK_STOCK_REFRESH, N_("_Refresh"), "F5", N_("Refresh any maps displayed"), (GCallback)draw_refresh_cb },
3944 { "SetHLColor",GTK_STOCK_SELECT_COLOR, N_("Set _Highlight Color..."), NULL, NULL, (GCallback)set_highlight_color },
3945 { "SetBGColor",GTK_STOCK_SELECT_COLOR, N_("Set Bac_kground Color..."), NULL, NULL, (GCallback)set_bg_color },
3946 { "ZoomIn", GTK_STOCK_ZOOM_IN, N_("Zoom _In"), "<control>plus", NULL, (GCallback)draw_zoom_cb },
3947 { "ZoomOut", GTK_STOCK_ZOOM_OUT, N_("Zoom _Out"), "<control>minus", NULL, (GCallback)draw_zoom_cb },
3948 { "ZoomTo", GTK_STOCK_ZOOM_FIT, N_("Zoom _To..."), "<control>Z", NULL, (GCallback)zoom_to_cb },
3949 { "PanNorth", NULL, N_("Pan _North"), "<control>Up", NULL, (GCallback)draw_pan_cb },
3950 { "PanEast", NULL, N_("Pan _East"), "<control>Right", NULL, (GCallback)draw_pan_cb },
3951 { "PanSouth", NULL, N_("Pan _South"), "<control>Down", NULL, (GCallback)draw_pan_cb },
3952 { "PanWest", NULL, N_("Pan _West"), "<control>Left", NULL, (GCallback)draw_pan_cb },
3953 { "BGJobs", GTK_STOCK_EXECUTE, N_("Background _Jobs"), NULL, NULL, (GCallback)a_background_show_window },
3955 { "Cut", GTK_STOCK_CUT, N_("Cu_t"), NULL, NULL, (GCallback)menu_cut_layer_cb },
3956 { "Copy", GTK_STOCK_COPY, N_("_Copy"), NULL, NULL, (GCallback)menu_copy_layer_cb },
3957 { "Paste", GTK_STOCK_PASTE, N_("_Paste"), NULL, NULL, (GCallback)menu_paste_layer_cb },
3958 { "Delete", GTK_STOCK_DELETE, N_("_Delete"), NULL, NULL, (GCallback)menu_delete_layer_cb },
3959 { "DeleteAll", NULL, N_("Delete All"), NULL, NULL, (GCallback)clear_cb },
3960 { "MapCacheFlush",NULL, N_("_Flush Map Cache"), NULL, NULL, (GCallback)mapcache_flush_cb },
3961 { "SetDefaultLocation", GTK_STOCK_GO_FORWARD, N_("_Set the Default Location"), NULL, N_("Set the Default Location to the current position"),(GCallback)default_location_cb },
3962 { "Preferences",GTK_STOCK_PREFERENCES, N_("_Preferences"), NULL, NULL, (GCallback)preferences_cb },
3963 { "LayerDefaults",GTK_STOCK_PROPERTIES, N_("_Layer Defaults"), NULL, NULL, NULL },
3964 { "Properties",GTK_STOCK_PROPERTIES, N_("_Properties"), NULL, NULL, (GCallback)menu_properties_cb },
3966 { "HelpEntry", GTK_STOCK_HELP, N_("_Help"), "F1", NULL, (GCallback)help_help_cb },
3967 { "About", GTK_STOCK_ABOUT, N_("_About"), NULL, NULL, (GCallback)help_about_cb },
3970 static GtkActionEntry entries_gpsbabel[] = {
3971 { "ExportKML", NULL, N_("_KML..."), NULL, N_("Export as KML"), (GCallback)export_to_kml },
3975 /* FIXME use VIEWPORT_DRAWMODE values */
3976 static GtkRadioActionEntry mode_entries[] = {
3977 { "ModeUTM", NULL, N_("_UTM Mode"), "<control>u", NULL, 0 },
3978 { "ModeExpedia", NULL, N_("_Expedia Mode"), "<control>e", NULL, 1 },
3979 { "ModeMercator", NULL, N_("_Mercator Mode"), "<control>m", NULL, 4 },
3980 { "ModeLatLon", NULL, N_("Lat_/Lon Mode"), "<control>l", NULL, 5 },
3983 static GtkToggleActionEntry toggle_entries[] = {
3984 { "ShowScale", NULL, N_("Show _Scale"), "<shift>F5", N_("Show Scale"), (GCallback)set_draw_scale, TRUE },
3985 { "ShowCenterMark", NULL, N_("Show _Center Mark"), "F6", N_("Show Center Mark"), (GCallback)set_draw_centermark, TRUE },
3986 { "ShowHighlight", GTK_STOCK_UNDERLINE, N_("Show _Highlight"), "F7", N_("Show Highlight"), (GCallback)set_draw_highlight, TRUE },
3987 { "FullScreen", GTK_STOCK_FULLSCREEN, N_("_Full Screen"), "F11", N_("Activate full screen mode"), (GCallback)full_screen_cb, FALSE },
3988 { "ViewSidePanel", GTK_STOCK_INDEX, N_("Show Side _Panel"), "F9", N_("Show Side Panel"), (GCallback)view_side_panel_cb, TRUE },
3989 { "ViewStatusBar", NULL, N_("Show Status_bar"), "F12", N_("Show Statusbar"), (GCallback)view_statusbar_cb, TRUE },
3990 { "ViewToolbar", NULL, N_("Show _Toolbar"), "F3", N_("Show Toolbar"), (GCallback)view_toolbar_cb, TRUE },
3991 { "ViewMainMenu", NULL, N_("Show _Menu"), "F4", N_("Show Menu"), (GCallback)view_main_menu_cb, TRUE },
3994 #include "menu.xml.h"
3995 static void window_create_ui( VikWindow *window )
3998 GtkActionGroup *action_group;
3999 GtkAccelGroup *accel_group;
4002 GtkIconFactory *icon_factory;
4003 GtkIconSet *icon_set;
4004 GtkRadioActionEntry *tools = NULL, *radio;
4007 uim = gtk_ui_manager_new ();
4010 toolbox_add_tool(window->vt, &ruler_tool, TOOL_LAYER_TYPE_NONE);
4011 toolbox_add_tool(window->vt, &zoom_tool, TOOL_LAYER_TYPE_NONE);
4012 toolbox_add_tool(window->vt, &pan_tool, TOOL_LAYER_TYPE_NONE);
4013 toolbox_add_tool(window->vt, &select_tool, TOOL_LAYER_TYPE_NONE);
4016 if (!(mid = gtk_ui_manager_add_ui_from_string (uim, menu_xml, -1, &error))) {
4017 g_error_free (error);
4021 action_group = gtk_action_group_new ("MenuActions");
4022 gtk_action_group_set_translation_domain(action_group, PACKAGE_NAME);
4023 gtk_action_group_add_actions (action_group, entries, G_N_ELEMENTS (entries), window);
4024 gtk_action_group_add_toggle_actions (action_group, toggle_entries, G_N_ELEMENTS (toggle_entries), window);
4025 gtk_action_group_add_radio_actions (action_group, mode_entries, G_N_ELEMENTS (mode_entries), 4, (GCallback)window_change_coord_mode_cb, window);
4027 // Use this to see if GPSBabel is available:
4028 if ( a_babel_available () ) {
4029 // If going to add more entries then might be worth creating a menu_gpsbabel.xml.h file
4030 if ( gtk_ui_manager_add_ui_from_string ( uim,
4031 "<ui><menubar name='MainMenu'><menu action='File'><menu action='Export'><menuitem action='ExportKML'/></menu></menu></menubar></ui>",
4033 gtk_action_group_add_actions ( action_group, entries_gpsbabel, G_N_ELEMENTS (entries_gpsbabel), window );
4036 icon_factory = gtk_icon_factory_new ();
4037 gtk_icon_factory_add_default (icon_factory);
4039 register_vik_icons(icon_factory);
4041 // Copy the tool RadioActionEntries out of the main Window structure into an extending array 'tools'
4042 // so that it can be applied to the UI in one action group add function call below
4044 for (i=0; i<window->vt->n_tools; i++) {
4045 tools = g_renew(GtkRadioActionEntry, tools, ntools+1);
4046 radio = &tools[ntools];
4048 *radio = window->vt->tools[i].ti.radioActionEntry;
4049 radio->value = ntools;
4052 for (i=0; i<VIK_LAYER_NUM_TYPES; i++) {
4053 GtkActionEntry action;
4054 gtk_ui_manager_add_ui(uim, mid, "/ui/MainMenu/Layers/",
4055 vik_layer_get_interface(i)->name,
4056 vik_layer_get_interface(i)->name,
4057 GTK_UI_MANAGER_MENUITEM, FALSE);
4059 icon_set = gtk_icon_set_new_from_pixbuf (gdk_pixbuf_from_pixdata (vik_layer_get_interface(i)->icon, FALSE, NULL ));
4060 gtk_icon_factory_add (icon_factory, vik_layer_get_interface(i)->name, icon_set);
4061 gtk_icon_set_unref (icon_set);
4063 action.name = vik_layer_get_interface(i)->name;
4064 action.stock_id = vik_layer_get_interface(i)->name;
4065 action.label = g_strdup_printf( _("New _%s Layer"), vik_layer_get_interface(i)->name);
4066 action.accelerator = vik_layer_get_interface(i)->accelerator;
4067 action.tooltip = NULL;
4068 action.callback = (GCallback)menu_addlayer_cb;
4069 gtk_action_group_add_actions(action_group, &action, 1, window);
4071 g_free ( (gchar*)action.label );
4073 if ( vik_layer_get_interface(i)->tools_count ) {
4074 gtk_ui_manager_add_ui(uim, mid, "/ui/MainMenu/Tools/", vik_layer_get_interface(i)->name, NULL, GTK_UI_MANAGER_SEPARATOR, FALSE);
4075 gtk_ui_manager_add_ui(uim, mid, "/ui/MainToolbar/ToolItems/", vik_layer_get_interface(i)->name, NULL, GTK_UI_MANAGER_SEPARATOR, FALSE);
4078 // Further tool copying for to apply to the UI, also apply menu UI setup
4079 for ( j = 0; j < vik_layer_get_interface(i)->tools_count; j++ ) {
4080 tools = g_renew(GtkRadioActionEntry, tools, ntools+1);
4081 radio = &tools[ntools];
4084 gtk_ui_manager_add_ui(uim, mid, "/ui/MainMenu/Tools",
4085 vik_layer_get_interface(i)->tools[j].radioActionEntry.label,
4086 vik_layer_get_interface(i)->tools[j].radioActionEntry.name,
4087 GTK_UI_MANAGER_MENUITEM, FALSE);
4088 gtk_ui_manager_add_ui(uim, mid, "/ui/MainToolbar/ToolItems",
4089 vik_layer_get_interface(i)->tools[j].radioActionEntry.label,
4090 vik_layer_get_interface(i)->tools[j].radioActionEntry.name,
4091 GTK_UI_MANAGER_TOOLITEM, FALSE);
4093 toolbox_add_tool(window->vt, &(vik_layer_get_interface(i)->tools[j]), i);
4095 *radio = vik_layer_get_interface(i)->tools[j].radioActionEntry;
4096 // Overwrite with actual number to use
4097 radio->value = ntools;
4100 GtkActionEntry action_dl;
4101 gchar *layername = g_strdup_printf ( "Layer%s", vik_layer_get_interface(i)->fixed_layer_name );
4102 gtk_ui_manager_add_ui(uim, mid, "/ui/MainMenu/Edit/LayerDefaults",
4103 vik_layer_get_interface(i)->name,
4105 GTK_UI_MANAGER_MENUITEM, FALSE);
4108 // For default layers use action names of the form 'Layer<LayerName>'
4109 // This is to avoid clashing with just the layer name used above for the tool actions
4110 action_dl.name = g_strconcat("Layer", vik_layer_get_interface(i)->fixed_layer_name, NULL);
4111 action_dl.stock_id = NULL;
4112 action_dl.label = g_strconcat("_", vik_layer_get_interface(i)->name, "...", NULL); // Prepend marker for keyboard accelerator
4113 action_dl.accelerator = NULL;
4114 action_dl.tooltip = NULL;
4115 action_dl.callback = (GCallback)layer_defaults_cb;
4116 gtk_action_group_add_actions(action_group, &action_dl, 1, window);
4117 g_free ( (gchar*)action_dl.name );
4118 g_free ( (gchar*)action_dl.label );
4120 g_object_unref (icon_factory);
4122 gtk_action_group_add_radio_actions(action_group, tools, ntools, 0, (GCallback)menu_tool_cb, window);
4125 gtk_ui_manager_insert_action_group (uim, action_group, 0);
4127 for (i=0; i<VIK_LAYER_NUM_TYPES; i++) {
4128 for ( j = 0; j < vik_layer_get_interface(i)->tools_count; j++ ) {
4129 GtkAction *action = gtk_action_group_get_action(action_group,
4130 vik_layer_get_interface(i)->tools[j].radioActionEntry.name);
4131 g_object_set(action, "sensitive", FALSE, NULL);
4135 // This is done last so we don't need to track the value of mid anymore
4136 vik_ext_tools_add_action_items ( window, window->uim, action_group, mid );
4138 window->action_group = action_group;
4140 accel_group = gtk_ui_manager_get_accel_group (uim);
4141 gtk_window_add_accel_group (GTK_WINDOW (window), accel_group);
4142 gtk_ui_manager_ensure_update (uim);
4144 setup_recent_files(window);
4148 // TODO - add method to add tool icons defined from outside this file
4149 // and remove the reverse dependency on icon definition from this file
4151 const GdkPixdata *data;
4154 { &mover_22_pixbuf, "vik-icon-pan" },
4155 { &zoom_18_pixbuf, "vik-icon-zoom" },
4156 { &ruler_18_pixbuf, "vik-icon-ruler" },
4157 { &select_18_pixbuf, "vik-icon-select" },
4158 { &vik_new_route_18_pixbuf, "vik-icon-Create Route" },
4159 { &route_finder_18_pixbuf, "vik-icon-Route Finder" },
4160 { &demdl_18_pixbuf, "vik-icon-DEM Download" },
4161 { &showpic_18_pixbuf, "vik-icon-Show Picture" },
4162 { &addtr_18_pixbuf, "vik-icon-Create Track" },
4163 { &edtr_18_pixbuf, "vik-icon-Edit Trackpoint" },
4164 { &addwp_18_pixbuf, "vik-icon-Create Waypoint" },
4165 { &edwp_18_pixbuf, "vik-icon-Edit Waypoint" },
4166 { &geozoom_18_pixbuf, "vik-icon-Georef Zoom Tool" },
4167 { &geomove_18_pixbuf, "vik-icon-Georef Move Map" },
4168 { &mapdl_18_pixbuf, "vik-icon-Maps Download" },
4171 static gint n_stock_icons = G_N_ELEMENTS (stock_icons);
4174 register_vik_icons (GtkIconFactory *icon_factory)
4176 GtkIconSet *icon_set;
4179 for (i = 0; i < n_stock_icons; i++) {
4180 icon_set = gtk_icon_set_new_from_pixbuf (gdk_pixbuf_from_pixdata (
4181 stock_icons[i].data, FALSE, NULL ));
4182 gtk_icon_factory_add (icon_factory, stock_icons[i].stock_id, icon_set);
4183 gtk_icon_set_unref (icon_set);
4187 gpointer vik_window_get_selected_trw_layer ( VikWindow *vw )
4189 return vw->selected_vtl;
4192 void vik_window_set_selected_trw_layer ( VikWindow *vw, gpointer vtl )
4194 vw->selected_vtl = vtl;
4195 vw->containing_vtl = vtl;
4197 vw->selected_track = NULL;
4198 vw->selected_tracks = NULL;
4199 vw->selected_waypoint = NULL;
4200 vw->selected_waypoints = NULL;
4201 // Set highlight thickness
4202 vik_viewport_set_highlight_thickness ( vw->viking_vvp, vik_trw_layer_get_property_tracks_line_thickness (vw->containing_vtl) );
4205 GHashTable *vik_window_get_selected_tracks ( VikWindow *vw )
4207 return vw->selected_tracks;
4210 void vik_window_set_selected_tracks ( VikWindow *vw, GHashTable *ght, gpointer vtl )
4212 vw->selected_tracks = ght;
4213 vw->containing_vtl = vtl;
4215 vw->selected_vtl = NULL;
4216 vw->selected_track = NULL;
4217 vw->selected_waypoint = NULL;
4218 vw->selected_waypoints = NULL;
4219 // Set highlight thickness
4220 vik_viewport_set_highlight_thickness ( vw->viking_vvp, vik_trw_layer_get_property_tracks_line_thickness (vw->containing_vtl) );
4223 gpointer vik_window_get_selected_track ( VikWindow *vw )
4225 return vw->selected_track;
4228 void vik_window_set_selected_track ( VikWindow *vw, gpointer *vt, gpointer vtl )
4230 vw->selected_track = vt;
4231 vw->containing_vtl = vtl;
4233 vw->selected_vtl = NULL;
4234 vw->selected_tracks = NULL;
4235 vw->selected_waypoint = NULL;
4236 vw->selected_waypoints = NULL;
4237 // Set highlight thickness
4238 vik_viewport_set_highlight_thickness ( vw->viking_vvp, vik_trw_layer_get_property_tracks_line_thickness (vw->containing_vtl) );
4241 GHashTable *vik_window_get_selected_waypoints ( VikWindow *vw )
4243 return vw->selected_waypoints;
4246 void vik_window_set_selected_waypoints ( VikWindow *vw, GHashTable *ght, gpointer vtl )
4248 vw->selected_waypoints = ght;
4249 vw->containing_vtl = vtl;
4251 vw->selected_vtl = NULL;
4252 vw->selected_track = NULL;
4253 vw->selected_tracks = NULL;
4254 vw->selected_waypoint = NULL;
4257 gpointer vik_window_get_selected_waypoint ( VikWindow *vw )
4259 return vw->selected_waypoint;
4262 void vik_window_set_selected_waypoint ( VikWindow *vw, gpointer *vwp, gpointer vtl )
4264 vw->selected_waypoint = vwp;
4265 vw->containing_vtl = vtl;
4267 vw->selected_vtl = NULL;
4268 vw->selected_track = NULL;
4269 vw->selected_tracks = NULL;
4270 vw->selected_waypoints = NULL;
4273 gboolean vik_window_clear_highlight ( VikWindow *vw )
4275 gboolean need_redraw = FALSE;
4276 if ( vw->selected_vtl != NULL ) {
4277 vw->selected_vtl = NULL;
4280 if ( vw->selected_track != NULL ) {
4281 vw->selected_track = NULL;
4284 if ( vw->selected_tracks != NULL ) {
4285 vw->selected_tracks = NULL;
4288 if ( vw->selected_waypoint != NULL ) {
4289 vw->selected_waypoint = NULL;
4292 if ( vw->selected_waypoints != NULL ) {
4293 vw->selected_waypoints = NULL;
4299 GThread *vik_window_get_thread ( VikWindow *vw )