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 = { 0 }; // = G_VALUE_INIT; // GLIB 2.30+ only
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,
1659 /*** end ruler code ********************************************************/
1663 /********************************************************************************
1665 ********************************************************************************/
1670 // Track zoom bounds for zoom tool with shift modifier:
1671 gboolean bounds_active;
1674 } zoom_tool_state_t;
1677 * In case the screen size has changed
1679 static void zoomtool_resize_pixmap (zoom_tool_state_t *zts)
1683 // Allocate a drawing area the size of the viewport
1684 w1 = vik_viewport_get_width ( zts->vw->viking_vvp );
1685 h1 = vik_viewport_get_height ( zts->vw->viking_vvp );
1687 if ( !zts->pixmap ) {
1689 zts->pixmap = gdk_pixmap_new ( gtk_widget_get_window(GTK_WIDGET(zts->vw->viking_vvp)), w1, h1, -1 );
1692 gdk_drawable_get_size ( zts->pixmap, &w2, &h2 );
1694 if ( w1 != w2 || h1 != h2 ) {
1695 // Has changed - delete and recreate with new values
1696 g_object_unref ( G_OBJECT ( zts->pixmap ) );
1697 zts->pixmap = gdk_pixmap_new ( gtk_widget_get_window(GTK_WIDGET(zts->vw->viking_vvp)), w1, h1, -1 );
1701 static gpointer zoomtool_create (VikWindow *vw, VikViewport *vvp)
1703 zoom_tool_state_t *zts = g_new(zoom_tool_state_t, 1);
1708 zts->bounds_active = FALSE;
1712 static void zoomtool_destroy ( zoom_tool_state_t *zts)
1715 g_object_unref ( G_OBJECT ( zts->pixmap ) );
1719 static VikLayerToolFuncStatus zoomtool_click (VikLayer *vl, GdkEventButton *event, zoom_tool_state_t *zts)
1721 zts->vw->modified = TRUE;
1722 guint modifiers = event->state & (GDK_SHIFT_MASK | GDK_CONTROL_MASK);
1726 gint center_x = vik_viewport_get_width ( zts->vw->viking_vvp ) / 2;
1727 gint center_y = vik_viewport_get_height ( zts->vw->viking_vvp ) / 2;
1729 gboolean skip_update = FALSE;
1731 zts->bounds_active = FALSE;
1733 if ( modifiers == (GDK_CONTROL_MASK | GDK_SHIFT_MASK) ) {
1734 // This zoom is on the center position
1735 vik_viewport_set_center_screen ( zts->vw->viking_vvp, center_x, center_y );
1736 if ( event->button == 1 )
1737 vik_viewport_zoom_in (zts->vw->viking_vvp);
1738 else if ( event->button == 3 )
1739 vik_viewport_zoom_out (zts->vw->viking_vvp);
1741 else if ( modifiers == GDK_CONTROL_MASK ) {
1742 // This zoom is to recenter on the mouse position
1743 vik_viewport_set_center_screen ( zts->vw->viking_vvp, (gint) event->x, (gint) event->y );
1744 if ( event->button == 1 )
1745 vik_viewport_zoom_in (zts->vw->viking_vvp);
1746 else if ( event->button == 3 )
1747 vik_viewport_zoom_out (zts->vw->viking_vvp);
1749 else if ( modifiers == GDK_SHIFT_MASK ) {
1750 // Get start of new zoom bounds
1751 if ( event->button == 1 ) {
1752 zts->bounds_active = TRUE;
1753 zts->start_x = (gint) event->x;
1754 zts->start_y = (gint) event->y;
1759 /* make sure mouse is still over the same point on the map when we zoom */
1760 vik_viewport_screen_to_coord ( zts->vw->viking_vvp, event->x, event->y, &coord );
1761 if ( event->button == 1 )
1762 vik_viewport_zoom_in (zts->vw->viking_vvp);
1763 else if ( event->button == 3 )
1764 vik_viewport_zoom_out(zts->vw->viking_vvp);
1765 vik_viewport_coord_to_screen ( zts->vw->viking_vvp, &coord, &x, &y );
1766 vik_viewport_set_center_screen ( zts->vw->viking_vvp,
1767 center_x + (x - event->x),
1768 center_y + (y - event->y) );
1772 draw_update ( zts->vw );
1774 return VIK_LAYER_TOOL_ACK;
1777 static VikLayerToolFuncStatus zoomtool_move (VikLayer *vl, GdkEventMotion *event, zoom_tool_state_t *zts)
1779 guint modifiers = event->state & (GDK_SHIFT_MASK | GDK_CONTROL_MASK);
1781 if ( zts->bounds_active && modifiers == GDK_SHIFT_MASK ) {
1782 zoomtool_resize_pixmap ( zts );
1784 // Blank out currently drawn area
1785 gdk_draw_drawable ( zts->pixmap,
1786 gtk_widget_get_style(GTK_WIDGET(zts->vw->viking_vvp))->black_gc,
1787 vik_viewport_get_pixmap(zts->vw->viking_vvp),
1788 0, 0, 0, 0, -1, -1);
1790 // Calculate new box starting point & size in pixels
1791 int xx, yy, width, height;
1792 if ( event->y > zts->start_y ) {
1794 height = event->y-zts->start_y;
1798 height = zts->start_y-event->y;
1800 if ( event->x > zts->start_x ) {
1802 width = event->x-zts->start_x;
1806 width = zts->start_x-event->x;
1810 gdk_draw_rectangle (zts->pixmap, gtk_widget_get_style(GTK_WIDGET(zts->vw->viking_vvp))->black_gc, FALSE, xx, yy, width, height);
1812 // Only actually draw when there's time to do so
1813 if (draw_buf_done) {
1814 static gpointer pass_along[3];
1815 pass_along[0] = gtk_widget_get_window(GTK_WIDGET(zts->vw->viking_vvp));
1816 pass_along[1] = gtk_widget_get_style(GTK_WIDGET(zts->vw->viking_vvp))->black_gc;
1817 pass_along[2] = zts->pixmap;
1818 g_idle_add_full (G_PRIORITY_HIGH_IDLE + 10, draw_buf, pass_along, NULL);
1819 draw_buf_done = FALSE;
1823 zts->bounds_active = FALSE;
1825 return VIK_LAYER_TOOL_ACK;
1828 static VikLayerToolFuncStatus zoomtool_release (VikLayer *vl, GdkEventButton *event, zoom_tool_state_t *zts)
1830 guint modifiers = event->state & (GDK_SHIFT_MASK | GDK_CONTROL_MASK);
1832 // Ensure haven't just released on the exact same position
1833 // i.e. probably haven't moved the mouse at all
1834 if ( zts->bounds_active && modifiers == GDK_SHIFT_MASK &&
1835 ( event->x < zts->start_x-5 || event->x > zts->start_x+5 ) &&
1836 ( event->y < zts->start_y-5 || event->y > zts->start_y+5 ) ) {
1838 VikCoord coord1, coord2;
1839 vik_viewport_screen_to_coord ( zts->vw->viking_vvp, zts->start_x, zts->start_y, &coord1);
1840 vik_viewport_screen_to_coord ( zts->vw->viking_vvp, event->x, event->y, &coord2);
1842 // From the extend of the bounds pick the best zoom level
1843 // c.f. trw_layer_zoom_to_show_latlons()
1844 // Maybe refactor...
1845 struct LatLon ll1, ll2;
1846 vik_coord_to_latlon(&coord1, &ll1);
1847 vik_coord_to_latlon(&coord2, &ll2);
1848 struct LatLon average = { (ll1.lat+ll2.lat)/2,
1849 (ll1.lon+ll2.lon)/2 };
1851 VikCoord new_center;
1852 vik_coord_load_from_latlon ( &new_center, vik_viewport_get_coord_mode ( zts->vw->viking_vvp ), &average );
1853 vik_viewport_set_center_coord ( zts->vw->viking_vvp, &new_center, FALSE );
1855 /* Convert into definite 'smallest' and 'largest' positions */
1856 struct LatLon minmin;
1857 if ( ll1.lat < ll2.lat )
1858 minmin.lat = ll1.lat;
1860 minmin.lat = ll2.lat;
1862 struct LatLon maxmax;
1863 if ( ll1.lon > ll2.lon )
1864 maxmax.lon = ll1.lon;
1866 maxmax.lon = ll2.lon;
1868 /* Always recalculate the 'best' zoom level */
1869 gdouble zoom = VIK_VIEWPORT_MIN_ZOOM;
1870 vik_viewport_set_zoom ( zts->vw->viking_vvp, zoom );
1872 gdouble min_lat, max_lat, min_lon, max_lon;
1873 /* Should only be a maximum of about 18 iterations from min to max zoom levels */
1874 while ( zoom <= VIK_VIEWPORT_MAX_ZOOM ) {
1875 vik_viewport_get_min_max_lat_lon ( zts->vw->viking_vvp, &min_lat, &max_lat, &min_lon, &max_lon );
1876 /* NB I think the logic used in this test to determine if the bounds is within view
1877 fails if track goes across 180 degrees longitude.
1878 Hopefully that situation is not too common...
1879 Mind you viking doesn't really do edge locations to well anyway */
1880 if ( min_lat < minmin.lat &&
1881 max_lat > minmin.lat &&
1882 min_lon < maxmax.lon &&
1883 max_lon > maxmax.lon )
1884 /* Found within zoom level */
1889 vik_viewport_set_zoom ( zts->vw->viking_vvp, zoom );
1893 // When pressing shift and clicking for zoom, then jump three levels
1894 if ( modifiers == GDK_SHIFT_MASK ) {
1895 // Zoom in/out by three if possible
1896 vik_viewport_set_center_screen ( zts->vw->viking_vvp, event->x, event->y );
1897 if ( event->button == 1 ) {
1898 vik_viewport_zoom_in ( zts->vw->viking_vvp );
1899 vik_viewport_zoom_in ( zts->vw->viking_vvp );
1900 vik_viewport_zoom_in ( zts->vw->viking_vvp );
1902 else if ( event->button == 3 ) {
1903 vik_viewport_zoom_out ( zts->vw->viking_vvp );
1904 vik_viewport_zoom_out ( zts->vw->viking_vvp );
1905 vik_viewport_zoom_out ( zts->vw->viking_vvp );
1910 draw_update ( zts->vw );
1913 zts->bounds_active = FALSE;
1915 return VIK_LAYER_TOOL_ACK;
1918 static VikToolInterface zoom_tool =
1919 { { "Zoom", "vik-icon-zoom", N_("_Zoom"), "<control><shift>Z", N_("Zoom Tool"), 1 },
1920 (VikToolConstructorFunc) zoomtool_create,
1921 (VikToolDestructorFunc) zoomtool_destroy,
1922 (VikToolActivationFunc) NULL,
1923 (VikToolActivationFunc) NULL,
1924 (VikToolMouseFunc) zoomtool_click,
1925 (VikToolMouseMoveFunc) zoomtool_move,
1926 (VikToolMouseFunc) zoomtool_release,
1929 GDK_CURSOR_IS_PIXMAP,
1930 &cursor_zoom_pixbuf,
1932 /*** end zoom code ********************************************************/
1934 /********************************************************************************
1936 ********************************************************************************/
1937 static gpointer pantool_create (VikWindow *vw, VikViewport *vvp)
1942 // NB Double clicking means this gets called THREE times!!!
1943 static VikLayerToolFuncStatus pantool_click (VikLayer *vl, GdkEventButton *event, VikWindow *vw)
1945 vw->modified = TRUE;
1947 if ( event->type == GDK_2BUTTON_PRESS ) {
1948 // Zoom in / out on double click
1949 // No need to change the center as that has already occurred in the first click of a double click occurrence
1950 if ( event->button == 1 ) {
1951 guint modifier = event->state & GDK_SHIFT_MASK;
1953 vik_viewport_zoom_out ( vw->viking_vvp );
1955 vik_viewport_zoom_in ( vw->viking_vvp );
1957 else if ( event->button == 3 )
1958 vik_viewport_zoom_out ( vw->viking_vvp );
1963 // Standard pan click
1964 if ( event->button == 1 )
1965 vik_window_pan_click ( vw, event );
1967 return VIK_LAYER_TOOL_ACK;
1970 static VikLayerToolFuncStatus pantool_move (VikLayer *vl, GdkEventMotion *event, VikWindow *vw)
1972 vik_window_pan_move ( vw, event );
1973 return VIK_LAYER_TOOL_ACK;
1976 static VikLayerToolFuncStatus pantool_release (VikLayer *vl, GdkEventButton *event, VikWindow *vw)
1978 if ( event->button == 1 )
1979 vik_window_pan_release ( vw, event );
1980 return VIK_LAYER_TOOL_ACK;
1983 static VikToolInterface pan_tool =
1984 { { "Pan", "vik-icon-pan", N_("_Pan"), "<control><shift>P", N_("Pan Tool"), 0 },
1985 (VikToolConstructorFunc) pantool_create,
1986 (VikToolDestructorFunc) NULL,
1987 (VikToolActivationFunc) NULL,
1988 (VikToolActivationFunc) NULL,
1989 (VikToolMouseFunc) pantool_click,
1990 (VikToolMouseMoveFunc) pantool_move,
1991 (VikToolMouseFunc) pantool_release,
1997 /*** end pan code ********************************************************/
1999 /********************************************************************************
2001 ********************************************************************************/
2002 static gpointer selecttool_create (VikWindow *vw, VikViewport *vvp)
2004 tool_ed_t *t = g_new(tool_ed_t, 1);
2008 t->is_waypoint = FALSE;
2012 static void selecttool_destroy (tool_ed_t *t)
2020 GdkEventButton *event;
2021 tool_ed_t *tool_edit;
2024 static void click_layer_selected (VikLayer *vl, clicker *ck)
2026 /* Do nothing when function call returns true; */
2027 /* i.e. stop on first found item */
2030 if ( vik_layer_get_interface(vl->type)->select_click )
2031 ck->cont = !vik_layer_get_interface(vl->type)->select_click ( vl, ck->event, ck->vvp, ck->tool_edit );
2034 static VikLayerToolFuncStatus selecttool_click (VikLayer *vl, GdkEventButton *event, tool_ed_t *t)
2036 /* Only allow selection on primary button */
2037 if ( event->button == 1 ) {
2038 /* Enable click to apply callback to potentially all track/waypoint layers */
2039 /* Useful as we can find things that aren't necessarily in the currently selected layer */
2040 GList* gl = vik_layers_panel_get_all_layers_of_type ( t->vw->viking_vlp, VIK_LAYER_TRW, FALSE ); // Don't get invisible layers
2043 ck.vvp = t->vw->viking_vvp;
2046 g_list_foreach ( gl, (GFunc) click_layer_selected, &ck );
2049 // If nothing found then deselect & redraw screen if necessary to remove the highlight
2052 VikTreeview *vtv = vik_layers_panel_get_treeview ( t->vw->viking_vlp );
2054 if ( vik_treeview_get_selected_iter ( vtv, &iter ) ) {
2055 // Only clear if selected thing is a TrackWaypoint layer or a sublayer
2056 gint type = vik_treeview_item_get_type ( vtv, &iter );
2057 if ( type == VIK_TREEVIEW_TYPE_SUBLAYER ||
2058 VIK_LAYER(vik_treeview_item_get_pointer ( vtv, &iter ))->type == VIK_LAYER_TRW ) {
2060 vik_treeview_item_unselect ( vtv, &iter );
2061 if ( vik_window_clear_highlight ( t->vw ) )
2062 draw_update ( t->vw );
2067 else if ( ( event->button == 3 ) && ( vl && ( vl->type == VIK_LAYER_TRW ) ) ) {
2069 /* Act on currently selected item to show menu */
2070 if ( t->vw->selected_track || t->vw->selected_waypoint )
2071 if ( vik_layer_get_interface(vl->type)->show_viewport_menu )
2072 vik_layer_get_interface(vl->type)->show_viewport_menu ( vl, event, t->vw->viking_vvp );
2075 return VIK_LAYER_TOOL_ACK;
2078 static VikLayerToolFuncStatus selecttool_move (VikLayer *vl, GdkEventButton *event, tool_ed_t *t)
2080 /* Only allow selection on primary button */
2081 if ( event->button == 1 ) {
2082 // Don't care about vl here
2084 if ( vik_layer_get_interface(VIK_LAYER_TRW)->select_move )
2085 vik_layer_get_interface(VIK_LAYER_TRW)->select_move ( vl, event, t->vvp, t );
2087 return VIK_LAYER_TOOL_ACK;
2090 static VikLayerToolFuncStatus selecttool_release (VikLayer *vl, GdkEventButton *event, tool_ed_t *t)
2092 /* Only allow selection on primary button */
2093 if ( event->button == 1 ) {
2094 // Don't care about vl here
2096 if ( vik_layer_get_interface(VIK_LAYER_TRW)->select_release )
2097 vik_layer_get_interface(VIK_LAYER_TRW)->select_release ( (VikLayer*)t->vtl, event, t->vvp, t );
2099 return VIK_LAYER_TOOL_ACK;
2102 static VikToolInterface select_tool =
2103 { { "Select", "vik-icon-select", N_("_Select"), "<control><shift>S", N_("Select Tool"), 3 },
2104 (VikToolConstructorFunc) selecttool_create,
2105 (VikToolDestructorFunc) selecttool_destroy,
2106 (VikToolActivationFunc) NULL,
2107 (VikToolActivationFunc) NULL,
2108 (VikToolMouseFunc) selecttool_click,
2109 (VikToolMouseMoveFunc) selecttool_move,
2110 (VikToolMouseFunc) selecttool_release,
2111 (VikToolKeyFunc) NULL,
2116 /*** end select tool code ********************************************************/
2118 static void draw_pan_cb ( GtkAction *a, VikWindow *vw )
2120 // Since the treeview cell editting intercepts standard keyboard handlers, it means we can receive events here
2121 // Thus if currently editting, ensure we don't move the viewport when Ctrl+<arrow> is received
2122 VikLayer *sel = vik_layers_panel_get_selected ( vw->viking_vlp );
2123 if ( sel && vik_treeview_get_editing ( sel->vt ) )
2126 if (!strcmp(gtk_action_get_name(a), "PanNorth")) {
2127 vik_viewport_set_center_screen ( vw->viking_vvp, vik_viewport_get_width(vw->viking_vvp)/2, 0 );
2128 } else if (!strcmp(gtk_action_get_name(a), "PanEast")) {
2129 vik_viewport_set_center_screen ( vw->viking_vvp, vik_viewport_get_width(vw->viking_vvp), vik_viewport_get_height(vw->viking_vvp)/2 );
2130 } else if (!strcmp(gtk_action_get_name(a), "PanSouth")) {
2131 vik_viewport_set_center_screen ( vw->viking_vvp, vik_viewport_get_width(vw->viking_vvp)/2, vik_viewport_get_height(vw->viking_vvp) );
2132 } else if (!strcmp(gtk_action_get_name(a), "PanWest")) {
2133 vik_viewport_set_center_screen ( vw->viking_vvp, 0, vik_viewport_get_height(vw->viking_vvp)/2 );
2138 static void full_screen_cb ( GtkAction *a, VikWindow *vw )
2140 GtkWidget *check_box = gtk_ui_manager_get_widget ( vw->uim, "/ui/MainMenu/View/FullScreen" );
2141 g_assert(check_box);
2142 gboolean state = gtk_check_menu_item_get_active ( GTK_CHECK_MENU_ITEM(check_box));
2144 gtk_window_fullscreen ( GTK_WINDOW(vw) );
2146 gtk_window_unfullscreen ( GTK_WINDOW(vw) );
2149 static void draw_zoom_cb ( GtkAction *a, VikWindow *vw )
2153 if (!strcmp(gtk_action_get_name(a), "ZoomIn")) {
2156 else if (!strcmp(gtk_action_get_name(a), "ZoomOut")) {
2159 else if (!strcmp(gtk_action_get_name(a), "Zoom0.25")) {
2162 else if (!strcmp(gtk_action_get_name(a), "Zoom0.5")) {
2166 gchar *s = (gchar *)gtk_action_get_name(a);
2172 case -3: vik_viewport_zoom_in ( vw->viking_vvp ); break;
2173 case -4: vik_viewport_zoom_out ( vw->viking_vvp ); break;
2174 case -1: vik_viewport_set_zoom ( vw->viking_vvp, 0.5 ); break;
2175 case -2: vik_viewport_set_zoom ( vw->viking_vvp, 0.25 ); break;
2176 default: vik_viewport_set_zoom ( vw->viking_vvp, what );
2181 static void draw_goto_cb ( GtkAction *a, VikWindow *vw )
2183 VikCoord new_center;
2185 if (!strcmp(gtk_action_get_name(a), "GotoLL")) {
2186 struct LatLon ll, llold;
2187 vik_coord_to_latlon ( vik_viewport_get_center ( vw->viking_vvp ), &llold );
2188 if ( a_dialog_goto_latlon ( GTK_WINDOW(vw), &ll, &llold ) )
2189 vik_coord_load_from_latlon ( &new_center, vik_viewport_get_coord_mode(vw->viking_vvp), &ll );
2193 else if (!strcmp(gtk_action_get_name(a), "GotoUTM")) {
2194 struct UTM utm, utmold;
2195 vik_coord_to_utm ( vik_viewport_get_center ( vw->viking_vvp ), &utmold );
2196 if ( a_dialog_goto_utm ( GTK_WINDOW(vw), &utm, &utmold ) )
2197 vik_coord_load_from_utm ( &new_center, vik_viewport_get_coord_mode(vw->viking_vvp), &utm );
2202 g_critical("Houston, we've had a problem.");
2206 vik_viewport_set_center_coord ( vw->viking_vvp, &new_center, TRUE );
2211 * center_changed_cb:
2213 static void center_changed_cb ( VikWindow *vw )
2215 // ATM Keep back always available, so when we pan - we can jump to the last requested position
2217 GtkAction* action_back = gtk_action_group_get_action ( vw->action_group, "GoBack" );
2218 if ( action_back ) {
2219 gtk_action_set_sensitive ( action_back, vik_viewport_back_available(vw->viking_vvp) );
2222 GtkAction* action_forward = gtk_action_group_get_action ( vw->action_group, "GoForward" );
2223 if ( action_forward ) {
2224 gtk_action_set_sensitive ( action_forward, vik_viewport_forward_available(vw->viking_vvp) );
2229 * draw_goto_back_and_forth:
2231 static void draw_goto_back_and_forth ( GtkAction *a, VikWindow *vw )
2233 gboolean changed = FALSE;
2234 if (!strcmp(gtk_action_get_name(a), "GoBack")) {
2235 changed = vik_viewport_go_back ( vw->viking_vvp );
2237 else if (!strcmp(gtk_action_get_name(a), "GoForward")) {
2238 changed = vik_viewport_go_forward ( vw->viking_vvp );
2244 // Recheck buttons sensitivities, as the center changed signal is not sent on back/forward changes
2245 // (otherwise we would get stuck in an infinite loop!)
2246 center_changed_cb ( vw );
2253 * Refresh maps displayed
2255 static void draw_refresh_cb ( GtkAction *a, VikWindow *vw )
2257 // Only get 'new' maps
2258 simple_map_update ( vw, TRUE );
2261 static void menu_addlayer_cb ( GtkAction *a, VikWindow *vw )
2263 VikLayerTypeEnum type;
2264 for ( type = 0; type < VIK_LAYER_NUM_TYPES; type++ ) {
2265 if (!strcmp(vik_layer_get_interface(type)->name, gtk_action_get_name(a))) {
2266 if ( vik_layers_panel_new_layer ( vw->viking_vlp, type ) ) {
2268 vw->modified = TRUE;
2274 static void menu_copy_layer_cb ( GtkAction *a, VikWindow *vw )
2276 a_clipboard_copy_selected ( vw->viking_vlp );
2279 static void menu_cut_layer_cb ( GtkAction *a, VikWindow *vw )
2281 vik_layers_panel_cut_selected ( vw->viking_vlp );
2282 vw->modified = TRUE;
2285 static void menu_paste_layer_cb ( GtkAction *a, VikWindow *vw )
2287 if ( vik_layers_panel_paste_selected ( vw->viking_vlp ) )
2289 vw->modified = TRUE;
2293 static void menu_properties_cb ( GtkAction *a, VikWindow *vw )
2295 if ( ! vik_layers_panel_properties ( vw->viking_vlp ) )
2296 a_dialog_info_msg ( GTK_WINDOW(vw), _("You must select a layer to show its properties.") );
2299 static void help_help_cb ( GtkAction *a, VikWindow *vw )
2302 ShellExecute(NULL, "open", ""PACKAGE".pdf", NULL, NULL, SW_SHOWNORMAL);
2305 uri = g_strdup_printf("ghelp:%s", PACKAGE);
2306 GError *error = NULL;
2307 gboolean show = gtk_show_uri (NULL, uri, GDK_CURRENT_TIME, &error);
2308 if ( !show && !error )
2309 // No error to show, so unlikely this will get called
2310 a_dialog_error_msg ( GTK_WINDOW(vw), _("The help system is not available.") );
2313 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 );
2314 g_error_free ( error );
2317 #endif /* WINDOWS */
2320 static void help_about_cb ( GtkAction *a, VikWindow *vw )
2322 a_dialog_about(GTK_WINDOW(vw));
2325 static void menu_delete_layer_cb ( GtkAction *a, VikWindow *vw )
2327 if ( vik_layers_panel_get_selected ( vw->viking_vlp ) )
2329 vik_layers_panel_delete_selected ( vw->viking_vlp );
2330 vw->modified = TRUE;
2333 a_dialog_info_msg ( GTK_WINDOW(vw), _("You must select a layer to delete.") );
2336 static void view_side_panel_cb ( GtkAction *a, VikWindow *vw )
2338 GtkWidget *check_box = gtk_ui_manager_get_widget ( vw->uim, "/ui/MainMenu/View/SetShow/ViewSidePanel" );
2339 g_assert(check_box);
2340 gboolean state = gtk_check_menu_item_get_active ( GTK_CHECK_MENU_ITEM(check_box));
2342 gtk_widget_show(GTK_WIDGET(vw->viking_vlp));
2344 gtk_widget_hide(GTK_WIDGET(vw->viking_vlp));
2347 static void view_statusbar_cb ( GtkAction *a, VikWindow *vw )
2349 GtkWidget *check_box = gtk_ui_manager_get_widget ( vw->uim, "/ui/MainMenu/View/SetShow/ViewStatusBar" );
2352 gboolean state = gtk_check_menu_item_get_active ( GTK_CHECK_MENU_ITEM(check_box) );
2354 gtk_widget_show ( GTK_WIDGET(vw->viking_vs) );
2356 gtk_widget_hide ( GTK_WIDGET(vw->viking_vs) );
2359 static void view_toolbar_cb ( GtkAction *a, VikWindow *vw )
2361 GtkWidget *check_box = gtk_ui_manager_get_widget ( vw->uim, "/ui/MainMenu/View/SetShow/ViewToolbar" );
2364 gboolean state = gtk_check_menu_item_get_active ( GTK_CHECK_MENU_ITEM(check_box) );
2366 gtk_widget_show ( GTK_WIDGET(vw->toolbar) );
2368 gtk_widget_hide ( GTK_WIDGET(vw->toolbar) );
2371 static void view_main_menu_cb ( GtkAction *a, VikWindow *vw )
2373 GtkWidget *check_box = gtk_ui_manager_get_widget ( vw->uim, "/ui/MainMenu/View/SetShow/ViewMainMenu" );
2376 gboolean state = gtk_check_menu_item_get_active ( GTK_CHECK_MENU_ITEM(check_box) );
2378 gtk_widget_hide ( gtk_ui_manager_get_widget ( vw->uim, "/ui/MainMenu" ) );
2380 gtk_widget_show ( gtk_ui_manager_get_widget ( vw->uim, "/ui/MainMenu" ) );
2383 /***************************************
2384 ** tool management routines
2386 ***************************************/
2388 static toolbox_tools_t* toolbox_create(VikWindow *vw)
2390 toolbox_tools_t *vt = g_new(toolbox_tools_t, 1);
2393 vt->active_tool = -1;
2398 static void toolbox_add_tool(toolbox_tools_t *vt, VikToolInterface *vti, gint layer_type )
2400 vt->tools = g_renew(toolbox_tool_t, vt->tools, vt->n_tools+1);
2401 vt->tools[vt->n_tools].ti = *vti;
2402 vt->tools[vt->n_tools].layer_type = layer_type;
2404 vt->tools[vt->n_tools].state = vti->create(vt->vw, vt->vw->viking_vvp);
2407 vt->tools[vt->n_tools].state = NULL;
2412 static int toolbox_get_tool(toolbox_tools_t *vt, const gchar *tool_name)
2415 for (i=0; i<vt->n_tools; i++) {
2416 if (!strcmp(tool_name, vt->tools[i].ti.radioActionEntry.name)) {
2423 static void toolbox_activate(toolbox_tools_t *vt, const gchar *tool_name)
2425 int tool = toolbox_get_tool(vt, tool_name);
2426 toolbox_tool_t *t = &vt->tools[tool];
2427 VikLayer *vl = vik_layers_panel_get_selected ( vt->vw->viking_vlp );
2429 if (tool == vt->n_tools) {
2430 g_critical("trying to activate a non-existent tool...");
2433 /* is the tool already active? */
2434 if (vt->active_tool == tool) {
2438 if (vt->active_tool != -1) {
2439 if (vt->tools[vt->active_tool].ti.deactivate) {
2440 vt->tools[vt->active_tool].ti.deactivate(NULL, vt->tools[vt->active_tool].state);
2443 if (t->ti.activate) {
2444 t->ti.activate(vl, t->state);
2446 vt->active_tool = tool;
2449 static const GdkCursor *toolbox_get_cursor(toolbox_tools_t *vt, const gchar *tool_name)
2451 int tool = toolbox_get_tool(vt, tool_name);
2452 toolbox_tool_t *t = &vt->tools[tool];
2453 if (t->ti.cursor == NULL) {
2454 if (t->ti.cursor_type == GDK_CURSOR_IS_PIXMAP && t->ti.cursor_data != NULL) {
2455 GError *cursor_load_err = NULL;
2456 GdkPixbuf *cursor_pixbuf = gdk_pixbuf_from_pixdata (t->ti.cursor_data, FALSE, &cursor_load_err);
2457 /* TODO: settable offeset */
2458 t->ti.cursor = gdk_cursor_new_from_pixbuf ( gdk_display_get_default(), cursor_pixbuf, 3, 3 );
2459 g_object_unref ( G_OBJECT(cursor_pixbuf) );
2461 t->ti.cursor = gdk_cursor_new ( t->ti.cursor_type );
2464 return t->ti.cursor;
2467 static void toolbox_click (toolbox_tools_t *vt, GdkEventButton *event)
2469 VikLayer *vl = vik_layers_panel_get_selected ( vt->vw->viking_vlp );
2470 if (vt->active_tool != -1 && vt->tools[vt->active_tool].ti.click) {
2471 gint ltype = vt->tools[vt->active_tool].layer_type;
2472 if ( ltype == TOOL_LAYER_TYPE_NONE || (vl && ltype == vl->type) )
2473 vt->tools[vt->active_tool].ti.click(vl, event, vt->tools[vt->active_tool].state);
2477 static void toolbox_move (toolbox_tools_t *vt, GdkEventMotion *event)
2479 VikLayer *vl = vik_layers_panel_get_selected ( vt->vw->viking_vlp );
2480 if (vt->active_tool != -1 && vt->tools[vt->active_tool].ti.move) {
2481 gint ltype = vt->tools[vt->active_tool].layer_type;
2482 if ( ltype == TOOL_LAYER_TYPE_NONE || (vl && ltype == vl->type) )
2483 if ( VIK_LAYER_TOOL_ACK_GRAB_FOCUS == vt->tools[vt->active_tool].ti.move(vl, event, vt->tools[vt->active_tool].state) )
2484 gtk_widget_grab_focus ( GTK_WIDGET(vt->vw->viking_vvp) );
2488 static void toolbox_release (toolbox_tools_t *vt, GdkEventButton *event)
2490 VikLayer *vl = vik_layers_panel_get_selected ( vt->vw->viking_vlp );
2491 if (vt->active_tool != -1 && vt->tools[vt->active_tool].ti.release ) {
2492 gint ltype = vt->tools[vt->active_tool].layer_type;
2493 if ( ltype == TOOL_LAYER_TYPE_NONE || (vl && ltype == vl->type) )
2494 vt->tools[vt->active_tool].ti.release(vl, event, vt->tools[vt->active_tool].state);
2497 /** End tool management ************************************/
2499 void vik_window_enable_layer_tool ( VikWindow *vw, gint layer_id, gint tool_id )
2501 gtk_action_activate ( gtk_action_group_get_action ( vw->action_group, vik_layer_get_interface(layer_id)->tools[tool_id].radioActionEntry.name ) );
2504 /* this function gets called whenever a toolbar tool is clicked */
2505 static void menu_tool_cb ( GtkAction *old, GtkAction *a, VikWindow *vw )
2507 /* White Magic, my friends ... White Magic... */
2509 toolbox_activate(vw->vt, gtk_action_get_name(a));
2511 vw->viewport_cursor = (GdkCursor *)toolbox_get_cursor(vw->vt, gtk_action_get_name(a));
2513 if ( gtk_widget_get_window(GTK_WIDGET(vw->viking_vvp)) )
2514 /* We set cursor, even if it is NULL: it resets to default */
2515 gdk_window_set_cursor ( gtk_widget_get_window(GTK_WIDGET(vw->viking_vvp)), vw->viewport_cursor );
2517 if (!strcmp(gtk_action_get_name(a), "Pan")) {
2518 vw->current_tool = TOOL_PAN;
2520 else if (!strcmp(gtk_action_get_name(a), "Zoom")) {
2521 vw->current_tool = TOOL_ZOOM;
2523 else if (!strcmp(gtk_action_get_name(a), "Ruler")) {
2524 vw->current_tool = TOOL_RULER;
2526 else if (!strcmp(gtk_action_get_name(a), "Select")) {
2527 vw->current_tool = TOOL_SELECT;
2530 VikLayerTypeEnum layer_id;
2531 for (layer_id=0; layer_id<VIK_LAYER_NUM_TYPES; layer_id++) {
2532 for ( tool_id = 0; tool_id < vik_layer_get_interface(layer_id)->tools_count; tool_id++ ) {
2533 if (!strcmp(vik_layer_get_interface(layer_id)->tools[tool_id].radioActionEntry.name, gtk_action_get_name(a))) {
2534 vw->current_tool = TOOL_LAYER;
2535 vw->tool_layer_id = layer_id;
2536 vw->tool_tool_id = tool_id;
2541 draw_status_tool ( vw );
2544 static void window_set_filename ( VikWindow *vw, const gchar *filename )
2549 g_free ( vw->filename );
2550 if ( filename == NULL )
2552 vw->filename = NULL;
2556 vw->filename = g_strdup(filename);
2559 /* Refresh window's title */
2560 file = window_get_filename ( vw );
2561 title = g_strdup_printf( "%s - Viking", file );
2562 gtk_window_set_title ( GTK_WINDOW(vw), title );
2566 static const gchar *window_get_filename ( VikWindow *vw )
2568 return vw->filename ? a_file_basename ( vw->filename ) : _("Untitled");
2571 GtkWidget *vik_window_get_drawmode_button ( VikWindow *vw, VikViewportDrawMode mode )
2573 GtkWidget *mode_button;
2576 #ifdef VIK_CONFIG_EXPEDIA
2577 case VIK_VIEWPORT_DRAWMODE_EXPEDIA: buttonname = "/ui/MainMenu/View/ModeExpedia"; break;
2579 case VIK_VIEWPORT_DRAWMODE_MERCATOR: buttonname = "/ui/MainMenu/View/ModeMercator"; break;
2580 case VIK_VIEWPORT_DRAWMODE_LATLON: buttonname = "/ui/MainMenu/View/ModeLatLon"; break;
2581 default: buttonname = "/ui/MainMenu/View/ModeUTM";
2583 mode_button = gtk_ui_manager_get_widget ( vw->uim, buttonname );
2584 g_assert ( mode_button );
2589 * vik_window_get_pan_move:
2590 * @vw: some VikWindow
2592 * Retrieves @vw's pan_move.
2594 * Should be removed as soon as possible.
2596 * Returns: @vw's pan_move
2600 gboolean vik_window_get_pan_move ( VikWindow *vw )
2602 return vw->pan_move;
2605 static void on_activate_recent_item (GtkRecentChooser *chooser,
2610 filename = gtk_recent_chooser_get_current_uri (chooser);
2611 if (filename != NULL)
2613 GFile *file = g_file_new_for_uri ( filename );
2614 gchar *path = g_file_get_path ( file );
2615 g_object_unref ( file );
2616 if ( self->filename )
2618 GSList *filenames = NULL;
2619 filenames = g_slist_append ( filenames, path );
2620 g_signal_emit ( G_OBJECT(self), window_signals[VW_OPENWINDOW_SIGNAL], 0, filenames );
2621 // NB: GSList & contents are freed by main.open_window
2624 vik_window_open_file ( self, path, TRUE );
2632 static void setup_recent_files (VikWindow *self)
2634 GtkRecentManager *manager;
2635 GtkRecentFilter *filter;
2636 GtkWidget *menu, *menu_item;
2638 filter = gtk_recent_filter_new ();
2639 /* gtk_recent_filter_add_application (filter, g_get_application_name()); */
2640 gtk_recent_filter_add_group(filter, "viking");
2642 manager = gtk_recent_manager_get_default ();
2643 menu = gtk_recent_chooser_menu_new_for_manager (manager);
2644 gtk_recent_chooser_set_sort_type (GTK_RECENT_CHOOSER (menu), GTK_RECENT_SORT_MRU);
2645 gtk_recent_chooser_add_filter (GTK_RECENT_CHOOSER (menu), filter);
2646 gtk_recent_chooser_set_limit (GTK_RECENT_CHOOSER (menu), a_vik_get_recent_number_files() );
2648 menu_item = gtk_ui_manager_get_widget (self->uim, "/ui/MainMenu/File/OpenRecentFile");
2649 gtk_menu_item_set_submenu (GTK_MENU_ITEM (menu_item), menu);
2651 g_signal_connect (G_OBJECT (menu), "item-activated",
2652 G_CALLBACK (on_activate_recent_item), (gpointer) self);
2655 static void update_recently_used_document(const gchar *filename)
2657 /* Update Recently Used Document framework */
2658 GtkRecentManager *manager = gtk_recent_manager_get_default();
2659 GtkRecentData *recent_data = g_slice_new (GtkRecentData);
2660 gchar *groups[] = {"viking", NULL};
2661 GFile *file = g_file_new_for_commandline_arg(filename);
2662 gchar *uri = g_file_get_uri(file);
2663 gchar *basename = g_path_get_basename(filename);
2664 g_object_unref(file);
2667 recent_data->display_name = basename;
2668 recent_data->description = NULL;
2669 recent_data->mime_type = "text/x-gps-data";
2670 recent_data->app_name = (gchar *) g_get_application_name ();
2671 recent_data->app_exec = g_strjoin (" ", g_get_prgname (), "%f", NULL);
2672 recent_data->groups = groups;
2673 recent_data->is_private = FALSE;
2674 if (!gtk_recent_manager_add_full (manager, uri, recent_data))
2676 g_warning (_("Unable to add '%s' to the list of recently used documents"), uri);
2681 g_free (recent_data->app_exec);
2682 g_slice_free (GtkRecentData, recent_data);
2686 * Call this before doing things that may take a long time and otherwise not show any other feedback
2687 * such as loading and saving files
2689 void vik_window_set_busy_cursor ( VikWindow *vw )
2691 gdk_window_set_cursor ( gtk_widget_get_window(GTK_WIDGET(vw)), vw->busy_cursor );
2692 // Viewport has a separate cursor
2693 gdk_window_set_cursor ( gtk_widget_get_window(GTK_WIDGET(vw->viking_vvp)), vw->busy_cursor );
2694 // Ensure cursor updated before doing stuff
2695 while( gtk_events_pending() )
2696 gtk_main_iteration();
2699 void vik_window_clear_busy_cursor ( VikWindow *vw )
2701 gdk_window_set_cursor ( gtk_widget_get_window(GTK_WIDGET(vw)), NULL );
2702 // Restore viewport cursor
2703 gdk_window_set_cursor ( gtk_widget_get_window(GTK_WIDGET(vw->viking_vvp)), vw->viewport_cursor );
2706 void vik_window_open_file ( VikWindow *vw, const gchar *filename, gboolean change_filename )
2708 vik_window_set_busy_cursor ( vw );
2710 // Enable the *new* filename to be accessible by the Layers codez
2711 gchar *original_filename = g_strdup ( vw->filename );
2712 g_free ( vw->filename );
2713 vw->filename = g_strdup ( filename );
2714 gboolean success = FALSE;
2715 gboolean restore_original_filename = FALSE;
2717 vw->loaded_type = a_file_load ( vik_layers_panel_get_top_layer(vw->viking_vlp), vw->viking_vvp, filename );
2718 switch ( vw->loaded_type )
2720 case LOAD_TYPE_READ_FAILURE:
2721 a_dialog_error_msg ( GTK_WINDOW(vw), _("The file you requested could not be opened.") );
2723 case LOAD_TYPE_GPSBABEL_FAILURE:
2724 a_dialog_error_msg ( GTK_WINDOW(vw), _("GPSBabel is required to load files of this type or GPSBabel encountered problems.") );
2726 case LOAD_TYPE_GPX_FAILURE:
2727 a_dialog_error_msg_extra ( GTK_WINDOW(vw), _("Unable to load malformed GPX file %s"), filename );
2729 case LOAD_TYPE_UNSUPPORTED_FAILURE:
2730 a_dialog_error_msg_extra ( GTK_WINDOW(vw), _("Unsupported file type for %s"), filename );
2732 case LOAD_TYPE_VIK_FAILURE_NON_FATAL:
2734 // Since we can process .vik files with issues just show a warning in the status bar
2735 // Not that a user can do much about it... or tells them what this issue is yet...
2736 gchar *msg = g_strdup_printf (_("WARNING: issues encountered loading %s"), a_file_basename (filename) );
2737 vik_statusbar_set_message ( vw->viking_vs, VIK_STATUSBAR_INFO, msg );
2740 // No break, carry on to show any data
2741 case LOAD_TYPE_VIK_SUCCESS:
2743 restore_original_filename = TRUE; // NB Will actually get inverted by the 'success' component below
2744 GtkWidget *mode_button;
2746 if ( change_filename )
2747 window_set_filename ( vw, filename );
2748 mode_button = vik_window_get_drawmode_button ( vw, vik_viewport_get_drawmode ( vw->viking_vvp ) );
2749 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. */
2750 gtk_check_menu_item_set_active ( GTK_CHECK_MENU_ITEM(mode_button), TRUE );
2751 vw->only_updating_coord_mode_ui = FALSE;
2753 vik_layers_panel_change_coord_mode ( vw->viking_vlp, vik_viewport_get_coord_mode ( vw->viking_vvp ) );
2755 mode_button = gtk_ui_manager_get_widget ( vw->uim, "/ui/MainMenu/View/SetShow/ShowScale" );
2756 g_assert ( mode_button );
2757 gtk_check_menu_item_set_active ( GTK_CHECK_MENU_ITEM(mode_button),vik_viewport_get_draw_scale(vw->viking_vvp) );
2759 mode_button = gtk_ui_manager_get_widget ( vw->uim, "/ui/MainMenu/View/SetShow/ShowCenterMark" );
2760 g_assert ( mode_button );
2761 gtk_check_menu_item_set_active ( GTK_CHECK_MENU_ITEM(mode_button),vik_viewport_get_draw_centermark(vw->viking_vvp) );
2763 mode_button = gtk_ui_manager_get_widget ( vw->uim, "/ui/MainMenu/View/SetShow/ShowHighlight" );
2764 g_assert ( mode_button );
2765 gtk_check_menu_item_set_active ( GTK_CHECK_MENU_ITEM(mode_button),vik_viewport_get_draw_highlight (vw->viking_vvp) );
2767 // NB No break, carry on to redraw
2768 //case LOAD_TYPE_OTHER_SUCCESS:
2771 // When LOAD_TYPE_OTHER_SUCCESS *only*, this will maintain the existing Viking project
2772 restore_original_filename = ! restore_original_filename;
2773 update_recently_used_document(filename);
2778 if ( ! success || restore_original_filename )
2779 // Load didn't work or want to keep as the existing Viking project, keep using the original name
2780 window_set_filename ( vw, original_filename );
2781 g_free ( original_filename );
2783 vik_window_clear_busy_cursor ( vw );
2786 static void load_file ( GtkAction *a, VikWindow *vw )
2788 GSList *files = NULL;
2789 GSList *cur_file = NULL;
2791 if (!strcmp(gtk_action_get_name(a), "Open")) {
2794 else if (!strcmp(gtk_action_get_name(a), "Append")) {
2798 g_critical("Houston, we've had a problem.");
2802 if ( ! vw->open_dia )
2804 vw->open_dia = gtk_file_chooser_dialog_new (_("Please select a GPS data file to open. "),
2806 GTK_FILE_CHOOSER_ACTION_OPEN,
2807 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
2808 GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT,
2810 gchar *cwd = g_get_current_dir();
2812 gtk_file_chooser_set_current_folder ( GTK_FILE_CHOOSER(vw->open_dia), cwd );
2816 GtkFileFilter *filter;
2817 // NB file filters are listed this way for alphabetical ordering
2818 #ifdef VIK_CONFIG_GEOCACHES
2819 filter = gtk_file_filter_new ();
2820 gtk_file_filter_set_name( filter, _("Geocaching") );
2821 gtk_file_filter_add_pattern ( filter, "*.loc" ); // No MIME type available
2822 gtk_file_chooser_add_filter (GTK_FILE_CHOOSER(vw->open_dia), filter);
2825 filter = gtk_file_filter_new ();
2826 gtk_file_filter_set_name( filter, _("Google Earth") );
2827 gtk_file_filter_add_mime_type ( filter, "application/vnd.google-earth.kml+xml");
2828 gtk_file_chooser_add_filter (GTK_FILE_CHOOSER(vw->open_dia), filter);
2830 filter = gtk_file_filter_new ();
2831 gtk_file_filter_set_name( filter, _("GPX") );
2832 gtk_file_filter_add_pattern ( filter, "*.gpx" ); // No MIME type available
2833 gtk_file_chooser_add_filter (GTK_FILE_CHOOSER(vw->open_dia), filter);
2835 filter = gtk_file_filter_new ();
2836 gtk_file_filter_set_name ( filter, _("JPG") );
2837 gtk_file_filter_add_mime_type ( filter, "image/jpeg");
2838 gtk_file_chooser_add_filter (GTK_FILE_CHOOSER(vw->open_dia), filter);
2840 filter = gtk_file_filter_new ();
2841 gtk_file_filter_set_name( filter, _("Viking") );
2842 gtk_file_filter_add_pattern ( filter, "*.vik" );
2843 gtk_file_filter_add_pattern ( filter, "*.viking" );
2844 gtk_file_chooser_add_filter (GTK_FILE_CHOOSER(vw->open_dia), filter);
2846 // NB could have filters for gpspoint (*.gps,*.gpsoint?) + gpsmapper (*.gsm,*.gpsmapper?)
2847 // However assume this are barely used and thus not worthy of inclusion
2848 // as they'll just make the options too many and have no clear file pattern
2849 // one can always use the all option
2850 filter = gtk_file_filter_new ();
2851 gtk_file_filter_set_name( filter, _("All") );
2852 gtk_file_filter_add_pattern ( filter, "*" );
2853 gtk_file_chooser_add_filter (GTK_FILE_CHOOSER(vw->open_dia), filter);
2854 // Default to any file - same as before open filters were added
2855 gtk_file_chooser_set_filter (GTK_FILE_CHOOSER(vw->open_dia), filter);
2857 gtk_file_chooser_set_select_multiple ( GTK_FILE_CHOOSER(vw->open_dia), TRUE );
2858 gtk_window_set_transient_for ( GTK_WINDOW(vw->open_dia), GTK_WINDOW(vw) );
2859 gtk_window_set_destroy_with_parent ( GTK_WINDOW(vw->open_dia), TRUE );
2861 if ( gtk_dialog_run ( GTK_DIALOG(vw->open_dia) ) == GTK_RESPONSE_ACCEPT )
2863 gtk_widget_hide ( vw->open_dia );
2864 #ifdef VIKING_PROMPT_IF_MODIFIED
2865 if ( (vw->modified || vw->filename) && newwindow )
2867 if ( vw->filename && newwindow )
2869 g_signal_emit ( G_OBJECT(vw), window_signals[VW_OPENWINDOW_SIGNAL], 0, gtk_file_chooser_get_filenames (GTK_FILE_CHOOSER(vw->open_dia) ) );
2871 files = gtk_file_chooser_get_filenames (GTK_FILE_CHOOSER(vw->open_dia) );
2872 gboolean change_fn = newwindow && (g_slist_length(files)==1); /* only change fn if one file */
2873 gboolean first_vik_file = TRUE;
2875 while ( cur_file ) {
2877 gchar *file_name = cur_file->data;
2878 if ( newwindow && check_file_magic_vik ( file_name ) ) {
2879 // Load first of many .vik files in current window
2880 if ( first_vik_file ) {
2881 vik_window_open_file ( vw, file_name, TRUE );
2882 first_vik_file = FALSE;
2885 // Load each subsequent .vik file in a separate window
2886 VikWindow *newvw = vik_window_new_window ();
2888 vik_window_open_file ( newvw, file_name, TRUE );
2893 vik_window_open_file ( vw, file_name, change_fn );
2896 cur_file = g_slist_next (cur_file);
2898 g_slist_free (files);
2902 gtk_widget_hide ( vw->open_dia );
2905 static gboolean save_file_as ( GtkAction *a, VikWindow *vw )
2907 gboolean rv = FALSE;
2909 if ( ! vw->save_dia )
2911 vw->save_dia = gtk_file_chooser_dialog_new (_("Save as Viking File."),
2913 GTK_FILE_CHOOSER_ACTION_SAVE,
2914 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
2915 GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT,
2917 gchar *cwd = g_get_current_dir();
2919 gtk_file_chooser_set_current_folder ( GTK_FILE_CHOOSER(vw->save_dia), cwd );
2923 GtkFileFilter *filter;
2924 filter = gtk_file_filter_new ();
2925 gtk_file_filter_set_name( filter, _("All") );
2926 gtk_file_filter_add_pattern ( filter, "*" );
2927 gtk_file_chooser_add_filter (GTK_FILE_CHOOSER(vw->save_dia), filter);
2929 filter = gtk_file_filter_new ();
2930 gtk_file_filter_set_name( filter, _("Viking") );
2931 gtk_file_filter_add_pattern ( filter, "*.vik" );
2932 gtk_file_filter_add_pattern ( filter, "*.viking" );
2933 gtk_file_chooser_add_filter (GTK_FILE_CHOOSER(vw->save_dia), filter);
2934 // Default to a Viking file
2935 gtk_file_chooser_set_filter (GTK_FILE_CHOOSER(vw->save_dia), filter);
2937 gtk_window_set_transient_for ( GTK_WINDOW(vw->save_dia), GTK_WINDOW(vw) );
2938 gtk_window_set_destroy_with_parent ( GTK_WINDOW(vw->save_dia), TRUE );
2940 // Auto append / replace extension with '.vik' to the suggested file name as it's going to be a Viking File
2941 gchar* auto_save_name = g_strdup ( window_get_filename ( vw ) );
2942 if ( ! a_file_check_ext ( auto_save_name, ".vik" ) )
2943 auto_save_name = g_strconcat ( auto_save_name, ".vik", NULL );
2945 gtk_file_chooser_set_current_name (GTK_FILE_CHOOSER(vw->save_dia), auto_save_name);
2947 while ( gtk_dialog_run ( GTK_DIALOG(vw->save_dia) ) == GTK_RESPONSE_ACCEPT )
2949 fn = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER(vw->save_dia) );
2950 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 ) ) )
2952 window_set_filename ( vw, fn );
2953 rv = window_save ( vw );
2954 vw->modified = FALSE;
2958 g_free ( auto_save_name );
2959 gtk_widget_hide ( vw->save_dia );
2963 static gboolean window_save ( VikWindow *vw )
2965 vik_window_set_busy_cursor ( vw );
2966 gboolean success = TRUE;
2968 if ( a_file_save ( vik_layers_panel_get_top_layer ( vw->viking_vlp ), vw->viking_vvp, vw->filename ) )
2970 update_recently_used_document ( vw->filename );
2974 a_dialog_error_msg ( GTK_WINDOW(vw), _("The filename you requested could not be opened for writing.") );
2977 vik_window_clear_busy_cursor ( vw );
2981 static gboolean save_file ( GtkAction *a, VikWindow *vw )
2983 if ( ! vw->filename )
2984 return save_file_as ( NULL, vw );
2987 vw->modified = FALSE;
2988 return window_save ( vw );
2995 * Export all TRW Layers in the list to individual files in the specified directory
2997 * Returns: %TRUE on success
2999 static gboolean export_to ( VikWindow *vw, GList *gl, VikFileType_t vft, const gchar *dir, const gchar *extension )
3001 gboolean success = TRUE;
3003 gint export_count = 0;
3005 vik_window_set_busy_cursor ( vw );
3009 gchar *fn = g_strconcat ( dir, G_DIR_SEPARATOR_S, VIK_LAYER(gl->data)->name, extension, NULL );
3011 // Some protection in attempting to write too many same named files
3012 // As this will get horribly slow...
3013 gboolean safe = FALSE;
3015 while ( ii < 5000 ) {
3016 if ( g_file_test ( fn, G_FILE_TEST_EXISTS ) ) {
3019 fn = g_strdup_printf ( "%s%s%s#%03d%s", dir, G_DIR_SEPARATOR_S, VIK_LAYER(gl->data)->name, ii, extension );
3030 // NB: We allow exporting empty layers
3032 gboolean this_success = a_file_export ( VIK_TRW_LAYER(gl->data), fn, vft, NULL, TRUE );
3034 // Show some progress
3035 if ( this_success ) {
3037 gchar *message = g_strdup_printf ( _("Exporting to file: %s"), fn );
3038 vik_statusbar_set_message ( vw->viking_vs, VIK_STATUSBAR_INFO, message );
3039 while ( gtk_events_pending() )
3040 gtk_main_iteration ();
3044 success = success && this_success;
3048 gl = g_list_next ( gl );
3051 vik_window_clear_busy_cursor ( vw );
3053 // Confirm what happened.
3054 gchar *message = g_strdup_printf ( _("Exported files: %d"), export_count );
3055 vik_statusbar_set_message ( vw->viking_vs, VIK_STATUSBAR_INFO, message );
3061 static void export_to_common ( VikWindow *vw, VikFileType_t vft, const gchar *extension )
3063 GList *gl = vik_layers_panel_get_all_layers_of_type ( vw->viking_vlp, VIK_LAYER_TRW, TRUE );
3066 a_dialog_info_msg ( GTK_WINDOW(vw), _("Nothing to Export!") );
3070 GtkWidget *dialog = gtk_file_chooser_dialog_new ( _("Export to directory"),
3072 GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER,
3074 GTK_RESPONSE_REJECT,
3076 GTK_RESPONSE_ACCEPT,
3078 gtk_window_set_transient_for ( GTK_WINDOW(dialog), GTK_WINDOW(vw) );
3079 gtk_window_set_destroy_with_parent ( GTK_WINDOW(dialog), TRUE );
3080 gtk_window_set_modal ( GTK_WINDOW(dialog), TRUE );
3082 gtk_widget_show_all ( dialog );
3084 if ( gtk_dialog_run ( GTK_DIALOG(dialog) ) == GTK_RESPONSE_ACCEPT ) {
3085 gchar *dir = gtk_file_chooser_get_filename ( GTK_FILE_CHOOSER(dialog) );
3086 gtk_widget_destroy ( dialog );
3088 if ( !export_to ( vw, gl, vft, dir, extension ) )
3089 a_dialog_error_msg ( GTK_WINDOW(vw),_("Could not convert all files") );
3094 gtk_widget_destroy ( dialog );
3099 static void export_to_gpx ( GtkAction *a, VikWindow *vw )
3101 export_to_common ( vw, FILE_TYPE_GPX, ".gpx" );
3104 static void export_to_kml ( GtkAction *a, VikWindow *vw )
3106 export_to_common ( vw, FILE_TYPE_KML, ".kml" );
3109 #if !GLIB_CHECK_VERSION(2,26,0)
3110 typedef struct stat GStatBuf;
3113 static void file_properties_cb ( GtkAction *a, VikWindow *vw )
3115 gchar *message = NULL;
3116 if ( vw->filename ) {
3117 if ( g_file_test ( vw->filename, G_FILE_TEST_EXISTS ) ) {
3118 // Get some timestamp information of the file
3120 if ( g_stat ( vw->filename, &stat_buf ) == 0 ) {
3122 strftime ( time_buf, sizeof(time_buf), "%c", gmtime((const time_t *)&stat_buf.st_mtime) );
3124 gint byte_size = stat_buf.st_size;
3125 // See http://en.wikipedia.org/wiki/Megabyte (and Kilobyte)
3126 // hence using 1000 rather than 1024
3127 // so get output as per 'ls' or the Gtk file open dialog
3128 if ( byte_size < 1000 )
3129 size = g_strdup_printf ( _("%d bytes"), byte_size );
3130 else if ( byte_size < 1000*1000 )
3131 size = g_strdup_printf ( _("%3.1f kB"), (gdouble)byte_size / 1000 );
3133 size = g_strdup_printf ( _("%3.1f MB"), (gdouble)byte_size / (1000*1000) );
3134 message = g_strdup_printf ( _("%s\n\n%s\n\n%s"), vw->filename, time_buf, size );
3139 message = g_strdup ( _("File not accessible") );
3142 message = g_strdup ( _("No Viking File") );
3145 a_dialog_info_msg ( GTK_WINDOW(vw), message );
3149 static void acquire_from_gps ( GtkAction *a, VikWindow *vw )
3151 // Via the file menu, acquiring from a GPS makes a new layer
3152 // this has always been the way (not entirely sure if this was the real intention!)
3153 // thus maintain the behaviour ATM.
3154 // Hence explicit setting here (as the value may be changed elsewhere)
3155 vik_datasource_gps_interface.mode = VIK_DATASOURCE_CREATENEWLAYER;
3156 a_acquire(vw, vw->viking_vlp, vw->viking_vvp, &vik_datasource_gps_interface, NULL, NULL );
3159 static void acquire_from_file ( GtkAction *a, VikWindow *vw )
3161 a_acquire(vw, vw->viking_vlp, vw->viking_vvp, &vik_datasource_file_interface, NULL, NULL );
3164 static void acquire_from_routing ( GtkAction *a, VikWindow *vw )
3166 a_acquire(vw, vw->viking_vlp, vw->viking_vvp, &vik_datasource_routing_interface, NULL, NULL );
3169 #ifdef VIK_CONFIG_OPENSTREETMAP
3170 static void acquire_from_osm ( GtkAction *a, VikWindow *vw )
3172 a_acquire(vw, vw->viking_vlp, vw->viking_vvp, &vik_datasource_osm_interface, NULL, NULL );
3175 static void acquire_from_my_osm ( GtkAction *a, VikWindow *vw )
3177 a_acquire(vw, vw->viking_vlp, vw->viking_vvp, &vik_datasource_osm_my_traces_interface, NULL, NULL );
3181 #ifdef VIK_CONFIG_GEOCACHES
3182 static void acquire_from_gc ( GtkAction *a, VikWindow *vw )
3184 a_acquire(vw, vw->viking_vlp, vw->viking_vvp, &vik_datasource_gc_interface, NULL, NULL );
3188 #ifdef VIK_CONFIG_GEOTAG
3189 static void acquire_from_geotag ( GtkAction *a, VikWindow *vw )
3191 vik_datasource_geotag_interface.mode = VIK_DATASOURCE_CREATENEWLAYER;
3192 a_acquire(vw, vw->viking_vlp, vw->viking_vvp, &vik_datasource_geotag_interface, NULL, NULL );
3196 #ifdef VIK_CONFIG_GEONAMES
3197 static void acquire_from_wikipedia ( GtkAction *a, VikWindow *vw )
3199 a_acquire(vw, vw->viking_vlp, vw->viking_vvp, &vik_datasource_wikipedia_interface, NULL, NULL );
3203 static void acquire_from_url ( GtkAction *a, VikWindow *vw )
3205 vik_datasource_url_interface.mode = VIK_DATASOURCE_CREATENEWLAYER;
3206 a_acquire(vw, vw->viking_vlp, vw->viking_vvp, &vik_datasource_url_interface, NULL, NULL );
3209 static void goto_default_location( GtkAction *a, VikWindow *vw)
3212 ll.lat = a_vik_get_default_lat();
3213 ll.lon = a_vik_get_default_long();
3214 vik_viewport_set_center_latlon(vw->viking_vvp, &ll, TRUE);
3215 vik_layers_panel_emit_update(vw->viking_vlp);
3219 static void goto_address( GtkAction *a, VikWindow *vw)
3221 a_vik_goto ( vw, vw->viking_vvp );
3222 vik_layers_panel_emit_update ( vw->viking_vlp );
3225 static void mapcache_flush_cb ( GtkAction *a, VikWindow *vw )
3230 static void layer_defaults_cb ( GtkAction *a, VikWindow *vw )
3232 gchar **texts = g_strsplit ( gtk_action_get_name(a), "Layer", 0 );
3235 return; // Internally broken :(
3237 if ( ! a_layer_defaults_show_window ( GTK_WINDOW(vw), texts[1] ) )
3238 a_dialog_info_msg ( GTK_WINDOW(vw), _("This layer has no configurable properties.") );
3239 // NB no update needed
3241 g_strfreev ( texts );
3244 static void preferences_change_update ( VikWindow *vw, gpointer data )
3246 // Want to update all TrackWaypoint layers
3247 GList *layers = vik_layers_panel_get_all_layers_of_type ( vw->viking_vlp, VIK_LAYER_TRW, TRUE );
3253 // Reset the individual waypoints themselves due to the preferences change
3254 VikTrwLayer *vtl = VIK_TRW_LAYER(layers->data);
3255 vik_trw_layer_reset_waypoints ( vtl );
3256 layers = g_list_next ( layers );
3259 g_list_free ( layers );
3264 static void preferences_cb ( GtkAction *a, VikWindow *vw )
3266 gboolean wp_icon_size = a_vik_get_use_large_waypoint_icons();
3268 a_preferences_show_window ( GTK_WINDOW(vw) );
3270 // Has the waypoint size setting changed?
3271 if (wp_icon_size != a_vik_get_use_large_waypoint_icons()) {
3272 // Delete icon indexing 'cache' and so automatically regenerates with the new setting when changed
3273 clear_garmin_icon_syms ();
3275 // Update all windows
3276 g_slist_foreach ( window_list, (GFunc) preferences_change_update, NULL );
3280 static void default_location_cb ( GtkAction *a, VikWindow *vw )
3282 /* Simplistic repeat of preference setting
3283 Only the name & type are important for setting the preference via this 'external' way */
3284 VikLayerParam pref_lat[] = {
3285 { VIK_LAYER_NUM_TYPES,
3286 VIKING_PREFERENCES_NAMESPACE "default_latitude",
3287 VIK_LAYER_PARAM_DOUBLE,
3290 VIK_LAYER_WIDGET_SPINBUTTON,
3299 VikLayerParam pref_lon[] = {
3300 { VIK_LAYER_NUM_TYPES,
3301 VIKING_PREFERENCES_NAMESPACE "default_longitude",
3302 VIK_LAYER_PARAM_DOUBLE,
3305 VIK_LAYER_WIDGET_SPINBUTTON,
3315 /* Get current center */
3317 vik_coord_to_latlon ( vik_viewport_get_center ( vw->viking_vvp ), &ll );
3319 /* Apply to preferences */
3320 VikLayerParamData vlp_data;
3321 vlp_data.d = ll.lat;
3322 a_preferences_run_setparam (vlp_data, pref_lat);
3323 vlp_data.d = ll.lon;
3324 a_preferences_run_setparam (vlp_data, pref_lon);
3325 /* Remember to save */
3326 a_preferences_save_to_file();
3329 static void clear_cb ( GtkAction *a, VikWindow *vw )
3331 vik_layers_panel_clear ( vw->viking_vlp );
3332 window_set_filename ( vw, NULL );
3336 static void window_close ( GtkAction *a, VikWindow *vw )
3338 if ( ! delete_event ( vw ) )
3339 gtk_widget_destroy ( GTK_WIDGET(vw) );
3342 static gboolean save_file_and_exit ( GtkAction *a, VikWindow *vw )
3344 if (save_file( NULL, vw)) {
3345 window_close( NULL, vw);
3352 static void zoom_to_cb ( GtkAction *a, VikWindow *vw )
3354 gdouble xmpp = vik_viewport_get_xmpp ( vw->viking_vvp ), ympp = vik_viewport_get_ympp ( vw->viking_vvp );
3355 if ( a_dialog_custom_zoom ( GTK_WINDOW(vw), &xmpp, &ympp ) )
3357 vik_viewport_set_xmpp ( vw->viking_vvp, xmpp );
3358 vik_viewport_set_ympp ( vw->viking_vvp, ympp );
3363 static void save_image_file ( VikWindow *vw, const gchar *fn, guint w, guint h, gdouble zoom, gboolean save_as_png )
3365 /* more efficient way: stuff draws directly to pixbuf (fork viewport) */
3366 GdkPixbuf *pixbuf_to_save;
3367 gdouble old_xmpp, old_ympp;
3368 GError *error = NULL;
3370 GtkWidget *msgbox = gtk_message_dialog_new ( GTK_WINDOW(vw),
3371 GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
3374 _("Generating image file...") );
3376 g_signal_connect_swapped (msgbox, "response", G_CALLBACK (gtk_widget_destroy), msgbox);
3377 // Ensure dialog shown
3378 gtk_widget_show_all ( msgbox );
3380 vik_statusbar_set_message ( vw->viking_vs, VIK_STATUSBAR_INFO, _("Generating image file...") );
3381 while ( gtk_events_pending() )
3382 gtk_main_iteration ();
3383 // Despite many efforts & variations, GTK on my Linux system doesn't show the actual msgbox contents :(
3384 // At least the empty box can give a clue something's going on + the statusbar msg...
3385 // Windows version under Wine OK!
3387 /* backup old zoom & set new */
3388 old_xmpp = vik_viewport_get_xmpp ( vw->viking_vvp );
3389 old_ympp = vik_viewport_get_ympp ( vw->viking_vvp );
3390 vik_viewport_set_zoom ( vw->viking_vvp, zoom );
3392 /* reset width and height: */
3393 vik_viewport_configure_manually ( vw->viking_vvp, w, h );
3395 /* draw all layers */
3398 /* save buffer as file. */
3399 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);
3400 if ( !pixbuf_to_save ) {
3401 g_warning("Failed to generate internal pixmap size: %d x %d", w, h);
3402 gtk_message_dialog_set_markup ( GTK_MESSAGE_DIALOG(msgbox), _("Failed to generate internal image.\n\nTry creating a smaller image.") );
3406 gdk_pixbuf_save ( pixbuf_to_save, fn, save_as_png ? "png" : "jpeg", &error, NULL );
3409 g_warning("Unable to write to file %s: %s", fn, error->message );
3410 gtk_message_dialog_set_markup ( GTK_MESSAGE_DIALOG(msgbox), _("Failed to generate image file.") );
3411 g_error_free (error);
3415 gtk_message_dialog_set_markup ( GTK_MESSAGE_DIALOG(msgbox), _("Image file generated.") );
3417 g_object_unref ( G_OBJECT(pixbuf_to_save) );
3420 vik_statusbar_set_message ( vw->viking_vs, VIK_STATUSBAR_INFO, "" );
3421 gtk_dialog_add_button ( GTK_DIALOG(msgbox), GTK_STOCK_OK, GTK_RESPONSE_OK );
3422 gtk_dialog_run ( GTK_DIALOG(msgbox) ); // Don't care about the result
3424 /* pretend like nothing happened ;) */
3425 vik_viewport_set_xmpp ( vw->viking_vvp, old_xmpp );
3426 vik_viewport_set_ympp ( vw->viking_vvp, old_ympp );
3427 vik_viewport_configure ( vw->viking_vvp );
3431 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 )
3433 gulong size = sizeof(gchar) * (strlen(fn) + 15);
3434 gchar *name_of_file = g_malloc ( size );
3436 struct UTM utm_orig, utm;
3438 /* *** copied from above *** */
3439 GdkPixbuf *pixbuf_to_save;
3440 gdouble old_xmpp, old_ympp;
3441 GError *error = NULL;
3443 /* backup old zoom & set new */
3444 old_xmpp = vik_viewport_get_xmpp ( vw->viking_vvp );
3445 old_ympp = vik_viewport_get_ympp ( vw->viking_vvp );
3446 vik_viewport_set_zoom ( vw->viking_vvp, zoom );
3448 /* reset width and height: do this only once for all images (same size) */
3449 vik_viewport_configure_manually ( vw->viking_vvp, w, h );
3450 /* *** end copy from above *** */
3452 g_assert ( vik_viewport_get_coord_mode ( vw->viking_vvp ) == VIK_COORD_UTM );
3456 utm_orig = *((const struct UTM *)vik_viewport_get_center ( vw->viking_vvp ));
3458 for ( y = 1; y <= tiles_h; y++ )
3460 for ( x = 1; x <= tiles_w; x++ )
3462 g_snprintf ( name_of_file, size, "%s%cy%d-x%d.%s", fn, G_DIR_SEPARATOR, y, x, save_as_png ? "png" : "jpg" );
3464 if ( tiles_w & 0x1 )
3465 utm.easting += ((gdouble)x - ceil(((gdouble)tiles_w)/2)) * (w*zoom);
3467 utm.easting += ((gdouble)x - (((gdouble)tiles_w)+1)/2) * (w*zoom);
3468 if ( tiles_h & 0x1 ) /* odd */
3469 utm.northing -= ((gdouble)y - ceil(((gdouble)tiles_h)/2)) * (h*zoom);
3471 utm.northing -= ((gdouble)y - (((gdouble)tiles_h)+1)/2) * (h*zoom);
3473 /* move to correct place. */
3474 vik_viewport_set_center_utm ( vw->viking_vvp, &utm, FALSE );
3478 /* save buffer as file. */
3479 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);
3480 gdk_pixbuf_save ( pixbuf_to_save, name_of_file, save_as_png ? "png" : "jpeg", &error, NULL );
3483 g_warning("Unable to write to file %s: %s", name_of_file, error->message );
3484 g_error_free (error);
3487 g_object_unref ( G_OBJECT(pixbuf_to_save) );
3491 vik_viewport_set_center_utm ( vw->viking_vvp, &utm_orig, FALSE );
3492 vik_viewport_set_xmpp ( vw->viking_vvp, old_xmpp );
3493 vik_viewport_set_ympp ( vw->viking_vvp, old_ympp );
3494 vik_viewport_configure ( vw->viking_vvp );
3497 g_free ( name_of_file );
3500 static void draw_to_image_file_current_window_cb(GtkWidget* widget,GdkEventButton *event,gpointer *pass_along)
3502 VikWindow *vw = VIK_WINDOW(pass_along[0]);
3503 GtkSpinButton *width_spin = GTK_SPIN_BUTTON(pass_along[1]), *height_spin = GTK_SPIN_BUTTON(pass_along[2]);
3505 gint active = gtk_combo_box_get_active ( GTK_COMBO_BOX(pass_along[3]) );
3506 gdouble zoom = pow (2, active-2 );
3508 gdouble width_min, width_max, height_min, height_max;
3511 gtk_spin_button_get_range ( width_spin, &width_min, &width_max );
3512 gtk_spin_button_get_range ( height_spin, &height_min, &height_max );
3514 /* TODO: support for xzoom and yzoom values */
3515 width = vik_viewport_get_width ( vw->viking_vvp ) * vik_viewport_get_xmpp ( vw->viking_vvp ) / zoom;
3516 height = vik_viewport_get_height ( vw->viking_vvp ) * vik_viewport_get_xmpp ( vw->viking_vvp ) / zoom;
3518 if ( width > width_max || width < width_min || height > height_max || height < height_min )
3519 a_dialog_info_msg ( GTK_WINDOW(vw), _("Viewable region outside allowable pixel size bounds for image. Clipping width/height values.") );
3521 gtk_spin_button_set_value ( width_spin, width );
3522 gtk_spin_button_set_value ( height_spin, height );
3525 static void draw_to_image_file_total_area_cb (GtkSpinButton *spinbutton, gpointer *pass_along)
3527 GtkSpinButton *width_spin = GTK_SPIN_BUTTON(pass_along[1]), *height_spin = GTK_SPIN_BUTTON(pass_along[2]);
3529 gint active = gtk_combo_box_get_active ( GTK_COMBO_BOX(pass_along[3]) );
3530 gdouble zoom = pow (2, active-2 );
3534 w = gtk_spin_button_get_value(width_spin) * zoom;
3535 h = gtk_spin_button_get_value(height_spin) * zoom;
3536 if (pass_along[4]) /* save many images; find TOTAL area covered */
3538 w *= gtk_spin_button_get_value(GTK_SPIN_BUTTON(pass_along[4]));
3539 h *= gtk_spin_button_get_value(GTK_SPIN_BUTTON(pass_along[5]));
3541 vik_units_distance_t dist_units = a_vik_get_units_distance ();
3542 switch (dist_units) {
3543 case VIK_UNITS_DISTANCE_KILOMETRES:
3544 label_text = g_strdup_printf ( _("Total area: %ldm x %ldm (%.3f sq. km)"), (glong)w, (glong)h, (w*h/1000000));
3546 case VIK_UNITS_DISTANCE_MILES:
3547 label_text = g_strdup_printf ( _("Total area: %ldm x %ldm (%.3f sq. miles)"), (glong)w, (glong)h, (w*h/2589988.11));
3550 label_text = g_strdup_printf ("Just to keep the compiler happy");
3551 g_critical("Houston, we've had a problem. distance=%d", dist_units);
3554 gtk_label_set_text(GTK_LABEL(pass_along[6]), label_text);
3555 g_free ( label_text );
3559 * Get an allocated filename (or directory as specified)
3561 static gchar* draw_image_filename ( VikWindow *vw, gboolean one_image_only )
3564 if ( one_image_only )
3567 if (!vw->save_img_dia) {
3568 vw->save_img_dia = gtk_file_chooser_dialog_new (_("Save Image"),
3570 GTK_FILE_CHOOSER_ACTION_SAVE,
3571 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
3572 GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT,
3575 gchar *cwd = g_get_current_dir();
3577 gtk_file_chooser_set_current_folder ( GTK_FILE_CHOOSER(vw->save_img_dia), cwd );
3581 GtkFileChooser *chooser = GTK_FILE_CHOOSER ( vw->save_img_dia );
3583 GtkFileFilter *filter;
3584 filter = gtk_file_filter_new ();
3585 gtk_file_filter_set_name ( filter, _("All") );
3586 gtk_file_filter_add_pattern ( filter, "*" );
3587 gtk_file_chooser_add_filter ( chooser, filter );
3589 filter = gtk_file_filter_new ();
3590 gtk_file_filter_set_name ( filter, _("JPG") );
3591 gtk_file_filter_add_mime_type ( filter, "image/jpeg");
3592 gtk_file_chooser_add_filter ( chooser, filter );
3594 if ( !vw->draw_image_save_as_png )
3595 gtk_file_chooser_set_filter ( chooser, filter );
3597 filter = gtk_file_filter_new ();
3598 gtk_file_filter_set_name ( filter, _("PNG") );
3599 gtk_file_filter_add_mime_type ( filter, "image/png");
3600 gtk_file_chooser_add_filter ( chooser, filter );
3602 if ( vw->draw_image_save_as_png )
3603 gtk_file_chooser_set_filter ( chooser, filter );
3605 gtk_window_set_transient_for ( GTK_WINDOW(vw->save_img_dia), GTK_WINDOW(vw) );
3606 gtk_window_set_destroy_with_parent ( GTK_WINDOW(vw->save_img_dia), TRUE );
3609 if ( gtk_dialog_run ( GTK_DIALOG(vw->save_img_dia) ) == GTK_RESPONSE_ACCEPT ) {
3610 fn = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER(vw->save_img_dia) );
3611 if ( g_file_test ( fn, G_FILE_TEST_EXISTS ) )
3612 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 ) ) )
3615 gtk_widget_hide ( vw->save_img_dia );
3619 // For some reason this method is only written to work in UTM...
3620 if ( vik_viewport_get_coord_mode(vw->viking_vvp) != VIK_COORD_UTM ) {
3621 a_dialog_error_msg ( GTK_WINDOW(vw), _("You must be in UTM mode to use this feature") );
3625 if (!vw->save_img_dir_dia) {
3626 vw->save_img_dir_dia = gtk_file_chooser_dialog_new (_("Choose a directory to hold images"),
3628 GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER,
3629 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
3630 GTK_STOCK_OK, GTK_RESPONSE_ACCEPT,
3632 gtk_window_set_transient_for ( GTK_WINDOW(vw->save_img_dir_dia), GTK_WINDOW(vw) );
3633 gtk_window_set_destroy_with_parent ( GTK_WINDOW(vw->save_img_dir_dia), TRUE );
3636 if ( gtk_dialog_run ( GTK_DIALOG(vw->save_img_dir_dia) ) == GTK_RESPONSE_ACCEPT ) {
3637 fn = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER(vw->save_img_dir_dia) );
3639 gtk_widget_hide ( vw->save_img_dir_dia );
3644 static void draw_to_image_file ( VikWindow *vw, gboolean one_image_only )
3646 /* todo: default for answers inside VikWindow or static (thruout instance) */
3647 GtkWidget *dialog = gtk_dialog_new_with_buttons ( _("Save to Image File"), GTK_WINDOW(vw),
3648 GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
3650 GTK_RESPONSE_REJECT,
3652 GTK_RESPONSE_ACCEPT,
3654 GtkWidget *width_label, *width_spin, *height_label, *height_spin;
3655 GtkWidget *png_radio, *jpeg_radio;
3656 GtkWidget *current_window_button;
3657 gpointer current_window_pass_along[7];
3658 GtkWidget *zoom_label, *zoom_combo;
3659 GtkWidget *total_size_label;
3661 /* only used if (!one_image_only) */
3662 GtkWidget *tiles_width_spin = NULL, *tiles_height_spin = NULL;
3664 width_label = gtk_label_new ( _("Width (pixels):") );
3665 width_spin = gtk_spin_button_new ( GTK_ADJUSTMENT(gtk_adjustment_new ( vw->draw_image_width, 10, 50000, 10, 100, 0 )), 10, 0 );
3666 height_label = gtk_label_new ( _("Height (pixels):") );
3667 height_spin = gtk_spin_button_new ( GTK_ADJUSTMENT(gtk_adjustment_new ( vw->draw_image_height, 10, 50000, 10, 100, 0 )), 10, 0 );
3669 GtkWidget *win_warning_label = gtk_label_new ( _("WARNING: USING LARGE IMAGES OVER 10000x10000\nMAY CRASH THE PROGRAM!") );
3671 zoom_label = gtk_label_new ( _("Zoom (meters per pixel):") );
3672 /* TODO: separate xzoom and yzoom factors */
3673 zoom_combo = create_zoom_combo_all_levels();
3675 gdouble mpp = vik_viewport_get_xmpp(vw->viking_vvp);
3676 gint active = 2 + round ( log (mpp) / log (2) );
3678 // Can we not hard code size here?
3683 gtk_combo_box_set_active ( GTK_COMBO_BOX(zoom_combo), active );
3685 total_size_label = gtk_label_new ( NULL );
3687 current_window_button = gtk_button_new_with_label ( _("Area in current viewable window") );
3688 current_window_pass_along [0] = vw;
3689 current_window_pass_along [1] = width_spin;
3690 current_window_pass_along [2] = height_spin;
3691 current_window_pass_along [3] = zoom_combo;
3692 current_window_pass_along [4] = NULL; /* used for one_image_only != 1 */
3693 current_window_pass_along [5] = NULL;
3694 current_window_pass_along [6] = total_size_label;
3695 g_signal_connect ( G_OBJECT(current_window_button), "button_press_event", G_CALLBACK(draw_to_image_file_current_window_cb), current_window_pass_along );
3697 png_radio = gtk_radio_button_new_with_label ( NULL, _("Save as PNG") );
3698 jpeg_radio = gtk_radio_button_new_with_label_from_widget ( GTK_RADIO_BUTTON(png_radio), _("Save as JPEG") );
3700 gtk_box_pack_start (GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), png_radio, FALSE, FALSE, 0);
3701 gtk_box_pack_start (GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), jpeg_radio, FALSE, FALSE, 0);
3703 if ( ! vw->draw_image_save_as_png )
3704 gtk_toggle_button_set_active ( GTK_TOGGLE_BUTTON(jpeg_radio), TRUE );
3706 gtk_box_pack_start (GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), width_label, FALSE, FALSE, 0);
3707 gtk_box_pack_start (GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), width_spin, FALSE, FALSE, 0);
3708 gtk_box_pack_start (GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), height_label, FALSE, FALSE, 0);
3709 gtk_box_pack_start (GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), height_spin, FALSE, FALSE, 0);
3711 gtk_box_pack_start (GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), win_warning_label, FALSE, FALSE, 0);
3713 gtk_box_pack_start (GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), current_window_button, FALSE, FALSE, 0);
3714 gtk_box_pack_start (GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), zoom_label, FALSE, FALSE, 0);
3715 gtk_box_pack_start (GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), zoom_combo, FALSE, FALSE, 0);
3717 if ( ! one_image_only )
3719 GtkWidget *tiles_width_label, *tiles_height_label;
3721 tiles_width_label = gtk_label_new ( _("East-west image tiles:") );
3722 tiles_width_spin = gtk_spin_button_new ( GTK_ADJUSTMENT(gtk_adjustment_new ( 5, 1, 10, 1, 100, 0 )), 1, 0 );
3723 tiles_height_label = gtk_label_new ( _("North-south image tiles:") );
3724 tiles_height_spin = gtk_spin_button_new ( GTK_ADJUSTMENT(gtk_adjustment_new ( 5, 1, 10, 1, 100, 0 )), 1, 0 );
3725 gtk_box_pack_start (GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), tiles_width_label, FALSE, FALSE, 0);
3726 gtk_box_pack_start (GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), tiles_width_spin, FALSE, FALSE, 0);
3727 gtk_box_pack_start (GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), tiles_height_label, FALSE, FALSE, 0);
3728 gtk_box_pack_start (GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), tiles_height_spin, FALSE, FALSE, 0);
3730 current_window_pass_along [4] = tiles_width_spin;
3731 current_window_pass_along [5] = tiles_height_spin;
3732 g_signal_connect ( G_OBJECT(tiles_width_spin), "value-changed", G_CALLBACK(draw_to_image_file_total_area_cb), current_window_pass_along );
3733 g_signal_connect ( G_OBJECT(tiles_height_spin), "value-changed", G_CALLBACK(draw_to_image_file_total_area_cb), current_window_pass_along );
3735 gtk_box_pack_start (GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), total_size_label, FALSE, FALSE, 0);
3736 g_signal_connect ( G_OBJECT(width_spin), "value-changed", G_CALLBACK(draw_to_image_file_total_area_cb), current_window_pass_along );
3737 g_signal_connect ( G_OBJECT(height_spin), "value-changed", G_CALLBACK(draw_to_image_file_total_area_cb), current_window_pass_along );
3738 g_signal_connect ( G_OBJECT(zoom_combo), "changed", G_CALLBACK(draw_to_image_file_total_area_cb), current_window_pass_along );
3740 draw_to_image_file_total_area_cb ( NULL, current_window_pass_along ); /* set correct size info now */
3742 gtk_dialog_set_default_response ( GTK_DIALOG(dialog), GTK_RESPONSE_ACCEPT );
3744 gtk_widget_show_all ( gtk_dialog_get_content_area(GTK_DIALOG(dialog)) );
3746 if ( gtk_dialog_run ( GTK_DIALOG(dialog) ) == GTK_RESPONSE_ACCEPT )
3748 gtk_widget_hide ( GTK_WIDGET(dialog) );
3750 gchar *fn = draw_image_filename ( vw, one_image_only );
3754 gint active_z = gtk_combo_box_get_active ( GTK_COMBO_BOX(zoom_combo) );
3755 gdouble zoom = pow (2, active_z-2 );
3757 if ( one_image_only )
3758 save_image_file ( vw, fn,
3759 vw->draw_image_width = gtk_spin_button_get_value_as_int ( GTK_SPIN_BUTTON(width_spin) ),
3760 vw->draw_image_height = gtk_spin_button_get_value_as_int ( GTK_SPIN_BUTTON(height_spin) ),
3762 vw->draw_image_save_as_png = gtk_toggle_button_get_active ( GTK_TOGGLE_BUTTON(png_radio) ) );
3764 // NB is in UTM mode ATM
3765 save_image_dir ( vw, fn,
3766 vw->draw_image_width = gtk_spin_button_get_value_as_int ( GTK_SPIN_BUTTON(width_spin) ),
3767 vw->draw_image_height = gtk_spin_button_get_value_as_int ( GTK_SPIN_BUTTON(height_spin) ),
3769 vw->draw_image_save_as_png = gtk_toggle_button_get_active ( GTK_TOGGLE_BUTTON(png_radio) ),
3770 gtk_spin_button_get_value ( GTK_SPIN_BUTTON(tiles_width_spin) ),
3771 gtk_spin_button_get_value ( GTK_SPIN_BUTTON(tiles_height_spin) ) );
3776 gtk_widget_destroy ( GTK_WIDGET(dialog) );
3780 static void draw_to_image_file_cb ( GtkAction *a, VikWindow *vw )
3782 draw_to_image_file ( vw, TRUE );
3785 static void draw_to_image_dir_cb ( GtkAction *a, VikWindow *vw )
3787 draw_to_image_file ( vw, FALSE );
3790 static void print_cb ( GtkAction *a, VikWindow *vw )
3792 a_print(vw, vw->viking_vvp);
3795 /* really a misnomer: changes coord mode (actual coordinates) AND/OR draw mode (viewport only) */
3796 static void window_change_coord_mode_cb ( GtkAction *old_a, GtkAction *a, VikWindow *vw )
3798 VikViewportDrawMode drawmode;
3799 if (!strcmp(gtk_action_get_name(a), "ModeUTM")) {
3800 drawmode = VIK_VIEWPORT_DRAWMODE_UTM;
3802 else if (!strcmp(gtk_action_get_name(a), "ModeLatLon")) {
3803 drawmode = VIK_VIEWPORT_DRAWMODE_LATLON;
3805 else if (!strcmp(gtk_action_get_name(a), "ModeExpedia")) {
3806 drawmode = VIK_VIEWPORT_DRAWMODE_EXPEDIA;
3808 else if (!strcmp(gtk_action_get_name(a), "ModeMercator")) {
3809 drawmode = VIK_VIEWPORT_DRAWMODE_MERCATOR;
3812 g_critical("Houston, we've had a problem.");
3816 if ( !vw->only_updating_coord_mode_ui )
3818 VikViewportDrawMode olddrawmode = vik_viewport_get_drawmode ( vw->viking_vvp );
3819 if ( olddrawmode != drawmode )
3821 /* this takes care of coord mode too */
3822 vik_viewport_set_drawmode ( vw->viking_vvp, drawmode );
3823 if ( drawmode == VIK_VIEWPORT_DRAWMODE_UTM ) {
3824 vik_layers_panel_change_coord_mode ( vw->viking_vlp, VIK_COORD_UTM );
3825 } else if ( olddrawmode == VIK_VIEWPORT_DRAWMODE_UTM ) {
3826 vik_layers_panel_change_coord_mode ( vw->viking_vlp, VIK_COORD_LATLON );
3833 static void set_draw_scale ( GtkAction *a, VikWindow *vw )
3835 GtkWidget *check_box = gtk_ui_manager_get_widget ( vw->uim, "/ui/MainMenu/View/SetShow/ShowScale" );
3836 g_assert(check_box);
3837 gboolean state = gtk_check_menu_item_get_active ( GTK_CHECK_MENU_ITEM(check_box));
3838 vik_viewport_set_draw_scale ( vw->viking_vvp, state );
3842 static void set_draw_centermark ( GtkAction *a, VikWindow *vw )
3844 GtkWidget *check_box = gtk_ui_manager_get_widget ( vw->uim, "/ui/MainMenu/View/SetShow/ShowCenterMark" );
3845 g_assert(check_box);
3846 gboolean state = gtk_check_menu_item_get_active ( GTK_CHECK_MENU_ITEM(check_box));
3847 vik_viewport_set_draw_centermark ( vw->viking_vvp, state );
3851 static void set_draw_highlight ( GtkAction *a, VikWindow *vw )
3853 GtkWidget *check_box = gtk_ui_manager_get_widget ( vw->uim, "/ui/MainMenu/View/SetShow/ShowHighlight" );
3854 g_assert(check_box);
3855 gboolean state = gtk_check_menu_item_get_active ( GTK_CHECK_MENU_ITEM(check_box));
3856 vik_viewport_set_draw_highlight ( vw->viking_vvp, state );
3860 static void set_bg_color ( GtkAction *a, VikWindow *vw )
3862 GtkWidget *colorsd = gtk_color_selection_dialog_new ( _("Choose a background color") );
3863 GdkColor *color = vik_viewport_get_background_gdkcolor ( vw->viking_vvp );
3864 gtk_color_selection_set_previous_color ( GTK_COLOR_SELECTION(gtk_color_selection_dialog_get_color_selection(GTK_COLOR_SELECTION_DIALOG(colorsd))), color );
3865 gtk_color_selection_set_current_color ( GTK_COLOR_SELECTION(gtk_color_selection_dialog_get_color_selection(GTK_COLOR_SELECTION_DIALOG(colorsd))), color );
3866 if ( gtk_dialog_run ( GTK_DIALOG(colorsd) ) == GTK_RESPONSE_OK )
3868 gtk_color_selection_get_current_color ( GTK_COLOR_SELECTION(gtk_color_selection_dialog_get_color_selection(GTK_COLOR_SELECTION_DIALOG(colorsd))), color );
3869 vik_viewport_set_background_gdkcolor ( vw->viking_vvp, color );
3873 gtk_widget_destroy ( colorsd );
3876 static void set_highlight_color ( GtkAction *a, VikWindow *vw )
3878 GtkWidget *colorsd = gtk_color_selection_dialog_new ( _("Choose a track highlight color") );
3879 GdkColor *color = vik_viewport_get_highlight_gdkcolor ( vw->viking_vvp );
3880 gtk_color_selection_set_previous_color ( GTK_COLOR_SELECTION(gtk_color_selection_dialog_get_color_selection(GTK_COLOR_SELECTION_DIALOG(colorsd))), color );
3881 gtk_color_selection_set_current_color ( GTK_COLOR_SELECTION(gtk_color_selection_dialog_get_color_selection(GTK_COLOR_SELECTION_DIALOG(colorsd))), color );
3882 if ( gtk_dialog_run ( GTK_DIALOG(colorsd) ) == GTK_RESPONSE_OK )
3884 gtk_color_selection_get_current_color ( GTK_COLOR_SELECTION(gtk_color_selection_dialog_get_color_selection(GTK_COLOR_SELECTION_DIALOG(colorsd))), color );
3885 vik_viewport_set_highlight_gdkcolor ( vw->viking_vvp, color );
3889 gtk_widget_destroy ( colorsd );
3894 /***********************************************************************************************
3896 ***********************************************************************************************/
3898 static GtkActionEntry entries[] = {
3899 { "File", NULL, N_("_File"), 0, 0, 0 },
3900 { "Edit", NULL, N_("_Edit"), 0, 0, 0 },
3901 { "View", NULL, N_("_View"), 0, 0, 0 },
3902 { "SetShow", NULL, N_("_Show"), 0, 0, 0 },
3903 { "SetZoom", NULL, N_("_Zoom"), 0, 0, 0 },
3904 { "SetPan", NULL, N_("_Pan"), 0, 0, 0 },
3905 { "Layers", NULL, N_("_Layers"), 0, 0, 0 },
3906 { "Tools", NULL, N_("_Tools"), 0, 0, 0 },
3907 { "Exttools", NULL, N_("_Webtools"), 0, 0, 0 },
3908 { "Help", NULL, N_("_Help"), 0, 0, 0 },
3910 { "New", GTK_STOCK_NEW, N_("_New"), "<control>N", N_("New file"), (GCallback)newwindow_cb },
3911 { "Open", GTK_STOCK_OPEN, N_("_Open..."), "<control>O", N_("Open a file"), (GCallback)load_file },
3912 { "OpenRecentFile", NULL, N_("Open _Recent File"), NULL, NULL, (GCallback)NULL },
3913 { "Append", GTK_STOCK_ADD, N_("Append _File..."), NULL, N_("Append data from a different file"), (GCallback)load_file },
3914 { "Export", GTK_STOCK_CONVERT, N_("_Export All"), NULL, N_("Export All TrackWaypoint Layers"), (GCallback)NULL },
3915 { "ExportGPX", NULL, N_("_GPX..."), NULL, N_("Export as GPX"), (GCallback)export_to_gpx },
3916 { "Acquire", GTK_STOCK_GO_DOWN, N_("A_cquire"), NULL, NULL, (GCallback)NULL },
3917 { "AcquireGPS", NULL, N_("From _GPS..."), NULL, N_("Transfer data from a GPS device"), (GCallback)acquire_from_gps },
3918 { "AcquireGPSBabel", NULL, N_("Import File With GPS_Babel..."), NULL, N_("Import file via GPSBabel converter"), (GCallback)acquire_from_file },
3919 { "AcquireRouting", NULL, N_("_Directions..."), NULL, N_("Get driving directions"), (GCallback)acquire_from_routing },
3920 #ifdef VIK_CONFIG_OPENSTREETMAP
3921 { "AcquireOSM", NULL, N_("_OSM Traces..."), NULL, N_("Get traces from OpenStreetMap"), (GCallback)acquire_from_osm },
3922 { "AcquireMyOSM", NULL, N_("_My OSM Traces..."), NULL, N_("Get Your Own Traces from OpenStreetMap"), (GCallback)acquire_from_my_osm },
3924 #ifdef VIK_CONFIG_GEOCACHES
3925 { "AcquireGC", NULL, N_("Geo_caches..."), NULL, N_("Get Geocaches from geocaching.com"), (GCallback)acquire_from_gc },
3927 #ifdef VIK_CONFIG_GEOTAG
3928 { "AcquireGeotag", NULL, N_("From Geotagged _Images..."), NULL, N_("Create waypoints from geotagged images"), (GCallback)acquire_from_geotag },
3930 { "AcquireURL", NULL, N_("From _URL..."), NULL, N_("Get a file from a URL"), (GCallback)acquire_from_url },
3931 #ifdef VIK_CONFIG_GEONAMES
3932 { "AcquireWikipedia", NULL, N_("From _Wikipedia Waypoints"), NULL, N_("Create waypoints from Wikipedia items in the current view"), (GCallback)acquire_from_wikipedia },
3934 { "Save", GTK_STOCK_SAVE, N_("_Save"), "<control>S", N_("Save the file"), (GCallback)save_file },
3935 { "SaveAs", GTK_STOCK_SAVE_AS, N_("Save _As..."), NULL, N_("Save the file under different name"), (GCallback)save_file_as },
3936 { "FileProperties", NULL, N_("Properties..."), NULL, N_("File Properties"), (GCallback)file_properties_cb },
3937 { "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 },
3938 { "GenImgDir", GTK_STOCK_DND_MULTIPLE, N_("Generate _Directory of Images..."), NULL, N_("FIXME:IMGDIR"), (GCallback)draw_to_image_dir_cb },
3939 { "Print", GTK_STOCK_PRINT, N_("_Print..."), NULL, N_("Print maps"), (GCallback)print_cb },
3940 { "Exit", GTK_STOCK_QUIT, N_("E_xit"), "<control>W", N_("Exit the program"), (GCallback)window_close },
3941 { "SaveExit", GTK_STOCK_QUIT, N_("Save and Exit"), NULL, N_("Save and Exit the program"), (GCallback)save_file_and_exit },
3943 { "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 },
3944 { "GoForward", GTK_STOCK_GO_FORWARD, N_("Go to the _Next Location"), NULL, N_("Go to the next location"), (GCallback)draw_goto_back_and_forth },
3945 { "GotoDefaultLocation", GTK_STOCK_HOME, N_("Go to the _Default Location"), NULL, N_("Go to the default location"), (GCallback)goto_default_location },
3946 { "GotoSearch", GTK_STOCK_JUMP_TO, N_("Go to _Location..."), NULL, N_("Go to address/place using text search"), (GCallback)goto_address },
3947 { "GotoLL", GTK_STOCK_JUMP_TO, N_("_Go to Lat/Lon..."), NULL, N_("Go to arbitrary lat/lon coordinate"), (GCallback)draw_goto_cb },
3948 { "GotoUTM", GTK_STOCK_JUMP_TO, N_("Go to UTM..."), NULL, N_("Go to arbitrary UTM coordinate"), (GCallback)draw_goto_cb },
3949 { "Refresh", GTK_STOCK_REFRESH, N_("_Refresh"), "F5", N_("Refresh any maps displayed"), (GCallback)draw_refresh_cb },
3950 { "SetHLColor",GTK_STOCK_SELECT_COLOR, N_("Set _Highlight Color..."), NULL, NULL, (GCallback)set_highlight_color },
3951 { "SetBGColor",GTK_STOCK_SELECT_COLOR, N_("Set Bac_kground Color..."), NULL, NULL, (GCallback)set_bg_color },
3952 { "ZoomIn", GTK_STOCK_ZOOM_IN, N_("Zoom _In"), "<control>plus", NULL, (GCallback)draw_zoom_cb },
3953 { "ZoomOut", GTK_STOCK_ZOOM_OUT, N_("Zoom _Out"), "<control>minus", NULL, (GCallback)draw_zoom_cb },
3954 { "ZoomTo", GTK_STOCK_ZOOM_FIT, N_("Zoom _To..."), "<control>Z", NULL, (GCallback)zoom_to_cb },
3955 { "PanNorth", NULL, N_("Pan _North"), "<control>Up", NULL, (GCallback)draw_pan_cb },
3956 { "PanEast", NULL, N_("Pan _East"), "<control>Right", NULL, (GCallback)draw_pan_cb },
3957 { "PanSouth", NULL, N_("Pan _South"), "<control>Down", NULL, (GCallback)draw_pan_cb },
3958 { "PanWest", NULL, N_("Pan _West"), "<control>Left", NULL, (GCallback)draw_pan_cb },
3959 { "BGJobs", GTK_STOCK_EXECUTE, N_("Background _Jobs"), NULL, NULL, (GCallback)a_background_show_window },
3961 { "Cut", GTK_STOCK_CUT, N_("Cu_t"), NULL, NULL, (GCallback)menu_cut_layer_cb },
3962 { "Copy", GTK_STOCK_COPY, N_("_Copy"), NULL, NULL, (GCallback)menu_copy_layer_cb },
3963 { "Paste", GTK_STOCK_PASTE, N_("_Paste"), NULL, NULL, (GCallback)menu_paste_layer_cb },
3964 { "Delete", GTK_STOCK_DELETE, N_("_Delete"), NULL, NULL, (GCallback)menu_delete_layer_cb },
3965 { "DeleteAll", NULL, N_("Delete All"), NULL, NULL, (GCallback)clear_cb },
3966 { "MapCacheFlush",NULL, N_("_Flush Map Cache"), NULL, NULL, (GCallback)mapcache_flush_cb },
3967 { "SetDefaultLocation", GTK_STOCK_GO_FORWARD, N_("_Set the Default Location"), NULL, N_("Set the Default Location to the current position"),(GCallback)default_location_cb },
3968 { "Preferences",GTK_STOCK_PREFERENCES, N_("_Preferences"), NULL, NULL, (GCallback)preferences_cb },
3969 { "LayerDefaults",GTK_STOCK_PROPERTIES, N_("_Layer Defaults"), NULL, NULL, NULL },
3970 { "Properties",GTK_STOCK_PROPERTIES, N_("_Properties"), NULL, NULL, (GCallback)menu_properties_cb },
3972 { "HelpEntry", GTK_STOCK_HELP, N_("_Help"), "F1", NULL, (GCallback)help_help_cb },
3973 { "About", GTK_STOCK_ABOUT, N_("_About"), NULL, NULL, (GCallback)help_about_cb },
3976 static GtkActionEntry entries_gpsbabel[] = {
3977 { "ExportKML", NULL, N_("_KML..."), NULL, N_("Export as KML"), (GCallback)export_to_kml },
3981 /* FIXME use VIEWPORT_DRAWMODE values */
3982 static GtkRadioActionEntry mode_entries[] = {
3983 { "ModeUTM", NULL, N_("_UTM Mode"), "<control>u", NULL, 0 },
3984 { "ModeExpedia", NULL, N_("_Expedia Mode"), "<control>e", NULL, 1 },
3985 { "ModeMercator", NULL, N_("_Mercator Mode"), "<control>m", NULL, 4 },
3986 { "ModeLatLon", NULL, N_("Lat_/Lon Mode"), "<control>l", NULL, 5 },
3989 static GtkToggleActionEntry toggle_entries[] = {
3990 { "ShowScale", NULL, N_("Show _Scale"), "<shift>F5", N_("Show Scale"), (GCallback)set_draw_scale, TRUE },
3991 { "ShowCenterMark", NULL, N_("Show _Center Mark"), "F6", N_("Show Center Mark"), (GCallback)set_draw_centermark, TRUE },
3992 { "ShowHighlight", GTK_STOCK_UNDERLINE, N_("Show _Highlight"), "F7", N_("Show Highlight"), (GCallback)set_draw_highlight, TRUE },
3993 { "FullScreen", GTK_STOCK_FULLSCREEN, N_("_Full Screen"), "F11", N_("Activate full screen mode"), (GCallback)full_screen_cb, FALSE },
3994 { "ViewSidePanel", GTK_STOCK_INDEX, N_("Show Side _Panel"), "F9", N_("Show Side Panel"), (GCallback)view_side_panel_cb, TRUE },
3995 { "ViewStatusBar", NULL, N_("Show Status_bar"), "F12", N_("Show Statusbar"), (GCallback)view_statusbar_cb, TRUE },
3996 { "ViewToolbar", NULL, N_("Show _Toolbar"), "F3", N_("Show Toolbar"), (GCallback)view_toolbar_cb, TRUE },
3997 { "ViewMainMenu", NULL, N_("Show _Menu"), "F4", N_("Show Menu"), (GCallback)view_main_menu_cb, TRUE },
4000 #include "menu.xml.h"
4001 static void window_create_ui( VikWindow *window )
4004 GtkActionGroup *action_group;
4005 GtkAccelGroup *accel_group;
4008 GtkIconFactory *icon_factory;
4009 GtkIconSet *icon_set;
4010 GtkRadioActionEntry *tools = NULL, *radio;
4013 uim = gtk_ui_manager_new ();
4016 toolbox_add_tool(window->vt, &ruler_tool, TOOL_LAYER_TYPE_NONE);
4017 toolbox_add_tool(window->vt, &zoom_tool, TOOL_LAYER_TYPE_NONE);
4018 toolbox_add_tool(window->vt, &pan_tool, TOOL_LAYER_TYPE_NONE);
4019 toolbox_add_tool(window->vt, &select_tool, TOOL_LAYER_TYPE_NONE);
4022 if (!(mid = gtk_ui_manager_add_ui_from_string (uim, menu_xml, -1, &error))) {
4023 g_error_free (error);
4027 action_group = gtk_action_group_new ("MenuActions");
4028 gtk_action_group_set_translation_domain(action_group, PACKAGE_NAME);
4029 gtk_action_group_add_actions (action_group, entries, G_N_ELEMENTS (entries), window);
4030 gtk_action_group_add_toggle_actions (action_group, toggle_entries, G_N_ELEMENTS (toggle_entries), window);
4031 gtk_action_group_add_radio_actions (action_group, mode_entries, G_N_ELEMENTS (mode_entries), 4, (GCallback)window_change_coord_mode_cb, window);
4033 // Use this to see if GPSBabel is available:
4034 if ( a_babel_available () ) {
4035 // If going to add more entries then might be worth creating a menu_gpsbabel.xml.h file
4036 if ( gtk_ui_manager_add_ui_from_string ( uim,
4037 "<ui><menubar name='MainMenu'><menu action='File'><menu action='Export'><menuitem action='ExportKML'/></menu></menu></menubar></ui>",
4039 gtk_action_group_add_actions ( action_group, entries_gpsbabel, G_N_ELEMENTS (entries_gpsbabel), window );
4042 icon_factory = gtk_icon_factory_new ();
4043 gtk_icon_factory_add_default (icon_factory);
4045 register_vik_icons(icon_factory);
4047 // Copy the tool RadioActionEntries out of the main Window structure into an extending array 'tools'
4048 // so that it can be applied to the UI in one action group add function call below
4050 for (i=0; i<window->vt->n_tools; i++) {
4051 tools = g_renew(GtkRadioActionEntry, tools, ntools+1);
4052 radio = &tools[ntools];
4054 *radio = window->vt->tools[i].ti.radioActionEntry;
4055 radio->value = ntools;
4058 for (i=0; i<VIK_LAYER_NUM_TYPES; i++) {
4059 GtkActionEntry action;
4060 gtk_ui_manager_add_ui(uim, mid, "/ui/MainMenu/Layers/",
4061 vik_layer_get_interface(i)->name,
4062 vik_layer_get_interface(i)->name,
4063 GTK_UI_MANAGER_MENUITEM, FALSE);
4065 icon_set = gtk_icon_set_new_from_pixbuf (gdk_pixbuf_from_pixdata (vik_layer_get_interface(i)->icon, FALSE, NULL ));
4066 gtk_icon_factory_add (icon_factory, vik_layer_get_interface(i)->name, icon_set);
4067 gtk_icon_set_unref (icon_set);
4069 action.name = vik_layer_get_interface(i)->name;
4070 action.stock_id = vik_layer_get_interface(i)->name;
4071 action.label = g_strdup_printf( _("New _%s Layer"), vik_layer_get_interface(i)->name);
4072 action.accelerator = vik_layer_get_interface(i)->accelerator;
4073 action.tooltip = NULL;
4074 action.callback = (GCallback)menu_addlayer_cb;
4075 gtk_action_group_add_actions(action_group, &action, 1, window);
4077 g_free ( (gchar*)action.label );
4079 if ( vik_layer_get_interface(i)->tools_count ) {
4080 gtk_ui_manager_add_ui(uim, mid, "/ui/MainMenu/Tools/", vik_layer_get_interface(i)->name, NULL, GTK_UI_MANAGER_SEPARATOR, FALSE);
4081 gtk_ui_manager_add_ui(uim, mid, "/ui/MainToolbar/ToolItems/", vik_layer_get_interface(i)->name, NULL, GTK_UI_MANAGER_SEPARATOR, FALSE);
4084 // Further tool copying for to apply to the UI, also apply menu UI setup
4085 for ( j = 0; j < vik_layer_get_interface(i)->tools_count; j++ ) {
4086 tools = g_renew(GtkRadioActionEntry, tools, ntools+1);
4087 radio = &tools[ntools];
4090 gtk_ui_manager_add_ui(uim, mid, "/ui/MainMenu/Tools",
4091 vik_layer_get_interface(i)->tools[j].radioActionEntry.label,
4092 vik_layer_get_interface(i)->tools[j].radioActionEntry.name,
4093 GTK_UI_MANAGER_MENUITEM, FALSE);
4094 gtk_ui_manager_add_ui(uim, mid, "/ui/MainToolbar/ToolItems",
4095 vik_layer_get_interface(i)->tools[j].radioActionEntry.label,
4096 vik_layer_get_interface(i)->tools[j].radioActionEntry.name,
4097 GTK_UI_MANAGER_TOOLITEM, FALSE);
4099 toolbox_add_tool(window->vt, &(vik_layer_get_interface(i)->tools[j]), i);
4101 *radio = vik_layer_get_interface(i)->tools[j].radioActionEntry;
4102 // Overwrite with actual number to use
4103 radio->value = ntools;
4106 GtkActionEntry action_dl;
4107 gchar *layername = g_strdup_printf ( "Layer%s", vik_layer_get_interface(i)->fixed_layer_name );
4108 gtk_ui_manager_add_ui(uim, mid, "/ui/MainMenu/Edit/LayerDefaults",
4109 vik_layer_get_interface(i)->name,
4111 GTK_UI_MANAGER_MENUITEM, FALSE);
4114 // For default layers use action names of the form 'Layer<LayerName>'
4115 // This is to avoid clashing with just the layer name used above for the tool actions
4116 action_dl.name = g_strconcat("Layer", vik_layer_get_interface(i)->fixed_layer_name, NULL);
4117 action_dl.stock_id = NULL;
4118 action_dl.label = g_strconcat("_", vik_layer_get_interface(i)->name, "...", NULL); // Prepend marker for keyboard accelerator
4119 action_dl.accelerator = NULL;
4120 action_dl.tooltip = NULL;
4121 action_dl.callback = (GCallback)layer_defaults_cb;
4122 gtk_action_group_add_actions(action_group, &action_dl, 1, window);
4123 g_free ( (gchar*)action_dl.name );
4124 g_free ( (gchar*)action_dl.label );
4126 g_object_unref (icon_factory);
4128 gtk_action_group_add_radio_actions(action_group, tools, ntools, 0, (GCallback)menu_tool_cb, window);
4131 gtk_ui_manager_insert_action_group (uim, action_group, 0);
4133 for (i=0; i<VIK_LAYER_NUM_TYPES; i++) {
4134 for ( j = 0; j < vik_layer_get_interface(i)->tools_count; j++ ) {
4135 GtkAction *action = gtk_action_group_get_action(action_group,
4136 vik_layer_get_interface(i)->tools[j].radioActionEntry.name);
4137 g_object_set(action, "sensitive", FALSE, NULL);
4141 // This is done last so we don't need to track the value of mid anymore
4142 vik_ext_tools_add_action_items ( window, window->uim, action_group, mid );
4144 window->action_group = action_group;
4146 accel_group = gtk_ui_manager_get_accel_group (uim);
4147 gtk_window_add_accel_group (GTK_WINDOW (window), accel_group);
4148 gtk_ui_manager_ensure_update (uim);
4150 setup_recent_files(window);
4154 // TODO - add method to add tool icons defined from outside this file
4155 // and remove the reverse dependency on icon definition from this file
4157 const GdkPixdata *data;
4160 { &mover_22_pixbuf, "vik-icon-pan" },
4161 { &zoom_18_pixbuf, "vik-icon-zoom" },
4162 { &ruler_18_pixbuf, "vik-icon-ruler" },
4163 { &select_18_pixbuf, "vik-icon-select" },
4164 { &vik_new_route_18_pixbuf, "vik-icon-Create Route" },
4165 { &route_finder_18_pixbuf, "vik-icon-Route Finder" },
4166 { &demdl_18_pixbuf, "vik-icon-DEM Download" },
4167 { &showpic_18_pixbuf, "vik-icon-Show Picture" },
4168 { &addtr_18_pixbuf, "vik-icon-Create Track" },
4169 { &edtr_18_pixbuf, "vik-icon-Edit Trackpoint" },
4170 { &addwp_18_pixbuf, "vik-icon-Create Waypoint" },
4171 { &edwp_18_pixbuf, "vik-icon-Edit Waypoint" },
4172 { &geozoom_18_pixbuf, "vik-icon-Georef Zoom Tool" },
4173 { &geomove_18_pixbuf, "vik-icon-Georef Move Map" },
4174 { &mapdl_18_pixbuf, "vik-icon-Maps Download" },
4177 static gint n_stock_icons = G_N_ELEMENTS (stock_icons);
4180 register_vik_icons (GtkIconFactory *icon_factory)
4182 GtkIconSet *icon_set;
4185 for (i = 0; i < n_stock_icons; i++) {
4186 icon_set = gtk_icon_set_new_from_pixbuf (gdk_pixbuf_from_pixdata (
4187 stock_icons[i].data, FALSE, NULL ));
4188 gtk_icon_factory_add (icon_factory, stock_icons[i].stock_id, icon_set);
4189 gtk_icon_set_unref (icon_set);
4193 gpointer vik_window_get_selected_trw_layer ( VikWindow *vw )
4195 return vw->selected_vtl;
4198 void vik_window_set_selected_trw_layer ( VikWindow *vw, gpointer vtl )
4200 vw->selected_vtl = vtl;
4201 vw->containing_vtl = vtl;
4203 vw->selected_track = NULL;
4204 vw->selected_tracks = NULL;
4205 vw->selected_waypoint = NULL;
4206 vw->selected_waypoints = NULL;
4207 // Set highlight thickness
4208 vik_viewport_set_highlight_thickness ( vw->viking_vvp, vik_trw_layer_get_property_tracks_line_thickness (vw->containing_vtl) );
4211 GHashTable *vik_window_get_selected_tracks ( VikWindow *vw )
4213 return vw->selected_tracks;
4216 void vik_window_set_selected_tracks ( VikWindow *vw, GHashTable *ght, gpointer vtl )
4218 vw->selected_tracks = ght;
4219 vw->containing_vtl = vtl;
4221 vw->selected_vtl = NULL;
4222 vw->selected_track = NULL;
4223 vw->selected_waypoint = NULL;
4224 vw->selected_waypoints = NULL;
4225 // Set highlight thickness
4226 vik_viewport_set_highlight_thickness ( vw->viking_vvp, vik_trw_layer_get_property_tracks_line_thickness (vw->containing_vtl) );
4229 gpointer vik_window_get_selected_track ( VikWindow *vw )
4231 return vw->selected_track;
4234 void vik_window_set_selected_track ( VikWindow *vw, gpointer *vt, gpointer vtl )
4236 vw->selected_track = vt;
4237 vw->containing_vtl = vtl;
4239 vw->selected_vtl = NULL;
4240 vw->selected_tracks = NULL;
4241 vw->selected_waypoint = NULL;
4242 vw->selected_waypoints = NULL;
4243 // Set highlight thickness
4244 vik_viewport_set_highlight_thickness ( vw->viking_vvp, vik_trw_layer_get_property_tracks_line_thickness (vw->containing_vtl) );
4247 GHashTable *vik_window_get_selected_waypoints ( VikWindow *vw )
4249 return vw->selected_waypoints;
4252 void vik_window_set_selected_waypoints ( VikWindow *vw, GHashTable *ght, gpointer vtl )
4254 vw->selected_waypoints = ght;
4255 vw->containing_vtl = vtl;
4257 vw->selected_vtl = NULL;
4258 vw->selected_track = NULL;
4259 vw->selected_tracks = NULL;
4260 vw->selected_waypoint = NULL;
4263 gpointer vik_window_get_selected_waypoint ( VikWindow *vw )
4265 return vw->selected_waypoint;
4268 void vik_window_set_selected_waypoint ( VikWindow *vw, gpointer *vwp, gpointer vtl )
4270 vw->selected_waypoint = vwp;
4271 vw->containing_vtl = vtl;
4273 vw->selected_vtl = NULL;
4274 vw->selected_track = NULL;
4275 vw->selected_tracks = NULL;
4276 vw->selected_waypoints = NULL;
4279 gboolean vik_window_clear_highlight ( VikWindow *vw )
4281 gboolean need_redraw = FALSE;
4282 if ( vw->selected_vtl != NULL ) {
4283 vw->selected_vtl = NULL;
4286 if ( vw->selected_track != NULL ) {
4287 vw->selected_track = NULL;
4290 if ( vw->selected_tracks != NULL ) {
4291 vw->selected_tracks = NULL;
4294 if ( vw->selected_waypoint != NULL ) {
4295 vw->selected_waypoint = NULL;
4298 if ( vw->selected_waypoints != NULL ) {
4299 vw->selected_waypoints = NULL;
4305 GThread *vik_window_get_thread ( VikWindow *vw )