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 && vw->filename && 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 // Drag+Drop gives URIs. And so in particular, %20 in place of spaces in filenames
655 // thus need to convert the text into a plain string
656 gchar *filename = g_filename_from_uri ( entry, NULL, NULL );
658 filenames = g_slist_append ( filenames, filename );
661 entry = entries[entry_runner];
665 g_signal_emit ( G_OBJECT(VIK_WINDOW_FROM_WIDGET(widget)), window_signals[VW_OPENWINDOW_SIGNAL], 0, filenames );
666 // NB: GSList & contents are freed by main.open_window
675 gtk_drag_finish ( context, success, FALSE, time );
678 #define VIK_SETTINGS_WIN_MAX "window_maximized"
679 #define VIK_SETTINGS_WIN_FULLSCREEN "window_fullscreen"
680 #define VIK_SETTINGS_WIN_WIDTH "window_width"
681 #define VIK_SETTINGS_WIN_HEIGHT "window_height"
682 #define VIK_SETTINGS_WIN_SAVE_IMAGE_WIDTH "window_save_image_width"
683 #define VIK_SETTINGS_WIN_SAVE_IMAGE_HEIGHT "window_save_image_height"
684 #define VIK_SETTINGS_WIN_SAVE_IMAGE_PNG "window_save_image_as_png"
686 static void vik_window_init ( VikWindow *vw )
688 GtkWidget *main_vbox;
691 vw->action_group = NULL;
693 vw->viking_vvp = vik_viewport_new();
694 vw->viking_vlp = vik_layers_panel_new();
695 vik_layers_panel_set_viewport ( vw->viking_vlp, vw->viking_vvp );
696 vw->viking_vs = vik_statusbar_new();
698 vw->vt = toolbox_create(vw);
699 window_create_ui(vw);
700 window_set_filename (vw, NULL);
701 vw->toolbar = GTK_TOOLBAR(gtk_ui_manager_get_widget (vw->uim, "/MainToolbar"));
703 vw->busy_cursor = gdk_cursor_new ( GDK_WATCH );
705 // Set the default tool
706 gtk_action_activate ( gtk_action_group_get_action ( vw->action_group, "Pan" ) );
709 vw->loaded_type = LOAD_TYPE_READ_FAILURE; //AKA none
710 vw->modified = FALSE;
711 vw->only_updating_coord_mode_ui = FALSE;
713 vw->pan_move = FALSE;
714 vw->pan_x = vw->pan_y = -1;
716 gint draw_image_width;
717 if ( a_settings_get_integer ( VIK_SETTINGS_WIN_SAVE_IMAGE_WIDTH, &draw_image_width ) )
718 vw->draw_image_width = draw_image_width;
720 vw->draw_image_width = DRAW_IMAGE_DEFAULT_WIDTH;
721 gint draw_image_height;
722 if ( a_settings_get_integer ( VIK_SETTINGS_WIN_SAVE_IMAGE_HEIGHT, &draw_image_height ) )
723 vw->draw_image_height = draw_image_height;
725 vw->draw_image_height = DRAW_IMAGE_DEFAULT_HEIGHT;
726 gboolean draw_image_save_as_png;
727 if ( a_settings_get_boolean ( VIK_SETTINGS_WIN_SAVE_IMAGE_PNG, &draw_image_save_as_png ) )
728 vw->draw_image_save_as_png = draw_image_save_as_png;
730 vw->draw_image_save_as_png = DRAW_IMAGE_DEFAULT_SAVE_AS_PNG;
732 main_vbox = gtk_vbox_new(FALSE, 1);
733 gtk_container_add (GTK_CONTAINER (vw), main_vbox);
735 gtk_box_pack_start (GTK_BOX(main_vbox), gtk_ui_manager_get_widget (vw->uim, "/MainMenu"), FALSE, TRUE, 0);
736 gtk_box_pack_start (GTK_BOX(main_vbox), GTK_WIDGET(vw->toolbar), FALSE, TRUE, 0);
737 gtk_toolbar_set_icon_size (vw->toolbar, GTK_ICON_SIZE_SMALL_TOOLBAR);
738 gtk_toolbar_set_style (vw->toolbar, GTK_TOOLBAR_ICONS);
740 vik_ext_tool_datasources_add_menu_items ( vw, vw->uim );
742 GtkWidget * zoom_levels = gtk_ui_manager_get_widget (vw->uim, "/MainMenu/View/SetZoom");
743 GtkWidget * zoom_levels_menu = create_zoom_menu_all_levels ( vik_viewport_get_zoom(vw->viking_vvp) );
744 gtk_menu_item_set_submenu (GTK_MENU_ITEM (zoom_levels), zoom_levels_menu);
745 g_signal_connect ( G_OBJECT(zoom_levels_menu), "selection-done", G_CALLBACK(zoom_changed), vw);
746 g_signal_connect_swapped ( G_OBJECT(vw->viking_vs), "clicked", G_CALLBACK(zoom_popup_handler), zoom_levels_menu );
748 g_signal_connect (G_OBJECT (vw), "delete_event", G_CALLBACK (delete_event), NULL);
750 g_signal_connect_swapped (G_OBJECT(vw->viking_vvp), "expose_event", G_CALLBACK(draw_sync), vw);
751 g_signal_connect_swapped (G_OBJECT(vw->viking_vvp), "configure_event", G_CALLBACK(window_configure_event), vw);
752 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 );
753 g_signal_connect_swapped (G_OBJECT(vw->viking_vvp), "scroll_event", G_CALLBACK(draw_scroll), vw);
754 g_signal_connect_swapped (G_OBJECT(vw->viking_vvp), "button_press_event", G_CALLBACK(draw_click), vw);
755 g_signal_connect_swapped (G_OBJECT(vw->viking_vvp), "button_release_event", G_CALLBACK(draw_release), vw);
756 g_signal_connect_swapped (G_OBJECT(vw->viking_vvp), "motion_notify_event", G_CALLBACK(draw_mouse_motion), vw);
757 g_signal_connect_swapped (G_OBJECT(vw->viking_vlp), "update", G_CALLBACK(draw_update), vw);
759 // Allow key presses to be processed anywhere
760 g_signal_connect_swapped (G_OBJECT (vw), "key_press_event", G_CALLBACK (key_press_event), vw);
762 hpaned = gtk_hpaned_new ();
763 gtk_paned_pack1 ( GTK_PANED(hpaned), GTK_WIDGET (vw->viking_vlp), FALSE, FALSE );
764 gtk_paned_pack2 ( GTK_PANED(hpaned), GTK_WIDGET (vw->viking_vvp), TRUE, TRUE );
766 /* This packs the button into the window (a gtk container). */
767 gtk_box_pack_start (GTK_BOX(main_vbox), hpaned, TRUE, TRUE, 0);
769 gtk_box_pack_end (GTK_BOX(main_vbox), GTK_WIDGET(vw->viking_vs), FALSE, TRUE, 0);
771 a_background_add_window ( vw );
773 window_list = g_slist_prepend ( window_list, vw);
775 gint height = VIKING_WINDOW_HEIGHT;
776 gint width = VIKING_WINDOW_WIDTH;
778 if ( a_vik_get_restore_window_state() ) {
779 if ( a_settings_get_integer ( VIK_SETTINGS_WIN_HEIGHT, &height ) ) {
780 // Enforce a basic minimum size
785 // No setting - so use default
786 height = VIKING_WINDOW_HEIGHT;
788 if ( a_settings_get_integer ( VIK_SETTINGS_WIN_WIDTH, &width ) ) {
789 // Enforce a basic minimum size
794 // No setting - so use default
795 width = VIKING_WINDOW_WIDTH;
798 if ( a_settings_get_boolean ( VIK_SETTINGS_WIN_MAX, &maxed ) )
800 gtk_window_maximize ( GTK_WINDOW(vw) );
803 if ( a_settings_get_boolean ( VIK_SETTINGS_WIN_FULLSCREEN, &full ) ) {
805 gtk_window_fullscreen ( GTK_WINDOW(vw) );
806 GtkWidget *check_box = gtk_ui_manager_get_widget ( vw->uim, "/ui/MainMenu/View/FullScreen" );
807 gtk_check_menu_item_set_active ( GTK_CHECK_MENU_ITEM(check_box), TRUE );
812 gtk_window_set_default_size ( GTK_WINDOW(vw), width, height );
816 vw->save_img_dia = NULL;
817 vw->save_img_dir_dia = NULL;
819 // Only accept Drag and Drop of files onto the viewport
820 gtk_drag_dest_set ( GTK_WIDGET(vw->viking_vvp), GTK_DEST_DEFAULT_ALL, NULL, 0, GDK_ACTION_COPY );
821 gtk_drag_dest_add_uri_targets ( GTK_WIDGET(vw->viking_vvp) );
822 g_signal_connect ( GTK_WIDGET(vw->viking_vvp), "drag-data-received", G_CALLBACK(drag_data_received_cb), NULL );
824 // Store the thread value so comparisons can be made to determine the gdk update method
825 // Hopefully we are storing the main thread value here :)
826 // [ATM any window initialization is always be performed by the main thread]
827 vw->thread = g_thread_self();
830 static VikWindow *window_new ()
832 return VIK_WINDOW ( g_object_new ( VIK_WINDOW_TYPE, NULL ) );
836 * Update the displayed map
837 * Only update the top most visible map layer
838 * ATM this assumes (as per defaults) the top most map has full alpha setting
839 * such that other other maps even though they may be active will not be seen
840 * It's more complicated to work out which maps are actually visible due to alpha settings
841 * and overkill for this simple refresh method.
843 static void simple_map_update ( VikWindow *vw, gboolean only_new )
845 // Find the most relevent single map layer to operate on
846 VikLayer *vl = vik_aggregate_layer_get_top_visible_layer_of_type (vik_layers_panel_get_top_layer(vw->viking_vlp), VIK_LAYER_MAPS);
848 vik_maps_layer_download ( VIK_MAPS_LAYER(vl), vw->viking_vvp, only_new );
852 * This is the global key press handler
853 * Global shortcuts are available at any time and hence are not restricted to when a certain tool is enabled
855 static gboolean key_press_event( VikWindow *vw, GdkEventKey *event, gpointer data )
857 // The keys handled here are not in the menuing system for a couple of reasons:
858 // . Keeps the menu size compact (alebit at expense of discoverably)
859 // . Allows differing key bindings to perform the same actions
861 // First decide if key events are related to the maps layer
862 gboolean map_download = FALSE;
863 gboolean map_download_only_new = TRUE; // Only new or reload
865 GdkModifierType modifiers = gtk_accelerator_get_default_mod_mask();
867 // Standard 'Refresh' keys: F5 or Ctrl+r
868 // Note 'F5' is actually handled via draw_refresh_cb() later on
869 // (not 'R' it's 'r' notice the case difference!!)
870 if ( event->keyval == GDK_r && (event->state & modifiers) == GDK_CONTROL_MASK ) {
872 map_download_only_new = TRUE;
874 // Full cache reload with Ctrl+F5 or Ctrl+Shift+r [This is not in the menu system]
875 // Note the use of uppercase R here since shift key has been pressed
876 else if ( (event->keyval == GDK_F5 && (event->state & modifiers) == GDK_CONTROL_MASK ) ||
877 ( event->keyval == GDK_R && (event->state & modifiers) == (GDK_CONTROL_MASK + GDK_SHIFT_MASK) ) ) {
879 map_download_only_new = FALSE;
882 if ( map_download ) {
883 simple_map_update ( vw, map_download_only_new );
886 VikLayer *vl = vik_layers_panel_get_selected ( vw->viking_vlp );
887 if (vl && vw->vt->active_tool != -1 && vw->vt->tools[vw->vt->active_tool].ti.key_press ) {
888 gint ltype = vw->vt->tools[vw->vt->active_tool].layer_type;
889 if ( vl && ltype == vl->type )
890 return vw->vt->tools[vw->vt->active_tool].ti.key_press(vl, event, vw->vt->tools[vw->vt->active_tool].state);
893 // Ensure called only on window tools (i.e. not on any of the Layer tools since the layer is NULL)
894 if ( vw->current_tool < TOOL_LAYER ) {
895 // No layer - but enable window tool keypress processing - these should be able to handle a NULL layer
896 if ( vw->vt->tools[vw->vt->active_tool].ti.key_press ) {
897 return vw->vt->tools[vw->vt->active_tool].ti.key_press ( vl, event, vw->vt->tools[vw->vt->active_tool].state );
901 /* Restore Main Menu via Escape key if the user has hidden it */
902 /* This key is more likely to be used as they may not remember the function key */
903 if ( event->keyval == GDK_Escape ) {
904 GtkWidget *check_box = gtk_ui_manager_get_widget ( vw->uim, "/ui/MainMenu/View/SetShow/ViewMainMenu" );
906 gboolean state = gtk_check_menu_item_get_active ( GTK_CHECK_MENU_ITEM(check_box) );
908 gtk_widget_show ( gtk_ui_manager_get_widget ( vw->uim, "/ui/MainMenu" ) );
909 gtk_check_menu_item_set_active ( GTK_CHECK_MENU_ITEM(check_box), TRUE );
910 return TRUE; /* handled keypress */
915 return FALSE; /* don't handle the keypress */
918 static gboolean delete_event( VikWindow *vw )
920 #ifdef VIKING_PROMPT_IF_MODIFIED
927 dia = GTK_DIALOG ( gtk_message_dialog_new ( GTK_WINDOW(vw), GTK_DIALOG_DESTROY_WITH_PARENT, GTK_MESSAGE_QUESTION, GTK_BUTTONS_NONE,
928 _("Do you want to save the changes you made to the document \"%s\"?\n"
930 "Your changes will be lost if you don't save them."),
931 window_get_filename ( vw ) ) );
932 gtk_dialog_add_buttons ( dia, _("Don't Save"), GTK_RESPONSE_NO, GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, GTK_STOCK_SAVE, GTK_RESPONSE_YES, NULL );
933 switch ( gtk_dialog_run ( dia ) )
935 case GTK_RESPONSE_NO: gtk_widget_destroy ( GTK_WIDGET(dia) ); return FALSE;
936 case GTK_RESPONSE_CANCEL: gtk_widget_destroy ( GTK_WIDGET(dia) ); return TRUE;
937 default: gtk_widget_destroy ( GTK_WIDGET(dia) ); return ! save_file(NULL, vw);
941 if ( window_count == 1 ) {
942 // On the final window close - save latest state - if it's wanted...
943 if ( a_vik_get_restore_window_state() ) {
944 gint state = gdk_window_get_state ( GTK_WIDGET(vw)->window );
945 gboolean state_max = state & GDK_WINDOW_STATE_MAXIMIZED;
946 a_settings_set_boolean ( VIK_SETTINGS_WIN_MAX, state_max );
948 gboolean state_fullscreen = state & GDK_WINDOW_STATE_FULLSCREEN;
949 a_settings_set_boolean ( VIK_SETTINGS_WIN_FULLSCREEN, state_fullscreen );
951 a_settings_set_boolean ( VIK_SETTINGS_WIN_SIDEPANEL, GTK_WIDGET_VISIBLE (GTK_WIDGET(vw->viking_vlp)) );
953 a_settings_set_boolean ( VIK_SETTINGS_WIN_STATUSBAR, GTK_WIDGET_VISIBLE (GTK_WIDGET(vw->viking_vs)) );
955 a_settings_set_boolean ( VIK_SETTINGS_WIN_TOOLBAR, GTK_WIDGET_VISIBLE (GTK_WIDGET(vw->toolbar)) );
957 // If supersized - no need to save the enlarged width+height values
958 if ( ! (state_fullscreen || state_max) ) {
960 gtk_window_get_size ( GTK_WINDOW (vw), &width, &height );
961 a_settings_set_integer ( VIK_SETTINGS_WIN_WIDTH, width );
962 a_settings_set_integer ( VIK_SETTINGS_WIN_HEIGHT, height );
966 a_settings_set_integer ( VIK_SETTINGS_WIN_SAVE_IMAGE_WIDTH, vw->draw_image_width );
967 a_settings_set_integer ( VIK_SETTINGS_WIN_SAVE_IMAGE_HEIGHT, vw->draw_image_height );
968 a_settings_set_boolean ( VIK_SETTINGS_WIN_SAVE_IMAGE_PNG, vw->draw_image_save_as_png );
975 static void newwindow_cb ( GtkAction *a, VikWindow *vw )
977 g_signal_emit ( G_OBJECT(vw), window_signals[VW_NEWWINDOW_SIGNAL], 0 );
980 static void draw_update ( VikWindow *vw )
986 static void draw_sync ( VikWindow *vw )
988 vik_viewport_sync(vw->viking_vvp);
993 * Split the status update, as sometimes only need to update the tool part
994 * also on initialization the zoom related stuff is not ready to be used
996 static void draw_status_tool ( VikWindow *vw )
998 if ( vw->current_tool == TOOL_LAYER )
999 // Use tooltip rather than the internal name as the tooltip is i8n
1000 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 );
1002 vik_statusbar_set_message ( vw->viking_vs, VIK_STATUSBAR_TOOL, _(tool_names[vw->current_tool]) );
1005 static void draw_status ( VikWindow *vw )
1007 static gchar zoom_level[22];
1008 gdouble xmpp = vik_viewport_get_xmpp (vw->viking_vvp);
1009 gdouble ympp = vik_viewport_get_ympp(vw->viking_vvp);
1010 gchar *unit = vik_viewport_get_coord_mode(vw->viking_vvp) == VIK_COORD_UTM ? _("mpp") : _("pixelfact");
1012 g_snprintf ( zoom_level, 22, "%.3f/%.3f %s", xmpp, ympp, unit );
1014 if ( (int)xmpp - xmpp < 0.0 )
1015 g_snprintf ( zoom_level, 22, "%.3f %s", xmpp, unit );
1017 /* xmpp should be a whole number so don't show useless .000 bit */
1018 g_snprintf ( zoom_level, 22, "%d %s", (int)xmpp, unit );
1020 vik_statusbar_set_message ( vw->viking_vs, VIK_STATUSBAR_ZOOM, zoom_level );
1022 draw_status_tool ( vw );
1025 void vik_window_set_redraw_trigger(VikLayer *vl)
1027 VikWindow *vw = VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vl));
1032 static void window_configure_event ( VikWindow *vw )
1034 static int first = 1;
1037 // This is a hack to set the cursor corresponding to the first tool
1038 // FIXME find the correct way to initialize both tool and its cursor
1040 vw->viewport_cursor = (GdkCursor *)toolbox_get_cursor(vw->vt, "Pan");
1041 /* We set cursor, even if it is NULL: it resets to default */
1042 gdk_window_set_cursor ( gtk_widget_get_window(GTK_WIDGET(vw->viking_vvp)), vw->viewport_cursor );
1046 static void draw_redraw ( VikWindow *vw )
1048 VikCoord old_center = vw->trigger_center;
1049 vw->trigger_center = *(vik_viewport_get_center(vw->viking_vvp));
1050 VikLayer *new_trigger = vw->trigger;
1052 VikLayer *old_trigger = VIK_LAYER(vik_viewport_get_trigger(vw->viking_vvp));
1054 if ( ! new_trigger )
1055 ; /* do nothing -- have to redraw everything. */
1056 else if ( (old_trigger != new_trigger) || !vik_coord_equals(&old_center, &vw->trigger_center) || (new_trigger->type == VIK_LAYER_AGGREGATE) )
1057 vik_viewport_set_trigger ( vw->viking_vvp, new_trigger ); /* todo: set to half_drawn mode if new trigger is above old */
1059 vik_viewport_set_half_drawn ( vw->viking_vvp, TRUE );
1062 vik_viewport_clear ( vw->viking_vvp);
1063 vik_layers_panel_draw_all ( vw->viking_vlp );
1064 vik_viewport_draw_scale ( vw->viking_vvp );
1065 vik_viewport_draw_copyright ( vw->viking_vvp );
1066 vik_viewport_draw_centermark ( vw->viking_vvp );
1067 vik_viewport_draw_logo ( vw->viking_vvp );
1069 vik_viewport_set_half_drawn ( vw->viking_vvp, FALSE ); /* just in case. */
1072 gboolean draw_buf_done = TRUE;
1074 static gboolean draw_buf(gpointer data)
1076 gpointer *pass_along = data;
1077 gdk_threads_enter();
1078 gdk_draw_drawable (pass_along[0], pass_along[1],
1079 pass_along[2], 0, 0, 0, 0, -1, -1);
1080 draw_buf_done = TRUE;
1081 gdk_threads_leave();
1086 /* Mouse event handlers ************************************************************************/
1088 static void vik_window_pan_click (VikWindow *vw, GdkEventButton *event)
1090 /* set panning origin */
1091 vw->pan_move = FALSE;
1092 vw->pan_x = (gint) event->x;
1093 vw->pan_y = (gint) event->y;
1096 static void draw_click (VikWindow *vw, GdkEventButton *event)
1098 gtk_widget_grab_focus ( GTK_WIDGET(vw->viking_vvp) );
1100 /* middle button pressed. we reserve all middle button and scroll events
1101 * for panning and zooming; tools only get left/right/movement
1103 if ( event->button == 2) {
1104 if ( vw->vt->tools[vw->vt->active_tool].ti.pan_handler )
1105 // Tool still may need to do something (such as disable something)
1106 toolbox_click(vw->vt, event);
1107 vik_window_pan_click ( vw, event );
1110 toolbox_click(vw->vt, event);
1114 static void vik_window_pan_move (VikWindow *vw, GdkEventMotion *event)
1116 if ( vw->pan_x != -1 ) {
1117 vik_viewport_set_center_screen ( vw->viking_vvp, vik_viewport_get_width(vw->viking_vvp)/2 - event->x + vw->pan_x,
1118 vik_viewport_get_height(vw->viking_vvp)/2 - event->y + vw->pan_y );
1119 vw->pan_move = TRUE;
1120 vw->pan_x = event->x;
1121 vw->pan_y = event->y;
1126 static void draw_mouse_motion (VikWindow *vw, GdkEventMotion *event)
1128 static VikCoord coord;
1129 static struct UTM utm;
1130 static struct LatLon ll;
1131 #define BUFFER_SIZE 50
1132 static char pointer_buf[BUFFER_SIZE];
1133 gchar *lat = NULL, *lon = NULL;
1136 VikDemInterpol interpol_method;
1138 /* This is a hack, but work far the best, at least for single pointer systems.
1139 * See http://bugzilla.gnome.org/show_bug.cgi?id=587714 for more. */
1141 gdk_window_get_pointer (event->window, &x, &y, NULL);
1145 toolbox_move(vw->vt, event);
1147 vik_viewport_screen_to_coord ( vw->viking_vvp, event->x, event->y, &coord );
1148 vik_coord_to_utm ( &coord, &utm );
1150 if ( vik_viewport_get_drawmode ( vw->viking_vvp ) == VIK_VIEWPORT_DRAWMODE_UTM ) {
1151 // Reuse lat for the first part (Zone + N or S, and lon for the second part (easting and northing) of a UTM format:
1152 // ZONE[N|S] EASTING NORTHING
1153 lat = g_malloc(4*sizeof(gchar));
1154 // NB zone is stored in a char but is an actual number
1155 g_snprintf (lat, 4, "%d%c", utm.zone, utm.letter);
1156 lon = g_malloc(16*sizeof(gchar));
1157 g_snprintf (lon, 16, "%d %d", (gint)utm.easting, (gint)utm.northing);
1160 a_coords_utm_to_latlon ( &utm, &ll );
1161 a_coords_latlon_to_string ( &ll, &lat, &lon );
1164 /* Change interpolate method according to scale */
1165 zoom = vik_viewport_get_zoom(vw->viking_vvp);
1167 interpol_method = VIK_DEM_INTERPOL_NONE;
1168 else if (zoom >= 1.0)
1169 interpol_method = VIK_DEM_INTERPOL_SIMPLE;
1171 interpol_method = VIK_DEM_INTERPOL_BEST;
1172 if ((alt = a_dems_get_elev_by_coord(&coord, interpol_method)) != VIK_DEM_INVALID_ELEVATION) {
1173 if ( a_vik_get_units_height () == VIK_UNITS_HEIGHT_METRES )
1174 g_snprintf ( pointer_buf, BUFFER_SIZE, _("%s %s %dm"), lat, lon, alt );
1176 g_snprintf ( pointer_buf, BUFFER_SIZE, _("%s %s %dft"), lat, lon, (int)VIK_METERS_TO_FEET(alt) );
1179 g_snprintf ( pointer_buf, BUFFER_SIZE, _("%s %s"), lat, lon );
1184 vik_statusbar_set_message ( vw->viking_vs, VIK_STATUSBAR_POSITION, pointer_buf );
1186 vik_window_pan_move ( vw, event );
1188 /* This is recommended by the GTK+ documentation, but does not work properly.
1189 * Use deprecated way until GTK+ gets a solution for correct motion hint handling:
1190 * http://bugzilla.gnome.org/show_bug.cgi?id=587714
1192 /* gdk_event_request_motions ( event ); */
1195 static void vik_window_pan_release ( VikWindow *vw, GdkEventButton *event )
1197 if ( vw->pan_move == FALSE )
1198 vik_viewport_set_center_screen ( vw->viking_vvp, vw->pan_x, vw->pan_y );
1200 vik_viewport_set_center_screen ( vw->viking_vvp, vik_viewport_get_width(vw->viking_vvp)/2 - event->x + vw->pan_x,
1201 vik_viewport_get_height(vw->viking_vvp)/2 - event->y + vw->pan_y );
1202 vw->pan_move = FALSE;
1203 vw->pan_x = vw->pan_y = -1;
1207 static void draw_release ( VikWindow *vw, GdkEventButton *event )
1209 gtk_widget_grab_focus ( GTK_WIDGET(vw->viking_vvp) );
1211 if ( event->button == 2 ) { /* move / pan */
1212 if ( vw->vt->tools[vw->vt->active_tool].ti.pan_handler )
1213 // Tool still may need to do something (such as reenable something)
1214 toolbox_release(vw->vt, event);
1215 vik_window_pan_release ( vw, event );
1218 toolbox_release(vw->vt, event);
1222 static void draw_scroll (VikWindow *vw, GdkEventScroll *event)
1224 guint modifiers = event->state & (GDK_SHIFT_MASK | GDK_CONTROL_MASK);
1225 if ( modifiers == GDK_CONTROL_MASK ) {
1226 /* control == pan up & down */
1227 if ( event->direction == GDK_SCROLL_UP )
1228 vik_viewport_set_center_screen ( vw->viking_vvp, vik_viewport_get_width(vw->viking_vvp)/2, vik_viewport_get_height(vw->viking_vvp)/3 );
1230 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 );
1231 } else if ( modifiers == GDK_SHIFT_MASK ) {
1232 /* shift == pan left & right */
1233 if ( event->direction == GDK_SCROLL_UP )
1234 vik_viewport_set_center_screen ( vw->viking_vvp, vik_viewport_get_width(vw->viking_vvp)/3, vik_viewport_get_height(vw->viking_vvp)/2 );
1236 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 );
1237 } else if ( modifiers == (GDK_CONTROL_MASK | GDK_SHIFT_MASK) ) {
1238 // This zoom is on the center position
1239 if ( event->direction == GDK_SCROLL_UP )
1240 vik_viewport_zoom_in (vw->viking_vvp);
1242 vik_viewport_zoom_out (vw->viking_vvp);
1244 /* make sure mouse is still over the same point on the map when we zoom */
1247 gint center_x = vik_viewport_get_width ( vw->viking_vvp ) / 2;
1248 gint center_y = vik_viewport_get_height ( vw->viking_vvp ) / 2;
1249 vik_viewport_screen_to_coord ( vw->viking_vvp, event->x, event->y, &coord );
1250 if ( event->direction == GDK_SCROLL_UP )
1251 vik_viewport_zoom_in (vw->viking_vvp);
1253 vik_viewport_zoom_out(vw->viking_vvp);
1254 vik_viewport_coord_to_screen ( vw->viking_vvp, &coord, &x, &y );
1255 vik_viewport_set_center_screen ( vw->viking_vvp, center_x + (x - event->x),
1256 center_y + (y - event->y) );
1264 /********************************************************************************
1266 ********************************************************************************/
1267 static void draw_ruler(VikViewport *vvp, GdkDrawable *d, GdkGC *gc, gint x1, gint y1, gint x2, gint y2, gdouble distance)
1271 GdkGC *labgc = vik_viewport_new_gc ( vvp, "#cccccc", 1);
1272 GdkGC *thickgc = gdk_gc_new(d);
1274 gdouble len = sqrt((x1-x2)*(x1-x2) + (y1-y2)*(y1-y2));
1275 gdouble dx = (x2-x1)/len*10;
1276 gdouble dy = (y2-y1)/len*10;
1277 gdouble c = cos(DEG2RAD(15.0));
1278 gdouble s = sin(DEG2RAD(15.0));
1280 gdouble baseangle = 0;
1283 /* draw line with arrow ends */
1285 gint tmp_x1=x1, tmp_y1=y1, tmp_x2=x2, tmp_y2=y2;
1286 a_viewport_clip_line(&tmp_x1, &tmp_y1, &tmp_x2, &tmp_y2);
1287 gdk_draw_line(d, gc, tmp_x1, tmp_y1, tmp_x2, tmp_y2);
1290 a_viewport_clip_line(&x1, &y1, &x2, &y2);
1291 gdk_draw_line(d, gc, x1, y1, x2, y2);
1293 gdk_draw_line(d, gc, x1 - dy, y1 + dx, x1 + dy, y1 - dx);
1294 gdk_draw_line(d, gc, x2 - dy, y2 + dx, x2 + dy, y2 - dx);
1295 gdk_draw_line(d, gc, x2, y2, x2 - (dx * c + dy * s), y2 - (dy * c - dx * s));
1296 gdk_draw_line(d, gc, x2, y2, x2 - (dx * c - dy * s), y2 - (dy * c + dx * s));
1297 gdk_draw_line(d, gc, x1, y1, x1 + (dx * c + dy * s), y1 + (dy * c - dx * s));
1298 gdk_draw_line(d, gc, x1, y1, x1 + (dx * c - dy * s), y1 + (dy * c + dx * s));
1304 vik_viewport_compute_bearing ( vvp, x1, y1, x2, y2, &angle, &baseangle );
1308 gdk_gc_copy(thickgc, gc);
1309 gdk_gc_set_line_attributes(thickgc, CW, GDK_LINE_SOLID, GDK_CAP_BUTT, GDK_JOIN_MITER);
1310 gdk_color_parse("#2255cc", &color);
1311 gdk_gc_set_rgb_fg_color(thickgc, &color);
1313 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);
1316 gdk_gc_copy(thickgc, gc);
1317 gdk_gc_set_line_attributes(thickgc, 2, GDK_LINE_SOLID, GDK_CAP_BUTT, GDK_JOIN_MITER);
1318 for (i=0; i<180; i++) {
1319 c = cos(DEG2RAD(i)*2 + baseangle);
1320 s = sin(DEG2RAD(i)*2 + baseangle);
1323 gdk_draw_line (d, gc, x1 + CR*c, y1 + CR*s, x1 + (CR+CW)*c, y1 + (CR+CW)*s);
1325 gdouble ticksize = 2*CW;
1326 gdk_draw_line (d, thickgc, x1 + (CR-CW)*c, y1 + (CR-CW)*s, x1 + (CR+ticksize)*c, y1 + (CR+ticksize)*s);
1330 gdk_draw_arc (d, gc, FALSE, x1-CR, y1-CR, 2*CR, 2*CR, 0, 64*360);
1331 gdk_draw_arc (d, gc, FALSE, x1-CR-CW, y1-CR-CW, 2*(CR+CW), 2*(CR+CW), 0, 64*360);
1332 gdk_draw_arc (d, gc, FALSE, x1-CR+CW, y1-CR+CW, 2*(CR-CW), 2*(CR-CW), 0, 64*360);
1333 c = (CR+CW*2)*cos(baseangle);
1334 s = (CR+CW*2)*sin(baseangle);
1335 gdk_draw_line (d, gc, x1-c, y1-s, x1+c, y1+s);
1336 gdk_draw_line (d, gc, x1+s, y1-c, x1-s, y1+c);
1339 #define LABEL(x, y, w, h) { \
1340 gdk_draw_rectangle(d, labgc, TRUE, (x)-2, (y)-1, (w)+4, (h)+1); \
1341 gdk_draw_rectangle(d, gc, FALSE, (x)-2, (y)-1, (w)+4, (h)+1); \
1342 gdk_draw_layout(d, gc, (x), (y), pl); }
1344 gint wd, hd, xd, yd;
1345 gint wb, hb, xb, yb;
1347 pl = gtk_widget_create_pango_layout (GTK_WIDGET(vvp), NULL);
1348 pango_layout_set_font_description (pl, gtk_widget_get_style(GTK_WIDGET(vvp))->font_desc);
1349 pango_layout_set_text(pl, "N", -1);
1350 gdk_draw_layout(d, gc, x1-5, y1-CR-3*CW-8, pl);
1352 /* draw label with distance */
1353 vik_units_distance_t dist_units = a_vik_get_units_distance ();
1354 switch (dist_units) {
1355 case VIK_UNITS_DISTANCE_KILOMETRES:
1356 if (distance >= 1000 && distance < 100000) {
1357 g_sprintf(str, "%3.2f km", distance/1000.0);
1358 } else if (distance < 1000) {
1359 g_sprintf(str, "%d m", (int)distance);
1361 g_sprintf(str, "%d km", (int)distance/1000);
1364 case VIK_UNITS_DISTANCE_MILES:
1365 if (distance >= VIK_MILES_TO_METERS(1) && distance < VIK_MILES_TO_METERS(100)) {
1366 g_sprintf(str, "%3.2f miles", VIK_METERS_TO_MILES(distance));
1367 } else if (distance < VIK_MILES_TO_METERS(1)) {
1368 g_sprintf(str, "%d yards", (int)(distance*1.0936133));
1370 g_sprintf(str, "%d miles", (int)VIK_METERS_TO_MILES(distance));
1374 g_critical("Houston, we've had a problem. distance=%d", dist_units);
1377 pango_layout_set_text(pl, str, -1);
1379 pango_layout_get_pixel_size ( pl, &wd, &hd );
1381 xd = (x1+x2)/2 + dy;
1382 yd = (y1+y2)/2 - hd/2 - dx;
1384 xd = (x1+x2)/2 - dy;
1385 yd = (y1+y2)/2 - hd/2 + dx;
1388 if ( xd < -5 || yd < -5 || xd > vik_viewport_get_width(vvp)+5 || yd > vik_viewport_get_height(vvp)+5 ) {
1393 LABEL(xd, yd, wd, hd);
1395 /* draw label with bearing */
1396 g_sprintf(str, "%3.1f°", RAD2DEG(angle));
1397 pango_layout_set_text(pl, str, -1);
1398 pango_layout_get_pixel_size ( pl, &wb, &hb );
1399 xb = x1 + CR*cos(angle-M_PI_2);
1400 yb = y1 + CR*sin(angle-M_PI_2);
1402 if ( xb < -5 || yb < -5 || xb > vik_viewport_get_width(vvp)+5 || yb > vik_viewport_get_height(vvp)+5 ) {
1408 GdkRectangle r1 = {xd-2, yd-1, wd+4, hd+1}, r2 = {xb-2, yb-1, wb+4, hb+1};
1409 if (gdk_rectangle_intersect(&r1, &r2, &r2)) {
1413 LABEL(xb, yb, wb, hb);
1417 g_object_unref ( G_OBJECT ( pl ) );
1418 g_object_unref ( G_OBJECT ( labgc ) );
1419 g_object_unref ( G_OBJECT ( thickgc ) );
1425 gboolean has_oldcoord;
1427 } ruler_tool_state_t;
1429 static gpointer ruler_create (VikWindow *vw, VikViewport *vvp)
1431 ruler_tool_state_t *s = g_new(ruler_tool_state_t, 1);
1434 s->has_oldcoord = FALSE;
1438 static void ruler_destroy (ruler_tool_state_t *s)
1443 static VikLayerToolFuncStatus ruler_click (VikLayer *vl, GdkEventButton *event, ruler_tool_state_t *s)
1448 if ( event->button == 1 ) {
1449 gchar *lat=NULL, *lon=NULL;
1450 vik_viewport_screen_to_coord ( s->vvp, (gint) event->x, (gint) event->y, &coord );
1451 vik_coord_to_latlon ( &coord, &ll );
1452 a_coords_latlon_to_string ( &ll, &lat, &lon );
1453 if ( s->has_oldcoord ) {
1454 vik_units_distance_t dist_units = a_vik_get_units_distance ();
1455 switch (dist_units) {
1456 case VIK_UNITS_DISTANCE_KILOMETRES:
1457 temp = g_strdup_printf ( "%s %s DIFF %f meters", lat, lon, vik_coord_diff( &coord, &(s->oldcoord) ) );
1459 case VIK_UNITS_DISTANCE_MILES:
1460 temp = g_strdup_printf ( "%s %s DIFF %f miles", lat, lon, VIK_METERS_TO_MILES(vik_coord_diff( &coord, &(s->oldcoord) )) );
1463 temp = g_strdup_printf ("Just to keep the compiler happy");
1464 g_critical("Houston, we've had a problem. distance=%d", dist_units);
1467 s->has_oldcoord = FALSE;
1470 temp = g_strdup_printf ( "%s %s", lat, lon );
1471 s->has_oldcoord = TRUE;
1474 vik_statusbar_set_message ( s->vw->viking_vs, VIK_STATUSBAR_INFO, temp );
1477 s->oldcoord = coord;
1480 vik_viewport_set_center_screen ( s->vvp, (gint) event->x, (gint) event->y );
1481 draw_update ( s->vw );
1483 return VIK_LAYER_TOOL_ACK;
1486 static VikLayerToolFuncStatus ruler_move (VikLayer *vl, GdkEventMotion *event, ruler_tool_state_t *s)
1488 VikViewport *vvp = s->vvp;
1489 VikWindow *vw = s->vw;
1495 if ( s->has_oldcoord ) {
1496 int oldx, oldy, w1, h1, w2, h2;
1497 static GdkPixmap *buf = NULL;
1498 gchar *lat=NULL, *lon=NULL;
1499 w1 = vik_viewport_get_width(vvp);
1500 h1 = vik_viewport_get_height(vvp);
1502 buf = gdk_pixmap_new ( gtk_widget_get_window(GTK_WIDGET(vvp)), w1, h1, -1 );
1504 gdk_drawable_get_size(buf, &w2, &h2);
1505 if (w1 != w2 || h1 != h2) {
1506 g_object_unref ( G_OBJECT ( buf ) );
1507 buf = gdk_pixmap_new ( gtk_widget_get_window(GTK_WIDGET(vvp)), w1, h1, -1 );
1510 vik_viewport_screen_to_coord ( vvp, (gint) event->x, (gint) event->y, &coord );
1511 vik_coord_to_latlon ( &coord, &ll );
1512 vik_viewport_coord_to_screen ( vvp, &s->oldcoord, &oldx, &oldy );
1514 gdk_draw_drawable (buf, gtk_widget_get_style(GTK_WIDGET(vvp))->black_gc,
1515 vik_viewport_get_pixmap(vvp), 0, 0, 0, 0, -1, -1);
1516 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)) );
1517 if (draw_buf_done) {
1518 static gpointer pass_along[3];
1519 pass_along[0] = gtk_widget_get_window(GTK_WIDGET(vvp));
1520 pass_along[1] = gtk_widget_get_style(GTK_WIDGET(vvp))->black_gc;
1521 pass_along[2] = buf;
1522 g_idle_add_full (G_PRIORITY_HIGH_IDLE + 10, draw_buf, pass_along, NULL);
1523 draw_buf_done = FALSE;
1525 a_coords_latlon_to_string(&ll, &lat, &lon);
1526 vik_units_distance_t dist_units = a_vik_get_units_distance ();
1527 switch (dist_units) {
1528 case VIK_UNITS_DISTANCE_KILOMETRES:
1529 temp = g_strdup_printf ( "%s %s DIFF %f meters", lat, lon, vik_coord_diff( &coord, &(s->oldcoord) ) );
1531 case VIK_UNITS_DISTANCE_MILES:
1532 temp = g_strdup_printf ( "%s %s DIFF %f miles", lat, lon, VIK_METERS_TO_MILES (vik_coord_diff( &coord, &(s->oldcoord) )) );
1535 temp = g_strdup_printf ("Just to keep the compiler happy");
1536 g_critical("Houston, we've had a problem. distance=%d", dist_units);
1538 vik_statusbar_set_message ( vw->viking_vs, VIK_STATUSBAR_INFO, temp );
1541 return VIK_LAYER_TOOL_ACK;
1544 static VikLayerToolFuncStatus ruler_release (VikLayer *vl, GdkEventButton *event, ruler_tool_state_t *s)
1546 return VIK_LAYER_TOOL_ACK;
1549 static void ruler_deactivate (VikLayer *vl, ruler_tool_state_t *s)
1551 draw_update ( s->vw );
1554 static gboolean ruler_key_press (VikLayer *vl, GdkEventKey *event, ruler_tool_state_t *s)
1556 if (event->keyval == GDK_Escape) {
1557 s->has_oldcoord = FALSE;
1558 ruler_deactivate ( vl, s );
1561 // Regardless of whether we used it, return false so other GTK things may use it
1565 static VikToolInterface ruler_tool =
1566 // NB Ctrl+Shift+R is used for Refresh (deemed more important), so use 'U' instead
1567 { { "Ruler", "vik-icon-ruler", N_("_Ruler"), "<control><shift>U", N_("Ruler Tool"), 2 },
1568 (VikToolConstructorFunc) ruler_create,
1569 (VikToolDestructorFunc) ruler_destroy,
1570 (VikToolActivationFunc) NULL,
1571 (VikToolActivationFunc) ruler_deactivate,
1572 (VikToolMouseFunc) ruler_click,
1573 (VikToolMouseMoveFunc) ruler_move,
1574 (VikToolMouseFunc) ruler_release,
1575 (VikToolKeyFunc) ruler_key_press,
1577 GDK_CURSOR_IS_PIXMAP,
1578 &cursor_ruler_pixbuf };
1579 /*** end ruler code ********************************************************/
1583 /********************************************************************************
1585 ********************************************************************************/
1590 // Track zoom bounds for zoom tool with shift modifier:
1591 gboolean bounds_active;
1594 } zoom_tool_state_t;
1597 * In case the screen size has changed
1599 static void zoomtool_resize_pixmap (zoom_tool_state_t *zts)
1603 // Allocate a drawing area the size of the viewport
1604 w1 = vik_viewport_get_width ( zts->vw->viking_vvp );
1605 h1 = vik_viewport_get_height ( zts->vw->viking_vvp );
1607 if ( !zts->pixmap ) {
1609 zts->pixmap = gdk_pixmap_new ( gtk_widget_get_window(GTK_WIDGET(zts->vw->viking_vvp)), w1, h1, -1 );
1612 gdk_drawable_get_size ( zts->pixmap, &w2, &h2 );
1614 if ( w1 != w2 || h1 != h2 ) {
1615 // Has changed - delete and recreate with new values
1616 g_object_unref ( G_OBJECT ( zts->pixmap ) );
1617 zts->pixmap = gdk_pixmap_new ( gtk_widget_get_window(GTK_WIDGET(zts->vw->viking_vvp)), w1, h1, -1 );
1621 static gpointer zoomtool_create (VikWindow *vw, VikViewport *vvp)
1623 zoom_tool_state_t *zts = g_new(zoom_tool_state_t, 1);
1628 zts->bounds_active = FALSE;
1632 static void zoomtool_destroy ( zoom_tool_state_t *zts)
1635 g_object_unref ( G_OBJECT ( zts->pixmap ) );
1639 static VikLayerToolFuncStatus zoomtool_click (VikLayer *vl, GdkEventButton *event, zoom_tool_state_t *zts)
1641 zts->vw->modified = TRUE;
1642 guint modifiers = event->state & (GDK_SHIFT_MASK | GDK_CONTROL_MASK);
1646 gint center_x = vik_viewport_get_width ( zts->vw->viking_vvp ) / 2;
1647 gint center_y = vik_viewport_get_height ( zts->vw->viking_vvp ) / 2;
1649 gboolean skip_update = FALSE;
1651 zts->bounds_active = FALSE;
1653 if ( modifiers == (GDK_CONTROL_MASK | GDK_SHIFT_MASK) ) {
1654 // This zoom is on the center position
1655 vik_viewport_set_center_screen ( zts->vw->viking_vvp, center_x, center_y );
1656 if ( event->button == 1 )
1657 vik_viewport_zoom_in (zts->vw->viking_vvp);
1658 else if ( event->button == 3 )
1659 vik_viewport_zoom_out (zts->vw->viking_vvp);
1661 else if ( modifiers == GDK_CONTROL_MASK ) {
1662 // This zoom is to recenter on the mouse position
1663 vik_viewport_set_center_screen ( zts->vw->viking_vvp, (gint) event->x, (gint) event->y );
1664 if ( event->button == 1 )
1665 vik_viewport_zoom_in (zts->vw->viking_vvp);
1666 else if ( event->button == 3 )
1667 vik_viewport_zoom_out (zts->vw->viking_vvp);
1669 else if ( modifiers == GDK_SHIFT_MASK ) {
1670 // Get start of new zoom bounds
1671 if ( event->button == 1 ) {
1672 zts->bounds_active = TRUE;
1673 zts->start_x = (gint) event->x;
1674 zts->start_y = (gint) event->y;
1679 /* make sure mouse is still over the same point on the map when we zoom */
1680 vik_viewport_screen_to_coord ( zts->vw->viking_vvp, event->x, event->y, &coord );
1681 if ( event->button == 1 )
1682 vik_viewport_zoom_in (zts->vw->viking_vvp);
1683 else if ( event->button == 3 )
1684 vik_viewport_zoom_out(zts->vw->viking_vvp);
1685 vik_viewport_coord_to_screen ( zts->vw->viking_vvp, &coord, &x, &y );
1686 vik_viewport_set_center_screen ( zts->vw->viking_vvp,
1687 center_x + (x - event->x),
1688 center_y + (y - event->y) );
1692 draw_update ( zts->vw );
1694 return VIK_LAYER_TOOL_ACK;
1697 static VikLayerToolFuncStatus zoomtool_move (VikLayer *vl, GdkEventMotion *event, zoom_tool_state_t *zts)
1699 guint modifiers = event->state & (GDK_SHIFT_MASK | GDK_CONTROL_MASK);
1701 if ( zts->bounds_active && modifiers == GDK_SHIFT_MASK ) {
1702 zoomtool_resize_pixmap ( zts );
1704 // Blank out currently drawn area
1705 gdk_draw_drawable ( zts->pixmap,
1706 gtk_widget_get_style(GTK_WIDGET(zts->vw->viking_vvp))->black_gc,
1707 vik_viewport_get_pixmap(zts->vw->viking_vvp),
1708 0, 0, 0, 0, -1, -1);
1710 // Calculate new box starting point & size in pixels
1711 int xx, yy, width, height;
1712 if ( event->y > zts->start_y ) {
1714 height = event->y-zts->start_y;
1718 height = zts->start_y-event->y;
1720 if ( event->x > zts->start_x ) {
1722 width = event->x-zts->start_x;
1726 width = zts->start_x-event->x;
1730 gdk_draw_rectangle (zts->pixmap, gtk_widget_get_style(GTK_WIDGET(zts->vw->viking_vvp))->black_gc, FALSE, xx, yy, width, height);
1732 // Only actually draw when there's time to do so
1733 if (draw_buf_done) {
1734 static gpointer pass_along[3];
1735 pass_along[0] = gtk_widget_get_window(GTK_WIDGET(zts->vw->viking_vvp));
1736 pass_along[1] = gtk_widget_get_style(GTK_WIDGET(zts->vw->viking_vvp))->black_gc;
1737 pass_along[2] = zts->pixmap;
1738 g_idle_add_full (G_PRIORITY_HIGH_IDLE + 10, draw_buf, pass_along, NULL);
1739 draw_buf_done = FALSE;
1742 return VIK_LAYER_TOOL_ACK;
1745 static VikLayerToolFuncStatus zoomtool_release (VikLayer *vl, GdkEventButton *event, zoom_tool_state_t *zts)
1747 guint modifiers = event->state & (GDK_SHIFT_MASK | GDK_CONTROL_MASK);
1749 zts->bounds_active = FALSE;
1751 // Ensure haven't just released on the exact same position
1752 // i.e. probably haven't moved the mouse at all
1753 if ( modifiers == GDK_SHIFT_MASK && !( ( event->x == zts->start_x ) && ( event->y == zts->start_y )) ) {
1755 VikCoord coord1, coord2;
1756 vik_viewport_screen_to_coord ( zts->vw->viking_vvp, zts->start_x, zts->start_y, &coord1);
1757 vik_viewport_screen_to_coord ( zts->vw->viking_vvp, event->x, event->y, &coord2);
1759 // From the extend of the bounds pick the best zoom level
1760 // c.f. trw_layer_zoom_to_show_latlons()
1761 // Maybe refactor...
1762 struct LatLon ll1, ll2;
1763 vik_coord_to_latlon(&coord1, &ll1);
1764 vik_coord_to_latlon(&coord2, &ll2);
1765 struct LatLon average = { (ll1.lat+ll2.lat)/2,
1766 (ll1.lon+ll2.lon)/2 };
1768 VikCoord new_center;
1769 vik_coord_load_from_latlon ( &new_center, vik_viewport_get_coord_mode ( zts->vw->viking_vvp ), &average );
1770 vik_viewport_set_center_coord ( zts->vw->viking_vvp, &new_center );
1772 /* Convert into definite 'smallest' and 'largest' positions */
1773 struct LatLon minmin;
1774 if ( ll1.lat < ll2.lat )
1775 minmin.lat = ll1.lat;
1777 minmin.lat = ll2.lat;
1779 struct LatLon maxmax;
1780 if ( ll1.lon > ll2.lon )
1781 maxmax.lon = ll1.lon;
1783 maxmax.lon = ll2.lon;
1785 /* Always recalculate the 'best' zoom level */
1786 gdouble zoom = VIK_VIEWPORT_MIN_ZOOM;
1787 vik_viewport_set_zoom ( zts->vw->viking_vvp, zoom );
1789 gdouble min_lat, max_lat, min_lon, max_lon;
1790 /* Should only be a maximum of about 18 iterations from min to max zoom levels */
1791 while ( zoom <= VIK_VIEWPORT_MAX_ZOOM ) {
1792 vik_viewport_get_min_max_lat_lon ( zts->vw->viking_vvp, &min_lat, &max_lat, &min_lon, &max_lon );
1793 /* NB I think the logic used in this test to determine if the bounds is within view
1794 fails if track goes across 180 degrees longitude.
1795 Hopefully that situation is not too common...
1796 Mind you viking doesn't really do edge locations to well anyway */
1797 if ( min_lat < minmin.lat &&
1798 max_lat > minmin.lat &&
1799 min_lon < maxmax.lon &&
1800 max_lon > maxmax.lon )
1801 /* Found within zoom level */
1806 vik_viewport_set_zoom ( zts->vw->viking_vvp, zoom );
1809 draw_update ( zts->vw );
1811 return VIK_LAYER_TOOL_ACK;
1814 static VikToolInterface zoom_tool =
1815 { { "Zoom", "vik-icon-zoom", N_("_Zoom"), "<control><shift>Z", N_("Zoom Tool"), 1 },
1816 (VikToolConstructorFunc) zoomtool_create,
1817 (VikToolDestructorFunc) zoomtool_destroy,
1818 (VikToolActivationFunc) NULL,
1819 (VikToolActivationFunc) NULL,
1820 (VikToolMouseFunc) zoomtool_click,
1821 (VikToolMouseMoveFunc) zoomtool_move,
1822 (VikToolMouseFunc) zoomtool_release,
1825 GDK_CURSOR_IS_PIXMAP,
1826 &cursor_zoom_pixbuf };
1827 /*** end zoom code ********************************************************/
1829 /********************************************************************************
1831 ********************************************************************************/
1832 static gpointer pantool_create (VikWindow *vw, VikViewport *vvp)
1837 static VikLayerToolFuncStatus pantool_click (VikLayer *vl, GdkEventButton *event, VikWindow *vw)
1839 vw->modified = TRUE;
1840 if ( event->button == 1 )
1841 vik_window_pan_click ( vw, event );
1843 return VIK_LAYER_TOOL_ACK;
1846 static VikLayerToolFuncStatus pantool_move (VikLayer *vl, GdkEventMotion *event, VikWindow *vw)
1848 vik_window_pan_move ( vw, event );
1849 return VIK_LAYER_TOOL_ACK;
1852 static VikLayerToolFuncStatus pantool_release (VikLayer *vl, GdkEventButton *event, VikWindow *vw)
1854 if ( event->button == 1 )
1855 vik_window_pan_release ( vw, event );
1856 return VIK_LAYER_TOOL_ACK;
1859 static VikToolInterface pan_tool =
1860 { { "Pan", "vik-icon-pan", N_("_Pan"), "<control><shift>P", N_("Pan Tool"), 0 },
1861 (VikToolConstructorFunc) pantool_create,
1862 (VikToolDestructorFunc) NULL,
1863 (VikToolActivationFunc) NULL,
1864 (VikToolActivationFunc) NULL,
1865 (VikToolMouseFunc) pantool_click,
1866 (VikToolMouseMoveFunc) pantool_move,
1867 (VikToolMouseFunc) pantool_release,
1871 /*** end pan code ********************************************************/
1873 /********************************************************************************
1875 ********************************************************************************/
1876 static gpointer selecttool_create (VikWindow *vw, VikViewport *vvp)
1878 tool_ed_t *t = g_new(tool_ed_t, 1);
1882 t->is_waypoint = FALSE;
1886 static void selecttool_destroy (tool_ed_t *t)
1894 GdkEventButton *event;
1895 tool_ed_t *tool_edit;
1898 static void click_layer_selected (VikLayer *vl, clicker *ck)
1900 /* Do nothing when function call returns true; */
1901 /* i.e. stop on first found item */
1904 if ( vik_layer_get_interface(vl->type)->select_click )
1905 ck->cont = !vik_layer_get_interface(vl->type)->select_click ( vl, ck->event, ck->vvp, ck->tool_edit );
1908 static VikLayerToolFuncStatus selecttool_click (VikLayer *vl, GdkEventButton *event, tool_ed_t *t)
1910 /* Only allow selection on primary button */
1911 if ( event->button == 1 ) {
1912 /* Enable click to apply callback to potentially all track/waypoint layers */
1913 /* Useful as we can find things that aren't necessarily in the currently selected layer */
1914 GList* gl = vik_layers_panel_get_all_layers_of_type ( t->vw->viking_vlp, VIK_LAYER_TRW, FALSE ); // Don't get invisible layers
1917 ck.vvp = t->vw->viking_vvp;
1920 g_list_foreach ( gl, (GFunc) click_layer_selected, &ck );
1923 // If nothing found then deselect & redraw screen if necessary to remove the highlight
1926 VikTreeview *vtv = vik_layers_panel_get_treeview ( t->vw->viking_vlp );
1928 if ( vik_treeview_get_selected_iter ( vtv, &iter ) ) {
1929 // Only clear if selected thing is a TrackWaypoint layer or a sublayer
1930 gint type = vik_treeview_item_get_type ( vtv, &iter );
1931 if ( type == VIK_TREEVIEW_TYPE_SUBLAYER ||
1932 VIK_LAYER(vik_treeview_item_get_pointer ( vtv, &iter ))->type == VIK_LAYER_TRW ) {
1934 vik_treeview_item_unselect ( vtv, &iter );
1935 if ( vik_window_clear_highlight ( t->vw ) )
1936 draw_update ( t->vw );
1941 else if ( ( event->button == 3 ) && ( vl && ( vl->type == VIK_LAYER_TRW ) ) ) {
1943 /* Act on currently selected item to show menu */
1944 if ( t->vw->selected_track || t->vw->selected_waypoint )
1945 if ( vik_layer_get_interface(vl->type)->show_viewport_menu )
1946 vik_layer_get_interface(vl->type)->show_viewport_menu ( vl, event, t->vw->viking_vvp );
1949 return VIK_LAYER_TOOL_ACK;
1952 static VikLayerToolFuncStatus selecttool_move (VikLayer *vl, GdkEventButton *event, tool_ed_t *t)
1954 /* Only allow selection on primary button */
1955 if ( event->button == 1 ) {
1956 // Don't care about vl here
1958 if ( vik_layer_get_interface(VIK_LAYER_TRW)->select_move )
1959 vik_layer_get_interface(VIK_LAYER_TRW)->select_move ( vl, event, t->vvp, t );
1961 return VIK_LAYER_TOOL_ACK;
1964 static VikLayerToolFuncStatus selecttool_release (VikLayer *vl, GdkEventButton *event, tool_ed_t *t)
1966 /* Only allow selection on primary button */
1967 if ( event->button == 1 ) {
1968 // Don't care about vl here
1970 if ( vik_layer_get_interface(VIK_LAYER_TRW)->select_release )
1971 vik_layer_get_interface(VIK_LAYER_TRW)->select_release ( (VikLayer*)t->vtl, event, t->vvp, t );
1973 return VIK_LAYER_TOOL_ACK;
1976 static VikToolInterface select_tool =
1977 { { "Select", "vik-icon-select", N_("_Select"), "<control><shift>S", N_("Select Tool"), 3 },
1978 (VikToolConstructorFunc) selecttool_create,
1979 (VikToolDestructorFunc) selecttool_destroy,
1980 (VikToolActivationFunc) NULL,
1981 (VikToolActivationFunc) NULL,
1982 (VikToolMouseFunc) selecttool_click,
1983 (VikToolMouseMoveFunc) selecttool_move,
1984 (VikToolMouseFunc) selecttool_release,
1985 (VikToolKeyFunc) NULL,
1990 /*** end select tool code ********************************************************/
1992 static void draw_pan_cb ( GtkAction *a, VikWindow *vw )
1994 // Since the treeview cell editting intercepts standard keyboard handlers, it means we can receive events here
1995 // Thus if currently editting, ensure we don't move the viewport when Ctrl+<arrow> is received
1996 VikLayer *sel = vik_layers_panel_get_selected ( vw->viking_vlp );
1997 if ( sel && vik_treeview_get_editing ( sel->vt ) )
2000 if (!strcmp(gtk_action_get_name(a), "PanNorth")) {
2001 vik_viewport_set_center_screen ( vw->viking_vvp, vik_viewport_get_width(vw->viking_vvp)/2, 0 );
2002 } else if (!strcmp(gtk_action_get_name(a), "PanEast")) {
2003 vik_viewport_set_center_screen ( vw->viking_vvp, vik_viewport_get_width(vw->viking_vvp), vik_viewport_get_height(vw->viking_vvp)/2 );
2004 } else if (!strcmp(gtk_action_get_name(a), "PanSouth")) {
2005 vik_viewport_set_center_screen ( vw->viking_vvp, vik_viewport_get_width(vw->viking_vvp)/2, vik_viewport_get_height(vw->viking_vvp) );
2006 } else if (!strcmp(gtk_action_get_name(a), "PanWest")) {
2007 vik_viewport_set_center_screen ( vw->viking_vvp, 0, vik_viewport_get_height(vw->viking_vvp)/2 );
2012 static void full_screen_cb ( GtkAction *a, VikWindow *vw )
2014 GtkWidget *check_box = gtk_ui_manager_get_widget ( vw->uim, "/ui/MainMenu/View/FullScreen" );
2015 g_assert(check_box);
2016 gboolean state = gtk_check_menu_item_get_active ( GTK_CHECK_MENU_ITEM(check_box));
2018 gtk_window_fullscreen ( GTK_WINDOW(vw) );
2020 gtk_window_unfullscreen ( GTK_WINDOW(vw) );
2023 static void draw_zoom_cb ( GtkAction *a, VikWindow *vw )
2027 if (!strcmp(gtk_action_get_name(a), "ZoomIn")) {
2030 else if (!strcmp(gtk_action_get_name(a), "ZoomOut")) {
2033 else if (!strcmp(gtk_action_get_name(a), "Zoom0.25")) {
2036 else if (!strcmp(gtk_action_get_name(a), "Zoom0.5")) {
2040 gchar *s = (gchar *)gtk_action_get_name(a);
2046 case -3: vik_viewport_zoom_in ( vw->viking_vvp ); break;
2047 case -4: vik_viewport_zoom_out ( vw->viking_vvp ); break;
2048 case -1: vik_viewport_set_zoom ( vw->viking_vvp, 0.5 ); break;
2049 case -2: vik_viewport_set_zoom ( vw->viking_vvp, 0.25 ); break;
2050 default: vik_viewport_set_zoom ( vw->viking_vvp, what );
2055 static void draw_goto_cb ( GtkAction *a, VikWindow *vw )
2057 VikCoord new_center;
2059 if (!strcmp(gtk_action_get_name(a), "GotoLL")) {
2060 struct LatLon ll, llold;
2061 vik_coord_to_latlon ( vik_viewport_get_center ( vw->viking_vvp ), &llold );
2062 if ( a_dialog_goto_latlon ( GTK_WINDOW(vw), &ll, &llold ) )
2063 vik_coord_load_from_latlon ( &new_center, vik_viewport_get_coord_mode(vw->viking_vvp), &ll );
2067 else if (!strcmp(gtk_action_get_name(a), "GotoUTM")) {
2068 struct UTM utm, utmold;
2069 vik_coord_to_utm ( vik_viewport_get_center ( vw->viking_vvp ), &utmold );
2070 if ( a_dialog_goto_utm ( GTK_WINDOW(vw), &utm, &utmold ) )
2071 vik_coord_load_from_utm ( &new_center, vik_viewport_get_coord_mode(vw->viking_vvp), &utm );
2076 g_critical("Houston, we've had a problem.");
2080 vik_viewport_set_center_coord ( vw->viking_vvp, &new_center );
2085 * Refresh maps displayed
2087 static void draw_refresh_cb ( GtkAction *a, VikWindow *vw )
2089 // Only get 'new' maps
2090 simple_map_update ( vw, TRUE );
2093 static void menu_addlayer_cb ( GtkAction *a, VikWindow *vw )
2095 VikLayerTypeEnum type;
2096 for ( type = 0; type < VIK_LAYER_NUM_TYPES; type++ ) {
2097 if (!strcmp(vik_layer_get_interface(type)->name, gtk_action_get_name(a))) {
2098 if ( vik_layers_panel_new_layer ( vw->viking_vlp, type ) ) {
2100 vw->modified = TRUE;
2106 static void menu_copy_layer_cb ( GtkAction *a, VikWindow *vw )
2108 a_clipboard_copy_selected ( vw->viking_vlp );
2111 static void menu_cut_layer_cb ( GtkAction *a, VikWindow *vw )
2113 vik_layers_panel_cut_selected ( vw->viking_vlp );
2114 vw->modified = TRUE;
2117 static void menu_paste_layer_cb ( GtkAction *a, VikWindow *vw )
2119 if ( vik_layers_panel_paste_selected ( vw->viking_vlp ) )
2121 vw->modified = TRUE;
2125 static void menu_properties_cb ( GtkAction *a, VikWindow *vw )
2127 if ( ! vik_layers_panel_properties ( vw->viking_vlp ) )
2128 a_dialog_info_msg ( GTK_WINDOW(vw), _("You must select a layer to show its properties.") );
2131 static void help_help_cb ( GtkAction *a, VikWindow *vw )
2134 ShellExecute(NULL, "open", ""PACKAGE".pdf", NULL, NULL, SW_SHOWNORMAL);
2137 uri = g_strdup_printf("ghelp:%s", PACKAGE);
2138 GError *error = NULL;
2139 gboolean show = gtk_show_uri (NULL, uri, GDK_CURRENT_TIME, &error);
2140 if ( !show && !error )
2141 // No error to show, so unlikely this will get called
2142 a_dialog_error_msg ( GTK_WINDOW(vw), _("The help system is not available.") );
2145 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 );
2146 g_error_free ( error );
2149 #endif /* WINDOWS */
2152 static void help_about_cb ( GtkAction *a, VikWindow *vw )
2154 a_dialog_about(GTK_WINDOW(vw));
2157 static void menu_delete_layer_cb ( GtkAction *a, VikWindow *vw )
2159 if ( vik_layers_panel_get_selected ( vw->viking_vlp ) )
2161 vik_layers_panel_delete_selected ( vw->viking_vlp );
2162 vw->modified = TRUE;
2165 a_dialog_info_msg ( GTK_WINDOW(vw), _("You must select a layer to delete.") );
2168 static void view_side_panel_cb ( GtkAction *a, VikWindow *vw )
2170 GtkWidget *check_box = gtk_ui_manager_get_widget ( vw->uim, "/ui/MainMenu/View/SetShow/ViewSidePanel" );
2171 g_assert(check_box);
2172 gboolean state = gtk_check_menu_item_get_active ( GTK_CHECK_MENU_ITEM(check_box));
2174 gtk_widget_show(GTK_WIDGET(vw->viking_vlp));
2176 gtk_widget_hide(GTK_WIDGET(vw->viking_vlp));
2179 static void view_statusbar_cb ( GtkAction *a, VikWindow *vw )
2181 GtkWidget *check_box = gtk_ui_manager_get_widget ( vw->uim, "/ui/MainMenu/View/SetShow/ViewStatusBar" );
2184 gboolean state = gtk_check_menu_item_get_active ( GTK_CHECK_MENU_ITEM(check_box) );
2186 gtk_widget_show ( GTK_WIDGET(vw->viking_vs) );
2188 gtk_widget_hide ( GTK_WIDGET(vw->viking_vs) );
2191 static void view_toolbar_cb ( GtkAction *a, VikWindow *vw )
2193 GtkWidget *check_box = gtk_ui_manager_get_widget ( vw->uim, "/ui/MainMenu/View/SetShow/ViewToolbar" );
2196 gboolean state = gtk_check_menu_item_get_active ( GTK_CHECK_MENU_ITEM(check_box) );
2198 gtk_widget_show ( GTK_WIDGET(vw->toolbar) );
2200 gtk_widget_hide ( GTK_WIDGET(vw->toolbar) );
2203 static void view_main_menu_cb ( GtkAction *a, VikWindow *vw )
2205 GtkWidget *check_box = gtk_ui_manager_get_widget ( vw->uim, "/ui/MainMenu/View/SetShow/ViewMainMenu" );
2208 gboolean state = gtk_check_menu_item_get_active ( GTK_CHECK_MENU_ITEM(check_box) );
2210 gtk_widget_hide ( gtk_ui_manager_get_widget ( vw->uim, "/ui/MainMenu" ) );
2212 gtk_widget_show ( gtk_ui_manager_get_widget ( vw->uim, "/ui/MainMenu" ) );
2215 /***************************************
2216 ** tool management routines
2218 ***************************************/
2220 static toolbox_tools_t* toolbox_create(VikWindow *vw)
2222 toolbox_tools_t *vt = g_new(toolbox_tools_t, 1);
2225 vt->active_tool = -1;
2230 static void toolbox_add_tool(toolbox_tools_t *vt, VikToolInterface *vti, gint layer_type )
2232 vt->tools = g_renew(toolbox_tool_t, vt->tools, vt->n_tools+1);
2233 vt->tools[vt->n_tools].ti = *vti;
2234 vt->tools[vt->n_tools].layer_type = layer_type;
2236 vt->tools[vt->n_tools].state = vti->create(vt->vw, vt->vw->viking_vvp);
2239 vt->tools[vt->n_tools].state = NULL;
2244 static int toolbox_get_tool(toolbox_tools_t *vt, const gchar *tool_name)
2247 for (i=0; i<vt->n_tools; i++) {
2248 if (!strcmp(tool_name, vt->tools[i].ti.radioActionEntry.name)) {
2255 static void toolbox_activate(toolbox_tools_t *vt, const gchar *tool_name)
2257 int tool = toolbox_get_tool(vt, tool_name);
2258 toolbox_tool_t *t = &vt->tools[tool];
2259 VikLayer *vl = vik_layers_panel_get_selected ( vt->vw->viking_vlp );
2261 if (tool == vt->n_tools) {
2262 g_critical("trying to activate a non-existent tool...");
2265 /* is the tool already active? */
2266 if (vt->active_tool == tool) {
2270 if (vt->active_tool != -1) {
2271 if (vt->tools[vt->active_tool].ti.deactivate) {
2272 vt->tools[vt->active_tool].ti.deactivate(NULL, vt->tools[vt->active_tool].state);
2275 if (t->ti.activate) {
2276 t->ti.activate(vl, t->state);
2278 vt->active_tool = tool;
2281 static const GdkCursor *toolbox_get_cursor(toolbox_tools_t *vt, const gchar *tool_name)
2283 int tool = toolbox_get_tool(vt, tool_name);
2284 toolbox_tool_t *t = &vt->tools[tool];
2285 if (t->ti.cursor == NULL) {
2286 if (t->ti.cursor_type == GDK_CURSOR_IS_PIXMAP && t->ti.cursor_data != NULL) {
2287 GError *cursor_load_err = NULL;
2288 GdkPixbuf *cursor_pixbuf = gdk_pixbuf_from_pixdata (t->ti.cursor_data, FALSE, &cursor_load_err);
2289 /* TODO: settable offeset */
2290 t->ti.cursor = gdk_cursor_new_from_pixbuf ( gdk_display_get_default(), cursor_pixbuf, 3, 3 );
2291 g_object_unref ( G_OBJECT(cursor_pixbuf) );
2293 t->ti.cursor = gdk_cursor_new ( t->ti.cursor_type );
2296 return t->ti.cursor;
2299 static void toolbox_click (toolbox_tools_t *vt, GdkEventButton *event)
2301 VikLayer *vl = vik_layers_panel_get_selected ( vt->vw->viking_vlp );
2302 if (vt->active_tool != -1 && vt->tools[vt->active_tool].ti.click) {
2303 gint ltype = vt->tools[vt->active_tool].layer_type;
2304 if ( ltype == TOOL_LAYER_TYPE_NONE || (vl && ltype == vl->type) )
2305 vt->tools[vt->active_tool].ti.click(vl, event, vt->tools[vt->active_tool].state);
2309 static void toolbox_move (toolbox_tools_t *vt, GdkEventMotion *event)
2311 VikLayer *vl = vik_layers_panel_get_selected ( vt->vw->viking_vlp );
2312 if (vt->active_tool != -1 && vt->tools[vt->active_tool].ti.move) {
2313 gint ltype = vt->tools[vt->active_tool].layer_type;
2314 if ( ltype == TOOL_LAYER_TYPE_NONE || (vl && ltype == vl->type) )
2315 if ( VIK_LAYER_TOOL_ACK_GRAB_FOCUS == vt->tools[vt->active_tool].ti.move(vl, event, vt->tools[vt->active_tool].state) )
2316 gtk_widget_grab_focus ( GTK_WIDGET(vt->vw->viking_vvp) );
2320 static void toolbox_release (toolbox_tools_t *vt, GdkEventButton *event)
2322 VikLayer *vl = vik_layers_panel_get_selected ( vt->vw->viking_vlp );
2323 if (vt->active_tool != -1 && vt->tools[vt->active_tool].ti.release ) {
2324 gint ltype = vt->tools[vt->active_tool].layer_type;
2325 if ( ltype == TOOL_LAYER_TYPE_NONE || (vl && ltype == vl->type) )
2326 vt->tools[vt->active_tool].ti.release(vl, event, vt->tools[vt->active_tool].state);
2329 /** End tool management ************************************/
2331 void vik_window_enable_layer_tool ( VikWindow *vw, gint layer_id, gint tool_id )
2333 gtk_action_activate ( gtk_action_group_get_action ( vw->action_group, vik_layer_get_interface(layer_id)->tools[tool_id].radioActionEntry.name ) );
2336 /* this function gets called whenever a toolbar tool is clicked */
2337 static void menu_tool_cb ( GtkAction *old, GtkAction *a, VikWindow *vw )
2339 /* White Magic, my friends ... White Magic... */
2341 toolbox_activate(vw->vt, gtk_action_get_name(a));
2343 vw->viewport_cursor = (GdkCursor *)toolbox_get_cursor(vw->vt, gtk_action_get_name(a));
2345 if ( gtk_widget_get_window(GTK_WIDGET(vw->viking_vvp)) )
2346 /* We set cursor, even if it is NULL: it resets to default */
2347 gdk_window_set_cursor ( gtk_widget_get_window(GTK_WIDGET(vw->viking_vvp)), vw->viewport_cursor );
2349 if (!strcmp(gtk_action_get_name(a), "Pan")) {
2350 vw->current_tool = TOOL_PAN;
2352 else if (!strcmp(gtk_action_get_name(a), "Zoom")) {
2353 vw->current_tool = TOOL_ZOOM;
2355 else if (!strcmp(gtk_action_get_name(a), "Ruler")) {
2356 vw->current_tool = TOOL_RULER;
2358 else if (!strcmp(gtk_action_get_name(a), "Select")) {
2359 vw->current_tool = TOOL_SELECT;
2362 /* TODO: only enable tools from active layer */
2363 VikLayerTypeEnum layer_id;
2364 for (layer_id=0; layer_id<VIK_LAYER_NUM_TYPES; layer_id++) {
2365 for ( tool_id = 0; tool_id < vik_layer_get_interface(layer_id)->tools_count; tool_id++ ) {
2366 if (!strcmp(vik_layer_get_interface(layer_id)->tools[tool_id].radioActionEntry.name, gtk_action_get_name(a))) {
2367 vw->current_tool = TOOL_LAYER;
2368 vw->tool_layer_id = layer_id;
2369 vw->tool_tool_id = tool_id;
2374 draw_status_tool ( vw );
2377 static void window_set_filename ( VikWindow *vw, const gchar *filename )
2382 g_free ( vw->filename );
2383 if ( filename == NULL )
2385 vw->filename = NULL;
2389 vw->filename = g_strdup(filename);
2392 /* Refresh window's title */
2393 file = window_get_filename ( vw );
2394 title = g_strdup_printf( "%s - Viking", file );
2395 gtk_window_set_title ( GTK_WINDOW(vw), title );
2399 static const gchar *window_get_filename ( VikWindow *vw )
2401 return vw->filename ? a_file_basename ( vw->filename ) : _("Untitled");
2404 GtkWidget *vik_window_get_drawmode_button ( VikWindow *vw, VikViewportDrawMode mode )
2406 GtkWidget *mode_button;
2409 #ifdef VIK_CONFIG_EXPEDIA
2410 case VIK_VIEWPORT_DRAWMODE_EXPEDIA: buttonname = "/ui/MainMenu/View/ModeExpedia"; break;
2412 case VIK_VIEWPORT_DRAWMODE_MERCATOR: buttonname = "/ui/MainMenu/View/ModeMercator"; break;
2413 case VIK_VIEWPORT_DRAWMODE_LATLON: buttonname = "/ui/MainMenu/View/ModeLatLon"; break;
2414 default: buttonname = "/ui/MainMenu/View/ModeUTM";
2416 mode_button = gtk_ui_manager_get_widget ( vw->uim, buttonname );
2417 g_assert ( mode_button );
2422 * vik_window_get_pan_move:
2423 * @vw: some VikWindow
2425 * Retrieves @vw's pan_move.
2427 * Should be removed as soon as possible.
2429 * Returns: @vw's pan_move
2433 gboolean vik_window_get_pan_move ( VikWindow *vw )
2435 return vw->pan_move;
2438 static void on_activate_recent_item (GtkRecentChooser *chooser,
2443 filename = gtk_recent_chooser_get_current_uri (chooser);
2444 if (filename != NULL)
2446 GFile *file = g_file_new_for_uri ( filename );
2447 gchar *path = g_file_get_path ( file );
2448 g_object_unref ( file );
2449 if ( self->filename )
2451 GSList *filenames = NULL;
2452 filenames = g_slist_append ( filenames, path );
2453 g_signal_emit ( G_OBJECT(self), window_signals[VW_OPENWINDOW_SIGNAL], 0, filenames );
2454 // NB: GSList & contents are freed by main.open_window
2457 vik_window_open_file ( self, path, TRUE );
2465 static void setup_recent_files (VikWindow *self)
2467 GtkRecentManager *manager;
2468 GtkRecentFilter *filter;
2469 GtkWidget *menu, *menu_item;
2471 filter = gtk_recent_filter_new ();
2472 /* gtk_recent_filter_add_application (filter, g_get_application_name()); */
2473 gtk_recent_filter_add_group(filter, "viking");
2475 manager = gtk_recent_manager_get_default ();
2476 menu = gtk_recent_chooser_menu_new_for_manager (manager);
2477 gtk_recent_chooser_set_sort_type (GTK_RECENT_CHOOSER (menu), GTK_RECENT_SORT_MRU);
2478 gtk_recent_chooser_add_filter (GTK_RECENT_CHOOSER (menu), filter);
2480 menu_item = gtk_ui_manager_get_widget (self->uim, "/ui/MainMenu/File/OpenRecentFile");
2481 gtk_menu_item_set_submenu (GTK_MENU_ITEM (menu_item), menu);
2483 g_signal_connect (G_OBJECT (menu), "item-activated",
2484 G_CALLBACK (on_activate_recent_item), (gpointer) self);
2487 static void update_recently_used_document(const gchar *filename)
2489 /* Update Recently Used Document framework */
2490 GtkRecentManager *manager = gtk_recent_manager_get_default();
2491 GtkRecentData *recent_data = g_slice_new (GtkRecentData);
2492 gchar *groups[] = {"viking", NULL};
2493 GFile *file = g_file_new_for_commandline_arg(filename);
2494 gchar *uri = g_file_get_uri(file);
2495 gchar *basename = g_path_get_basename(filename);
2496 g_object_unref(file);
2499 recent_data->display_name = basename;
2500 recent_data->description = NULL;
2501 recent_data->mime_type = "text/x-gps-data";
2502 recent_data->app_name = (gchar *) g_get_application_name ();
2503 recent_data->app_exec = g_strjoin (" ", g_get_prgname (), "%f", NULL);
2504 recent_data->groups = groups;
2505 recent_data->is_private = FALSE;
2506 if (!gtk_recent_manager_add_full (manager, uri, recent_data))
2508 g_warning (_("Unable to add '%s' to the list of recently used documents"), uri);
2513 g_free (recent_data->app_exec);
2514 g_slice_free (GtkRecentData, recent_data);
2518 * Call this before doing things that may take a long time and otherwise not show any other feedback
2519 * such as loading and saving files
2521 void vik_window_set_busy_cursor ( VikWindow *vw )
2523 gdk_window_set_cursor ( gtk_widget_get_window(GTK_WIDGET(vw)), vw->busy_cursor );
2524 // Viewport has a separate cursor
2525 gdk_window_set_cursor ( gtk_widget_get_window(GTK_WIDGET(vw->viking_vvp)), vw->busy_cursor );
2526 // Ensure cursor updated before doing stuff
2527 while( gtk_events_pending() )
2528 gtk_main_iteration();
2531 void vik_window_clear_busy_cursor ( VikWindow *vw )
2533 gdk_window_set_cursor ( gtk_widget_get_window(GTK_WIDGET(vw)), NULL );
2534 // Restore viewport cursor
2535 gdk_window_set_cursor ( gtk_widget_get_window(GTK_WIDGET(vw->viking_vvp)), vw->viewport_cursor );
2538 void vik_window_open_file ( VikWindow *vw, const gchar *filename, gboolean change_filename )
2540 vik_window_set_busy_cursor ( vw );
2541 vw->loaded_type = a_file_load ( vik_layers_panel_get_top_layer(vw->viking_vlp), vw->viking_vvp, filename );
2542 switch ( vw->loaded_type )
2544 case LOAD_TYPE_READ_FAILURE:
2545 a_dialog_error_msg ( GTK_WINDOW(vw), _("The file you requested could not be opened.") );
2547 case LOAD_TYPE_GPSBABEL_FAILURE:
2548 a_dialog_error_msg ( GTK_WINDOW(vw), _("GPSBabel is required to load files of this type or GPSBabel encountered problems.") );
2550 case LOAD_TYPE_GPX_FAILURE:
2551 a_dialog_error_msg_extra ( GTK_WINDOW(vw), _("Unable to load malformed GPX file %s"), filename );
2553 case LOAD_TYPE_UNSUPPORTED_FAILURE:
2554 a_dialog_error_msg_extra ( GTK_WINDOW(vw), _("Unsupported file type for %s"), filename );
2556 case LOAD_TYPE_VIK_FAILURE_NON_FATAL:
2558 // Since we can process .vik files with issues just show a warning in the status bar
2559 // Not that a user can do much about it... or tells them what this issue is yet...
2560 gchar *msg = g_strdup_printf (_("WARNING: issues encountered loading %s"), a_file_basename (filename) );
2561 vik_statusbar_set_message ( vw->viking_vs, VIK_STATUSBAR_INFO, msg );
2564 // No break, carry on to show any data
2565 case LOAD_TYPE_VIK_SUCCESS:
2567 GtkWidget *mode_button;
2569 if ( change_filename )
2570 window_set_filename ( vw, filename );
2571 mode_button = vik_window_get_drawmode_button ( vw, vik_viewport_get_drawmode ( vw->viking_vvp ) );
2572 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. */
2573 gtk_check_menu_item_set_active ( GTK_CHECK_MENU_ITEM(mode_button), TRUE );
2574 vw->only_updating_coord_mode_ui = FALSE;
2576 vik_layers_panel_change_coord_mode ( vw->viking_vlp, vik_viewport_get_coord_mode ( vw->viking_vvp ) );
2578 mode_button = gtk_ui_manager_get_widget ( vw->uim, "/ui/MainMenu/View/SetShow/ShowScale" );
2579 g_assert ( mode_button );
2580 gtk_check_menu_item_set_active ( GTK_CHECK_MENU_ITEM(mode_button),vik_viewport_get_draw_scale(vw->viking_vvp) );
2582 mode_button = gtk_ui_manager_get_widget ( vw->uim, "/ui/MainMenu/View/SetShow/ShowCenterMark" );
2583 g_assert ( mode_button );
2584 gtk_check_menu_item_set_active ( GTK_CHECK_MENU_ITEM(mode_button),vik_viewport_get_draw_centermark(vw->viking_vvp) );
2586 mode_button = gtk_ui_manager_get_widget ( vw->uim, "/ui/MainMenu/View/SetShow/ShowHighlight" );
2587 g_assert ( mode_button );
2588 gtk_check_menu_item_set_active ( GTK_CHECK_MENU_ITEM(mode_button),vik_viewport_get_draw_highlight (vw->viking_vvp) );
2590 //case LOAD_TYPE_OTHER_SUCCESS:
2592 update_recently_used_document(filename);
2597 vik_window_clear_busy_cursor ( vw );
2600 static void load_file ( GtkAction *a, VikWindow *vw )
2602 GSList *files = NULL;
2603 GSList *cur_file = NULL;
2605 if (!strcmp(gtk_action_get_name(a), "Open")) {
2608 else if (!strcmp(gtk_action_get_name(a), "Append")) {
2612 g_critical("Houston, we've had a problem.");
2616 if ( ! vw->open_dia )
2618 vw->open_dia = gtk_file_chooser_dialog_new (_("Please select a GPS data file to open. "),
2620 GTK_FILE_CHOOSER_ACTION_OPEN,
2621 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
2622 GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT,
2624 gchar *cwd = g_get_current_dir();
2626 gtk_file_chooser_set_current_folder ( GTK_FILE_CHOOSER(vw->open_dia), cwd );
2630 GtkFileFilter *filter;
2631 // NB file filters are listed this way for alphabetical ordering
2632 #ifdef VIK_CONFIG_GEOCACHES
2633 filter = gtk_file_filter_new ();
2634 gtk_file_filter_set_name( filter, _("Geocaching") );
2635 gtk_file_filter_add_pattern ( filter, "*.loc" ); // No MIME type available
2636 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, _("Google Earth") );
2641 gtk_file_filter_add_mime_type ( filter, "application/vnd.google-earth.kml+xml");
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, _("GPX") );
2646 gtk_file_filter_add_pattern ( filter, "*.gpx" ); // No MIME type available
2647 gtk_file_chooser_add_filter (GTK_FILE_CHOOSER(vw->open_dia), filter);
2649 filter = gtk_file_filter_new ();
2650 gtk_file_filter_set_name( filter, _("Viking") );
2651 gtk_file_filter_add_pattern ( filter, "*.vik" );
2652 gtk_file_filter_add_pattern ( filter, "*.viking" );
2653 gtk_file_chooser_add_filter (GTK_FILE_CHOOSER(vw->open_dia), filter);
2655 // NB could have filters for gpspoint (*.gps,*.gpsoint?) + gpsmapper (*.gsm,*.gpsmapper?)
2656 // However assume this are barely used and thus not worthy of inclusion
2657 // as they'll just make the options too many and have no clear file pattern
2658 // one can always use the all option
2659 filter = gtk_file_filter_new ();
2660 gtk_file_filter_set_name( filter, _("All") );
2661 gtk_file_filter_add_pattern ( filter, "*" );
2662 gtk_file_chooser_add_filter (GTK_FILE_CHOOSER(vw->open_dia), filter);
2663 // Default to any file - same as before open filters were added
2664 gtk_file_chooser_set_filter (GTK_FILE_CHOOSER(vw->open_dia), filter);
2666 gtk_file_chooser_set_select_multiple ( GTK_FILE_CHOOSER(vw->open_dia), TRUE );
2667 gtk_window_set_transient_for ( GTK_WINDOW(vw->open_dia), GTK_WINDOW(vw) );
2668 gtk_window_set_destroy_with_parent ( GTK_WINDOW(vw->open_dia), TRUE );
2670 if ( gtk_dialog_run ( GTK_DIALOG(vw->open_dia) ) == GTK_RESPONSE_ACCEPT )
2672 gtk_widget_hide ( vw->open_dia );
2673 #ifdef VIKING_PROMPT_IF_MODIFIED
2674 if ( (vw->modified || vw->filename) && newwindow )
2676 if ( vw->filename && newwindow )
2678 g_signal_emit ( G_OBJECT(vw), window_signals[VW_OPENWINDOW_SIGNAL], 0, gtk_file_chooser_get_filenames (GTK_FILE_CHOOSER(vw->open_dia) ) );
2680 files = gtk_file_chooser_get_filenames (GTK_FILE_CHOOSER(vw->open_dia) );
2681 gboolean change_fn = newwindow && (g_slist_length(files)==1); /* only change fn if one file */
2682 gboolean first_vik_file = TRUE;
2684 while ( cur_file ) {
2686 gchar *file_name = cur_file->data;
2687 if ( newwindow && check_file_magic_vik ( file_name ) ) {
2688 // Load first of many .vik files in current window
2689 if ( first_vik_file ) {
2690 vik_window_open_file ( vw, file_name, TRUE );
2691 first_vik_file = FALSE;
2694 // Load each subsequent .vik file in a separate window
2695 VikWindow *newvw = vik_window_new_window ();
2697 vik_window_open_file ( newvw, file_name, TRUE );
2702 vik_window_open_file ( vw, file_name, change_fn );
2705 cur_file = g_slist_next (cur_file);
2707 g_slist_free (files);
2711 gtk_widget_hide ( vw->open_dia );
2714 static gboolean save_file_as ( GtkAction *a, VikWindow *vw )
2716 gboolean rv = FALSE;
2718 if ( ! vw->save_dia )
2720 vw->save_dia = gtk_file_chooser_dialog_new (_("Save as Viking File."),
2722 GTK_FILE_CHOOSER_ACTION_SAVE,
2723 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
2724 GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT,
2726 gchar *cwd = g_get_current_dir();
2728 gtk_file_chooser_set_current_folder ( GTK_FILE_CHOOSER(vw->save_dia), cwd );
2732 GtkFileFilter *filter;
2733 filter = gtk_file_filter_new ();
2734 gtk_file_filter_set_name( filter, _("All") );
2735 gtk_file_filter_add_pattern ( filter, "*" );
2736 gtk_file_chooser_add_filter (GTK_FILE_CHOOSER(vw->save_dia), filter);
2738 filter = gtk_file_filter_new ();
2739 gtk_file_filter_set_name( filter, _("Viking") );
2740 gtk_file_filter_add_pattern ( filter, "*.vik" );
2741 gtk_file_filter_add_pattern ( filter, "*.viking" );
2742 gtk_file_chooser_add_filter (GTK_FILE_CHOOSER(vw->save_dia), filter);
2743 // Default to a Viking file
2744 gtk_file_chooser_set_filter (GTK_FILE_CHOOSER(vw->save_dia), filter);
2746 gtk_window_set_transient_for ( GTK_WINDOW(vw->save_dia), GTK_WINDOW(vw) );
2747 gtk_window_set_destroy_with_parent ( GTK_WINDOW(vw->save_dia), TRUE );
2749 // Auto append / replace extension with '.vik' to the suggested file name as it's going to be a Viking File
2750 gchar* auto_save_name = g_strdup ( window_get_filename ( vw ) );
2751 if ( ! check_file_ext ( auto_save_name, ".vik" ) )
2752 auto_save_name = g_strconcat ( auto_save_name, ".vik", NULL );
2754 gtk_file_chooser_set_current_name (GTK_FILE_CHOOSER(vw->save_dia), auto_save_name);
2756 while ( gtk_dialog_run ( GTK_DIALOG(vw->save_dia) ) == GTK_RESPONSE_ACCEPT )
2758 fn = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER(vw->save_dia) );
2759 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 ) ) )
2761 window_set_filename ( vw, fn );
2762 rv = window_save ( vw );
2763 vw->modified = FALSE;
2767 g_free ( auto_save_name );
2768 gtk_widget_hide ( vw->save_dia );
2772 static gboolean window_save ( VikWindow *vw )
2774 vik_window_set_busy_cursor ( vw );
2775 gboolean success = TRUE;
2777 if ( a_file_save ( vik_layers_panel_get_top_layer ( vw->viking_vlp ), vw->viking_vvp, vw->filename ) )
2779 update_recently_used_document ( vw->filename );
2783 a_dialog_error_msg ( GTK_WINDOW(vw), _("The filename you requested could not be opened for writing.") );
2786 vik_window_clear_busy_cursor ( vw );
2790 static gboolean save_file ( GtkAction *a, VikWindow *vw )
2792 if ( ! vw->filename )
2793 return save_file_as ( NULL, vw );
2796 vw->modified = FALSE;
2797 return window_save ( vw );
2804 * Export all TRW Layers in the list to individual files in the specified directory
2806 * Returns: %TRUE on success
2808 static gboolean export_to ( VikWindow *vw, GList *gl, VikFileType_t vft, const gchar *dir, const gchar *extension )
2810 gboolean success = TRUE;
2812 gint export_count = 0;
2814 vik_window_set_busy_cursor ( vw );
2818 gchar *fn = g_strconcat ( dir, G_DIR_SEPARATOR_S, VIK_LAYER(gl->data)->name, extension, NULL );
2820 // Some protection in attempting to write too many same named files
2821 // As this will get horribly slow...
2822 gboolean safe = FALSE;
2824 while ( ii < 5000 ) {
2825 if ( g_file_test ( fn, G_FILE_TEST_EXISTS ) ) {
2828 fn = g_strdup_printf ( "%s%s%s#%03d%s", dir, G_DIR_SEPARATOR_S, VIK_LAYER(gl->data)->name, ii, extension );
2839 // NB: We allow exporting empty layers
2841 gboolean this_success = a_file_export ( VIK_TRW_LAYER(gl->data), fn, vft, NULL, TRUE );
2843 // Show some progress
2844 if ( this_success ) {
2846 gchar *message = g_strconcat ( _("Exporting to file: "), fn, NULL );
2847 vik_statusbar_set_message ( vw->viking_vs, VIK_STATUSBAR_INFO, message );
2848 while ( gtk_events_pending() )
2849 gtk_main_iteration ();
2853 success = success && this_success;
2857 gl = g_list_next ( gl );
2860 vik_window_clear_busy_cursor ( vw );
2862 // Confirm what happened.
2863 gchar *message = g_strdup_printf ( _("Exported files: %d"), export_count );
2864 vik_statusbar_set_message ( vw->viking_vs, VIK_STATUSBAR_INFO, message );
2870 static void export_to_common ( VikWindow *vw, VikFileType_t vft, const gchar *extension )
2872 GList *gl = vik_layers_panel_get_all_layers_of_type ( vw->viking_vlp, VIK_LAYER_TRW, TRUE );
2875 a_dialog_info_msg ( GTK_WINDOW(vw), _("Nothing to Export!") );
2879 GtkWidget *dialog = gtk_dialog_new_with_buttons ( _("Export to directory"),
2881 GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
2883 GTK_RESPONSE_REJECT,
2885 GTK_RESPONSE_ACCEPT,
2888 GtkWidget *gw = gtk_file_chooser_widget_new ( GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER );
2889 gtk_box_pack_start ( GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), gw, TRUE, TRUE, 0 );
2891 // try to make it a nice size - otherwise seems to default to something impractically small
2892 gtk_window_set_default_size ( GTK_WINDOW(dialog), 600, 300 );
2894 gtk_widget_show_all ( dialog );
2896 if ( gtk_dialog_run ( GTK_DIALOG(dialog) ) == GTK_RESPONSE_ACCEPT ) {
2897 gchar *dir = gtk_file_chooser_get_filename ( GTK_FILE_CHOOSER(gw) );
2898 gtk_widget_destroy ( dialog );
2900 if ( !export_to ( vw, gl, vft, dir, extension ) )
2901 a_dialog_error_msg ( GTK_WINDOW(vw),_("Could not convert all files") );
2906 gtk_widget_destroy ( dialog );
2911 static void export_to_gpx ( GtkAction *a, VikWindow *vw )
2913 export_to_common ( vw, FILE_TYPE_GPX, ".gpx" );
2916 static void export_to_kml ( GtkAction *a, VikWindow *vw )
2918 export_to_common ( vw, FILE_TYPE_KML, ".kml" );
2921 static void file_properties_cb ( GtkAction *a, VikWindow *vw )
2923 gchar *message = NULL;
2924 if ( vw->filename ) {
2925 if ( g_file_test ( vw->filename, G_FILE_TEST_EXISTS ) ) {
2926 // Get some timestamp information of the file
2928 if ( g_stat ( vw->filename, &stat_buf ) == 0 ) {
2930 strftime ( time_buf, sizeof(time_buf), "%c", gmtime((const time_t *)&stat_buf.st_mtime) );
2932 gint byte_size = stat_buf.st_size;
2933 // See http://en.wikipedia.org/wiki/Megabyte (and Kilobyte)
2934 // hence using 1000 rather than 1024
2935 // so get output as per 'ls' or the Gtk file open dialog
2936 if ( byte_size < 1000 )
2937 size = g_strdup_printf ( _("%d bytes"), byte_size );
2938 else if ( byte_size < 1000*1000 )
2939 size = g_strdup_printf ( _("%3.1f kB"), (gdouble)byte_size / 1000 );
2941 size = g_strdup_printf ( _("%3.1f MB"), (gdouble)byte_size / (1000*1000) );
2942 message = g_strdup_printf ( _("%s\n\n%s\n\n%s"), vw->filename, time_buf, size );
2947 message = g_strdup ( _("File not accessible") );
2950 message = g_strdup ( _("No Viking File") );
2953 a_dialog_info_msg ( GTK_WINDOW(vw), message );
2957 static void acquire_from_gps ( GtkAction *a, VikWindow *vw )
2959 // Via the file menu, acquiring from a GPS makes a new layer
2960 // this has always been the way (not entirely sure if this was the real intention!)
2961 // thus maintain the behaviour ATM.
2962 // Hence explicit setting here (as the value may be changed elsewhere)
2963 vik_datasource_gps_interface.mode = VIK_DATASOURCE_CREATENEWLAYER;
2964 a_acquire(vw, vw->viking_vlp, vw->viking_vvp, &vik_datasource_gps_interface, NULL, NULL );
2967 static void acquire_from_file ( GtkAction *a, VikWindow *vw )
2969 a_acquire(vw, vw->viking_vlp, vw->viking_vvp, &vik_datasource_file_interface, NULL, NULL );
2972 static void acquire_from_routing ( GtkAction *a, VikWindow *vw )
2974 a_acquire(vw, vw->viking_vlp, vw->viking_vvp, &vik_datasource_routing_interface, NULL, NULL );
2977 #ifdef VIK_CONFIG_OPENSTREETMAP
2978 static void acquire_from_osm ( GtkAction *a, VikWindow *vw )
2980 a_acquire(vw, vw->viking_vlp, vw->viking_vvp, &vik_datasource_osm_interface, NULL, NULL );
2983 static void acquire_from_my_osm ( GtkAction *a, VikWindow *vw )
2985 a_acquire(vw, vw->viking_vlp, vw->viking_vvp, &vik_datasource_osm_my_traces_interface, NULL, NULL );
2989 #ifdef VIK_CONFIG_GEOCACHES
2990 static void acquire_from_gc ( GtkAction *a, VikWindow *vw )
2992 a_acquire(vw, vw->viking_vlp, vw->viking_vvp, &vik_datasource_gc_interface, NULL, NULL );
2996 #ifdef VIK_CONFIG_GEOTAG
2997 static void acquire_from_geotag ( GtkAction *a, VikWindow *vw )
2999 vik_datasource_geotag_interface.mode = VIK_DATASOURCE_CREATENEWLAYER;
3000 a_acquire(vw, vw->viking_vlp, vw->viking_vvp, &vik_datasource_geotag_interface, NULL, NULL );
3004 #ifdef VIK_CONFIG_GEONAMES
3005 static void acquire_from_wikipedia ( GtkAction *a, VikWindow *vw )
3007 a_acquire(vw, vw->viking_vlp, vw->viking_vvp, &vik_datasource_wikipedia_interface, NULL, NULL );
3011 static void goto_default_location( GtkAction *a, VikWindow *vw)
3014 ll.lat = a_vik_get_default_lat();
3015 ll.lon = a_vik_get_default_long();
3016 vik_viewport_set_center_latlon(vw->viking_vvp, &ll);
3017 vik_layers_panel_emit_update(vw->viking_vlp);
3021 static void goto_address( GtkAction *a, VikWindow *vw)
3023 a_vik_goto ( vw, vw->viking_vvp );
3024 vik_layers_panel_emit_update ( vw->viking_vlp );
3027 static void mapcache_flush_cb ( GtkAction *a, VikWindow *vw )
3032 static void layer_defaults_cb ( GtkAction *a, VikWindow *vw )
3034 gchar **texts = g_strsplit ( gtk_action_get_name(a), "Layer", 0 );
3037 return; // Internally broken :(
3039 if ( ! a_layer_defaults_show_window ( GTK_WINDOW(vw), texts[1] ) )
3040 a_dialog_info_msg ( GTK_WINDOW(vw), _("This layer has no configurable properties.") );
3041 // NB no update needed
3043 g_strfreev ( texts );
3046 static void preferences_change_update ( VikWindow *vw, gpointer data )
3048 // Want to update all TrackWaypoint layers
3049 GList *layers = vik_layers_panel_get_all_layers_of_type ( vw->viking_vlp, VIK_LAYER_TRW, TRUE );
3055 // Reset the individual waypoints themselves due to the preferences change
3056 VikTrwLayer *vtl = VIK_TRW_LAYER(layers->data);
3057 vik_trw_layer_reset_waypoints ( vtl );
3058 layers = g_list_next ( layers );
3061 g_list_free ( layers );
3066 static void preferences_cb ( GtkAction *a, VikWindow *vw )
3068 gboolean wp_icon_size = a_vik_get_use_large_waypoint_icons();
3070 a_preferences_show_window ( GTK_WINDOW(vw) );
3072 // Has the waypoint size setting changed?
3073 if (wp_icon_size != a_vik_get_use_large_waypoint_icons()) {
3074 // Delete icon indexing 'cache' and so automatically regenerates with the new setting when changed
3075 clear_garmin_icon_syms ();
3077 // Update all windows
3078 g_slist_foreach ( window_list, (GFunc) preferences_change_update, NULL );
3082 static void default_location_cb ( GtkAction *a, VikWindow *vw )
3084 /* Simplistic repeat of preference setting
3085 Only the name & type are important for setting the preference via this 'external' way */
3086 VikLayerParam pref_lat[] = {
3087 { VIK_LAYER_NUM_TYPES,
3088 VIKING_PREFERENCES_NAMESPACE "default_latitude",
3089 VIK_LAYER_PARAM_DOUBLE,
3092 VIK_LAYER_WIDGET_SPINBUTTON,
3100 VikLayerParam pref_lon[] = {
3101 { VIK_LAYER_NUM_TYPES,
3102 VIKING_PREFERENCES_NAMESPACE "default_longitude",
3103 VIK_LAYER_PARAM_DOUBLE,
3106 VIK_LAYER_WIDGET_SPINBUTTON,
3115 /* Get current center */
3117 vik_coord_to_latlon ( vik_viewport_get_center ( vw->viking_vvp ), &ll );
3119 /* Apply to preferences */
3120 VikLayerParamData vlp_data;
3121 vlp_data.d = ll.lat;
3122 a_preferences_run_setparam (vlp_data, pref_lat);
3123 vlp_data.d = ll.lon;
3124 a_preferences_run_setparam (vlp_data, pref_lon);
3125 /* Remember to save */
3126 a_preferences_save_to_file();
3129 static void clear_cb ( GtkAction *a, VikWindow *vw )
3131 vik_layers_panel_clear ( vw->viking_vlp );
3132 window_set_filename ( vw, NULL );
3136 static void window_close ( GtkAction *a, VikWindow *vw )
3138 if ( ! delete_event ( vw ) )
3139 gtk_widget_destroy ( GTK_WIDGET(vw) );
3142 static gboolean save_file_and_exit ( GtkAction *a, VikWindow *vw )
3144 if (save_file( NULL, vw)) {
3145 window_close( NULL, vw);
3152 static void zoom_to_cb ( GtkAction *a, VikWindow *vw )
3154 gdouble xmpp = vik_viewport_get_xmpp ( vw->viking_vvp ), ympp = vik_viewport_get_ympp ( vw->viking_vvp );
3155 if ( a_dialog_custom_zoom ( GTK_WINDOW(vw), &xmpp, &ympp ) )
3157 vik_viewport_set_xmpp ( vw->viking_vvp, xmpp );
3158 vik_viewport_set_ympp ( vw->viking_vvp, ympp );
3163 static void save_image_file ( VikWindow *vw, const gchar *fn, guint w, guint h, gdouble zoom, gboolean save_as_png )
3165 /* more efficient way: stuff draws directly to pixbuf (fork viewport) */
3166 GdkPixbuf *pixbuf_to_save;
3167 gdouble old_xmpp, old_ympp;
3168 GError *error = NULL;
3170 GtkWidget *msgbox = gtk_message_dialog_new ( GTK_WINDOW(vw),
3171 GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
3174 _("Generating image file...") );
3176 g_signal_connect_swapped (msgbox, "response", G_CALLBACK (gtk_widget_destroy), msgbox);
3177 // Ensure dialog shown
3178 gtk_widget_show_all ( msgbox );
3180 vik_statusbar_set_message ( vw->viking_vs, VIK_STATUSBAR_INFO, _("Generating image file...") );
3181 while ( gtk_events_pending() )
3182 gtk_main_iteration ();
3183 // Despite many efforts & variations, GTK on my Linux system doesn't show the actual msgbox contents :(
3184 // At least the empty box can give a clue something's going on + the statusbar msg...
3185 // Windows version under Wine OK!
3187 /* backup old zoom & set new */
3188 old_xmpp = vik_viewport_get_xmpp ( vw->viking_vvp );
3189 old_ympp = vik_viewport_get_ympp ( vw->viking_vvp );
3190 vik_viewport_set_zoom ( vw->viking_vvp, zoom );
3192 /* reset width and height: */
3193 vik_viewport_configure_manually ( vw->viking_vvp, w, h );
3195 /* draw all layers */
3198 /* save buffer as file. */
3199 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);
3200 if ( !pixbuf_to_save ) {
3201 g_warning("Failed to generate internal pixmap size: %d x %d", w, h);
3202 gtk_message_dialog_set_markup ( GTK_MESSAGE_DIALOG(msgbox), _("Failed to generate internal image.\n\nTry creating a smaller image.") );
3206 gdk_pixbuf_save ( pixbuf_to_save, fn, save_as_png ? "png" : "jpeg", &error, NULL );
3209 g_warning("Unable to write to file %s: %s", fn, error->message );
3210 gtk_message_dialog_set_markup ( GTK_MESSAGE_DIALOG(msgbox), _("Failed to generate image file.") );
3211 g_error_free (error);
3215 gtk_message_dialog_set_markup ( GTK_MESSAGE_DIALOG(msgbox), _("Image file generated.") );
3217 g_object_unref ( G_OBJECT(pixbuf_to_save) );
3220 vik_statusbar_set_message ( vw->viking_vs, VIK_STATUSBAR_INFO, "" );
3221 gtk_dialog_add_button ( GTK_DIALOG(msgbox), GTK_STOCK_OK, GTK_RESPONSE_OK );
3222 gtk_dialog_run ( GTK_DIALOG(msgbox) ); // Don't care about the result
3224 /* pretend like nothing happened ;) */
3225 vik_viewport_set_xmpp ( vw->viking_vvp, old_xmpp );
3226 vik_viewport_set_ympp ( vw->viking_vvp, old_ympp );
3227 vik_viewport_configure ( vw->viking_vvp );
3231 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 )
3233 gulong size = sizeof(gchar) * (strlen(fn) + 15);
3234 gchar *name_of_file = g_malloc ( size );
3236 struct UTM utm_orig, utm;
3238 /* *** copied from above *** */
3239 GdkPixbuf *pixbuf_to_save;
3240 gdouble old_xmpp, old_ympp;
3241 GError *error = NULL;
3243 /* backup old zoom & set new */
3244 old_xmpp = vik_viewport_get_xmpp ( vw->viking_vvp );
3245 old_ympp = vik_viewport_get_ympp ( vw->viking_vvp );
3246 vik_viewport_set_zoom ( vw->viking_vvp, zoom );
3248 /* reset width and height: do this only once for all images (same size) */
3249 vik_viewport_configure_manually ( vw->viking_vvp, w, h );
3250 /* *** end copy from above *** */
3252 g_assert ( vik_viewport_get_coord_mode ( vw->viking_vvp ) == VIK_COORD_UTM );
3256 utm_orig = *((const struct UTM *)vik_viewport_get_center ( vw->viking_vvp ));
3258 for ( y = 1; y <= tiles_h; y++ )
3260 for ( x = 1; x <= tiles_w; x++ )
3262 g_snprintf ( name_of_file, size, "%s%cy%d-x%d.%s", fn, G_DIR_SEPARATOR, y, x, save_as_png ? "png" : "jpg" );
3264 if ( tiles_w & 0x1 )
3265 utm.easting += ((gdouble)x - ceil(((gdouble)tiles_w)/2)) * (w*zoom);
3267 utm.easting += ((gdouble)x - (((gdouble)tiles_w)+1)/2) * (w*zoom);
3268 if ( tiles_h & 0x1 ) /* odd */
3269 utm.northing -= ((gdouble)y - ceil(((gdouble)tiles_h)/2)) * (h*zoom);
3271 utm.northing -= ((gdouble)y - (((gdouble)tiles_h)+1)/2) * (h*zoom);
3273 /* move to correct place. */
3274 vik_viewport_set_center_utm ( vw->viking_vvp, &utm );
3278 /* save buffer as file. */
3279 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);
3280 gdk_pixbuf_save ( pixbuf_to_save, name_of_file, save_as_png ? "png" : "jpeg", &error, NULL );
3283 g_warning("Unable to write to file %s: %s", name_of_file, error->message );
3284 g_error_free (error);
3287 g_object_unref ( G_OBJECT(pixbuf_to_save) );
3291 vik_viewport_set_center_utm ( vw->viking_vvp, &utm_orig );
3292 vik_viewport_set_xmpp ( vw->viking_vvp, old_xmpp );
3293 vik_viewport_set_ympp ( vw->viking_vvp, old_ympp );
3294 vik_viewport_configure ( vw->viking_vvp );
3297 g_free ( name_of_file );
3300 static void draw_to_image_file_current_window_cb(GtkWidget* widget,GdkEventButton *event,gpointer *pass_along)
3302 VikWindow *vw = VIK_WINDOW(pass_along[0]);
3303 GtkSpinButton *width_spin = GTK_SPIN_BUTTON(pass_along[1]), *height_spin = GTK_SPIN_BUTTON(pass_along[2]);
3305 gint active = gtk_combo_box_get_active ( GTK_COMBO_BOX(pass_along[3]) );
3306 gdouble zoom = pow (2, active-2 );
3308 gdouble width_min, width_max, height_min, height_max;
3311 gtk_spin_button_get_range ( width_spin, &width_min, &width_max );
3312 gtk_spin_button_get_range ( height_spin, &height_min, &height_max );
3314 /* TODO: support for xzoom and yzoom values */
3315 width = vik_viewport_get_width ( vw->viking_vvp ) * vik_viewport_get_xmpp ( vw->viking_vvp ) / zoom;
3316 height = vik_viewport_get_height ( vw->viking_vvp ) * vik_viewport_get_xmpp ( vw->viking_vvp ) / zoom;
3318 if ( width > width_max || width < width_min || height > height_max || height < height_min )
3319 a_dialog_info_msg ( GTK_WINDOW(vw), _("Viewable region outside allowable pixel size bounds for image. Clipping width/height values.") );
3321 gtk_spin_button_set_value ( width_spin, width );
3322 gtk_spin_button_set_value ( height_spin, height );
3325 static void draw_to_image_file_total_area_cb (GtkSpinButton *spinbutton, gpointer *pass_along)
3327 GtkSpinButton *width_spin = GTK_SPIN_BUTTON(pass_along[1]), *height_spin = GTK_SPIN_BUTTON(pass_along[2]);
3329 gint active = gtk_combo_box_get_active ( GTK_COMBO_BOX(pass_along[3]) );
3330 gdouble zoom = pow (2, active-2 );
3334 w = gtk_spin_button_get_value(width_spin) * zoom;
3335 h = gtk_spin_button_get_value(height_spin) * zoom;
3336 if (pass_along[4]) /* save many images; find TOTAL area covered */
3338 w *= gtk_spin_button_get_value(GTK_SPIN_BUTTON(pass_along[4]));
3339 h *= gtk_spin_button_get_value(GTK_SPIN_BUTTON(pass_along[5]));
3341 vik_units_distance_t dist_units = a_vik_get_units_distance ();
3342 switch (dist_units) {
3343 case VIK_UNITS_DISTANCE_KILOMETRES:
3344 label_text = g_strdup_printf ( _("Total area: %ldm x %ldm (%.3f sq. km)"), (glong)w, (glong)h, (w*h/1000000));
3346 case VIK_UNITS_DISTANCE_MILES:
3347 label_text = g_strdup_printf ( _("Total area: %ldm x %ldm (%.3f sq. miles)"), (glong)w, (glong)h, (w*h/2589988.11));
3350 label_text = g_strdup_printf ("Just to keep the compiler happy");
3351 g_critical("Houston, we've had a problem. distance=%d", dist_units);
3354 gtk_label_set_text(GTK_LABEL(pass_along[6]), label_text);
3355 g_free ( label_text );
3359 * Get an allocated filename (or directory as specified)
3361 static gchar* draw_image_filename ( VikWindow *vw, gboolean one_image_only )
3364 if ( one_image_only )
3367 if (!vw->save_img_dia) {
3368 vw->save_img_dia = gtk_file_chooser_dialog_new (_("Save Image"),
3370 GTK_FILE_CHOOSER_ACTION_SAVE,
3371 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
3372 GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT,
3375 gchar *cwd = g_get_current_dir();
3377 gtk_file_chooser_set_current_folder ( GTK_FILE_CHOOSER(vw->save_img_dia), cwd );
3381 GtkFileChooser *chooser = GTK_FILE_CHOOSER ( vw->save_img_dia );
3383 GtkFileFilter *filter;
3384 filter = gtk_file_filter_new ();
3385 gtk_file_filter_set_name ( filter, _("All") );
3386 gtk_file_filter_add_pattern ( filter, "*" );
3387 gtk_file_chooser_add_filter ( chooser, filter );
3389 filter = gtk_file_filter_new ();
3390 gtk_file_filter_set_name ( filter, _("JPG") );
3391 gtk_file_filter_add_mime_type ( filter, "image/jpeg");
3392 gtk_file_chooser_add_filter ( chooser, filter );
3394 if ( !vw->draw_image_save_as_png )
3395 gtk_file_chooser_set_filter ( chooser, filter );
3397 filter = gtk_file_filter_new ();
3398 gtk_file_filter_set_name ( filter, _("PNG") );
3399 gtk_file_filter_add_mime_type ( filter, "image/png");
3400 gtk_file_chooser_add_filter ( chooser, filter );
3402 if ( vw->draw_image_save_as_png )
3403 gtk_file_chooser_set_filter ( chooser, filter );
3405 gtk_window_set_transient_for ( GTK_WINDOW(vw->save_img_dia), GTK_WINDOW(vw) );
3406 gtk_window_set_destroy_with_parent ( GTK_WINDOW(vw->save_img_dia), TRUE );
3409 if ( gtk_dialog_run ( GTK_DIALOG(vw->save_img_dia) ) == GTK_RESPONSE_ACCEPT ) {
3410 fn = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER(vw->save_img_dia) );
3411 if ( g_file_test ( fn, G_FILE_TEST_EXISTS ) )
3412 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 ) ) )
3415 gtk_widget_hide ( vw->save_img_dia );
3419 // For some reason this method is only written to work in UTM...
3420 if ( vik_viewport_get_coord_mode(vw->viking_vvp) != VIK_COORD_UTM ) {
3421 a_dialog_error_msg ( GTK_WINDOW(vw), _("You must be in UTM mode to use this feature") );
3425 if (!vw->save_img_dir_dia) {
3426 vw->save_img_dir_dia = gtk_file_chooser_dialog_new (_("Choose a directory to hold images"),
3428 GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER,
3429 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
3430 GTK_STOCK_OK, GTK_RESPONSE_ACCEPT,
3432 gtk_window_set_transient_for ( GTK_WINDOW(vw->save_img_dir_dia), GTK_WINDOW(vw) );
3433 gtk_window_set_destroy_with_parent ( GTK_WINDOW(vw->save_img_dir_dia), TRUE );
3436 if ( gtk_dialog_run ( GTK_DIALOG(vw->save_img_dir_dia) ) == GTK_RESPONSE_ACCEPT ) {
3437 fn = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER(vw->save_img_dir_dia) );
3439 gtk_widget_hide ( vw->save_img_dir_dia );
3444 static void draw_to_image_file ( VikWindow *vw, gboolean one_image_only )
3446 /* todo: default for answers inside VikWindow or static (thruout instance) */
3447 GtkWidget *dialog = gtk_dialog_new_with_buttons ( _("Save to Image File"), GTK_WINDOW(vw),
3448 GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
3450 GTK_RESPONSE_REJECT,
3452 GTK_RESPONSE_ACCEPT,
3454 GtkWidget *width_label, *width_spin, *height_label, *height_spin;
3455 GtkWidget *png_radio, *jpeg_radio;
3456 GtkWidget *current_window_button;
3457 gpointer current_window_pass_along[7];
3458 GtkWidget *zoom_label, *zoom_combo;
3459 GtkWidget *total_size_label;
3461 /* only used if (!one_image_only) */
3462 GtkWidget *tiles_width_spin = NULL, *tiles_height_spin = NULL;
3464 width_label = gtk_label_new ( _("Width (pixels):") );
3465 width_spin = gtk_spin_button_new ( GTK_ADJUSTMENT(gtk_adjustment_new ( vw->draw_image_width, 10, 50000, 10, 100, 0 )), 10, 0 );
3466 height_label = gtk_label_new ( _("Height (pixels):") );
3467 height_spin = gtk_spin_button_new ( GTK_ADJUSTMENT(gtk_adjustment_new ( vw->draw_image_height, 10, 50000, 10, 100, 0 )), 10, 0 );
3469 GtkWidget *win_warning_label = gtk_label_new ( _("WARNING: USING LARGE IMAGES OVER 10000x10000\nMAY CRASH THE PROGRAM!") );
3471 zoom_label = gtk_label_new ( _("Zoom (meters per pixel):") );
3472 /* TODO: separate xzoom and yzoom factors */
3473 zoom_combo = create_zoom_combo_all_levels();
3475 gdouble mpp = vik_viewport_get_xmpp(vw->viking_vvp);
3476 gint active = 2 + round ( log (mpp) / log (2) );
3478 // Can we not hard code size here?
3483 gtk_combo_box_set_active ( GTK_COMBO_BOX(zoom_combo), active );
3485 total_size_label = gtk_label_new ( NULL );
3487 current_window_button = gtk_button_new_with_label ( _("Area in current viewable window") );
3488 current_window_pass_along [0] = vw;
3489 current_window_pass_along [1] = width_spin;
3490 current_window_pass_along [2] = height_spin;
3491 current_window_pass_along [3] = zoom_combo;
3492 current_window_pass_along [4] = NULL; /* used for one_image_only != 1 */
3493 current_window_pass_along [5] = NULL;
3494 current_window_pass_along [6] = total_size_label;
3495 g_signal_connect ( G_OBJECT(current_window_button), "button_press_event", G_CALLBACK(draw_to_image_file_current_window_cb), current_window_pass_along );
3497 png_radio = gtk_radio_button_new_with_label ( NULL, _("Save as PNG") );
3498 jpeg_radio = gtk_radio_button_new_with_label_from_widget ( GTK_RADIO_BUTTON(png_radio), _("Save as JPEG") );
3500 gtk_box_pack_start (GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), png_radio, FALSE, FALSE, 0);
3501 gtk_box_pack_start (GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), jpeg_radio, FALSE, FALSE, 0);
3503 if ( ! vw->draw_image_save_as_png )
3504 gtk_toggle_button_set_active ( GTK_TOGGLE_BUTTON(jpeg_radio), TRUE );
3506 gtk_box_pack_start (GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), width_label, FALSE, FALSE, 0);
3507 gtk_box_pack_start (GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), width_spin, FALSE, FALSE, 0);
3508 gtk_box_pack_start (GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), height_label, FALSE, FALSE, 0);
3509 gtk_box_pack_start (GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), height_spin, FALSE, FALSE, 0);
3511 gtk_box_pack_start (GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), win_warning_label, FALSE, FALSE, 0);
3513 gtk_box_pack_start (GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), current_window_button, FALSE, FALSE, 0);
3514 gtk_box_pack_start (GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), zoom_label, FALSE, FALSE, 0);
3515 gtk_box_pack_start (GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), zoom_combo, FALSE, FALSE, 0);
3517 if ( ! one_image_only )
3519 GtkWidget *tiles_width_label, *tiles_height_label;
3521 tiles_width_label = gtk_label_new ( _("East-west image tiles:") );
3522 tiles_width_spin = gtk_spin_button_new ( GTK_ADJUSTMENT(gtk_adjustment_new ( 5, 1, 10, 1, 100, 0 )), 1, 0 );
3523 tiles_height_label = gtk_label_new ( _("North-south image tiles:") );
3524 tiles_height_spin = gtk_spin_button_new ( GTK_ADJUSTMENT(gtk_adjustment_new ( 5, 1, 10, 1, 100, 0 )), 1, 0 );
3525 gtk_box_pack_start (GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), tiles_width_label, FALSE, FALSE, 0);
3526 gtk_box_pack_start (GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), tiles_width_spin, FALSE, FALSE, 0);
3527 gtk_box_pack_start (GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), tiles_height_label, FALSE, FALSE, 0);
3528 gtk_box_pack_start (GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), tiles_height_spin, FALSE, FALSE, 0);
3530 current_window_pass_along [4] = tiles_width_spin;
3531 current_window_pass_along [5] = tiles_height_spin;
3532 g_signal_connect ( G_OBJECT(tiles_width_spin), "value-changed", G_CALLBACK(draw_to_image_file_total_area_cb), current_window_pass_along );
3533 g_signal_connect ( G_OBJECT(tiles_height_spin), "value-changed", G_CALLBACK(draw_to_image_file_total_area_cb), current_window_pass_along );
3535 gtk_box_pack_start (GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), total_size_label, FALSE, FALSE, 0);
3536 g_signal_connect ( G_OBJECT(width_spin), "value-changed", G_CALLBACK(draw_to_image_file_total_area_cb), current_window_pass_along );
3537 g_signal_connect ( G_OBJECT(height_spin), "value-changed", G_CALLBACK(draw_to_image_file_total_area_cb), current_window_pass_along );
3538 g_signal_connect ( G_OBJECT(zoom_combo), "changed", G_CALLBACK(draw_to_image_file_total_area_cb), current_window_pass_along );
3540 draw_to_image_file_total_area_cb ( NULL, current_window_pass_along ); /* set correct size info now */
3542 gtk_dialog_set_default_response ( GTK_DIALOG(dialog), GTK_RESPONSE_ACCEPT );
3544 gtk_widget_show_all ( gtk_dialog_get_content_area(GTK_DIALOG(dialog)) );
3546 if ( gtk_dialog_run ( GTK_DIALOG(dialog) ) == GTK_RESPONSE_ACCEPT )
3548 gtk_widget_hide ( GTK_WIDGET(dialog) );
3550 gchar *fn = draw_image_filename ( vw, one_image_only );
3554 gint active = gtk_combo_box_get_active ( GTK_COMBO_BOX(zoom_combo) );
3555 gdouble zoom = pow (2, active-2 );
3557 if ( one_image_only )
3558 save_image_file ( vw, fn,
3559 vw->draw_image_width = gtk_spin_button_get_value_as_int ( GTK_SPIN_BUTTON(width_spin) ),
3560 vw->draw_image_height = gtk_spin_button_get_value_as_int ( GTK_SPIN_BUTTON(height_spin) ),
3562 vw->draw_image_save_as_png = gtk_toggle_button_get_active ( GTK_TOGGLE_BUTTON(png_radio) ) );
3564 // NB is in UTM mode ATM
3565 save_image_dir ( vw, fn,
3566 vw->draw_image_width = gtk_spin_button_get_value_as_int ( GTK_SPIN_BUTTON(width_spin) ),
3567 vw->draw_image_height = gtk_spin_button_get_value_as_int ( GTK_SPIN_BUTTON(height_spin) ),
3569 vw->draw_image_save_as_png = gtk_toggle_button_get_active ( GTK_TOGGLE_BUTTON(png_radio) ),
3570 gtk_spin_button_get_value ( GTK_SPIN_BUTTON(tiles_width_spin) ),
3571 gtk_spin_button_get_value ( GTK_SPIN_BUTTON(tiles_height_spin) ) );
3576 gtk_widget_destroy ( GTK_WIDGET(dialog) );
3580 static void draw_to_image_file_cb ( GtkAction *a, VikWindow *vw )
3582 draw_to_image_file ( vw, TRUE );
3585 static void draw_to_image_dir_cb ( GtkAction *a, VikWindow *vw )
3587 draw_to_image_file ( vw, FALSE );
3590 static void print_cb ( GtkAction *a, VikWindow *vw )
3592 a_print(vw, vw->viking_vvp);
3595 /* really a misnomer: changes coord mode (actual coordinates) AND/OR draw mode (viewport only) */
3596 static void window_change_coord_mode_cb ( GtkAction *old_a, GtkAction *a, VikWindow *vw )
3598 VikViewportDrawMode drawmode;
3599 if (!strcmp(gtk_action_get_name(a), "ModeUTM")) {
3600 drawmode = VIK_VIEWPORT_DRAWMODE_UTM;
3602 else if (!strcmp(gtk_action_get_name(a), "ModeLatLon")) {
3603 drawmode = VIK_VIEWPORT_DRAWMODE_LATLON;
3605 else if (!strcmp(gtk_action_get_name(a), "ModeExpedia")) {
3606 drawmode = VIK_VIEWPORT_DRAWMODE_EXPEDIA;
3608 else if (!strcmp(gtk_action_get_name(a), "ModeMercator")) {
3609 drawmode = VIK_VIEWPORT_DRAWMODE_MERCATOR;
3612 g_critical("Houston, we've had a problem.");
3616 if ( !vw->only_updating_coord_mode_ui )
3618 VikViewportDrawMode olddrawmode = vik_viewport_get_drawmode ( vw->viking_vvp );
3619 if ( olddrawmode != drawmode )
3621 /* this takes care of coord mode too */
3622 vik_viewport_set_drawmode ( vw->viking_vvp, drawmode );
3623 if ( drawmode == VIK_VIEWPORT_DRAWMODE_UTM ) {
3624 vik_layers_panel_change_coord_mode ( vw->viking_vlp, VIK_COORD_UTM );
3625 } else if ( olddrawmode == VIK_VIEWPORT_DRAWMODE_UTM ) {
3626 vik_layers_panel_change_coord_mode ( vw->viking_vlp, VIK_COORD_LATLON );
3633 static void set_draw_scale ( GtkAction *a, VikWindow *vw )
3635 GtkWidget *check_box = gtk_ui_manager_get_widget ( vw->uim, "/ui/MainMenu/View/SetShow/ShowScale" );
3636 g_assert(check_box);
3637 gboolean state = gtk_check_menu_item_get_active ( GTK_CHECK_MENU_ITEM(check_box));
3638 vik_viewport_set_draw_scale ( vw->viking_vvp, state );
3642 static void set_draw_centermark ( GtkAction *a, VikWindow *vw )
3644 GtkWidget *check_box = gtk_ui_manager_get_widget ( vw->uim, "/ui/MainMenu/View/SetShow/ShowCenterMark" );
3645 g_assert(check_box);
3646 gboolean state = gtk_check_menu_item_get_active ( GTK_CHECK_MENU_ITEM(check_box));
3647 vik_viewport_set_draw_centermark ( vw->viking_vvp, state );
3651 static void set_draw_highlight ( GtkAction *a, VikWindow *vw )
3653 GtkWidget *check_box = gtk_ui_manager_get_widget ( vw->uim, "/ui/MainMenu/View/SetShow/ShowHighlight" );
3654 g_assert(check_box);
3655 gboolean state = gtk_check_menu_item_get_active ( GTK_CHECK_MENU_ITEM(check_box));
3656 vik_viewport_set_draw_highlight ( vw->viking_vvp, state );
3660 static void set_bg_color ( GtkAction *a, VikWindow *vw )
3662 GtkWidget *colorsd = gtk_color_selection_dialog_new ( _("Choose a background color") );
3663 GdkColor *color = vik_viewport_get_background_gdkcolor ( vw->viking_vvp );
3664 gtk_color_selection_set_previous_color ( GTK_COLOR_SELECTION(gtk_color_selection_dialog_get_color_selection(GTK_COLOR_SELECTION_DIALOG(colorsd))), color );
3665 gtk_color_selection_set_current_color ( GTK_COLOR_SELECTION(gtk_color_selection_dialog_get_color_selection(GTK_COLOR_SELECTION_DIALOG(colorsd))), color );
3666 if ( gtk_dialog_run ( GTK_DIALOG(colorsd) ) == GTK_RESPONSE_OK )
3668 gtk_color_selection_get_current_color ( GTK_COLOR_SELECTION(gtk_color_selection_dialog_get_color_selection(GTK_COLOR_SELECTION_DIALOG(colorsd))), color );
3669 vik_viewport_set_background_gdkcolor ( vw->viking_vvp, color );
3673 gtk_widget_destroy ( colorsd );
3676 static void set_highlight_color ( GtkAction *a, VikWindow *vw )
3678 GtkWidget *colorsd = gtk_color_selection_dialog_new ( _("Choose a track highlight color") );
3679 GdkColor *color = vik_viewport_get_highlight_gdkcolor ( vw->viking_vvp );
3680 gtk_color_selection_set_previous_color ( GTK_COLOR_SELECTION(gtk_color_selection_dialog_get_color_selection(GTK_COLOR_SELECTION_DIALOG(colorsd))), color );
3681 gtk_color_selection_set_current_color ( GTK_COLOR_SELECTION(gtk_color_selection_dialog_get_color_selection(GTK_COLOR_SELECTION_DIALOG(colorsd))), color );
3682 if ( gtk_dialog_run ( GTK_DIALOG(colorsd) ) == GTK_RESPONSE_OK )
3684 gtk_color_selection_get_current_color ( GTK_COLOR_SELECTION(gtk_color_selection_dialog_get_color_selection(GTK_COLOR_SELECTION_DIALOG(colorsd))), color );
3685 vik_viewport_set_highlight_gdkcolor ( vw->viking_vvp, color );
3689 gtk_widget_destroy ( colorsd );
3694 /***********************************************************************************************
3696 ***********************************************************************************************/
3698 static GtkActionEntry entries[] = {
3699 { "File", NULL, N_("_File"), 0, 0, 0 },
3700 { "Edit", NULL, N_("_Edit"), 0, 0, 0 },
3701 { "View", NULL, N_("_View"), 0, 0, 0 },
3702 { "SetShow", NULL, N_("_Show"), 0, 0, 0 },
3703 { "SetZoom", NULL, N_("_Zoom"), 0, 0, 0 },
3704 { "SetPan", NULL, N_("_Pan"), 0, 0, 0 },
3705 { "Layers", NULL, N_("_Layers"), 0, 0, 0 },
3706 { "Tools", NULL, N_("_Tools"), 0, 0, 0 },
3707 { "Exttools", NULL, N_("_Webtools"), 0, 0, 0 },
3708 { "Help", NULL, N_("_Help"), 0, 0, 0 },
3710 { "New", GTK_STOCK_NEW, N_("_New"), "<control>N", N_("New file"), (GCallback)newwindow_cb },
3711 { "Open", GTK_STOCK_OPEN, N_("_Open..."), "<control>O", N_("Open a file"), (GCallback)load_file },
3712 { "OpenRecentFile", NULL, N_("Open _Recent File"), NULL, NULL, (GCallback)NULL },
3713 { "Append", GTK_STOCK_ADD, N_("Append _File..."), NULL, N_("Append data from a different file"), (GCallback)load_file },
3714 { "Export", GTK_STOCK_CONVERT, N_("_Export All"), NULL, N_("Export All TrackWaypoint Layers"), (GCallback)NULL },
3715 { "ExportGPX", NULL, N_("_GPX..."), NULL, N_("Export as GPX"), (GCallback)export_to_gpx },
3716 { "Acquire", GTK_STOCK_GO_DOWN, N_("A_cquire"), NULL, NULL, (GCallback)NULL },
3717 { "AcquireGPS", NULL, N_("From _GPS..."), NULL, N_("Transfer data from a GPS device"), (GCallback)acquire_from_gps },
3718 { "AcquireGPSBabel", NULL, N_("Import File With GPS_Babel..."), NULL, N_("Import file via GPSBabel converter"), (GCallback)acquire_from_file },
3719 { "AcquireRouting", NULL, N_("_Directions..."), NULL, N_("Get driving directions"), (GCallback)acquire_from_routing },
3720 #ifdef VIK_CONFIG_OPENSTREETMAP
3721 { "AcquireOSM", NULL, N_("_OSM Traces..."), NULL, N_("Get traces from OpenStreetMap"), (GCallback)acquire_from_osm },
3722 { "AcquireMyOSM", NULL, N_("_My OSM Traces..."), NULL, N_("Get Your Own Traces from OpenStreetMap"), (GCallback)acquire_from_my_osm },
3724 #ifdef VIK_CONFIG_GEOCACHES
3725 { "AcquireGC", NULL, N_("Geo_caches..."), NULL, N_("Get Geocaches from geocaching.com"), (GCallback)acquire_from_gc },
3727 #ifdef VIK_CONFIG_GEOTAG
3728 { "AcquireGeotag", NULL, N_("From Geotagged _Images..."), NULL, N_("Create waypoints from geotagged images"), (GCallback)acquire_from_geotag },
3730 #ifdef VIK_CONFIG_GEONAMES
3731 { "AcquireWikipedia", NULL, N_("From _Wikipedia Waypoints"), NULL, N_("Create waypoints from Wikipedia items in the current view"), (GCallback)acquire_from_wikipedia },
3733 { "Save", GTK_STOCK_SAVE, N_("_Save"), "<control>S", N_("Save the file"), (GCallback)save_file },
3734 { "SaveAs", GTK_STOCK_SAVE_AS, N_("Save _As..."), NULL, N_("Save the file under different name"), (GCallback)save_file_as },
3735 { "FileProperties", NULL, N_("Properties..."), NULL, N_("File Properties"), (GCallback)file_properties_cb },
3736 { "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 },
3737 { "GenImgDir", GTK_STOCK_DND_MULTIPLE, N_("Generate _Directory of Images..."), NULL, N_("FIXME:IMGDIR"), (GCallback)draw_to_image_dir_cb },
3738 { "Print", GTK_STOCK_PRINT, N_("_Print..."), NULL, N_("Print maps"), (GCallback)print_cb },
3739 { "Exit", GTK_STOCK_QUIT, N_("E_xit"), "<control>W", N_("Exit the program"), (GCallback)window_close },
3740 { "SaveExit", GTK_STOCK_QUIT, N_("Save and Exit"), NULL, N_("Save and Exit the program"), (GCallback)save_file_and_exit },
3742 { "GotoDefaultLocation", GTK_STOCK_HOME, N_("Go to the _Default Location"), NULL, N_("Go to the default location"), (GCallback)goto_default_location },
3743 { "GotoSearch", GTK_STOCK_JUMP_TO, N_("Go to _Location..."), NULL, N_("Go to address/place using text search"), (GCallback)goto_address },
3744 { "GotoLL", GTK_STOCK_JUMP_TO, N_("_Go to Lat/Lon..."), NULL, N_("Go to arbitrary lat/lon coordinate"), (GCallback)draw_goto_cb },
3745 { "GotoUTM", GTK_STOCK_JUMP_TO, N_("Go to UTM..."), NULL, N_("Go to arbitrary UTM coordinate"), (GCallback)draw_goto_cb },
3746 { "Refresh", GTK_STOCK_REFRESH, N_("_Refresh"), "F5", N_("Refresh any maps displayed"), (GCallback)draw_refresh_cb },
3747 { "SetHLColor",GTK_STOCK_SELECT_COLOR, N_("Set _Highlight Color..."), NULL, NULL, (GCallback)set_highlight_color },
3748 { "SetBGColor",GTK_STOCK_SELECT_COLOR, N_("Set Bac_kground Color..."), NULL, NULL, (GCallback)set_bg_color },
3749 { "ZoomIn", GTK_STOCK_ZOOM_IN, N_("Zoom _In"), "<control>plus", NULL, (GCallback)draw_zoom_cb },
3750 { "ZoomOut", GTK_STOCK_ZOOM_OUT, N_("Zoom _Out"), "<control>minus", NULL, (GCallback)draw_zoom_cb },
3751 { "ZoomTo", GTK_STOCK_ZOOM_FIT, N_("Zoom _To..."), "<control>Z", NULL, (GCallback)zoom_to_cb },
3752 { "PanNorth", NULL, N_("Pan _North"), "<control>Up", NULL, (GCallback)draw_pan_cb },
3753 { "PanEast", NULL, N_("Pan _East"), "<control>Right", NULL, (GCallback)draw_pan_cb },
3754 { "PanSouth", NULL, N_("Pan _South"), "<control>Down", NULL, (GCallback)draw_pan_cb },
3755 { "PanWest", NULL, N_("Pan _West"), "<control>Left", NULL, (GCallback)draw_pan_cb },
3756 { "BGJobs", GTK_STOCK_EXECUTE, N_("Background _Jobs"), NULL, NULL, (GCallback)a_background_show_window },
3758 { "Cut", GTK_STOCK_CUT, N_("Cu_t"), NULL, NULL, (GCallback)menu_cut_layer_cb },
3759 { "Copy", GTK_STOCK_COPY, N_("_Copy"), NULL, NULL, (GCallback)menu_copy_layer_cb },
3760 { "Paste", GTK_STOCK_PASTE, N_("_Paste"), NULL, NULL, (GCallback)menu_paste_layer_cb },
3761 { "Delete", GTK_STOCK_DELETE, N_("_Delete"), NULL, NULL, (GCallback)menu_delete_layer_cb },
3762 { "DeleteAll", NULL, N_("Delete All"), NULL, NULL, (GCallback)clear_cb },
3763 { "MapCacheFlush",NULL, N_("_Flush Map Cache"), NULL, NULL, (GCallback)mapcache_flush_cb },
3764 { "SetDefaultLocation", GTK_STOCK_GO_FORWARD, N_("_Set the Default Location"), NULL, N_("Set the Default Location to the current position"),(GCallback)default_location_cb },
3765 { "Preferences",GTK_STOCK_PREFERENCES, N_("_Preferences"), NULL, NULL, (GCallback)preferences_cb },
3766 { "LayerDefaults",GTK_STOCK_PROPERTIES, N_("_Layer Defaults"), NULL, NULL, NULL },
3767 { "Properties",GTK_STOCK_PROPERTIES, N_("_Properties"), NULL, NULL, (GCallback)menu_properties_cb },
3769 { "HelpEntry", GTK_STOCK_HELP, N_("_Help"), "F1", NULL, (GCallback)help_help_cb },
3770 { "About", GTK_STOCK_ABOUT, N_("_About"), NULL, NULL, (GCallback)help_about_cb },
3773 static GtkActionEntry entries_gpsbabel[] = {
3774 { "ExportKML", NULL, N_("_KML..."), NULL, N_("Export as KML"), (GCallback)export_to_kml },
3778 /* FIXME use VIEWPORT_DRAWMODE values */
3779 static GtkRadioActionEntry mode_entries[] = {
3780 { "ModeUTM", NULL, N_("_UTM Mode"), "<control>u", NULL, 0 },
3781 { "ModeExpedia", NULL, N_("_Expedia Mode"), "<control>e", NULL, 1 },
3782 { "ModeMercator", NULL, N_("_Mercator Mode"), "<control>m", NULL, 4 },
3783 { "ModeLatLon", NULL, N_("Lat_/Lon Mode"), "<control>l", NULL, 5 },
3786 static GtkToggleActionEntry toggle_entries[] = {
3787 { "ShowScale", NULL, N_("Show _Scale"), "<shift>F5", N_("Show Scale"), (GCallback)set_draw_scale, TRUE },
3788 { "ShowCenterMark", NULL, N_("Show _Center Mark"), "F6", N_("Show Center Mark"), (GCallback)set_draw_centermark, TRUE },
3789 { "ShowHighlight", GTK_STOCK_UNDERLINE, N_("Show _Highlight"), "F7", N_("Show Highlight"), (GCallback)set_draw_highlight, TRUE },
3790 { "FullScreen", GTK_STOCK_FULLSCREEN, N_("_Full Screen"), "F11", N_("Activate full screen mode"), (GCallback)full_screen_cb, FALSE },
3791 { "ViewSidePanel", GTK_STOCK_INDEX, N_("Show Side _Panel"), "F9", N_("Show Side Panel"), (GCallback)view_side_panel_cb, TRUE },
3792 { "ViewStatusBar", NULL, N_("Show Status_bar"), "F12", N_("Show Statusbar"), (GCallback)view_statusbar_cb, TRUE },
3793 { "ViewToolbar", NULL, N_("Show _Toolbar"), "F3", N_("Show Toolbar"), (GCallback)view_toolbar_cb, TRUE },
3794 { "ViewMainMenu", NULL, N_("Show _Menu"), "F4", N_("Show Menu"), (GCallback)view_main_menu_cb, TRUE },
3797 #include "menu.xml.h"
3798 static void window_create_ui( VikWindow *window )
3801 GtkActionGroup *action_group;
3802 GtkAccelGroup *accel_group;
3805 GtkIconFactory *icon_factory;
3806 GtkIconSet *icon_set;
3807 GtkRadioActionEntry *tools = NULL, *radio;
3810 uim = gtk_ui_manager_new ();
3813 toolbox_add_tool(window->vt, &ruler_tool, TOOL_LAYER_TYPE_NONE);
3814 toolbox_add_tool(window->vt, &zoom_tool, TOOL_LAYER_TYPE_NONE);
3815 toolbox_add_tool(window->vt, &pan_tool, TOOL_LAYER_TYPE_NONE);
3816 toolbox_add_tool(window->vt, &select_tool, TOOL_LAYER_TYPE_NONE);
3819 if (!(mid = gtk_ui_manager_add_ui_from_string (uim, menu_xml, -1, &error))) {
3820 g_error_free (error);
3824 action_group = gtk_action_group_new ("MenuActions");
3825 gtk_action_group_set_translation_domain(action_group, PACKAGE_NAME);
3826 gtk_action_group_add_actions (action_group, entries, G_N_ELEMENTS (entries), window);
3827 gtk_action_group_add_toggle_actions (action_group, toggle_entries, G_N_ELEMENTS (toggle_entries), window);
3828 gtk_action_group_add_radio_actions (action_group, mode_entries, G_N_ELEMENTS (mode_entries), 4, (GCallback)window_change_coord_mode_cb, window);
3830 // Use this to see if GPSBabel is available:
3831 if ( a_babel_device_list ) {
3832 // If going to add more entries then might be worth creating a menu_gpsbabel.xml.h file
3833 if ( gtk_ui_manager_add_ui_from_string ( uim,
3834 "<ui><menubar name='MainMenu'><menu action='File'><menu action='Export'><menuitem action='ExportKML'/></menu></menu></menubar></ui>",
3836 gtk_action_group_add_actions ( action_group, entries_gpsbabel, G_N_ELEMENTS (entries_gpsbabel), window );
3839 icon_factory = gtk_icon_factory_new ();
3840 gtk_icon_factory_add_default (icon_factory);
3842 register_vik_icons(icon_factory);
3844 // Copy the tool RadioActionEntries out of the main Window structure into an extending array 'tools'
3845 // so that it can be applied to the UI in one action group add function call below
3847 for (i=0; i<window->vt->n_tools; i++) {
3848 tools = g_renew(GtkRadioActionEntry, tools, ntools+1);
3849 radio = &tools[ntools];
3851 *radio = window->vt->tools[i].ti.radioActionEntry;
3852 radio->value = ntools;
3855 for (i=0; i<VIK_LAYER_NUM_TYPES; i++) {
3856 GtkActionEntry action;
3857 gtk_ui_manager_add_ui(uim, mid, "/ui/MainMenu/Layers/",
3858 vik_layer_get_interface(i)->name,
3859 vik_layer_get_interface(i)->name,
3860 GTK_UI_MANAGER_MENUITEM, FALSE);
3862 icon_set = gtk_icon_set_new_from_pixbuf (gdk_pixbuf_from_pixdata (vik_layer_get_interface(i)->icon, FALSE, NULL ));
3863 gtk_icon_factory_add (icon_factory, vik_layer_get_interface(i)->name, icon_set);
3864 gtk_icon_set_unref (icon_set);
3866 action.name = vik_layer_get_interface(i)->name;
3867 action.stock_id = vik_layer_get_interface(i)->name;
3868 action.label = g_strdup_printf( _("New _%s Layer"), vik_layer_get_interface(i)->name);
3869 action.accelerator = vik_layer_get_interface(i)->accelerator;
3870 action.tooltip = NULL;
3871 action.callback = (GCallback)menu_addlayer_cb;
3872 gtk_action_group_add_actions(action_group, &action, 1, window);
3874 g_free ( (gchar*)action.label );
3876 if ( vik_layer_get_interface(i)->tools_count ) {
3877 gtk_ui_manager_add_ui(uim, mid, "/ui/MainMenu/Tools/", vik_layer_get_interface(i)->name, NULL, GTK_UI_MANAGER_SEPARATOR, FALSE);
3878 gtk_ui_manager_add_ui(uim, mid, "/ui/MainToolbar/ToolItems/", vik_layer_get_interface(i)->name, NULL, GTK_UI_MANAGER_SEPARATOR, FALSE);
3881 // Further tool copying for to apply to the UI, also apply menu UI setup
3882 for ( j = 0; j < vik_layer_get_interface(i)->tools_count; j++ ) {
3883 tools = g_renew(GtkRadioActionEntry, tools, ntools+1);
3884 radio = &tools[ntools];
3887 gtk_ui_manager_add_ui(uim, mid, "/ui/MainMenu/Tools",
3888 vik_layer_get_interface(i)->tools[j].radioActionEntry.label,
3889 vik_layer_get_interface(i)->tools[j].radioActionEntry.name,
3890 GTK_UI_MANAGER_MENUITEM, FALSE);
3891 gtk_ui_manager_add_ui(uim, mid, "/ui/MainToolbar/ToolItems",
3892 vik_layer_get_interface(i)->tools[j].radioActionEntry.label,
3893 vik_layer_get_interface(i)->tools[j].radioActionEntry.name,
3894 GTK_UI_MANAGER_TOOLITEM, FALSE);
3896 toolbox_add_tool(window->vt, &(vik_layer_get_interface(i)->tools[j]), i);
3898 *radio = vik_layer_get_interface(i)->tools[j].radioActionEntry;
3899 // Overwrite with actual number to use
3900 radio->value = ntools;
3903 GtkActionEntry action_dl;
3904 gchar *layername = g_strdup_printf ( "Layer%s", vik_layer_get_interface(i)->fixed_layer_name );
3905 gtk_ui_manager_add_ui(uim, mid, "/ui/MainMenu/Edit/LayerDefaults",
3906 vik_layer_get_interface(i)->name,
3908 GTK_UI_MANAGER_MENUITEM, FALSE);
3911 // For default layers use action names of the form 'Layer<LayerName>'
3912 // This is to avoid clashing with just the layer name used above for the tool actions
3913 action_dl.name = g_strconcat("Layer", vik_layer_get_interface(i)->fixed_layer_name, NULL);
3914 action_dl.stock_id = NULL;
3915 action_dl.label = g_strconcat("_", vik_layer_get_interface(i)->name, "...", NULL); // Prepend marker for keyboard accelerator
3916 action_dl.accelerator = NULL;
3917 action_dl.tooltip = NULL;
3918 action_dl.callback = (GCallback)layer_defaults_cb;
3919 gtk_action_group_add_actions(action_group, &action_dl, 1, window);
3920 g_free ( (gchar*)action_dl.name );
3921 g_free ( (gchar*)action_dl.label );
3923 g_object_unref (icon_factory);
3925 gtk_action_group_add_radio_actions(action_group, tools, ntools, 0, (GCallback)menu_tool_cb, window);
3928 gtk_ui_manager_insert_action_group (uim, action_group, 0);
3930 for (i=0; i<VIK_LAYER_NUM_TYPES; i++) {
3931 for ( j = 0; j < vik_layer_get_interface(i)->tools_count; j++ ) {
3932 GtkAction *action = gtk_action_group_get_action(action_group,
3933 vik_layer_get_interface(i)->tools[j].radioActionEntry.name);
3934 g_object_set(action, "sensitive", FALSE, NULL);
3938 // This is done last so we don't need to track the value of mid anymore
3939 vik_ext_tools_add_action_items ( window, window->uim, action_group, mid );
3941 window->action_group = action_group;
3943 accel_group = gtk_ui_manager_get_accel_group (uim);
3944 gtk_window_add_accel_group (GTK_WINDOW (window), accel_group);
3945 gtk_ui_manager_ensure_update (uim);
3947 setup_recent_files(window);
3951 // TODO - add method to add tool icons defined from outside this file
3952 // and remove the reverse dependency on icon definition from this file
3954 const GdkPixdata *data;
3957 { &mover_22_pixbuf, "vik-icon-pan" },
3958 { &zoom_18_pixbuf, "vik-icon-zoom" },
3959 { &ruler_18_pixbuf, "vik-icon-ruler" },
3960 { &select_18_pixbuf, "vik-icon-select" },
3961 { &vik_new_route_18_pixbuf, "vik-icon-Create Route" },
3962 { &route_finder_18_pixbuf, "vik-icon-Route Finder" },
3963 { &demdl_18_pixbuf, "vik-icon-DEM Download" },
3964 { &showpic_18_pixbuf, "vik-icon-Show Picture" },
3965 { &addtr_18_pixbuf, "vik-icon-Create Track" },
3966 { &edtr_18_pixbuf, "vik-icon-Edit Trackpoint" },
3967 { &addwp_18_pixbuf, "vik-icon-Create Waypoint" },
3968 { &edwp_18_pixbuf, "vik-icon-Edit Waypoint" },
3969 { &geozoom_18_pixbuf, "vik-icon-Georef Zoom Tool" },
3970 { &geomove_18_pixbuf, "vik-icon-Georef Move Map" },
3971 { &mapdl_18_pixbuf, "vik-icon-Maps Download" },
3974 static gint n_stock_icons = G_N_ELEMENTS (stock_icons);
3977 register_vik_icons (GtkIconFactory *icon_factory)
3979 GtkIconSet *icon_set;
3982 for (i = 0; i < n_stock_icons; i++) {
3983 icon_set = gtk_icon_set_new_from_pixbuf (gdk_pixbuf_from_pixdata (
3984 stock_icons[i].data, FALSE, NULL ));
3985 gtk_icon_factory_add (icon_factory, stock_icons[i].stock_id, icon_set);
3986 gtk_icon_set_unref (icon_set);
3990 gpointer vik_window_get_selected_trw_layer ( VikWindow *vw )
3992 return vw->selected_vtl;
3995 void vik_window_set_selected_trw_layer ( VikWindow *vw, gpointer vtl )
3997 vw->selected_vtl = vtl;
3998 vw->containing_vtl = vtl;
4000 vw->selected_track = NULL;
4001 vw->selected_tracks = NULL;
4002 vw->selected_waypoint = NULL;
4003 vw->selected_waypoints = NULL;
4004 // Set highlight thickness
4005 vik_viewport_set_highlight_thickness ( vw->viking_vvp, vik_trw_layer_get_property_tracks_line_thickness (vw->containing_vtl) );
4008 GHashTable *vik_window_get_selected_tracks ( VikWindow *vw )
4010 return vw->selected_tracks;
4013 void vik_window_set_selected_tracks ( VikWindow *vw, GHashTable *ght, gpointer vtl )
4015 vw->selected_tracks = ght;
4016 vw->containing_vtl = vtl;
4018 vw->selected_vtl = NULL;
4019 vw->selected_track = NULL;
4020 vw->selected_waypoint = NULL;
4021 vw->selected_waypoints = NULL;
4022 // Set highlight thickness
4023 vik_viewport_set_highlight_thickness ( vw->viking_vvp, vik_trw_layer_get_property_tracks_line_thickness (vw->containing_vtl) );
4026 gpointer vik_window_get_selected_track ( VikWindow *vw )
4028 return vw->selected_track;
4031 void vik_window_set_selected_track ( VikWindow *vw, gpointer *vt, gpointer vtl )
4033 vw->selected_track = vt;
4034 vw->containing_vtl = vtl;
4036 vw->selected_vtl = NULL;
4037 vw->selected_tracks = NULL;
4038 vw->selected_waypoint = NULL;
4039 vw->selected_waypoints = NULL;
4040 // Set highlight thickness
4041 vik_viewport_set_highlight_thickness ( vw->viking_vvp, vik_trw_layer_get_property_tracks_line_thickness (vw->containing_vtl) );
4044 GHashTable *vik_window_get_selected_waypoints ( VikWindow *vw )
4046 return vw->selected_waypoints;
4049 void vik_window_set_selected_waypoints ( VikWindow *vw, GHashTable *ght, gpointer vtl )
4051 vw->selected_waypoints = ght;
4052 vw->containing_vtl = vtl;
4054 vw->selected_vtl = NULL;
4055 vw->selected_track = NULL;
4056 vw->selected_tracks = NULL;
4057 vw->selected_waypoint = NULL;
4060 gpointer vik_window_get_selected_waypoint ( VikWindow *vw )
4062 return vw->selected_waypoint;
4065 void vik_window_set_selected_waypoint ( VikWindow *vw, gpointer *vwp, gpointer vtl )
4067 vw->selected_waypoint = vwp;
4068 vw->containing_vtl = vtl;
4070 vw->selected_vtl = NULL;
4071 vw->selected_track = NULL;
4072 vw->selected_tracks = NULL;
4073 vw->selected_waypoints = NULL;
4076 gboolean vik_window_clear_highlight ( VikWindow *vw )
4078 gboolean need_redraw = FALSE;
4079 if ( vw->selected_vtl != NULL ) {
4080 vw->selected_vtl = NULL;
4083 if ( vw->selected_track != NULL ) {
4084 vw->selected_track = NULL;
4087 if ( vw->selected_tracks != NULL ) {
4088 vw->selected_tracks = NULL;
4091 if ( vw->selected_waypoint != NULL ) {
4092 vw->selected_waypoint = NULL;
4095 if ( vw->selected_waypoints != NULL ) {
4096 vw->selected_waypoints = NULL;
4102 GThread *vik_window_get_thread ( VikWindow *vw )