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 window_configure_event ( VikWindow *vw );
99 static void draw_sync ( VikWindow *vw );
100 static void draw_redraw ( VikWindow *vw );
101 static void draw_scroll ( VikWindow *vw, GdkEventScroll *event );
102 static void draw_click ( VikWindow *vw, GdkEventButton *event );
103 static void draw_release ( VikWindow *vw, GdkEventButton *event );
104 static void draw_mouse_motion ( VikWindow *vw, GdkEventMotion *event );
105 static void draw_zoom_cb ( GtkAction *a, VikWindow *vw );
106 static void draw_goto_cb ( GtkAction *a, VikWindow *vw );
107 static void draw_refresh_cb ( GtkAction *a, VikWindow *vw );
109 static void draw_status ( VikWindow *vw );
111 /* End Drawing Functions */
113 static void menu_addlayer_cb ( GtkAction *a, VikWindow *vw );
114 static void menu_properties_cb ( GtkAction *a, VikWindow *vw );
115 static void menu_delete_layer_cb ( GtkAction *a, VikWindow *vw );
117 /* tool management */
123 #define TOOL_LAYER_TYPE_NONE -1
128 toolbox_tool_t *tools;
132 static void menu_tool_cb ( GtkAction *old, GtkAction *a, VikWindow *vw );
133 static toolbox_tools_t* toolbox_create(VikWindow *vw);
134 static void toolbox_add_tool(toolbox_tools_t *vt, VikToolInterface *vti, gint layer_type );
135 static int toolbox_get_tool(toolbox_tools_t *vt, const gchar *tool_name);
136 static void toolbox_activate(toolbox_tools_t *vt, const gchar *tool_name);
137 static const GdkCursor *toolbox_get_cursor(toolbox_tools_t *vt, const gchar *tool_name);
138 static void toolbox_click (toolbox_tools_t *vt, GdkEventButton *event);
139 static void toolbox_move (toolbox_tools_t *vt, GdkEventMotion *event);
140 static void toolbox_release (toolbox_tools_t *vt, GdkEventButton *event);
144 static void window_create_ui( VikWindow *window );
145 static void register_vik_icons (GtkIconFactory *icon_factory);
148 static void load_file ( GtkAction *a, VikWindow *vw );
149 static gboolean save_file_as ( GtkAction *a, VikWindow *vw );
150 static gboolean save_file ( GtkAction *a, VikWindow *vw );
151 static gboolean save_file_and_exit ( GtkAction *a, VikWindow *vw );
152 static gboolean window_save ( VikWindow *vw );
156 VikViewport *viking_vvp;
157 VikLayersPanel *viking_vlp;
158 VikStatusbar *viking_vs;
162 GdkCursor *busy_cursor;
163 GdkCursor *viewport_cursor; // only a reference
165 /* tool management state */
168 guint16 tool_layer_id;
169 guint16 tool_tool_id;
171 GtkActionGroup *action_group;
176 guint draw_image_width, draw_image_height;
177 gboolean draw_image_save_as_png;
181 VikLoadType_t loaded_type;
183 GtkWidget *open_dia, *save_dia;
184 GtkWidget *save_img_dia, *save_img_dir_dia;
186 gboolean only_updating_coord_mode_ui; /* hack for a bug in GTK */
190 /* half-drawn update */
192 VikCoord trigger_center;
194 /* Store at this level for highlighted selection drawing since it applies to the viewport and the layers panel */
195 /* Only one of these items can be selected at the same time */
196 gpointer selected_vtl; /* notionally VikTrwLayer */
197 GHashTable *selected_tracks;
198 gpointer selected_track; /* notionally VikTrack */
199 GHashTable *selected_waypoints;
200 gpointer selected_waypoint; /* notionally VikWaypoint */
201 /* only use for individual track or waypoint */
202 /* For track(s) & waypoint(s) it is the layer they are in - this helps refering to the individual item easier */
203 gpointer containing_vtl; /* notionally VikTrwLayer */
217 VW_OPENWINDOW_SIGNAL,
221 static guint window_signals[VW_LAST_SIGNAL] = { 0 };
223 // TODO get rid of this as this is unnecessary duplication...
224 static gchar *tool_names[NUMBER_OF_TOOLS] = { N_("Pan"), N_("Zoom"), N_("Ruler"), N_("Select") };
226 G_DEFINE_TYPE (VikWindow, vik_window, GTK_TYPE_WINDOW)
228 VikViewport * vik_window_viewport(VikWindow *vw)
230 return(vw->viking_vvp);
233 VikLayersPanel * vik_window_layers_panel(VikWindow *vw)
235 return(vw->viking_vlp);
239 * Returns the statusbar for the window
241 VikStatusbar * vik_window_get_statusbar ( VikWindow *vw )
243 return vw->viking_vs;
248 vik_statusbar_type_t vs_type;
249 gchar* message; // Always make a copy of this data
250 } statusbar_idle_data;
253 * For the actual statusbar update!
255 static gboolean statusbar_idle_update ( statusbar_idle_data *sid )
257 vik_statusbar_set_message ( sid->vs, sid->vs_type, sid->message );
258 g_free ( sid->message );
264 * vik_window_statusbar_update:
265 * @vw: The main window in which the statusbar will be updated.
266 * @message: The string to be displayed. This is copied.
267 * @vs_type: The part of the statusbar to be updated.
269 * This updates any part of the statusbar with the new string.
270 * It handles calling from the main thread or any background thread
271 * ATM this mostly used from background threads - as from the main thread
272 * one may use the vik_statusbar_set_message() directly.
274 void vik_window_statusbar_update ( VikWindow *vw, const gchar* message, vik_statusbar_type_t vs_type )
276 statusbar_idle_data *sid = g_malloc ( sizeof (statusbar_idle_data) );
277 sid->vs = vw->viking_vs;
278 sid->vs_type = vs_type;
279 sid->message = g_strdup ( message );
281 if ( g_thread_self() == vik_window_get_thread ( vw ) ) {
282 g_idle_add ( (GSourceFunc) statusbar_idle_update, sid );
285 // From a background thread
286 gdk_threads_add_idle ( (GSourceFunc) statusbar_idle_update, sid );
290 // Actual signal handlers
291 static void destroy_window ( GtkWidget *widget,
294 if ( ! --window_count )
298 #define VIK_SETTINGS_WIN_SIDEPANEL "window_sidepanel"
299 #define VIK_SETTINGS_WIN_STATUSBAR "window_statusbar"
300 #define VIK_SETTINGS_WIN_TOOLBAR "window_toolbar"
301 // Menubar setting to off is never auto saved in case it's accidentally turned off
302 // It's not so obvious so to recover the menu visibility.
303 // Thus this value is for setting manually via editting the settings file directly
304 #define VIK_SETTINGS_WIN_MENUBAR "window_menubar"
306 VikWindow *vik_window_new_window ()
308 if ( window_count < MAX_WINDOWS )
310 VikWindow *vw = window_new ();
312 g_signal_connect (G_OBJECT (vw), "destroy",
313 G_CALLBACK (destroy_window), NULL);
314 g_signal_connect (G_OBJECT (vw), "newwindow",
315 G_CALLBACK (vik_window_new_window), NULL);
316 g_signal_connect (G_OBJECT (vw), "openwindow",
317 G_CALLBACK (open_window), NULL);
319 gtk_widget_show_all ( GTK_WIDGET(vw) );
321 if ( a_vik_get_restore_window_state() ) {
322 // These settings are applied after the show all as these options hide widgets
324 if ( a_settings_get_boolean ( VIK_SETTINGS_WIN_SIDEPANEL, &sidepanel ) )
326 gtk_widget_hide ( GTK_WIDGET(vw->viking_vlp) );
327 GtkWidget *check_box = gtk_ui_manager_get_widget ( vw->uim, "/ui/MainMenu/View/SetShow/ViewSidePanel" );
328 gtk_check_menu_item_set_active ( GTK_CHECK_MENU_ITEM(check_box), FALSE );
332 if ( a_settings_get_boolean ( VIK_SETTINGS_WIN_STATUSBAR, &statusbar ) )
334 gtk_widget_hide ( GTK_WIDGET(vw->viking_vs) );
335 GtkWidget *check_box = gtk_ui_manager_get_widget ( vw->uim, "/ui/MainMenu/View/SetShow/ViewStatusBar" );
336 gtk_check_menu_item_set_active ( GTK_CHECK_MENU_ITEM(check_box), FALSE );
340 if ( a_settings_get_boolean ( VIK_SETTINGS_WIN_TOOLBAR, &toolbar ) )
342 gtk_widget_hide ( GTK_WIDGET(vw->toolbar) );
343 GtkWidget *check_box = gtk_ui_manager_get_widget ( vw->uim, "/ui/MainMenu/View/SetShow/ViewToolBar" );
344 gtk_check_menu_item_set_active ( GTK_CHECK_MENU_ITEM(check_box), FALSE );
348 if ( a_settings_get_boolean ( VIK_SETTINGS_WIN_MENUBAR, &menubar ) )
350 gtk_widget_hide ( gtk_ui_manager_get_widget ( vw->uim, "/ui/MainMenu" ) );
351 GtkWidget *check_box = gtk_ui_manager_get_widget ( vw->uim, "/ui/MainMenu/View/SetShow/ViewMainMenu" );
352 gtk_check_menu_item_set_active ( GTK_CHECK_MENU_ITEM(check_box), FALSE );
363 * determine_location_thread:
364 * @vw: The window that will get updated
365 * @threaddata: Data used by our background thread mechanism
367 * Use the features in vikgoto to determine where we are
368 * Then set up the viewport:
369 * 1. To goto the location
370 * 2. Set an appropriate level zoom for the location type
371 * 3. Some statusbar message feedback
373 static int determine_location_thread ( VikWindow *vw, gpointer threaddata )
377 gint ans = a_vik_goto_where_am_i ( vw->viking_vvp, &ll, &name );
379 int result = a_background_thread_progress ( threaddata, 1.0 );
381 vik_window_statusbar_update ( vw, _("Location lookup aborted"), VIK_STATUSBAR_INFO );
382 return -1; /* Abort thread */
390 // Position found with city precision - so zoom out more
393 else if ( ans == 3 ) {
394 // Position found via country name search - so zoom wayyyy out
398 vik_viewport_set_zoom ( vw->viking_vvp, zoom );
399 vik_viewport_set_center_latlon ( vw->viking_vvp, &ll );
401 gchar *message = g_strdup_printf ( _("Location found: %s"), name );
402 vik_window_statusbar_update ( vw, message, VIK_STATUSBAR_INFO );
406 // Signal to redraw from the background
407 vik_layers_panel_emit_update ( vw->viking_vlp );
410 vik_window_statusbar_update ( vw, _("Unable to determine location"), VIK_STATUSBAR_INFO );
416 * Steps to be taken once initial loading has completed
418 void vik_window_new_window_finish ( VikWindow *vw )
420 // Don't add a map if we've loaded a Viking file already
424 if ( a_vik_get_startup_method ( ) == VIK_STARTUP_METHOD_SPECIFIED_FILE ) {
425 vik_window_open_file ( vw, a_vik_get_startup_file(), TRUE );
430 // Maybe add a default map layer
431 if ( a_vik_get_add_default_map_layer () ) {
432 VikMapsLayer *vml = VIK_MAPS_LAYER ( vik_layer_create(VIK_LAYER_MAPS, vw->viking_vvp, NULL, FALSE) );
433 vik_maps_layer_pretend_licence_shown ( vml );
434 vik_layer_rename ( VIK_LAYER(vml), _("Default Map") );
435 vik_aggregate_layer_add_layer ( vik_layers_panel_get_top_layer(vw->viking_vlp), VIK_LAYER(vml), TRUE );
440 // If not loaded any file, maybe try the location lookup
441 if ( vw->loaded_type == LOAD_TYPE_READ_FAILURE ) {
442 if ( a_vik_get_startup_method ( ) == VIK_STARTUP_METHOD_AUTO_LOCATION ) {
444 vik_statusbar_set_message ( vw->viking_vs, VIK_STATUSBAR_INFO, _("Trying to determine location...") );
446 a_background_thread ( GTK_WINDOW(vw),
447 _("Determining location"),
448 (vik_thr_func) determine_location_thread,
457 static void open_window ( VikWindow *vw, GSList *files )
459 gboolean change_fn = (g_slist_length(files) == 1); /* only change fn if one file */
460 GSList *cur_file = files;
462 // Only open a new window if a viking file
463 gchar *file_name = cur_file->data;
464 if (vw != NULL && check_file_magic_vik ( file_name ) ) {
465 VikWindow *newvw = vik_window_new_window ();
467 vik_window_open_file ( newvw, file_name, TRUE );
470 vik_window_open_file ( vw, file_name, change_fn );
473 cur_file = g_slist_next (cur_file);
475 g_slist_free (files);
479 void vik_window_selected_layer(VikWindow *vw, VikLayer *vl)
481 int i, j, tool_count;
482 VikLayerInterface *layer_interface;
484 if (!vw->action_group) return;
486 for (i=0; i<VIK_LAYER_NUM_TYPES; i++) {
488 layer_interface = vik_layer_get_interface(i);
489 tool_count = layer_interface->tools_count;
491 for (j = 0; j < tool_count; j++) {
492 action = gtk_action_group_get_action(vw->action_group,
493 layer_interface->tools[j].radioActionEntry.name);
494 g_object_set(action, "sensitive", i == vl->type, NULL);
499 static void window_finalize ( GObject *gob )
501 VikWindow *vw = VIK_WINDOW(gob);
502 g_return_if_fail ( vw != NULL );
504 a_background_remove_window ( vw );
506 window_list = g_slist_remove ( window_list, vw );
508 gdk_cursor_unref ( vw->busy_cursor );
510 for (tt = 0; tt < vw->vt->n_tools; tt++ )
511 if ( vw->vt->tools[tt].ti.destroy )
512 vw->vt->tools[tt].ti.destroy ( vw->vt->tools[tt].state );
513 g_free ( vw->vt->tools );
516 G_OBJECT_CLASS(parent_class)->finalize(gob);
520 static void vik_window_class_init ( VikWindowClass *klass )
523 GObjectClass *object_class;
525 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);
526 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);
528 object_class = G_OBJECT_CLASS (klass);
530 object_class->finalize = window_finalize;
532 parent_class = g_type_class_peek_parent (klass);
536 static void zoom_changed (GtkMenuShell *menushell,
539 VikWindow *vw = VIK_WINDOW (user_data);
541 GtkWidget *aw = gtk_menu_get_active ( GTK_MENU (menushell) );
542 gint active = GPOINTER_TO_INT(g_object_get_data ( G_OBJECT (aw), "position" ));
544 gdouble zoom_request = pow (2, active-2 );
546 // But has it really changed?
547 gdouble current_zoom = vik_viewport_get_zoom ( vw->viking_vvp );
548 if ( current_zoom != 0.0 && zoom_request != current_zoom ) {
549 vik_viewport_set_zoom ( vw->viking_vvp, zoom_request );
550 // Force drawing update
556 * @mpp: The initial zoom level
558 static GtkWidget *create_zoom_menu_all_levels ( gdouble mpp )
560 GtkWidget *menu = gtk_menu_new ();
561 char *itemLabels[] = { "0.25", "0.5", "1", "2", "4", "8", "16", "32", "64", "128", "256", "512", "1024", "2048", "4096", "8192", "16384", "32768" };
564 for (i = 0 ; i < G_N_ELEMENTS(itemLabels) ; i++)
566 GtkWidget *item = gtk_menu_item_new_with_label (itemLabels[i]);
567 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
568 gtk_widget_show (item);
569 g_object_set_data (G_OBJECT (item), "position", GINT_TO_POINTER(i));
572 gint active = 2 + round ( log (mpp) / log (2) );
573 // Ensure value derived from mpp is in bounds of the menu
574 if ( active >= G_N_ELEMENTS(itemLabels) )
575 active = G_N_ELEMENTS(itemLabels) - 1;
578 gtk_menu_set_active ( GTK_MENU(menu), active );
583 static GtkWidget *create_zoom_combo_all_levels ()
585 GtkWidget *combo = vik_combo_box_text_new();
586 vik_combo_box_text_append ( combo, "0.25");
587 vik_combo_box_text_append ( combo, "0.5");
588 vik_combo_box_text_append ( combo, "1");
589 vik_combo_box_text_append ( combo, "2");
590 vik_combo_box_text_append ( combo, "4");
591 vik_combo_box_text_append ( combo, "8");
592 vik_combo_box_text_append ( combo, "16");
593 vik_combo_box_text_append ( combo, "32");
594 vik_combo_box_text_append ( combo, "64");
595 vik_combo_box_text_append ( combo, "128");
596 vik_combo_box_text_append ( combo, "256");
597 vik_combo_box_text_append ( combo, "512");
598 vik_combo_box_text_append ( combo, "1024");
599 vik_combo_box_text_append ( combo, "2048");
600 vik_combo_box_text_append ( combo, "4096");
601 vik_combo_box_text_append ( combo, "8192");
602 vik_combo_box_text_append ( combo, "16384");
603 vik_combo_box_text_append ( combo, "32768");
605 gtk_widget_set_tooltip_text (combo, _("Select zoom level"));
609 static gint zoom_popup_handler (GtkWidget *widget)
613 g_return_val_if_fail (widget != NULL, FALSE);
614 g_return_val_if_fail (GTK_IS_MENU (widget), FALSE);
616 /* The "widget" is the menu that was supplied when
617 * g_signal_connect_swapped() was called.
619 menu = GTK_MENU (widget);
621 gtk_menu_popup (menu, NULL, NULL, NULL, NULL,
622 1, gtk_get_current_event_time());
630 static void drag_data_received_cb ( GtkWidget *widget,
631 GdkDragContext *context,
634 GtkSelectionData *selection_data,
639 gboolean success = FALSE;
641 if ( (selection_data != NULL) && (gtk_selection_data_get_length(selection_data) > 0) ) {
642 switch (target_type) {
644 gchar *str = (gchar*)gtk_selection_data_get_data(selection_data);
645 g_debug ("drag received string:%s \n", str);
647 // Convert string into GSList of individual entries for use with our open signal
648 gchar **entries = g_strsplit(str, "\r\n", 0);
649 GSList *filenames = NULL;
650 gint entry_runner = 0;
651 gchar *entry = entries[entry_runner];
653 if ( g_strcmp0 ( entry, "" ) )
654 filenames = g_slist_append ( filenames, entry );
656 entry = entries[entry_runner];
660 g_signal_emit ( G_OBJECT(VIK_WINDOW_FROM_WIDGET(widget)), window_signals[VW_OPENWINDOW_SIGNAL], 0, filenames );
661 // NB: GSList & contents are freed by main.open_window
670 gtk_drag_finish ( context, success, FALSE, time );
673 #define VIK_SETTINGS_WIN_MAX "window_maximized"
674 #define VIK_SETTINGS_WIN_FULLSCREEN "window_fullscreen"
675 #define VIK_SETTINGS_WIN_WIDTH "window_width"
676 #define VIK_SETTINGS_WIN_HEIGHT "window_height"
677 #define VIK_SETTINGS_WIN_SAVE_IMAGE_WIDTH "window_save_image_width"
678 #define VIK_SETTINGS_WIN_SAVE_IMAGE_HEIGHT "window_save_image_height"
679 #define VIK_SETTINGS_WIN_SAVE_IMAGE_PNG "window_save_image_as_png"
681 static void vik_window_init ( VikWindow *vw )
683 GtkWidget *main_vbox;
686 vw->action_group = NULL;
688 vw->viking_vvp = vik_viewport_new();
689 vw->viking_vlp = vik_layers_panel_new();
690 vik_layers_panel_set_viewport ( vw->viking_vlp, vw->viking_vvp );
691 vw->viking_vs = vik_statusbar_new();
693 vw->vt = toolbox_create(vw);
694 window_create_ui(vw);
695 window_set_filename (vw, NULL);
696 vw->toolbar = GTK_TOOLBAR(gtk_ui_manager_get_widget (vw->uim, "/MainToolbar"));
698 vw->busy_cursor = gdk_cursor_new ( GDK_WATCH );
700 // Set the default tool
701 gtk_action_activate ( gtk_action_group_get_action ( vw->action_group, "Pan" ) );
704 vw->loaded_type = LOAD_TYPE_READ_FAILURE; //AKA none
705 vw->modified = FALSE;
706 vw->only_updating_coord_mode_ui = FALSE;
708 vw->pan_move = FALSE;
709 vw->pan_x = vw->pan_y = -1;
711 gint draw_image_width;
712 if ( a_settings_get_integer ( VIK_SETTINGS_WIN_SAVE_IMAGE_WIDTH, &draw_image_width ) )
713 vw->draw_image_width = draw_image_width;
715 vw->draw_image_width = DRAW_IMAGE_DEFAULT_WIDTH;
716 gint draw_image_height;
717 if ( a_settings_get_integer ( VIK_SETTINGS_WIN_SAVE_IMAGE_HEIGHT, &draw_image_height ) )
718 vw->draw_image_height = draw_image_height;
720 vw->draw_image_height = DRAW_IMAGE_DEFAULT_HEIGHT;
721 gboolean draw_image_save_as_png;
722 if ( a_settings_get_boolean ( VIK_SETTINGS_WIN_SAVE_IMAGE_PNG, &draw_image_save_as_png ) )
723 vw->draw_image_save_as_png = draw_image_save_as_png;
725 vw->draw_image_save_as_png = DRAW_IMAGE_DEFAULT_SAVE_AS_PNG;
727 main_vbox = gtk_vbox_new(FALSE, 1);
728 gtk_container_add (GTK_CONTAINER (vw), main_vbox);
730 gtk_box_pack_start (GTK_BOX(main_vbox), gtk_ui_manager_get_widget (vw->uim, "/MainMenu"), FALSE, TRUE, 0);
731 gtk_box_pack_start (GTK_BOX(main_vbox), GTK_WIDGET(vw->toolbar), FALSE, TRUE, 0);
732 gtk_toolbar_set_icon_size (vw->toolbar, GTK_ICON_SIZE_SMALL_TOOLBAR);
733 gtk_toolbar_set_style (vw->toolbar, GTK_TOOLBAR_ICONS);
735 vik_ext_tool_datasources_add_menu_items ( vw, vw->uim );
737 GtkWidget * zoom_levels = gtk_ui_manager_get_widget (vw->uim, "/MainMenu/View/SetZoom");
738 GtkWidget * zoom_levels_menu = create_zoom_menu_all_levels ( vik_viewport_get_zoom(vw->viking_vvp) );
739 gtk_menu_item_set_submenu (GTK_MENU_ITEM (zoom_levels), zoom_levels_menu);
740 g_signal_connect ( G_OBJECT(zoom_levels_menu), "selection-done", G_CALLBACK(zoom_changed), vw);
741 g_signal_connect_swapped ( G_OBJECT(vw->viking_vs), "clicked", G_CALLBACK(zoom_popup_handler), zoom_levels_menu );
743 g_signal_connect (G_OBJECT (vw), "delete_event", G_CALLBACK (delete_event), NULL);
745 g_signal_connect_swapped (G_OBJECT(vw->viking_vvp), "expose_event", G_CALLBACK(draw_sync), vw);
746 g_signal_connect_swapped (G_OBJECT(vw->viking_vvp), "configure_event", G_CALLBACK(window_configure_event), vw);
747 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 );
748 g_signal_connect_swapped (G_OBJECT(vw->viking_vvp), "scroll_event", G_CALLBACK(draw_scroll), vw);
749 g_signal_connect_swapped (G_OBJECT(vw->viking_vvp), "button_press_event", G_CALLBACK(draw_click), vw);
750 g_signal_connect_swapped (G_OBJECT(vw->viking_vvp), "button_release_event", G_CALLBACK(draw_release), vw);
751 g_signal_connect_swapped (G_OBJECT(vw->viking_vvp), "motion_notify_event", G_CALLBACK(draw_mouse_motion), vw);
752 g_signal_connect_swapped (G_OBJECT(vw->viking_vlp), "update", G_CALLBACK(draw_update), vw);
754 // Allow key presses to be processed anywhere
755 g_signal_connect_swapped (G_OBJECT (vw), "key_press_event", G_CALLBACK (key_press_event), vw);
757 hpaned = gtk_hpaned_new ();
758 gtk_paned_pack1 ( GTK_PANED(hpaned), GTK_WIDGET (vw->viking_vlp), FALSE, FALSE );
759 gtk_paned_pack2 ( GTK_PANED(hpaned), GTK_WIDGET (vw->viking_vvp), TRUE, TRUE );
761 /* This packs the button into the window (a gtk container). */
762 gtk_box_pack_start (GTK_BOX(main_vbox), hpaned, TRUE, TRUE, 0);
764 gtk_box_pack_end (GTK_BOX(main_vbox), GTK_WIDGET(vw->viking_vs), FALSE, TRUE, 0);
766 a_background_add_window ( vw );
768 window_list = g_slist_prepend ( window_list, vw);
770 gint height = VIKING_WINDOW_HEIGHT;
771 gint width = VIKING_WINDOW_WIDTH;
773 if ( a_vik_get_restore_window_state() ) {
774 if ( a_settings_get_integer ( VIK_SETTINGS_WIN_HEIGHT, &height ) ) {
775 // Enforce a basic minimum size
780 // No setting - so use default
781 height = VIKING_WINDOW_HEIGHT;
783 if ( a_settings_get_integer ( VIK_SETTINGS_WIN_WIDTH, &width ) ) {
784 // Enforce a basic minimum size
789 // No setting - so use default
790 width = VIKING_WINDOW_WIDTH;
793 if ( a_settings_get_boolean ( VIK_SETTINGS_WIN_MAX, &maxed ) )
795 gtk_window_maximize ( GTK_WINDOW(vw) );
798 if ( a_settings_get_boolean ( VIK_SETTINGS_WIN_FULLSCREEN, &full ) ) {
800 gtk_window_fullscreen ( GTK_WINDOW(vw) );
801 GtkWidget *check_box = gtk_ui_manager_get_widget ( vw->uim, "/ui/MainMenu/View/FullScreen" );
802 gtk_check_menu_item_set_active ( GTK_CHECK_MENU_ITEM(check_box), TRUE );
807 gtk_window_set_default_size ( GTK_WINDOW(vw), width, height );
811 vw->save_img_dia = NULL;
812 vw->save_img_dir_dia = NULL;
814 // Only accept Drag and Drop of files onto the viewport
815 gtk_drag_dest_set ( GTK_WIDGET(vw->viking_vvp), GTK_DEST_DEFAULT_ALL, NULL, 0, GDK_ACTION_COPY );
816 gtk_drag_dest_add_uri_targets ( GTK_WIDGET(vw->viking_vvp) );
817 g_signal_connect ( GTK_WIDGET(vw->viking_vvp), "drag-data-received", G_CALLBACK(drag_data_received_cb), NULL );
819 // Store the thread value so comparisons can be made to determine the gdk update method
820 // Hopefully we are storing the main thread value here :)
821 // [ATM any window initialization is always be performed by the main thread]
822 vw->thread = g_thread_self();
825 static VikWindow *window_new ()
827 return VIK_WINDOW ( g_object_new ( VIK_WINDOW_TYPE, NULL ) );
831 * Update the displayed map
832 * Only update the top most visible map layer
833 * ATM this assumes (as per defaults) the top most map has full alpha setting
834 * such that other other maps even though they may be active will not be seen
835 * It's more complicated to work out which maps are actually visible due to alpha settings
836 * and overkill for this simple refresh method.
838 static void simple_map_update ( VikWindow *vw, gboolean only_new )
840 // Find the most relevent single map layer to operate on
841 VikLayer *vl = vik_aggregate_layer_get_top_visible_layer_of_type (vik_layers_panel_get_top_layer(vw->viking_vlp), VIK_LAYER_MAPS);
843 vik_maps_layer_download ( VIK_MAPS_LAYER(vl), vw->viking_vvp, only_new );
847 * This is the global key press handler
848 * Global shortcuts are available at any time and hence are not restricted to when a certain tool is enabled
850 static gboolean key_press_event( VikWindow *vw, GdkEventKey *event, gpointer data )
852 // The keys handled here are not in the menuing system for a couple of reasons:
853 // . Keeps the menu size compact (alebit at expense of discoverably)
854 // . Allows differing key bindings to perform the same actions
856 // First decide if key events are related to the maps layer
857 gboolean map_download = FALSE;
858 gboolean map_download_only_new = TRUE; // Only new or reload
860 GdkModifierType modifiers = gtk_accelerator_get_default_mod_mask();
862 // Standard 'Refresh' keys: F5 or Ctrl+r
863 // Note 'F5' is actually handled via draw_refresh_cb() later on
864 // (not 'R' it's 'r' notice the case difference!!)
865 if ( event->keyval == GDK_r && (event->state & modifiers) == GDK_CONTROL_MASK ) {
867 map_download_only_new = TRUE;
869 // Full cache reload with Ctrl+F5 or Ctrl+Shift+r [This is not in the menu system]
870 // Note the use of uppercase R here since shift key has been pressed
871 else if ( (event->keyval == GDK_F5 && (event->state & modifiers) == GDK_CONTROL_MASK ) ||
872 ( event->keyval == GDK_R && (event->state & modifiers) == (GDK_CONTROL_MASK + GDK_SHIFT_MASK) ) ) {
874 map_download_only_new = FALSE;
877 if ( map_download ) {
878 simple_map_update ( vw, map_download_only_new );
881 VikLayer *vl = vik_layers_panel_get_selected ( vw->viking_vlp );
882 if (vl && vw->vt->active_tool != -1 && vw->vt->tools[vw->vt->active_tool].ti.key_press ) {
883 gint ltype = vw->vt->tools[vw->vt->active_tool].layer_type;
884 if ( vl && ltype == vl->type )
885 return vw->vt->tools[vw->vt->active_tool].ti.key_press(vl, event, vw->vt->tools[vw->vt->active_tool].state);
888 // Ensure called only on window tools (i.e. not on any of the Layer tools since the layer is NULL)
889 if ( vw->current_tool < TOOL_LAYER ) {
890 // No layer - but enable window tool keypress processing - these should be able to handle a NULL layer
891 if ( vw->vt->tools[vw->vt->active_tool].ti.key_press ) {
892 return vw->vt->tools[vw->vt->active_tool].ti.key_press ( vl, event, vw->vt->tools[vw->vt->active_tool].state );
896 /* Restore Main Menu via Escape key if the user has hidden it */
897 /* This key is more likely to be used as they may not remember the function key */
898 if ( event->keyval == GDK_Escape ) {
899 GtkWidget *check_box = gtk_ui_manager_get_widget ( vw->uim, "/ui/MainMenu/View/SetShow/ViewMainMenu" );
901 gboolean state = gtk_check_menu_item_get_active ( GTK_CHECK_MENU_ITEM(check_box) );
903 gtk_widget_show ( gtk_ui_manager_get_widget ( vw->uim, "/ui/MainMenu" ) );
904 gtk_check_menu_item_set_active ( GTK_CHECK_MENU_ITEM(check_box), TRUE );
905 return TRUE; /* handled keypress */
910 return FALSE; /* don't handle the keypress */
913 static gboolean delete_event( VikWindow *vw )
915 #ifdef VIKING_PROMPT_IF_MODIFIED
922 dia = GTK_DIALOG ( gtk_message_dialog_new ( GTK_WINDOW(vw), GTK_DIALOG_DESTROY_WITH_PARENT, GTK_MESSAGE_QUESTION, GTK_BUTTONS_NONE,
923 _("Do you want to save the changes you made to the document \"%s\"?\n"
925 "Your changes will be lost if you don't save them."),
926 window_get_filename ( vw ) ) );
927 gtk_dialog_add_buttons ( dia, _("Don't Save"), GTK_RESPONSE_NO, GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, GTK_STOCK_SAVE, GTK_RESPONSE_YES, NULL );
928 switch ( gtk_dialog_run ( dia ) )
930 case GTK_RESPONSE_NO: gtk_widget_destroy ( GTK_WIDGET(dia) ); return FALSE;
931 case GTK_RESPONSE_CANCEL: gtk_widget_destroy ( GTK_WIDGET(dia) ); return TRUE;
932 default: gtk_widget_destroy ( GTK_WIDGET(dia) ); return ! save_file(NULL, vw);
936 if ( window_count == 1 ) {
937 // On the final window close - save latest state - if it's wanted...
938 if ( a_vik_get_restore_window_state() ) {
939 gint state = gdk_window_get_state ( GTK_WIDGET(vw)->window );
940 gboolean state_max = state & GDK_WINDOW_STATE_MAXIMIZED;
941 a_settings_set_boolean ( VIK_SETTINGS_WIN_MAX, state_max );
943 gboolean state_fullscreen = state & GDK_WINDOW_STATE_FULLSCREEN;
944 a_settings_set_boolean ( VIK_SETTINGS_WIN_FULLSCREEN, state_fullscreen );
946 a_settings_set_boolean ( VIK_SETTINGS_WIN_SIDEPANEL, GTK_WIDGET_VISIBLE (GTK_WIDGET(vw->viking_vlp)) );
948 a_settings_set_boolean ( VIK_SETTINGS_WIN_STATUSBAR, GTK_WIDGET_VISIBLE (GTK_WIDGET(vw->viking_vs)) );
950 a_settings_set_boolean ( VIK_SETTINGS_WIN_TOOLBAR, GTK_WIDGET_VISIBLE (GTK_WIDGET(vw->toolbar)) );
952 // If supersized - no need to save the enlarged width+height values
953 if ( ! (state_fullscreen || state_max) ) {
955 gtk_window_get_size ( GTK_WINDOW (vw), &width, &height );
956 a_settings_set_integer ( VIK_SETTINGS_WIN_WIDTH, width );
957 a_settings_set_integer ( VIK_SETTINGS_WIN_HEIGHT, height );
961 a_settings_set_integer ( VIK_SETTINGS_WIN_SAVE_IMAGE_WIDTH, vw->draw_image_width );
962 a_settings_set_integer ( VIK_SETTINGS_WIN_SAVE_IMAGE_HEIGHT, vw->draw_image_height );
963 a_settings_set_boolean ( VIK_SETTINGS_WIN_SAVE_IMAGE_PNG, vw->draw_image_save_as_png );
970 static void newwindow_cb ( GtkAction *a, VikWindow *vw )
972 g_signal_emit ( G_OBJECT(vw), window_signals[VW_NEWWINDOW_SIGNAL], 0 );
975 static void draw_update ( VikWindow *vw )
981 static void draw_sync ( VikWindow *vw )
983 vik_viewport_sync(vw->viking_vvp);
988 * Split the status update, as sometimes only need to update the tool part
989 * also on initialization the zoom related stuff is not ready to be used
991 static void draw_status_tool ( VikWindow *vw )
993 if ( vw->current_tool == TOOL_LAYER )
994 // Use tooltip rather than the internal name as the tooltip is i8n
995 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 );
997 vik_statusbar_set_message ( vw->viking_vs, VIK_STATUSBAR_TOOL, _(tool_names[vw->current_tool]) );
1000 static void draw_status ( VikWindow *vw )
1002 static gchar zoom_level[22];
1003 gdouble xmpp = vik_viewport_get_xmpp (vw->viking_vvp);
1004 gdouble ympp = vik_viewport_get_ympp(vw->viking_vvp);
1005 gchar *unit = vik_viewport_get_coord_mode(vw->viking_vvp) == VIK_COORD_UTM ? _("mpp") : _("pixelfact");
1007 g_snprintf ( zoom_level, 22, "%.3f/%.3f %s", xmpp, ympp, unit );
1009 if ( (int)xmpp - xmpp < 0.0 )
1010 g_snprintf ( zoom_level, 22, "%.3f %s", xmpp, unit );
1012 /* xmpp should be a whole number so don't show useless .000 bit */
1013 g_snprintf ( zoom_level, 22, "%d %s", (int)xmpp, unit );
1015 vik_statusbar_set_message ( vw->viking_vs, VIK_STATUSBAR_ZOOM, zoom_level );
1017 draw_status_tool ( vw );
1020 void vik_window_set_redraw_trigger(VikLayer *vl)
1022 VikWindow *vw = VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vl));
1027 static void window_configure_event ( VikWindow *vw )
1029 static int first = 1;
1032 // This is a hack to set the cursor corresponding to the first tool
1033 // FIXME find the correct way to initialize both tool and its cursor
1035 vw->viewport_cursor = (GdkCursor *)toolbox_get_cursor(vw->vt, "Pan");
1036 /* We set cursor, even if it is NULL: it resets to default */
1037 gdk_window_set_cursor ( gtk_widget_get_window(GTK_WIDGET(vw->viking_vvp)), vw->viewport_cursor );
1041 static void draw_redraw ( VikWindow *vw )
1043 VikCoord old_center = vw->trigger_center;
1044 vw->trigger_center = *(vik_viewport_get_center(vw->viking_vvp));
1045 VikLayer *new_trigger = vw->trigger;
1047 VikLayer *old_trigger = VIK_LAYER(vik_viewport_get_trigger(vw->viking_vvp));
1049 if ( ! new_trigger )
1050 ; /* do nothing -- have to redraw everything. */
1051 else if ( (old_trigger != new_trigger) || !vik_coord_equals(&old_center, &vw->trigger_center) || (new_trigger->type == VIK_LAYER_AGGREGATE) )
1052 vik_viewport_set_trigger ( vw->viking_vvp, new_trigger ); /* todo: set to half_drawn mode if new trigger is above old */
1054 vik_viewport_set_half_drawn ( vw->viking_vvp, TRUE );
1057 vik_viewport_clear ( vw->viking_vvp);
1058 vik_layers_panel_draw_all ( vw->viking_vlp );
1059 vik_viewport_draw_scale ( vw->viking_vvp );
1060 vik_viewport_draw_copyright ( vw->viking_vvp );
1061 vik_viewport_draw_centermark ( vw->viking_vvp );
1062 vik_viewport_draw_logo ( vw->viking_vvp );
1064 vik_viewport_set_half_drawn ( vw->viking_vvp, FALSE ); /* just in case. */
1067 gboolean draw_buf_done = TRUE;
1069 static gboolean draw_buf(gpointer data)
1071 gpointer *pass_along = data;
1072 gdk_threads_enter();
1073 gdk_draw_drawable (pass_along[0], pass_along[1],
1074 pass_along[2], 0, 0, 0, 0, -1, -1);
1075 draw_buf_done = TRUE;
1076 gdk_threads_leave();
1081 /* Mouse event handlers ************************************************************************/
1083 static void vik_window_pan_click (VikWindow *vw, GdkEventButton *event)
1085 /* set panning origin */
1086 vw->pan_move = FALSE;
1087 vw->pan_x = (gint) event->x;
1088 vw->pan_y = (gint) event->y;
1091 static void draw_click (VikWindow *vw, GdkEventButton *event)
1093 gtk_widget_grab_focus ( GTK_WIDGET(vw->viking_vvp) );
1095 /* middle button pressed. we reserve all middle button and scroll events
1096 * for panning and zooming; tools only get left/right/movement
1098 if ( event->button == 2) {
1099 if ( vw->vt->tools[vw->vt->active_tool].ti.pan_handler )
1100 // Tool still may need to do something (such as disable something)
1101 toolbox_click(vw->vt, event);
1102 vik_window_pan_click ( vw, event );
1105 toolbox_click(vw->vt, event);
1109 static void vik_window_pan_move (VikWindow *vw, GdkEventMotion *event)
1111 if ( vw->pan_x != -1 ) {
1112 vik_viewport_set_center_screen ( vw->viking_vvp, vik_viewport_get_width(vw->viking_vvp)/2 - event->x + vw->pan_x,
1113 vik_viewport_get_height(vw->viking_vvp)/2 - event->y + vw->pan_y );
1114 vw->pan_move = TRUE;
1115 vw->pan_x = event->x;
1116 vw->pan_y = event->y;
1121 static void draw_mouse_motion (VikWindow *vw, GdkEventMotion *event)
1123 static VikCoord coord;
1124 static struct UTM utm;
1125 static struct LatLon ll;
1126 #define BUFFER_SIZE 50
1127 static char pointer_buf[BUFFER_SIZE];
1128 gchar *lat = NULL, *lon = NULL;
1131 VikDemInterpol interpol_method;
1133 /* This is a hack, but work far the best, at least for single pointer systems.
1134 * See http://bugzilla.gnome.org/show_bug.cgi?id=587714 for more. */
1136 gdk_window_get_pointer (event->window, &x, &y, NULL);
1140 toolbox_move(vw->vt, event);
1142 vik_viewport_screen_to_coord ( vw->viking_vvp, event->x, event->y, &coord );
1143 vik_coord_to_utm ( &coord, &utm );
1145 if ( vik_viewport_get_drawmode ( vw->viking_vvp ) == VIK_VIEWPORT_DRAWMODE_UTM ) {
1146 // Reuse lat for the first part (Zone + N or S, and lon for the second part (easting and northing) of a UTM format:
1147 // ZONE[N|S] EASTING NORTHING
1148 lat = g_malloc(4*sizeof(gchar));
1149 // NB zone is stored in a char but is an actual number
1150 g_snprintf (lat, 4, "%d%c", utm.zone, utm.letter);
1151 lon = g_malloc(16*sizeof(gchar));
1152 g_snprintf (lon, 16, "%d %d", (gint)utm.easting, (gint)utm.northing);
1155 a_coords_utm_to_latlon ( &utm, &ll );
1156 a_coords_latlon_to_string ( &ll, &lat, &lon );
1159 /* Change interpolate method according to scale */
1160 zoom = vik_viewport_get_zoom(vw->viking_vvp);
1162 interpol_method = VIK_DEM_INTERPOL_NONE;
1163 else if (zoom >= 1.0)
1164 interpol_method = VIK_DEM_INTERPOL_SIMPLE;
1166 interpol_method = VIK_DEM_INTERPOL_BEST;
1167 if ((alt = a_dems_get_elev_by_coord(&coord, interpol_method)) != VIK_DEM_INVALID_ELEVATION) {
1168 if ( a_vik_get_units_height () == VIK_UNITS_HEIGHT_METRES )
1169 g_snprintf ( pointer_buf, BUFFER_SIZE, _("%s %s %dm"), lat, lon, alt );
1171 g_snprintf ( pointer_buf, BUFFER_SIZE, _("%s %s %dft"), lat, lon, (int)VIK_METERS_TO_FEET(alt) );
1174 g_snprintf ( pointer_buf, BUFFER_SIZE, _("%s %s"), lat, lon );
1179 vik_statusbar_set_message ( vw->viking_vs, VIK_STATUSBAR_POSITION, pointer_buf );
1181 vik_window_pan_move ( vw, event );
1183 /* This is recommended by the GTK+ documentation, but does not work properly.
1184 * Use deprecated way until GTK+ gets a solution for correct motion hint handling:
1185 * http://bugzilla.gnome.org/show_bug.cgi?id=587714
1187 /* gdk_event_request_motions ( event ); */
1190 static void vik_window_pan_release ( VikWindow *vw, GdkEventButton *event )
1192 if ( vw->pan_move == FALSE )
1193 vik_viewport_set_center_screen ( vw->viking_vvp, vw->pan_x, vw->pan_y );
1195 vik_viewport_set_center_screen ( vw->viking_vvp, vik_viewport_get_width(vw->viking_vvp)/2 - event->x + vw->pan_x,
1196 vik_viewport_get_height(vw->viking_vvp)/2 - event->y + vw->pan_y );
1197 vw->pan_move = FALSE;
1198 vw->pan_x = vw->pan_y = -1;
1202 static void draw_release ( VikWindow *vw, GdkEventButton *event )
1204 gtk_widget_grab_focus ( GTK_WIDGET(vw->viking_vvp) );
1206 if ( event->button == 2 ) { /* move / pan */
1207 if ( vw->vt->tools[vw->vt->active_tool].ti.pan_handler )
1208 // Tool still may need to do something (such as reenable something)
1209 toolbox_release(vw->vt, event);
1210 vik_window_pan_release ( vw, event );
1213 toolbox_release(vw->vt, event);
1217 static void draw_scroll (VikWindow *vw, GdkEventScroll *event)
1219 guint modifiers = event->state & (GDK_SHIFT_MASK | GDK_CONTROL_MASK);
1220 if ( modifiers == GDK_CONTROL_MASK ) {
1221 /* control == pan up & down */
1222 if ( event->direction == GDK_SCROLL_UP )
1223 vik_viewport_set_center_screen ( vw->viking_vvp, vik_viewport_get_width(vw->viking_vvp)/2, vik_viewport_get_height(vw->viking_vvp)/3 );
1225 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 );
1226 } else if ( modifiers == GDK_SHIFT_MASK ) {
1227 /* shift == pan left & right */
1228 if ( event->direction == GDK_SCROLL_UP )
1229 vik_viewport_set_center_screen ( vw->viking_vvp, vik_viewport_get_width(vw->viking_vvp)/3, vik_viewport_get_height(vw->viking_vvp)/2 );
1231 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 );
1232 } else if ( modifiers == (GDK_CONTROL_MASK | GDK_SHIFT_MASK) ) {
1233 // This zoom is on the center position
1234 if ( event->direction == GDK_SCROLL_UP )
1235 vik_viewport_zoom_in (vw->viking_vvp);
1237 vik_viewport_zoom_out (vw->viking_vvp);
1239 /* make sure mouse is still over the same point on the map when we zoom */
1242 gint center_x = vik_viewport_get_width ( vw->viking_vvp ) / 2;
1243 gint center_y = vik_viewport_get_height ( vw->viking_vvp ) / 2;
1244 vik_viewport_screen_to_coord ( vw->viking_vvp, event->x, event->y, &coord );
1245 if ( event->direction == GDK_SCROLL_UP )
1246 vik_viewport_zoom_in (vw->viking_vvp);
1248 vik_viewport_zoom_out(vw->viking_vvp);
1249 vik_viewport_coord_to_screen ( vw->viking_vvp, &coord, &x, &y );
1250 vik_viewport_set_center_screen ( vw->viking_vvp, center_x + (x - event->x),
1251 center_y + (y - event->y) );
1259 /********************************************************************************
1261 ********************************************************************************/
1262 static void draw_ruler(VikViewport *vvp, GdkDrawable *d, GdkGC *gc, gint x1, gint y1, gint x2, gint y2, gdouble distance)
1266 GdkGC *labgc = vik_viewport_new_gc ( vvp, "#cccccc", 1);
1267 GdkGC *thickgc = gdk_gc_new(d);
1269 gdouble len = sqrt((x1-x2)*(x1-x2) + (y1-y2)*(y1-y2));
1270 gdouble dx = (x2-x1)/len*10;
1271 gdouble dy = (y2-y1)/len*10;
1272 gdouble c = cos(DEG2RAD(15.0));
1273 gdouble s = sin(DEG2RAD(15.0));
1275 gdouble baseangle = 0;
1278 /* draw line with arrow ends */
1280 gint tmp_x1=x1, tmp_y1=y1, tmp_x2=x2, tmp_y2=y2;
1281 a_viewport_clip_line(&tmp_x1, &tmp_y1, &tmp_x2, &tmp_y2);
1282 gdk_draw_line(d, gc, tmp_x1, tmp_y1, tmp_x2, tmp_y2);
1285 a_viewport_clip_line(&x1, &y1, &x2, &y2);
1286 gdk_draw_line(d, gc, x1, y1, x2, y2);
1288 gdk_draw_line(d, gc, x1 - dy, y1 + dx, x1 + dy, y1 - dx);
1289 gdk_draw_line(d, gc, x2 - dy, y2 + dx, x2 + dy, y2 - dx);
1290 gdk_draw_line(d, gc, x2, y2, x2 - (dx * c + dy * s), y2 - (dy * c - dx * s));
1291 gdk_draw_line(d, gc, x2, y2, x2 - (dx * c - dy * s), y2 - (dy * c + dx * s));
1292 gdk_draw_line(d, gc, x1, y1, x1 + (dx * c + dy * s), y1 + (dy * c - dx * s));
1293 gdk_draw_line(d, gc, x1, y1, x1 + (dx * c - dy * s), y1 + (dy * c + dx * s));
1299 vik_viewport_compute_bearing ( vvp, x1, y1, x2, y2, &angle, &baseangle );
1303 gdk_gc_copy(thickgc, gc);
1304 gdk_gc_set_line_attributes(thickgc, CW, GDK_LINE_SOLID, GDK_CAP_BUTT, GDK_JOIN_MITER);
1305 gdk_color_parse("#2255cc", &color);
1306 gdk_gc_set_rgb_fg_color(thickgc, &color);
1308 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);
1311 gdk_gc_copy(thickgc, gc);
1312 gdk_gc_set_line_attributes(thickgc, 2, GDK_LINE_SOLID, GDK_CAP_BUTT, GDK_JOIN_MITER);
1313 for (i=0; i<180; i++) {
1314 c = cos(DEG2RAD(i)*2 + baseangle);
1315 s = sin(DEG2RAD(i)*2 + baseangle);
1318 gdk_draw_line (d, gc, x1 + CR*c, y1 + CR*s, x1 + (CR+CW)*c, y1 + (CR+CW)*s);
1320 gdouble ticksize = 2*CW;
1321 gdk_draw_line (d, thickgc, x1 + (CR-CW)*c, y1 + (CR-CW)*s, x1 + (CR+ticksize)*c, y1 + (CR+ticksize)*s);
1325 gdk_draw_arc (d, gc, FALSE, x1-CR, y1-CR, 2*CR, 2*CR, 0, 64*360);
1326 gdk_draw_arc (d, gc, FALSE, x1-CR-CW, y1-CR-CW, 2*(CR+CW), 2*(CR+CW), 0, 64*360);
1327 gdk_draw_arc (d, gc, FALSE, x1-CR+CW, y1-CR+CW, 2*(CR-CW), 2*(CR-CW), 0, 64*360);
1328 c = (CR+CW*2)*cos(baseangle);
1329 s = (CR+CW*2)*sin(baseangle);
1330 gdk_draw_line (d, gc, x1-c, y1-s, x1+c, y1+s);
1331 gdk_draw_line (d, gc, x1+s, y1-c, x1-s, y1+c);
1334 #define LABEL(x, y, w, h) { \
1335 gdk_draw_rectangle(d, labgc, TRUE, (x)-2, (y)-1, (w)+4, (h)+1); \
1336 gdk_draw_rectangle(d, gc, FALSE, (x)-2, (y)-1, (w)+4, (h)+1); \
1337 gdk_draw_layout(d, gc, (x), (y), pl); }
1339 gint wd, hd, xd, yd;
1340 gint wb, hb, xb, yb;
1342 pl = gtk_widget_create_pango_layout (GTK_WIDGET(vvp), NULL);
1343 pango_layout_set_font_description (pl, gtk_widget_get_style(GTK_WIDGET(vvp))->font_desc);
1344 pango_layout_set_text(pl, "N", -1);
1345 gdk_draw_layout(d, gc, x1-5, y1-CR-3*CW-8, pl);
1347 /* draw label with distance */
1348 vik_units_distance_t dist_units = a_vik_get_units_distance ();
1349 switch (dist_units) {
1350 case VIK_UNITS_DISTANCE_KILOMETRES:
1351 if (distance >= 1000 && distance < 100000) {
1352 g_sprintf(str, "%3.2f km", distance/1000.0);
1353 } else if (distance < 1000) {
1354 g_sprintf(str, "%d m", (int)distance);
1356 g_sprintf(str, "%d km", (int)distance/1000);
1359 case VIK_UNITS_DISTANCE_MILES:
1360 if (distance >= VIK_MILES_TO_METERS(1) && distance < VIK_MILES_TO_METERS(100)) {
1361 g_sprintf(str, "%3.2f miles", VIK_METERS_TO_MILES(distance));
1362 } else if (distance < VIK_MILES_TO_METERS(1)) {
1363 g_sprintf(str, "%d yards", (int)(distance*1.0936133));
1365 g_sprintf(str, "%d miles", (int)VIK_METERS_TO_MILES(distance));
1369 g_critical("Houston, we've had a problem. distance=%d", dist_units);
1372 pango_layout_set_text(pl, str, -1);
1374 pango_layout_get_pixel_size ( pl, &wd, &hd );
1376 xd = (x1+x2)/2 + dy;
1377 yd = (y1+y2)/2 - hd/2 - dx;
1379 xd = (x1+x2)/2 - dy;
1380 yd = (y1+y2)/2 - hd/2 + dx;
1383 if ( xd < -5 || yd < -5 || xd > vik_viewport_get_width(vvp)+5 || yd > vik_viewport_get_height(vvp)+5 ) {
1388 LABEL(xd, yd, wd, hd);
1390 /* draw label with bearing */
1391 g_sprintf(str, "%3.1f°", RAD2DEG(angle));
1392 pango_layout_set_text(pl, str, -1);
1393 pango_layout_get_pixel_size ( pl, &wb, &hb );
1394 xb = x1 + CR*cos(angle-M_PI_2);
1395 yb = y1 + CR*sin(angle-M_PI_2);
1397 if ( xb < -5 || yb < -5 || xb > vik_viewport_get_width(vvp)+5 || yb > vik_viewport_get_height(vvp)+5 ) {
1403 GdkRectangle r1 = {xd-2, yd-1, wd+4, hd+1}, r2 = {xb-2, yb-1, wb+4, hb+1};
1404 if (gdk_rectangle_intersect(&r1, &r2, &r2)) {
1408 LABEL(xb, yb, wb, hb);
1412 g_object_unref ( G_OBJECT ( pl ) );
1413 g_object_unref ( G_OBJECT ( labgc ) );
1414 g_object_unref ( G_OBJECT ( thickgc ) );
1420 gboolean has_oldcoord;
1422 } ruler_tool_state_t;
1424 static gpointer ruler_create (VikWindow *vw, VikViewport *vvp)
1426 ruler_tool_state_t *s = g_new(ruler_tool_state_t, 1);
1429 s->has_oldcoord = FALSE;
1433 static void ruler_destroy (ruler_tool_state_t *s)
1438 static VikLayerToolFuncStatus ruler_click (VikLayer *vl, GdkEventButton *event, ruler_tool_state_t *s)
1443 if ( event->button == 1 ) {
1444 gchar *lat=NULL, *lon=NULL;
1445 vik_viewport_screen_to_coord ( s->vvp, (gint) event->x, (gint) event->y, &coord );
1446 vik_coord_to_latlon ( &coord, &ll );
1447 a_coords_latlon_to_string ( &ll, &lat, &lon );
1448 if ( s->has_oldcoord ) {
1449 vik_units_distance_t dist_units = a_vik_get_units_distance ();
1450 switch (dist_units) {
1451 case VIK_UNITS_DISTANCE_KILOMETRES:
1452 temp = g_strdup_printf ( "%s %s DIFF %f meters", lat, lon, vik_coord_diff( &coord, &(s->oldcoord) ) );
1454 case VIK_UNITS_DISTANCE_MILES:
1455 temp = g_strdup_printf ( "%s %s DIFF %f miles", lat, lon, VIK_METERS_TO_MILES(vik_coord_diff( &coord, &(s->oldcoord) )) );
1458 temp = g_strdup_printf ("Just to keep the compiler happy");
1459 g_critical("Houston, we've had a problem. distance=%d", dist_units);
1462 s->has_oldcoord = FALSE;
1465 temp = g_strdup_printf ( "%s %s", lat, lon );
1466 s->has_oldcoord = TRUE;
1469 vik_statusbar_set_message ( s->vw->viking_vs, VIK_STATUSBAR_INFO, temp );
1472 s->oldcoord = coord;
1475 vik_viewport_set_center_screen ( s->vvp, (gint) event->x, (gint) event->y );
1476 draw_update ( s->vw );
1478 return VIK_LAYER_TOOL_ACK;
1481 static VikLayerToolFuncStatus ruler_move (VikLayer *vl, GdkEventMotion *event, ruler_tool_state_t *s)
1483 VikViewport *vvp = s->vvp;
1484 VikWindow *vw = s->vw;
1490 if ( s->has_oldcoord ) {
1491 int oldx, oldy, w1, h1, w2, h2;
1492 static GdkPixmap *buf = NULL;
1493 gchar *lat=NULL, *lon=NULL;
1494 w1 = vik_viewport_get_width(vvp);
1495 h1 = vik_viewport_get_height(vvp);
1497 buf = gdk_pixmap_new ( gtk_widget_get_window(GTK_WIDGET(vvp)), w1, h1, -1 );
1499 gdk_drawable_get_size(buf, &w2, &h2);
1500 if (w1 != w2 || h1 != h2) {
1501 g_object_unref ( G_OBJECT ( buf ) );
1502 buf = gdk_pixmap_new ( gtk_widget_get_window(GTK_WIDGET(vvp)), w1, h1, -1 );
1505 vik_viewport_screen_to_coord ( vvp, (gint) event->x, (gint) event->y, &coord );
1506 vik_coord_to_latlon ( &coord, &ll );
1507 vik_viewport_coord_to_screen ( vvp, &s->oldcoord, &oldx, &oldy );
1509 gdk_draw_drawable (buf, gtk_widget_get_style(GTK_WIDGET(vvp))->black_gc,
1510 vik_viewport_get_pixmap(vvp), 0, 0, 0, 0, -1, -1);
1511 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)) );
1512 if (draw_buf_done) {
1513 static gpointer pass_along[3];
1514 pass_along[0] = gtk_widget_get_window(GTK_WIDGET(vvp));
1515 pass_along[1] = gtk_widget_get_style(GTK_WIDGET(vvp))->black_gc;
1516 pass_along[2] = buf;
1517 g_idle_add_full (G_PRIORITY_HIGH_IDLE + 10, draw_buf, pass_along, NULL);
1518 draw_buf_done = FALSE;
1520 a_coords_latlon_to_string(&ll, &lat, &lon);
1521 vik_units_distance_t dist_units = a_vik_get_units_distance ();
1522 switch (dist_units) {
1523 case VIK_UNITS_DISTANCE_KILOMETRES:
1524 temp = g_strdup_printf ( "%s %s DIFF %f meters", lat, lon, vik_coord_diff( &coord, &(s->oldcoord) ) );
1526 case VIK_UNITS_DISTANCE_MILES:
1527 temp = g_strdup_printf ( "%s %s DIFF %f miles", lat, lon, VIK_METERS_TO_MILES (vik_coord_diff( &coord, &(s->oldcoord) )) );
1530 temp = g_strdup_printf ("Just to keep the compiler happy");
1531 g_critical("Houston, we've had a problem. distance=%d", dist_units);
1533 vik_statusbar_set_message ( vw->viking_vs, VIK_STATUSBAR_INFO, temp );
1536 return VIK_LAYER_TOOL_ACK;
1539 static VikLayerToolFuncStatus ruler_release (VikLayer *vl, GdkEventButton *event, ruler_tool_state_t *s)
1541 return VIK_LAYER_TOOL_ACK;
1544 static void ruler_deactivate (VikLayer *vl, ruler_tool_state_t *s)
1546 draw_update ( s->vw );
1549 static gboolean ruler_key_press (VikLayer *vl, GdkEventKey *event, ruler_tool_state_t *s)
1551 if (event->keyval == GDK_Escape) {
1552 s->has_oldcoord = FALSE;
1553 ruler_deactivate ( vl, s );
1556 // Regardless of whether we used it, return false so other GTK things may use it
1560 static VikToolInterface ruler_tool =
1561 // NB Ctrl+Shift+R is used for Refresh (deemed more important), so use 'U' instead
1562 { { "Ruler", "vik-icon-ruler", N_("_Ruler"), "<control><shift>U", N_("Ruler Tool"), 2 },
1563 (VikToolConstructorFunc) ruler_create,
1564 (VikToolDestructorFunc) ruler_destroy,
1565 (VikToolActivationFunc) NULL,
1566 (VikToolActivationFunc) ruler_deactivate,
1567 (VikToolMouseFunc) ruler_click,
1568 (VikToolMouseMoveFunc) ruler_move,
1569 (VikToolMouseFunc) ruler_release,
1570 (VikToolKeyFunc) ruler_key_press,
1572 GDK_CURSOR_IS_PIXMAP,
1573 &cursor_ruler_pixbuf };
1574 /*** end ruler code ********************************************************/
1578 /********************************************************************************
1580 ********************************************************************************/
1585 // Track zoom bounds for zoom tool with shift modifier:
1586 gboolean bounds_active;
1589 } zoom_tool_state_t;
1592 * In case the screen size has changed
1594 static void zoomtool_resize_pixmap (zoom_tool_state_t *zts)
1598 // Allocate a drawing area the size of the viewport
1599 w1 = vik_viewport_get_width ( zts->vw->viking_vvp );
1600 h1 = vik_viewport_get_height ( zts->vw->viking_vvp );
1602 if ( !zts->pixmap ) {
1604 zts->pixmap = gdk_pixmap_new ( gtk_widget_get_window(GTK_WIDGET(zts->vw->viking_vvp)), w1, h1, -1 );
1607 gdk_drawable_get_size ( zts->pixmap, &w2, &h2 );
1609 if ( w1 != w2 || h1 != h2 ) {
1610 // Has changed - delete and recreate with new values
1611 g_object_unref ( G_OBJECT ( zts->pixmap ) );
1612 zts->pixmap = gdk_pixmap_new ( gtk_widget_get_window(GTK_WIDGET(zts->vw->viking_vvp)), w1, h1, -1 );
1616 static gpointer zoomtool_create (VikWindow *vw, VikViewport *vvp)
1618 zoom_tool_state_t *zts = g_new(zoom_tool_state_t, 1);
1623 zts->bounds_active = FALSE;
1627 static void zoomtool_destroy ( zoom_tool_state_t *zts)
1630 g_object_unref ( G_OBJECT ( zts->pixmap ) );
1634 static VikLayerToolFuncStatus zoomtool_click (VikLayer *vl, GdkEventButton *event, zoom_tool_state_t *zts)
1636 zts->vw->modified = TRUE;
1637 guint modifiers = event->state & (GDK_SHIFT_MASK | GDK_CONTROL_MASK);
1641 gint center_x = vik_viewport_get_width ( zts->vw->viking_vvp ) / 2;
1642 gint center_y = vik_viewport_get_height ( zts->vw->viking_vvp ) / 2;
1644 gboolean skip_update = FALSE;
1646 zts->bounds_active = FALSE;
1648 if ( modifiers == (GDK_CONTROL_MASK | GDK_SHIFT_MASK) ) {
1649 // This zoom is on the center position
1650 vik_viewport_set_center_screen ( zts->vw->viking_vvp, center_x, center_y );
1651 if ( event->button == 1 )
1652 vik_viewport_zoom_in (zts->vw->viking_vvp);
1653 else if ( event->button == 3 )
1654 vik_viewport_zoom_out (zts->vw->viking_vvp);
1656 else if ( modifiers == GDK_CONTROL_MASK ) {
1657 // This zoom is to recenter on the mouse position
1658 vik_viewport_set_center_screen ( zts->vw->viking_vvp, (gint) event->x, (gint) event->y );
1659 if ( event->button == 1 )
1660 vik_viewport_zoom_in (zts->vw->viking_vvp);
1661 else if ( event->button == 3 )
1662 vik_viewport_zoom_out (zts->vw->viking_vvp);
1664 else if ( modifiers == GDK_SHIFT_MASK ) {
1665 // Get start of new zoom bounds
1666 if ( event->button == 1 ) {
1667 zts->bounds_active = TRUE;
1668 zts->start_x = (gint) event->x;
1669 zts->start_y = (gint) event->y;
1674 /* make sure mouse is still over the same point on the map when we zoom */
1675 vik_viewport_screen_to_coord ( zts->vw->viking_vvp, event->x, event->y, &coord );
1676 if ( event->button == 1 )
1677 vik_viewport_zoom_in (zts->vw->viking_vvp);
1678 else if ( event->button == 3 )
1679 vik_viewport_zoom_out(zts->vw->viking_vvp);
1680 vik_viewport_coord_to_screen ( zts->vw->viking_vvp, &coord, &x, &y );
1681 vik_viewport_set_center_screen ( zts->vw->viking_vvp,
1682 center_x + (x - event->x),
1683 center_y + (y - event->y) );
1687 draw_update ( zts->vw );
1689 return VIK_LAYER_TOOL_ACK;
1692 static VikLayerToolFuncStatus zoomtool_move (VikLayer *vl, GdkEventMotion *event, zoom_tool_state_t *zts)
1694 guint modifiers = event->state & (GDK_SHIFT_MASK | GDK_CONTROL_MASK);
1696 if ( zts->bounds_active && modifiers == GDK_SHIFT_MASK ) {
1697 zoomtool_resize_pixmap ( zts );
1699 // Blank out currently drawn area
1700 gdk_draw_drawable ( zts->pixmap,
1701 gtk_widget_get_style(GTK_WIDGET(zts->vw->viking_vvp))->black_gc,
1702 vik_viewport_get_pixmap(zts->vw->viking_vvp),
1703 0, 0, 0, 0, -1, -1);
1705 // Calculate new box starting point & size in pixels
1706 int xx, yy, width, height;
1707 if ( event->y > zts->start_y ) {
1709 height = event->y-zts->start_y;
1713 height = zts->start_y-event->y;
1715 if ( event->x > zts->start_x ) {
1717 width = event->x-zts->start_x;
1721 width = zts->start_x-event->x;
1725 gdk_draw_rectangle (zts->pixmap, gtk_widget_get_style(GTK_WIDGET(zts->vw->viking_vvp))->black_gc, FALSE, xx, yy, width, height);
1727 // Only actually draw when there's time to do so
1728 if (draw_buf_done) {
1729 static gpointer pass_along[3];
1730 pass_along[0] = gtk_widget_get_window(GTK_WIDGET(zts->vw->viking_vvp));
1731 pass_along[1] = gtk_widget_get_style(GTK_WIDGET(zts->vw->viking_vvp))->black_gc;
1732 pass_along[2] = zts->pixmap;
1733 g_idle_add_full (G_PRIORITY_HIGH_IDLE + 10, draw_buf, pass_along, NULL);
1734 draw_buf_done = FALSE;
1737 return VIK_LAYER_TOOL_ACK;
1740 static VikLayerToolFuncStatus zoomtool_release (VikLayer *vl, GdkEventButton *event, zoom_tool_state_t *zts)
1742 guint modifiers = event->state & (GDK_SHIFT_MASK | GDK_CONTROL_MASK);
1744 zts->bounds_active = FALSE;
1746 // Ensure haven't just released on the exact same position
1747 // i.e. probably haven't moved the mouse at all
1748 if ( modifiers == GDK_SHIFT_MASK && !( ( event->x == zts->start_x ) && ( event->y == zts->start_y )) ) {
1750 VikCoord coord1, coord2;
1751 vik_viewport_screen_to_coord ( zts->vw->viking_vvp, zts->start_x, zts->start_y, &coord1);
1752 vik_viewport_screen_to_coord ( zts->vw->viking_vvp, event->x, event->y, &coord2);
1754 // From the extend of the bounds pick the best zoom level
1755 // c.f. trw_layer_zoom_to_show_latlons()
1756 // Maybe refactor...
1757 struct LatLon ll1, ll2;
1758 vik_coord_to_latlon(&coord1, &ll1);
1759 vik_coord_to_latlon(&coord2, &ll2);
1760 struct LatLon average = { (ll1.lat+ll2.lat)/2,
1761 (ll1.lon+ll2.lon)/2 };
1763 VikCoord new_center;
1764 vik_coord_load_from_latlon ( &new_center, vik_viewport_get_coord_mode ( zts->vw->viking_vvp ), &average );
1765 vik_viewport_set_center_coord ( zts->vw->viking_vvp, &new_center );
1767 /* Convert into definite 'smallest' and 'largest' positions */
1768 struct LatLon minmin;
1769 if ( ll1.lat < ll2.lat )
1770 minmin.lat = ll1.lat;
1772 minmin.lat = ll2.lat;
1774 struct LatLon maxmax;
1775 if ( ll1.lon > ll2.lon )
1776 maxmax.lon = ll1.lon;
1778 maxmax.lon = ll2.lon;
1780 /* Always recalculate the 'best' zoom level */
1781 gdouble zoom = VIK_VIEWPORT_MIN_ZOOM;
1782 vik_viewport_set_zoom ( zts->vw->viking_vvp, zoom );
1784 gdouble min_lat, max_lat, min_lon, max_lon;
1785 /* Should only be a maximum of about 18 iterations from min to max zoom levels */
1786 while ( zoom <= VIK_VIEWPORT_MAX_ZOOM ) {
1787 vik_viewport_get_min_max_lat_lon ( zts->vw->viking_vvp, &min_lat, &max_lat, &min_lon, &max_lon );
1788 /* NB I think the logic used in this test to determine if the bounds is within view
1789 fails if track goes across 180 degrees longitude.
1790 Hopefully that situation is not too common...
1791 Mind you viking doesn't really do edge locations to well anyway */
1792 if ( min_lat < minmin.lat &&
1793 max_lat > minmin.lat &&
1794 min_lon < maxmax.lon &&
1795 max_lon > maxmax.lon )
1796 /* Found within zoom level */
1801 vik_viewport_set_zoom ( zts->vw->viking_vvp, zoom );
1804 draw_update ( zts->vw );
1806 return VIK_LAYER_TOOL_ACK;
1809 static VikToolInterface zoom_tool =
1810 { { "Zoom", "vik-icon-zoom", N_("_Zoom"), "<control><shift>Z", N_("Zoom Tool"), 1 },
1811 (VikToolConstructorFunc) zoomtool_create,
1812 (VikToolDestructorFunc) zoomtool_destroy,
1813 (VikToolActivationFunc) NULL,
1814 (VikToolActivationFunc) NULL,
1815 (VikToolMouseFunc) zoomtool_click,
1816 (VikToolMouseMoveFunc) zoomtool_move,
1817 (VikToolMouseFunc) zoomtool_release,
1820 GDK_CURSOR_IS_PIXMAP,
1821 &cursor_zoom_pixbuf };
1822 /*** end zoom code ********************************************************/
1824 /********************************************************************************
1826 ********************************************************************************/
1827 static gpointer pantool_create (VikWindow *vw, VikViewport *vvp)
1832 static VikLayerToolFuncStatus pantool_click (VikLayer *vl, GdkEventButton *event, VikWindow *vw)
1834 vw->modified = TRUE;
1835 if ( event->button == 1 )
1836 vik_window_pan_click ( vw, event );
1838 return VIK_LAYER_TOOL_ACK;
1841 static VikLayerToolFuncStatus pantool_move (VikLayer *vl, GdkEventMotion *event, VikWindow *vw)
1843 vik_window_pan_move ( vw, event );
1844 return VIK_LAYER_TOOL_ACK;
1847 static VikLayerToolFuncStatus pantool_release (VikLayer *vl, GdkEventButton *event, VikWindow *vw)
1849 if ( event->button == 1 )
1850 vik_window_pan_release ( vw, event );
1851 return VIK_LAYER_TOOL_ACK;
1854 static VikToolInterface pan_tool =
1855 { { "Pan", "vik-icon-pan", N_("_Pan"), "<control><shift>P", N_("Pan Tool"), 0 },
1856 (VikToolConstructorFunc) pantool_create,
1857 (VikToolDestructorFunc) NULL,
1858 (VikToolActivationFunc) NULL,
1859 (VikToolActivationFunc) NULL,
1860 (VikToolMouseFunc) pantool_click,
1861 (VikToolMouseMoveFunc) pantool_move,
1862 (VikToolMouseFunc) pantool_release,
1866 /*** end pan code ********************************************************/
1868 /********************************************************************************
1870 ********************************************************************************/
1871 static gpointer selecttool_create (VikWindow *vw, VikViewport *vvp)
1873 tool_ed_t *t = g_new(tool_ed_t, 1);
1877 t->is_waypoint = FALSE;
1881 static void selecttool_destroy (tool_ed_t *t)
1889 GdkEventButton *event;
1890 tool_ed_t *tool_edit;
1893 static void click_layer_selected (VikLayer *vl, clicker *ck)
1895 /* Do nothing when function call returns true; */
1896 /* i.e. stop on first found item */
1899 if ( vik_layer_get_interface(vl->type)->select_click )
1900 ck->cont = !vik_layer_get_interface(vl->type)->select_click ( vl, ck->event, ck->vvp, ck->tool_edit );
1903 static VikLayerToolFuncStatus selecttool_click (VikLayer *vl, GdkEventButton *event, tool_ed_t *t)
1905 /* Only allow selection on primary button */
1906 if ( event->button == 1 ) {
1907 /* Enable click to apply callback to potentially all track/waypoint layers */
1908 /* Useful as we can find things that aren't necessarily in the currently selected layer */
1909 GList* gl = vik_layers_panel_get_all_layers_of_type ( t->vw->viking_vlp, VIK_LAYER_TRW, FALSE ); // Don't get invisible layers
1912 ck.vvp = t->vw->viking_vvp;
1915 g_list_foreach ( gl, (GFunc) click_layer_selected, &ck );
1918 // If nothing found then deselect & redraw screen if necessary to remove the highlight
1921 VikTreeview *vtv = vik_layers_panel_get_treeview ( t->vw->viking_vlp );
1923 if ( vik_treeview_get_selected_iter ( vtv, &iter ) ) {
1924 // Only clear if selected thing is a TrackWaypoint layer or a sublayer
1925 gint type = vik_treeview_item_get_type ( vtv, &iter );
1926 if ( type == VIK_TREEVIEW_TYPE_SUBLAYER ||
1927 VIK_LAYER(vik_treeview_item_get_pointer ( vtv, &iter ))->type == VIK_LAYER_TRW ) {
1929 vik_treeview_item_unselect ( vtv, &iter );
1930 if ( vik_window_clear_highlight ( t->vw ) )
1931 draw_update ( t->vw );
1936 else if ( ( event->button == 3 ) && ( vl && ( vl->type == VIK_LAYER_TRW ) ) ) {
1938 /* Act on currently selected item to show menu */
1939 if ( t->vw->selected_track || t->vw->selected_waypoint )
1940 if ( vik_layer_get_interface(vl->type)->show_viewport_menu )
1941 vik_layer_get_interface(vl->type)->show_viewport_menu ( vl, event, t->vw->viking_vvp );
1944 return VIK_LAYER_TOOL_ACK;
1947 static VikLayerToolFuncStatus selecttool_move (VikLayer *vl, GdkEventButton *event, tool_ed_t *t)
1949 /* Only allow selection on primary button */
1950 if ( event->button == 1 ) {
1951 // Don't care about vl here
1953 if ( vik_layer_get_interface(VIK_LAYER_TRW)->select_move )
1954 vik_layer_get_interface(VIK_LAYER_TRW)->select_move ( vl, event, t->vvp, t );
1956 return VIK_LAYER_TOOL_ACK;
1959 static VikLayerToolFuncStatus selecttool_release (VikLayer *vl, GdkEventButton *event, tool_ed_t *t)
1961 /* Only allow selection on primary button */
1962 if ( event->button == 1 ) {
1963 // Don't care about vl here
1965 if ( vik_layer_get_interface(VIK_LAYER_TRW)->select_release )
1966 vik_layer_get_interface(VIK_LAYER_TRW)->select_release ( (VikLayer*)t->vtl, event, t->vvp, t );
1968 return VIK_LAYER_TOOL_ACK;
1971 static VikToolInterface select_tool =
1972 { { "Select", "vik-icon-select", N_("_Select"), "<control><shift>S", N_("Select Tool"), 3 },
1973 (VikToolConstructorFunc) selecttool_create,
1974 (VikToolDestructorFunc) selecttool_destroy,
1975 (VikToolActivationFunc) NULL,
1976 (VikToolActivationFunc) NULL,
1977 (VikToolMouseFunc) selecttool_click,
1978 (VikToolMouseMoveFunc) selecttool_move,
1979 (VikToolMouseFunc) selecttool_release,
1980 (VikToolKeyFunc) NULL,
1985 /*** end select tool code ********************************************************/
1987 static void draw_pan_cb ( GtkAction *a, VikWindow *vw )
1989 // Since the treeview cell editting intercepts standard keyboard handlers, it means we can receive events here
1990 // Thus if currently editting, ensure we don't move the viewport when Ctrl+<arrow> is received
1991 VikLayer *sel = vik_layers_panel_get_selected ( vw->viking_vlp );
1992 if ( sel && vik_treeview_get_editing ( sel->vt ) )
1995 if (!strcmp(gtk_action_get_name(a), "PanNorth")) {
1996 vik_viewport_set_center_screen ( vw->viking_vvp, vik_viewport_get_width(vw->viking_vvp)/2, 0 );
1997 } else if (!strcmp(gtk_action_get_name(a), "PanEast")) {
1998 vik_viewport_set_center_screen ( vw->viking_vvp, vik_viewport_get_width(vw->viking_vvp), vik_viewport_get_height(vw->viking_vvp)/2 );
1999 } else if (!strcmp(gtk_action_get_name(a), "PanSouth")) {
2000 vik_viewport_set_center_screen ( vw->viking_vvp, vik_viewport_get_width(vw->viking_vvp)/2, vik_viewport_get_height(vw->viking_vvp) );
2001 } else if (!strcmp(gtk_action_get_name(a), "PanWest")) {
2002 vik_viewport_set_center_screen ( vw->viking_vvp, 0, vik_viewport_get_height(vw->viking_vvp)/2 );
2007 static void full_screen_cb ( GtkAction *a, VikWindow *vw )
2009 GtkWidget *check_box = gtk_ui_manager_get_widget ( vw->uim, "/ui/MainMenu/View/FullScreen" );
2010 g_assert(check_box);
2011 gboolean state = gtk_check_menu_item_get_active ( GTK_CHECK_MENU_ITEM(check_box));
2013 gtk_window_fullscreen ( GTK_WINDOW(vw) );
2015 gtk_window_unfullscreen ( GTK_WINDOW(vw) );
2018 static void draw_zoom_cb ( GtkAction *a, VikWindow *vw )
2022 if (!strcmp(gtk_action_get_name(a), "ZoomIn")) {
2025 else if (!strcmp(gtk_action_get_name(a), "ZoomOut")) {
2028 else if (!strcmp(gtk_action_get_name(a), "Zoom0.25")) {
2031 else if (!strcmp(gtk_action_get_name(a), "Zoom0.5")) {
2035 gchar *s = (gchar *)gtk_action_get_name(a);
2041 case -3: vik_viewport_zoom_in ( vw->viking_vvp ); break;
2042 case -4: vik_viewport_zoom_out ( vw->viking_vvp ); break;
2043 case -1: vik_viewport_set_zoom ( vw->viking_vvp, 0.5 ); break;
2044 case -2: vik_viewport_set_zoom ( vw->viking_vvp, 0.25 ); break;
2045 default: vik_viewport_set_zoom ( vw->viking_vvp, what );
2050 static void draw_goto_cb ( GtkAction *a, VikWindow *vw )
2052 VikCoord new_center;
2054 if (!strcmp(gtk_action_get_name(a), "GotoLL")) {
2055 struct LatLon ll, llold;
2056 vik_coord_to_latlon ( vik_viewport_get_center ( vw->viking_vvp ), &llold );
2057 if ( a_dialog_goto_latlon ( GTK_WINDOW(vw), &ll, &llold ) )
2058 vik_coord_load_from_latlon ( &new_center, vik_viewport_get_coord_mode(vw->viking_vvp), &ll );
2062 else if (!strcmp(gtk_action_get_name(a), "GotoUTM")) {
2063 struct UTM utm, utmold;
2064 vik_coord_to_utm ( vik_viewport_get_center ( vw->viking_vvp ), &utmold );
2065 if ( a_dialog_goto_utm ( GTK_WINDOW(vw), &utm, &utmold ) )
2066 vik_coord_load_from_utm ( &new_center, vik_viewport_get_coord_mode(vw->viking_vvp), &utm );
2071 g_critical("Houston, we've had a problem.");
2075 vik_viewport_set_center_coord ( vw->viking_vvp, &new_center );
2080 * Refresh maps displayed
2082 static void draw_refresh_cb ( GtkAction *a, VikWindow *vw )
2084 // Only get 'new' maps
2085 simple_map_update ( vw, TRUE );
2088 static void menu_addlayer_cb ( GtkAction *a, VikWindow *vw )
2090 VikLayerTypeEnum type;
2091 for ( type = 0; type < VIK_LAYER_NUM_TYPES; type++ ) {
2092 if (!strcmp(vik_layer_get_interface(type)->name, gtk_action_get_name(a))) {
2093 if ( vik_layers_panel_new_layer ( vw->viking_vlp, type ) ) {
2095 vw->modified = TRUE;
2101 static void menu_copy_layer_cb ( GtkAction *a, VikWindow *vw )
2103 a_clipboard_copy_selected ( vw->viking_vlp );
2106 static void menu_cut_layer_cb ( GtkAction *a, VikWindow *vw )
2108 vik_layers_panel_cut_selected ( vw->viking_vlp );
2109 vw->modified = TRUE;
2112 static void menu_paste_layer_cb ( GtkAction *a, VikWindow *vw )
2114 if ( vik_layers_panel_paste_selected ( vw->viking_vlp ) )
2116 vw->modified = TRUE;
2120 static void menu_properties_cb ( GtkAction *a, VikWindow *vw )
2122 if ( ! vik_layers_panel_properties ( vw->viking_vlp ) )
2123 a_dialog_info_msg ( GTK_WINDOW(vw), _("You must select a layer to show its properties.") );
2126 static void help_help_cb ( GtkAction *a, VikWindow *vw )
2129 ShellExecute(NULL, "open", ""PACKAGE".pdf", NULL, NULL, SW_SHOWNORMAL);
2132 uri = g_strdup_printf("ghelp:%s", PACKAGE);
2133 GError *error = NULL;
2134 gboolean show = gtk_show_uri (NULL, uri, GDK_CURRENT_TIME, &error);
2135 if ( !show && !error )
2136 // No error to show, so unlikely this will get called
2137 a_dialog_error_msg ( GTK_WINDOW(vw), _("The help system is not available.") );
2140 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 );
2141 g_error_free ( error );
2144 #endif /* WINDOWS */
2147 static void help_about_cb ( GtkAction *a, VikWindow *vw )
2149 a_dialog_about(GTK_WINDOW(vw));
2152 static void menu_delete_layer_cb ( GtkAction *a, VikWindow *vw )
2154 if ( vik_layers_panel_get_selected ( vw->viking_vlp ) )
2156 vik_layers_panel_delete_selected ( vw->viking_vlp );
2157 vw->modified = TRUE;
2160 a_dialog_info_msg ( GTK_WINDOW(vw), _("You must select a layer to delete.") );
2163 static void view_side_panel_cb ( GtkAction *a, VikWindow *vw )
2165 GtkWidget *check_box = gtk_ui_manager_get_widget ( vw->uim, "/ui/MainMenu/View/SetShow/ViewSidePanel" );
2166 g_assert(check_box);
2167 gboolean state = gtk_check_menu_item_get_active ( GTK_CHECK_MENU_ITEM(check_box));
2169 gtk_widget_show(GTK_WIDGET(vw->viking_vlp));
2171 gtk_widget_hide(GTK_WIDGET(vw->viking_vlp));
2174 static void view_statusbar_cb ( GtkAction *a, VikWindow *vw )
2176 GtkWidget *check_box = gtk_ui_manager_get_widget ( vw->uim, "/ui/MainMenu/View/SetShow/ViewStatusBar" );
2179 gboolean state = gtk_check_menu_item_get_active ( GTK_CHECK_MENU_ITEM(check_box) );
2181 gtk_widget_show ( GTK_WIDGET(vw->viking_vs) );
2183 gtk_widget_hide ( GTK_WIDGET(vw->viking_vs) );
2186 static void view_toolbar_cb ( GtkAction *a, VikWindow *vw )
2188 GtkWidget *check_box = gtk_ui_manager_get_widget ( vw->uim, "/ui/MainMenu/View/SetShow/ViewToolbar" );
2191 gboolean state = gtk_check_menu_item_get_active ( GTK_CHECK_MENU_ITEM(check_box) );
2193 gtk_widget_show ( GTK_WIDGET(vw->toolbar) );
2195 gtk_widget_hide ( GTK_WIDGET(vw->toolbar) );
2198 static void view_main_menu_cb ( GtkAction *a, VikWindow *vw )
2200 GtkWidget *check_box = gtk_ui_manager_get_widget ( vw->uim, "/ui/MainMenu/View/SetShow/ViewMainMenu" );
2203 gboolean state = gtk_check_menu_item_get_active ( GTK_CHECK_MENU_ITEM(check_box) );
2205 gtk_widget_hide ( gtk_ui_manager_get_widget ( vw->uim, "/ui/MainMenu" ) );
2207 gtk_widget_show ( gtk_ui_manager_get_widget ( vw->uim, "/ui/MainMenu" ) );
2210 /***************************************
2211 ** tool management routines
2213 ***************************************/
2215 static toolbox_tools_t* toolbox_create(VikWindow *vw)
2217 toolbox_tools_t *vt = g_new(toolbox_tools_t, 1);
2220 vt->active_tool = -1;
2225 static void toolbox_add_tool(toolbox_tools_t *vt, VikToolInterface *vti, gint layer_type )
2227 vt->tools = g_renew(toolbox_tool_t, vt->tools, vt->n_tools+1);
2228 vt->tools[vt->n_tools].ti = *vti;
2229 vt->tools[vt->n_tools].layer_type = layer_type;
2231 vt->tools[vt->n_tools].state = vti->create(vt->vw, vt->vw->viking_vvp);
2234 vt->tools[vt->n_tools].state = NULL;
2239 static int toolbox_get_tool(toolbox_tools_t *vt, const gchar *tool_name)
2242 for (i=0; i<vt->n_tools; i++) {
2243 if (!strcmp(tool_name, vt->tools[i].ti.radioActionEntry.name)) {
2250 static void toolbox_activate(toolbox_tools_t *vt, const gchar *tool_name)
2252 int tool = toolbox_get_tool(vt, tool_name);
2253 toolbox_tool_t *t = &vt->tools[tool];
2254 VikLayer *vl = vik_layers_panel_get_selected ( vt->vw->viking_vlp );
2256 if (tool == vt->n_tools) {
2257 g_critical("trying to activate a non-existent tool...");
2260 /* is the tool already active? */
2261 if (vt->active_tool == tool) {
2265 if (vt->active_tool != -1) {
2266 if (vt->tools[vt->active_tool].ti.deactivate) {
2267 vt->tools[vt->active_tool].ti.deactivate(NULL, vt->tools[vt->active_tool].state);
2270 if (t->ti.activate) {
2271 t->ti.activate(vl, t->state);
2273 vt->active_tool = tool;
2276 static const GdkCursor *toolbox_get_cursor(toolbox_tools_t *vt, const gchar *tool_name)
2278 int tool = toolbox_get_tool(vt, tool_name);
2279 toolbox_tool_t *t = &vt->tools[tool];
2280 if (t->ti.cursor == NULL) {
2281 if (t->ti.cursor_type == GDK_CURSOR_IS_PIXMAP && t->ti.cursor_data != NULL) {
2282 GError *cursor_load_err = NULL;
2283 GdkPixbuf *cursor_pixbuf = gdk_pixbuf_from_pixdata (t->ti.cursor_data, FALSE, &cursor_load_err);
2284 /* TODO: settable offeset */
2285 t->ti.cursor = gdk_cursor_new_from_pixbuf ( gdk_display_get_default(), cursor_pixbuf, 3, 3 );
2286 g_object_unref ( G_OBJECT(cursor_pixbuf) );
2288 t->ti.cursor = gdk_cursor_new ( t->ti.cursor_type );
2291 return t->ti.cursor;
2294 static void toolbox_click (toolbox_tools_t *vt, GdkEventButton *event)
2296 VikLayer *vl = vik_layers_panel_get_selected ( vt->vw->viking_vlp );
2297 if (vt->active_tool != -1 && vt->tools[vt->active_tool].ti.click) {
2298 gint ltype = vt->tools[vt->active_tool].layer_type;
2299 if ( ltype == TOOL_LAYER_TYPE_NONE || (vl && ltype == vl->type) )
2300 vt->tools[vt->active_tool].ti.click(vl, event, vt->tools[vt->active_tool].state);
2304 static void toolbox_move (toolbox_tools_t *vt, GdkEventMotion *event)
2306 VikLayer *vl = vik_layers_panel_get_selected ( vt->vw->viking_vlp );
2307 if (vt->active_tool != -1 && vt->tools[vt->active_tool].ti.move) {
2308 gint ltype = vt->tools[vt->active_tool].layer_type;
2309 if ( ltype == TOOL_LAYER_TYPE_NONE || (vl && ltype == vl->type) )
2310 if ( VIK_LAYER_TOOL_ACK_GRAB_FOCUS == vt->tools[vt->active_tool].ti.move(vl, event, vt->tools[vt->active_tool].state) )
2311 gtk_widget_grab_focus ( GTK_WIDGET(vt->vw->viking_vvp) );
2315 static void toolbox_release (toolbox_tools_t *vt, GdkEventButton *event)
2317 VikLayer *vl = vik_layers_panel_get_selected ( vt->vw->viking_vlp );
2318 if (vt->active_tool != -1 && vt->tools[vt->active_tool].ti.release ) {
2319 gint ltype = vt->tools[vt->active_tool].layer_type;
2320 if ( ltype == TOOL_LAYER_TYPE_NONE || (vl && ltype == vl->type) )
2321 vt->tools[vt->active_tool].ti.release(vl, event, vt->tools[vt->active_tool].state);
2324 /** End tool management ************************************/
2326 void vik_window_enable_layer_tool ( VikWindow *vw, gint layer_id, gint tool_id )
2328 gtk_action_activate ( gtk_action_group_get_action ( vw->action_group, vik_layer_get_interface(layer_id)->tools[tool_id].radioActionEntry.name ) );
2331 /* this function gets called whenever a toolbar tool is clicked */
2332 static void menu_tool_cb ( GtkAction *old, GtkAction *a, VikWindow *vw )
2334 /* White Magic, my friends ... White Magic... */
2336 toolbox_activate(vw->vt, gtk_action_get_name(a));
2338 vw->viewport_cursor = (GdkCursor *)toolbox_get_cursor(vw->vt, gtk_action_get_name(a));
2340 if ( gtk_widget_get_window(GTK_WIDGET(vw->viking_vvp)) )
2341 /* We set cursor, even if it is NULL: it resets to default */
2342 gdk_window_set_cursor ( gtk_widget_get_window(GTK_WIDGET(vw->viking_vvp)), vw->viewport_cursor );
2344 if (!strcmp(gtk_action_get_name(a), "Pan")) {
2345 vw->current_tool = TOOL_PAN;
2347 else if (!strcmp(gtk_action_get_name(a), "Zoom")) {
2348 vw->current_tool = TOOL_ZOOM;
2350 else if (!strcmp(gtk_action_get_name(a), "Ruler")) {
2351 vw->current_tool = TOOL_RULER;
2353 else if (!strcmp(gtk_action_get_name(a), "Select")) {
2354 vw->current_tool = TOOL_SELECT;
2357 /* TODO: only enable tools from active layer */
2358 VikLayerTypeEnum layer_id;
2359 for (layer_id=0; layer_id<VIK_LAYER_NUM_TYPES; layer_id++) {
2360 for ( tool_id = 0; tool_id < vik_layer_get_interface(layer_id)->tools_count; tool_id++ ) {
2361 if (!strcmp(vik_layer_get_interface(layer_id)->tools[tool_id].radioActionEntry.name, gtk_action_get_name(a))) {
2362 vw->current_tool = TOOL_LAYER;
2363 vw->tool_layer_id = layer_id;
2364 vw->tool_tool_id = tool_id;
2369 draw_status_tool ( vw );
2372 static void window_set_filename ( VikWindow *vw, const gchar *filename )
2377 g_free ( vw->filename );
2378 if ( filename == NULL )
2380 vw->filename = NULL;
2384 vw->filename = g_strdup(filename);
2387 /* Refresh window's title */
2388 file = window_get_filename ( vw );
2389 title = g_strdup_printf( "%s - Viking", file );
2390 gtk_window_set_title ( GTK_WINDOW(vw), title );
2394 static const gchar *window_get_filename ( VikWindow *vw )
2396 return vw->filename ? a_file_basename ( vw->filename ) : _("Untitled");
2399 GtkWidget *vik_window_get_drawmode_button ( VikWindow *vw, VikViewportDrawMode mode )
2401 GtkWidget *mode_button;
2404 #ifdef VIK_CONFIG_EXPEDIA
2405 case VIK_VIEWPORT_DRAWMODE_EXPEDIA: buttonname = "/ui/MainMenu/View/ModeExpedia"; break;
2407 case VIK_VIEWPORT_DRAWMODE_MERCATOR: buttonname = "/ui/MainMenu/View/ModeMercator"; break;
2408 case VIK_VIEWPORT_DRAWMODE_LATLON: buttonname = "/ui/MainMenu/View/ModeLatLon"; break;
2409 default: buttonname = "/ui/MainMenu/View/ModeUTM";
2411 mode_button = gtk_ui_manager_get_widget ( vw->uim, buttonname );
2412 g_assert ( mode_button );
2417 * vik_window_get_pan_move:
2418 * @vw: some VikWindow
2420 * Retrieves @vw's pan_move.
2422 * Should be removed as soon as possible.
2424 * Returns: @vw's pan_move
2428 gboolean vik_window_get_pan_move ( VikWindow *vw )
2430 return vw->pan_move;
2433 static void on_activate_recent_item (GtkRecentChooser *chooser,
2438 filename = gtk_recent_chooser_get_current_uri (chooser);
2439 if (filename != NULL)
2441 GFile *file = g_file_new_for_uri ( filename );
2442 gchar *path = g_file_get_path ( file );
2443 g_object_unref ( file );
2444 if ( self->filename )
2446 GSList *filenames = NULL;
2447 filenames = g_slist_append ( filenames, path );
2448 g_signal_emit ( G_OBJECT(self), window_signals[VW_OPENWINDOW_SIGNAL], 0, filenames );
2449 // NB: GSList & contents are freed by main.open_window
2452 vik_window_open_file ( self, path, TRUE );
2460 static void setup_recent_files (VikWindow *self)
2462 GtkRecentManager *manager;
2463 GtkRecentFilter *filter;
2464 GtkWidget *menu, *menu_item;
2466 filter = gtk_recent_filter_new ();
2467 /* gtk_recent_filter_add_application (filter, g_get_application_name()); */
2468 gtk_recent_filter_add_group(filter, "viking");
2470 manager = gtk_recent_manager_get_default ();
2471 menu = gtk_recent_chooser_menu_new_for_manager (manager);
2472 gtk_recent_chooser_set_sort_type (GTK_RECENT_CHOOSER (menu), GTK_RECENT_SORT_MRU);
2473 gtk_recent_chooser_add_filter (GTK_RECENT_CHOOSER (menu), filter);
2475 menu_item = gtk_ui_manager_get_widget (self->uim, "/ui/MainMenu/File/OpenRecentFile");
2476 gtk_menu_item_set_submenu (GTK_MENU_ITEM (menu_item), menu);
2478 g_signal_connect (G_OBJECT (menu), "item-activated",
2479 G_CALLBACK (on_activate_recent_item), (gpointer) self);
2482 static void update_recently_used_document(const gchar *filename)
2484 /* Update Recently Used Document framework */
2485 GtkRecentManager *manager = gtk_recent_manager_get_default();
2486 GtkRecentData *recent_data = g_slice_new (GtkRecentData);
2487 gchar *groups[] = {"viking", NULL};
2488 GFile *file = g_file_new_for_commandline_arg(filename);
2489 gchar *uri = g_file_get_uri(file);
2490 gchar *basename = g_path_get_basename(filename);
2491 g_object_unref(file);
2494 recent_data->display_name = basename;
2495 recent_data->description = NULL;
2496 recent_data->mime_type = "text/x-gps-data";
2497 recent_data->app_name = (gchar *) g_get_application_name ();
2498 recent_data->app_exec = g_strjoin (" ", g_get_prgname (), "%f", NULL);
2499 recent_data->groups = groups;
2500 recent_data->is_private = FALSE;
2501 if (!gtk_recent_manager_add_full (manager, uri, recent_data))
2503 g_warning (_("Unable to add '%s' to the list of recently used documents"), uri);
2508 g_free (recent_data->app_exec);
2509 g_slice_free (GtkRecentData, recent_data);
2513 * Call this before doing things that may take a long time and otherwise not show any other feedback
2514 * such as loading and saving files
2516 void vik_window_set_busy_cursor ( VikWindow *vw )
2518 gdk_window_set_cursor ( gtk_widget_get_window(GTK_WIDGET(vw)), vw->busy_cursor );
2519 // Viewport has a separate cursor
2520 gdk_window_set_cursor ( gtk_widget_get_window(GTK_WIDGET(vw->viking_vvp)), vw->busy_cursor );
2521 // Ensure cursor updated before doing stuff
2522 while( gtk_events_pending() )
2523 gtk_main_iteration();
2526 void vik_window_clear_busy_cursor ( VikWindow *vw )
2528 gdk_window_set_cursor ( gtk_widget_get_window(GTK_WIDGET(vw)), NULL );
2529 // Restore viewport cursor
2530 gdk_window_set_cursor ( gtk_widget_get_window(GTK_WIDGET(vw->viking_vvp)), vw->viewport_cursor );
2533 void vik_window_open_file ( VikWindow *vw, const gchar *filename, gboolean change_filename )
2535 vik_window_set_busy_cursor ( vw );
2536 vw->loaded_type = a_file_load ( vik_layers_panel_get_top_layer(vw->viking_vlp), vw->viking_vvp, filename );
2537 switch ( vw->loaded_type )
2539 case LOAD_TYPE_READ_FAILURE:
2540 a_dialog_error_msg ( GTK_WINDOW(vw), _("The file you requested could not be opened.") );
2542 case LOAD_TYPE_GPSBABEL_FAILURE:
2543 a_dialog_error_msg ( GTK_WINDOW(vw), _("GPSBabel is required to load files of this type or GPSBabel encountered problems.") );
2545 case LOAD_TYPE_GPX_FAILURE:
2546 a_dialog_error_msg_extra ( GTK_WINDOW(vw), _("Unable to load malformed GPX file %s"), filename );
2548 case LOAD_TYPE_UNSUPPORTED_FAILURE:
2549 a_dialog_error_msg_extra ( GTK_WINDOW(vw), _("Unsupported file type for %s"), filename );
2551 case LOAD_TYPE_VIK_FAILURE_NON_FATAL:
2553 // Since we can process .vik files with issues just show a warning in the status bar
2554 // Not that a user can do much about it... or tells them what this issue is yet...
2555 gchar *msg = g_strdup_printf (_("WARNING: issues encountered loading %s"), a_file_basename (filename) );
2556 vik_statusbar_set_message ( vw->viking_vs, VIK_STATUSBAR_INFO, msg );
2559 // No break, carry on to show any data
2560 case LOAD_TYPE_VIK_SUCCESS:
2562 GtkWidget *mode_button;
2564 if ( change_filename )
2565 window_set_filename ( vw, filename );
2566 mode_button = vik_window_get_drawmode_button ( vw, vik_viewport_get_drawmode ( vw->viking_vvp ) );
2567 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. */
2568 gtk_check_menu_item_set_active ( GTK_CHECK_MENU_ITEM(mode_button), TRUE );
2569 vw->only_updating_coord_mode_ui = FALSE;
2571 vik_layers_panel_change_coord_mode ( vw->viking_vlp, vik_viewport_get_coord_mode ( vw->viking_vvp ) );
2573 mode_button = gtk_ui_manager_get_widget ( vw->uim, "/ui/MainMenu/View/SetShow/ShowScale" );
2574 g_assert ( mode_button );
2575 gtk_check_menu_item_set_active ( GTK_CHECK_MENU_ITEM(mode_button),vik_viewport_get_draw_scale(vw->viking_vvp) );
2577 mode_button = gtk_ui_manager_get_widget ( vw->uim, "/ui/MainMenu/View/SetShow/ShowCenterMark" );
2578 g_assert ( mode_button );
2579 gtk_check_menu_item_set_active ( GTK_CHECK_MENU_ITEM(mode_button),vik_viewport_get_draw_centermark(vw->viking_vvp) );
2581 mode_button = gtk_ui_manager_get_widget ( vw->uim, "/ui/MainMenu/View/SetShow/ShowHighlight" );
2582 g_assert ( mode_button );
2583 gtk_check_menu_item_set_active ( GTK_CHECK_MENU_ITEM(mode_button),vik_viewport_get_draw_highlight (vw->viking_vvp) );
2585 //case LOAD_TYPE_OTHER_SUCCESS:
2587 update_recently_used_document(filename);
2592 vik_window_clear_busy_cursor ( vw );
2595 static void load_file ( GtkAction *a, VikWindow *vw )
2597 GSList *files = NULL;
2598 GSList *cur_file = NULL;
2600 if (!strcmp(gtk_action_get_name(a), "Open")) {
2603 else if (!strcmp(gtk_action_get_name(a), "Append")) {
2607 g_critical("Houston, we've had a problem.");
2611 if ( ! vw->open_dia )
2613 vw->open_dia = gtk_file_chooser_dialog_new (_("Please select a GPS data file to open. "),
2615 GTK_FILE_CHOOSER_ACTION_OPEN,
2616 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
2617 GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT,
2619 gchar *cwd = g_get_current_dir();
2621 gtk_file_chooser_set_current_folder ( GTK_FILE_CHOOSER(vw->open_dia), cwd );
2625 GtkFileFilter *filter;
2626 // NB file filters are listed this way for alphabetical ordering
2627 #ifdef VIK_CONFIG_GEOCACHES
2628 filter = gtk_file_filter_new ();
2629 gtk_file_filter_set_name( filter, _("Geocaching") );
2630 gtk_file_filter_add_pattern ( filter, "*.loc" ); // No MIME type available
2631 gtk_file_chooser_add_filter (GTK_FILE_CHOOSER(vw->open_dia), filter);
2634 filter = gtk_file_filter_new ();
2635 gtk_file_filter_set_name( filter, _("Google Earth") );
2636 gtk_file_filter_add_mime_type ( filter, "application/vnd.google-earth.kml+xml");
2637 gtk_file_chooser_add_filter (GTK_FILE_CHOOSER(vw->open_dia), filter);
2639 filter = gtk_file_filter_new ();
2640 gtk_file_filter_set_name( filter, _("GPX") );
2641 gtk_file_filter_add_pattern ( filter, "*.gpx" ); // No MIME type available
2642 gtk_file_chooser_add_filter (GTK_FILE_CHOOSER(vw->open_dia), filter);
2644 filter = gtk_file_filter_new ();
2645 gtk_file_filter_set_name( filter, _("Viking") );
2646 gtk_file_filter_add_pattern ( filter, "*.vik" );
2647 gtk_file_filter_add_pattern ( filter, "*.viking" );
2648 gtk_file_chooser_add_filter (GTK_FILE_CHOOSER(vw->open_dia), filter);
2650 // NB could have filters for gpspoint (*.gps,*.gpsoint?) + gpsmapper (*.gsm,*.gpsmapper?)
2651 // However assume this are barely used and thus not worthy of inclusion
2652 // as they'll just make the options too many and have no clear file pattern
2653 // one can always use the all option
2654 filter = gtk_file_filter_new ();
2655 gtk_file_filter_set_name( filter, _("All") );
2656 gtk_file_filter_add_pattern ( filter, "*" );
2657 gtk_file_chooser_add_filter (GTK_FILE_CHOOSER(vw->open_dia), filter);
2658 // Default to any file - same as before open filters were added
2659 gtk_file_chooser_set_filter (GTK_FILE_CHOOSER(vw->open_dia), filter);
2661 gtk_file_chooser_set_select_multiple ( GTK_FILE_CHOOSER(vw->open_dia), TRUE );
2662 gtk_window_set_transient_for ( GTK_WINDOW(vw->open_dia), GTK_WINDOW(vw) );
2663 gtk_window_set_destroy_with_parent ( GTK_WINDOW(vw->open_dia), TRUE );
2665 if ( gtk_dialog_run ( GTK_DIALOG(vw->open_dia) ) == GTK_RESPONSE_ACCEPT )
2667 gtk_widget_hide ( vw->open_dia );
2668 #ifdef VIKING_PROMPT_IF_MODIFIED
2669 if ( (vw->modified || vw->filename) && newwindow )
2671 if ( vw->filename && newwindow )
2673 g_signal_emit ( G_OBJECT(vw), window_signals[VW_OPENWINDOW_SIGNAL], 0, gtk_file_chooser_get_filenames (GTK_FILE_CHOOSER(vw->open_dia) ) );
2675 files = gtk_file_chooser_get_filenames (GTK_FILE_CHOOSER(vw->open_dia) );
2676 gboolean change_fn = newwindow && (g_slist_length(files)==1); /* only change fn if one file */
2677 gboolean first_vik_file = TRUE;
2679 while ( cur_file ) {
2681 gchar *file_name = cur_file->data;
2682 if ( newwindow && check_file_magic_vik ( file_name ) ) {
2683 // Load first of many .vik files in current window
2684 if ( first_vik_file ) {
2685 vik_window_open_file ( vw, file_name, TRUE );
2686 first_vik_file = FALSE;
2689 // Load each subsequent .vik file in a separate window
2690 VikWindow *newvw = vik_window_new_window ();
2692 vik_window_open_file ( newvw, file_name, TRUE );
2697 vik_window_open_file ( vw, file_name, change_fn );
2700 cur_file = g_slist_next (cur_file);
2702 g_slist_free (files);
2706 gtk_widget_hide ( vw->open_dia );
2709 static gboolean save_file_as ( GtkAction *a, VikWindow *vw )
2711 gboolean rv = FALSE;
2713 if ( ! vw->save_dia )
2715 vw->save_dia = gtk_file_chooser_dialog_new (_("Save as Viking File."),
2717 GTK_FILE_CHOOSER_ACTION_SAVE,
2718 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
2719 GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT,
2721 gchar *cwd = g_get_current_dir();
2723 gtk_file_chooser_set_current_folder ( GTK_FILE_CHOOSER(vw->save_dia), cwd );
2727 GtkFileFilter *filter;
2728 filter = gtk_file_filter_new ();
2729 gtk_file_filter_set_name( filter, _("All") );
2730 gtk_file_filter_add_pattern ( filter, "*" );
2731 gtk_file_chooser_add_filter (GTK_FILE_CHOOSER(vw->save_dia), filter);
2733 filter = gtk_file_filter_new ();
2734 gtk_file_filter_set_name( filter, _("Viking") );
2735 gtk_file_filter_add_pattern ( filter, "*.vik" );
2736 gtk_file_filter_add_pattern ( filter, "*.viking" );
2737 gtk_file_chooser_add_filter (GTK_FILE_CHOOSER(vw->save_dia), filter);
2738 // Default to a Viking file
2739 gtk_file_chooser_set_filter (GTK_FILE_CHOOSER(vw->save_dia), filter);
2741 gtk_window_set_transient_for ( GTK_WINDOW(vw->save_dia), GTK_WINDOW(vw) );
2742 gtk_window_set_destroy_with_parent ( GTK_WINDOW(vw->save_dia), TRUE );
2744 // Auto append / replace extension with '.vik' to the suggested file name as it's going to be a Viking File
2745 gchar* auto_save_name = g_strdup ( window_get_filename ( vw ) );
2746 if ( ! check_file_ext ( auto_save_name, ".vik" ) )
2747 auto_save_name = g_strconcat ( auto_save_name, ".vik", NULL );
2749 gtk_file_chooser_set_current_name (GTK_FILE_CHOOSER(vw->save_dia), auto_save_name);
2751 while ( gtk_dialog_run ( GTK_DIALOG(vw->save_dia) ) == GTK_RESPONSE_ACCEPT )
2753 fn = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER(vw->save_dia) );
2754 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 ) ) )
2756 window_set_filename ( vw, fn );
2757 rv = window_save ( vw );
2758 vw->modified = FALSE;
2762 g_free ( auto_save_name );
2763 gtk_widget_hide ( vw->save_dia );
2767 static gboolean window_save ( VikWindow *vw )
2769 vik_window_set_busy_cursor ( vw );
2770 gboolean success = TRUE;
2772 if ( a_file_save ( vik_layers_panel_get_top_layer ( vw->viking_vlp ), vw->viking_vvp, vw->filename ) )
2774 update_recently_used_document ( vw->filename );
2778 a_dialog_error_msg ( GTK_WINDOW(vw), _("The filename you requested could not be opened for writing.") );
2781 vik_window_clear_busy_cursor ( vw );
2785 static gboolean save_file ( GtkAction *a, VikWindow *vw )
2787 if ( ! vw->filename )
2788 return save_file_as ( NULL, vw );
2791 vw->modified = FALSE;
2792 return window_save ( vw );
2799 * Export all TRW Layers in the list to individual files in the specified directory
2801 * Returns: %TRUE on success
2803 static gboolean export_to ( VikWindow *vw, GList *gl, VikFileType_t vft, const gchar *dir, const gchar *extension )
2805 gboolean success = TRUE;
2807 gint export_count = 0;
2809 vik_window_set_busy_cursor ( vw );
2813 gchar *fn = g_strconcat ( dir, G_DIR_SEPARATOR_S, VIK_LAYER(gl->data)->name, extension, NULL );
2815 // Some protection in attempting to write too many same named files
2816 // As this will get horribly slow...
2817 gboolean safe = FALSE;
2819 while ( ii < 5000 ) {
2820 if ( g_file_test ( fn, G_FILE_TEST_EXISTS ) ) {
2823 fn = g_strdup_printf ( "%s%s%s#%03d%s", dir, G_DIR_SEPARATOR_S, VIK_LAYER(gl->data)->name, ii, extension );
2834 // NB: We allow exporting empty layers
2836 gboolean this_success = a_file_export ( VIK_TRW_LAYER(gl->data), fn, vft, NULL, TRUE );
2838 // Show some progress
2839 if ( this_success ) {
2841 gchar *message = g_strconcat ( _("Exporting to file: "), fn, NULL );
2842 vik_statusbar_set_message ( vw->viking_vs, VIK_STATUSBAR_INFO, message );
2843 while ( gtk_events_pending() )
2844 gtk_main_iteration ();
2848 success = success && this_success;
2852 gl = g_list_next ( gl );
2855 vik_window_clear_busy_cursor ( vw );
2857 // Confirm what happened.
2858 gchar *message = g_strdup_printf ( _("Exported files: %d"), export_count );
2859 vik_statusbar_set_message ( vw->viking_vs, VIK_STATUSBAR_INFO, message );
2865 static void export_to_common ( VikWindow *vw, VikFileType_t vft, const gchar *extension )
2867 GList *gl = vik_layers_panel_get_all_layers_of_type ( vw->viking_vlp, VIK_LAYER_TRW, TRUE );
2870 a_dialog_info_msg ( GTK_WINDOW(vw), _("Nothing to Export!") );
2874 GtkWidget *dialog = gtk_dialog_new_with_buttons ( _("Export to directory"),
2876 GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
2878 GTK_RESPONSE_REJECT,
2880 GTK_RESPONSE_ACCEPT,
2883 GtkWidget *gw = gtk_file_chooser_widget_new ( GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER );
2884 gtk_box_pack_start ( GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), gw, TRUE, TRUE, 0 );
2886 // try to make it a nice size - otherwise seems to default to something impractically small
2887 gtk_window_set_default_size ( GTK_WINDOW(dialog), 600, 300 );
2889 gtk_widget_show_all ( dialog );
2891 if ( gtk_dialog_run ( GTK_DIALOG(dialog) ) == GTK_RESPONSE_ACCEPT ) {
2892 gchar *dir = gtk_file_chooser_get_filename ( GTK_FILE_CHOOSER(gw) );
2893 gtk_widget_destroy ( dialog );
2895 if ( !export_to ( vw, gl, vft, dir, extension ) )
2896 a_dialog_error_msg ( GTK_WINDOW(vw),_("Could not convert all files") );
2901 gtk_widget_destroy ( dialog );
2906 static void export_to_gpx ( GtkAction *a, VikWindow *vw )
2908 export_to_common ( vw, FILE_TYPE_GPX, ".gpx" );
2911 static void export_to_kml ( GtkAction *a, VikWindow *vw )
2913 export_to_common ( vw, FILE_TYPE_KML, ".kml" );
2916 static void file_properties_cb ( GtkAction *a, VikWindow *vw )
2918 gchar *message = NULL;
2919 if ( vw->filename ) {
2920 if ( g_file_test ( vw->filename, G_FILE_TEST_EXISTS ) ) {
2921 // Get some timestamp information of the file
2923 if ( g_stat ( vw->filename, &stat_buf ) == 0 ) {
2925 strftime ( time_buf, sizeof(time_buf), "%c", gmtime((const time_t *)&stat_buf.st_mtime) );
2927 gint byte_size = stat_buf.st_size;
2928 // See http://en.wikipedia.org/wiki/Megabyte (and Kilobyte)
2929 // hence using 1000 rather than 1024
2930 // so get output as per 'ls' or the Gtk file open dialog
2931 if ( byte_size < 1000 )
2932 size = g_strdup_printf ( _("%d bytes"), byte_size );
2933 else if ( byte_size < 1000*1000 )
2934 size = g_strdup_printf ( _("%3.1f kB"), (gdouble)byte_size / 1000 );
2936 size = g_strdup_printf ( _("%3.1f MB"), (gdouble)byte_size / (1000*1000) );
2937 message = g_strdup_printf ( _("%s\n\n%s\n\n%s"), vw->filename, time_buf, size );
2942 message = g_strdup ( _("File not accessible") );
2945 message = g_strdup ( _("No Viking File") );
2948 a_dialog_info_msg ( GTK_WINDOW(vw), message );
2952 static void acquire_from_gps ( GtkAction *a, VikWindow *vw )
2954 // Via the file menu, acquiring from a GPS makes a new layer
2955 // this has always been the way (not entirely sure if this was the real intention!)
2956 // thus maintain the behaviour ATM.
2957 // Hence explicit setting here (as the value may be changed elsewhere)
2958 vik_datasource_gps_interface.mode = VIK_DATASOURCE_CREATENEWLAYER;
2959 a_acquire(vw, vw->viking_vlp, vw->viking_vvp, &vik_datasource_gps_interface, NULL, NULL );
2962 static void acquire_from_file ( GtkAction *a, VikWindow *vw )
2964 a_acquire(vw, vw->viking_vlp, vw->viking_vvp, &vik_datasource_file_interface, NULL, NULL );
2967 static void acquire_from_routing ( GtkAction *a, VikWindow *vw )
2969 a_acquire(vw, vw->viking_vlp, vw->viking_vvp, &vik_datasource_routing_interface, NULL, NULL );
2972 #ifdef VIK_CONFIG_OPENSTREETMAP
2973 static void acquire_from_osm ( GtkAction *a, VikWindow *vw )
2975 a_acquire(vw, vw->viking_vlp, vw->viking_vvp, &vik_datasource_osm_interface, NULL, NULL );
2978 static void acquire_from_my_osm ( GtkAction *a, VikWindow *vw )
2980 a_acquire(vw, vw->viking_vlp, vw->viking_vvp, &vik_datasource_osm_my_traces_interface, NULL, NULL );
2984 #ifdef VIK_CONFIG_GEOCACHES
2985 static void acquire_from_gc ( GtkAction *a, VikWindow *vw )
2987 a_acquire(vw, vw->viking_vlp, vw->viking_vvp, &vik_datasource_gc_interface, NULL, NULL );
2991 #ifdef VIK_CONFIG_GEOTAG
2992 static void acquire_from_geotag ( GtkAction *a, VikWindow *vw )
2994 vik_datasource_geotag_interface.mode = VIK_DATASOURCE_CREATENEWLAYER;
2995 a_acquire(vw, vw->viking_vlp, vw->viking_vvp, &vik_datasource_geotag_interface, NULL, NULL );
2999 #ifdef VIK_CONFIG_GEONAMES
3000 static void acquire_from_wikipedia ( GtkAction *a, VikWindow *vw )
3002 a_acquire(vw, vw->viking_vlp, vw->viking_vvp, &vik_datasource_wikipedia_interface, NULL, NULL );
3006 static void goto_default_location( GtkAction *a, VikWindow *vw)
3009 ll.lat = a_vik_get_default_lat();
3010 ll.lon = a_vik_get_default_long();
3011 vik_viewport_set_center_latlon(vw->viking_vvp, &ll);
3012 vik_layers_panel_emit_update(vw->viking_vlp);
3016 static void goto_address( GtkAction *a, VikWindow *vw)
3018 a_vik_goto ( vw, vw->viking_vvp );
3019 vik_layers_panel_emit_update ( vw->viking_vlp );
3022 static void mapcache_flush_cb ( GtkAction *a, VikWindow *vw )
3027 static void layer_defaults_cb ( GtkAction *a, VikWindow *vw )
3029 gchar **texts = g_strsplit ( gtk_action_get_name(a), "Layer", 0 );
3032 return; // Internally broken :(
3034 if ( ! a_layer_defaults_show_window ( GTK_WINDOW(vw), texts[1] ) )
3035 a_dialog_info_msg ( GTK_WINDOW(vw), _("This layer has no configurable properties.") );
3036 // NB no update needed
3038 g_strfreev ( texts );
3041 static void preferences_change_update ( VikWindow *vw, gpointer data )
3043 // Want to update all TrackWaypoint layers
3044 GList *layers = vik_layers_panel_get_all_layers_of_type ( vw->viking_vlp, VIK_LAYER_TRW, TRUE );
3050 // Reset the individual waypoints themselves due to the preferences change
3051 VikTrwLayer *vtl = VIK_TRW_LAYER(layers->data);
3052 vik_trw_layer_reset_waypoints ( vtl );
3053 layers = g_list_next ( layers );
3056 g_list_free ( layers );
3061 static void preferences_cb ( GtkAction *a, VikWindow *vw )
3063 gboolean wp_icon_size = a_vik_get_use_large_waypoint_icons();
3065 a_preferences_show_window ( GTK_WINDOW(vw) );
3067 // Has the waypoint size setting changed?
3068 if (wp_icon_size != a_vik_get_use_large_waypoint_icons()) {
3069 // Delete icon indexing 'cache' and so automatically regenerates with the new setting when changed
3070 clear_garmin_icon_syms ();
3072 // Update all windows
3073 g_slist_foreach ( window_list, (GFunc) preferences_change_update, NULL );
3077 static void default_location_cb ( GtkAction *a, VikWindow *vw )
3079 /* Simplistic repeat of preference setting
3080 Only the name & type are important for setting the preference via this 'external' way */
3081 VikLayerParam pref_lat[] = {
3082 { VIK_LAYER_NUM_TYPES,
3083 VIKING_PREFERENCES_NAMESPACE "default_latitude",
3084 VIK_LAYER_PARAM_DOUBLE,
3087 VIK_LAYER_WIDGET_SPINBUTTON,
3095 VikLayerParam pref_lon[] = {
3096 { VIK_LAYER_NUM_TYPES,
3097 VIKING_PREFERENCES_NAMESPACE "default_longitude",
3098 VIK_LAYER_PARAM_DOUBLE,
3101 VIK_LAYER_WIDGET_SPINBUTTON,
3110 /* Get current center */
3112 vik_coord_to_latlon ( vik_viewport_get_center ( vw->viking_vvp ), &ll );
3114 /* Apply to preferences */
3115 VikLayerParamData vlp_data;
3116 vlp_data.d = ll.lat;
3117 a_preferences_run_setparam (vlp_data, pref_lat);
3118 vlp_data.d = ll.lon;
3119 a_preferences_run_setparam (vlp_data, pref_lon);
3120 /* Remember to save */
3121 a_preferences_save_to_file();
3124 static void clear_cb ( GtkAction *a, VikWindow *vw )
3126 vik_layers_panel_clear ( vw->viking_vlp );
3127 window_set_filename ( vw, NULL );
3131 static void window_close ( GtkAction *a, VikWindow *vw )
3133 if ( ! delete_event ( vw ) )
3134 gtk_widget_destroy ( GTK_WIDGET(vw) );
3137 static gboolean save_file_and_exit ( GtkAction *a, VikWindow *vw )
3139 if (save_file( NULL, vw)) {
3140 window_close( NULL, vw);
3147 static void zoom_to_cb ( GtkAction *a, VikWindow *vw )
3149 gdouble xmpp = vik_viewport_get_xmpp ( vw->viking_vvp ), ympp = vik_viewport_get_ympp ( vw->viking_vvp );
3150 if ( a_dialog_custom_zoom ( GTK_WINDOW(vw), &xmpp, &ympp ) )
3152 vik_viewport_set_xmpp ( vw->viking_vvp, xmpp );
3153 vik_viewport_set_ympp ( vw->viking_vvp, ympp );
3158 static void save_image_file ( VikWindow *vw, const gchar *fn, guint w, guint h, gdouble zoom, gboolean save_as_png )
3160 /* more efficient way: stuff draws directly to pixbuf (fork viewport) */
3161 GdkPixbuf *pixbuf_to_save;
3162 gdouble old_xmpp, old_ympp;
3163 GError *error = NULL;
3165 GtkWidget *msgbox = gtk_message_dialog_new ( GTK_WINDOW(vw),
3166 GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
3169 _("Generating image file...") );
3171 g_signal_connect_swapped (msgbox, "response", G_CALLBACK (gtk_widget_destroy), msgbox);
3172 // Ensure dialog shown
3173 gtk_widget_show_all ( msgbox );
3175 vik_statusbar_set_message ( vw->viking_vs, VIK_STATUSBAR_INFO, _("Generating image file...") );
3176 while ( gtk_events_pending() )
3177 gtk_main_iteration ();
3178 // Despite many efforts & variations, GTK on my Linux system doesn't show the actual msgbox contents :(
3179 // At least the empty box can give a clue something's going on + the statusbar msg...
3180 // Windows version under Wine OK!
3182 /* backup old zoom & set new */
3183 old_xmpp = vik_viewport_get_xmpp ( vw->viking_vvp );
3184 old_ympp = vik_viewport_get_ympp ( vw->viking_vvp );
3185 vik_viewport_set_zoom ( vw->viking_vvp, zoom );
3187 /* reset width and height: */
3188 vik_viewport_configure_manually ( vw->viking_vvp, w, h );
3190 /* draw all layers */
3193 /* save buffer as file. */
3194 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);
3195 if ( !pixbuf_to_save ) {
3196 g_warning("Failed to generate internal pixmap size: %d x %d", w, h);
3197 gtk_message_dialog_set_markup ( GTK_MESSAGE_DIALOG(msgbox), _("Failed to generate internal image.\n\nTry creating a smaller image.") );
3201 gdk_pixbuf_save ( pixbuf_to_save, fn, save_as_png ? "png" : "jpeg", &error, NULL );
3204 g_warning("Unable to write to file %s: %s", fn, error->message );
3205 gtk_message_dialog_set_markup ( GTK_MESSAGE_DIALOG(msgbox), _("Failed to generate image file.") );
3206 g_error_free (error);
3210 gtk_message_dialog_set_markup ( GTK_MESSAGE_DIALOG(msgbox), _("Image file generated.") );
3212 g_object_unref ( G_OBJECT(pixbuf_to_save) );
3215 vik_statusbar_set_message ( vw->viking_vs, VIK_STATUSBAR_INFO, "" );
3216 gtk_dialog_add_button ( GTK_DIALOG(msgbox), GTK_STOCK_OK, GTK_RESPONSE_OK );
3217 gtk_dialog_run ( GTK_DIALOG(msgbox) ); // Don't care about the result
3219 /* pretend like nothing happened ;) */
3220 vik_viewport_set_xmpp ( vw->viking_vvp, old_xmpp );
3221 vik_viewport_set_ympp ( vw->viking_vvp, old_ympp );
3222 vik_viewport_configure ( vw->viking_vvp );
3226 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 )
3228 gulong size = sizeof(gchar) * (strlen(fn) + 15);
3229 gchar *name_of_file = g_malloc ( size );
3231 struct UTM utm_orig, utm;
3233 /* *** copied from above *** */
3234 GdkPixbuf *pixbuf_to_save;
3235 gdouble old_xmpp, old_ympp;
3236 GError *error = NULL;
3238 /* backup old zoom & set new */
3239 old_xmpp = vik_viewport_get_xmpp ( vw->viking_vvp );
3240 old_ympp = vik_viewport_get_ympp ( vw->viking_vvp );
3241 vik_viewport_set_zoom ( vw->viking_vvp, zoom );
3243 /* reset width and height: do this only once for all images (same size) */
3244 vik_viewport_configure_manually ( vw->viking_vvp, w, h );
3245 /* *** end copy from above *** */
3247 g_assert ( vik_viewport_get_coord_mode ( vw->viking_vvp ) == VIK_COORD_UTM );
3251 utm_orig = *((const struct UTM *)vik_viewport_get_center ( vw->viking_vvp ));
3253 for ( y = 1; y <= tiles_h; y++ )
3255 for ( x = 1; x <= tiles_w; x++ )
3257 g_snprintf ( name_of_file, size, "%s%cy%d-x%d.%s", fn, G_DIR_SEPARATOR, y, x, save_as_png ? "png" : "jpg" );
3259 if ( tiles_w & 0x1 )
3260 utm.easting += ((gdouble)x - ceil(((gdouble)tiles_w)/2)) * (w*zoom);
3262 utm.easting += ((gdouble)x - (((gdouble)tiles_w)+1)/2) * (w*zoom);
3263 if ( tiles_h & 0x1 ) /* odd */
3264 utm.northing -= ((gdouble)y - ceil(((gdouble)tiles_h)/2)) * (h*zoom);
3266 utm.northing -= ((gdouble)y - (((gdouble)tiles_h)+1)/2) * (h*zoom);
3268 /* move to correct place. */
3269 vik_viewport_set_center_utm ( vw->viking_vvp, &utm );
3273 /* save buffer as file. */
3274 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);
3275 gdk_pixbuf_save ( pixbuf_to_save, name_of_file, save_as_png ? "png" : "jpeg", &error, NULL );
3278 g_warning("Unable to write to file %s: %s", name_of_file, error->message );
3279 g_error_free (error);
3282 g_object_unref ( G_OBJECT(pixbuf_to_save) );
3286 vik_viewport_set_center_utm ( vw->viking_vvp, &utm_orig );
3287 vik_viewport_set_xmpp ( vw->viking_vvp, old_xmpp );
3288 vik_viewport_set_ympp ( vw->viking_vvp, old_ympp );
3289 vik_viewport_configure ( vw->viking_vvp );
3292 g_free ( name_of_file );
3295 static void draw_to_image_file_current_window_cb(GtkWidget* widget,GdkEventButton *event,gpointer *pass_along)
3297 VikWindow *vw = VIK_WINDOW(pass_along[0]);
3298 GtkSpinButton *width_spin = GTK_SPIN_BUTTON(pass_along[1]), *height_spin = GTK_SPIN_BUTTON(pass_along[2]);
3300 gint active = gtk_combo_box_get_active ( GTK_COMBO_BOX(pass_along[3]) );
3301 gdouble zoom = pow (2, active-2 );
3303 gdouble width_min, width_max, height_min, height_max;
3306 gtk_spin_button_get_range ( width_spin, &width_min, &width_max );
3307 gtk_spin_button_get_range ( height_spin, &height_min, &height_max );
3309 /* TODO: support for xzoom and yzoom values */
3310 width = vik_viewport_get_width ( vw->viking_vvp ) * vik_viewport_get_xmpp ( vw->viking_vvp ) / zoom;
3311 height = vik_viewport_get_height ( vw->viking_vvp ) * vik_viewport_get_xmpp ( vw->viking_vvp ) / zoom;
3313 if ( width > width_max || width < width_min || height > height_max || height < height_min )
3314 a_dialog_info_msg ( GTK_WINDOW(vw), _("Viewable region outside allowable pixel size bounds for image. Clipping width/height values.") );
3316 gtk_spin_button_set_value ( width_spin, width );
3317 gtk_spin_button_set_value ( height_spin, height );
3320 static void draw_to_image_file_total_area_cb (GtkSpinButton *spinbutton, gpointer *pass_along)
3322 GtkSpinButton *width_spin = GTK_SPIN_BUTTON(pass_along[1]), *height_spin = GTK_SPIN_BUTTON(pass_along[2]);
3324 gint active = gtk_combo_box_get_active ( GTK_COMBO_BOX(pass_along[3]) );
3325 gdouble zoom = pow (2, active-2 );
3329 w = gtk_spin_button_get_value(width_spin) * zoom;
3330 h = gtk_spin_button_get_value(height_spin) * zoom;
3331 if (pass_along[4]) /* save many images; find TOTAL area covered */
3333 w *= gtk_spin_button_get_value(GTK_SPIN_BUTTON(pass_along[4]));
3334 h *= gtk_spin_button_get_value(GTK_SPIN_BUTTON(pass_along[5]));
3336 vik_units_distance_t dist_units = a_vik_get_units_distance ();
3337 switch (dist_units) {
3338 case VIK_UNITS_DISTANCE_KILOMETRES:
3339 label_text = g_strdup_printf ( _("Total area: %ldm x %ldm (%.3f sq. km)"), (glong)w, (glong)h, (w*h/1000000));
3341 case VIK_UNITS_DISTANCE_MILES:
3342 label_text = g_strdup_printf ( _("Total area: %ldm x %ldm (%.3f sq. miles)"), (glong)w, (glong)h, (w*h/2589988.11));
3345 label_text = g_strdup_printf ("Just to keep the compiler happy");
3346 g_critical("Houston, we've had a problem. distance=%d", dist_units);
3349 gtk_label_set_text(GTK_LABEL(pass_along[6]), label_text);
3350 g_free ( label_text );
3354 * Get an allocated filename (or directory as specified)
3356 static gchar* draw_image_filename ( VikWindow *vw, gboolean one_image_only )
3359 if ( one_image_only )
3362 if (!vw->save_img_dia) {
3363 vw->save_img_dia = gtk_file_chooser_dialog_new (_("Save Image"),
3365 GTK_FILE_CHOOSER_ACTION_SAVE,
3366 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
3367 GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT,
3370 gchar *cwd = g_get_current_dir();
3372 gtk_file_chooser_set_current_folder ( GTK_FILE_CHOOSER(vw->save_img_dia), cwd );
3376 GtkFileChooser *chooser = GTK_FILE_CHOOSER ( vw->save_img_dia );
3378 GtkFileFilter *filter;
3379 filter = gtk_file_filter_new ();
3380 gtk_file_filter_set_name ( filter, _("All") );
3381 gtk_file_filter_add_pattern ( filter, "*" );
3382 gtk_file_chooser_add_filter ( chooser, filter );
3384 filter = gtk_file_filter_new ();
3385 gtk_file_filter_set_name ( filter, _("JPG") );
3386 gtk_file_filter_add_mime_type ( filter, "image/jpeg");
3387 gtk_file_chooser_add_filter ( chooser, filter );
3389 if ( !vw->draw_image_save_as_png )
3390 gtk_file_chooser_set_filter ( chooser, filter );
3392 filter = gtk_file_filter_new ();
3393 gtk_file_filter_set_name ( filter, _("PNG") );
3394 gtk_file_filter_add_mime_type ( filter, "image/png");
3395 gtk_file_chooser_add_filter ( chooser, filter );
3397 if ( vw->draw_image_save_as_png )
3398 gtk_file_chooser_set_filter ( chooser, filter );
3400 gtk_window_set_transient_for ( GTK_WINDOW(vw->save_img_dia), GTK_WINDOW(vw) );
3401 gtk_window_set_destroy_with_parent ( GTK_WINDOW(vw->save_img_dia), TRUE );
3404 if ( gtk_dialog_run ( GTK_DIALOG(vw->save_img_dia) ) == GTK_RESPONSE_ACCEPT ) {
3405 fn = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER(vw->save_img_dia) );
3406 if ( g_file_test ( fn, G_FILE_TEST_EXISTS ) )
3407 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 ) ) )
3410 gtk_widget_hide ( vw->save_img_dia );
3414 // For some reason this method is only written to work in UTM...
3415 if ( vik_viewport_get_coord_mode(vw->viking_vvp) != VIK_COORD_UTM ) {
3416 a_dialog_error_msg ( GTK_WINDOW(vw), _("You must be in UTM mode to use this feature") );
3420 if (!vw->save_img_dir_dia) {
3421 vw->save_img_dir_dia = gtk_file_chooser_dialog_new (_("Choose a directory to hold images"),
3423 GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER,
3424 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
3425 GTK_STOCK_OK, GTK_RESPONSE_ACCEPT,
3427 gtk_window_set_transient_for ( GTK_WINDOW(vw->save_img_dir_dia), GTK_WINDOW(vw) );
3428 gtk_window_set_destroy_with_parent ( GTK_WINDOW(vw->save_img_dir_dia), TRUE );
3431 if ( gtk_dialog_run ( GTK_DIALOG(vw->save_img_dir_dia) ) == GTK_RESPONSE_ACCEPT ) {
3432 fn = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER(vw->save_img_dir_dia) );
3434 gtk_widget_hide ( vw->save_img_dir_dia );
3439 static void draw_to_image_file ( VikWindow *vw, gboolean one_image_only )
3441 /* todo: default for answers inside VikWindow or static (thruout instance) */
3442 GtkWidget *dialog = gtk_dialog_new_with_buttons ( _("Save to Image File"), GTK_WINDOW(vw),
3443 GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
3445 GTK_RESPONSE_REJECT,
3447 GTK_RESPONSE_ACCEPT,
3449 GtkWidget *width_label, *width_spin, *height_label, *height_spin;
3450 GtkWidget *png_radio, *jpeg_radio;
3451 GtkWidget *current_window_button;
3452 gpointer current_window_pass_along[7];
3453 GtkWidget *zoom_label, *zoom_combo;
3454 GtkWidget *total_size_label;
3456 /* only used if (!one_image_only) */
3457 GtkWidget *tiles_width_spin = NULL, *tiles_height_spin = NULL;
3459 width_label = gtk_label_new ( _("Width (pixels):") );
3460 width_spin = gtk_spin_button_new ( GTK_ADJUSTMENT(gtk_adjustment_new ( vw->draw_image_width, 10, 50000, 10, 100, 0 )), 10, 0 );
3461 height_label = gtk_label_new ( _("Height (pixels):") );
3462 height_spin = gtk_spin_button_new ( GTK_ADJUSTMENT(gtk_adjustment_new ( vw->draw_image_height, 10, 50000, 10, 100, 0 )), 10, 0 );
3464 GtkWidget *win_warning_label = gtk_label_new ( _("WARNING: USING LARGE IMAGES OVER 10000x10000\nMAY CRASH THE PROGRAM!") );
3466 zoom_label = gtk_label_new ( _("Zoom (meters per pixel):") );
3467 /* TODO: separate xzoom and yzoom factors */
3468 zoom_combo = create_zoom_combo_all_levels();
3470 gdouble mpp = vik_viewport_get_xmpp(vw->viking_vvp);
3471 gint active = 2 + round ( log (mpp) / log (2) );
3473 // Can we not hard code size here?
3478 gtk_combo_box_set_active ( GTK_COMBO_BOX(zoom_combo), active );
3480 total_size_label = gtk_label_new ( NULL );
3482 current_window_button = gtk_button_new_with_label ( _("Area in current viewable window") );
3483 current_window_pass_along [0] = vw;
3484 current_window_pass_along [1] = width_spin;
3485 current_window_pass_along [2] = height_spin;
3486 current_window_pass_along [3] = zoom_combo;
3487 current_window_pass_along [4] = NULL; /* used for one_image_only != 1 */
3488 current_window_pass_along [5] = NULL;
3489 current_window_pass_along [6] = total_size_label;
3490 g_signal_connect ( G_OBJECT(current_window_button), "button_press_event", G_CALLBACK(draw_to_image_file_current_window_cb), current_window_pass_along );
3492 png_radio = gtk_radio_button_new_with_label ( NULL, _("Save as PNG") );
3493 jpeg_radio = gtk_radio_button_new_with_label_from_widget ( GTK_RADIO_BUTTON(png_radio), _("Save as JPEG") );
3495 if ( ! vw->draw_image_save_as_png )
3496 gtk_toggle_button_set_active ( GTK_TOGGLE_BUTTON(jpeg_radio), TRUE );
3498 gtk_box_pack_start (GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), width_label, FALSE, FALSE, 0);
3499 gtk_box_pack_start (GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), width_spin, FALSE, FALSE, 0);
3500 gtk_box_pack_start (GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), height_label, FALSE, FALSE, 0);
3501 gtk_box_pack_start (GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), height_spin, FALSE, FALSE, 0);
3503 gtk_box_pack_start (GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), win_warning_label, FALSE, FALSE, 0);
3505 gtk_box_pack_start (GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), current_window_button, FALSE, FALSE, 0);
3506 gtk_box_pack_start (GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), png_radio, FALSE, FALSE, 0);
3507 gtk_box_pack_start (GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), jpeg_radio, FALSE, FALSE, 0);
3508 gtk_box_pack_start (GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), zoom_label, FALSE, FALSE, 0);
3509 gtk_box_pack_start (GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), zoom_combo, FALSE, FALSE, 0);
3511 if ( ! one_image_only )
3513 GtkWidget *tiles_width_label, *tiles_height_label;
3515 tiles_width_label = gtk_label_new ( _("East-west image tiles:") );
3516 tiles_width_spin = gtk_spin_button_new ( GTK_ADJUSTMENT(gtk_adjustment_new ( 5, 1, 10, 1, 100, 0 )), 1, 0 );
3517 tiles_height_label = gtk_label_new ( _("North-south image tiles:") );
3518 tiles_height_spin = gtk_spin_button_new ( GTK_ADJUSTMENT(gtk_adjustment_new ( 5, 1, 10, 1, 100, 0 )), 1, 0 );
3519 gtk_box_pack_start (GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), tiles_width_label, FALSE, FALSE, 0);
3520 gtk_box_pack_start (GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), tiles_width_spin, FALSE, FALSE, 0);
3521 gtk_box_pack_start (GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), tiles_height_label, FALSE, FALSE, 0);
3522 gtk_box_pack_start (GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), tiles_height_spin, FALSE, FALSE, 0);
3524 current_window_pass_along [4] = tiles_width_spin;
3525 current_window_pass_along [5] = tiles_height_spin;
3526 g_signal_connect ( G_OBJECT(tiles_width_spin), "value-changed", G_CALLBACK(draw_to_image_file_total_area_cb), current_window_pass_along );
3527 g_signal_connect ( G_OBJECT(tiles_height_spin), "value-changed", G_CALLBACK(draw_to_image_file_total_area_cb), current_window_pass_along );
3529 gtk_box_pack_start (GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), total_size_label, FALSE, FALSE, 0);
3530 g_signal_connect ( G_OBJECT(width_spin), "value-changed", G_CALLBACK(draw_to_image_file_total_area_cb), current_window_pass_along );
3531 g_signal_connect ( G_OBJECT(height_spin), "value-changed", G_CALLBACK(draw_to_image_file_total_area_cb), current_window_pass_along );
3532 g_signal_connect ( G_OBJECT(zoom_combo), "changed", G_CALLBACK(draw_to_image_file_total_area_cb), current_window_pass_along );
3534 draw_to_image_file_total_area_cb ( NULL, current_window_pass_along ); /* set correct size info now */
3536 gtk_dialog_set_default_response ( GTK_DIALOG(dialog), GTK_RESPONSE_ACCEPT );
3538 gtk_widget_show_all ( gtk_dialog_get_content_area(GTK_DIALOG(dialog)) );
3540 if ( gtk_dialog_run ( GTK_DIALOG(dialog) ) == GTK_RESPONSE_ACCEPT )
3542 gtk_widget_hide ( GTK_WIDGET(dialog) );
3544 gchar *fn = draw_image_filename ( vw, one_image_only );
3548 gint active = gtk_combo_box_get_active ( GTK_COMBO_BOX(zoom_combo) );
3549 gdouble zoom = pow (2, active-2 );
3551 if ( one_image_only )
3552 save_image_file ( vw, fn,
3553 vw->draw_image_width = gtk_spin_button_get_value_as_int ( GTK_SPIN_BUTTON(width_spin) ),
3554 vw->draw_image_height = gtk_spin_button_get_value_as_int ( GTK_SPIN_BUTTON(height_spin) ),
3556 vw->draw_image_save_as_png = gtk_toggle_button_get_active ( GTK_TOGGLE_BUTTON(png_radio) ) );
3558 // NB is in UTM mode ATM
3559 save_image_dir ( vw, fn,
3560 vw->draw_image_width = gtk_spin_button_get_value_as_int ( GTK_SPIN_BUTTON(width_spin) ),
3561 vw->draw_image_height = gtk_spin_button_get_value_as_int ( GTK_SPIN_BUTTON(height_spin) ),
3563 vw->draw_image_save_as_png = gtk_toggle_button_get_active ( GTK_TOGGLE_BUTTON(png_radio) ),
3564 gtk_spin_button_get_value ( GTK_SPIN_BUTTON(tiles_width_spin) ),
3565 gtk_spin_button_get_value ( GTK_SPIN_BUTTON(tiles_height_spin) ) );
3570 gtk_widget_destroy ( GTK_WIDGET(dialog) );
3574 static void draw_to_image_file_cb ( GtkAction *a, VikWindow *vw )
3576 draw_to_image_file ( vw, TRUE );
3579 static void draw_to_image_dir_cb ( GtkAction *a, VikWindow *vw )
3581 draw_to_image_file ( vw, FALSE );
3584 static void print_cb ( GtkAction *a, VikWindow *vw )
3586 a_print(vw, vw->viking_vvp);
3589 /* really a misnomer: changes coord mode (actual coordinates) AND/OR draw mode (viewport only) */
3590 static void window_change_coord_mode_cb ( GtkAction *old_a, GtkAction *a, VikWindow *vw )
3592 VikViewportDrawMode drawmode;
3593 if (!strcmp(gtk_action_get_name(a), "ModeUTM")) {
3594 drawmode = VIK_VIEWPORT_DRAWMODE_UTM;
3596 else if (!strcmp(gtk_action_get_name(a), "ModeLatLon")) {
3597 drawmode = VIK_VIEWPORT_DRAWMODE_LATLON;
3599 else if (!strcmp(gtk_action_get_name(a), "ModeExpedia")) {
3600 drawmode = VIK_VIEWPORT_DRAWMODE_EXPEDIA;
3602 else if (!strcmp(gtk_action_get_name(a), "ModeMercator")) {
3603 drawmode = VIK_VIEWPORT_DRAWMODE_MERCATOR;
3606 g_critical("Houston, we've had a problem.");
3610 if ( !vw->only_updating_coord_mode_ui )
3612 VikViewportDrawMode olddrawmode = vik_viewport_get_drawmode ( vw->viking_vvp );
3613 if ( olddrawmode != drawmode )
3615 /* this takes care of coord mode too */
3616 vik_viewport_set_drawmode ( vw->viking_vvp, drawmode );
3617 if ( drawmode == VIK_VIEWPORT_DRAWMODE_UTM ) {
3618 vik_layers_panel_change_coord_mode ( vw->viking_vlp, VIK_COORD_UTM );
3619 } else if ( olddrawmode == VIK_VIEWPORT_DRAWMODE_UTM ) {
3620 vik_layers_panel_change_coord_mode ( vw->viking_vlp, VIK_COORD_LATLON );
3627 static void set_draw_scale ( GtkAction *a, VikWindow *vw )
3629 GtkWidget *check_box = gtk_ui_manager_get_widget ( vw->uim, "/ui/MainMenu/View/SetShow/ShowScale" );
3630 g_assert(check_box);
3631 gboolean state = gtk_check_menu_item_get_active ( GTK_CHECK_MENU_ITEM(check_box));
3632 vik_viewport_set_draw_scale ( vw->viking_vvp, state );
3636 static void set_draw_centermark ( GtkAction *a, VikWindow *vw )
3638 GtkWidget *check_box = gtk_ui_manager_get_widget ( vw->uim, "/ui/MainMenu/View/SetShow/ShowCenterMark" );
3639 g_assert(check_box);
3640 gboolean state = gtk_check_menu_item_get_active ( GTK_CHECK_MENU_ITEM(check_box));
3641 vik_viewport_set_draw_centermark ( vw->viking_vvp, state );
3645 static void set_draw_highlight ( GtkAction *a, VikWindow *vw )
3647 GtkWidget *check_box = gtk_ui_manager_get_widget ( vw->uim, "/ui/MainMenu/View/SetShow/ShowHighlight" );
3648 g_assert(check_box);
3649 gboolean state = gtk_check_menu_item_get_active ( GTK_CHECK_MENU_ITEM(check_box));
3650 vik_viewport_set_draw_highlight ( vw->viking_vvp, state );
3654 static void set_bg_color ( GtkAction *a, VikWindow *vw )
3656 GtkWidget *colorsd = gtk_color_selection_dialog_new ( _("Choose a background color") );
3657 GdkColor *color = vik_viewport_get_background_gdkcolor ( vw->viking_vvp );
3658 gtk_color_selection_set_previous_color ( GTK_COLOR_SELECTION(gtk_color_selection_dialog_get_color_selection(GTK_COLOR_SELECTION_DIALOG(colorsd))), color );
3659 gtk_color_selection_set_current_color ( GTK_COLOR_SELECTION(gtk_color_selection_dialog_get_color_selection(GTK_COLOR_SELECTION_DIALOG(colorsd))), color );
3660 if ( gtk_dialog_run ( GTK_DIALOG(colorsd) ) == GTK_RESPONSE_OK )
3662 gtk_color_selection_get_current_color ( GTK_COLOR_SELECTION(gtk_color_selection_dialog_get_color_selection(GTK_COLOR_SELECTION_DIALOG(colorsd))), color );
3663 vik_viewport_set_background_gdkcolor ( vw->viking_vvp, color );
3667 gtk_widget_destroy ( colorsd );
3670 static void set_highlight_color ( GtkAction *a, VikWindow *vw )
3672 GtkWidget *colorsd = gtk_color_selection_dialog_new ( _("Choose a track highlight color") );
3673 GdkColor *color = vik_viewport_get_highlight_gdkcolor ( vw->viking_vvp );
3674 gtk_color_selection_set_previous_color ( GTK_COLOR_SELECTION(gtk_color_selection_dialog_get_color_selection(GTK_COLOR_SELECTION_DIALOG(colorsd))), color );
3675 gtk_color_selection_set_current_color ( GTK_COLOR_SELECTION(gtk_color_selection_dialog_get_color_selection(GTK_COLOR_SELECTION_DIALOG(colorsd))), color );
3676 if ( gtk_dialog_run ( GTK_DIALOG(colorsd) ) == GTK_RESPONSE_OK )
3678 gtk_color_selection_get_current_color ( GTK_COLOR_SELECTION(gtk_color_selection_dialog_get_color_selection(GTK_COLOR_SELECTION_DIALOG(colorsd))), color );
3679 vik_viewport_set_highlight_gdkcolor ( vw->viking_vvp, color );
3683 gtk_widget_destroy ( colorsd );
3688 /***********************************************************************************************
3690 ***********************************************************************************************/
3692 static GtkActionEntry entries[] = {
3693 { "File", NULL, N_("_File"), 0, 0, 0 },
3694 { "Edit", NULL, N_("_Edit"), 0, 0, 0 },
3695 { "View", NULL, N_("_View"), 0, 0, 0 },
3696 { "SetShow", NULL, N_("_Show"), 0, 0, 0 },
3697 { "SetZoom", NULL, N_("_Zoom"), 0, 0, 0 },
3698 { "SetPan", NULL, N_("_Pan"), 0, 0, 0 },
3699 { "Layers", NULL, N_("_Layers"), 0, 0, 0 },
3700 { "Tools", NULL, N_("_Tools"), 0, 0, 0 },
3701 { "Exttools", NULL, N_("_Webtools"), 0, 0, 0 },
3702 { "Help", NULL, N_("_Help"), 0, 0, 0 },
3704 { "New", GTK_STOCK_NEW, N_("_New"), "<control>N", N_("New file"), (GCallback)newwindow_cb },
3705 { "Open", GTK_STOCK_OPEN, N_("_Open..."), "<control>O", N_("Open a file"), (GCallback)load_file },
3706 { "OpenRecentFile", NULL, N_("Open _Recent File"), NULL, NULL, (GCallback)NULL },
3707 { "Append", GTK_STOCK_ADD, N_("Append _File..."), NULL, N_("Append data from a different file"), (GCallback)load_file },
3708 { "Export", GTK_STOCK_CONVERT, N_("_Export All"), NULL, N_("Export All TrackWaypoint Layers"), (GCallback)NULL },
3709 { "ExportGPX", NULL, N_("_GPX..."), NULL, N_("Export as GPX"), (GCallback)export_to_gpx },
3710 { "Acquire", GTK_STOCK_GO_DOWN, N_("A_cquire"), NULL, NULL, (GCallback)NULL },
3711 { "AcquireGPS", NULL, N_("From _GPS..."), NULL, N_("Transfer data from a GPS device"), (GCallback)acquire_from_gps },
3712 { "AcquireGPSBabel", NULL, N_("Import File With GPS_Babel..."), NULL, N_("Import file via GPSBabel converter"), (GCallback)acquire_from_file },
3713 { "AcquireRouting", NULL, N_("_Directions..."), NULL, N_("Get driving directions"), (GCallback)acquire_from_routing },
3714 #ifdef VIK_CONFIG_OPENSTREETMAP
3715 { "AcquireOSM", NULL, N_("_OSM Traces..."), NULL, N_("Get traces from OpenStreetMap"), (GCallback)acquire_from_osm },
3716 { "AcquireMyOSM", NULL, N_("_My OSM Traces..."), NULL, N_("Get Your Own Traces from OpenStreetMap"), (GCallback)acquire_from_my_osm },
3718 #ifdef VIK_CONFIG_GEOCACHES
3719 { "AcquireGC", NULL, N_("Geo_caches..."), NULL, N_("Get Geocaches from geocaching.com"), (GCallback)acquire_from_gc },
3721 #ifdef VIK_CONFIG_GEOTAG
3722 { "AcquireGeotag", NULL, N_("From Geotagged _Images..."), NULL, N_("Create waypoints from geotagged images"), (GCallback)acquire_from_geotag },
3724 #ifdef VIK_CONFIG_GEONAMES
3725 { "AcquireWikipedia", NULL, N_("From _Wikipedia Waypoints"), NULL, N_("Create waypoints from Wikipedia items in the current view"), (GCallback)acquire_from_wikipedia },
3727 { "Save", GTK_STOCK_SAVE, N_("_Save"), "<control>S", N_("Save the file"), (GCallback)save_file },
3728 { "SaveAs", GTK_STOCK_SAVE_AS, N_("Save _As..."), NULL, N_("Save the file under different name"), (GCallback)save_file_as },
3729 { "FileProperties", NULL, N_("Properties..."), NULL, N_("File Properties"), (GCallback)file_properties_cb },
3730 { "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 },
3731 { "GenImgDir", GTK_STOCK_DND_MULTIPLE, N_("Generate _Directory of Images..."), NULL, N_("FIXME:IMGDIR"), (GCallback)draw_to_image_dir_cb },
3732 { "Print", GTK_STOCK_PRINT, N_("_Print..."), NULL, N_("Print maps"), (GCallback)print_cb },
3733 { "Exit", GTK_STOCK_QUIT, N_("E_xit"), "<control>W", N_("Exit the program"), (GCallback)window_close },
3734 { "SaveExit", GTK_STOCK_QUIT, N_("Save and Exit"), NULL, N_("Save and Exit the program"), (GCallback)save_file_and_exit },
3736 { "GotoDefaultLocation", GTK_STOCK_HOME, N_("Go to the _Default Location"), NULL, N_("Go to the default location"), (GCallback)goto_default_location },
3737 { "GotoSearch", GTK_STOCK_JUMP_TO, N_("Go to _Location..."), NULL, N_("Go to address/place using text search"), (GCallback)goto_address },
3738 { "GotoLL", GTK_STOCK_JUMP_TO, N_("_Go to Lat/Lon..."), NULL, N_("Go to arbitrary lat/lon coordinate"), (GCallback)draw_goto_cb },
3739 { "GotoUTM", GTK_STOCK_JUMP_TO, N_("Go to UTM..."), NULL, N_("Go to arbitrary UTM coordinate"), (GCallback)draw_goto_cb },
3740 { "Refresh", GTK_STOCK_REFRESH, N_("_Refresh"), "F5", N_("Refresh any maps displayed"), (GCallback)draw_refresh_cb },
3741 { "SetHLColor",GTK_STOCK_SELECT_COLOR, N_("Set _Highlight Color..."), NULL, NULL, (GCallback)set_highlight_color },
3742 { "SetBGColor",GTK_STOCK_SELECT_COLOR, N_("Set Bac_kground Color..."), NULL, NULL, (GCallback)set_bg_color },
3743 { "ZoomIn", GTK_STOCK_ZOOM_IN, N_("Zoom _In"), "<control>plus", NULL, (GCallback)draw_zoom_cb },
3744 { "ZoomOut", GTK_STOCK_ZOOM_OUT, N_("Zoom _Out"), "<control>minus", NULL, (GCallback)draw_zoom_cb },
3745 { "ZoomTo", GTK_STOCK_ZOOM_FIT, N_("Zoom _To..."), "<control>Z", NULL, (GCallback)zoom_to_cb },
3746 { "PanNorth", NULL, N_("Pan _North"), "<control>Up", NULL, (GCallback)draw_pan_cb },
3747 { "PanEast", NULL, N_("Pan _East"), "<control>Right", NULL, (GCallback)draw_pan_cb },
3748 { "PanSouth", NULL, N_("Pan _South"), "<control>Down", NULL, (GCallback)draw_pan_cb },
3749 { "PanWest", NULL, N_("Pan _West"), "<control>Left", NULL, (GCallback)draw_pan_cb },
3750 { "BGJobs", GTK_STOCK_EXECUTE, N_("Background _Jobs"), NULL, NULL, (GCallback)a_background_show_window },
3752 { "Cut", GTK_STOCK_CUT, N_("Cu_t"), NULL, NULL, (GCallback)menu_cut_layer_cb },
3753 { "Copy", GTK_STOCK_COPY, N_("_Copy"), NULL, NULL, (GCallback)menu_copy_layer_cb },
3754 { "Paste", GTK_STOCK_PASTE, N_("_Paste"), NULL, NULL, (GCallback)menu_paste_layer_cb },
3755 { "Delete", GTK_STOCK_DELETE, N_("_Delete"), NULL, NULL, (GCallback)menu_delete_layer_cb },
3756 { "DeleteAll", NULL, N_("Delete All"), NULL, NULL, (GCallback)clear_cb },
3757 { "MapCacheFlush",NULL, N_("_Flush Map Cache"), NULL, NULL, (GCallback)mapcache_flush_cb },
3758 { "SetDefaultLocation", GTK_STOCK_GO_FORWARD, N_("_Set the Default Location"), NULL, N_("Set the Default Location to the current position"),(GCallback)default_location_cb },
3759 { "Preferences",GTK_STOCK_PREFERENCES, N_("_Preferences"), NULL, NULL, (GCallback)preferences_cb },
3760 { "LayerDefaults",GTK_STOCK_PROPERTIES, N_("_Layer Defaults"), NULL, NULL, NULL },
3761 { "Properties",GTK_STOCK_PROPERTIES, N_("_Properties"), NULL, NULL, (GCallback)menu_properties_cb },
3763 { "HelpEntry", GTK_STOCK_HELP, N_("_Help"), "F1", NULL, (GCallback)help_help_cb },
3764 { "About", GTK_STOCK_ABOUT, N_("_About"), NULL, NULL, (GCallback)help_about_cb },
3767 static GtkActionEntry entries_gpsbabel[] = {
3768 { "ExportKML", NULL, N_("_KML..."), NULL, N_("Export as KML"), (GCallback)export_to_kml },
3772 /* FIXME use VIEWPORT_DRAWMODE values */
3773 static GtkRadioActionEntry mode_entries[] = {
3774 { "ModeUTM", NULL, N_("_UTM Mode"), "<control>u", NULL, 0 },
3775 { "ModeExpedia", NULL, N_("_Expedia Mode"), "<control>e", NULL, 1 },
3776 { "ModeMercator", NULL, N_("_Mercator Mode"), "<control>m", NULL, 4 },
3777 { "ModeLatLon", NULL, N_("Lat_/Lon Mode"), "<control>l", NULL, 5 },
3780 static GtkToggleActionEntry toggle_entries[] = {
3781 { "ShowScale", NULL, N_("Show _Scale"), "<shift>F5", N_("Show Scale"), (GCallback)set_draw_scale, TRUE },
3782 { "ShowCenterMark", NULL, N_("Show _Center Mark"), "F6", N_("Show Center Mark"), (GCallback)set_draw_centermark, TRUE },
3783 { "ShowHighlight", GTK_STOCK_UNDERLINE, N_("Show _Highlight"), "F7", N_("Show Highlight"), (GCallback)set_draw_highlight, TRUE },
3784 { "FullScreen", GTK_STOCK_FULLSCREEN, N_("_Full Screen"), "F11", N_("Activate full screen mode"), (GCallback)full_screen_cb, FALSE },
3785 { "ViewSidePanel", GTK_STOCK_INDEX, N_("Show Side _Panel"), "F9", N_("Show Side Panel"), (GCallback)view_side_panel_cb, TRUE },
3786 { "ViewStatusBar", NULL, N_("Show Status_bar"), "F12", N_("Show Statusbar"), (GCallback)view_statusbar_cb, TRUE },
3787 { "ViewToolbar", NULL, N_("Show _Toolbar"), "F3", N_("Show Toolbar"), (GCallback)view_toolbar_cb, TRUE },
3788 { "ViewMainMenu", NULL, N_("Show _Menu"), "F4", N_("Show Menu"), (GCallback)view_main_menu_cb, TRUE },
3791 #include "menu.xml.h"
3792 static void window_create_ui( VikWindow *window )
3795 GtkActionGroup *action_group;
3796 GtkAccelGroup *accel_group;
3799 GtkIconFactory *icon_factory;
3800 GtkIconSet *icon_set;
3801 GtkRadioActionEntry *tools = NULL, *radio;
3804 uim = gtk_ui_manager_new ();
3807 toolbox_add_tool(window->vt, &ruler_tool, TOOL_LAYER_TYPE_NONE);
3808 toolbox_add_tool(window->vt, &zoom_tool, TOOL_LAYER_TYPE_NONE);
3809 toolbox_add_tool(window->vt, &pan_tool, TOOL_LAYER_TYPE_NONE);
3810 toolbox_add_tool(window->vt, &select_tool, TOOL_LAYER_TYPE_NONE);
3813 if (!(mid = gtk_ui_manager_add_ui_from_string (uim, menu_xml, -1, &error))) {
3814 g_error_free (error);
3818 action_group = gtk_action_group_new ("MenuActions");
3819 gtk_action_group_set_translation_domain(action_group, PACKAGE_NAME);
3820 gtk_action_group_add_actions (action_group, entries, G_N_ELEMENTS (entries), window);
3821 gtk_action_group_add_toggle_actions (action_group, toggle_entries, G_N_ELEMENTS (toggle_entries), window);
3822 gtk_action_group_add_radio_actions (action_group, mode_entries, G_N_ELEMENTS (mode_entries), 4, (GCallback)window_change_coord_mode_cb, window);
3824 // Use this to see if GPSBabel is available:
3825 if ( a_babel_device_list ) {
3826 // If going to add more entries then might be worth creating a menu_gpsbabel.xml.h file
3827 if ( gtk_ui_manager_add_ui_from_string ( uim,
3828 "<ui><menubar name='MainMenu'><menu action='File'><menu action='Export'><menuitem action='ExportKML'/></menu></menu></menubar></ui>",
3830 gtk_action_group_add_actions ( action_group, entries_gpsbabel, G_N_ELEMENTS (entries_gpsbabel), window );
3833 icon_factory = gtk_icon_factory_new ();
3834 gtk_icon_factory_add_default (icon_factory);
3836 register_vik_icons(icon_factory);
3838 // Copy the tool RadioActionEntries out of the main Window structure into an extending array 'tools'
3839 // so that it can be applied to the UI in one action group add function call below
3841 for (i=0; i<window->vt->n_tools; i++) {
3842 tools = g_renew(GtkRadioActionEntry, tools, ntools+1);
3843 radio = &tools[ntools];
3845 *radio = window->vt->tools[i].ti.radioActionEntry;
3846 radio->value = ntools;
3849 for (i=0; i<VIK_LAYER_NUM_TYPES; i++) {
3850 GtkActionEntry action;
3851 gtk_ui_manager_add_ui(uim, mid, "/ui/MainMenu/Layers/",
3852 vik_layer_get_interface(i)->name,
3853 vik_layer_get_interface(i)->name,
3854 GTK_UI_MANAGER_MENUITEM, FALSE);
3856 icon_set = gtk_icon_set_new_from_pixbuf (gdk_pixbuf_from_pixdata (vik_layer_get_interface(i)->icon, FALSE, NULL ));
3857 gtk_icon_factory_add (icon_factory, vik_layer_get_interface(i)->name, icon_set);
3858 gtk_icon_set_unref (icon_set);
3860 action.name = vik_layer_get_interface(i)->name;
3861 action.stock_id = vik_layer_get_interface(i)->name;
3862 action.label = g_strdup_printf( _("New _%s Layer"), vik_layer_get_interface(i)->name);
3863 action.accelerator = vik_layer_get_interface(i)->accelerator;
3864 action.tooltip = NULL;
3865 action.callback = (GCallback)menu_addlayer_cb;
3866 gtk_action_group_add_actions(action_group, &action, 1, window);
3868 g_free ( (gchar*)action.label );
3870 if ( vik_layer_get_interface(i)->tools_count ) {
3871 gtk_ui_manager_add_ui(uim, mid, "/ui/MainMenu/Tools/", vik_layer_get_interface(i)->name, NULL, GTK_UI_MANAGER_SEPARATOR, FALSE);
3872 gtk_ui_manager_add_ui(uim, mid, "/ui/MainToolbar/ToolItems/", vik_layer_get_interface(i)->name, NULL, GTK_UI_MANAGER_SEPARATOR, FALSE);
3875 // Further tool copying for to apply to the UI, also apply menu UI setup
3876 for ( j = 0; j < vik_layer_get_interface(i)->tools_count; j++ ) {
3877 tools = g_renew(GtkRadioActionEntry, tools, ntools+1);
3878 radio = &tools[ntools];
3881 gtk_ui_manager_add_ui(uim, mid, "/ui/MainMenu/Tools",
3882 vik_layer_get_interface(i)->tools[j].radioActionEntry.label,
3883 vik_layer_get_interface(i)->tools[j].radioActionEntry.name,
3884 GTK_UI_MANAGER_MENUITEM, FALSE);
3885 gtk_ui_manager_add_ui(uim, mid, "/ui/MainToolbar/ToolItems",
3886 vik_layer_get_interface(i)->tools[j].radioActionEntry.label,
3887 vik_layer_get_interface(i)->tools[j].radioActionEntry.name,
3888 GTK_UI_MANAGER_TOOLITEM, FALSE);
3890 toolbox_add_tool(window->vt, &(vik_layer_get_interface(i)->tools[j]), i);
3892 *radio = vik_layer_get_interface(i)->tools[j].radioActionEntry;
3893 // Overwrite with actual number to use
3894 radio->value = ntools;
3897 GtkActionEntry action_dl;
3898 gchar *layername = g_strdup_printf ( "Layer%s", vik_layer_get_interface(i)->fixed_layer_name );
3899 gtk_ui_manager_add_ui(uim, mid, "/ui/MainMenu/Edit/LayerDefaults",
3900 vik_layer_get_interface(i)->name,
3902 GTK_UI_MANAGER_MENUITEM, FALSE);
3905 // For default layers use action names of the form 'Layer<LayerName>'
3906 // This is to avoid clashing with just the layer name used above for the tool actions
3907 action_dl.name = g_strconcat("Layer", vik_layer_get_interface(i)->fixed_layer_name, NULL);
3908 action_dl.stock_id = NULL;
3909 action_dl.label = g_strconcat("_", vik_layer_get_interface(i)->name, "...", NULL); // Prepend marker for keyboard accelerator
3910 action_dl.accelerator = NULL;
3911 action_dl.tooltip = NULL;
3912 action_dl.callback = (GCallback)layer_defaults_cb;
3913 gtk_action_group_add_actions(action_group, &action_dl, 1, window);
3914 g_free ( (gchar*)action_dl.name );
3915 g_free ( (gchar*)action_dl.label );
3917 g_object_unref (icon_factory);
3919 gtk_action_group_add_radio_actions(action_group, tools, ntools, 0, (GCallback)menu_tool_cb, window);
3922 gtk_ui_manager_insert_action_group (uim, action_group, 0);
3924 for (i=0; i<VIK_LAYER_NUM_TYPES; i++) {
3925 for ( j = 0; j < vik_layer_get_interface(i)->tools_count; j++ ) {
3926 GtkAction *action = gtk_action_group_get_action(action_group,
3927 vik_layer_get_interface(i)->tools[j].radioActionEntry.name);
3928 g_object_set(action, "sensitive", FALSE, NULL);
3932 // This is done last so we don't need to track the value of mid anymore
3933 vik_ext_tools_add_action_items ( window, window->uim, action_group, mid );
3935 window->action_group = action_group;
3937 accel_group = gtk_ui_manager_get_accel_group (uim);
3938 gtk_window_add_accel_group (GTK_WINDOW (window), accel_group);
3939 gtk_ui_manager_ensure_update (uim);
3941 setup_recent_files(window);
3945 // TODO - add method to add tool icons defined from outside this file
3946 // and remove the reverse dependency on icon definition from this file
3948 const GdkPixdata *data;
3951 { &mover_22_pixbuf, "vik-icon-pan" },
3952 { &zoom_18_pixbuf, "vik-icon-zoom" },
3953 { &ruler_18_pixbuf, "vik-icon-ruler" },
3954 { &select_18_pixbuf, "vik-icon-select" },
3955 { &vik_new_route_18_pixbuf, "vik-icon-Create Route" },
3956 { &route_finder_18_pixbuf, "vik-icon-Route Finder" },
3957 { &demdl_18_pixbuf, "vik-icon-DEM Download" },
3958 { &showpic_18_pixbuf, "vik-icon-Show Picture" },
3959 { &addtr_18_pixbuf, "vik-icon-Create Track" },
3960 { &edtr_18_pixbuf, "vik-icon-Edit Trackpoint" },
3961 { &addwp_18_pixbuf, "vik-icon-Create Waypoint" },
3962 { &edwp_18_pixbuf, "vik-icon-Edit Waypoint" },
3963 { &geozoom_18_pixbuf, "vik-icon-Georef Zoom Tool" },
3964 { &geomove_18_pixbuf, "vik-icon-Georef Move Map" },
3965 { &mapdl_18_pixbuf, "vik-icon-Maps Download" },
3968 static gint n_stock_icons = G_N_ELEMENTS (stock_icons);
3971 register_vik_icons (GtkIconFactory *icon_factory)
3973 GtkIconSet *icon_set;
3976 for (i = 0; i < n_stock_icons; i++) {
3977 icon_set = gtk_icon_set_new_from_pixbuf (gdk_pixbuf_from_pixdata (
3978 stock_icons[i].data, FALSE, NULL ));
3979 gtk_icon_factory_add (icon_factory, stock_icons[i].stock_id, icon_set);
3980 gtk_icon_set_unref (icon_set);
3984 gpointer vik_window_get_selected_trw_layer ( VikWindow *vw )
3986 return vw->selected_vtl;
3989 void vik_window_set_selected_trw_layer ( VikWindow *vw, gpointer vtl )
3991 vw->selected_vtl = vtl;
3992 vw->containing_vtl = vtl;
3994 vw->selected_track = NULL;
3995 vw->selected_tracks = NULL;
3996 vw->selected_waypoint = NULL;
3997 vw->selected_waypoints = NULL;
3998 // Set highlight thickness
3999 vik_viewport_set_highlight_thickness ( vw->viking_vvp, vik_trw_layer_get_property_tracks_line_thickness (vw->containing_vtl) );
4002 GHashTable *vik_window_get_selected_tracks ( VikWindow *vw )
4004 return vw->selected_tracks;
4007 void vik_window_set_selected_tracks ( VikWindow *vw, GHashTable *ght, gpointer vtl )
4009 vw->selected_tracks = ght;
4010 vw->containing_vtl = vtl;
4012 vw->selected_vtl = NULL;
4013 vw->selected_track = NULL;
4014 vw->selected_waypoint = NULL;
4015 vw->selected_waypoints = NULL;
4016 // Set highlight thickness
4017 vik_viewport_set_highlight_thickness ( vw->viking_vvp, vik_trw_layer_get_property_tracks_line_thickness (vw->containing_vtl) );
4020 gpointer vik_window_get_selected_track ( VikWindow *vw )
4022 return vw->selected_track;
4025 void vik_window_set_selected_track ( VikWindow *vw, gpointer *vt, gpointer vtl )
4027 vw->selected_track = vt;
4028 vw->containing_vtl = vtl;
4030 vw->selected_vtl = NULL;
4031 vw->selected_tracks = NULL;
4032 vw->selected_waypoint = NULL;
4033 vw->selected_waypoints = NULL;
4034 // Set highlight thickness
4035 vik_viewport_set_highlight_thickness ( vw->viking_vvp, vik_trw_layer_get_property_tracks_line_thickness (vw->containing_vtl) );
4038 GHashTable *vik_window_get_selected_waypoints ( VikWindow *vw )
4040 return vw->selected_waypoints;
4043 void vik_window_set_selected_waypoints ( VikWindow *vw, GHashTable *ght, gpointer vtl )
4045 vw->selected_waypoints = ght;
4046 vw->containing_vtl = vtl;
4048 vw->selected_vtl = NULL;
4049 vw->selected_track = NULL;
4050 vw->selected_tracks = NULL;
4051 vw->selected_waypoint = NULL;
4054 gpointer vik_window_get_selected_waypoint ( VikWindow *vw )
4056 return vw->selected_waypoint;
4059 void vik_window_set_selected_waypoint ( VikWindow *vw, gpointer *vwp, gpointer vtl )
4061 vw->selected_waypoint = vwp;
4062 vw->containing_vtl = vtl;
4064 vw->selected_vtl = NULL;
4065 vw->selected_track = NULL;
4066 vw->selected_tracks = NULL;
4067 vw->selected_waypoints = NULL;
4070 gboolean vik_window_clear_highlight ( VikWindow *vw )
4072 gboolean need_redraw = FALSE;
4073 if ( vw->selected_vtl != NULL ) {
4074 vw->selected_vtl = NULL;
4077 if ( vw->selected_track != NULL ) {
4078 vw->selected_track = NULL;
4081 if ( vw->selected_tracks != NULL ) {
4082 vw->selected_tracks = NULL;
4085 if ( vw->selected_waypoint != NULL ) {
4086 vw->selected_waypoint = NULL;
4089 if ( vw->selected_waypoints != NULL ) {
4090 vw->selected_waypoints = NULL;
4096 GThread *vik_window_get_thread ( VikWindow *vw )