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;
177 guint draw_image_width, draw_image_height;
178 gboolean draw_image_save_as_png;
182 VikLoadType_t loaded_type;
184 GtkWidget *open_dia, *save_dia;
185 GtkWidget *save_img_dia, *save_img_dir_dia;
187 gboolean only_updating_coord_mode_ui; /* hack for a bug in GTK */
191 /* half-drawn update */
193 VikCoord trigger_center;
195 /* Store at this level for highlighted selection drawing since it applies to the viewport and the layers panel */
196 /* Only one of these items can be selected at the same time */
197 gpointer selected_vtl; /* notionally VikTrwLayer */
198 GHashTable *selected_tracks;
199 gpointer selected_track; /* notionally VikTrack */
200 GHashTable *selected_waypoints;
201 gpointer selected_waypoint; /* notionally VikWaypoint */
202 /* only use for individual track or waypoint */
203 /* For track(s) & waypoint(s) it is the layer they are in - this helps refering to the individual item easier */
204 gpointer containing_vtl; /* notionally VikTrwLayer */
218 VW_OPENWINDOW_SIGNAL,
222 static guint window_signals[VW_LAST_SIGNAL] = { 0 };
224 // TODO get rid of this as this is unnecessary duplication...
225 static gchar *tool_names[NUMBER_OF_TOOLS] = { N_("Pan"), N_("Zoom"), N_("Ruler"), N_("Select") };
227 G_DEFINE_TYPE (VikWindow, vik_window, GTK_TYPE_WINDOW)
229 VikViewport * vik_window_viewport(VikWindow *vw)
231 return(vw->viking_vvp);
234 VikLayersPanel * vik_window_layers_panel(VikWindow *vw)
236 return(vw->viking_vlp);
240 * Returns the statusbar for the window
242 VikStatusbar * vik_window_get_statusbar ( VikWindow *vw )
244 return vw->viking_vs;
248 * Returns the 'project' filename
250 const gchar *vik_window_get_filename (VikWindow *vw)
257 vik_statusbar_type_t vs_type;
258 gchar* message; // Always make a copy of this data
259 } statusbar_idle_data;
262 * For the actual statusbar update!
264 static gboolean statusbar_idle_update ( statusbar_idle_data *sid )
266 vik_statusbar_set_message ( sid->vs, sid->vs_type, sid->message );
267 g_free ( sid->message );
273 * vik_window_statusbar_update:
274 * @vw: The main window in which the statusbar will be updated.
275 * @message: The string to be displayed. This is copied.
276 * @vs_type: The part of the statusbar to be updated.
278 * This updates any part of the statusbar with the new string.
279 * It handles calling from the main thread or any background thread
280 * ATM this mostly used from background threads - as from the main thread
281 * one may use the vik_statusbar_set_message() directly.
283 void vik_window_statusbar_update ( VikWindow *vw, const gchar* message, vik_statusbar_type_t vs_type )
285 statusbar_idle_data *sid = g_malloc ( sizeof (statusbar_idle_data) );
286 sid->vs = vw->viking_vs;
287 sid->vs_type = vs_type;
288 sid->message = g_strdup ( message );
290 if ( g_thread_self() == vik_window_get_thread ( vw ) ) {
291 g_idle_add ( (GSourceFunc) statusbar_idle_update, sid );
294 // From a background thread
295 gdk_threads_add_idle ( (GSourceFunc) statusbar_idle_update, sid );
299 // Actual signal handlers
300 static void destroy_window ( GtkWidget *widget,
303 if ( ! --window_count )
307 #define VIK_SETTINGS_WIN_SIDEPANEL "window_sidepanel"
308 #define VIK_SETTINGS_WIN_STATUSBAR "window_statusbar"
309 #define VIK_SETTINGS_WIN_TOOLBAR "window_toolbar"
310 // Menubar setting to off is never auto saved in case it's accidentally turned off
311 // It's not so obvious so to recover the menu visibility.
312 // Thus this value is for setting manually via editting the settings file directly
313 #define VIK_SETTINGS_WIN_MENUBAR "window_menubar"
315 VikWindow *vik_window_new_window ()
317 if ( window_count < MAX_WINDOWS )
319 VikWindow *vw = window_new ();
321 g_signal_connect (G_OBJECT (vw), "destroy",
322 G_CALLBACK (destroy_window), NULL);
323 g_signal_connect (G_OBJECT (vw), "newwindow",
324 G_CALLBACK (vik_window_new_window), NULL);
325 g_signal_connect (G_OBJECT (vw), "openwindow",
326 G_CALLBACK (open_window), NULL);
328 gtk_widget_show_all ( GTK_WIDGET(vw) );
330 if ( a_vik_get_restore_window_state() ) {
331 // These settings are applied after the show all as these options hide widgets
333 if ( a_settings_get_boolean ( VIK_SETTINGS_WIN_SIDEPANEL, &sidepanel ) )
335 gtk_widget_hide ( GTK_WIDGET(vw->viking_vlp) );
336 GtkWidget *check_box = gtk_ui_manager_get_widget ( vw->uim, "/ui/MainMenu/View/SetShow/ViewSidePanel" );
337 gtk_check_menu_item_set_active ( GTK_CHECK_MENU_ITEM(check_box), FALSE );
341 if ( a_settings_get_boolean ( VIK_SETTINGS_WIN_STATUSBAR, &statusbar ) )
343 gtk_widget_hide ( GTK_WIDGET(vw->viking_vs) );
344 GtkWidget *check_box = gtk_ui_manager_get_widget ( vw->uim, "/ui/MainMenu/View/SetShow/ViewStatusBar" );
345 gtk_check_menu_item_set_active ( GTK_CHECK_MENU_ITEM(check_box), FALSE );
349 if ( a_settings_get_boolean ( VIK_SETTINGS_WIN_TOOLBAR, &toolbar ) )
351 gtk_widget_hide ( GTK_WIDGET(vw->toolbar) );
352 GtkWidget *check_box = gtk_ui_manager_get_widget ( vw->uim, "/ui/MainMenu/View/SetShow/ViewToolBar" );
353 gtk_check_menu_item_set_active ( GTK_CHECK_MENU_ITEM(check_box), FALSE );
357 if ( a_settings_get_boolean ( VIK_SETTINGS_WIN_MENUBAR, &menubar ) )
359 gtk_widget_hide ( gtk_ui_manager_get_widget ( vw->uim, "/ui/MainMenu" ) );
360 GtkWidget *check_box = gtk_ui_manager_get_widget ( vw->uim, "/ui/MainMenu/View/SetShow/ViewMainMenu" );
361 gtk_check_menu_item_set_active ( GTK_CHECK_MENU_ITEM(check_box), FALSE );
372 * determine_location_thread:
373 * @vw: The window that will get updated
374 * @threaddata: Data used by our background thread mechanism
376 * Use the features in vikgoto to determine where we are
377 * Then set up the viewport:
378 * 1. To goto the location
379 * 2. Set an appropriate level zoom for the location type
380 * 3. Some statusbar message feedback
382 static int determine_location_thread ( VikWindow *vw, gpointer threaddata )
386 gint ans = a_vik_goto_where_am_i ( vw->viking_vvp, &ll, &name );
388 int result = a_background_thread_progress ( threaddata, 1.0 );
390 vik_window_statusbar_update ( vw, _("Location lookup aborted"), VIK_STATUSBAR_INFO );
391 return -1; /* Abort thread */
399 // Position found with city precision - so zoom out more
402 else if ( ans == 3 ) {
403 // Position found via country name search - so zoom wayyyy out
407 vik_viewport_set_zoom ( vw->viking_vvp, zoom );
408 vik_viewport_set_center_latlon ( vw->viking_vvp, &ll, FALSE );
410 gchar *message = g_strdup_printf ( _("Location found: %s"), name );
411 vik_window_statusbar_update ( vw, message, VIK_STATUSBAR_INFO );
415 // Signal to redraw from the background
416 vik_layers_panel_emit_update ( vw->viking_vlp );
419 vik_window_statusbar_update ( vw, _("Unable to determine location"), VIK_STATUSBAR_INFO );
425 * Steps to be taken once initial loading has completed
427 void vik_window_new_window_finish ( VikWindow *vw )
429 // Don't add a map if we've loaded a Viking file already
433 if ( a_vik_get_startup_method ( ) == VIK_STARTUP_METHOD_SPECIFIED_FILE ) {
434 vik_window_open_file ( vw, a_vik_get_startup_file(), TRUE );
439 // Maybe add a default map layer
440 if ( a_vik_get_add_default_map_layer () ) {
441 VikMapsLayer *vml = VIK_MAPS_LAYER ( vik_layer_create(VIK_LAYER_MAPS, vw->viking_vvp, FALSE) );
442 vik_layer_rename ( VIK_LAYER(vml), _("Default Map") );
443 vik_aggregate_layer_add_layer ( vik_layers_panel_get_top_layer(vw->viking_vlp), VIK_LAYER(vml), TRUE );
448 // If not loaded any file, maybe try the location lookup
449 if ( vw->loaded_type == LOAD_TYPE_READ_FAILURE ) {
450 if ( a_vik_get_startup_method ( ) == VIK_STARTUP_METHOD_AUTO_LOCATION ) {
452 vik_statusbar_set_message ( vw->viking_vs, VIK_STATUSBAR_INFO, _("Trying to determine location...") );
454 a_background_thread ( GTK_WINDOW(vw),
455 _("Determining location"),
456 (vik_thr_func) determine_location_thread,
465 static void open_window ( VikWindow *vw, GSList *files )
467 gboolean change_fn = (g_slist_length(files) == 1); /* only change fn if one file */
468 GSList *cur_file = files;
470 // Only open a new window if a viking file
471 gchar *file_name = cur_file->data;
472 if (vw != NULL && vw->filename && check_file_magic_vik ( file_name ) ) {
473 VikWindow *newvw = vik_window_new_window ();
475 vik_window_open_file ( newvw, file_name, TRUE );
478 vik_window_open_file ( vw, file_name, change_fn );
481 cur_file = g_slist_next (cur_file);
483 g_slist_free (files);
487 void vik_window_selected_layer(VikWindow *vw, VikLayer *vl)
489 int i, j, tool_count;
490 VikLayerInterface *layer_interface;
492 if (!vw->action_group) return;
494 for (i=0; i<VIK_LAYER_NUM_TYPES; i++) {
496 layer_interface = vik_layer_get_interface(i);
497 tool_count = layer_interface->tools_count;
499 for (j = 0; j < tool_count; j++) {
500 action = gtk_action_group_get_action(vw->action_group,
501 layer_interface->tools[j].radioActionEntry.name);
502 g_object_set(action, "sensitive", i == vl->type, NULL);
507 static void window_finalize ( GObject *gob )
509 VikWindow *vw = VIK_WINDOW(gob);
510 g_return_if_fail ( vw != NULL );
512 a_background_remove_window ( vw );
514 window_list = g_slist_remove ( window_list, vw );
516 gdk_cursor_unref ( vw->busy_cursor );
518 for (tt = 0; tt < vw->vt->n_tools; tt++ )
519 if ( vw->vt->tools[tt].ti.destroy )
520 vw->vt->tools[tt].ti.destroy ( vw->vt->tools[tt].state );
521 g_free ( vw->vt->tools );
524 G_OBJECT_CLASS(parent_class)->finalize(gob);
528 static void vik_window_class_init ( VikWindowClass *klass )
531 GObjectClass *object_class;
533 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);
534 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);
536 object_class = G_OBJECT_CLASS (klass);
538 object_class->finalize = window_finalize;
540 parent_class = g_type_class_peek_parent (klass);
544 static void zoom_changed (GtkMenuShell *menushell,
547 VikWindow *vw = VIK_WINDOW (user_data);
549 GtkWidget *aw = gtk_menu_get_active ( GTK_MENU (menushell) );
550 gint active = GPOINTER_TO_INT(g_object_get_data ( G_OBJECT (aw), "position" ));
552 gdouble zoom_request = pow (2, active-2 );
554 // But has it really changed?
555 gdouble current_zoom = vik_viewport_get_zoom ( vw->viking_vvp );
556 if ( current_zoom != 0.0 && zoom_request != current_zoom ) {
557 vik_viewport_set_zoom ( vw->viking_vvp, zoom_request );
558 // Force drawing update
564 * @mpp: The initial zoom level
566 static GtkWidget *create_zoom_menu_all_levels ( gdouble mpp )
568 GtkWidget *menu = gtk_menu_new ();
569 char *itemLabels[] = { "0.25", "0.5", "1", "2", "4", "8", "16", "32", "64", "128", "256", "512", "1024", "2048", "4096", "8192", "16384", "32768" };
572 for (i = 0 ; i < G_N_ELEMENTS(itemLabels) ; i++)
574 GtkWidget *item = gtk_menu_item_new_with_label (itemLabels[i]);
575 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
576 gtk_widget_show (item);
577 g_object_set_data (G_OBJECT (item), "position", GINT_TO_POINTER(i));
580 gint active = 2 + round ( log (mpp) / log (2) );
581 // Ensure value derived from mpp is in bounds of the menu
582 if ( active >= G_N_ELEMENTS(itemLabels) )
583 active = G_N_ELEMENTS(itemLabels) - 1;
586 gtk_menu_set_active ( GTK_MENU(menu), active );
591 static GtkWidget *create_zoom_combo_all_levels ()
593 GtkWidget *combo = vik_combo_box_text_new();
594 vik_combo_box_text_append ( combo, "0.25");
595 vik_combo_box_text_append ( combo, "0.5");
596 vik_combo_box_text_append ( combo, "1");
597 vik_combo_box_text_append ( combo, "2");
598 vik_combo_box_text_append ( combo, "4");
599 vik_combo_box_text_append ( combo, "8");
600 vik_combo_box_text_append ( combo, "16");
601 vik_combo_box_text_append ( combo, "32");
602 vik_combo_box_text_append ( combo, "64");
603 vik_combo_box_text_append ( combo, "128");
604 vik_combo_box_text_append ( combo, "256");
605 vik_combo_box_text_append ( combo, "512");
606 vik_combo_box_text_append ( combo, "1024");
607 vik_combo_box_text_append ( combo, "2048");
608 vik_combo_box_text_append ( combo, "4096");
609 vik_combo_box_text_append ( combo, "8192");
610 vik_combo_box_text_append ( combo, "16384");
611 vik_combo_box_text_append ( combo, "32768");
613 gtk_widget_set_tooltip_text (combo, _("Select zoom level"));
617 static gint zoom_popup_handler (GtkWidget *widget)
621 g_return_val_if_fail (widget != NULL, FALSE);
622 g_return_val_if_fail (GTK_IS_MENU (widget), FALSE);
624 /* The "widget" is the menu that was supplied when
625 * g_signal_connect_swapped() was called.
627 menu = GTK_MENU (widget);
629 gtk_menu_popup (menu, NULL, NULL, NULL, NULL,
630 1, gtk_get_current_event_time());
638 static void drag_data_received_cb ( GtkWidget *widget,
639 GdkDragContext *context,
642 GtkSelectionData *selection_data,
647 gboolean success = FALSE;
649 if ( (selection_data != NULL) && (gtk_selection_data_get_length(selection_data) > 0) ) {
650 switch (target_type) {
652 gchar *str = (gchar*)gtk_selection_data_get_data(selection_data);
653 g_debug ("drag received string:%s \n", str);
655 // Convert string into GSList of individual entries for use with our open signal
656 gchar **entries = g_strsplit(str, "\r\n", 0);
657 GSList *filenames = NULL;
658 gint entry_runner = 0;
659 gchar *entry = entries[entry_runner];
661 if ( g_strcmp0 ( entry, "" ) ) {
662 // Drag+Drop gives URIs. And so in particular, %20 in place of spaces in filenames
663 // thus need to convert the text into a plain string
664 gchar *filename = g_filename_from_uri ( entry, NULL, NULL );
666 filenames = g_slist_append ( filenames, filename );
669 entry = entries[entry_runner];
673 g_signal_emit ( G_OBJECT(VIK_WINDOW_FROM_WIDGET(widget)), window_signals[VW_OPENWINDOW_SIGNAL], 0, filenames );
674 // NB: GSList & contents are freed by main.open_window
683 gtk_drag_finish ( context, success, FALSE, time );
686 #define VIK_SETTINGS_WIN_MAX "window_maximized"
687 #define VIK_SETTINGS_WIN_FULLSCREEN "window_fullscreen"
688 #define VIK_SETTINGS_WIN_WIDTH "window_width"
689 #define VIK_SETTINGS_WIN_HEIGHT "window_height"
690 #define VIK_SETTINGS_WIN_SAVE_IMAGE_WIDTH "window_save_image_width"
691 #define VIK_SETTINGS_WIN_SAVE_IMAGE_HEIGHT "window_save_image_height"
692 #define VIK_SETTINGS_WIN_SAVE_IMAGE_PNG "window_save_image_as_png"
694 static void vik_window_init ( VikWindow *vw )
696 GtkWidget *main_vbox;
699 vw->action_group = NULL;
701 vw->viking_vvp = vik_viewport_new();
702 vw->viking_vlp = vik_layers_panel_new();
703 vik_layers_panel_set_viewport ( vw->viking_vlp, vw->viking_vvp );
704 vw->viking_vs = vik_statusbar_new();
706 vw->vt = toolbox_create(vw);
707 window_create_ui(vw);
708 window_set_filename (vw, NULL);
709 vw->toolbar = GTK_TOOLBAR(gtk_ui_manager_get_widget (vw->uim, "/MainToolbar"));
711 vw->busy_cursor = gdk_cursor_new ( GDK_WATCH );
713 // Set the default tool
714 gtk_action_activate ( gtk_action_group_get_action ( vw->action_group, "Pan" ) );
717 vw->loaded_type = LOAD_TYPE_READ_FAILURE; //AKA none
718 vw->modified = FALSE;
719 vw->only_updating_coord_mode_ui = FALSE;
721 vw->pan_move = FALSE;
722 vw->pan_x = vw->pan_y = -1;
724 gint draw_image_width;
725 if ( a_settings_get_integer ( VIK_SETTINGS_WIN_SAVE_IMAGE_WIDTH, &draw_image_width ) )
726 vw->draw_image_width = draw_image_width;
728 vw->draw_image_width = DRAW_IMAGE_DEFAULT_WIDTH;
729 gint draw_image_height;
730 if ( a_settings_get_integer ( VIK_SETTINGS_WIN_SAVE_IMAGE_HEIGHT, &draw_image_height ) )
731 vw->draw_image_height = draw_image_height;
733 vw->draw_image_height = DRAW_IMAGE_DEFAULT_HEIGHT;
734 gboolean draw_image_save_as_png;
735 if ( a_settings_get_boolean ( VIK_SETTINGS_WIN_SAVE_IMAGE_PNG, &draw_image_save_as_png ) )
736 vw->draw_image_save_as_png = draw_image_save_as_png;
738 vw->draw_image_save_as_png = DRAW_IMAGE_DEFAULT_SAVE_AS_PNG;
740 main_vbox = gtk_vbox_new(FALSE, 1);
741 gtk_container_add (GTK_CONTAINER (vw), main_vbox);
743 gtk_box_pack_start (GTK_BOX(main_vbox), gtk_ui_manager_get_widget (vw->uim, "/MainMenu"), FALSE, TRUE, 0);
744 gtk_box_pack_start (GTK_BOX(main_vbox), GTK_WIDGET(vw->toolbar), FALSE, TRUE, 0);
745 gtk_toolbar_set_icon_size (vw->toolbar, GTK_ICON_SIZE_SMALL_TOOLBAR);
746 gtk_toolbar_set_style (vw->toolbar, GTK_TOOLBAR_ICONS);
748 vik_ext_tool_datasources_add_menu_items ( vw, vw->uim );
750 GtkWidget * zoom_levels = gtk_ui_manager_get_widget (vw->uim, "/MainMenu/View/SetZoom");
751 GtkWidget * zoom_levels_menu = create_zoom_menu_all_levels ( vik_viewport_get_zoom(vw->viking_vvp) );
752 gtk_menu_item_set_submenu (GTK_MENU_ITEM (zoom_levels), zoom_levels_menu);
753 g_signal_connect ( G_OBJECT(zoom_levels_menu), "selection-done", G_CALLBACK(zoom_changed), vw);
754 g_signal_connect_swapped ( G_OBJECT(vw->viking_vs), "clicked", G_CALLBACK(zoom_popup_handler), zoom_levels_menu );
756 g_signal_connect (G_OBJECT (vw), "delete_event", G_CALLBACK (delete_event), NULL);
759 g_signal_connect_swapped (G_OBJECT(vw->viking_vvp), "updated_center", G_CALLBACK(center_changed_cb), vw);
761 g_signal_connect_swapped (G_OBJECT(vw->viking_vvp), "expose_event", G_CALLBACK(draw_sync), vw);
762 g_signal_connect_swapped (G_OBJECT(vw->viking_vvp), "configure_event", G_CALLBACK(window_configure_event), vw);
763 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 );
764 g_signal_connect_swapped (G_OBJECT(vw->viking_vvp), "scroll_event", G_CALLBACK(draw_scroll), vw);
765 g_signal_connect_swapped (G_OBJECT(vw->viking_vvp), "button_press_event", G_CALLBACK(draw_click), vw);
766 g_signal_connect_swapped (G_OBJECT(vw->viking_vvp), "button_release_event", G_CALLBACK(draw_release), vw);
767 g_signal_connect_swapped (G_OBJECT(vw->viking_vvp), "motion_notify_event", G_CALLBACK(draw_mouse_motion), vw);
769 g_signal_connect_swapped (G_OBJECT(vw->viking_vlp), "update", G_CALLBACK(draw_update), vw);
770 g_signal_connect_swapped (G_OBJECT(vw->viking_vlp), "delete_layer", G_CALLBACK(vik_window_clear_highlight), vw);
772 // Allow key presses to be processed anywhere
773 g_signal_connect_swapped (G_OBJECT (vw), "key_press_event", G_CALLBACK (key_press_event), vw);
775 // Set initial button sensitivity
776 center_changed_cb ( vw );
778 hpaned = gtk_hpaned_new ();
779 gtk_paned_pack1 ( GTK_PANED(hpaned), GTK_WIDGET (vw->viking_vlp), FALSE, FALSE );
780 gtk_paned_pack2 ( GTK_PANED(hpaned), GTK_WIDGET (vw->viking_vvp), TRUE, TRUE );
782 /* This packs the button into the window (a gtk container). */
783 gtk_box_pack_start (GTK_BOX(main_vbox), hpaned, TRUE, TRUE, 0);
785 gtk_box_pack_end (GTK_BOX(main_vbox), GTK_WIDGET(vw->viking_vs), FALSE, TRUE, 0);
787 a_background_add_window ( vw );
789 window_list = g_slist_prepend ( window_list, vw);
791 gint height = VIKING_WINDOW_HEIGHT;
792 gint width = VIKING_WINDOW_WIDTH;
794 if ( a_vik_get_restore_window_state() ) {
795 if ( a_settings_get_integer ( VIK_SETTINGS_WIN_HEIGHT, &height ) ) {
796 // Enforce a basic minimum size
801 // No setting - so use default
802 height = VIKING_WINDOW_HEIGHT;
804 if ( a_settings_get_integer ( VIK_SETTINGS_WIN_WIDTH, &width ) ) {
805 // Enforce a basic minimum size
810 // No setting - so use default
811 width = VIKING_WINDOW_WIDTH;
814 if ( a_settings_get_boolean ( VIK_SETTINGS_WIN_MAX, &maxed ) )
816 gtk_window_maximize ( GTK_WINDOW(vw) );
819 if ( a_settings_get_boolean ( VIK_SETTINGS_WIN_FULLSCREEN, &full ) ) {
821 gtk_window_fullscreen ( GTK_WINDOW(vw) );
822 GtkWidget *check_box = gtk_ui_manager_get_widget ( vw->uim, "/ui/MainMenu/View/FullScreen" );
823 gtk_check_menu_item_set_active ( GTK_CHECK_MENU_ITEM(check_box), TRUE );
828 gtk_window_set_default_size ( GTK_WINDOW(vw), width, height );
832 vw->save_img_dia = NULL;
833 vw->save_img_dir_dia = NULL;
835 // Only accept Drag and Drop of files onto the viewport
836 gtk_drag_dest_set ( GTK_WIDGET(vw->viking_vvp), GTK_DEST_DEFAULT_ALL, NULL, 0, GDK_ACTION_COPY );
837 gtk_drag_dest_add_uri_targets ( GTK_WIDGET(vw->viking_vvp) );
838 g_signal_connect ( GTK_WIDGET(vw->viking_vvp), "drag-data-received", G_CALLBACK(drag_data_received_cb), NULL );
840 // Store the thread value so comparisons can be made to determine the gdk update method
841 // Hopefully we are storing the main thread value here :)
842 // [ATM any window initialization is always be performed by the main thread]
843 vw->thread = g_thread_self();
846 static VikWindow *window_new ()
848 return VIK_WINDOW ( g_object_new ( VIK_WINDOW_TYPE, NULL ) );
852 * Update the displayed map
853 * Only update the top most visible map layer
854 * ATM this assumes (as per defaults) the top most map has full alpha setting
855 * such that other other maps even though they may be active will not be seen
856 * It's more complicated to work out which maps are actually visible due to alpha settings
857 * and overkill for this simple refresh method.
859 static void simple_map_update ( VikWindow *vw, gboolean only_new )
861 // Find the most relevent single map layer to operate on
862 VikLayer *vl = vik_aggregate_layer_get_top_visible_layer_of_type (vik_layers_panel_get_top_layer(vw->viking_vlp), VIK_LAYER_MAPS);
864 vik_maps_layer_download ( VIK_MAPS_LAYER(vl), vw->viking_vvp, only_new );
868 * This is the global key press handler
869 * Global shortcuts are available at any time and hence are not restricted to when a certain tool is enabled
871 static gboolean key_press_event( VikWindow *vw, GdkEventKey *event, gpointer data )
873 // The keys handled here are not in the menuing system for a couple of reasons:
874 // . Keeps the menu size compact (alebit at expense of discoverably)
875 // . Allows differing key bindings to perform the same actions
877 // First decide if key events are related to the maps layer
878 gboolean map_download = FALSE;
879 gboolean map_download_only_new = TRUE; // Only new or reload
881 GdkModifierType modifiers = gtk_accelerator_get_default_mod_mask();
883 // Standard 'Refresh' keys: F5 or Ctrl+r
884 // Note 'F5' is actually handled via draw_refresh_cb() later on
885 // (not 'R' it's 'r' notice the case difference!!)
886 if ( event->keyval == GDK_r && (event->state & modifiers) == GDK_CONTROL_MASK ) {
888 map_download_only_new = TRUE;
890 // Full cache reload with Ctrl+F5 or Ctrl+Shift+r [This is not in the menu system]
891 // Note the use of uppercase R here since shift key has been pressed
892 else if ( (event->keyval == GDK_F5 && (event->state & modifiers) == GDK_CONTROL_MASK ) ||
893 ( event->keyval == GDK_R && (event->state & modifiers) == (GDK_CONTROL_MASK + GDK_SHIFT_MASK) ) ) {
895 map_download_only_new = FALSE;
898 if ( map_download ) {
899 simple_map_update ( vw, map_download_only_new );
902 VikLayer *vl = vik_layers_panel_get_selected ( vw->viking_vlp );
903 if (vl && vw->vt->active_tool != -1 && vw->vt->tools[vw->vt->active_tool].ti.key_press ) {
904 gint ltype = vw->vt->tools[vw->vt->active_tool].layer_type;
905 if ( vl && ltype == vl->type )
906 return vw->vt->tools[vw->vt->active_tool].ti.key_press(vl, event, vw->vt->tools[vw->vt->active_tool].state);
909 // Ensure called only on window tools (i.e. not on any of the Layer tools since the layer is NULL)
910 if ( vw->current_tool < TOOL_LAYER ) {
911 // No layer - but enable window tool keypress processing - these should be able to handle a NULL layer
912 if ( vw->vt->tools[vw->vt->active_tool].ti.key_press ) {
913 return vw->vt->tools[vw->vt->active_tool].ti.key_press ( vl, event, vw->vt->tools[vw->vt->active_tool].state );
917 /* Restore Main Menu via Escape key if the user has hidden it */
918 /* This key is more likely to be used as they may not remember the function key */
919 if ( event->keyval == GDK_Escape ) {
920 GtkWidget *check_box = gtk_ui_manager_get_widget ( vw->uim, "/ui/MainMenu/View/SetShow/ViewMainMenu" );
922 gboolean state = gtk_check_menu_item_get_active ( GTK_CHECK_MENU_ITEM(check_box) );
924 gtk_widget_show ( gtk_ui_manager_get_widget ( vw->uim, "/ui/MainMenu" ) );
925 gtk_check_menu_item_set_active ( GTK_CHECK_MENU_ITEM(check_box), TRUE );
926 return TRUE; /* handled keypress */
931 return FALSE; /* don't handle the keypress */
934 static gboolean delete_event( VikWindow *vw )
936 #ifdef VIKING_PROMPT_IF_MODIFIED
943 dia = GTK_DIALOG ( gtk_message_dialog_new ( GTK_WINDOW(vw), GTK_DIALOG_DESTROY_WITH_PARENT, GTK_MESSAGE_QUESTION, GTK_BUTTONS_NONE,
944 _("Do you want to save the changes you made to the document \"%s\"?\n"
946 "Your changes will be lost if you don't save them."),
947 window_get_filename ( vw ) ) );
948 gtk_dialog_add_buttons ( dia, _("Don't Save"), GTK_RESPONSE_NO, GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, GTK_STOCK_SAVE, GTK_RESPONSE_YES, NULL );
949 switch ( gtk_dialog_run ( dia ) )
951 case GTK_RESPONSE_NO: gtk_widget_destroy ( GTK_WIDGET(dia) ); return FALSE;
952 case GTK_RESPONSE_CANCEL: gtk_widget_destroy ( GTK_WIDGET(dia) ); return TRUE;
953 default: gtk_widget_destroy ( GTK_WIDGET(dia) ); return ! save_file(NULL, vw);
957 if ( window_count == 1 ) {
958 // On the final window close - save latest state - if it's wanted...
959 if ( a_vik_get_restore_window_state() ) {
960 gint state = gdk_window_get_state ( GTK_WIDGET(vw)->window );
961 gboolean state_max = state & GDK_WINDOW_STATE_MAXIMIZED;
962 a_settings_set_boolean ( VIK_SETTINGS_WIN_MAX, state_max );
964 gboolean state_fullscreen = state & GDK_WINDOW_STATE_FULLSCREEN;
965 a_settings_set_boolean ( VIK_SETTINGS_WIN_FULLSCREEN, state_fullscreen );
967 a_settings_set_boolean ( VIK_SETTINGS_WIN_SIDEPANEL, GTK_WIDGET_VISIBLE (GTK_WIDGET(vw->viking_vlp)) );
969 a_settings_set_boolean ( VIK_SETTINGS_WIN_STATUSBAR, GTK_WIDGET_VISIBLE (GTK_WIDGET(vw->viking_vs)) );
971 a_settings_set_boolean ( VIK_SETTINGS_WIN_TOOLBAR, GTK_WIDGET_VISIBLE (GTK_WIDGET(vw->toolbar)) );
973 // If supersized - no need to save the enlarged width+height values
974 if ( ! (state_fullscreen || state_max) ) {
976 gtk_window_get_size ( GTK_WINDOW (vw), &width, &height );
977 a_settings_set_integer ( VIK_SETTINGS_WIN_WIDTH, width );
978 a_settings_set_integer ( VIK_SETTINGS_WIN_HEIGHT, height );
982 a_settings_set_integer ( VIK_SETTINGS_WIN_SAVE_IMAGE_WIDTH, vw->draw_image_width );
983 a_settings_set_integer ( VIK_SETTINGS_WIN_SAVE_IMAGE_HEIGHT, vw->draw_image_height );
984 a_settings_set_boolean ( VIK_SETTINGS_WIN_SAVE_IMAGE_PNG, vw->draw_image_save_as_png );
991 static void newwindow_cb ( GtkAction *a, VikWindow *vw )
993 g_signal_emit ( G_OBJECT(vw), window_signals[VW_NEWWINDOW_SIGNAL], 0 );
996 static void draw_update ( VikWindow *vw )
1002 static void draw_sync ( VikWindow *vw )
1004 vik_viewport_sync(vw->viking_vvp);
1009 * Split the status update, as sometimes only need to update the tool part
1010 * also on initialization the zoom related stuff is not ready to be used
1012 static void draw_status_tool ( VikWindow *vw )
1014 if ( vw->current_tool == TOOL_LAYER )
1015 // Use tooltip rather than the internal name as the tooltip is i8n
1016 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 );
1018 vik_statusbar_set_message ( vw->viking_vs, VIK_STATUSBAR_TOOL, _(tool_names[vw->current_tool]) );
1021 static void draw_status ( VikWindow *vw )
1023 static gchar zoom_level[22];
1024 gdouble xmpp = vik_viewport_get_xmpp (vw->viking_vvp);
1025 gdouble ympp = vik_viewport_get_ympp(vw->viking_vvp);
1026 gchar *unit = vik_viewport_get_coord_mode(vw->viking_vvp) == VIK_COORD_UTM ? _("mpp") : _("pixelfact");
1028 g_snprintf ( zoom_level, 22, "%.3f/%.3f %s", xmpp, ympp, unit );
1030 if ( (int)xmpp - xmpp < 0.0 )
1031 g_snprintf ( zoom_level, 22, "%.3f %s", xmpp, unit );
1033 /* xmpp should be a whole number so don't show useless .000 bit */
1034 g_snprintf ( zoom_level, 22, "%d %s", (int)xmpp, unit );
1036 vik_statusbar_set_message ( vw->viking_vs, VIK_STATUSBAR_ZOOM, zoom_level );
1038 draw_status_tool ( vw );
1041 void vik_window_set_redraw_trigger(VikLayer *vl)
1043 VikWindow *vw = VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vl));
1048 static void window_configure_event ( VikWindow *vw )
1050 static int first = 1;
1053 // This is a hack to set the cursor corresponding to the first tool
1054 // FIXME find the correct way to initialize both tool and its cursor
1056 vw->viewport_cursor = (GdkCursor *)toolbox_get_cursor(vw->vt, "Pan");
1057 /* We set cursor, even if it is NULL: it resets to default */
1058 gdk_window_set_cursor ( gtk_widget_get_window(GTK_WIDGET(vw->viking_vvp)), vw->viewport_cursor );
1062 static void draw_redraw ( VikWindow *vw )
1064 VikCoord old_center = vw->trigger_center;
1065 vw->trigger_center = *(vik_viewport_get_center(vw->viking_vvp));
1066 VikLayer *new_trigger = vw->trigger;
1068 VikLayer *old_trigger = VIK_LAYER(vik_viewport_get_trigger(vw->viking_vvp));
1070 if ( ! new_trigger )
1071 ; /* do nothing -- have to redraw everything. */
1072 else if ( (old_trigger != new_trigger) || !vik_coord_equals(&old_center, &vw->trigger_center) || (new_trigger->type == VIK_LAYER_AGGREGATE) )
1073 vik_viewport_set_trigger ( vw->viking_vvp, new_trigger ); /* todo: set to half_drawn mode if new trigger is above old */
1075 vik_viewport_set_half_drawn ( vw->viking_vvp, TRUE );
1078 vik_viewport_clear ( vw->viking_vvp);
1079 // Main layer drawing
1080 vik_layers_panel_draw_all ( vw->viking_vlp );
1081 // Draw highlight (possibly again but ensures it is on top - especially for when tracks overlap)
1082 if ( vik_viewport_get_draw_highlight (vw->viking_vvp) ) {
1083 if ( vw->containing_vtl && (vw->selected_tracks || vw->selected_waypoints ) ) {
1084 vik_trw_layer_draw_highlight_items ( vw->containing_vtl, vw->selected_tracks, vw->selected_waypoints, vw->viking_vvp );
1086 else if ( vw->containing_vtl && (vw->selected_track || vw->selected_waypoint) ) {
1087 vik_trw_layer_draw_highlight_item ( vw->containing_vtl, vw->selected_track, vw->selected_waypoint, vw->viking_vvp );
1089 else if ( vw->selected_vtl ) {
1090 vik_trw_layer_draw_highlight ( vw->selected_vtl, vw->viking_vvp );
1093 // Other viewport decoration items on top if they are enabled/in use
1094 vik_viewport_draw_scale ( vw->viking_vvp );
1095 vik_viewport_draw_copyright ( vw->viking_vvp );
1096 vik_viewport_draw_centermark ( vw->viking_vvp );
1097 vik_viewport_draw_logo ( vw->viking_vvp );
1099 vik_viewport_set_half_drawn ( vw->viking_vvp, FALSE ); /* just in case. */
1102 gboolean draw_buf_done = TRUE;
1104 static gboolean draw_buf(gpointer data)
1106 gpointer *pass_along = data;
1107 gdk_threads_enter();
1108 gdk_draw_drawable (pass_along[0], pass_along[1],
1109 pass_along[2], 0, 0, 0, 0, -1, -1);
1110 draw_buf_done = TRUE;
1111 gdk_threads_leave();
1116 /* Mouse event handlers ************************************************************************/
1118 static void vik_window_pan_click (VikWindow *vw, GdkEventButton *event)
1120 /* set panning origin */
1121 vw->pan_move = FALSE;
1122 vw->pan_x = (gint) event->x;
1123 vw->pan_y = (gint) event->y;
1126 static void draw_click (VikWindow *vw, GdkEventButton *event)
1128 gtk_widget_grab_focus ( GTK_WIDGET(vw->viking_vvp) );
1130 /* middle button pressed. we reserve all middle button and scroll events
1131 * for panning and zooming; tools only get left/right/movement
1133 if ( event->button == 2) {
1134 if ( vw->vt->tools[vw->vt->active_tool].ti.pan_handler )
1135 // Tool still may need to do something (such as disable something)
1136 toolbox_click(vw->vt, event);
1137 vik_window_pan_click ( vw, event );
1140 toolbox_click(vw->vt, event);
1144 static void vik_window_pan_move (VikWindow *vw, GdkEventMotion *event)
1146 if ( vw->pan_x != -1 ) {
1147 vik_viewport_set_center_screen ( vw->viking_vvp, vik_viewport_get_width(vw->viking_vvp)/2 - event->x + vw->pan_x,
1148 vik_viewport_get_height(vw->viking_vvp)/2 - event->y + vw->pan_y );
1149 vw->pan_move = TRUE;
1150 vw->pan_x = event->x;
1151 vw->pan_y = event->y;
1156 static void draw_mouse_motion (VikWindow *vw, GdkEventMotion *event)
1158 static VikCoord coord;
1159 static struct UTM utm;
1160 static struct LatLon ll;
1161 #define BUFFER_SIZE 50
1162 static char pointer_buf[BUFFER_SIZE];
1163 gchar *lat = NULL, *lon = NULL;
1166 VikDemInterpol interpol_method;
1168 /* This is a hack, but work far the best, at least for single pointer systems.
1169 * See http://bugzilla.gnome.org/show_bug.cgi?id=587714 for more. */
1171 gdk_window_get_pointer (event->window, &x, &y, NULL);
1175 toolbox_move(vw->vt, event);
1177 vik_viewport_screen_to_coord ( vw->viking_vvp, event->x, event->y, &coord );
1178 vik_coord_to_utm ( &coord, &utm );
1180 if ( vik_viewport_get_drawmode ( vw->viking_vvp ) == VIK_VIEWPORT_DRAWMODE_UTM ) {
1181 // Reuse lat for the first part (Zone + N or S, and lon for the second part (easting and northing) of a UTM format:
1182 // ZONE[N|S] EASTING NORTHING
1183 lat = g_malloc(4*sizeof(gchar));
1184 // NB zone is stored in a char but is an actual number
1185 g_snprintf (lat, 4, "%d%c", utm.zone, utm.letter);
1186 lon = g_malloc(16*sizeof(gchar));
1187 g_snprintf (lon, 16, "%d %d", (gint)utm.easting, (gint)utm.northing);
1190 a_coords_utm_to_latlon ( &utm, &ll );
1191 a_coords_latlon_to_string ( &ll, &lat, &lon );
1194 /* Change interpolate method according to scale */
1195 zoom = vik_viewport_get_zoom(vw->viking_vvp);
1197 interpol_method = VIK_DEM_INTERPOL_NONE;
1198 else if (zoom >= 1.0)
1199 interpol_method = VIK_DEM_INTERPOL_SIMPLE;
1201 interpol_method = VIK_DEM_INTERPOL_BEST;
1202 if ((alt = a_dems_get_elev_by_coord(&coord, interpol_method)) != VIK_DEM_INVALID_ELEVATION) {
1203 if ( a_vik_get_units_height () == VIK_UNITS_HEIGHT_METRES )
1204 g_snprintf ( pointer_buf, BUFFER_SIZE, _("%s %s %dm"), lat, lon, alt );
1206 g_snprintf ( pointer_buf, BUFFER_SIZE, _("%s %s %dft"), lat, lon, (int)VIK_METERS_TO_FEET(alt) );
1209 g_snprintf ( pointer_buf, BUFFER_SIZE, _("%s %s"), lat, lon );
1214 vik_statusbar_set_message ( vw->viking_vs, VIK_STATUSBAR_POSITION, pointer_buf );
1216 vik_window_pan_move ( vw, event );
1218 /* This is recommended by the GTK+ documentation, but does not work properly.
1219 * Use deprecated way until GTK+ gets a solution for correct motion hint handling:
1220 * http://bugzilla.gnome.org/show_bug.cgi?id=587714
1222 /* gdk_event_request_motions ( event ); */
1225 static void vik_window_pan_release ( VikWindow *vw, GdkEventButton *event )
1227 if ( vw->pan_move == FALSE )
1228 vik_viewport_set_center_screen ( vw->viking_vvp, vw->pan_x, vw->pan_y );
1230 vik_viewport_set_center_screen ( vw->viking_vvp, vik_viewport_get_width(vw->viking_vvp)/2 - event->x + vw->pan_x,
1231 vik_viewport_get_height(vw->viking_vvp)/2 - event->y + vw->pan_y );
1232 vw->pan_move = FALSE;
1233 vw->pan_x = vw->pan_y = -1;
1237 static void draw_release ( VikWindow *vw, GdkEventButton *event )
1239 gtk_widget_grab_focus ( GTK_WIDGET(vw->viking_vvp) );
1241 if ( event->button == 2 ) { /* move / pan */
1242 if ( vw->vt->tools[vw->vt->active_tool].ti.pan_handler )
1243 // Tool still may need to do something (such as reenable something)
1244 toolbox_release(vw->vt, event);
1245 vik_window_pan_release ( vw, event );
1248 toolbox_release(vw->vt, event);
1252 static void draw_scroll (VikWindow *vw, GdkEventScroll *event)
1254 guint modifiers = event->state & (GDK_SHIFT_MASK | GDK_CONTROL_MASK);
1255 if ( modifiers == GDK_CONTROL_MASK ) {
1256 /* control == pan up & down */
1257 if ( event->direction == GDK_SCROLL_UP )
1258 vik_viewport_set_center_screen ( vw->viking_vvp, vik_viewport_get_width(vw->viking_vvp)/2, vik_viewport_get_height(vw->viking_vvp)/3 );
1260 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 );
1261 } else if ( modifiers == GDK_SHIFT_MASK ) {
1262 /* shift == pan left & right */
1263 if ( event->direction == GDK_SCROLL_UP )
1264 vik_viewport_set_center_screen ( vw->viking_vvp, vik_viewport_get_width(vw->viking_vvp)/3, vik_viewport_get_height(vw->viking_vvp)/2 );
1266 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 );
1267 } else if ( modifiers == (GDK_CONTROL_MASK | GDK_SHIFT_MASK) ) {
1268 // This zoom is on the center position
1269 if ( event->direction == GDK_SCROLL_UP )
1270 vik_viewport_zoom_in (vw->viking_vvp);
1272 vik_viewport_zoom_out (vw->viking_vvp);
1274 /* make sure mouse is still over the same point on the map when we zoom */
1277 gint center_x = vik_viewport_get_width ( vw->viking_vvp ) / 2;
1278 gint center_y = vik_viewport_get_height ( vw->viking_vvp ) / 2;
1279 vik_viewport_screen_to_coord ( vw->viking_vvp, event->x, event->y, &coord );
1280 if ( event->direction == GDK_SCROLL_UP )
1281 vik_viewport_zoom_in (vw->viking_vvp);
1283 vik_viewport_zoom_out(vw->viking_vvp);
1284 vik_viewport_coord_to_screen ( vw->viking_vvp, &coord, &x, &y );
1285 vik_viewport_set_center_screen ( vw->viking_vvp, center_x + (x - event->x),
1286 center_y + (y - event->y) );
1294 /********************************************************************************
1296 ********************************************************************************/
1297 static void draw_ruler(VikViewport *vvp, GdkDrawable *d, GdkGC *gc, gint x1, gint y1, gint x2, gint y2, gdouble distance)
1301 GdkGC *labgc = vik_viewport_new_gc ( vvp, "#cccccc", 1);
1302 GdkGC *thickgc = gdk_gc_new(d);
1304 gdouble len = sqrt((x1-x2)*(x1-x2) + (y1-y2)*(y1-y2));
1305 gdouble dx = (x2-x1)/len*10;
1306 gdouble dy = (y2-y1)/len*10;
1307 gdouble c = cos(DEG2RAD(15.0));
1308 gdouble s = sin(DEG2RAD(15.0));
1310 gdouble baseangle = 0;
1313 /* draw line with arrow ends */
1315 gint tmp_x1=x1, tmp_y1=y1, tmp_x2=x2, tmp_y2=y2;
1316 a_viewport_clip_line(&tmp_x1, &tmp_y1, &tmp_x2, &tmp_y2);
1317 gdk_draw_line(d, gc, tmp_x1, tmp_y1, tmp_x2, tmp_y2);
1320 a_viewport_clip_line(&x1, &y1, &x2, &y2);
1321 gdk_draw_line(d, gc, x1, y1, x2, y2);
1323 gdk_draw_line(d, gc, x1 - dy, y1 + dx, x1 + dy, y1 - dx);
1324 gdk_draw_line(d, gc, x2 - dy, y2 + dx, x2 + dy, y2 - dx);
1325 gdk_draw_line(d, gc, x2, y2, x2 - (dx * c + dy * s), y2 - (dy * c - dx * s));
1326 gdk_draw_line(d, gc, x2, y2, x2 - (dx * c - dy * s), y2 - (dy * c + dx * s));
1327 gdk_draw_line(d, gc, x1, y1, x1 + (dx * c + dy * s), y1 + (dy * c - dx * s));
1328 gdk_draw_line(d, gc, x1, y1, x1 + (dx * c - dy * s), y1 + (dy * c + dx * s));
1334 vik_viewport_compute_bearing ( vvp, x1, y1, x2, y2, &angle, &baseangle );
1338 gdk_gc_copy(thickgc, gc);
1339 gdk_gc_set_line_attributes(thickgc, CW, GDK_LINE_SOLID, GDK_CAP_BUTT, GDK_JOIN_MITER);
1340 gdk_color_parse("#2255cc", &color);
1341 gdk_gc_set_rgb_fg_color(thickgc, &color);
1343 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);
1346 gdk_gc_copy(thickgc, gc);
1347 gdk_gc_set_line_attributes(thickgc, 2, GDK_LINE_SOLID, GDK_CAP_BUTT, GDK_JOIN_MITER);
1348 for (i=0; i<180; i++) {
1349 c = cos(DEG2RAD(i)*2 + baseangle);
1350 s = sin(DEG2RAD(i)*2 + baseangle);
1353 gdk_draw_line (d, gc, x1 + CR*c, y1 + CR*s, x1 + (CR+CW)*c, y1 + (CR+CW)*s);
1355 gdouble ticksize = 2*CW;
1356 gdk_draw_line (d, thickgc, x1 + (CR-CW)*c, y1 + (CR-CW)*s, x1 + (CR+ticksize)*c, y1 + (CR+ticksize)*s);
1360 gdk_draw_arc (d, gc, FALSE, x1-CR, y1-CR, 2*CR, 2*CR, 0, 64*360);
1361 gdk_draw_arc (d, gc, FALSE, x1-CR-CW, y1-CR-CW, 2*(CR+CW), 2*(CR+CW), 0, 64*360);
1362 gdk_draw_arc (d, gc, FALSE, x1-CR+CW, y1-CR+CW, 2*(CR-CW), 2*(CR-CW), 0, 64*360);
1363 c = (CR+CW*2)*cos(baseangle);
1364 s = (CR+CW*2)*sin(baseangle);
1365 gdk_draw_line (d, gc, x1-c, y1-s, x1+c, y1+s);
1366 gdk_draw_line (d, gc, x1+s, y1-c, x1-s, y1+c);
1369 #define LABEL(x, y, w, h) { \
1370 gdk_draw_rectangle(d, labgc, TRUE, (x)-2, (y)-1, (w)+4, (h)+1); \
1371 gdk_draw_rectangle(d, gc, FALSE, (x)-2, (y)-1, (w)+4, (h)+1); \
1372 gdk_draw_layout(d, gc, (x), (y), pl); }
1374 gint wd, hd, xd, yd;
1375 gint wb, hb, xb, yb;
1377 pl = gtk_widget_create_pango_layout (GTK_WIDGET(vvp), NULL);
1378 pango_layout_set_font_description (pl, gtk_widget_get_style(GTK_WIDGET(vvp))->font_desc);
1379 pango_layout_set_text(pl, "N", -1);
1380 gdk_draw_layout(d, gc, x1-5, y1-CR-3*CW-8, pl);
1382 /* draw label with distance */
1383 vik_units_distance_t dist_units = a_vik_get_units_distance ();
1384 switch (dist_units) {
1385 case VIK_UNITS_DISTANCE_KILOMETRES:
1386 if (distance >= 1000 && distance < 100000) {
1387 g_sprintf(str, "%3.2f km", distance/1000.0);
1388 } else if (distance < 1000) {
1389 g_sprintf(str, "%d m", (int)distance);
1391 g_sprintf(str, "%d km", (int)distance/1000);
1394 case VIK_UNITS_DISTANCE_MILES:
1395 if (distance >= VIK_MILES_TO_METERS(1) && distance < VIK_MILES_TO_METERS(100)) {
1396 g_sprintf(str, "%3.2f miles", VIK_METERS_TO_MILES(distance));
1397 } else if (distance < VIK_MILES_TO_METERS(1)) {
1398 g_sprintf(str, "%d yards", (int)(distance*1.0936133));
1400 g_sprintf(str, "%d miles", (int)VIK_METERS_TO_MILES(distance));
1404 g_critical("Houston, we've had a problem. distance=%d", dist_units);
1407 pango_layout_set_text(pl, str, -1);
1409 pango_layout_get_pixel_size ( pl, &wd, &hd );
1411 xd = (x1+x2)/2 + dy;
1412 yd = (y1+y2)/2 - hd/2 - dx;
1414 xd = (x1+x2)/2 - dy;
1415 yd = (y1+y2)/2 - hd/2 + dx;
1418 if ( xd < -5 || yd < -5 || xd > vik_viewport_get_width(vvp)+5 || yd > vik_viewport_get_height(vvp)+5 ) {
1423 LABEL(xd, yd, wd, hd);
1425 /* draw label with bearing */
1426 g_sprintf(str, "%3.1f°", RAD2DEG(angle));
1427 pango_layout_set_text(pl, str, -1);
1428 pango_layout_get_pixel_size ( pl, &wb, &hb );
1429 xb = x1 + CR*cos(angle-M_PI_2);
1430 yb = y1 + CR*sin(angle-M_PI_2);
1432 if ( xb < -5 || yb < -5 || xb > vik_viewport_get_width(vvp)+5 || yb > vik_viewport_get_height(vvp)+5 ) {
1438 GdkRectangle r1 = {xd-2, yd-1, wd+4, hd+1}, r2 = {xb-2, yb-1, wb+4, hb+1};
1439 if (gdk_rectangle_intersect(&r1, &r2, &r2)) {
1443 LABEL(xb, yb, wb, hb);
1447 g_object_unref ( G_OBJECT ( pl ) );
1448 g_object_unref ( G_OBJECT ( labgc ) );
1449 g_object_unref ( G_OBJECT ( thickgc ) );
1455 gboolean has_oldcoord;
1457 } ruler_tool_state_t;
1459 static gpointer ruler_create (VikWindow *vw, VikViewport *vvp)
1461 ruler_tool_state_t *s = g_new(ruler_tool_state_t, 1);
1464 s->has_oldcoord = FALSE;
1468 static void ruler_destroy (ruler_tool_state_t *s)
1473 static VikLayerToolFuncStatus ruler_click (VikLayer *vl, GdkEventButton *event, ruler_tool_state_t *s)
1478 if ( event->button == 1 ) {
1479 gchar *lat=NULL, *lon=NULL;
1480 vik_viewport_screen_to_coord ( s->vvp, (gint) event->x, (gint) event->y, &coord );
1481 vik_coord_to_latlon ( &coord, &ll );
1482 a_coords_latlon_to_string ( &ll, &lat, &lon );
1483 if ( s->has_oldcoord ) {
1484 vik_units_distance_t dist_units = a_vik_get_units_distance ();
1485 switch (dist_units) {
1486 case VIK_UNITS_DISTANCE_KILOMETRES:
1487 temp = g_strdup_printf ( "%s %s DIFF %f meters", lat, lon, vik_coord_diff( &coord, &(s->oldcoord) ) );
1489 case VIK_UNITS_DISTANCE_MILES:
1490 temp = g_strdup_printf ( "%s %s DIFF %f miles", lat, lon, VIK_METERS_TO_MILES(vik_coord_diff( &coord, &(s->oldcoord) )) );
1493 temp = g_strdup_printf ("Just to keep the compiler happy");
1494 g_critical("Houston, we've had a problem. distance=%d", dist_units);
1497 s->has_oldcoord = FALSE;
1500 temp = g_strdup_printf ( "%s %s", lat, lon );
1501 s->has_oldcoord = TRUE;
1504 vik_statusbar_set_message ( s->vw->viking_vs, VIK_STATUSBAR_INFO, temp );
1507 s->oldcoord = coord;
1510 vik_viewport_set_center_screen ( s->vvp, (gint) event->x, (gint) event->y );
1511 draw_update ( s->vw );
1513 return VIK_LAYER_TOOL_ACK;
1516 static VikLayerToolFuncStatus ruler_move (VikLayer *vl, GdkEventMotion *event, ruler_tool_state_t *s)
1518 VikViewport *vvp = s->vvp;
1519 VikWindow *vw = s->vw;
1525 if ( s->has_oldcoord ) {
1526 int oldx, oldy, w1, h1, w2, h2;
1527 static GdkPixmap *buf = NULL;
1528 gchar *lat=NULL, *lon=NULL;
1529 w1 = vik_viewport_get_width(vvp);
1530 h1 = vik_viewport_get_height(vvp);
1532 buf = gdk_pixmap_new ( gtk_widget_get_window(GTK_WIDGET(vvp)), w1, h1, -1 );
1534 gdk_drawable_get_size(buf, &w2, &h2);
1535 if (w1 != w2 || h1 != h2) {
1536 g_object_unref ( G_OBJECT ( buf ) );
1537 buf = gdk_pixmap_new ( gtk_widget_get_window(GTK_WIDGET(vvp)), w1, h1, -1 );
1540 vik_viewport_screen_to_coord ( vvp, (gint) event->x, (gint) event->y, &coord );
1541 vik_coord_to_latlon ( &coord, &ll );
1542 vik_viewport_coord_to_screen ( vvp, &s->oldcoord, &oldx, &oldy );
1544 gdk_draw_drawable (buf, gtk_widget_get_style(GTK_WIDGET(vvp))->black_gc,
1545 vik_viewport_get_pixmap(vvp), 0, 0, 0, 0, -1, -1);
1546 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)) );
1547 if (draw_buf_done) {
1548 static gpointer pass_along[3];
1549 pass_along[0] = gtk_widget_get_window(GTK_WIDGET(vvp));
1550 pass_along[1] = gtk_widget_get_style(GTK_WIDGET(vvp))->black_gc;
1551 pass_along[2] = buf;
1552 g_idle_add_full (G_PRIORITY_HIGH_IDLE + 10, draw_buf, pass_along, NULL);
1553 draw_buf_done = FALSE;
1555 a_coords_latlon_to_string(&ll, &lat, &lon);
1556 vik_units_distance_t dist_units = a_vik_get_units_distance ();
1557 switch (dist_units) {
1558 case VIK_UNITS_DISTANCE_KILOMETRES:
1559 temp = g_strdup_printf ( "%s %s DIFF %f meters", lat, lon, vik_coord_diff( &coord, &(s->oldcoord) ) );
1561 case VIK_UNITS_DISTANCE_MILES:
1562 temp = g_strdup_printf ( "%s %s DIFF %f miles", lat, lon, VIK_METERS_TO_MILES (vik_coord_diff( &coord, &(s->oldcoord) )) );
1565 temp = g_strdup_printf ("Just to keep the compiler happy");
1566 g_critical("Houston, we've had a problem. distance=%d", dist_units);
1568 vik_statusbar_set_message ( vw->viking_vs, VIK_STATUSBAR_INFO, temp );
1571 return VIK_LAYER_TOOL_ACK;
1574 static VikLayerToolFuncStatus ruler_release (VikLayer *vl, GdkEventButton *event, ruler_tool_state_t *s)
1576 return VIK_LAYER_TOOL_ACK;
1579 static void ruler_deactivate (VikLayer *vl, ruler_tool_state_t *s)
1581 draw_update ( s->vw );
1584 static gboolean ruler_key_press (VikLayer *vl, GdkEventKey *event, ruler_tool_state_t *s)
1586 if (event->keyval == GDK_Escape) {
1587 s->has_oldcoord = FALSE;
1588 ruler_deactivate ( vl, s );
1591 // Regardless of whether we used it, return false so other GTK things may use it
1595 static VikToolInterface ruler_tool =
1596 // NB Ctrl+Shift+R is used for Refresh (deemed more important), so use 'U' instead
1597 { { "Ruler", "vik-icon-ruler", N_("_Ruler"), "<control><shift>U", N_("Ruler Tool"), 2 },
1598 (VikToolConstructorFunc) ruler_create,
1599 (VikToolDestructorFunc) ruler_destroy,
1600 (VikToolActivationFunc) NULL,
1601 (VikToolActivationFunc) ruler_deactivate,
1602 (VikToolMouseFunc) ruler_click,
1603 (VikToolMouseMoveFunc) ruler_move,
1604 (VikToolMouseFunc) ruler_release,
1605 (VikToolKeyFunc) ruler_key_press,
1607 GDK_CURSOR_IS_PIXMAP,
1608 &cursor_ruler_pixbuf };
1609 /*** end ruler code ********************************************************/
1613 /********************************************************************************
1615 ********************************************************************************/
1620 // Track zoom bounds for zoom tool with shift modifier:
1621 gboolean bounds_active;
1624 } zoom_tool_state_t;
1627 * In case the screen size has changed
1629 static void zoomtool_resize_pixmap (zoom_tool_state_t *zts)
1633 // Allocate a drawing area the size of the viewport
1634 w1 = vik_viewport_get_width ( zts->vw->viking_vvp );
1635 h1 = vik_viewport_get_height ( zts->vw->viking_vvp );
1637 if ( !zts->pixmap ) {
1639 zts->pixmap = gdk_pixmap_new ( gtk_widget_get_window(GTK_WIDGET(zts->vw->viking_vvp)), w1, h1, -1 );
1642 gdk_drawable_get_size ( zts->pixmap, &w2, &h2 );
1644 if ( w1 != w2 || h1 != h2 ) {
1645 // Has changed - delete and recreate with new values
1646 g_object_unref ( G_OBJECT ( zts->pixmap ) );
1647 zts->pixmap = gdk_pixmap_new ( gtk_widget_get_window(GTK_WIDGET(zts->vw->viking_vvp)), w1, h1, -1 );
1651 static gpointer zoomtool_create (VikWindow *vw, VikViewport *vvp)
1653 zoom_tool_state_t *zts = g_new(zoom_tool_state_t, 1);
1658 zts->bounds_active = FALSE;
1662 static void zoomtool_destroy ( zoom_tool_state_t *zts)
1665 g_object_unref ( G_OBJECT ( zts->pixmap ) );
1669 static VikLayerToolFuncStatus zoomtool_click (VikLayer *vl, GdkEventButton *event, zoom_tool_state_t *zts)
1671 zts->vw->modified = TRUE;
1672 guint modifiers = event->state & (GDK_SHIFT_MASK | GDK_CONTROL_MASK);
1676 gint center_x = vik_viewport_get_width ( zts->vw->viking_vvp ) / 2;
1677 gint center_y = vik_viewport_get_height ( zts->vw->viking_vvp ) / 2;
1679 gboolean skip_update = FALSE;
1681 zts->bounds_active = FALSE;
1683 if ( modifiers == (GDK_CONTROL_MASK | GDK_SHIFT_MASK) ) {
1684 // This zoom is on the center position
1685 vik_viewport_set_center_screen ( zts->vw->viking_vvp, center_x, center_y );
1686 if ( event->button == 1 )
1687 vik_viewport_zoom_in (zts->vw->viking_vvp);
1688 else if ( event->button == 3 )
1689 vik_viewport_zoom_out (zts->vw->viking_vvp);
1691 else if ( modifiers == GDK_CONTROL_MASK ) {
1692 // This zoom is to recenter on the mouse position
1693 vik_viewport_set_center_screen ( zts->vw->viking_vvp, (gint) event->x, (gint) event->y );
1694 if ( event->button == 1 )
1695 vik_viewport_zoom_in (zts->vw->viking_vvp);
1696 else if ( event->button == 3 )
1697 vik_viewport_zoom_out (zts->vw->viking_vvp);
1699 else if ( modifiers == GDK_SHIFT_MASK ) {
1700 // Get start of new zoom bounds
1701 if ( event->button == 1 ) {
1702 zts->bounds_active = TRUE;
1703 zts->start_x = (gint) event->x;
1704 zts->start_y = (gint) event->y;
1709 /* make sure mouse is still over the same point on the map when we zoom */
1710 vik_viewport_screen_to_coord ( zts->vw->viking_vvp, event->x, event->y, &coord );
1711 if ( event->button == 1 )
1712 vik_viewport_zoom_in (zts->vw->viking_vvp);
1713 else if ( event->button == 3 )
1714 vik_viewport_zoom_out(zts->vw->viking_vvp);
1715 vik_viewport_coord_to_screen ( zts->vw->viking_vvp, &coord, &x, &y );
1716 vik_viewport_set_center_screen ( zts->vw->viking_vvp,
1717 center_x + (x - event->x),
1718 center_y + (y - event->y) );
1722 draw_update ( zts->vw );
1724 return VIK_LAYER_TOOL_ACK;
1727 static VikLayerToolFuncStatus zoomtool_move (VikLayer *vl, GdkEventMotion *event, zoom_tool_state_t *zts)
1729 guint modifiers = event->state & (GDK_SHIFT_MASK | GDK_CONTROL_MASK);
1731 if ( zts->bounds_active && modifiers == GDK_SHIFT_MASK ) {
1732 zoomtool_resize_pixmap ( zts );
1734 // Blank out currently drawn area
1735 gdk_draw_drawable ( zts->pixmap,
1736 gtk_widget_get_style(GTK_WIDGET(zts->vw->viking_vvp))->black_gc,
1737 vik_viewport_get_pixmap(zts->vw->viking_vvp),
1738 0, 0, 0, 0, -1, -1);
1740 // Calculate new box starting point & size in pixels
1741 int xx, yy, width, height;
1742 if ( event->y > zts->start_y ) {
1744 height = event->y-zts->start_y;
1748 height = zts->start_y-event->y;
1750 if ( event->x > zts->start_x ) {
1752 width = event->x-zts->start_x;
1756 width = zts->start_x-event->x;
1760 gdk_draw_rectangle (zts->pixmap, gtk_widget_get_style(GTK_WIDGET(zts->vw->viking_vvp))->black_gc, FALSE, xx, yy, width, height);
1762 // Only actually draw when there's time to do so
1763 if (draw_buf_done) {
1764 static gpointer pass_along[3];
1765 pass_along[0] = gtk_widget_get_window(GTK_WIDGET(zts->vw->viking_vvp));
1766 pass_along[1] = gtk_widget_get_style(GTK_WIDGET(zts->vw->viking_vvp))->black_gc;
1767 pass_along[2] = zts->pixmap;
1768 g_idle_add_full (G_PRIORITY_HIGH_IDLE + 10, draw_buf, pass_along, NULL);
1769 draw_buf_done = FALSE;
1772 return VIK_LAYER_TOOL_ACK;
1775 static VikLayerToolFuncStatus zoomtool_release (VikLayer *vl, GdkEventButton *event, zoom_tool_state_t *zts)
1777 guint modifiers = event->state & (GDK_SHIFT_MASK | GDK_CONTROL_MASK);
1779 zts->bounds_active = FALSE;
1781 // Ensure haven't just released on the exact same position
1782 // i.e. probably haven't moved the mouse at all
1783 if ( modifiers == GDK_SHIFT_MASK && !( ( event->x == zts->start_x ) && ( event->y == zts->start_y )) ) {
1785 VikCoord coord1, coord2;
1786 vik_viewport_screen_to_coord ( zts->vw->viking_vvp, zts->start_x, zts->start_y, &coord1);
1787 vik_viewport_screen_to_coord ( zts->vw->viking_vvp, event->x, event->y, &coord2);
1789 // From the extend of the bounds pick the best zoom level
1790 // c.f. trw_layer_zoom_to_show_latlons()
1791 // Maybe refactor...
1792 struct LatLon ll1, ll2;
1793 vik_coord_to_latlon(&coord1, &ll1);
1794 vik_coord_to_latlon(&coord2, &ll2);
1795 struct LatLon average = { (ll1.lat+ll2.lat)/2,
1796 (ll1.lon+ll2.lon)/2 };
1798 VikCoord new_center;
1799 vik_coord_load_from_latlon ( &new_center, vik_viewport_get_coord_mode ( zts->vw->viking_vvp ), &average );
1800 vik_viewport_set_center_coord ( zts->vw->viking_vvp, &new_center, FALSE );
1802 /* Convert into definite 'smallest' and 'largest' positions */
1803 struct LatLon minmin;
1804 if ( ll1.lat < ll2.lat )
1805 minmin.lat = ll1.lat;
1807 minmin.lat = ll2.lat;
1809 struct LatLon maxmax;
1810 if ( ll1.lon > ll2.lon )
1811 maxmax.lon = ll1.lon;
1813 maxmax.lon = ll2.lon;
1815 /* Always recalculate the 'best' zoom level */
1816 gdouble zoom = VIK_VIEWPORT_MIN_ZOOM;
1817 vik_viewport_set_zoom ( zts->vw->viking_vvp, zoom );
1819 gdouble min_lat, max_lat, min_lon, max_lon;
1820 /* Should only be a maximum of about 18 iterations from min to max zoom levels */
1821 while ( zoom <= VIK_VIEWPORT_MAX_ZOOM ) {
1822 vik_viewport_get_min_max_lat_lon ( zts->vw->viking_vvp, &min_lat, &max_lat, &min_lon, &max_lon );
1823 /* NB I think the logic used in this test to determine if the bounds is within view
1824 fails if track goes across 180 degrees longitude.
1825 Hopefully that situation is not too common...
1826 Mind you viking doesn't really do edge locations to well anyway */
1827 if ( min_lat < minmin.lat &&
1828 max_lat > minmin.lat &&
1829 min_lon < maxmax.lon &&
1830 max_lon > maxmax.lon )
1831 /* Found within zoom level */
1836 vik_viewport_set_zoom ( zts->vw->viking_vvp, zoom );
1839 draw_update ( zts->vw );
1841 return VIK_LAYER_TOOL_ACK;
1844 static VikToolInterface zoom_tool =
1845 { { "Zoom", "vik-icon-zoom", N_("_Zoom"), "<control><shift>Z", N_("Zoom Tool"), 1 },
1846 (VikToolConstructorFunc) zoomtool_create,
1847 (VikToolDestructorFunc) zoomtool_destroy,
1848 (VikToolActivationFunc) NULL,
1849 (VikToolActivationFunc) NULL,
1850 (VikToolMouseFunc) zoomtool_click,
1851 (VikToolMouseMoveFunc) zoomtool_move,
1852 (VikToolMouseFunc) zoomtool_release,
1855 GDK_CURSOR_IS_PIXMAP,
1856 &cursor_zoom_pixbuf };
1857 /*** end zoom code ********************************************************/
1859 /********************************************************************************
1861 ********************************************************************************/
1862 static gpointer pantool_create (VikWindow *vw, VikViewport *vvp)
1867 static VikLayerToolFuncStatus pantool_click (VikLayer *vl, GdkEventButton *event, VikWindow *vw)
1869 vw->modified = TRUE;
1870 if ( event->button == 1 )
1871 vik_window_pan_click ( vw, event );
1873 return VIK_LAYER_TOOL_ACK;
1876 static VikLayerToolFuncStatus pantool_move (VikLayer *vl, GdkEventMotion *event, VikWindow *vw)
1878 vik_window_pan_move ( vw, event );
1879 return VIK_LAYER_TOOL_ACK;
1882 static VikLayerToolFuncStatus pantool_release (VikLayer *vl, GdkEventButton *event, VikWindow *vw)
1884 if ( event->button == 1 )
1885 vik_window_pan_release ( vw, event );
1886 return VIK_LAYER_TOOL_ACK;
1889 static VikToolInterface pan_tool =
1890 { { "Pan", "vik-icon-pan", N_("_Pan"), "<control><shift>P", N_("Pan Tool"), 0 },
1891 (VikToolConstructorFunc) pantool_create,
1892 (VikToolDestructorFunc) NULL,
1893 (VikToolActivationFunc) NULL,
1894 (VikToolActivationFunc) NULL,
1895 (VikToolMouseFunc) pantool_click,
1896 (VikToolMouseMoveFunc) pantool_move,
1897 (VikToolMouseFunc) pantool_release,
1901 /*** end pan code ********************************************************/
1903 /********************************************************************************
1905 ********************************************************************************/
1906 static gpointer selecttool_create (VikWindow *vw, VikViewport *vvp)
1908 tool_ed_t *t = g_new(tool_ed_t, 1);
1912 t->is_waypoint = FALSE;
1916 static void selecttool_destroy (tool_ed_t *t)
1924 GdkEventButton *event;
1925 tool_ed_t *tool_edit;
1928 static void click_layer_selected (VikLayer *vl, clicker *ck)
1930 /* Do nothing when function call returns true; */
1931 /* i.e. stop on first found item */
1934 if ( vik_layer_get_interface(vl->type)->select_click )
1935 ck->cont = !vik_layer_get_interface(vl->type)->select_click ( vl, ck->event, ck->vvp, ck->tool_edit );
1938 static VikLayerToolFuncStatus selecttool_click (VikLayer *vl, GdkEventButton *event, tool_ed_t *t)
1940 /* Only allow selection on primary button */
1941 if ( event->button == 1 ) {
1942 /* Enable click to apply callback to potentially all track/waypoint layers */
1943 /* Useful as we can find things that aren't necessarily in the currently selected layer */
1944 GList* gl = vik_layers_panel_get_all_layers_of_type ( t->vw->viking_vlp, VIK_LAYER_TRW, FALSE ); // Don't get invisible layers
1947 ck.vvp = t->vw->viking_vvp;
1950 g_list_foreach ( gl, (GFunc) click_layer_selected, &ck );
1953 // If nothing found then deselect & redraw screen if necessary to remove the highlight
1956 VikTreeview *vtv = vik_layers_panel_get_treeview ( t->vw->viking_vlp );
1958 if ( vik_treeview_get_selected_iter ( vtv, &iter ) ) {
1959 // Only clear if selected thing is a TrackWaypoint layer or a sublayer
1960 gint type = vik_treeview_item_get_type ( vtv, &iter );
1961 if ( type == VIK_TREEVIEW_TYPE_SUBLAYER ||
1962 VIK_LAYER(vik_treeview_item_get_pointer ( vtv, &iter ))->type == VIK_LAYER_TRW ) {
1964 vik_treeview_item_unselect ( vtv, &iter );
1965 if ( vik_window_clear_highlight ( t->vw ) )
1966 draw_update ( t->vw );
1971 else if ( ( event->button == 3 ) && ( vl && ( vl->type == VIK_LAYER_TRW ) ) ) {
1973 /* Act on currently selected item to show menu */
1974 if ( t->vw->selected_track || t->vw->selected_waypoint )
1975 if ( vik_layer_get_interface(vl->type)->show_viewport_menu )
1976 vik_layer_get_interface(vl->type)->show_viewport_menu ( vl, event, t->vw->viking_vvp );
1979 return VIK_LAYER_TOOL_ACK;
1982 static VikLayerToolFuncStatus selecttool_move (VikLayer *vl, GdkEventButton *event, tool_ed_t *t)
1984 /* Only allow selection on primary button */
1985 if ( event->button == 1 ) {
1986 // Don't care about vl here
1988 if ( vik_layer_get_interface(VIK_LAYER_TRW)->select_move )
1989 vik_layer_get_interface(VIK_LAYER_TRW)->select_move ( vl, event, t->vvp, t );
1991 return VIK_LAYER_TOOL_ACK;
1994 static VikLayerToolFuncStatus selecttool_release (VikLayer *vl, GdkEventButton *event, tool_ed_t *t)
1996 /* Only allow selection on primary button */
1997 if ( event->button == 1 ) {
1998 // Don't care about vl here
2000 if ( vik_layer_get_interface(VIK_LAYER_TRW)->select_release )
2001 vik_layer_get_interface(VIK_LAYER_TRW)->select_release ( (VikLayer*)t->vtl, event, t->vvp, t );
2003 return VIK_LAYER_TOOL_ACK;
2006 static VikToolInterface select_tool =
2007 { { "Select", "vik-icon-select", N_("_Select"), "<control><shift>S", N_("Select Tool"), 3 },
2008 (VikToolConstructorFunc) selecttool_create,
2009 (VikToolDestructorFunc) selecttool_destroy,
2010 (VikToolActivationFunc) NULL,
2011 (VikToolActivationFunc) NULL,
2012 (VikToolMouseFunc) selecttool_click,
2013 (VikToolMouseMoveFunc) selecttool_move,
2014 (VikToolMouseFunc) selecttool_release,
2015 (VikToolKeyFunc) NULL,
2020 /*** end select tool code ********************************************************/
2022 static void draw_pan_cb ( GtkAction *a, VikWindow *vw )
2024 // Since the treeview cell editting intercepts standard keyboard handlers, it means we can receive events here
2025 // Thus if currently editting, ensure we don't move the viewport when Ctrl+<arrow> is received
2026 VikLayer *sel = vik_layers_panel_get_selected ( vw->viking_vlp );
2027 if ( sel && vik_treeview_get_editing ( sel->vt ) )
2030 if (!strcmp(gtk_action_get_name(a), "PanNorth")) {
2031 vik_viewport_set_center_screen ( vw->viking_vvp, vik_viewport_get_width(vw->viking_vvp)/2, 0 );
2032 } else if (!strcmp(gtk_action_get_name(a), "PanEast")) {
2033 vik_viewport_set_center_screen ( vw->viking_vvp, vik_viewport_get_width(vw->viking_vvp), vik_viewport_get_height(vw->viking_vvp)/2 );
2034 } else if (!strcmp(gtk_action_get_name(a), "PanSouth")) {
2035 vik_viewport_set_center_screen ( vw->viking_vvp, vik_viewport_get_width(vw->viking_vvp)/2, vik_viewport_get_height(vw->viking_vvp) );
2036 } else if (!strcmp(gtk_action_get_name(a), "PanWest")) {
2037 vik_viewport_set_center_screen ( vw->viking_vvp, 0, vik_viewport_get_height(vw->viking_vvp)/2 );
2042 static void full_screen_cb ( GtkAction *a, VikWindow *vw )
2044 GtkWidget *check_box = gtk_ui_manager_get_widget ( vw->uim, "/ui/MainMenu/View/FullScreen" );
2045 g_assert(check_box);
2046 gboolean state = gtk_check_menu_item_get_active ( GTK_CHECK_MENU_ITEM(check_box));
2048 gtk_window_fullscreen ( GTK_WINDOW(vw) );
2050 gtk_window_unfullscreen ( GTK_WINDOW(vw) );
2053 static void draw_zoom_cb ( GtkAction *a, VikWindow *vw )
2057 if (!strcmp(gtk_action_get_name(a), "ZoomIn")) {
2060 else if (!strcmp(gtk_action_get_name(a), "ZoomOut")) {
2063 else if (!strcmp(gtk_action_get_name(a), "Zoom0.25")) {
2066 else if (!strcmp(gtk_action_get_name(a), "Zoom0.5")) {
2070 gchar *s = (gchar *)gtk_action_get_name(a);
2076 case -3: vik_viewport_zoom_in ( vw->viking_vvp ); break;
2077 case -4: vik_viewport_zoom_out ( vw->viking_vvp ); break;
2078 case -1: vik_viewport_set_zoom ( vw->viking_vvp, 0.5 ); break;
2079 case -2: vik_viewport_set_zoom ( vw->viking_vvp, 0.25 ); break;
2080 default: vik_viewport_set_zoom ( vw->viking_vvp, what );
2085 static void draw_goto_cb ( GtkAction *a, VikWindow *vw )
2087 VikCoord new_center;
2089 if (!strcmp(gtk_action_get_name(a), "GotoLL")) {
2090 struct LatLon ll, llold;
2091 vik_coord_to_latlon ( vik_viewport_get_center ( vw->viking_vvp ), &llold );
2092 if ( a_dialog_goto_latlon ( GTK_WINDOW(vw), &ll, &llold ) )
2093 vik_coord_load_from_latlon ( &new_center, vik_viewport_get_coord_mode(vw->viking_vvp), &ll );
2097 else if (!strcmp(gtk_action_get_name(a), "GotoUTM")) {
2098 struct UTM utm, utmold;
2099 vik_coord_to_utm ( vik_viewport_get_center ( vw->viking_vvp ), &utmold );
2100 if ( a_dialog_goto_utm ( GTK_WINDOW(vw), &utm, &utmold ) )
2101 vik_coord_load_from_utm ( &new_center, vik_viewport_get_coord_mode(vw->viking_vvp), &utm );
2106 g_critical("Houston, we've had a problem.");
2110 vik_viewport_set_center_coord ( vw->viking_vvp, &new_center, TRUE );
2115 * center_changed_cb:
2117 static void center_changed_cb ( VikWindow *vw )
2119 // ATM Keep back always available, so when we pan - we can jump to the last requested position
2121 GtkAction* action_back = gtk_action_group_get_action ( vw->action_group, "GoBack" );
2122 if ( action_back ) {
2123 gtk_action_set_sensitive ( action_back, vik_viewport_back_available(vw->viking_vvp) );
2126 GtkAction* action_forward = gtk_action_group_get_action ( vw->action_group, "GoForward" );
2127 if ( action_forward ) {
2128 gtk_action_set_sensitive ( action_forward, vik_viewport_forward_available(vw->viking_vvp) );
2133 * draw_goto_back_and_forth:
2135 static void draw_goto_back_and_forth ( GtkAction *a, VikWindow *vw )
2137 gboolean changed = FALSE;
2138 if (!strcmp(gtk_action_get_name(a), "GoBack")) {
2139 changed = vik_viewport_go_back ( vw->viking_vvp );
2141 else if (!strcmp(gtk_action_get_name(a), "GoForward")) {
2142 changed = vik_viewport_go_forward ( vw->viking_vvp );
2148 // Recheck buttons sensitivities, as the center changed signal is not sent on back/forward changes
2149 // (otherwise we would get stuck in an infinite loop!)
2150 center_changed_cb ( vw );
2157 * Refresh maps displayed
2159 static void draw_refresh_cb ( GtkAction *a, VikWindow *vw )
2161 // Only get 'new' maps
2162 simple_map_update ( vw, TRUE );
2165 static void menu_addlayer_cb ( GtkAction *a, VikWindow *vw )
2167 VikLayerTypeEnum type;
2168 for ( type = 0; type < VIK_LAYER_NUM_TYPES; type++ ) {
2169 if (!strcmp(vik_layer_get_interface(type)->name, gtk_action_get_name(a))) {
2170 if ( vik_layers_panel_new_layer ( vw->viking_vlp, type ) ) {
2172 vw->modified = TRUE;
2178 static void menu_copy_layer_cb ( GtkAction *a, VikWindow *vw )
2180 a_clipboard_copy_selected ( vw->viking_vlp );
2183 static void menu_cut_layer_cb ( GtkAction *a, VikWindow *vw )
2185 vik_layers_panel_cut_selected ( vw->viking_vlp );
2186 vw->modified = TRUE;
2189 static void menu_paste_layer_cb ( GtkAction *a, VikWindow *vw )
2191 if ( vik_layers_panel_paste_selected ( vw->viking_vlp ) )
2193 vw->modified = TRUE;
2197 static void menu_properties_cb ( GtkAction *a, VikWindow *vw )
2199 if ( ! vik_layers_panel_properties ( vw->viking_vlp ) )
2200 a_dialog_info_msg ( GTK_WINDOW(vw), _("You must select a layer to show its properties.") );
2203 static void help_help_cb ( GtkAction *a, VikWindow *vw )
2206 ShellExecute(NULL, "open", ""PACKAGE".pdf", NULL, NULL, SW_SHOWNORMAL);
2209 uri = g_strdup_printf("ghelp:%s", PACKAGE);
2210 GError *error = NULL;
2211 gboolean show = gtk_show_uri (NULL, uri, GDK_CURRENT_TIME, &error);
2212 if ( !show && !error )
2213 // No error to show, so unlikely this will get called
2214 a_dialog_error_msg ( GTK_WINDOW(vw), _("The help system is not available.") );
2217 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 );
2218 g_error_free ( error );
2221 #endif /* WINDOWS */
2224 static void help_about_cb ( GtkAction *a, VikWindow *vw )
2226 a_dialog_about(GTK_WINDOW(vw));
2229 static void menu_delete_layer_cb ( GtkAction *a, VikWindow *vw )
2231 if ( vik_layers_panel_get_selected ( vw->viking_vlp ) )
2233 vik_layers_panel_delete_selected ( vw->viking_vlp );
2234 vw->modified = TRUE;
2237 a_dialog_info_msg ( GTK_WINDOW(vw), _("You must select a layer to delete.") );
2240 static void view_side_panel_cb ( GtkAction *a, VikWindow *vw )
2242 GtkWidget *check_box = gtk_ui_manager_get_widget ( vw->uim, "/ui/MainMenu/View/SetShow/ViewSidePanel" );
2243 g_assert(check_box);
2244 gboolean state = gtk_check_menu_item_get_active ( GTK_CHECK_MENU_ITEM(check_box));
2246 gtk_widget_show(GTK_WIDGET(vw->viking_vlp));
2248 gtk_widget_hide(GTK_WIDGET(vw->viking_vlp));
2251 static void view_statusbar_cb ( GtkAction *a, VikWindow *vw )
2253 GtkWidget *check_box = gtk_ui_manager_get_widget ( vw->uim, "/ui/MainMenu/View/SetShow/ViewStatusBar" );
2256 gboolean state = gtk_check_menu_item_get_active ( GTK_CHECK_MENU_ITEM(check_box) );
2258 gtk_widget_show ( GTK_WIDGET(vw->viking_vs) );
2260 gtk_widget_hide ( GTK_WIDGET(vw->viking_vs) );
2263 static void view_toolbar_cb ( GtkAction *a, VikWindow *vw )
2265 GtkWidget *check_box = gtk_ui_manager_get_widget ( vw->uim, "/ui/MainMenu/View/SetShow/ViewToolbar" );
2268 gboolean state = gtk_check_menu_item_get_active ( GTK_CHECK_MENU_ITEM(check_box) );
2270 gtk_widget_show ( GTK_WIDGET(vw->toolbar) );
2272 gtk_widget_hide ( GTK_WIDGET(vw->toolbar) );
2275 static void view_main_menu_cb ( GtkAction *a, VikWindow *vw )
2277 GtkWidget *check_box = gtk_ui_manager_get_widget ( vw->uim, "/ui/MainMenu/View/SetShow/ViewMainMenu" );
2280 gboolean state = gtk_check_menu_item_get_active ( GTK_CHECK_MENU_ITEM(check_box) );
2282 gtk_widget_hide ( gtk_ui_manager_get_widget ( vw->uim, "/ui/MainMenu" ) );
2284 gtk_widget_show ( gtk_ui_manager_get_widget ( vw->uim, "/ui/MainMenu" ) );
2287 /***************************************
2288 ** tool management routines
2290 ***************************************/
2292 static toolbox_tools_t* toolbox_create(VikWindow *vw)
2294 toolbox_tools_t *vt = g_new(toolbox_tools_t, 1);
2297 vt->active_tool = -1;
2302 static void toolbox_add_tool(toolbox_tools_t *vt, VikToolInterface *vti, gint layer_type )
2304 vt->tools = g_renew(toolbox_tool_t, vt->tools, vt->n_tools+1);
2305 vt->tools[vt->n_tools].ti = *vti;
2306 vt->tools[vt->n_tools].layer_type = layer_type;
2308 vt->tools[vt->n_tools].state = vti->create(vt->vw, vt->vw->viking_vvp);
2311 vt->tools[vt->n_tools].state = NULL;
2316 static int toolbox_get_tool(toolbox_tools_t *vt, const gchar *tool_name)
2319 for (i=0; i<vt->n_tools; i++) {
2320 if (!strcmp(tool_name, vt->tools[i].ti.radioActionEntry.name)) {
2327 static void toolbox_activate(toolbox_tools_t *vt, const gchar *tool_name)
2329 int tool = toolbox_get_tool(vt, tool_name);
2330 toolbox_tool_t *t = &vt->tools[tool];
2331 VikLayer *vl = vik_layers_panel_get_selected ( vt->vw->viking_vlp );
2333 if (tool == vt->n_tools) {
2334 g_critical("trying to activate a non-existent tool...");
2337 /* is the tool already active? */
2338 if (vt->active_tool == tool) {
2342 if (vt->active_tool != -1) {
2343 if (vt->tools[vt->active_tool].ti.deactivate) {
2344 vt->tools[vt->active_tool].ti.deactivate(NULL, vt->tools[vt->active_tool].state);
2347 if (t->ti.activate) {
2348 t->ti.activate(vl, t->state);
2350 vt->active_tool = tool;
2353 static const GdkCursor *toolbox_get_cursor(toolbox_tools_t *vt, const gchar *tool_name)
2355 int tool = toolbox_get_tool(vt, tool_name);
2356 toolbox_tool_t *t = &vt->tools[tool];
2357 if (t->ti.cursor == NULL) {
2358 if (t->ti.cursor_type == GDK_CURSOR_IS_PIXMAP && t->ti.cursor_data != NULL) {
2359 GError *cursor_load_err = NULL;
2360 GdkPixbuf *cursor_pixbuf = gdk_pixbuf_from_pixdata (t->ti.cursor_data, FALSE, &cursor_load_err);
2361 /* TODO: settable offeset */
2362 t->ti.cursor = gdk_cursor_new_from_pixbuf ( gdk_display_get_default(), cursor_pixbuf, 3, 3 );
2363 g_object_unref ( G_OBJECT(cursor_pixbuf) );
2365 t->ti.cursor = gdk_cursor_new ( t->ti.cursor_type );
2368 return t->ti.cursor;
2371 static void toolbox_click (toolbox_tools_t *vt, GdkEventButton *event)
2373 VikLayer *vl = vik_layers_panel_get_selected ( vt->vw->viking_vlp );
2374 if (vt->active_tool != -1 && vt->tools[vt->active_tool].ti.click) {
2375 gint ltype = vt->tools[vt->active_tool].layer_type;
2376 if ( ltype == TOOL_LAYER_TYPE_NONE || (vl && ltype == vl->type) )
2377 vt->tools[vt->active_tool].ti.click(vl, event, vt->tools[vt->active_tool].state);
2381 static void toolbox_move (toolbox_tools_t *vt, GdkEventMotion *event)
2383 VikLayer *vl = vik_layers_panel_get_selected ( vt->vw->viking_vlp );
2384 if (vt->active_tool != -1 && vt->tools[vt->active_tool].ti.move) {
2385 gint ltype = vt->tools[vt->active_tool].layer_type;
2386 if ( ltype == TOOL_LAYER_TYPE_NONE || (vl && ltype == vl->type) )
2387 if ( VIK_LAYER_TOOL_ACK_GRAB_FOCUS == vt->tools[vt->active_tool].ti.move(vl, event, vt->tools[vt->active_tool].state) )
2388 gtk_widget_grab_focus ( GTK_WIDGET(vt->vw->viking_vvp) );
2392 static void toolbox_release (toolbox_tools_t *vt, GdkEventButton *event)
2394 VikLayer *vl = vik_layers_panel_get_selected ( vt->vw->viking_vlp );
2395 if (vt->active_tool != -1 && vt->tools[vt->active_tool].ti.release ) {
2396 gint ltype = vt->tools[vt->active_tool].layer_type;
2397 if ( ltype == TOOL_LAYER_TYPE_NONE || (vl && ltype == vl->type) )
2398 vt->tools[vt->active_tool].ti.release(vl, event, vt->tools[vt->active_tool].state);
2401 /** End tool management ************************************/
2403 void vik_window_enable_layer_tool ( VikWindow *vw, gint layer_id, gint tool_id )
2405 gtk_action_activate ( gtk_action_group_get_action ( vw->action_group, vik_layer_get_interface(layer_id)->tools[tool_id].radioActionEntry.name ) );
2408 /* this function gets called whenever a toolbar tool is clicked */
2409 static void menu_tool_cb ( GtkAction *old, GtkAction *a, VikWindow *vw )
2411 /* White Magic, my friends ... White Magic... */
2413 toolbox_activate(vw->vt, gtk_action_get_name(a));
2415 vw->viewport_cursor = (GdkCursor *)toolbox_get_cursor(vw->vt, gtk_action_get_name(a));
2417 if ( gtk_widget_get_window(GTK_WIDGET(vw->viking_vvp)) )
2418 /* We set cursor, even if it is NULL: it resets to default */
2419 gdk_window_set_cursor ( gtk_widget_get_window(GTK_WIDGET(vw->viking_vvp)), vw->viewport_cursor );
2421 if (!strcmp(gtk_action_get_name(a), "Pan")) {
2422 vw->current_tool = TOOL_PAN;
2424 else if (!strcmp(gtk_action_get_name(a), "Zoom")) {
2425 vw->current_tool = TOOL_ZOOM;
2427 else if (!strcmp(gtk_action_get_name(a), "Ruler")) {
2428 vw->current_tool = TOOL_RULER;
2430 else if (!strcmp(gtk_action_get_name(a), "Select")) {
2431 vw->current_tool = TOOL_SELECT;
2434 VikLayerTypeEnum layer_id;
2435 for (layer_id=0; layer_id<VIK_LAYER_NUM_TYPES; layer_id++) {
2436 for ( tool_id = 0; tool_id < vik_layer_get_interface(layer_id)->tools_count; tool_id++ ) {
2437 if (!strcmp(vik_layer_get_interface(layer_id)->tools[tool_id].radioActionEntry.name, gtk_action_get_name(a))) {
2438 vw->current_tool = TOOL_LAYER;
2439 vw->tool_layer_id = layer_id;
2440 vw->tool_tool_id = tool_id;
2445 draw_status_tool ( vw );
2448 static void window_set_filename ( VikWindow *vw, const gchar *filename )
2453 g_free ( vw->filename );
2454 if ( filename == NULL )
2456 vw->filename = NULL;
2460 vw->filename = g_strdup(filename);
2463 /* Refresh window's title */
2464 file = window_get_filename ( vw );
2465 title = g_strdup_printf( "%s - Viking", file );
2466 gtk_window_set_title ( GTK_WINDOW(vw), title );
2470 static const gchar *window_get_filename ( VikWindow *vw )
2472 return vw->filename ? a_file_basename ( vw->filename ) : _("Untitled");
2475 GtkWidget *vik_window_get_drawmode_button ( VikWindow *vw, VikViewportDrawMode mode )
2477 GtkWidget *mode_button;
2480 #ifdef VIK_CONFIG_EXPEDIA
2481 case VIK_VIEWPORT_DRAWMODE_EXPEDIA: buttonname = "/ui/MainMenu/View/ModeExpedia"; break;
2483 case VIK_VIEWPORT_DRAWMODE_MERCATOR: buttonname = "/ui/MainMenu/View/ModeMercator"; break;
2484 case VIK_VIEWPORT_DRAWMODE_LATLON: buttonname = "/ui/MainMenu/View/ModeLatLon"; break;
2485 default: buttonname = "/ui/MainMenu/View/ModeUTM";
2487 mode_button = gtk_ui_manager_get_widget ( vw->uim, buttonname );
2488 g_assert ( mode_button );
2493 * vik_window_get_pan_move:
2494 * @vw: some VikWindow
2496 * Retrieves @vw's pan_move.
2498 * Should be removed as soon as possible.
2500 * Returns: @vw's pan_move
2504 gboolean vik_window_get_pan_move ( VikWindow *vw )
2506 return vw->pan_move;
2509 static void on_activate_recent_item (GtkRecentChooser *chooser,
2514 filename = gtk_recent_chooser_get_current_uri (chooser);
2515 if (filename != NULL)
2517 GFile *file = g_file_new_for_uri ( filename );
2518 gchar *path = g_file_get_path ( file );
2519 g_object_unref ( file );
2520 if ( self->filename )
2522 GSList *filenames = NULL;
2523 filenames = g_slist_append ( filenames, path );
2524 g_signal_emit ( G_OBJECT(self), window_signals[VW_OPENWINDOW_SIGNAL], 0, filenames );
2525 // NB: GSList & contents are freed by main.open_window
2528 vik_window_open_file ( self, path, TRUE );
2536 static void setup_recent_files (VikWindow *self)
2538 GtkRecentManager *manager;
2539 GtkRecentFilter *filter;
2540 GtkWidget *menu, *menu_item;
2542 filter = gtk_recent_filter_new ();
2543 /* gtk_recent_filter_add_application (filter, g_get_application_name()); */
2544 gtk_recent_filter_add_group(filter, "viking");
2546 manager = gtk_recent_manager_get_default ();
2547 menu = gtk_recent_chooser_menu_new_for_manager (manager);
2548 gtk_recent_chooser_set_sort_type (GTK_RECENT_CHOOSER (menu), GTK_RECENT_SORT_MRU);
2549 gtk_recent_chooser_add_filter (GTK_RECENT_CHOOSER (menu), filter);
2551 menu_item = gtk_ui_manager_get_widget (self->uim, "/ui/MainMenu/File/OpenRecentFile");
2552 gtk_menu_item_set_submenu (GTK_MENU_ITEM (menu_item), menu);
2554 g_signal_connect (G_OBJECT (menu), "item-activated",
2555 G_CALLBACK (on_activate_recent_item), (gpointer) self);
2558 static void update_recently_used_document(const gchar *filename)
2560 /* Update Recently Used Document framework */
2561 GtkRecentManager *manager = gtk_recent_manager_get_default();
2562 GtkRecentData *recent_data = g_slice_new (GtkRecentData);
2563 gchar *groups[] = {"viking", NULL};
2564 GFile *file = g_file_new_for_commandline_arg(filename);
2565 gchar *uri = g_file_get_uri(file);
2566 gchar *basename = g_path_get_basename(filename);
2567 g_object_unref(file);
2570 recent_data->display_name = basename;
2571 recent_data->description = NULL;
2572 recent_data->mime_type = "text/x-gps-data";
2573 recent_data->app_name = (gchar *) g_get_application_name ();
2574 recent_data->app_exec = g_strjoin (" ", g_get_prgname (), "%f", NULL);
2575 recent_data->groups = groups;
2576 recent_data->is_private = FALSE;
2577 if (!gtk_recent_manager_add_full (manager, uri, recent_data))
2579 g_warning (_("Unable to add '%s' to the list of recently used documents"), uri);
2584 g_free (recent_data->app_exec);
2585 g_slice_free (GtkRecentData, recent_data);
2589 * Call this before doing things that may take a long time and otherwise not show any other feedback
2590 * such as loading and saving files
2592 void vik_window_set_busy_cursor ( VikWindow *vw )
2594 gdk_window_set_cursor ( gtk_widget_get_window(GTK_WIDGET(vw)), vw->busy_cursor );
2595 // Viewport has a separate cursor
2596 gdk_window_set_cursor ( gtk_widget_get_window(GTK_WIDGET(vw->viking_vvp)), vw->busy_cursor );
2597 // Ensure cursor updated before doing stuff
2598 while( gtk_events_pending() )
2599 gtk_main_iteration();
2602 void vik_window_clear_busy_cursor ( VikWindow *vw )
2604 gdk_window_set_cursor ( gtk_widget_get_window(GTK_WIDGET(vw)), NULL );
2605 // Restore viewport cursor
2606 gdk_window_set_cursor ( gtk_widget_get_window(GTK_WIDGET(vw->viking_vvp)), vw->viewport_cursor );
2609 void vik_window_open_file ( VikWindow *vw, const gchar *filename, gboolean change_filename )
2611 vik_window_set_busy_cursor ( vw );
2613 // Enable the *new* filename to be accessible by the Layers codez
2614 gchar *original_filename = g_strdup ( vw->filename );
2615 g_free ( vw->filename );
2616 vw->filename = g_strdup ( filename );
2617 gboolean success = FALSE;
2618 gboolean restore_original_filename = FALSE;
2620 vw->loaded_type = a_file_load ( vik_layers_panel_get_top_layer(vw->viking_vlp), vw->viking_vvp, filename );
2621 switch ( vw->loaded_type )
2623 case LOAD_TYPE_READ_FAILURE:
2624 a_dialog_error_msg ( GTK_WINDOW(vw), _("The file you requested could not be opened.") );
2626 case LOAD_TYPE_GPSBABEL_FAILURE:
2627 a_dialog_error_msg ( GTK_WINDOW(vw), _("GPSBabel is required to load files of this type or GPSBabel encountered problems.") );
2629 case LOAD_TYPE_GPX_FAILURE:
2630 a_dialog_error_msg_extra ( GTK_WINDOW(vw), _("Unable to load malformed GPX file %s"), filename );
2632 case LOAD_TYPE_UNSUPPORTED_FAILURE:
2633 a_dialog_error_msg_extra ( GTK_WINDOW(vw), _("Unsupported file type for %s"), filename );
2635 case LOAD_TYPE_VIK_FAILURE_NON_FATAL:
2637 // Since we can process .vik files with issues just show a warning in the status bar
2638 // Not that a user can do much about it... or tells them what this issue is yet...
2639 gchar *msg = g_strdup_printf (_("WARNING: issues encountered loading %s"), a_file_basename (filename) );
2640 vik_statusbar_set_message ( vw->viking_vs, VIK_STATUSBAR_INFO, msg );
2643 // No break, carry on to show any data
2644 case LOAD_TYPE_VIK_SUCCESS:
2646 restore_original_filename = TRUE; // NB Will actually get inverted by the 'success' component below
2647 GtkWidget *mode_button;
2649 if ( change_filename )
2650 window_set_filename ( vw, filename );
2651 mode_button = vik_window_get_drawmode_button ( vw, vik_viewport_get_drawmode ( vw->viking_vvp ) );
2652 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. */
2653 gtk_check_menu_item_set_active ( GTK_CHECK_MENU_ITEM(mode_button), TRUE );
2654 vw->only_updating_coord_mode_ui = FALSE;
2656 vik_layers_panel_change_coord_mode ( vw->viking_vlp, vik_viewport_get_coord_mode ( vw->viking_vvp ) );
2658 mode_button = gtk_ui_manager_get_widget ( vw->uim, "/ui/MainMenu/View/SetShow/ShowScale" );
2659 g_assert ( mode_button );
2660 gtk_check_menu_item_set_active ( GTK_CHECK_MENU_ITEM(mode_button),vik_viewport_get_draw_scale(vw->viking_vvp) );
2662 mode_button = gtk_ui_manager_get_widget ( vw->uim, "/ui/MainMenu/View/SetShow/ShowCenterMark" );
2663 g_assert ( mode_button );
2664 gtk_check_menu_item_set_active ( GTK_CHECK_MENU_ITEM(mode_button),vik_viewport_get_draw_centermark(vw->viking_vvp) );
2666 mode_button = gtk_ui_manager_get_widget ( vw->uim, "/ui/MainMenu/View/SetShow/ShowHighlight" );
2667 g_assert ( mode_button );
2668 gtk_check_menu_item_set_active ( GTK_CHECK_MENU_ITEM(mode_button),vik_viewport_get_draw_highlight (vw->viking_vvp) );
2670 // NB No break, carry on to redraw
2671 //case LOAD_TYPE_OTHER_SUCCESS:
2674 // When LOAD_TYPE_OTHER_SUCCESS *only*, this will maintain the existing Viking project
2675 restore_original_filename = ! restore_original_filename;
2676 update_recently_used_document(filename);
2681 if ( ! success || restore_original_filename )
2682 // Load didn't work or want to keep as the existing Viking project, keep using the original name
2683 window_set_filename ( vw, original_filename );
2684 g_free ( original_filename );
2686 vik_window_clear_busy_cursor ( vw );
2689 static void load_file ( GtkAction *a, VikWindow *vw )
2691 GSList *files = NULL;
2692 GSList *cur_file = NULL;
2694 if (!strcmp(gtk_action_get_name(a), "Open")) {
2697 else if (!strcmp(gtk_action_get_name(a), "Append")) {
2701 g_critical("Houston, we've had a problem.");
2705 if ( ! vw->open_dia )
2707 vw->open_dia = gtk_file_chooser_dialog_new (_("Please select a GPS data file to open. "),
2709 GTK_FILE_CHOOSER_ACTION_OPEN,
2710 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
2711 GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT,
2713 gchar *cwd = g_get_current_dir();
2715 gtk_file_chooser_set_current_folder ( GTK_FILE_CHOOSER(vw->open_dia), cwd );
2719 GtkFileFilter *filter;
2720 // NB file filters are listed this way for alphabetical ordering
2721 #ifdef VIK_CONFIG_GEOCACHES
2722 filter = gtk_file_filter_new ();
2723 gtk_file_filter_set_name( filter, _("Geocaching") );
2724 gtk_file_filter_add_pattern ( filter, "*.loc" ); // No MIME type available
2725 gtk_file_chooser_add_filter (GTK_FILE_CHOOSER(vw->open_dia), filter);
2728 filter = gtk_file_filter_new ();
2729 gtk_file_filter_set_name( filter, _("Google Earth") );
2730 gtk_file_filter_add_mime_type ( filter, "application/vnd.google-earth.kml+xml");
2731 gtk_file_chooser_add_filter (GTK_FILE_CHOOSER(vw->open_dia), filter);
2733 filter = gtk_file_filter_new ();
2734 gtk_file_filter_set_name( filter, _("GPX") );
2735 gtk_file_filter_add_pattern ( filter, "*.gpx" ); // No MIME type available
2736 gtk_file_chooser_add_filter (GTK_FILE_CHOOSER(vw->open_dia), filter);
2738 filter = gtk_file_filter_new ();
2739 gtk_file_filter_set_name( filter, _("Viking") );
2740 gtk_file_filter_add_pattern ( filter, "*.vik" );
2741 gtk_file_filter_add_pattern ( filter, "*.viking" );
2742 gtk_file_chooser_add_filter (GTK_FILE_CHOOSER(vw->open_dia), filter);
2744 // NB could have filters for gpspoint (*.gps,*.gpsoint?) + gpsmapper (*.gsm,*.gpsmapper?)
2745 // However assume this are barely used and thus not worthy of inclusion
2746 // as they'll just make the options too many and have no clear file pattern
2747 // one can always use the all option
2748 filter = gtk_file_filter_new ();
2749 gtk_file_filter_set_name( filter, _("All") );
2750 gtk_file_filter_add_pattern ( filter, "*" );
2751 gtk_file_chooser_add_filter (GTK_FILE_CHOOSER(vw->open_dia), filter);
2752 // Default to any file - same as before open filters were added
2753 gtk_file_chooser_set_filter (GTK_FILE_CHOOSER(vw->open_dia), filter);
2755 gtk_file_chooser_set_select_multiple ( GTK_FILE_CHOOSER(vw->open_dia), TRUE );
2756 gtk_window_set_transient_for ( GTK_WINDOW(vw->open_dia), GTK_WINDOW(vw) );
2757 gtk_window_set_destroy_with_parent ( GTK_WINDOW(vw->open_dia), TRUE );
2759 if ( gtk_dialog_run ( GTK_DIALOG(vw->open_dia) ) == GTK_RESPONSE_ACCEPT )
2761 gtk_widget_hide ( vw->open_dia );
2762 #ifdef VIKING_PROMPT_IF_MODIFIED
2763 if ( (vw->modified || vw->filename) && newwindow )
2765 if ( vw->filename && newwindow )
2767 g_signal_emit ( G_OBJECT(vw), window_signals[VW_OPENWINDOW_SIGNAL], 0, gtk_file_chooser_get_filenames (GTK_FILE_CHOOSER(vw->open_dia) ) );
2769 files = gtk_file_chooser_get_filenames (GTK_FILE_CHOOSER(vw->open_dia) );
2770 gboolean change_fn = newwindow && (g_slist_length(files)==1); /* only change fn if one file */
2771 gboolean first_vik_file = TRUE;
2773 while ( cur_file ) {
2775 gchar *file_name = cur_file->data;
2776 if ( newwindow && check_file_magic_vik ( file_name ) ) {
2777 // Load first of many .vik files in current window
2778 if ( first_vik_file ) {
2779 vik_window_open_file ( vw, file_name, TRUE );
2780 first_vik_file = FALSE;
2783 // Load each subsequent .vik file in a separate window
2784 VikWindow *newvw = vik_window_new_window ();
2786 vik_window_open_file ( newvw, file_name, TRUE );
2791 vik_window_open_file ( vw, file_name, change_fn );
2794 cur_file = g_slist_next (cur_file);
2796 g_slist_free (files);
2800 gtk_widget_hide ( vw->open_dia );
2803 static gboolean save_file_as ( GtkAction *a, VikWindow *vw )
2805 gboolean rv = FALSE;
2807 if ( ! vw->save_dia )
2809 vw->save_dia = gtk_file_chooser_dialog_new (_("Save as Viking File."),
2811 GTK_FILE_CHOOSER_ACTION_SAVE,
2812 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
2813 GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT,
2815 gchar *cwd = g_get_current_dir();
2817 gtk_file_chooser_set_current_folder ( GTK_FILE_CHOOSER(vw->save_dia), cwd );
2821 GtkFileFilter *filter;
2822 filter = gtk_file_filter_new ();
2823 gtk_file_filter_set_name( filter, _("All") );
2824 gtk_file_filter_add_pattern ( filter, "*" );
2825 gtk_file_chooser_add_filter (GTK_FILE_CHOOSER(vw->save_dia), filter);
2827 filter = gtk_file_filter_new ();
2828 gtk_file_filter_set_name( filter, _("Viking") );
2829 gtk_file_filter_add_pattern ( filter, "*.vik" );
2830 gtk_file_filter_add_pattern ( filter, "*.viking" );
2831 gtk_file_chooser_add_filter (GTK_FILE_CHOOSER(vw->save_dia), filter);
2832 // Default to a Viking file
2833 gtk_file_chooser_set_filter (GTK_FILE_CHOOSER(vw->save_dia), filter);
2835 gtk_window_set_transient_for ( GTK_WINDOW(vw->save_dia), GTK_WINDOW(vw) );
2836 gtk_window_set_destroy_with_parent ( GTK_WINDOW(vw->save_dia), TRUE );
2838 // Auto append / replace extension with '.vik' to the suggested file name as it's going to be a Viking File
2839 gchar* auto_save_name = g_strdup ( window_get_filename ( vw ) );
2840 if ( ! check_file_ext ( auto_save_name, ".vik" ) )
2841 auto_save_name = g_strconcat ( auto_save_name, ".vik", NULL );
2843 gtk_file_chooser_set_current_name (GTK_FILE_CHOOSER(vw->save_dia), auto_save_name);
2845 while ( gtk_dialog_run ( GTK_DIALOG(vw->save_dia) ) == GTK_RESPONSE_ACCEPT )
2847 fn = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER(vw->save_dia) );
2848 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 ) ) )
2850 window_set_filename ( vw, fn );
2851 rv = window_save ( vw );
2852 vw->modified = FALSE;
2856 g_free ( auto_save_name );
2857 gtk_widget_hide ( vw->save_dia );
2861 static gboolean window_save ( VikWindow *vw )
2863 vik_window_set_busy_cursor ( vw );
2864 gboolean success = TRUE;
2866 if ( a_file_save ( vik_layers_panel_get_top_layer ( vw->viking_vlp ), vw->viking_vvp, vw->filename ) )
2868 update_recently_used_document ( vw->filename );
2872 a_dialog_error_msg ( GTK_WINDOW(vw), _("The filename you requested could not be opened for writing.") );
2875 vik_window_clear_busy_cursor ( vw );
2879 static gboolean save_file ( GtkAction *a, VikWindow *vw )
2881 if ( ! vw->filename )
2882 return save_file_as ( NULL, vw );
2885 vw->modified = FALSE;
2886 return window_save ( vw );
2893 * Export all TRW Layers in the list to individual files in the specified directory
2895 * Returns: %TRUE on success
2897 static gboolean export_to ( VikWindow *vw, GList *gl, VikFileType_t vft, const gchar *dir, const gchar *extension )
2899 gboolean success = TRUE;
2901 gint export_count = 0;
2903 vik_window_set_busy_cursor ( vw );
2907 gchar *fn = g_strconcat ( dir, G_DIR_SEPARATOR_S, VIK_LAYER(gl->data)->name, extension, NULL );
2909 // Some protection in attempting to write too many same named files
2910 // As this will get horribly slow...
2911 gboolean safe = FALSE;
2913 while ( ii < 5000 ) {
2914 if ( g_file_test ( fn, G_FILE_TEST_EXISTS ) ) {
2917 fn = g_strdup_printf ( "%s%s%s#%03d%s", dir, G_DIR_SEPARATOR_S, VIK_LAYER(gl->data)->name, ii, extension );
2928 // NB: We allow exporting empty layers
2930 gboolean this_success = a_file_export ( VIK_TRW_LAYER(gl->data), fn, vft, NULL, TRUE );
2932 // Show some progress
2933 if ( this_success ) {
2935 gchar *message = g_strdup_printf ( _("Exporting to file: %s"), fn );
2936 vik_statusbar_set_message ( vw->viking_vs, VIK_STATUSBAR_INFO, message );
2937 while ( gtk_events_pending() )
2938 gtk_main_iteration ();
2942 success = success && this_success;
2946 gl = g_list_next ( gl );
2949 vik_window_clear_busy_cursor ( vw );
2951 // Confirm what happened.
2952 gchar *message = g_strdup_printf ( _("Exported files: %d"), export_count );
2953 vik_statusbar_set_message ( vw->viking_vs, VIK_STATUSBAR_INFO, message );
2959 static void export_to_common ( VikWindow *vw, VikFileType_t vft, const gchar *extension )
2961 GList *gl = vik_layers_panel_get_all_layers_of_type ( vw->viking_vlp, VIK_LAYER_TRW, TRUE );
2964 a_dialog_info_msg ( GTK_WINDOW(vw), _("Nothing to Export!") );
2968 GtkWidget *dialog = gtk_file_chooser_dialog_new ( _("Export to directory"),
2970 GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER,
2972 GTK_RESPONSE_REJECT,
2974 GTK_RESPONSE_ACCEPT,
2976 gtk_window_set_transient_for ( GTK_WINDOW(dialog), GTK_WINDOW(vw) );
2977 gtk_window_set_destroy_with_parent ( GTK_WINDOW(dialog), TRUE );
2978 gtk_window_set_modal ( GTK_WINDOW(dialog), TRUE );
2980 gtk_widget_show_all ( dialog );
2982 if ( gtk_dialog_run ( GTK_DIALOG(dialog) ) == GTK_RESPONSE_ACCEPT ) {
2983 gchar *dir = gtk_file_chooser_get_filename ( GTK_FILE_CHOOSER(dialog) );
2984 gtk_widget_destroy ( dialog );
2986 if ( !export_to ( vw, gl, vft, dir, extension ) )
2987 a_dialog_error_msg ( GTK_WINDOW(vw),_("Could not convert all files") );
2992 gtk_widget_destroy ( dialog );
2997 static void export_to_gpx ( GtkAction *a, VikWindow *vw )
2999 export_to_common ( vw, FILE_TYPE_GPX, ".gpx" );
3002 static void export_to_kml ( GtkAction *a, VikWindow *vw )
3004 export_to_common ( vw, FILE_TYPE_KML, ".kml" );
3007 #if !GLIB_CHECK_VERSION(2,26,0)
3008 typedef struct stat GStatBuf;
3011 static void file_properties_cb ( GtkAction *a, VikWindow *vw )
3013 gchar *message = NULL;
3014 if ( vw->filename ) {
3015 if ( g_file_test ( vw->filename, G_FILE_TEST_EXISTS ) ) {
3016 // Get some timestamp information of the file
3018 if ( g_stat ( vw->filename, &stat_buf ) == 0 ) {
3020 strftime ( time_buf, sizeof(time_buf), "%c", gmtime((const time_t *)&stat_buf.st_mtime) );
3022 gint byte_size = stat_buf.st_size;
3023 // See http://en.wikipedia.org/wiki/Megabyte (and Kilobyte)
3024 // hence using 1000 rather than 1024
3025 // so get output as per 'ls' or the Gtk file open dialog
3026 if ( byte_size < 1000 )
3027 size = g_strdup_printf ( _("%d bytes"), byte_size );
3028 else if ( byte_size < 1000*1000 )
3029 size = g_strdup_printf ( _("%3.1f kB"), (gdouble)byte_size / 1000 );
3031 size = g_strdup_printf ( _("%3.1f MB"), (gdouble)byte_size / (1000*1000) );
3032 message = g_strdup_printf ( _("%s\n\n%s\n\n%s"), vw->filename, time_buf, size );
3037 message = g_strdup ( _("File not accessible") );
3040 message = g_strdup ( _("No Viking File") );
3043 a_dialog_info_msg ( GTK_WINDOW(vw), message );
3047 static void acquire_from_gps ( GtkAction *a, VikWindow *vw )
3049 // Via the file menu, acquiring from a GPS makes a new layer
3050 // this has always been the way (not entirely sure if this was the real intention!)
3051 // thus maintain the behaviour ATM.
3052 // Hence explicit setting here (as the value may be changed elsewhere)
3053 vik_datasource_gps_interface.mode = VIK_DATASOURCE_CREATENEWLAYER;
3054 a_acquire(vw, vw->viking_vlp, vw->viking_vvp, &vik_datasource_gps_interface, NULL, NULL );
3057 static void acquire_from_file ( GtkAction *a, VikWindow *vw )
3059 a_acquire(vw, vw->viking_vlp, vw->viking_vvp, &vik_datasource_file_interface, NULL, NULL );
3062 static void acquire_from_routing ( GtkAction *a, VikWindow *vw )
3064 a_acquire(vw, vw->viking_vlp, vw->viking_vvp, &vik_datasource_routing_interface, NULL, NULL );
3067 #ifdef VIK_CONFIG_OPENSTREETMAP
3068 static void acquire_from_osm ( GtkAction *a, VikWindow *vw )
3070 a_acquire(vw, vw->viking_vlp, vw->viking_vvp, &vik_datasource_osm_interface, NULL, NULL );
3073 static void acquire_from_my_osm ( GtkAction *a, VikWindow *vw )
3075 a_acquire(vw, vw->viking_vlp, vw->viking_vvp, &vik_datasource_osm_my_traces_interface, NULL, NULL );
3079 #ifdef VIK_CONFIG_GEOCACHES
3080 static void acquire_from_gc ( GtkAction *a, VikWindow *vw )
3082 a_acquire(vw, vw->viking_vlp, vw->viking_vvp, &vik_datasource_gc_interface, NULL, NULL );
3086 #ifdef VIK_CONFIG_GEOTAG
3087 static void acquire_from_geotag ( GtkAction *a, VikWindow *vw )
3089 vik_datasource_geotag_interface.mode = VIK_DATASOURCE_CREATENEWLAYER;
3090 a_acquire(vw, vw->viking_vlp, vw->viking_vvp, &vik_datasource_geotag_interface, NULL, NULL );
3094 #ifdef VIK_CONFIG_GEONAMES
3095 static void acquire_from_wikipedia ( GtkAction *a, VikWindow *vw )
3097 a_acquire(vw, vw->viking_vlp, vw->viking_vvp, &vik_datasource_wikipedia_interface, NULL, NULL );
3101 static void acquire_from_url ( GtkAction *a, VikWindow *vw )
3103 vik_datasource_url_interface.mode = VIK_DATASOURCE_CREATENEWLAYER;
3104 a_acquire(vw, vw->viking_vlp, vw->viking_vvp, &vik_datasource_url_interface, NULL, NULL );
3107 static void goto_default_location( GtkAction *a, VikWindow *vw)
3110 ll.lat = a_vik_get_default_lat();
3111 ll.lon = a_vik_get_default_long();
3112 vik_viewport_set_center_latlon(vw->viking_vvp, &ll, TRUE);
3113 vik_layers_panel_emit_update(vw->viking_vlp);
3117 static void goto_address( GtkAction *a, VikWindow *vw)
3119 a_vik_goto ( vw, vw->viking_vvp );
3120 vik_layers_panel_emit_update ( vw->viking_vlp );
3123 static void mapcache_flush_cb ( GtkAction *a, VikWindow *vw )
3128 static void layer_defaults_cb ( GtkAction *a, VikWindow *vw )
3130 gchar **texts = g_strsplit ( gtk_action_get_name(a), "Layer", 0 );
3133 return; // Internally broken :(
3135 if ( ! a_layer_defaults_show_window ( GTK_WINDOW(vw), texts[1] ) )
3136 a_dialog_info_msg ( GTK_WINDOW(vw), _("This layer has no configurable properties.") );
3137 // NB no update needed
3139 g_strfreev ( texts );
3142 static void preferences_change_update ( VikWindow *vw, gpointer data )
3144 // Want to update all TrackWaypoint layers
3145 GList *layers = vik_layers_panel_get_all_layers_of_type ( vw->viking_vlp, VIK_LAYER_TRW, TRUE );
3151 // Reset the individual waypoints themselves due to the preferences change
3152 VikTrwLayer *vtl = VIK_TRW_LAYER(layers->data);
3153 vik_trw_layer_reset_waypoints ( vtl );
3154 layers = g_list_next ( layers );
3157 g_list_free ( layers );
3162 static void preferences_cb ( GtkAction *a, VikWindow *vw )
3164 gboolean wp_icon_size = a_vik_get_use_large_waypoint_icons();
3166 a_preferences_show_window ( GTK_WINDOW(vw) );
3168 // Has the waypoint size setting changed?
3169 if (wp_icon_size != a_vik_get_use_large_waypoint_icons()) {
3170 // Delete icon indexing 'cache' and so automatically regenerates with the new setting when changed
3171 clear_garmin_icon_syms ();
3173 // Update all windows
3174 g_slist_foreach ( window_list, (GFunc) preferences_change_update, NULL );
3178 static void default_location_cb ( GtkAction *a, VikWindow *vw )
3180 /* Simplistic repeat of preference setting
3181 Only the name & type are important for setting the preference via this 'external' way */
3182 VikLayerParam pref_lat[] = {
3183 { VIK_LAYER_NUM_TYPES,
3184 VIKING_PREFERENCES_NAMESPACE "default_latitude",
3185 VIK_LAYER_PARAM_DOUBLE,
3188 VIK_LAYER_WIDGET_SPINBUTTON,
3196 VikLayerParam pref_lon[] = {
3197 { VIK_LAYER_NUM_TYPES,
3198 VIKING_PREFERENCES_NAMESPACE "default_longitude",
3199 VIK_LAYER_PARAM_DOUBLE,
3202 VIK_LAYER_WIDGET_SPINBUTTON,
3211 /* Get current center */
3213 vik_coord_to_latlon ( vik_viewport_get_center ( vw->viking_vvp ), &ll );
3215 /* Apply to preferences */
3216 VikLayerParamData vlp_data;
3217 vlp_data.d = ll.lat;
3218 a_preferences_run_setparam (vlp_data, pref_lat);
3219 vlp_data.d = ll.lon;
3220 a_preferences_run_setparam (vlp_data, pref_lon);
3221 /* Remember to save */
3222 a_preferences_save_to_file();
3225 static void clear_cb ( GtkAction *a, VikWindow *vw )
3227 vik_layers_panel_clear ( vw->viking_vlp );
3228 window_set_filename ( vw, NULL );
3232 static void window_close ( GtkAction *a, VikWindow *vw )
3234 if ( ! delete_event ( vw ) )
3235 gtk_widget_destroy ( GTK_WIDGET(vw) );
3238 static gboolean save_file_and_exit ( GtkAction *a, VikWindow *vw )
3240 if (save_file( NULL, vw)) {
3241 window_close( NULL, vw);
3248 static void zoom_to_cb ( GtkAction *a, VikWindow *vw )
3250 gdouble xmpp = vik_viewport_get_xmpp ( vw->viking_vvp ), ympp = vik_viewport_get_ympp ( vw->viking_vvp );
3251 if ( a_dialog_custom_zoom ( GTK_WINDOW(vw), &xmpp, &ympp ) )
3253 vik_viewport_set_xmpp ( vw->viking_vvp, xmpp );
3254 vik_viewport_set_ympp ( vw->viking_vvp, ympp );
3259 static void save_image_file ( VikWindow *vw, const gchar *fn, guint w, guint h, gdouble zoom, gboolean save_as_png )
3261 /* more efficient way: stuff draws directly to pixbuf (fork viewport) */
3262 GdkPixbuf *pixbuf_to_save;
3263 gdouble old_xmpp, old_ympp;
3264 GError *error = NULL;
3266 GtkWidget *msgbox = gtk_message_dialog_new ( GTK_WINDOW(vw),
3267 GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
3270 _("Generating image file...") );
3272 g_signal_connect_swapped (msgbox, "response", G_CALLBACK (gtk_widget_destroy), msgbox);
3273 // Ensure dialog shown
3274 gtk_widget_show_all ( msgbox );
3276 vik_statusbar_set_message ( vw->viking_vs, VIK_STATUSBAR_INFO, _("Generating image file...") );
3277 while ( gtk_events_pending() )
3278 gtk_main_iteration ();
3279 // Despite many efforts & variations, GTK on my Linux system doesn't show the actual msgbox contents :(
3280 // At least the empty box can give a clue something's going on + the statusbar msg...
3281 // Windows version under Wine OK!
3283 /* backup old zoom & set new */
3284 old_xmpp = vik_viewport_get_xmpp ( vw->viking_vvp );
3285 old_ympp = vik_viewport_get_ympp ( vw->viking_vvp );
3286 vik_viewport_set_zoom ( vw->viking_vvp, zoom );
3288 /* reset width and height: */
3289 vik_viewport_configure_manually ( vw->viking_vvp, w, h );
3291 /* draw all layers */
3294 /* save buffer as file. */
3295 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);
3296 if ( !pixbuf_to_save ) {
3297 g_warning("Failed to generate internal pixmap size: %d x %d", w, h);
3298 gtk_message_dialog_set_markup ( GTK_MESSAGE_DIALOG(msgbox), _("Failed to generate internal image.\n\nTry creating a smaller image.") );
3302 gdk_pixbuf_save ( pixbuf_to_save, fn, save_as_png ? "png" : "jpeg", &error, NULL );
3305 g_warning("Unable to write to file %s: %s", fn, error->message );
3306 gtk_message_dialog_set_markup ( GTK_MESSAGE_DIALOG(msgbox), _("Failed to generate image file.") );
3307 g_error_free (error);
3311 gtk_message_dialog_set_markup ( GTK_MESSAGE_DIALOG(msgbox), _("Image file generated.") );
3313 g_object_unref ( G_OBJECT(pixbuf_to_save) );
3316 vik_statusbar_set_message ( vw->viking_vs, VIK_STATUSBAR_INFO, "" );
3317 gtk_dialog_add_button ( GTK_DIALOG(msgbox), GTK_STOCK_OK, GTK_RESPONSE_OK );
3318 gtk_dialog_run ( GTK_DIALOG(msgbox) ); // Don't care about the result
3320 /* pretend like nothing happened ;) */
3321 vik_viewport_set_xmpp ( vw->viking_vvp, old_xmpp );
3322 vik_viewport_set_ympp ( vw->viking_vvp, old_ympp );
3323 vik_viewport_configure ( vw->viking_vvp );
3327 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 )
3329 gulong size = sizeof(gchar) * (strlen(fn) + 15);
3330 gchar *name_of_file = g_malloc ( size );
3332 struct UTM utm_orig, utm;
3334 /* *** copied from above *** */
3335 GdkPixbuf *pixbuf_to_save;
3336 gdouble old_xmpp, old_ympp;
3337 GError *error = NULL;
3339 /* backup old zoom & set new */
3340 old_xmpp = vik_viewport_get_xmpp ( vw->viking_vvp );
3341 old_ympp = vik_viewport_get_ympp ( vw->viking_vvp );
3342 vik_viewport_set_zoom ( vw->viking_vvp, zoom );
3344 /* reset width and height: do this only once for all images (same size) */
3345 vik_viewport_configure_manually ( vw->viking_vvp, w, h );
3346 /* *** end copy from above *** */
3348 g_assert ( vik_viewport_get_coord_mode ( vw->viking_vvp ) == VIK_COORD_UTM );
3352 utm_orig = *((const struct UTM *)vik_viewport_get_center ( vw->viking_vvp ));
3354 for ( y = 1; y <= tiles_h; y++ )
3356 for ( x = 1; x <= tiles_w; x++ )
3358 g_snprintf ( name_of_file, size, "%s%cy%d-x%d.%s", fn, G_DIR_SEPARATOR, y, x, save_as_png ? "png" : "jpg" );
3360 if ( tiles_w & 0x1 )
3361 utm.easting += ((gdouble)x - ceil(((gdouble)tiles_w)/2)) * (w*zoom);
3363 utm.easting += ((gdouble)x - (((gdouble)tiles_w)+1)/2) * (w*zoom);
3364 if ( tiles_h & 0x1 ) /* odd */
3365 utm.northing -= ((gdouble)y - ceil(((gdouble)tiles_h)/2)) * (h*zoom);
3367 utm.northing -= ((gdouble)y - (((gdouble)tiles_h)+1)/2) * (h*zoom);
3369 /* move to correct place. */
3370 vik_viewport_set_center_utm ( vw->viking_vvp, &utm, FALSE );
3374 /* save buffer as file. */
3375 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);
3376 gdk_pixbuf_save ( pixbuf_to_save, name_of_file, save_as_png ? "png" : "jpeg", &error, NULL );
3379 g_warning("Unable to write to file %s: %s", name_of_file, error->message );
3380 g_error_free (error);
3383 g_object_unref ( G_OBJECT(pixbuf_to_save) );
3387 vik_viewport_set_center_utm ( vw->viking_vvp, &utm_orig, FALSE );
3388 vik_viewport_set_xmpp ( vw->viking_vvp, old_xmpp );
3389 vik_viewport_set_ympp ( vw->viking_vvp, old_ympp );
3390 vik_viewport_configure ( vw->viking_vvp );
3393 g_free ( name_of_file );
3396 static void draw_to_image_file_current_window_cb(GtkWidget* widget,GdkEventButton *event,gpointer *pass_along)
3398 VikWindow *vw = VIK_WINDOW(pass_along[0]);
3399 GtkSpinButton *width_spin = GTK_SPIN_BUTTON(pass_along[1]), *height_spin = GTK_SPIN_BUTTON(pass_along[2]);
3401 gint active = gtk_combo_box_get_active ( GTK_COMBO_BOX(pass_along[3]) );
3402 gdouble zoom = pow (2, active-2 );
3404 gdouble width_min, width_max, height_min, height_max;
3407 gtk_spin_button_get_range ( width_spin, &width_min, &width_max );
3408 gtk_spin_button_get_range ( height_spin, &height_min, &height_max );
3410 /* TODO: support for xzoom and yzoom values */
3411 width = vik_viewport_get_width ( vw->viking_vvp ) * vik_viewport_get_xmpp ( vw->viking_vvp ) / zoom;
3412 height = vik_viewport_get_height ( vw->viking_vvp ) * vik_viewport_get_xmpp ( vw->viking_vvp ) / zoom;
3414 if ( width > width_max || width < width_min || height > height_max || height < height_min )
3415 a_dialog_info_msg ( GTK_WINDOW(vw), _("Viewable region outside allowable pixel size bounds for image. Clipping width/height values.") );
3417 gtk_spin_button_set_value ( width_spin, width );
3418 gtk_spin_button_set_value ( height_spin, height );
3421 static void draw_to_image_file_total_area_cb (GtkSpinButton *spinbutton, gpointer *pass_along)
3423 GtkSpinButton *width_spin = GTK_SPIN_BUTTON(pass_along[1]), *height_spin = GTK_SPIN_BUTTON(pass_along[2]);
3425 gint active = gtk_combo_box_get_active ( GTK_COMBO_BOX(pass_along[3]) );
3426 gdouble zoom = pow (2, active-2 );
3430 w = gtk_spin_button_get_value(width_spin) * zoom;
3431 h = gtk_spin_button_get_value(height_spin) * zoom;
3432 if (pass_along[4]) /* save many images; find TOTAL area covered */
3434 w *= gtk_spin_button_get_value(GTK_SPIN_BUTTON(pass_along[4]));
3435 h *= gtk_spin_button_get_value(GTK_SPIN_BUTTON(pass_along[5]));
3437 vik_units_distance_t dist_units = a_vik_get_units_distance ();
3438 switch (dist_units) {
3439 case VIK_UNITS_DISTANCE_KILOMETRES:
3440 label_text = g_strdup_printf ( _("Total area: %ldm x %ldm (%.3f sq. km)"), (glong)w, (glong)h, (w*h/1000000));
3442 case VIK_UNITS_DISTANCE_MILES:
3443 label_text = g_strdup_printf ( _("Total area: %ldm x %ldm (%.3f sq. miles)"), (glong)w, (glong)h, (w*h/2589988.11));
3446 label_text = g_strdup_printf ("Just to keep the compiler happy");
3447 g_critical("Houston, we've had a problem. distance=%d", dist_units);
3450 gtk_label_set_text(GTK_LABEL(pass_along[6]), label_text);
3451 g_free ( label_text );
3455 * Get an allocated filename (or directory as specified)
3457 static gchar* draw_image_filename ( VikWindow *vw, gboolean one_image_only )
3460 if ( one_image_only )
3463 if (!vw->save_img_dia) {
3464 vw->save_img_dia = gtk_file_chooser_dialog_new (_("Save Image"),
3466 GTK_FILE_CHOOSER_ACTION_SAVE,
3467 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
3468 GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT,
3471 gchar *cwd = g_get_current_dir();
3473 gtk_file_chooser_set_current_folder ( GTK_FILE_CHOOSER(vw->save_img_dia), cwd );
3477 GtkFileChooser *chooser = GTK_FILE_CHOOSER ( vw->save_img_dia );
3479 GtkFileFilter *filter;
3480 filter = gtk_file_filter_new ();
3481 gtk_file_filter_set_name ( filter, _("All") );
3482 gtk_file_filter_add_pattern ( filter, "*" );
3483 gtk_file_chooser_add_filter ( chooser, filter );
3485 filter = gtk_file_filter_new ();
3486 gtk_file_filter_set_name ( filter, _("JPG") );
3487 gtk_file_filter_add_mime_type ( filter, "image/jpeg");
3488 gtk_file_chooser_add_filter ( chooser, filter );
3490 if ( !vw->draw_image_save_as_png )
3491 gtk_file_chooser_set_filter ( chooser, filter );
3493 filter = gtk_file_filter_new ();
3494 gtk_file_filter_set_name ( filter, _("PNG") );
3495 gtk_file_filter_add_mime_type ( filter, "image/png");
3496 gtk_file_chooser_add_filter ( chooser, filter );
3498 if ( vw->draw_image_save_as_png )
3499 gtk_file_chooser_set_filter ( chooser, filter );
3501 gtk_window_set_transient_for ( GTK_WINDOW(vw->save_img_dia), GTK_WINDOW(vw) );
3502 gtk_window_set_destroy_with_parent ( GTK_WINDOW(vw->save_img_dia), TRUE );
3505 if ( gtk_dialog_run ( GTK_DIALOG(vw->save_img_dia) ) == GTK_RESPONSE_ACCEPT ) {
3506 fn = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER(vw->save_img_dia) );
3507 if ( g_file_test ( fn, G_FILE_TEST_EXISTS ) )
3508 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 ) ) )
3511 gtk_widget_hide ( vw->save_img_dia );
3515 // For some reason this method is only written to work in UTM...
3516 if ( vik_viewport_get_coord_mode(vw->viking_vvp) != VIK_COORD_UTM ) {
3517 a_dialog_error_msg ( GTK_WINDOW(vw), _("You must be in UTM mode to use this feature") );
3521 if (!vw->save_img_dir_dia) {
3522 vw->save_img_dir_dia = gtk_file_chooser_dialog_new (_("Choose a directory to hold images"),
3524 GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER,
3525 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
3526 GTK_STOCK_OK, GTK_RESPONSE_ACCEPT,
3528 gtk_window_set_transient_for ( GTK_WINDOW(vw->save_img_dir_dia), GTK_WINDOW(vw) );
3529 gtk_window_set_destroy_with_parent ( GTK_WINDOW(vw->save_img_dir_dia), TRUE );
3532 if ( gtk_dialog_run ( GTK_DIALOG(vw->save_img_dir_dia) ) == GTK_RESPONSE_ACCEPT ) {
3533 fn = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER(vw->save_img_dir_dia) );
3535 gtk_widget_hide ( vw->save_img_dir_dia );
3540 static void draw_to_image_file ( VikWindow *vw, gboolean one_image_only )
3542 /* todo: default for answers inside VikWindow or static (thruout instance) */
3543 GtkWidget *dialog = gtk_dialog_new_with_buttons ( _("Save to Image File"), GTK_WINDOW(vw),
3544 GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
3546 GTK_RESPONSE_REJECT,
3548 GTK_RESPONSE_ACCEPT,
3550 GtkWidget *width_label, *width_spin, *height_label, *height_spin;
3551 GtkWidget *png_radio, *jpeg_radio;
3552 GtkWidget *current_window_button;
3553 gpointer current_window_pass_along[7];
3554 GtkWidget *zoom_label, *zoom_combo;
3555 GtkWidget *total_size_label;
3557 /* only used if (!one_image_only) */
3558 GtkWidget *tiles_width_spin = NULL, *tiles_height_spin = NULL;
3560 width_label = gtk_label_new ( _("Width (pixels):") );
3561 width_spin = gtk_spin_button_new ( GTK_ADJUSTMENT(gtk_adjustment_new ( vw->draw_image_width, 10, 50000, 10, 100, 0 )), 10, 0 );
3562 height_label = gtk_label_new ( _("Height (pixels):") );
3563 height_spin = gtk_spin_button_new ( GTK_ADJUSTMENT(gtk_adjustment_new ( vw->draw_image_height, 10, 50000, 10, 100, 0 )), 10, 0 );
3565 GtkWidget *win_warning_label = gtk_label_new ( _("WARNING: USING LARGE IMAGES OVER 10000x10000\nMAY CRASH THE PROGRAM!") );
3567 zoom_label = gtk_label_new ( _("Zoom (meters per pixel):") );
3568 /* TODO: separate xzoom and yzoom factors */
3569 zoom_combo = create_zoom_combo_all_levels();
3571 gdouble mpp = vik_viewport_get_xmpp(vw->viking_vvp);
3572 gint active = 2 + round ( log (mpp) / log (2) );
3574 // Can we not hard code size here?
3579 gtk_combo_box_set_active ( GTK_COMBO_BOX(zoom_combo), active );
3581 total_size_label = gtk_label_new ( NULL );
3583 current_window_button = gtk_button_new_with_label ( _("Area in current viewable window") );
3584 current_window_pass_along [0] = vw;
3585 current_window_pass_along [1] = width_spin;
3586 current_window_pass_along [2] = height_spin;
3587 current_window_pass_along [3] = zoom_combo;
3588 current_window_pass_along [4] = NULL; /* used for one_image_only != 1 */
3589 current_window_pass_along [5] = NULL;
3590 current_window_pass_along [6] = total_size_label;
3591 g_signal_connect ( G_OBJECT(current_window_button), "button_press_event", G_CALLBACK(draw_to_image_file_current_window_cb), current_window_pass_along );
3593 png_radio = gtk_radio_button_new_with_label ( NULL, _("Save as PNG") );
3594 jpeg_radio = gtk_radio_button_new_with_label_from_widget ( GTK_RADIO_BUTTON(png_radio), _("Save as JPEG") );
3596 gtk_box_pack_start (GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), png_radio, FALSE, FALSE, 0);
3597 gtk_box_pack_start (GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), jpeg_radio, FALSE, FALSE, 0);
3599 if ( ! vw->draw_image_save_as_png )
3600 gtk_toggle_button_set_active ( GTK_TOGGLE_BUTTON(jpeg_radio), TRUE );
3602 gtk_box_pack_start (GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), width_label, FALSE, FALSE, 0);
3603 gtk_box_pack_start (GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), width_spin, FALSE, FALSE, 0);
3604 gtk_box_pack_start (GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), height_label, FALSE, FALSE, 0);
3605 gtk_box_pack_start (GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), height_spin, FALSE, FALSE, 0);
3607 gtk_box_pack_start (GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), win_warning_label, FALSE, FALSE, 0);
3609 gtk_box_pack_start (GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), current_window_button, FALSE, FALSE, 0);
3610 gtk_box_pack_start (GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), zoom_label, FALSE, FALSE, 0);
3611 gtk_box_pack_start (GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), zoom_combo, FALSE, FALSE, 0);
3613 if ( ! one_image_only )
3615 GtkWidget *tiles_width_label, *tiles_height_label;
3617 tiles_width_label = gtk_label_new ( _("East-west image tiles:") );
3618 tiles_width_spin = gtk_spin_button_new ( GTK_ADJUSTMENT(gtk_adjustment_new ( 5, 1, 10, 1, 100, 0 )), 1, 0 );
3619 tiles_height_label = gtk_label_new ( _("North-south image tiles:") );
3620 tiles_height_spin = gtk_spin_button_new ( GTK_ADJUSTMENT(gtk_adjustment_new ( 5, 1, 10, 1, 100, 0 )), 1, 0 );
3621 gtk_box_pack_start (GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), tiles_width_label, FALSE, FALSE, 0);
3622 gtk_box_pack_start (GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), tiles_width_spin, FALSE, FALSE, 0);
3623 gtk_box_pack_start (GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), tiles_height_label, FALSE, FALSE, 0);
3624 gtk_box_pack_start (GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), tiles_height_spin, FALSE, FALSE, 0);
3626 current_window_pass_along [4] = tiles_width_spin;
3627 current_window_pass_along [5] = tiles_height_spin;
3628 g_signal_connect ( G_OBJECT(tiles_width_spin), "value-changed", G_CALLBACK(draw_to_image_file_total_area_cb), current_window_pass_along );
3629 g_signal_connect ( G_OBJECT(tiles_height_spin), "value-changed", G_CALLBACK(draw_to_image_file_total_area_cb), current_window_pass_along );
3631 gtk_box_pack_start (GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), total_size_label, FALSE, FALSE, 0);
3632 g_signal_connect ( G_OBJECT(width_spin), "value-changed", G_CALLBACK(draw_to_image_file_total_area_cb), current_window_pass_along );
3633 g_signal_connect ( G_OBJECT(height_spin), "value-changed", G_CALLBACK(draw_to_image_file_total_area_cb), current_window_pass_along );
3634 g_signal_connect ( G_OBJECT(zoom_combo), "changed", G_CALLBACK(draw_to_image_file_total_area_cb), current_window_pass_along );
3636 draw_to_image_file_total_area_cb ( NULL, current_window_pass_along ); /* set correct size info now */
3638 gtk_dialog_set_default_response ( GTK_DIALOG(dialog), GTK_RESPONSE_ACCEPT );
3640 gtk_widget_show_all ( gtk_dialog_get_content_area(GTK_DIALOG(dialog)) );
3642 if ( gtk_dialog_run ( GTK_DIALOG(dialog) ) == GTK_RESPONSE_ACCEPT )
3644 gtk_widget_hide ( GTK_WIDGET(dialog) );
3646 gchar *fn = draw_image_filename ( vw, one_image_only );
3650 gint active = gtk_combo_box_get_active ( GTK_COMBO_BOX(zoom_combo) );
3651 gdouble zoom = pow (2, active-2 );
3653 if ( one_image_only )
3654 save_image_file ( vw, fn,
3655 vw->draw_image_width = gtk_spin_button_get_value_as_int ( GTK_SPIN_BUTTON(width_spin) ),
3656 vw->draw_image_height = gtk_spin_button_get_value_as_int ( GTK_SPIN_BUTTON(height_spin) ),
3658 vw->draw_image_save_as_png = gtk_toggle_button_get_active ( GTK_TOGGLE_BUTTON(png_radio) ) );
3660 // NB is in UTM mode ATM
3661 save_image_dir ( vw, fn,
3662 vw->draw_image_width = gtk_spin_button_get_value_as_int ( GTK_SPIN_BUTTON(width_spin) ),
3663 vw->draw_image_height = gtk_spin_button_get_value_as_int ( GTK_SPIN_BUTTON(height_spin) ),
3665 vw->draw_image_save_as_png = gtk_toggle_button_get_active ( GTK_TOGGLE_BUTTON(png_radio) ),
3666 gtk_spin_button_get_value ( GTK_SPIN_BUTTON(tiles_width_spin) ),
3667 gtk_spin_button_get_value ( GTK_SPIN_BUTTON(tiles_height_spin) ) );
3672 gtk_widget_destroy ( GTK_WIDGET(dialog) );
3676 static void draw_to_image_file_cb ( GtkAction *a, VikWindow *vw )
3678 draw_to_image_file ( vw, TRUE );
3681 static void draw_to_image_dir_cb ( GtkAction *a, VikWindow *vw )
3683 draw_to_image_file ( vw, FALSE );
3686 static void print_cb ( GtkAction *a, VikWindow *vw )
3688 a_print(vw, vw->viking_vvp);
3691 /* really a misnomer: changes coord mode (actual coordinates) AND/OR draw mode (viewport only) */
3692 static void window_change_coord_mode_cb ( GtkAction *old_a, GtkAction *a, VikWindow *vw )
3694 VikViewportDrawMode drawmode;
3695 if (!strcmp(gtk_action_get_name(a), "ModeUTM")) {
3696 drawmode = VIK_VIEWPORT_DRAWMODE_UTM;
3698 else if (!strcmp(gtk_action_get_name(a), "ModeLatLon")) {
3699 drawmode = VIK_VIEWPORT_DRAWMODE_LATLON;
3701 else if (!strcmp(gtk_action_get_name(a), "ModeExpedia")) {
3702 drawmode = VIK_VIEWPORT_DRAWMODE_EXPEDIA;
3704 else if (!strcmp(gtk_action_get_name(a), "ModeMercator")) {
3705 drawmode = VIK_VIEWPORT_DRAWMODE_MERCATOR;
3708 g_critical("Houston, we've had a problem.");
3712 if ( !vw->only_updating_coord_mode_ui )
3714 VikViewportDrawMode olddrawmode = vik_viewport_get_drawmode ( vw->viking_vvp );
3715 if ( olddrawmode != drawmode )
3717 /* this takes care of coord mode too */
3718 vik_viewport_set_drawmode ( vw->viking_vvp, drawmode );
3719 if ( drawmode == VIK_VIEWPORT_DRAWMODE_UTM ) {
3720 vik_layers_panel_change_coord_mode ( vw->viking_vlp, VIK_COORD_UTM );
3721 } else if ( olddrawmode == VIK_VIEWPORT_DRAWMODE_UTM ) {
3722 vik_layers_panel_change_coord_mode ( vw->viking_vlp, VIK_COORD_LATLON );
3729 static void set_draw_scale ( GtkAction *a, VikWindow *vw )
3731 GtkWidget *check_box = gtk_ui_manager_get_widget ( vw->uim, "/ui/MainMenu/View/SetShow/ShowScale" );
3732 g_assert(check_box);
3733 gboolean state = gtk_check_menu_item_get_active ( GTK_CHECK_MENU_ITEM(check_box));
3734 vik_viewport_set_draw_scale ( vw->viking_vvp, state );
3738 static void set_draw_centermark ( GtkAction *a, VikWindow *vw )
3740 GtkWidget *check_box = gtk_ui_manager_get_widget ( vw->uim, "/ui/MainMenu/View/SetShow/ShowCenterMark" );
3741 g_assert(check_box);
3742 gboolean state = gtk_check_menu_item_get_active ( GTK_CHECK_MENU_ITEM(check_box));
3743 vik_viewport_set_draw_centermark ( vw->viking_vvp, state );
3747 static void set_draw_highlight ( GtkAction *a, VikWindow *vw )
3749 GtkWidget *check_box = gtk_ui_manager_get_widget ( vw->uim, "/ui/MainMenu/View/SetShow/ShowHighlight" );
3750 g_assert(check_box);
3751 gboolean state = gtk_check_menu_item_get_active ( GTK_CHECK_MENU_ITEM(check_box));
3752 vik_viewport_set_draw_highlight ( vw->viking_vvp, state );
3756 static void set_bg_color ( GtkAction *a, VikWindow *vw )
3758 GtkWidget *colorsd = gtk_color_selection_dialog_new ( _("Choose a background color") );
3759 GdkColor *color = vik_viewport_get_background_gdkcolor ( vw->viking_vvp );
3760 gtk_color_selection_set_previous_color ( GTK_COLOR_SELECTION(gtk_color_selection_dialog_get_color_selection(GTK_COLOR_SELECTION_DIALOG(colorsd))), color );
3761 gtk_color_selection_set_current_color ( GTK_COLOR_SELECTION(gtk_color_selection_dialog_get_color_selection(GTK_COLOR_SELECTION_DIALOG(colorsd))), color );
3762 if ( gtk_dialog_run ( GTK_DIALOG(colorsd) ) == GTK_RESPONSE_OK )
3764 gtk_color_selection_get_current_color ( GTK_COLOR_SELECTION(gtk_color_selection_dialog_get_color_selection(GTK_COLOR_SELECTION_DIALOG(colorsd))), color );
3765 vik_viewport_set_background_gdkcolor ( vw->viking_vvp, color );
3769 gtk_widget_destroy ( colorsd );
3772 static void set_highlight_color ( GtkAction *a, VikWindow *vw )
3774 GtkWidget *colorsd = gtk_color_selection_dialog_new ( _("Choose a track highlight color") );
3775 GdkColor *color = vik_viewport_get_highlight_gdkcolor ( vw->viking_vvp );
3776 gtk_color_selection_set_previous_color ( GTK_COLOR_SELECTION(gtk_color_selection_dialog_get_color_selection(GTK_COLOR_SELECTION_DIALOG(colorsd))), color );
3777 gtk_color_selection_set_current_color ( GTK_COLOR_SELECTION(gtk_color_selection_dialog_get_color_selection(GTK_COLOR_SELECTION_DIALOG(colorsd))), color );
3778 if ( gtk_dialog_run ( GTK_DIALOG(colorsd) ) == GTK_RESPONSE_OK )
3780 gtk_color_selection_get_current_color ( GTK_COLOR_SELECTION(gtk_color_selection_dialog_get_color_selection(GTK_COLOR_SELECTION_DIALOG(colorsd))), color );
3781 vik_viewport_set_highlight_gdkcolor ( vw->viking_vvp, color );
3785 gtk_widget_destroy ( colorsd );
3790 /***********************************************************************************************
3792 ***********************************************************************************************/
3794 static GtkActionEntry entries[] = {
3795 { "File", NULL, N_("_File"), 0, 0, 0 },
3796 { "Edit", NULL, N_("_Edit"), 0, 0, 0 },
3797 { "View", NULL, N_("_View"), 0, 0, 0 },
3798 { "SetShow", NULL, N_("_Show"), 0, 0, 0 },
3799 { "SetZoom", NULL, N_("_Zoom"), 0, 0, 0 },
3800 { "SetPan", NULL, N_("_Pan"), 0, 0, 0 },
3801 { "Layers", NULL, N_("_Layers"), 0, 0, 0 },
3802 { "Tools", NULL, N_("_Tools"), 0, 0, 0 },
3803 { "Exttools", NULL, N_("_Webtools"), 0, 0, 0 },
3804 { "Help", NULL, N_("_Help"), 0, 0, 0 },
3806 { "New", GTK_STOCK_NEW, N_("_New"), "<control>N", N_("New file"), (GCallback)newwindow_cb },
3807 { "Open", GTK_STOCK_OPEN, N_("_Open..."), "<control>O", N_("Open a file"), (GCallback)load_file },
3808 { "OpenRecentFile", NULL, N_("Open _Recent File"), NULL, NULL, (GCallback)NULL },
3809 { "Append", GTK_STOCK_ADD, N_("Append _File..."), NULL, N_("Append data from a different file"), (GCallback)load_file },
3810 { "Export", GTK_STOCK_CONVERT, N_("_Export All"), NULL, N_("Export All TrackWaypoint Layers"), (GCallback)NULL },
3811 { "ExportGPX", NULL, N_("_GPX..."), NULL, N_("Export as GPX"), (GCallback)export_to_gpx },
3812 { "Acquire", GTK_STOCK_GO_DOWN, N_("A_cquire"), NULL, NULL, (GCallback)NULL },
3813 { "AcquireGPS", NULL, N_("From _GPS..."), NULL, N_("Transfer data from a GPS device"), (GCallback)acquire_from_gps },
3814 { "AcquireGPSBabel", NULL, N_("Import File With GPS_Babel..."), NULL, N_("Import file via GPSBabel converter"), (GCallback)acquire_from_file },
3815 { "AcquireRouting", NULL, N_("_Directions..."), NULL, N_("Get driving directions"), (GCallback)acquire_from_routing },
3816 #ifdef VIK_CONFIG_OPENSTREETMAP
3817 { "AcquireOSM", NULL, N_("_OSM Traces..."), NULL, N_("Get traces from OpenStreetMap"), (GCallback)acquire_from_osm },
3818 { "AcquireMyOSM", NULL, N_("_My OSM Traces..."), NULL, N_("Get Your Own Traces from OpenStreetMap"), (GCallback)acquire_from_my_osm },
3820 #ifdef VIK_CONFIG_GEOCACHES
3821 { "AcquireGC", NULL, N_("Geo_caches..."), NULL, N_("Get Geocaches from geocaching.com"), (GCallback)acquire_from_gc },
3823 #ifdef VIK_CONFIG_GEOTAG
3824 { "AcquireGeotag", NULL, N_("From Geotagged _Images..."), NULL, N_("Create waypoints from geotagged images"), (GCallback)acquire_from_geotag },
3826 { "AcquireURL", NULL, N_("From _URL..."), NULL, N_("Get a file from a URL"), (GCallback)acquire_from_url },
3827 #ifdef VIK_CONFIG_GEONAMES
3828 { "AcquireWikipedia", NULL, N_("From _Wikipedia Waypoints"), NULL, N_("Create waypoints from Wikipedia items in the current view"), (GCallback)acquire_from_wikipedia },
3830 { "Save", GTK_STOCK_SAVE, N_("_Save"), "<control>S", N_("Save the file"), (GCallback)save_file },
3831 { "SaveAs", GTK_STOCK_SAVE_AS, N_("Save _As..."), NULL, N_("Save the file under different name"), (GCallback)save_file_as },
3832 { "FileProperties", NULL, N_("Properties..."), NULL, N_("File Properties"), (GCallback)file_properties_cb },
3833 { "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 },
3834 { "GenImgDir", GTK_STOCK_DND_MULTIPLE, N_("Generate _Directory of Images..."), NULL, N_("FIXME:IMGDIR"), (GCallback)draw_to_image_dir_cb },
3835 { "Print", GTK_STOCK_PRINT, N_("_Print..."), NULL, N_("Print maps"), (GCallback)print_cb },
3836 { "Exit", GTK_STOCK_QUIT, N_("E_xit"), "<control>W", N_("Exit the program"), (GCallback)window_close },
3837 { "SaveExit", GTK_STOCK_QUIT, N_("Save and Exit"), NULL, N_("Save and Exit the program"), (GCallback)save_file_and_exit },
3839 { "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 },
3840 { "GoForward", GTK_STOCK_GO_FORWARD, N_("Go to the _Next Location"), NULL, N_("Go to the next location"), (GCallback)draw_goto_back_and_forth },
3841 { "GotoDefaultLocation", GTK_STOCK_HOME, N_("Go to the _Default Location"), NULL, N_("Go to the default location"), (GCallback)goto_default_location },
3842 { "GotoSearch", GTK_STOCK_JUMP_TO, N_("Go to _Location..."), NULL, N_("Go to address/place using text search"), (GCallback)goto_address },
3843 { "GotoLL", GTK_STOCK_JUMP_TO, N_("_Go to Lat/Lon..."), NULL, N_("Go to arbitrary lat/lon coordinate"), (GCallback)draw_goto_cb },
3844 { "GotoUTM", GTK_STOCK_JUMP_TO, N_("Go to UTM..."), NULL, N_("Go to arbitrary UTM coordinate"), (GCallback)draw_goto_cb },
3845 { "Refresh", GTK_STOCK_REFRESH, N_("_Refresh"), "F5", N_("Refresh any maps displayed"), (GCallback)draw_refresh_cb },
3846 { "SetHLColor",GTK_STOCK_SELECT_COLOR, N_("Set _Highlight Color..."), NULL, NULL, (GCallback)set_highlight_color },
3847 { "SetBGColor",GTK_STOCK_SELECT_COLOR, N_("Set Bac_kground Color..."), NULL, NULL, (GCallback)set_bg_color },
3848 { "ZoomIn", GTK_STOCK_ZOOM_IN, N_("Zoom _In"), "<control>plus", NULL, (GCallback)draw_zoom_cb },
3849 { "ZoomOut", GTK_STOCK_ZOOM_OUT, N_("Zoom _Out"), "<control>minus", NULL, (GCallback)draw_zoom_cb },
3850 { "ZoomTo", GTK_STOCK_ZOOM_FIT, N_("Zoom _To..."), "<control>Z", NULL, (GCallback)zoom_to_cb },
3851 { "PanNorth", NULL, N_("Pan _North"), "<control>Up", NULL, (GCallback)draw_pan_cb },
3852 { "PanEast", NULL, N_("Pan _East"), "<control>Right", NULL, (GCallback)draw_pan_cb },
3853 { "PanSouth", NULL, N_("Pan _South"), "<control>Down", NULL, (GCallback)draw_pan_cb },
3854 { "PanWest", NULL, N_("Pan _West"), "<control>Left", NULL, (GCallback)draw_pan_cb },
3855 { "BGJobs", GTK_STOCK_EXECUTE, N_("Background _Jobs"), NULL, NULL, (GCallback)a_background_show_window },
3857 { "Cut", GTK_STOCK_CUT, N_("Cu_t"), NULL, NULL, (GCallback)menu_cut_layer_cb },
3858 { "Copy", GTK_STOCK_COPY, N_("_Copy"), NULL, NULL, (GCallback)menu_copy_layer_cb },
3859 { "Paste", GTK_STOCK_PASTE, N_("_Paste"), NULL, NULL, (GCallback)menu_paste_layer_cb },
3860 { "Delete", GTK_STOCK_DELETE, N_("_Delete"), NULL, NULL, (GCallback)menu_delete_layer_cb },
3861 { "DeleteAll", NULL, N_("Delete All"), NULL, NULL, (GCallback)clear_cb },
3862 { "MapCacheFlush",NULL, N_("_Flush Map Cache"), NULL, NULL, (GCallback)mapcache_flush_cb },
3863 { "SetDefaultLocation", GTK_STOCK_GO_FORWARD, N_("_Set the Default Location"), NULL, N_("Set the Default Location to the current position"),(GCallback)default_location_cb },
3864 { "Preferences",GTK_STOCK_PREFERENCES, N_("_Preferences"), NULL, NULL, (GCallback)preferences_cb },
3865 { "LayerDefaults",GTK_STOCK_PROPERTIES, N_("_Layer Defaults"), NULL, NULL, NULL },
3866 { "Properties",GTK_STOCK_PROPERTIES, N_("_Properties"), NULL, NULL, (GCallback)menu_properties_cb },
3868 { "HelpEntry", GTK_STOCK_HELP, N_("_Help"), "F1", NULL, (GCallback)help_help_cb },
3869 { "About", GTK_STOCK_ABOUT, N_("_About"), NULL, NULL, (GCallback)help_about_cb },
3872 static GtkActionEntry entries_gpsbabel[] = {
3873 { "ExportKML", NULL, N_("_KML..."), NULL, N_("Export as KML"), (GCallback)export_to_kml },
3877 /* FIXME use VIEWPORT_DRAWMODE values */
3878 static GtkRadioActionEntry mode_entries[] = {
3879 { "ModeUTM", NULL, N_("_UTM Mode"), "<control>u", NULL, 0 },
3880 { "ModeExpedia", NULL, N_("_Expedia Mode"), "<control>e", NULL, 1 },
3881 { "ModeMercator", NULL, N_("_Mercator Mode"), "<control>m", NULL, 4 },
3882 { "ModeLatLon", NULL, N_("Lat_/Lon Mode"), "<control>l", NULL, 5 },
3885 static GtkToggleActionEntry toggle_entries[] = {
3886 { "ShowScale", NULL, N_("Show _Scale"), "<shift>F5", N_("Show Scale"), (GCallback)set_draw_scale, TRUE },
3887 { "ShowCenterMark", NULL, N_("Show _Center Mark"), "F6", N_("Show Center Mark"), (GCallback)set_draw_centermark, TRUE },
3888 { "ShowHighlight", GTK_STOCK_UNDERLINE, N_("Show _Highlight"), "F7", N_("Show Highlight"), (GCallback)set_draw_highlight, TRUE },
3889 { "FullScreen", GTK_STOCK_FULLSCREEN, N_("_Full Screen"), "F11", N_("Activate full screen mode"), (GCallback)full_screen_cb, FALSE },
3890 { "ViewSidePanel", GTK_STOCK_INDEX, N_("Show Side _Panel"), "F9", N_("Show Side Panel"), (GCallback)view_side_panel_cb, TRUE },
3891 { "ViewStatusBar", NULL, N_("Show Status_bar"), "F12", N_("Show Statusbar"), (GCallback)view_statusbar_cb, TRUE },
3892 { "ViewToolbar", NULL, N_("Show _Toolbar"), "F3", N_("Show Toolbar"), (GCallback)view_toolbar_cb, TRUE },
3893 { "ViewMainMenu", NULL, N_("Show _Menu"), "F4", N_("Show Menu"), (GCallback)view_main_menu_cb, TRUE },
3896 #include "menu.xml.h"
3897 static void window_create_ui( VikWindow *window )
3900 GtkActionGroup *action_group;
3901 GtkAccelGroup *accel_group;
3904 GtkIconFactory *icon_factory;
3905 GtkIconSet *icon_set;
3906 GtkRadioActionEntry *tools = NULL, *radio;
3909 uim = gtk_ui_manager_new ();
3912 toolbox_add_tool(window->vt, &ruler_tool, TOOL_LAYER_TYPE_NONE);
3913 toolbox_add_tool(window->vt, &zoom_tool, TOOL_LAYER_TYPE_NONE);
3914 toolbox_add_tool(window->vt, &pan_tool, TOOL_LAYER_TYPE_NONE);
3915 toolbox_add_tool(window->vt, &select_tool, TOOL_LAYER_TYPE_NONE);
3918 if (!(mid = gtk_ui_manager_add_ui_from_string (uim, menu_xml, -1, &error))) {
3919 g_error_free (error);
3923 action_group = gtk_action_group_new ("MenuActions");
3924 gtk_action_group_set_translation_domain(action_group, PACKAGE_NAME);
3925 gtk_action_group_add_actions (action_group, entries, G_N_ELEMENTS (entries), window);
3926 gtk_action_group_add_toggle_actions (action_group, toggle_entries, G_N_ELEMENTS (toggle_entries), window);
3927 gtk_action_group_add_radio_actions (action_group, mode_entries, G_N_ELEMENTS (mode_entries), 4, (GCallback)window_change_coord_mode_cb, window);
3929 // Use this to see if GPSBabel is available:
3930 if ( a_babel_available () ) {
3931 // If going to add more entries then might be worth creating a menu_gpsbabel.xml.h file
3932 if ( gtk_ui_manager_add_ui_from_string ( uim,
3933 "<ui><menubar name='MainMenu'><menu action='File'><menu action='Export'><menuitem action='ExportKML'/></menu></menu></menubar></ui>",
3935 gtk_action_group_add_actions ( action_group, entries_gpsbabel, G_N_ELEMENTS (entries_gpsbabel), window );
3938 icon_factory = gtk_icon_factory_new ();
3939 gtk_icon_factory_add_default (icon_factory);
3941 register_vik_icons(icon_factory);
3943 // Copy the tool RadioActionEntries out of the main Window structure into an extending array 'tools'
3944 // so that it can be applied to the UI in one action group add function call below
3946 for (i=0; i<window->vt->n_tools; i++) {
3947 tools = g_renew(GtkRadioActionEntry, tools, ntools+1);
3948 radio = &tools[ntools];
3950 *radio = window->vt->tools[i].ti.radioActionEntry;
3951 radio->value = ntools;
3954 for (i=0; i<VIK_LAYER_NUM_TYPES; i++) {
3955 GtkActionEntry action;
3956 gtk_ui_manager_add_ui(uim, mid, "/ui/MainMenu/Layers/",
3957 vik_layer_get_interface(i)->name,
3958 vik_layer_get_interface(i)->name,
3959 GTK_UI_MANAGER_MENUITEM, FALSE);
3961 icon_set = gtk_icon_set_new_from_pixbuf (gdk_pixbuf_from_pixdata (vik_layer_get_interface(i)->icon, FALSE, NULL ));
3962 gtk_icon_factory_add (icon_factory, vik_layer_get_interface(i)->name, icon_set);
3963 gtk_icon_set_unref (icon_set);
3965 action.name = vik_layer_get_interface(i)->name;
3966 action.stock_id = vik_layer_get_interface(i)->name;
3967 action.label = g_strdup_printf( _("New _%s Layer"), vik_layer_get_interface(i)->name);
3968 action.accelerator = vik_layer_get_interface(i)->accelerator;
3969 action.tooltip = NULL;
3970 action.callback = (GCallback)menu_addlayer_cb;
3971 gtk_action_group_add_actions(action_group, &action, 1, window);
3973 g_free ( (gchar*)action.label );
3975 if ( vik_layer_get_interface(i)->tools_count ) {
3976 gtk_ui_manager_add_ui(uim, mid, "/ui/MainMenu/Tools/", vik_layer_get_interface(i)->name, NULL, GTK_UI_MANAGER_SEPARATOR, FALSE);
3977 gtk_ui_manager_add_ui(uim, mid, "/ui/MainToolbar/ToolItems/", vik_layer_get_interface(i)->name, NULL, GTK_UI_MANAGER_SEPARATOR, FALSE);
3980 // Further tool copying for to apply to the UI, also apply menu UI setup
3981 for ( j = 0; j < vik_layer_get_interface(i)->tools_count; j++ ) {
3982 tools = g_renew(GtkRadioActionEntry, tools, ntools+1);
3983 radio = &tools[ntools];
3986 gtk_ui_manager_add_ui(uim, mid, "/ui/MainMenu/Tools",
3987 vik_layer_get_interface(i)->tools[j].radioActionEntry.label,
3988 vik_layer_get_interface(i)->tools[j].radioActionEntry.name,
3989 GTK_UI_MANAGER_MENUITEM, FALSE);
3990 gtk_ui_manager_add_ui(uim, mid, "/ui/MainToolbar/ToolItems",
3991 vik_layer_get_interface(i)->tools[j].radioActionEntry.label,
3992 vik_layer_get_interface(i)->tools[j].radioActionEntry.name,
3993 GTK_UI_MANAGER_TOOLITEM, FALSE);
3995 toolbox_add_tool(window->vt, &(vik_layer_get_interface(i)->tools[j]), i);
3997 *radio = vik_layer_get_interface(i)->tools[j].radioActionEntry;
3998 // Overwrite with actual number to use
3999 radio->value = ntools;
4002 GtkActionEntry action_dl;
4003 gchar *layername = g_strdup_printf ( "Layer%s", vik_layer_get_interface(i)->fixed_layer_name );
4004 gtk_ui_manager_add_ui(uim, mid, "/ui/MainMenu/Edit/LayerDefaults",
4005 vik_layer_get_interface(i)->name,
4007 GTK_UI_MANAGER_MENUITEM, FALSE);
4010 // For default layers use action names of the form 'Layer<LayerName>'
4011 // This is to avoid clashing with just the layer name used above for the tool actions
4012 action_dl.name = g_strconcat("Layer", vik_layer_get_interface(i)->fixed_layer_name, NULL);
4013 action_dl.stock_id = NULL;
4014 action_dl.label = g_strconcat("_", vik_layer_get_interface(i)->name, "...", NULL); // Prepend marker for keyboard accelerator
4015 action_dl.accelerator = NULL;
4016 action_dl.tooltip = NULL;
4017 action_dl.callback = (GCallback)layer_defaults_cb;
4018 gtk_action_group_add_actions(action_group, &action_dl, 1, window);
4019 g_free ( (gchar*)action_dl.name );
4020 g_free ( (gchar*)action_dl.label );
4022 g_object_unref (icon_factory);
4024 gtk_action_group_add_radio_actions(action_group, tools, ntools, 0, (GCallback)menu_tool_cb, window);
4027 gtk_ui_manager_insert_action_group (uim, action_group, 0);
4029 for (i=0; i<VIK_LAYER_NUM_TYPES; i++) {
4030 for ( j = 0; j < vik_layer_get_interface(i)->tools_count; j++ ) {
4031 GtkAction *action = gtk_action_group_get_action(action_group,
4032 vik_layer_get_interface(i)->tools[j].radioActionEntry.name);
4033 g_object_set(action, "sensitive", FALSE, NULL);
4037 // This is done last so we don't need to track the value of mid anymore
4038 vik_ext_tools_add_action_items ( window, window->uim, action_group, mid );
4040 window->action_group = action_group;
4042 accel_group = gtk_ui_manager_get_accel_group (uim);
4043 gtk_window_add_accel_group (GTK_WINDOW (window), accel_group);
4044 gtk_ui_manager_ensure_update (uim);
4046 setup_recent_files(window);
4050 // TODO - add method to add tool icons defined from outside this file
4051 // and remove the reverse dependency on icon definition from this file
4053 const GdkPixdata *data;
4056 { &mover_22_pixbuf, "vik-icon-pan" },
4057 { &zoom_18_pixbuf, "vik-icon-zoom" },
4058 { &ruler_18_pixbuf, "vik-icon-ruler" },
4059 { &select_18_pixbuf, "vik-icon-select" },
4060 { &vik_new_route_18_pixbuf, "vik-icon-Create Route" },
4061 { &route_finder_18_pixbuf, "vik-icon-Route Finder" },
4062 { &demdl_18_pixbuf, "vik-icon-DEM Download" },
4063 { &showpic_18_pixbuf, "vik-icon-Show Picture" },
4064 { &addtr_18_pixbuf, "vik-icon-Create Track" },
4065 { &edtr_18_pixbuf, "vik-icon-Edit Trackpoint" },
4066 { &addwp_18_pixbuf, "vik-icon-Create Waypoint" },
4067 { &edwp_18_pixbuf, "vik-icon-Edit Waypoint" },
4068 { &geozoom_18_pixbuf, "vik-icon-Georef Zoom Tool" },
4069 { &geomove_18_pixbuf, "vik-icon-Georef Move Map" },
4070 { &mapdl_18_pixbuf, "vik-icon-Maps Download" },
4073 static gint n_stock_icons = G_N_ELEMENTS (stock_icons);
4076 register_vik_icons (GtkIconFactory *icon_factory)
4078 GtkIconSet *icon_set;
4081 for (i = 0; i < n_stock_icons; i++) {
4082 icon_set = gtk_icon_set_new_from_pixbuf (gdk_pixbuf_from_pixdata (
4083 stock_icons[i].data, FALSE, NULL ));
4084 gtk_icon_factory_add (icon_factory, stock_icons[i].stock_id, icon_set);
4085 gtk_icon_set_unref (icon_set);
4089 gpointer vik_window_get_selected_trw_layer ( VikWindow *vw )
4091 return vw->selected_vtl;
4094 void vik_window_set_selected_trw_layer ( VikWindow *vw, gpointer vtl )
4096 vw->selected_vtl = vtl;
4097 vw->containing_vtl = vtl;
4099 vw->selected_track = NULL;
4100 vw->selected_tracks = NULL;
4101 vw->selected_waypoint = NULL;
4102 vw->selected_waypoints = NULL;
4103 // Set highlight thickness
4104 vik_viewport_set_highlight_thickness ( vw->viking_vvp, vik_trw_layer_get_property_tracks_line_thickness (vw->containing_vtl) );
4107 GHashTable *vik_window_get_selected_tracks ( VikWindow *vw )
4109 return vw->selected_tracks;
4112 void vik_window_set_selected_tracks ( VikWindow *vw, GHashTable *ght, gpointer vtl )
4114 vw->selected_tracks = ght;
4115 vw->containing_vtl = vtl;
4117 vw->selected_vtl = NULL;
4118 vw->selected_track = NULL;
4119 vw->selected_waypoint = NULL;
4120 vw->selected_waypoints = NULL;
4121 // Set highlight thickness
4122 vik_viewport_set_highlight_thickness ( vw->viking_vvp, vik_trw_layer_get_property_tracks_line_thickness (vw->containing_vtl) );
4125 gpointer vik_window_get_selected_track ( VikWindow *vw )
4127 return vw->selected_track;
4130 void vik_window_set_selected_track ( VikWindow *vw, gpointer *vt, gpointer vtl )
4132 vw->selected_track = vt;
4133 vw->containing_vtl = vtl;
4135 vw->selected_vtl = NULL;
4136 vw->selected_tracks = NULL;
4137 vw->selected_waypoint = NULL;
4138 vw->selected_waypoints = NULL;
4139 // Set highlight thickness
4140 vik_viewport_set_highlight_thickness ( vw->viking_vvp, vik_trw_layer_get_property_tracks_line_thickness (vw->containing_vtl) );
4143 GHashTable *vik_window_get_selected_waypoints ( VikWindow *vw )
4145 return vw->selected_waypoints;
4148 void vik_window_set_selected_waypoints ( VikWindow *vw, GHashTable *ght, gpointer vtl )
4150 vw->selected_waypoints = ght;
4151 vw->containing_vtl = vtl;
4153 vw->selected_vtl = NULL;
4154 vw->selected_track = NULL;
4155 vw->selected_tracks = NULL;
4156 vw->selected_waypoint = NULL;
4159 gpointer vik_window_get_selected_waypoint ( VikWindow *vw )
4161 return vw->selected_waypoint;
4164 void vik_window_set_selected_waypoint ( VikWindow *vw, gpointer *vwp, gpointer vtl )
4166 vw->selected_waypoint = vwp;
4167 vw->containing_vtl = vtl;
4169 vw->selected_vtl = NULL;
4170 vw->selected_track = NULL;
4171 vw->selected_tracks = NULL;
4172 vw->selected_waypoints = NULL;
4175 gboolean vik_window_clear_highlight ( VikWindow *vw )
4177 gboolean need_redraw = FALSE;
4178 if ( vw->selected_vtl != NULL ) {
4179 vw->selected_vtl = NULL;
4182 if ( vw->selected_track != NULL ) {
4183 vw->selected_track = NULL;
4186 if ( vw->selected_tracks != NULL ) {
4187 vw->selected_tracks = NULL;
4190 if ( vw->selected_waypoint != NULL ) {
4191 vw->selected_waypoint = NULL;
4194 if ( vw->selected_waypoints != NULL ) {
4195 vw->selected_waypoints = NULL;
4201 GThread *vik_window_get_thread ( VikWindow *vw )