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_layer_rename ( VIK_LAYER(vml), _("Default Map") );
434 vik_aggregate_layer_add_layer ( vik_layers_panel_get_top_layer(vw->viking_vlp), VIK_LAYER(vml), TRUE );
439 // If not loaded any file, maybe try the location lookup
440 if ( vw->loaded_type == LOAD_TYPE_READ_FAILURE ) {
441 if ( a_vik_get_startup_method ( ) == VIK_STARTUP_METHOD_AUTO_LOCATION ) {
443 vik_statusbar_set_message ( vw->viking_vs, VIK_STATUSBAR_INFO, _("Trying to determine location...") );
445 a_background_thread ( GTK_WINDOW(vw),
446 _("Determining location"),
447 (vik_thr_func) determine_location_thread,
456 static void open_window ( VikWindow *vw, GSList *files )
458 gboolean change_fn = (g_slist_length(files) == 1); /* only change fn if one file */
459 GSList *cur_file = files;
461 // Only open a new window if a viking file
462 gchar *file_name = cur_file->data;
463 if (vw != NULL && vw->filename && check_file_magic_vik ( file_name ) ) {
464 VikWindow *newvw = vik_window_new_window ();
466 vik_window_open_file ( newvw, file_name, TRUE );
469 vik_window_open_file ( vw, file_name, change_fn );
472 cur_file = g_slist_next (cur_file);
474 g_slist_free (files);
478 void vik_window_selected_layer(VikWindow *vw, VikLayer *vl)
480 int i, j, tool_count;
481 VikLayerInterface *layer_interface;
483 if (!vw->action_group) return;
485 for (i=0; i<VIK_LAYER_NUM_TYPES; i++) {
487 layer_interface = vik_layer_get_interface(i);
488 tool_count = layer_interface->tools_count;
490 for (j = 0; j < tool_count; j++) {
491 action = gtk_action_group_get_action(vw->action_group,
492 layer_interface->tools[j].radioActionEntry.name);
493 g_object_set(action, "sensitive", i == vl->type, NULL);
498 static void window_finalize ( GObject *gob )
500 VikWindow *vw = VIK_WINDOW(gob);
501 g_return_if_fail ( vw != NULL );
503 a_background_remove_window ( vw );
505 window_list = g_slist_remove ( window_list, vw );
507 gdk_cursor_unref ( vw->busy_cursor );
509 for (tt = 0; tt < vw->vt->n_tools; tt++ )
510 if ( vw->vt->tools[tt].ti.destroy )
511 vw->vt->tools[tt].ti.destroy ( vw->vt->tools[tt].state );
512 g_free ( vw->vt->tools );
515 G_OBJECT_CLASS(parent_class)->finalize(gob);
519 static void vik_window_class_init ( VikWindowClass *klass )
522 GObjectClass *object_class;
524 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);
525 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);
527 object_class = G_OBJECT_CLASS (klass);
529 object_class->finalize = window_finalize;
531 parent_class = g_type_class_peek_parent (klass);
535 static void zoom_changed (GtkMenuShell *menushell,
538 VikWindow *vw = VIK_WINDOW (user_data);
540 GtkWidget *aw = gtk_menu_get_active ( GTK_MENU (menushell) );
541 gint active = GPOINTER_TO_INT(g_object_get_data ( G_OBJECT (aw), "position" ));
543 gdouble zoom_request = pow (2, active-2 );
545 // But has it really changed?
546 gdouble current_zoom = vik_viewport_get_zoom ( vw->viking_vvp );
547 if ( current_zoom != 0.0 && zoom_request != current_zoom ) {
548 vik_viewport_set_zoom ( vw->viking_vvp, zoom_request );
549 // Force drawing update
555 * @mpp: The initial zoom level
557 static GtkWidget *create_zoom_menu_all_levels ( gdouble mpp )
559 GtkWidget *menu = gtk_menu_new ();
560 char *itemLabels[] = { "0.25", "0.5", "1", "2", "4", "8", "16", "32", "64", "128", "256", "512", "1024", "2048", "4096", "8192", "16384", "32768" };
563 for (i = 0 ; i < G_N_ELEMENTS(itemLabels) ; i++)
565 GtkWidget *item = gtk_menu_item_new_with_label (itemLabels[i]);
566 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
567 gtk_widget_show (item);
568 g_object_set_data (G_OBJECT (item), "position", GINT_TO_POINTER(i));
571 gint active = 2 + round ( log (mpp) / log (2) );
572 // Ensure value derived from mpp is in bounds of the menu
573 if ( active >= G_N_ELEMENTS(itemLabels) )
574 active = G_N_ELEMENTS(itemLabels) - 1;
577 gtk_menu_set_active ( GTK_MENU(menu), active );
582 static GtkWidget *create_zoom_combo_all_levels ()
584 GtkWidget *combo = vik_combo_box_text_new();
585 vik_combo_box_text_append ( combo, "0.25");
586 vik_combo_box_text_append ( combo, "0.5");
587 vik_combo_box_text_append ( combo, "1");
588 vik_combo_box_text_append ( combo, "2");
589 vik_combo_box_text_append ( combo, "4");
590 vik_combo_box_text_append ( combo, "8");
591 vik_combo_box_text_append ( combo, "16");
592 vik_combo_box_text_append ( combo, "32");
593 vik_combo_box_text_append ( combo, "64");
594 vik_combo_box_text_append ( combo, "128");
595 vik_combo_box_text_append ( combo, "256");
596 vik_combo_box_text_append ( combo, "512");
597 vik_combo_box_text_append ( combo, "1024");
598 vik_combo_box_text_append ( combo, "2048");
599 vik_combo_box_text_append ( combo, "4096");
600 vik_combo_box_text_append ( combo, "8192");
601 vik_combo_box_text_append ( combo, "16384");
602 vik_combo_box_text_append ( combo, "32768");
604 gtk_widget_set_tooltip_text (combo, _("Select zoom level"));
608 static gint zoom_popup_handler (GtkWidget *widget)
612 g_return_val_if_fail (widget != NULL, FALSE);
613 g_return_val_if_fail (GTK_IS_MENU (widget), FALSE);
615 /* The "widget" is the menu that was supplied when
616 * g_signal_connect_swapped() was called.
618 menu = GTK_MENU (widget);
620 gtk_menu_popup (menu, NULL, NULL, NULL, NULL,
621 1, gtk_get_current_event_time());
629 static void drag_data_received_cb ( GtkWidget *widget,
630 GdkDragContext *context,
633 GtkSelectionData *selection_data,
638 gboolean success = FALSE;
640 if ( (selection_data != NULL) && (gtk_selection_data_get_length(selection_data) > 0) ) {
641 switch (target_type) {
643 gchar *str = (gchar*)gtk_selection_data_get_data(selection_data);
644 g_debug ("drag received string:%s \n", str);
646 // Convert string into GSList of individual entries for use with our open signal
647 gchar **entries = g_strsplit(str, "\r\n", 0);
648 GSList *filenames = NULL;
649 gint entry_runner = 0;
650 gchar *entry = entries[entry_runner];
652 if ( g_strcmp0 ( entry, "" ) ) {
653 // Drag+Drop gives URIs. And so in particular, %20 in place of spaces in filenames
654 // thus need to convert the text into a plain string
655 gchar *filename = g_filename_from_uri ( entry, NULL, NULL );
657 filenames = g_slist_append ( filenames, filename );
660 entry = entries[entry_runner];
664 g_signal_emit ( G_OBJECT(VIK_WINDOW_FROM_WIDGET(widget)), window_signals[VW_OPENWINDOW_SIGNAL], 0, filenames );
665 // NB: GSList & contents are freed by main.open_window
674 gtk_drag_finish ( context, success, FALSE, time );
677 #define VIK_SETTINGS_WIN_MAX "window_maximized"
678 #define VIK_SETTINGS_WIN_FULLSCREEN "window_fullscreen"
679 #define VIK_SETTINGS_WIN_WIDTH "window_width"
680 #define VIK_SETTINGS_WIN_HEIGHT "window_height"
681 #define VIK_SETTINGS_WIN_SAVE_IMAGE_WIDTH "window_save_image_width"
682 #define VIK_SETTINGS_WIN_SAVE_IMAGE_HEIGHT "window_save_image_height"
683 #define VIK_SETTINGS_WIN_SAVE_IMAGE_PNG "window_save_image_as_png"
685 static void vik_window_init ( VikWindow *vw )
687 GtkWidget *main_vbox;
690 vw->action_group = NULL;
692 vw->viking_vvp = vik_viewport_new();
693 vw->viking_vlp = vik_layers_panel_new();
694 vik_layers_panel_set_viewport ( vw->viking_vlp, vw->viking_vvp );
695 vw->viking_vs = vik_statusbar_new();
697 vw->vt = toolbox_create(vw);
698 window_create_ui(vw);
699 window_set_filename (vw, NULL);
700 vw->toolbar = GTK_TOOLBAR(gtk_ui_manager_get_widget (vw->uim, "/MainToolbar"));
702 vw->busy_cursor = gdk_cursor_new ( GDK_WATCH );
704 // Set the default tool
705 gtk_action_activate ( gtk_action_group_get_action ( vw->action_group, "Pan" ) );
708 vw->loaded_type = LOAD_TYPE_READ_FAILURE; //AKA none
709 vw->modified = FALSE;
710 vw->only_updating_coord_mode_ui = FALSE;
712 vw->pan_move = FALSE;
713 vw->pan_x = vw->pan_y = -1;
715 gint draw_image_width;
716 if ( a_settings_get_integer ( VIK_SETTINGS_WIN_SAVE_IMAGE_WIDTH, &draw_image_width ) )
717 vw->draw_image_width = draw_image_width;
719 vw->draw_image_width = DRAW_IMAGE_DEFAULT_WIDTH;
720 gint draw_image_height;
721 if ( a_settings_get_integer ( VIK_SETTINGS_WIN_SAVE_IMAGE_HEIGHT, &draw_image_height ) )
722 vw->draw_image_height = draw_image_height;
724 vw->draw_image_height = DRAW_IMAGE_DEFAULT_HEIGHT;
725 gboolean draw_image_save_as_png;
726 if ( a_settings_get_boolean ( VIK_SETTINGS_WIN_SAVE_IMAGE_PNG, &draw_image_save_as_png ) )
727 vw->draw_image_save_as_png = draw_image_save_as_png;
729 vw->draw_image_save_as_png = DRAW_IMAGE_DEFAULT_SAVE_AS_PNG;
731 main_vbox = gtk_vbox_new(FALSE, 1);
732 gtk_container_add (GTK_CONTAINER (vw), main_vbox);
734 gtk_box_pack_start (GTK_BOX(main_vbox), gtk_ui_manager_get_widget (vw->uim, "/MainMenu"), FALSE, TRUE, 0);
735 gtk_box_pack_start (GTK_BOX(main_vbox), GTK_WIDGET(vw->toolbar), FALSE, TRUE, 0);
736 gtk_toolbar_set_icon_size (vw->toolbar, GTK_ICON_SIZE_SMALL_TOOLBAR);
737 gtk_toolbar_set_style (vw->toolbar, GTK_TOOLBAR_ICONS);
739 vik_ext_tool_datasources_add_menu_items ( vw, vw->uim );
741 GtkWidget * zoom_levels = gtk_ui_manager_get_widget (vw->uim, "/MainMenu/View/SetZoom");
742 GtkWidget * zoom_levels_menu = create_zoom_menu_all_levels ( vik_viewport_get_zoom(vw->viking_vvp) );
743 gtk_menu_item_set_submenu (GTK_MENU_ITEM (zoom_levels), zoom_levels_menu);
744 g_signal_connect ( G_OBJECT(zoom_levels_menu), "selection-done", G_CALLBACK(zoom_changed), vw);
745 g_signal_connect_swapped ( G_OBJECT(vw->viking_vs), "clicked", G_CALLBACK(zoom_popup_handler), zoom_levels_menu );
747 g_signal_connect (G_OBJECT (vw), "delete_event", G_CALLBACK (delete_event), NULL);
749 g_signal_connect_swapped (G_OBJECT(vw->viking_vvp), "expose_event", G_CALLBACK(draw_sync), vw);
750 g_signal_connect_swapped (G_OBJECT(vw->viking_vvp), "configure_event", G_CALLBACK(window_configure_event), vw);
751 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 );
752 g_signal_connect_swapped (G_OBJECT(vw->viking_vvp), "scroll_event", G_CALLBACK(draw_scroll), vw);
753 g_signal_connect_swapped (G_OBJECT(vw->viking_vvp), "button_press_event", G_CALLBACK(draw_click), vw);
754 g_signal_connect_swapped (G_OBJECT(vw->viking_vvp), "button_release_event", G_CALLBACK(draw_release), vw);
755 g_signal_connect_swapped (G_OBJECT(vw->viking_vvp), "motion_notify_event", G_CALLBACK(draw_mouse_motion), vw);
756 g_signal_connect_swapped (G_OBJECT(vw->viking_vlp), "update", G_CALLBACK(draw_update), vw);
758 // Allow key presses to be processed anywhere
759 g_signal_connect_swapped (G_OBJECT (vw), "key_press_event", G_CALLBACK (key_press_event), vw);
761 hpaned = gtk_hpaned_new ();
762 gtk_paned_pack1 ( GTK_PANED(hpaned), GTK_WIDGET (vw->viking_vlp), FALSE, FALSE );
763 gtk_paned_pack2 ( GTK_PANED(hpaned), GTK_WIDGET (vw->viking_vvp), TRUE, TRUE );
765 /* This packs the button into the window (a gtk container). */
766 gtk_box_pack_start (GTK_BOX(main_vbox), hpaned, TRUE, TRUE, 0);
768 gtk_box_pack_end (GTK_BOX(main_vbox), GTK_WIDGET(vw->viking_vs), FALSE, TRUE, 0);
770 a_background_add_window ( vw );
772 window_list = g_slist_prepend ( window_list, vw);
774 gint height = VIKING_WINDOW_HEIGHT;
775 gint width = VIKING_WINDOW_WIDTH;
777 if ( a_vik_get_restore_window_state() ) {
778 if ( a_settings_get_integer ( VIK_SETTINGS_WIN_HEIGHT, &height ) ) {
779 // Enforce a basic minimum size
784 // No setting - so use default
785 height = VIKING_WINDOW_HEIGHT;
787 if ( a_settings_get_integer ( VIK_SETTINGS_WIN_WIDTH, &width ) ) {
788 // Enforce a basic minimum size
793 // No setting - so use default
794 width = VIKING_WINDOW_WIDTH;
797 if ( a_settings_get_boolean ( VIK_SETTINGS_WIN_MAX, &maxed ) )
799 gtk_window_maximize ( GTK_WINDOW(vw) );
802 if ( a_settings_get_boolean ( VIK_SETTINGS_WIN_FULLSCREEN, &full ) ) {
804 gtk_window_fullscreen ( GTK_WINDOW(vw) );
805 GtkWidget *check_box = gtk_ui_manager_get_widget ( vw->uim, "/ui/MainMenu/View/FullScreen" );
806 gtk_check_menu_item_set_active ( GTK_CHECK_MENU_ITEM(check_box), TRUE );
811 gtk_window_set_default_size ( GTK_WINDOW(vw), width, height );
815 vw->save_img_dia = NULL;
816 vw->save_img_dir_dia = NULL;
818 // Only accept Drag and Drop of files onto the viewport
819 gtk_drag_dest_set ( GTK_WIDGET(vw->viking_vvp), GTK_DEST_DEFAULT_ALL, NULL, 0, GDK_ACTION_COPY );
820 gtk_drag_dest_add_uri_targets ( GTK_WIDGET(vw->viking_vvp) );
821 g_signal_connect ( GTK_WIDGET(vw->viking_vvp), "drag-data-received", G_CALLBACK(drag_data_received_cb), NULL );
823 // Store the thread value so comparisons can be made to determine the gdk update method
824 // Hopefully we are storing the main thread value here :)
825 // [ATM any window initialization is always be performed by the main thread]
826 vw->thread = g_thread_self();
829 static VikWindow *window_new ()
831 return VIK_WINDOW ( g_object_new ( VIK_WINDOW_TYPE, NULL ) );
835 * Update the displayed map
836 * Only update the top most visible map layer
837 * ATM this assumes (as per defaults) the top most map has full alpha setting
838 * such that other other maps even though they may be active will not be seen
839 * It's more complicated to work out which maps are actually visible due to alpha settings
840 * and overkill for this simple refresh method.
842 static void simple_map_update ( VikWindow *vw, gboolean only_new )
844 // Find the most relevent single map layer to operate on
845 VikLayer *vl = vik_aggregate_layer_get_top_visible_layer_of_type (vik_layers_panel_get_top_layer(vw->viking_vlp), VIK_LAYER_MAPS);
847 vik_maps_layer_download ( VIK_MAPS_LAYER(vl), vw->viking_vvp, only_new );
851 * This is the global key press handler
852 * Global shortcuts are available at any time and hence are not restricted to when a certain tool is enabled
854 static gboolean key_press_event( VikWindow *vw, GdkEventKey *event, gpointer data )
856 // The keys handled here are not in the menuing system for a couple of reasons:
857 // . Keeps the menu size compact (alebit at expense of discoverably)
858 // . Allows differing key bindings to perform the same actions
860 // First decide if key events are related to the maps layer
861 gboolean map_download = FALSE;
862 gboolean map_download_only_new = TRUE; // Only new or reload
864 GdkModifierType modifiers = gtk_accelerator_get_default_mod_mask();
866 // Standard 'Refresh' keys: F5 or Ctrl+r
867 // Note 'F5' is actually handled via draw_refresh_cb() later on
868 // (not 'R' it's 'r' notice the case difference!!)
869 if ( event->keyval == GDK_r && (event->state & modifiers) == GDK_CONTROL_MASK ) {
871 map_download_only_new = TRUE;
873 // Full cache reload with Ctrl+F5 or Ctrl+Shift+r [This is not in the menu system]
874 // Note the use of uppercase R here since shift key has been pressed
875 else if ( (event->keyval == GDK_F5 && (event->state & modifiers) == GDK_CONTROL_MASK ) ||
876 ( event->keyval == GDK_R && (event->state & modifiers) == (GDK_CONTROL_MASK + GDK_SHIFT_MASK) ) ) {
878 map_download_only_new = FALSE;
881 if ( map_download ) {
882 simple_map_update ( vw, map_download_only_new );
885 VikLayer *vl = vik_layers_panel_get_selected ( vw->viking_vlp );
886 if (vl && vw->vt->active_tool != -1 && vw->vt->tools[vw->vt->active_tool].ti.key_press ) {
887 gint ltype = vw->vt->tools[vw->vt->active_tool].layer_type;
888 if ( vl && ltype == vl->type )
889 return vw->vt->tools[vw->vt->active_tool].ti.key_press(vl, event, vw->vt->tools[vw->vt->active_tool].state);
892 // Ensure called only on window tools (i.e. not on any of the Layer tools since the layer is NULL)
893 if ( vw->current_tool < TOOL_LAYER ) {
894 // No layer - but enable window tool keypress processing - these should be able to handle a NULL layer
895 if ( vw->vt->tools[vw->vt->active_tool].ti.key_press ) {
896 return vw->vt->tools[vw->vt->active_tool].ti.key_press ( vl, event, vw->vt->tools[vw->vt->active_tool].state );
900 /* Restore Main Menu via Escape key if the user has hidden it */
901 /* This key is more likely to be used as they may not remember the function key */
902 if ( event->keyval == GDK_Escape ) {
903 GtkWidget *check_box = gtk_ui_manager_get_widget ( vw->uim, "/ui/MainMenu/View/SetShow/ViewMainMenu" );
905 gboolean state = gtk_check_menu_item_get_active ( GTK_CHECK_MENU_ITEM(check_box) );
907 gtk_widget_show ( gtk_ui_manager_get_widget ( vw->uim, "/ui/MainMenu" ) );
908 gtk_check_menu_item_set_active ( GTK_CHECK_MENU_ITEM(check_box), TRUE );
909 return TRUE; /* handled keypress */
914 return FALSE; /* don't handle the keypress */
917 static gboolean delete_event( VikWindow *vw )
919 #ifdef VIKING_PROMPT_IF_MODIFIED
926 dia = GTK_DIALOG ( gtk_message_dialog_new ( GTK_WINDOW(vw), GTK_DIALOG_DESTROY_WITH_PARENT, GTK_MESSAGE_QUESTION, GTK_BUTTONS_NONE,
927 _("Do you want to save the changes you made to the document \"%s\"?\n"
929 "Your changes will be lost if you don't save them."),
930 window_get_filename ( vw ) ) );
931 gtk_dialog_add_buttons ( dia, _("Don't Save"), GTK_RESPONSE_NO, GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, GTK_STOCK_SAVE, GTK_RESPONSE_YES, NULL );
932 switch ( gtk_dialog_run ( dia ) )
934 case GTK_RESPONSE_NO: gtk_widget_destroy ( GTK_WIDGET(dia) ); return FALSE;
935 case GTK_RESPONSE_CANCEL: gtk_widget_destroy ( GTK_WIDGET(dia) ); return TRUE;
936 default: gtk_widget_destroy ( GTK_WIDGET(dia) ); return ! save_file(NULL, vw);
940 if ( window_count == 1 ) {
941 // On the final window close - save latest state - if it's wanted...
942 if ( a_vik_get_restore_window_state() ) {
943 gint state = gdk_window_get_state ( GTK_WIDGET(vw)->window );
944 gboolean state_max = state & GDK_WINDOW_STATE_MAXIMIZED;
945 a_settings_set_boolean ( VIK_SETTINGS_WIN_MAX, state_max );
947 gboolean state_fullscreen = state & GDK_WINDOW_STATE_FULLSCREEN;
948 a_settings_set_boolean ( VIK_SETTINGS_WIN_FULLSCREEN, state_fullscreen );
950 a_settings_set_boolean ( VIK_SETTINGS_WIN_SIDEPANEL, GTK_WIDGET_VISIBLE (GTK_WIDGET(vw->viking_vlp)) );
952 a_settings_set_boolean ( VIK_SETTINGS_WIN_STATUSBAR, GTK_WIDGET_VISIBLE (GTK_WIDGET(vw->viking_vs)) );
954 a_settings_set_boolean ( VIK_SETTINGS_WIN_TOOLBAR, GTK_WIDGET_VISIBLE (GTK_WIDGET(vw->toolbar)) );
956 // If supersized - no need to save the enlarged width+height values
957 if ( ! (state_fullscreen || state_max) ) {
959 gtk_window_get_size ( GTK_WINDOW (vw), &width, &height );
960 a_settings_set_integer ( VIK_SETTINGS_WIN_WIDTH, width );
961 a_settings_set_integer ( VIK_SETTINGS_WIN_HEIGHT, height );
965 a_settings_set_integer ( VIK_SETTINGS_WIN_SAVE_IMAGE_WIDTH, vw->draw_image_width );
966 a_settings_set_integer ( VIK_SETTINGS_WIN_SAVE_IMAGE_HEIGHT, vw->draw_image_height );
967 a_settings_set_boolean ( VIK_SETTINGS_WIN_SAVE_IMAGE_PNG, vw->draw_image_save_as_png );
974 static void newwindow_cb ( GtkAction *a, VikWindow *vw )
976 g_signal_emit ( G_OBJECT(vw), window_signals[VW_NEWWINDOW_SIGNAL], 0 );
979 static void draw_update ( VikWindow *vw )
985 static void draw_sync ( VikWindow *vw )
987 vik_viewport_sync(vw->viking_vvp);
992 * Split the status update, as sometimes only need to update the tool part
993 * also on initialization the zoom related stuff is not ready to be used
995 static void draw_status_tool ( VikWindow *vw )
997 if ( vw->current_tool == TOOL_LAYER )
998 // Use tooltip rather than the internal name as the tooltip is i8n
999 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 );
1001 vik_statusbar_set_message ( vw->viking_vs, VIK_STATUSBAR_TOOL, _(tool_names[vw->current_tool]) );
1004 static void draw_status ( VikWindow *vw )
1006 static gchar zoom_level[22];
1007 gdouble xmpp = vik_viewport_get_xmpp (vw->viking_vvp);
1008 gdouble ympp = vik_viewport_get_ympp(vw->viking_vvp);
1009 gchar *unit = vik_viewport_get_coord_mode(vw->viking_vvp) == VIK_COORD_UTM ? _("mpp") : _("pixelfact");
1011 g_snprintf ( zoom_level, 22, "%.3f/%.3f %s", xmpp, ympp, unit );
1013 if ( (int)xmpp - xmpp < 0.0 )
1014 g_snprintf ( zoom_level, 22, "%.3f %s", xmpp, unit );
1016 /* xmpp should be a whole number so don't show useless .000 bit */
1017 g_snprintf ( zoom_level, 22, "%d %s", (int)xmpp, unit );
1019 vik_statusbar_set_message ( vw->viking_vs, VIK_STATUSBAR_ZOOM, zoom_level );
1021 draw_status_tool ( vw );
1024 void vik_window_set_redraw_trigger(VikLayer *vl)
1026 VikWindow *vw = VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vl));
1031 static void window_configure_event ( VikWindow *vw )
1033 static int first = 1;
1036 // This is a hack to set the cursor corresponding to the first tool
1037 // FIXME find the correct way to initialize both tool and its cursor
1039 vw->viewport_cursor = (GdkCursor *)toolbox_get_cursor(vw->vt, "Pan");
1040 /* We set cursor, even if it is NULL: it resets to default */
1041 gdk_window_set_cursor ( gtk_widget_get_window(GTK_WIDGET(vw->viking_vvp)), vw->viewport_cursor );
1045 static void draw_redraw ( VikWindow *vw )
1047 VikCoord old_center = vw->trigger_center;
1048 vw->trigger_center = *(vik_viewport_get_center(vw->viking_vvp));
1049 VikLayer *new_trigger = vw->trigger;
1051 VikLayer *old_trigger = VIK_LAYER(vik_viewport_get_trigger(vw->viking_vvp));
1053 if ( ! new_trigger )
1054 ; /* do nothing -- have to redraw everything. */
1055 else if ( (old_trigger != new_trigger) || !vik_coord_equals(&old_center, &vw->trigger_center) || (new_trigger->type == VIK_LAYER_AGGREGATE) )
1056 vik_viewport_set_trigger ( vw->viking_vvp, new_trigger ); /* todo: set to half_drawn mode if new trigger is above old */
1058 vik_viewport_set_half_drawn ( vw->viking_vvp, TRUE );
1061 vik_viewport_clear ( vw->viking_vvp);
1062 vik_layers_panel_draw_all ( vw->viking_vlp );
1063 vik_viewport_draw_scale ( vw->viking_vvp );
1064 vik_viewport_draw_copyright ( vw->viking_vvp );
1065 vik_viewport_draw_centermark ( vw->viking_vvp );
1066 vik_viewport_draw_logo ( vw->viking_vvp );
1068 vik_viewport_set_half_drawn ( vw->viking_vvp, FALSE ); /* just in case. */
1071 gboolean draw_buf_done = TRUE;
1073 static gboolean draw_buf(gpointer data)
1075 gpointer *pass_along = data;
1076 gdk_threads_enter();
1077 gdk_draw_drawable (pass_along[0], pass_along[1],
1078 pass_along[2], 0, 0, 0, 0, -1, -1);
1079 draw_buf_done = TRUE;
1080 gdk_threads_leave();
1085 /* Mouse event handlers ************************************************************************/
1087 static void vik_window_pan_click (VikWindow *vw, GdkEventButton *event)
1089 /* set panning origin */
1090 vw->pan_move = FALSE;
1091 vw->pan_x = (gint) event->x;
1092 vw->pan_y = (gint) event->y;
1095 static void draw_click (VikWindow *vw, GdkEventButton *event)
1097 gtk_widget_grab_focus ( GTK_WIDGET(vw->viking_vvp) );
1099 /* middle button pressed. we reserve all middle button and scroll events
1100 * for panning and zooming; tools only get left/right/movement
1102 if ( event->button == 2) {
1103 if ( vw->vt->tools[vw->vt->active_tool].ti.pan_handler )
1104 // Tool still may need to do something (such as disable something)
1105 toolbox_click(vw->vt, event);
1106 vik_window_pan_click ( vw, event );
1109 toolbox_click(vw->vt, event);
1113 static void vik_window_pan_move (VikWindow *vw, GdkEventMotion *event)
1115 if ( vw->pan_x != -1 ) {
1116 vik_viewport_set_center_screen ( vw->viking_vvp, vik_viewport_get_width(vw->viking_vvp)/2 - event->x + vw->pan_x,
1117 vik_viewport_get_height(vw->viking_vvp)/2 - event->y + vw->pan_y );
1118 vw->pan_move = TRUE;
1119 vw->pan_x = event->x;
1120 vw->pan_y = event->y;
1125 static void draw_mouse_motion (VikWindow *vw, GdkEventMotion *event)
1127 static VikCoord coord;
1128 static struct UTM utm;
1129 static struct LatLon ll;
1130 #define BUFFER_SIZE 50
1131 static char pointer_buf[BUFFER_SIZE];
1132 gchar *lat = NULL, *lon = NULL;
1135 VikDemInterpol interpol_method;
1137 /* This is a hack, but work far the best, at least for single pointer systems.
1138 * See http://bugzilla.gnome.org/show_bug.cgi?id=587714 for more. */
1140 gdk_window_get_pointer (event->window, &x, &y, NULL);
1144 toolbox_move(vw->vt, event);
1146 vik_viewport_screen_to_coord ( vw->viking_vvp, event->x, event->y, &coord );
1147 vik_coord_to_utm ( &coord, &utm );
1149 if ( vik_viewport_get_drawmode ( vw->viking_vvp ) == VIK_VIEWPORT_DRAWMODE_UTM ) {
1150 // Reuse lat for the first part (Zone + N or S, and lon for the second part (easting and northing) of a UTM format:
1151 // ZONE[N|S] EASTING NORTHING
1152 lat = g_malloc(4*sizeof(gchar));
1153 // NB zone is stored in a char but is an actual number
1154 g_snprintf (lat, 4, "%d%c", utm.zone, utm.letter);
1155 lon = g_malloc(16*sizeof(gchar));
1156 g_snprintf (lon, 16, "%d %d", (gint)utm.easting, (gint)utm.northing);
1159 a_coords_utm_to_latlon ( &utm, &ll );
1160 a_coords_latlon_to_string ( &ll, &lat, &lon );
1163 /* Change interpolate method according to scale */
1164 zoom = vik_viewport_get_zoom(vw->viking_vvp);
1166 interpol_method = VIK_DEM_INTERPOL_NONE;
1167 else if (zoom >= 1.0)
1168 interpol_method = VIK_DEM_INTERPOL_SIMPLE;
1170 interpol_method = VIK_DEM_INTERPOL_BEST;
1171 if ((alt = a_dems_get_elev_by_coord(&coord, interpol_method)) != VIK_DEM_INVALID_ELEVATION) {
1172 if ( a_vik_get_units_height () == VIK_UNITS_HEIGHT_METRES )
1173 g_snprintf ( pointer_buf, BUFFER_SIZE, _("%s %s %dm"), lat, lon, alt );
1175 g_snprintf ( pointer_buf, BUFFER_SIZE, _("%s %s %dft"), lat, lon, (int)VIK_METERS_TO_FEET(alt) );
1178 g_snprintf ( pointer_buf, BUFFER_SIZE, _("%s %s"), lat, lon );
1183 vik_statusbar_set_message ( vw->viking_vs, VIK_STATUSBAR_POSITION, pointer_buf );
1185 vik_window_pan_move ( vw, event );
1187 /* This is recommended by the GTK+ documentation, but does not work properly.
1188 * Use deprecated way until GTK+ gets a solution for correct motion hint handling:
1189 * http://bugzilla.gnome.org/show_bug.cgi?id=587714
1191 /* gdk_event_request_motions ( event ); */
1194 static void vik_window_pan_release ( VikWindow *vw, GdkEventButton *event )
1196 if ( vw->pan_move == FALSE )
1197 vik_viewport_set_center_screen ( vw->viking_vvp, vw->pan_x, vw->pan_y );
1199 vik_viewport_set_center_screen ( vw->viking_vvp, vik_viewport_get_width(vw->viking_vvp)/2 - event->x + vw->pan_x,
1200 vik_viewport_get_height(vw->viking_vvp)/2 - event->y + vw->pan_y );
1201 vw->pan_move = FALSE;
1202 vw->pan_x = vw->pan_y = -1;
1206 static void draw_release ( VikWindow *vw, GdkEventButton *event )
1208 gtk_widget_grab_focus ( GTK_WIDGET(vw->viking_vvp) );
1210 if ( event->button == 2 ) { /* move / pan */
1211 if ( vw->vt->tools[vw->vt->active_tool].ti.pan_handler )
1212 // Tool still may need to do something (such as reenable something)
1213 toolbox_release(vw->vt, event);
1214 vik_window_pan_release ( vw, event );
1217 toolbox_release(vw->vt, event);
1221 static void draw_scroll (VikWindow *vw, GdkEventScroll *event)
1223 guint modifiers = event->state & (GDK_SHIFT_MASK | GDK_CONTROL_MASK);
1224 if ( modifiers == GDK_CONTROL_MASK ) {
1225 /* control == pan up & down */
1226 if ( event->direction == GDK_SCROLL_UP )
1227 vik_viewport_set_center_screen ( vw->viking_vvp, vik_viewport_get_width(vw->viking_vvp)/2, vik_viewport_get_height(vw->viking_vvp)/3 );
1229 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 );
1230 } else if ( modifiers == GDK_SHIFT_MASK ) {
1231 /* shift == pan left & right */
1232 if ( event->direction == GDK_SCROLL_UP )
1233 vik_viewport_set_center_screen ( vw->viking_vvp, vik_viewport_get_width(vw->viking_vvp)/3, vik_viewport_get_height(vw->viking_vvp)/2 );
1235 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 );
1236 } else if ( modifiers == (GDK_CONTROL_MASK | GDK_SHIFT_MASK) ) {
1237 // This zoom is on the center position
1238 if ( event->direction == GDK_SCROLL_UP )
1239 vik_viewport_zoom_in (vw->viking_vvp);
1241 vik_viewport_zoom_out (vw->viking_vvp);
1243 /* make sure mouse is still over the same point on the map when we zoom */
1246 gint center_x = vik_viewport_get_width ( vw->viking_vvp ) / 2;
1247 gint center_y = vik_viewport_get_height ( vw->viking_vvp ) / 2;
1248 vik_viewport_screen_to_coord ( vw->viking_vvp, event->x, event->y, &coord );
1249 if ( event->direction == GDK_SCROLL_UP )
1250 vik_viewport_zoom_in (vw->viking_vvp);
1252 vik_viewport_zoom_out(vw->viking_vvp);
1253 vik_viewport_coord_to_screen ( vw->viking_vvp, &coord, &x, &y );
1254 vik_viewport_set_center_screen ( vw->viking_vvp, center_x + (x - event->x),
1255 center_y + (y - event->y) );
1263 /********************************************************************************
1265 ********************************************************************************/
1266 static void draw_ruler(VikViewport *vvp, GdkDrawable *d, GdkGC *gc, gint x1, gint y1, gint x2, gint y2, gdouble distance)
1270 GdkGC *labgc = vik_viewport_new_gc ( vvp, "#cccccc", 1);
1271 GdkGC *thickgc = gdk_gc_new(d);
1273 gdouble len = sqrt((x1-x2)*(x1-x2) + (y1-y2)*(y1-y2));
1274 gdouble dx = (x2-x1)/len*10;
1275 gdouble dy = (y2-y1)/len*10;
1276 gdouble c = cos(DEG2RAD(15.0));
1277 gdouble s = sin(DEG2RAD(15.0));
1279 gdouble baseangle = 0;
1282 /* draw line with arrow ends */
1284 gint tmp_x1=x1, tmp_y1=y1, tmp_x2=x2, tmp_y2=y2;
1285 a_viewport_clip_line(&tmp_x1, &tmp_y1, &tmp_x2, &tmp_y2);
1286 gdk_draw_line(d, gc, tmp_x1, tmp_y1, tmp_x2, tmp_y2);
1289 a_viewport_clip_line(&x1, &y1, &x2, &y2);
1290 gdk_draw_line(d, gc, x1, y1, x2, y2);
1292 gdk_draw_line(d, gc, x1 - dy, y1 + dx, x1 + dy, y1 - dx);
1293 gdk_draw_line(d, gc, x2 - dy, y2 + dx, x2 + dy, y2 - dx);
1294 gdk_draw_line(d, gc, x2, y2, x2 - (dx * c + dy * s), y2 - (dy * c - dx * s));
1295 gdk_draw_line(d, gc, x2, y2, x2 - (dx * c - dy * s), y2 - (dy * c + dx * s));
1296 gdk_draw_line(d, gc, x1, y1, x1 + (dx * c + dy * s), y1 + (dy * c - dx * s));
1297 gdk_draw_line(d, gc, x1, y1, x1 + (dx * c - dy * s), y1 + (dy * c + dx * s));
1303 vik_viewport_compute_bearing ( vvp, x1, y1, x2, y2, &angle, &baseangle );
1307 gdk_gc_copy(thickgc, gc);
1308 gdk_gc_set_line_attributes(thickgc, CW, GDK_LINE_SOLID, GDK_CAP_BUTT, GDK_JOIN_MITER);
1309 gdk_color_parse("#2255cc", &color);
1310 gdk_gc_set_rgb_fg_color(thickgc, &color);
1312 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);
1315 gdk_gc_copy(thickgc, gc);
1316 gdk_gc_set_line_attributes(thickgc, 2, GDK_LINE_SOLID, GDK_CAP_BUTT, GDK_JOIN_MITER);
1317 for (i=0; i<180; i++) {
1318 c = cos(DEG2RAD(i)*2 + baseangle);
1319 s = sin(DEG2RAD(i)*2 + baseangle);
1322 gdk_draw_line (d, gc, x1 + CR*c, y1 + CR*s, x1 + (CR+CW)*c, y1 + (CR+CW)*s);
1324 gdouble ticksize = 2*CW;
1325 gdk_draw_line (d, thickgc, x1 + (CR-CW)*c, y1 + (CR-CW)*s, x1 + (CR+ticksize)*c, y1 + (CR+ticksize)*s);
1329 gdk_draw_arc (d, gc, FALSE, x1-CR, y1-CR, 2*CR, 2*CR, 0, 64*360);
1330 gdk_draw_arc (d, gc, FALSE, x1-CR-CW, y1-CR-CW, 2*(CR+CW), 2*(CR+CW), 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 c = (CR+CW*2)*cos(baseangle);
1333 s = (CR+CW*2)*sin(baseangle);
1334 gdk_draw_line (d, gc, x1-c, y1-s, x1+c, y1+s);
1335 gdk_draw_line (d, gc, x1+s, y1-c, x1-s, y1+c);
1338 #define LABEL(x, y, w, h) { \
1339 gdk_draw_rectangle(d, labgc, TRUE, (x)-2, (y)-1, (w)+4, (h)+1); \
1340 gdk_draw_rectangle(d, gc, FALSE, (x)-2, (y)-1, (w)+4, (h)+1); \
1341 gdk_draw_layout(d, gc, (x), (y), pl); }
1343 gint wd, hd, xd, yd;
1344 gint wb, hb, xb, yb;
1346 pl = gtk_widget_create_pango_layout (GTK_WIDGET(vvp), NULL);
1347 pango_layout_set_font_description (pl, gtk_widget_get_style(GTK_WIDGET(vvp))->font_desc);
1348 pango_layout_set_text(pl, "N", -1);
1349 gdk_draw_layout(d, gc, x1-5, y1-CR-3*CW-8, pl);
1351 /* draw label with distance */
1352 vik_units_distance_t dist_units = a_vik_get_units_distance ();
1353 switch (dist_units) {
1354 case VIK_UNITS_DISTANCE_KILOMETRES:
1355 if (distance >= 1000 && distance < 100000) {
1356 g_sprintf(str, "%3.2f km", distance/1000.0);
1357 } else if (distance < 1000) {
1358 g_sprintf(str, "%d m", (int)distance);
1360 g_sprintf(str, "%d km", (int)distance/1000);
1363 case VIK_UNITS_DISTANCE_MILES:
1364 if (distance >= VIK_MILES_TO_METERS(1) && distance < VIK_MILES_TO_METERS(100)) {
1365 g_sprintf(str, "%3.2f miles", VIK_METERS_TO_MILES(distance));
1366 } else if (distance < VIK_MILES_TO_METERS(1)) {
1367 g_sprintf(str, "%d yards", (int)(distance*1.0936133));
1369 g_sprintf(str, "%d miles", (int)VIK_METERS_TO_MILES(distance));
1373 g_critical("Houston, we've had a problem. distance=%d", dist_units);
1376 pango_layout_set_text(pl, str, -1);
1378 pango_layout_get_pixel_size ( pl, &wd, &hd );
1380 xd = (x1+x2)/2 + dy;
1381 yd = (y1+y2)/2 - hd/2 - dx;
1383 xd = (x1+x2)/2 - dy;
1384 yd = (y1+y2)/2 - hd/2 + dx;
1387 if ( xd < -5 || yd < -5 || xd > vik_viewport_get_width(vvp)+5 || yd > vik_viewport_get_height(vvp)+5 ) {
1392 LABEL(xd, yd, wd, hd);
1394 /* draw label with bearing */
1395 g_sprintf(str, "%3.1f°", RAD2DEG(angle));
1396 pango_layout_set_text(pl, str, -1);
1397 pango_layout_get_pixel_size ( pl, &wb, &hb );
1398 xb = x1 + CR*cos(angle-M_PI_2);
1399 yb = y1 + CR*sin(angle-M_PI_2);
1401 if ( xb < -5 || yb < -5 || xb > vik_viewport_get_width(vvp)+5 || yb > vik_viewport_get_height(vvp)+5 ) {
1407 GdkRectangle r1 = {xd-2, yd-1, wd+4, hd+1}, r2 = {xb-2, yb-1, wb+4, hb+1};
1408 if (gdk_rectangle_intersect(&r1, &r2, &r2)) {
1412 LABEL(xb, yb, wb, hb);
1416 g_object_unref ( G_OBJECT ( pl ) );
1417 g_object_unref ( G_OBJECT ( labgc ) );
1418 g_object_unref ( G_OBJECT ( thickgc ) );
1424 gboolean has_oldcoord;
1426 } ruler_tool_state_t;
1428 static gpointer ruler_create (VikWindow *vw, VikViewport *vvp)
1430 ruler_tool_state_t *s = g_new(ruler_tool_state_t, 1);
1433 s->has_oldcoord = FALSE;
1437 static void ruler_destroy (ruler_tool_state_t *s)
1442 static VikLayerToolFuncStatus ruler_click (VikLayer *vl, GdkEventButton *event, ruler_tool_state_t *s)
1447 if ( event->button == 1 ) {
1448 gchar *lat=NULL, *lon=NULL;
1449 vik_viewport_screen_to_coord ( s->vvp, (gint) event->x, (gint) event->y, &coord );
1450 vik_coord_to_latlon ( &coord, &ll );
1451 a_coords_latlon_to_string ( &ll, &lat, &lon );
1452 if ( s->has_oldcoord ) {
1453 vik_units_distance_t dist_units = a_vik_get_units_distance ();
1454 switch (dist_units) {
1455 case VIK_UNITS_DISTANCE_KILOMETRES:
1456 temp = g_strdup_printf ( "%s %s DIFF %f meters", lat, lon, vik_coord_diff( &coord, &(s->oldcoord) ) );
1458 case VIK_UNITS_DISTANCE_MILES:
1459 temp = g_strdup_printf ( "%s %s DIFF %f miles", lat, lon, VIK_METERS_TO_MILES(vik_coord_diff( &coord, &(s->oldcoord) )) );
1462 temp = g_strdup_printf ("Just to keep the compiler happy");
1463 g_critical("Houston, we've had a problem. distance=%d", dist_units);
1466 s->has_oldcoord = FALSE;
1469 temp = g_strdup_printf ( "%s %s", lat, lon );
1470 s->has_oldcoord = TRUE;
1473 vik_statusbar_set_message ( s->vw->viking_vs, VIK_STATUSBAR_INFO, temp );
1476 s->oldcoord = coord;
1479 vik_viewport_set_center_screen ( s->vvp, (gint) event->x, (gint) event->y );
1480 draw_update ( s->vw );
1482 return VIK_LAYER_TOOL_ACK;
1485 static VikLayerToolFuncStatus ruler_move (VikLayer *vl, GdkEventMotion *event, ruler_tool_state_t *s)
1487 VikViewport *vvp = s->vvp;
1488 VikWindow *vw = s->vw;
1494 if ( s->has_oldcoord ) {
1495 int oldx, oldy, w1, h1, w2, h2;
1496 static GdkPixmap *buf = NULL;
1497 gchar *lat=NULL, *lon=NULL;
1498 w1 = vik_viewport_get_width(vvp);
1499 h1 = vik_viewport_get_height(vvp);
1501 buf = gdk_pixmap_new ( gtk_widget_get_window(GTK_WIDGET(vvp)), w1, h1, -1 );
1503 gdk_drawable_get_size(buf, &w2, &h2);
1504 if (w1 != w2 || h1 != h2) {
1505 g_object_unref ( G_OBJECT ( buf ) );
1506 buf = gdk_pixmap_new ( gtk_widget_get_window(GTK_WIDGET(vvp)), w1, h1, -1 );
1509 vik_viewport_screen_to_coord ( vvp, (gint) event->x, (gint) event->y, &coord );
1510 vik_coord_to_latlon ( &coord, &ll );
1511 vik_viewport_coord_to_screen ( vvp, &s->oldcoord, &oldx, &oldy );
1513 gdk_draw_drawable (buf, gtk_widget_get_style(GTK_WIDGET(vvp))->black_gc,
1514 vik_viewport_get_pixmap(vvp), 0, 0, 0, 0, -1, -1);
1515 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)) );
1516 if (draw_buf_done) {
1517 static gpointer pass_along[3];
1518 pass_along[0] = gtk_widget_get_window(GTK_WIDGET(vvp));
1519 pass_along[1] = gtk_widget_get_style(GTK_WIDGET(vvp))->black_gc;
1520 pass_along[2] = buf;
1521 g_idle_add_full (G_PRIORITY_HIGH_IDLE + 10, draw_buf, pass_along, NULL);
1522 draw_buf_done = FALSE;
1524 a_coords_latlon_to_string(&ll, &lat, &lon);
1525 vik_units_distance_t dist_units = a_vik_get_units_distance ();
1526 switch (dist_units) {
1527 case VIK_UNITS_DISTANCE_KILOMETRES:
1528 temp = g_strdup_printf ( "%s %s DIFF %f meters", lat, lon, vik_coord_diff( &coord, &(s->oldcoord) ) );
1530 case VIK_UNITS_DISTANCE_MILES:
1531 temp = g_strdup_printf ( "%s %s DIFF %f miles", lat, lon, VIK_METERS_TO_MILES (vik_coord_diff( &coord, &(s->oldcoord) )) );
1534 temp = g_strdup_printf ("Just to keep the compiler happy");
1535 g_critical("Houston, we've had a problem. distance=%d", dist_units);
1537 vik_statusbar_set_message ( vw->viking_vs, VIK_STATUSBAR_INFO, temp );
1540 return VIK_LAYER_TOOL_ACK;
1543 static VikLayerToolFuncStatus ruler_release (VikLayer *vl, GdkEventButton *event, ruler_tool_state_t *s)
1545 return VIK_LAYER_TOOL_ACK;
1548 static void ruler_deactivate (VikLayer *vl, ruler_tool_state_t *s)
1550 draw_update ( s->vw );
1553 static gboolean ruler_key_press (VikLayer *vl, GdkEventKey *event, ruler_tool_state_t *s)
1555 if (event->keyval == GDK_Escape) {
1556 s->has_oldcoord = FALSE;
1557 ruler_deactivate ( vl, s );
1560 // Regardless of whether we used it, return false so other GTK things may use it
1564 static VikToolInterface ruler_tool =
1565 // NB Ctrl+Shift+R is used for Refresh (deemed more important), so use 'U' instead
1566 { { "Ruler", "vik-icon-ruler", N_("_Ruler"), "<control><shift>U", N_("Ruler Tool"), 2 },
1567 (VikToolConstructorFunc) ruler_create,
1568 (VikToolDestructorFunc) ruler_destroy,
1569 (VikToolActivationFunc) NULL,
1570 (VikToolActivationFunc) ruler_deactivate,
1571 (VikToolMouseFunc) ruler_click,
1572 (VikToolMouseMoveFunc) ruler_move,
1573 (VikToolMouseFunc) ruler_release,
1574 (VikToolKeyFunc) ruler_key_press,
1576 GDK_CURSOR_IS_PIXMAP,
1577 &cursor_ruler_pixbuf };
1578 /*** end ruler code ********************************************************/
1582 /********************************************************************************
1584 ********************************************************************************/
1589 // Track zoom bounds for zoom tool with shift modifier:
1590 gboolean bounds_active;
1593 } zoom_tool_state_t;
1596 * In case the screen size has changed
1598 static void zoomtool_resize_pixmap (zoom_tool_state_t *zts)
1602 // Allocate a drawing area the size of the viewport
1603 w1 = vik_viewport_get_width ( zts->vw->viking_vvp );
1604 h1 = vik_viewport_get_height ( zts->vw->viking_vvp );
1606 if ( !zts->pixmap ) {
1608 zts->pixmap = gdk_pixmap_new ( gtk_widget_get_window(GTK_WIDGET(zts->vw->viking_vvp)), w1, h1, -1 );
1611 gdk_drawable_get_size ( zts->pixmap, &w2, &h2 );
1613 if ( w1 != w2 || h1 != h2 ) {
1614 // Has changed - delete and recreate with new values
1615 g_object_unref ( G_OBJECT ( zts->pixmap ) );
1616 zts->pixmap = gdk_pixmap_new ( gtk_widget_get_window(GTK_WIDGET(zts->vw->viking_vvp)), w1, h1, -1 );
1620 static gpointer zoomtool_create (VikWindow *vw, VikViewport *vvp)
1622 zoom_tool_state_t *zts = g_new(zoom_tool_state_t, 1);
1627 zts->bounds_active = FALSE;
1631 static void zoomtool_destroy ( zoom_tool_state_t *zts)
1634 g_object_unref ( G_OBJECT ( zts->pixmap ) );
1638 static VikLayerToolFuncStatus zoomtool_click (VikLayer *vl, GdkEventButton *event, zoom_tool_state_t *zts)
1640 zts->vw->modified = TRUE;
1641 guint modifiers = event->state & (GDK_SHIFT_MASK | GDK_CONTROL_MASK);
1645 gint center_x = vik_viewport_get_width ( zts->vw->viking_vvp ) / 2;
1646 gint center_y = vik_viewport_get_height ( zts->vw->viking_vvp ) / 2;
1648 gboolean skip_update = FALSE;
1650 zts->bounds_active = FALSE;
1652 if ( modifiers == (GDK_CONTROL_MASK | GDK_SHIFT_MASK) ) {
1653 // This zoom is on the center position
1654 vik_viewport_set_center_screen ( zts->vw->viking_vvp, center_x, center_y );
1655 if ( event->button == 1 )
1656 vik_viewport_zoom_in (zts->vw->viking_vvp);
1657 else if ( event->button == 3 )
1658 vik_viewport_zoom_out (zts->vw->viking_vvp);
1660 else if ( modifiers == GDK_CONTROL_MASK ) {
1661 // This zoom is to recenter on the mouse position
1662 vik_viewport_set_center_screen ( zts->vw->viking_vvp, (gint) event->x, (gint) event->y );
1663 if ( event->button == 1 )
1664 vik_viewport_zoom_in (zts->vw->viking_vvp);
1665 else if ( event->button == 3 )
1666 vik_viewport_zoom_out (zts->vw->viking_vvp);
1668 else if ( modifiers == GDK_SHIFT_MASK ) {
1669 // Get start of new zoom bounds
1670 if ( event->button == 1 ) {
1671 zts->bounds_active = TRUE;
1672 zts->start_x = (gint) event->x;
1673 zts->start_y = (gint) event->y;
1678 /* make sure mouse is still over the same point on the map when we zoom */
1679 vik_viewport_screen_to_coord ( zts->vw->viking_vvp, event->x, event->y, &coord );
1680 if ( event->button == 1 )
1681 vik_viewport_zoom_in (zts->vw->viking_vvp);
1682 else if ( event->button == 3 )
1683 vik_viewport_zoom_out(zts->vw->viking_vvp);
1684 vik_viewport_coord_to_screen ( zts->vw->viking_vvp, &coord, &x, &y );
1685 vik_viewport_set_center_screen ( zts->vw->viking_vvp,
1686 center_x + (x - event->x),
1687 center_y + (y - event->y) );
1691 draw_update ( zts->vw );
1693 return VIK_LAYER_TOOL_ACK;
1696 static VikLayerToolFuncStatus zoomtool_move (VikLayer *vl, GdkEventMotion *event, zoom_tool_state_t *zts)
1698 guint modifiers = event->state & (GDK_SHIFT_MASK | GDK_CONTROL_MASK);
1700 if ( zts->bounds_active && modifiers == GDK_SHIFT_MASK ) {
1701 zoomtool_resize_pixmap ( zts );
1703 // Blank out currently drawn area
1704 gdk_draw_drawable ( zts->pixmap,
1705 gtk_widget_get_style(GTK_WIDGET(zts->vw->viking_vvp))->black_gc,
1706 vik_viewport_get_pixmap(zts->vw->viking_vvp),
1707 0, 0, 0, 0, -1, -1);
1709 // Calculate new box starting point & size in pixels
1710 int xx, yy, width, height;
1711 if ( event->y > zts->start_y ) {
1713 height = event->y-zts->start_y;
1717 height = zts->start_y-event->y;
1719 if ( event->x > zts->start_x ) {
1721 width = event->x-zts->start_x;
1725 width = zts->start_x-event->x;
1729 gdk_draw_rectangle (zts->pixmap, gtk_widget_get_style(GTK_WIDGET(zts->vw->viking_vvp))->black_gc, FALSE, xx, yy, width, height);
1731 // Only actually draw when there's time to do so
1732 if (draw_buf_done) {
1733 static gpointer pass_along[3];
1734 pass_along[0] = gtk_widget_get_window(GTK_WIDGET(zts->vw->viking_vvp));
1735 pass_along[1] = gtk_widget_get_style(GTK_WIDGET(zts->vw->viking_vvp))->black_gc;
1736 pass_along[2] = zts->pixmap;
1737 g_idle_add_full (G_PRIORITY_HIGH_IDLE + 10, draw_buf, pass_along, NULL);
1738 draw_buf_done = FALSE;
1741 return VIK_LAYER_TOOL_ACK;
1744 static VikLayerToolFuncStatus zoomtool_release (VikLayer *vl, GdkEventButton *event, zoom_tool_state_t *zts)
1746 guint modifiers = event->state & (GDK_SHIFT_MASK | GDK_CONTROL_MASK);
1748 zts->bounds_active = FALSE;
1750 // Ensure haven't just released on the exact same position
1751 // i.e. probably haven't moved the mouse at all
1752 if ( modifiers == GDK_SHIFT_MASK && !( ( event->x == zts->start_x ) && ( event->y == zts->start_y )) ) {
1754 VikCoord coord1, coord2;
1755 vik_viewport_screen_to_coord ( zts->vw->viking_vvp, zts->start_x, zts->start_y, &coord1);
1756 vik_viewport_screen_to_coord ( zts->vw->viking_vvp, event->x, event->y, &coord2);
1758 // From the extend of the bounds pick the best zoom level
1759 // c.f. trw_layer_zoom_to_show_latlons()
1760 // Maybe refactor...
1761 struct LatLon ll1, ll2;
1762 vik_coord_to_latlon(&coord1, &ll1);
1763 vik_coord_to_latlon(&coord2, &ll2);
1764 struct LatLon average = { (ll1.lat+ll2.lat)/2,
1765 (ll1.lon+ll2.lon)/2 };
1767 VikCoord new_center;
1768 vik_coord_load_from_latlon ( &new_center, vik_viewport_get_coord_mode ( zts->vw->viking_vvp ), &average );
1769 vik_viewport_set_center_coord ( zts->vw->viking_vvp, &new_center );
1771 /* Convert into definite 'smallest' and 'largest' positions */
1772 struct LatLon minmin;
1773 if ( ll1.lat < ll2.lat )
1774 minmin.lat = ll1.lat;
1776 minmin.lat = ll2.lat;
1778 struct LatLon maxmax;
1779 if ( ll1.lon > ll2.lon )
1780 maxmax.lon = ll1.lon;
1782 maxmax.lon = ll2.lon;
1784 /* Always recalculate the 'best' zoom level */
1785 gdouble zoom = VIK_VIEWPORT_MIN_ZOOM;
1786 vik_viewport_set_zoom ( zts->vw->viking_vvp, zoom );
1788 gdouble min_lat, max_lat, min_lon, max_lon;
1789 /* Should only be a maximum of about 18 iterations from min to max zoom levels */
1790 while ( zoom <= VIK_VIEWPORT_MAX_ZOOM ) {
1791 vik_viewport_get_min_max_lat_lon ( zts->vw->viking_vvp, &min_lat, &max_lat, &min_lon, &max_lon );
1792 /* NB I think the logic used in this test to determine if the bounds is within view
1793 fails if track goes across 180 degrees longitude.
1794 Hopefully that situation is not too common...
1795 Mind you viking doesn't really do edge locations to well anyway */
1796 if ( min_lat < minmin.lat &&
1797 max_lat > minmin.lat &&
1798 min_lon < maxmax.lon &&
1799 max_lon > maxmax.lon )
1800 /* Found within zoom level */
1805 vik_viewport_set_zoom ( zts->vw->viking_vvp, zoom );
1808 draw_update ( zts->vw );
1810 return VIK_LAYER_TOOL_ACK;
1813 static VikToolInterface zoom_tool =
1814 { { "Zoom", "vik-icon-zoom", N_("_Zoom"), "<control><shift>Z", N_("Zoom Tool"), 1 },
1815 (VikToolConstructorFunc) zoomtool_create,
1816 (VikToolDestructorFunc) zoomtool_destroy,
1817 (VikToolActivationFunc) NULL,
1818 (VikToolActivationFunc) NULL,
1819 (VikToolMouseFunc) zoomtool_click,
1820 (VikToolMouseMoveFunc) zoomtool_move,
1821 (VikToolMouseFunc) zoomtool_release,
1824 GDK_CURSOR_IS_PIXMAP,
1825 &cursor_zoom_pixbuf };
1826 /*** end zoom code ********************************************************/
1828 /********************************************************************************
1830 ********************************************************************************/
1831 static gpointer pantool_create (VikWindow *vw, VikViewport *vvp)
1836 static VikLayerToolFuncStatus pantool_click (VikLayer *vl, GdkEventButton *event, VikWindow *vw)
1838 vw->modified = TRUE;
1839 if ( event->button == 1 )
1840 vik_window_pan_click ( vw, event );
1842 return VIK_LAYER_TOOL_ACK;
1845 static VikLayerToolFuncStatus pantool_move (VikLayer *vl, GdkEventMotion *event, VikWindow *vw)
1847 vik_window_pan_move ( vw, event );
1848 return VIK_LAYER_TOOL_ACK;
1851 static VikLayerToolFuncStatus pantool_release (VikLayer *vl, GdkEventButton *event, VikWindow *vw)
1853 if ( event->button == 1 )
1854 vik_window_pan_release ( vw, event );
1855 return VIK_LAYER_TOOL_ACK;
1858 static VikToolInterface pan_tool =
1859 { { "Pan", "vik-icon-pan", N_("_Pan"), "<control><shift>P", N_("Pan Tool"), 0 },
1860 (VikToolConstructorFunc) pantool_create,
1861 (VikToolDestructorFunc) NULL,
1862 (VikToolActivationFunc) NULL,
1863 (VikToolActivationFunc) NULL,
1864 (VikToolMouseFunc) pantool_click,
1865 (VikToolMouseMoveFunc) pantool_move,
1866 (VikToolMouseFunc) pantool_release,
1870 /*** end pan code ********************************************************/
1872 /********************************************************************************
1874 ********************************************************************************/
1875 static gpointer selecttool_create (VikWindow *vw, VikViewport *vvp)
1877 tool_ed_t *t = g_new(tool_ed_t, 1);
1881 t->is_waypoint = FALSE;
1885 static void selecttool_destroy (tool_ed_t *t)
1893 GdkEventButton *event;
1894 tool_ed_t *tool_edit;
1897 static void click_layer_selected (VikLayer *vl, clicker *ck)
1899 /* Do nothing when function call returns true; */
1900 /* i.e. stop on first found item */
1903 if ( vik_layer_get_interface(vl->type)->select_click )
1904 ck->cont = !vik_layer_get_interface(vl->type)->select_click ( vl, ck->event, ck->vvp, ck->tool_edit );
1907 static VikLayerToolFuncStatus selecttool_click (VikLayer *vl, GdkEventButton *event, tool_ed_t *t)
1909 /* Only allow selection on primary button */
1910 if ( event->button == 1 ) {
1911 /* Enable click to apply callback to potentially all track/waypoint layers */
1912 /* Useful as we can find things that aren't necessarily in the currently selected layer */
1913 GList* gl = vik_layers_panel_get_all_layers_of_type ( t->vw->viking_vlp, VIK_LAYER_TRW, FALSE ); // Don't get invisible layers
1916 ck.vvp = t->vw->viking_vvp;
1919 g_list_foreach ( gl, (GFunc) click_layer_selected, &ck );
1922 // If nothing found then deselect & redraw screen if necessary to remove the highlight
1925 VikTreeview *vtv = vik_layers_panel_get_treeview ( t->vw->viking_vlp );
1927 if ( vik_treeview_get_selected_iter ( vtv, &iter ) ) {
1928 // Only clear if selected thing is a TrackWaypoint layer or a sublayer
1929 gint type = vik_treeview_item_get_type ( vtv, &iter );
1930 if ( type == VIK_TREEVIEW_TYPE_SUBLAYER ||
1931 VIK_LAYER(vik_treeview_item_get_pointer ( vtv, &iter ))->type == VIK_LAYER_TRW ) {
1933 vik_treeview_item_unselect ( vtv, &iter );
1934 if ( vik_window_clear_highlight ( t->vw ) )
1935 draw_update ( t->vw );
1940 else if ( ( event->button == 3 ) && ( vl && ( vl->type == VIK_LAYER_TRW ) ) ) {
1942 /* Act on currently selected item to show menu */
1943 if ( t->vw->selected_track || t->vw->selected_waypoint )
1944 if ( vik_layer_get_interface(vl->type)->show_viewport_menu )
1945 vik_layer_get_interface(vl->type)->show_viewport_menu ( vl, event, t->vw->viking_vvp );
1948 return VIK_LAYER_TOOL_ACK;
1951 static VikLayerToolFuncStatus selecttool_move (VikLayer *vl, GdkEventButton *event, tool_ed_t *t)
1953 /* Only allow selection on primary button */
1954 if ( event->button == 1 ) {
1955 // Don't care about vl here
1957 if ( vik_layer_get_interface(VIK_LAYER_TRW)->select_move )
1958 vik_layer_get_interface(VIK_LAYER_TRW)->select_move ( vl, event, t->vvp, t );
1960 return VIK_LAYER_TOOL_ACK;
1963 static VikLayerToolFuncStatus selecttool_release (VikLayer *vl, GdkEventButton *event, tool_ed_t *t)
1965 /* Only allow selection on primary button */
1966 if ( event->button == 1 ) {
1967 // Don't care about vl here
1969 if ( vik_layer_get_interface(VIK_LAYER_TRW)->select_release )
1970 vik_layer_get_interface(VIK_LAYER_TRW)->select_release ( (VikLayer*)t->vtl, event, t->vvp, t );
1972 return VIK_LAYER_TOOL_ACK;
1975 static VikToolInterface select_tool =
1976 { { "Select", "vik-icon-select", N_("_Select"), "<control><shift>S", N_("Select Tool"), 3 },
1977 (VikToolConstructorFunc) selecttool_create,
1978 (VikToolDestructorFunc) selecttool_destroy,
1979 (VikToolActivationFunc) NULL,
1980 (VikToolActivationFunc) NULL,
1981 (VikToolMouseFunc) selecttool_click,
1982 (VikToolMouseMoveFunc) selecttool_move,
1983 (VikToolMouseFunc) selecttool_release,
1984 (VikToolKeyFunc) NULL,
1989 /*** end select tool code ********************************************************/
1991 static void draw_pan_cb ( GtkAction *a, VikWindow *vw )
1993 // Since the treeview cell editting intercepts standard keyboard handlers, it means we can receive events here
1994 // Thus if currently editting, ensure we don't move the viewport when Ctrl+<arrow> is received
1995 VikLayer *sel = vik_layers_panel_get_selected ( vw->viking_vlp );
1996 if ( sel && vik_treeview_get_editing ( sel->vt ) )
1999 if (!strcmp(gtk_action_get_name(a), "PanNorth")) {
2000 vik_viewport_set_center_screen ( vw->viking_vvp, vik_viewport_get_width(vw->viking_vvp)/2, 0 );
2001 } else if (!strcmp(gtk_action_get_name(a), "PanEast")) {
2002 vik_viewport_set_center_screen ( vw->viking_vvp, vik_viewport_get_width(vw->viking_vvp), vik_viewport_get_height(vw->viking_vvp)/2 );
2003 } else if (!strcmp(gtk_action_get_name(a), "PanSouth")) {
2004 vik_viewport_set_center_screen ( vw->viking_vvp, vik_viewport_get_width(vw->viking_vvp)/2, vik_viewport_get_height(vw->viking_vvp) );
2005 } else if (!strcmp(gtk_action_get_name(a), "PanWest")) {
2006 vik_viewport_set_center_screen ( vw->viking_vvp, 0, vik_viewport_get_height(vw->viking_vvp)/2 );
2011 static void full_screen_cb ( GtkAction *a, VikWindow *vw )
2013 GtkWidget *check_box = gtk_ui_manager_get_widget ( vw->uim, "/ui/MainMenu/View/FullScreen" );
2014 g_assert(check_box);
2015 gboolean state = gtk_check_menu_item_get_active ( GTK_CHECK_MENU_ITEM(check_box));
2017 gtk_window_fullscreen ( GTK_WINDOW(vw) );
2019 gtk_window_unfullscreen ( GTK_WINDOW(vw) );
2022 static void draw_zoom_cb ( GtkAction *a, VikWindow *vw )
2026 if (!strcmp(gtk_action_get_name(a), "ZoomIn")) {
2029 else if (!strcmp(gtk_action_get_name(a), "ZoomOut")) {
2032 else if (!strcmp(gtk_action_get_name(a), "Zoom0.25")) {
2035 else if (!strcmp(gtk_action_get_name(a), "Zoom0.5")) {
2039 gchar *s = (gchar *)gtk_action_get_name(a);
2045 case -3: vik_viewport_zoom_in ( vw->viking_vvp ); break;
2046 case -4: vik_viewport_zoom_out ( vw->viking_vvp ); break;
2047 case -1: vik_viewport_set_zoom ( vw->viking_vvp, 0.5 ); break;
2048 case -2: vik_viewport_set_zoom ( vw->viking_vvp, 0.25 ); break;
2049 default: vik_viewport_set_zoom ( vw->viking_vvp, what );
2054 static void draw_goto_cb ( GtkAction *a, VikWindow *vw )
2056 VikCoord new_center;
2058 if (!strcmp(gtk_action_get_name(a), "GotoLL")) {
2059 struct LatLon ll, llold;
2060 vik_coord_to_latlon ( vik_viewport_get_center ( vw->viking_vvp ), &llold );
2061 if ( a_dialog_goto_latlon ( GTK_WINDOW(vw), &ll, &llold ) )
2062 vik_coord_load_from_latlon ( &new_center, vik_viewport_get_coord_mode(vw->viking_vvp), &ll );
2066 else if (!strcmp(gtk_action_get_name(a), "GotoUTM")) {
2067 struct UTM utm, utmold;
2068 vik_coord_to_utm ( vik_viewport_get_center ( vw->viking_vvp ), &utmold );
2069 if ( a_dialog_goto_utm ( GTK_WINDOW(vw), &utm, &utmold ) )
2070 vik_coord_load_from_utm ( &new_center, vik_viewport_get_coord_mode(vw->viking_vvp), &utm );
2075 g_critical("Houston, we've had a problem.");
2079 vik_viewport_set_center_coord ( vw->viking_vvp, &new_center );
2084 * Refresh maps displayed
2086 static void draw_refresh_cb ( GtkAction *a, VikWindow *vw )
2088 // Only get 'new' maps
2089 simple_map_update ( vw, TRUE );
2092 static void menu_addlayer_cb ( GtkAction *a, VikWindow *vw )
2094 VikLayerTypeEnum type;
2095 for ( type = 0; type < VIK_LAYER_NUM_TYPES; type++ ) {
2096 if (!strcmp(vik_layer_get_interface(type)->name, gtk_action_get_name(a))) {
2097 if ( vik_layers_panel_new_layer ( vw->viking_vlp, type ) ) {
2099 vw->modified = TRUE;
2105 static void menu_copy_layer_cb ( GtkAction *a, VikWindow *vw )
2107 a_clipboard_copy_selected ( vw->viking_vlp );
2110 static void menu_cut_layer_cb ( GtkAction *a, VikWindow *vw )
2112 vik_layers_panel_cut_selected ( vw->viking_vlp );
2113 vw->modified = TRUE;
2116 static void menu_paste_layer_cb ( GtkAction *a, VikWindow *vw )
2118 if ( vik_layers_panel_paste_selected ( vw->viking_vlp ) )
2120 vw->modified = TRUE;
2124 static void menu_properties_cb ( GtkAction *a, VikWindow *vw )
2126 if ( ! vik_layers_panel_properties ( vw->viking_vlp ) )
2127 a_dialog_info_msg ( GTK_WINDOW(vw), _("You must select a layer to show its properties.") );
2130 static void help_help_cb ( GtkAction *a, VikWindow *vw )
2133 ShellExecute(NULL, "open", ""PACKAGE".pdf", NULL, NULL, SW_SHOWNORMAL);
2136 uri = g_strdup_printf("ghelp:%s", PACKAGE);
2137 GError *error = NULL;
2138 gboolean show = gtk_show_uri (NULL, uri, GDK_CURRENT_TIME, &error);
2139 if ( !show && !error )
2140 // No error to show, so unlikely this will get called
2141 a_dialog_error_msg ( GTK_WINDOW(vw), _("The help system is not available.") );
2144 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 );
2145 g_error_free ( error );
2148 #endif /* WINDOWS */
2151 static void help_about_cb ( GtkAction *a, VikWindow *vw )
2153 a_dialog_about(GTK_WINDOW(vw));
2156 static void menu_delete_layer_cb ( GtkAction *a, VikWindow *vw )
2158 if ( vik_layers_panel_get_selected ( vw->viking_vlp ) )
2160 vik_layers_panel_delete_selected ( vw->viking_vlp );
2161 vw->modified = TRUE;
2164 a_dialog_info_msg ( GTK_WINDOW(vw), _("You must select a layer to delete.") );
2167 static void view_side_panel_cb ( GtkAction *a, VikWindow *vw )
2169 GtkWidget *check_box = gtk_ui_manager_get_widget ( vw->uim, "/ui/MainMenu/View/SetShow/ViewSidePanel" );
2170 g_assert(check_box);
2171 gboolean state = gtk_check_menu_item_get_active ( GTK_CHECK_MENU_ITEM(check_box));
2173 gtk_widget_show(GTK_WIDGET(vw->viking_vlp));
2175 gtk_widget_hide(GTK_WIDGET(vw->viking_vlp));
2178 static void view_statusbar_cb ( GtkAction *a, VikWindow *vw )
2180 GtkWidget *check_box = gtk_ui_manager_get_widget ( vw->uim, "/ui/MainMenu/View/SetShow/ViewStatusBar" );
2183 gboolean state = gtk_check_menu_item_get_active ( GTK_CHECK_MENU_ITEM(check_box) );
2185 gtk_widget_show ( GTK_WIDGET(vw->viking_vs) );
2187 gtk_widget_hide ( GTK_WIDGET(vw->viking_vs) );
2190 static void view_toolbar_cb ( GtkAction *a, VikWindow *vw )
2192 GtkWidget *check_box = gtk_ui_manager_get_widget ( vw->uim, "/ui/MainMenu/View/SetShow/ViewToolbar" );
2195 gboolean state = gtk_check_menu_item_get_active ( GTK_CHECK_MENU_ITEM(check_box) );
2197 gtk_widget_show ( GTK_WIDGET(vw->toolbar) );
2199 gtk_widget_hide ( GTK_WIDGET(vw->toolbar) );
2202 static void view_main_menu_cb ( GtkAction *a, VikWindow *vw )
2204 GtkWidget *check_box = gtk_ui_manager_get_widget ( vw->uim, "/ui/MainMenu/View/SetShow/ViewMainMenu" );
2207 gboolean state = gtk_check_menu_item_get_active ( GTK_CHECK_MENU_ITEM(check_box) );
2209 gtk_widget_hide ( gtk_ui_manager_get_widget ( vw->uim, "/ui/MainMenu" ) );
2211 gtk_widget_show ( gtk_ui_manager_get_widget ( vw->uim, "/ui/MainMenu" ) );
2214 /***************************************
2215 ** tool management routines
2217 ***************************************/
2219 static toolbox_tools_t* toolbox_create(VikWindow *vw)
2221 toolbox_tools_t *vt = g_new(toolbox_tools_t, 1);
2224 vt->active_tool = -1;
2229 static void toolbox_add_tool(toolbox_tools_t *vt, VikToolInterface *vti, gint layer_type )
2231 vt->tools = g_renew(toolbox_tool_t, vt->tools, vt->n_tools+1);
2232 vt->tools[vt->n_tools].ti = *vti;
2233 vt->tools[vt->n_tools].layer_type = layer_type;
2235 vt->tools[vt->n_tools].state = vti->create(vt->vw, vt->vw->viking_vvp);
2238 vt->tools[vt->n_tools].state = NULL;
2243 static int toolbox_get_tool(toolbox_tools_t *vt, const gchar *tool_name)
2246 for (i=0; i<vt->n_tools; i++) {
2247 if (!strcmp(tool_name, vt->tools[i].ti.radioActionEntry.name)) {
2254 static void toolbox_activate(toolbox_tools_t *vt, const gchar *tool_name)
2256 int tool = toolbox_get_tool(vt, tool_name);
2257 toolbox_tool_t *t = &vt->tools[tool];
2258 VikLayer *vl = vik_layers_panel_get_selected ( vt->vw->viking_vlp );
2260 if (tool == vt->n_tools) {
2261 g_critical("trying to activate a non-existent tool...");
2264 /* is the tool already active? */
2265 if (vt->active_tool == tool) {
2269 if (vt->active_tool != -1) {
2270 if (vt->tools[vt->active_tool].ti.deactivate) {
2271 vt->tools[vt->active_tool].ti.deactivate(NULL, vt->tools[vt->active_tool].state);
2274 if (t->ti.activate) {
2275 t->ti.activate(vl, t->state);
2277 vt->active_tool = tool;
2280 static const GdkCursor *toolbox_get_cursor(toolbox_tools_t *vt, const gchar *tool_name)
2282 int tool = toolbox_get_tool(vt, tool_name);
2283 toolbox_tool_t *t = &vt->tools[tool];
2284 if (t->ti.cursor == NULL) {
2285 if (t->ti.cursor_type == GDK_CURSOR_IS_PIXMAP && t->ti.cursor_data != NULL) {
2286 GError *cursor_load_err = NULL;
2287 GdkPixbuf *cursor_pixbuf = gdk_pixbuf_from_pixdata (t->ti.cursor_data, FALSE, &cursor_load_err);
2288 /* TODO: settable offeset */
2289 t->ti.cursor = gdk_cursor_new_from_pixbuf ( gdk_display_get_default(), cursor_pixbuf, 3, 3 );
2290 g_object_unref ( G_OBJECT(cursor_pixbuf) );
2292 t->ti.cursor = gdk_cursor_new ( t->ti.cursor_type );
2295 return t->ti.cursor;
2298 static void toolbox_click (toolbox_tools_t *vt, GdkEventButton *event)
2300 VikLayer *vl = vik_layers_panel_get_selected ( vt->vw->viking_vlp );
2301 if (vt->active_tool != -1 && vt->tools[vt->active_tool].ti.click) {
2302 gint ltype = vt->tools[vt->active_tool].layer_type;
2303 if ( ltype == TOOL_LAYER_TYPE_NONE || (vl && ltype == vl->type) )
2304 vt->tools[vt->active_tool].ti.click(vl, event, vt->tools[vt->active_tool].state);
2308 static void toolbox_move (toolbox_tools_t *vt, GdkEventMotion *event)
2310 VikLayer *vl = vik_layers_panel_get_selected ( vt->vw->viking_vlp );
2311 if (vt->active_tool != -1 && vt->tools[vt->active_tool].ti.move) {
2312 gint ltype = vt->tools[vt->active_tool].layer_type;
2313 if ( ltype == TOOL_LAYER_TYPE_NONE || (vl && ltype == vl->type) )
2314 if ( VIK_LAYER_TOOL_ACK_GRAB_FOCUS == vt->tools[vt->active_tool].ti.move(vl, event, vt->tools[vt->active_tool].state) )
2315 gtk_widget_grab_focus ( GTK_WIDGET(vt->vw->viking_vvp) );
2319 static void toolbox_release (toolbox_tools_t *vt, GdkEventButton *event)
2321 VikLayer *vl = vik_layers_panel_get_selected ( vt->vw->viking_vlp );
2322 if (vt->active_tool != -1 && vt->tools[vt->active_tool].ti.release ) {
2323 gint ltype = vt->tools[vt->active_tool].layer_type;
2324 if ( ltype == TOOL_LAYER_TYPE_NONE || (vl && ltype == vl->type) )
2325 vt->tools[vt->active_tool].ti.release(vl, event, vt->tools[vt->active_tool].state);
2328 /** End tool management ************************************/
2330 void vik_window_enable_layer_tool ( VikWindow *vw, gint layer_id, gint tool_id )
2332 gtk_action_activate ( gtk_action_group_get_action ( vw->action_group, vik_layer_get_interface(layer_id)->tools[tool_id].radioActionEntry.name ) );
2335 /* this function gets called whenever a toolbar tool is clicked */
2336 static void menu_tool_cb ( GtkAction *old, GtkAction *a, VikWindow *vw )
2338 /* White Magic, my friends ... White Magic... */
2340 toolbox_activate(vw->vt, gtk_action_get_name(a));
2342 vw->viewport_cursor = (GdkCursor *)toolbox_get_cursor(vw->vt, gtk_action_get_name(a));
2344 if ( gtk_widget_get_window(GTK_WIDGET(vw->viking_vvp)) )
2345 /* We set cursor, even if it is NULL: it resets to default */
2346 gdk_window_set_cursor ( gtk_widget_get_window(GTK_WIDGET(vw->viking_vvp)), vw->viewport_cursor );
2348 if (!strcmp(gtk_action_get_name(a), "Pan")) {
2349 vw->current_tool = TOOL_PAN;
2351 else if (!strcmp(gtk_action_get_name(a), "Zoom")) {
2352 vw->current_tool = TOOL_ZOOM;
2354 else if (!strcmp(gtk_action_get_name(a), "Ruler")) {
2355 vw->current_tool = TOOL_RULER;
2357 else if (!strcmp(gtk_action_get_name(a), "Select")) {
2358 vw->current_tool = TOOL_SELECT;
2361 /* TODO: only enable tools from active layer */
2362 VikLayerTypeEnum layer_id;
2363 for (layer_id=0; layer_id<VIK_LAYER_NUM_TYPES; layer_id++) {
2364 for ( tool_id = 0; tool_id < vik_layer_get_interface(layer_id)->tools_count; tool_id++ ) {
2365 if (!strcmp(vik_layer_get_interface(layer_id)->tools[tool_id].radioActionEntry.name, gtk_action_get_name(a))) {
2366 vw->current_tool = TOOL_LAYER;
2367 vw->tool_layer_id = layer_id;
2368 vw->tool_tool_id = tool_id;
2373 draw_status_tool ( vw );
2376 static void window_set_filename ( VikWindow *vw, const gchar *filename )
2381 g_free ( vw->filename );
2382 if ( filename == NULL )
2384 vw->filename = NULL;
2388 vw->filename = g_strdup(filename);
2391 /* Refresh window's title */
2392 file = window_get_filename ( vw );
2393 title = g_strdup_printf( "%s - Viking", file );
2394 gtk_window_set_title ( GTK_WINDOW(vw), title );
2398 static const gchar *window_get_filename ( VikWindow *vw )
2400 return vw->filename ? a_file_basename ( vw->filename ) : _("Untitled");
2403 GtkWidget *vik_window_get_drawmode_button ( VikWindow *vw, VikViewportDrawMode mode )
2405 GtkWidget *mode_button;
2408 #ifdef VIK_CONFIG_EXPEDIA
2409 case VIK_VIEWPORT_DRAWMODE_EXPEDIA: buttonname = "/ui/MainMenu/View/ModeExpedia"; break;
2411 case VIK_VIEWPORT_DRAWMODE_MERCATOR: buttonname = "/ui/MainMenu/View/ModeMercator"; break;
2412 case VIK_VIEWPORT_DRAWMODE_LATLON: buttonname = "/ui/MainMenu/View/ModeLatLon"; break;
2413 default: buttonname = "/ui/MainMenu/View/ModeUTM";
2415 mode_button = gtk_ui_manager_get_widget ( vw->uim, buttonname );
2416 g_assert ( mode_button );
2421 * vik_window_get_pan_move:
2422 * @vw: some VikWindow
2424 * Retrieves @vw's pan_move.
2426 * Should be removed as soon as possible.
2428 * Returns: @vw's pan_move
2432 gboolean vik_window_get_pan_move ( VikWindow *vw )
2434 return vw->pan_move;
2437 static void on_activate_recent_item (GtkRecentChooser *chooser,
2442 filename = gtk_recent_chooser_get_current_uri (chooser);
2443 if (filename != NULL)
2445 GFile *file = g_file_new_for_uri ( filename );
2446 gchar *path = g_file_get_path ( file );
2447 g_object_unref ( file );
2448 if ( self->filename )
2450 GSList *filenames = NULL;
2451 filenames = g_slist_append ( filenames, path );
2452 g_signal_emit ( G_OBJECT(self), window_signals[VW_OPENWINDOW_SIGNAL], 0, filenames );
2453 // NB: GSList & contents are freed by main.open_window
2456 vik_window_open_file ( self, path, TRUE );
2464 static void setup_recent_files (VikWindow *self)
2466 GtkRecentManager *manager;
2467 GtkRecentFilter *filter;
2468 GtkWidget *menu, *menu_item;
2470 filter = gtk_recent_filter_new ();
2471 /* gtk_recent_filter_add_application (filter, g_get_application_name()); */
2472 gtk_recent_filter_add_group(filter, "viking");
2474 manager = gtk_recent_manager_get_default ();
2475 menu = gtk_recent_chooser_menu_new_for_manager (manager);
2476 gtk_recent_chooser_set_sort_type (GTK_RECENT_CHOOSER (menu), GTK_RECENT_SORT_MRU);
2477 gtk_recent_chooser_add_filter (GTK_RECENT_CHOOSER (menu), filter);
2479 menu_item = gtk_ui_manager_get_widget (self->uim, "/ui/MainMenu/File/OpenRecentFile");
2480 gtk_menu_item_set_submenu (GTK_MENU_ITEM (menu_item), menu);
2482 g_signal_connect (G_OBJECT (menu), "item-activated",
2483 G_CALLBACK (on_activate_recent_item), (gpointer) self);
2486 static void update_recently_used_document(const gchar *filename)
2488 /* Update Recently Used Document framework */
2489 GtkRecentManager *manager = gtk_recent_manager_get_default();
2490 GtkRecentData *recent_data = g_slice_new (GtkRecentData);
2491 gchar *groups[] = {"viking", NULL};
2492 GFile *file = g_file_new_for_commandline_arg(filename);
2493 gchar *uri = g_file_get_uri(file);
2494 gchar *basename = g_path_get_basename(filename);
2495 g_object_unref(file);
2498 recent_data->display_name = basename;
2499 recent_data->description = NULL;
2500 recent_data->mime_type = "text/x-gps-data";
2501 recent_data->app_name = (gchar *) g_get_application_name ();
2502 recent_data->app_exec = g_strjoin (" ", g_get_prgname (), "%f", NULL);
2503 recent_data->groups = groups;
2504 recent_data->is_private = FALSE;
2505 if (!gtk_recent_manager_add_full (manager, uri, recent_data))
2507 g_warning (_("Unable to add '%s' to the list of recently used documents"), uri);
2512 g_free (recent_data->app_exec);
2513 g_slice_free (GtkRecentData, recent_data);
2517 * Call this before doing things that may take a long time and otherwise not show any other feedback
2518 * such as loading and saving files
2520 void vik_window_set_busy_cursor ( VikWindow *vw )
2522 gdk_window_set_cursor ( gtk_widget_get_window(GTK_WIDGET(vw)), vw->busy_cursor );
2523 // Viewport has a separate cursor
2524 gdk_window_set_cursor ( gtk_widget_get_window(GTK_WIDGET(vw->viking_vvp)), vw->busy_cursor );
2525 // Ensure cursor updated before doing stuff
2526 while( gtk_events_pending() )
2527 gtk_main_iteration();
2530 void vik_window_clear_busy_cursor ( VikWindow *vw )
2532 gdk_window_set_cursor ( gtk_widget_get_window(GTK_WIDGET(vw)), NULL );
2533 // Restore viewport cursor
2534 gdk_window_set_cursor ( gtk_widget_get_window(GTK_WIDGET(vw->viking_vvp)), vw->viewport_cursor );
2537 void vik_window_open_file ( VikWindow *vw, const gchar *filename, gboolean change_filename )
2539 vik_window_set_busy_cursor ( vw );
2540 vw->loaded_type = a_file_load ( vik_layers_panel_get_top_layer(vw->viking_vlp), vw->viking_vvp, filename );
2541 switch ( vw->loaded_type )
2543 case LOAD_TYPE_READ_FAILURE:
2544 a_dialog_error_msg ( GTK_WINDOW(vw), _("The file you requested could not be opened.") );
2546 case LOAD_TYPE_GPSBABEL_FAILURE:
2547 a_dialog_error_msg ( GTK_WINDOW(vw), _("GPSBabel is required to load files of this type or GPSBabel encountered problems.") );
2549 case LOAD_TYPE_GPX_FAILURE:
2550 a_dialog_error_msg_extra ( GTK_WINDOW(vw), _("Unable to load malformed GPX file %s"), filename );
2552 case LOAD_TYPE_UNSUPPORTED_FAILURE:
2553 a_dialog_error_msg_extra ( GTK_WINDOW(vw), _("Unsupported file type for %s"), filename );
2555 case LOAD_TYPE_VIK_FAILURE_NON_FATAL:
2557 // Since we can process .vik files with issues just show a warning in the status bar
2558 // Not that a user can do much about it... or tells them what this issue is yet...
2559 gchar *msg = g_strdup_printf (_("WARNING: issues encountered loading %s"), a_file_basename (filename) );
2560 vik_statusbar_set_message ( vw->viking_vs, VIK_STATUSBAR_INFO, msg );
2563 // No break, carry on to show any data
2564 case LOAD_TYPE_VIK_SUCCESS:
2566 GtkWidget *mode_button;
2568 if ( change_filename )
2569 window_set_filename ( vw, filename );
2570 mode_button = vik_window_get_drawmode_button ( vw, vik_viewport_get_drawmode ( vw->viking_vvp ) );
2571 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. */
2572 gtk_check_menu_item_set_active ( GTK_CHECK_MENU_ITEM(mode_button), TRUE );
2573 vw->only_updating_coord_mode_ui = FALSE;
2575 vik_layers_panel_change_coord_mode ( vw->viking_vlp, vik_viewport_get_coord_mode ( vw->viking_vvp ) );
2577 mode_button = gtk_ui_manager_get_widget ( vw->uim, "/ui/MainMenu/View/SetShow/ShowScale" );
2578 g_assert ( mode_button );
2579 gtk_check_menu_item_set_active ( GTK_CHECK_MENU_ITEM(mode_button),vik_viewport_get_draw_scale(vw->viking_vvp) );
2581 mode_button = gtk_ui_manager_get_widget ( vw->uim, "/ui/MainMenu/View/SetShow/ShowCenterMark" );
2582 g_assert ( mode_button );
2583 gtk_check_menu_item_set_active ( GTK_CHECK_MENU_ITEM(mode_button),vik_viewport_get_draw_centermark(vw->viking_vvp) );
2585 mode_button = gtk_ui_manager_get_widget ( vw->uim, "/ui/MainMenu/View/SetShow/ShowHighlight" );
2586 g_assert ( mode_button );
2587 gtk_check_menu_item_set_active ( GTK_CHECK_MENU_ITEM(mode_button),vik_viewport_get_draw_highlight (vw->viking_vvp) );
2589 //case LOAD_TYPE_OTHER_SUCCESS:
2591 update_recently_used_document(filename);
2596 vik_window_clear_busy_cursor ( vw );
2599 static void load_file ( GtkAction *a, VikWindow *vw )
2601 GSList *files = NULL;
2602 GSList *cur_file = NULL;
2604 if (!strcmp(gtk_action_get_name(a), "Open")) {
2607 else if (!strcmp(gtk_action_get_name(a), "Append")) {
2611 g_critical("Houston, we've had a problem.");
2615 if ( ! vw->open_dia )
2617 vw->open_dia = gtk_file_chooser_dialog_new (_("Please select a GPS data file to open. "),
2619 GTK_FILE_CHOOSER_ACTION_OPEN,
2620 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
2621 GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT,
2623 gchar *cwd = g_get_current_dir();
2625 gtk_file_chooser_set_current_folder ( GTK_FILE_CHOOSER(vw->open_dia), cwd );
2629 GtkFileFilter *filter;
2630 // NB file filters are listed this way for alphabetical ordering
2631 #ifdef VIK_CONFIG_GEOCACHES
2632 filter = gtk_file_filter_new ();
2633 gtk_file_filter_set_name( filter, _("Geocaching") );
2634 gtk_file_filter_add_pattern ( filter, "*.loc" ); // No MIME type available
2635 gtk_file_chooser_add_filter (GTK_FILE_CHOOSER(vw->open_dia), filter);
2638 filter = gtk_file_filter_new ();
2639 gtk_file_filter_set_name( filter, _("Google Earth") );
2640 gtk_file_filter_add_mime_type ( filter, "application/vnd.google-earth.kml+xml");
2641 gtk_file_chooser_add_filter (GTK_FILE_CHOOSER(vw->open_dia), filter);
2643 filter = gtk_file_filter_new ();
2644 gtk_file_filter_set_name( filter, _("GPX") );
2645 gtk_file_filter_add_pattern ( filter, "*.gpx" ); // No MIME type available
2646 gtk_file_chooser_add_filter (GTK_FILE_CHOOSER(vw->open_dia), filter);
2648 filter = gtk_file_filter_new ();
2649 gtk_file_filter_set_name( filter, _("Viking") );
2650 gtk_file_filter_add_pattern ( filter, "*.vik" );
2651 gtk_file_filter_add_pattern ( filter, "*.viking" );
2652 gtk_file_chooser_add_filter (GTK_FILE_CHOOSER(vw->open_dia), filter);
2654 // NB could have filters for gpspoint (*.gps,*.gpsoint?) + gpsmapper (*.gsm,*.gpsmapper?)
2655 // However assume this are barely used and thus not worthy of inclusion
2656 // as they'll just make the options too many and have no clear file pattern
2657 // one can always use the all option
2658 filter = gtk_file_filter_new ();
2659 gtk_file_filter_set_name( filter, _("All") );
2660 gtk_file_filter_add_pattern ( filter, "*" );
2661 gtk_file_chooser_add_filter (GTK_FILE_CHOOSER(vw->open_dia), filter);
2662 // Default to any file - same as before open filters were added
2663 gtk_file_chooser_set_filter (GTK_FILE_CHOOSER(vw->open_dia), filter);
2665 gtk_file_chooser_set_select_multiple ( GTK_FILE_CHOOSER(vw->open_dia), TRUE );
2666 gtk_window_set_transient_for ( GTK_WINDOW(vw->open_dia), GTK_WINDOW(vw) );
2667 gtk_window_set_destroy_with_parent ( GTK_WINDOW(vw->open_dia), TRUE );
2669 if ( gtk_dialog_run ( GTK_DIALOG(vw->open_dia) ) == GTK_RESPONSE_ACCEPT )
2671 gtk_widget_hide ( vw->open_dia );
2672 #ifdef VIKING_PROMPT_IF_MODIFIED
2673 if ( (vw->modified || vw->filename) && newwindow )
2675 if ( vw->filename && newwindow )
2677 g_signal_emit ( G_OBJECT(vw), window_signals[VW_OPENWINDOW_SIGNAL], 0, gtk_file_chooser_get_filenames (GTK_FILE_CHOOSER(vw->open_dia) ) );
2679 files = gtk_file_chooser_get_filenames (GTK_FILE_CHOOSER(vw->open_dia) );
2680 gboolean change_fn = newwindow && (g_slist_length(files)==1); /* only change fn if one file */
2681 gboolean first_vik_file = TRUE;
2683 while ( cur_file ) {
2685 gchar *file_name = cur_file->data;
2686 if ( newwindow && check_file_magic_vik ( file_name ) ) {
2687 // Load first of many .vik files in current window
2688 if ( first_vik_file ) {
2689 vik_window_open_file ( vw, file_name, TRUE );
2690 first_vik_file = FALSE;
2693 // Load each subsequent .vik file in a separate window
2694 VikWindow *newvw = vik_window_new_window ();
2696 vik_window_open_file ( newvw, file_name, TRUE );
2701 vik_window_open_file ( vw, file_name, change_fn );
2704 cur_file = g_slist_next (cur_file);
2706 g_slist_free (files);
2710 gtk_widget_hide ( vw->open_dia );
2713 static gboolean save_file_as ( GtkAction *a, VikWindow *vw )
2715 gboolean rv = FALSE;
2717 if ( ! vw->save_dia )
2719 vw->save_dia = gtk_file_chooser_dialog_new (_("Save as Viking File."),
2721 GTK_FILE_CHOOSER_ACTION_SAVE,
2722 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
2723 GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT,
2725 gchar *cwd = g_get_current_dir();
2727 gtk_file_chooser_set_current_folder ( GTK_FILE_CHOOSER(vw->save_dia), cwd );
2731 GtkFileFilter *filter;
2732 filter = gtk_file_filter_new ();
2733 gtk_file_filter_set_name( filter, _("All") );
2734 gtk_file_filter_add_pattern ( filter, "*" );
2735 gtk_file_chooser_add_filter (GTK_FILE_CHOOSER(vw->save_dia), filter);
2737 filter = gtk_file_filter_new ();
2738 gtk_file_filter_set_name( filter, _("Viking") );
2739 gtk_file_filter_add_pattern ( filter, "*.vik" );
2740 gtk_file_filter_add_pattern ( filter, "*.viking" );
2741 gtk_file_chooser_add_filter (GTK_FILE_CHOOSER(vw->save_dia), filter);
2742 // Default to a Viking file
2743 gtk_file_chooser_set_filter (GTK_FILE_CHOOSER(vw->save_dia), filter);
2745 gtk_window_set_transient_for ( GTK_WINDOW(vw->save_dia), GTK_WINDOW(vw) );
2746 gtk_window_set_destroy_with_parent ( GTK_WINDOW(vw->save_dia), TRUE );
2748 // Auto append / replace extension with '.vik' to the suggested file name as it's going to be a Viking File
2749 gchar* auto_save_name = g_strdup ( window_get_filename ( vw ) );
2750 if ( ! check_file_ext ( auto_save_name, ".vik" ) )
2751 auto_save_name = g_strconcat ( auto_save_name, ".vik", NULL );
2753 gtk_file_chooser_set_current_name (GTK_FILE_CHOOSER(vw->save_dia), auto_save_name);
2755 while ( gtk_dialog_run ( GTK_DIALOG(vw->save_dia) ) == GTK_RESPONSE_ACCEPT )
2757 fn = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER(vw->save_dia) );
2758 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 ) ) )
2760 window_set_filename ( vw, fn );
2761 rv = window_save ( vw );
2762 vw->modified = FALSE;
2766 g_free ( auto_save_name );
2767 gtk_widget_hide ( vw->save_dia );
2771 static gboolean window_save ( VikWindow *vw )
2773 vik_window_set_busy_cursor ( vw );
2774 gboolean success = TRUE;
2776 if ( a_file_save ( vik_layers_panel_get_top_layer ( vw->viking_vlp ), vw->viking_vvp, vw->filename ) )
2778 update_recently_used_document ( vw->filename );
2782 a_dialog_error_msg ( GTK_WINDOW(vw), _("The filename you requested could not be opened for writing.") );
2785 vik_window_clear_busy_cursor ( vw );
2789 static gboolean save_file ( GtkAction *a, VikWindow *vw )
2791 if ( ! vw->filename )
2792 return save_file_as ( NULL, vw );
2795 vw->modified = FALSE;
2796 return window_save ( vw );
2803 * Export all TRW Layers in the list to individual files in the specified directory
2805 * Returns: %TRUE on success
2807 static gboolean export_to ( VikWindow *vw, GList *gl, VikFileType_t vft, const gchar *dir, const gchar *extension )
2809 gboolean success = TRUE;
2811 gint export_count = 0;
2813 vik_window_set_busy_cursor ( vw );
2817 gchar *fn = g_strconcat ( dir, G_DIR_SEPARATOR_S, VIK_LAYER(gl->data)->name, extension, NULL );
2819 // Some protection in attempting to write too many same named files
2820 // As this will get horribly slow...
2821 gboolean safe = FALSE;
2823 while ( ii < 5000 ) {
2824 if ( g_file_test ( fn, G_FILE_TEST_EXISTS ) ) {
2827 fn = g_strdup_printf ( "%s%s%s#%03d%s", dir, G_DIR_SEPARATOR_S, VIK_LAYER(gl->data)->name, ii, extension );
2838 // NB: We allow exporting empty layers
2840 gboolean this_success = a_file_export ( VIK_TRW_LAYER(gl->data), fn, vft, NULL, TRUE );
2842 // Show some progress
2843 if ( this_success ) {
2845 gchar *message = g_strconcat ( _("Exporting to file: "), fn, NULL );
2846 vik_statusbar_set_message ( vw->viking_vs, VIK_STATUSBAR_INFO, message );
2847 while ( gtk_events_pending() )
2848 gtk_main_iteration ();
2852 success = success && this_success;
2856 gl = g_list_next ( gl );
2859 vik_window_clear_busy_cursor ( vw );
2861 // Confirm what happened.
2862 gchar *message = g_strdup_printf ( _("Exported files: %d"), export_count );
2863 vik_statusbar_set_message ( vw->viking_vs, VIK_STATUSBAR_INFO, message );
2869 static void export_to_common ( VikWindow *vw, VikFileType_t vft, const gchar *extension )
2871 GList *gl = vik_layers_panel_get_all_layers_of_type ( vw->viking_vlp, VIK_LAYER_TRW, TRUE );
2874 a_dialog_info_msg ( GTK_WINDOW(vw), _("Nothing to Export!") );
2878 GtkWidget *dialog = gtk_dialog_new_with_buttons ( _("Export to directory"),
2880 GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
2882 GTK_RESPONSE_REJECT,
2884 GTK_RESPONSE_ACCEPT,
2887 GtkWidget *gw = gtk_file_chooser_widget_new ( GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER );
2888 gtk_box_pack_start ( GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), gw, TRUE, TRUE, 0 );
2890 // try to make it a nice size - otherwise seems to default to something impractically small
2891 gtk_window_set_default_size ( GTK_WINDOW(dialog), 600, 300 );
2893 gtk_widget_show_all ( dialog );
2895 if ( gtk_dialog_run ( GTK_DIALOG(dialog) ) == GTK_RESPONSE_ACCEPT ) {
2896 gchar *dir = gtk_file_chooser_get_filename ( GTK_FILE_CHOOSER(gw) );
2897 gtk_widget_destroy ( dialog );
2899 if ( !export_to ( vw, gl, vft, dir, extension ) )
2900 a_dialog_error_msg ( GTK_WINDOW(vw),_("Could not convert all files") );
2905 gtk_widget_destroy ( dialog );
2910 static void export_to_gpx ( GtkAction *a, VikWindow *vw )
2912 export_to_common ( vw, FILE_TYPE_GPX, ".gpx" );
2915 static void export_to_kml ( GtkAction *a, VikWindow *vw )
2917 export_to_common ( vw, FILE_TYPE_KML, ".kml" );
2920 static void file_properties_cb ( GtkAction *a, VikWindow *vw )
2922 gchar *message = NULL;
2923 if ( vw->filename ) {
2924 if ( g_file_test ( vw->filename, G_FILE_TEST_EXISTS ) ) {
2925 // Get some timestamp information of the file
2927 if ( g_stat ( vw->filename, &stat_buf ) == 0 ) {
2929 strftime ( time_buf, sizeof(time_buf), "%c", gmtime((const time_t *)&stat_buf.st_mtime) );
2931 gint byte_size = stat_buf.st_size;
2932 // See http://en.wikipedia.org/wiki/Megabyte (and Kilobyte)
2933 // hence using 1000 rather than 1024
2934 // so get output as per 'ls' or the Gtk file open dialog
2935 if ( byte_size < 1000 )
2936 size = g_strdup_printf ( _("%d bytes"), byte_size );
2937 else if ( byte_size < 1000*1000 )
2938 size = g_strdup_printf ( _("%3.1f kB"), (gdouble)byte_size / 1000 );
2940 size = g_strdup_printf ( _("%3.1f MB"), (gdouble)byte_size / (1000*1000) );
2941 message = g_strdup_printf ( _("%s\n\n%s\n\n%s"), vw->filename, time_buf, size );
2946 message = g_strdup ( _("File not accessible") );
2949 message = g_strdup ( _("No Viking File") );
2952 a_dialog_info_msg ( GTK_WINDOW(vw), message );
2956 static void acquire_from_gps ( GtkAction *a, VikWindow *vw )
2958 // Via the file menu, acquiring from a GPS makes a new layer
2959 // this has always been the way (not entirely sure if this was the real intention!)
2960 // thus maintain the behaviour ATM.
2961 // Hence explicit setting here (as the value may be changed elsewhere)
2962 vik_datasource_gps_interface.mode = VIK_DATASOURCE_CREATENEWLAYER;
2963 a_acquire(vw, vw->viking_vlp, vw->viking_vvp, &vik_datasource_gps_interface, NULL, NULL );
2966 static void acquire_from_file ( GtkAction *a, VikWindow *vw )
2968 a_acquire(vw, vw->viking_vlp, vw->viking_vvp, &vik_datasource_file_interface, NULL, NULL );
2971 static void acquire_from_routing ( GtkAction *a, VikWindow *vw )
2973 a_acquire(vw, vw->viking_vlp, vw->viking_vvp, &vik_datasource_routing_interface, NULL, NULL );
2976 #ifdef VIK_CONFIG_OPENSTREETMAP
2977 static void acquire_from_osm ( GtkAction *a, VikWindow *vw )
2979 a_acquire(vw, vw->viking_vlp, vw->viking_vvp, &vik_datasource_osm_interface, NULL, NULL );
2982 static void acquire_from_my_osm ( GtkAction *a, VikWindow *vw )
2984 a_acquire(vw, vw->viking_vlp, vw->viking_vvp, &vik_datasource_osm_my_traces_interface, NULL, NULL );
2988 #ifdef VIK_CONFIG_GEOCACHES
2989 static void acquire_from_gc ( GtkAction *a, VikWindow *vw )
2991 a_acquire(vw, vw->viking_vlp, vw->viking_vvp, &vik_datasource_gc_interface, NULL, NULL );
2995 #ifdef VIK_CONFIG_GEOTAG
2996 static void acquire_from_geotag ( GtkAction *a, VikWindow *vw )
2998 vik_datasource_geotag_interface.mode = VIK_DATASOURCE_CREATENEWLAYER;
2999 a_acquire(vw, vw->viking_vlp, vw->viking_vvp, &vik_datasource_geotag_interface, NULL, NULL );
3003 #ifdef VIK_CONFIG_GEONAMES
3004 static void acquire_from_wikipedia ( GtkAction *a, VikWindow *vw )
3006 a_acquire(vw, vw->viking_vlp, vw->viking_vvp, &vik_datasource_wikipedia_interface, NULL, NULL );
3010 static void acquire_from_url ( GtkAction *a, VikWindow *vw )
3012 vik_datasource_url_interface.mode = VIK_DATASOURCE_CREATENEWLAYER;
3013 a_acquire(vw, vw->viking_vlp, vw->viking_vvp, &vik_datasource_url_interface, NULL, NULL );
3016 static void goto_default_location( GtkAction *a, VikWindow *vw)
3019 ll.lat = a_vik_get_default_lat();
3020 ll.lon = a_vik_get_default_long();
3021 vik_viewport_set_center_latlon(vw->viking_vvp, &ll);
3022 vik_layers_panel_emit_update(vw->viking_vlp);
3026 static void goto_address( GtkAction *a, VikWindow *vw)
3028 a_vik_goto ( vw, vw->viking_vvp );
3029 vik_layers_panel_emit_update ( vw->viking_vlp );
3032 static void mapcache_flush_cb ( GtkAction *a, VikWindow *vw )
3037 static void layer_defaults_cb ( GtkAction *a, VikWindow *vw )
3039 gchar **texts = g_strsplit ( gtk_action_get_name(a), "Layer", 0 );
3042 return; // Internally broken :(
3044 if ( ! a_layer_defaults_show_window ( GTK_WINDOW(vw), texts[1] ) )
3045 a_dialog_info_msg ( GTK_WINDOW(vw), _("This layer has no configurable properties.") );
3046 // NB no update needed
3048 g_strfreev ( texts );
3051 static void preferences_change_update ( VikWindow *vw, gpointer data )
3053 // Want to update all TrackWaypoint layers
3054 GList *layers = vik_layers_panel_get_all_layers_of_type ( vw->viking_vlp, VIK_LAYER_TRW, TRUE );
3060 // Reset the individual waypoints themselves due to the preferences change
3061 VikTrwLayer *vtl = VIK_TRW_LAYER(layers->data);
3062 vik_trw_layer_reset_waypoints ( vtl );
3063 layers = g_list_next ( layers );
3066 g_list_free ( layers );
3071 static void preferences_cb ( GtkAction *a, VikWindow *vw )
3073 gboolean wp_icon_size = a_vik_get_use_large_waypoint_icons();
3075 a_preferences_show_window ( GTK_WINDOW(vw) );
3077 // Has the waypoint size setting changed?
3078 if (wp_icon_size != a_vik_get_use_large_waypoint_icons()) {
3079 // Delete icon indexing 'cache' and so automatically regenerates with the new setting when changed
3080 clear_garmin_icon_syms ();
3082 // Update all windows
3083 g_slist_foreach ( window_list, (GFunc) preferences_change_update, NULL );
3087 static void default_location_cb ( GtkAction *a, VikWindow *vw )
3089 /* Simplistic repeat of preference setting
3090 Only the name & type are important for setting the preference via this 'external' way */
3091 VikLayerParam pref_lat[] = {
3092 { VIK_LAYER_NUM_TYPES,
3093 VIKING_PREFERENCES_NAMESPACE "default_latitude",
3094 VIK_LAYER_PARAM_DOUBLE,
3097 VIK_LAYER_WIDGET_SPINBUTTON,
3105 VikLayerParam pref_lon[] = {
3106 { VIK_LAYER_NUM_TYPES,
3107 VIKING_PREFERENCES_NAMESPACE "default_longitude",
3108 VIK_LAYER_PARAM_DOUBLE,
3111 VIK_LAYER_WIDGET_SPINBUTTON,
3120 /* Get current center */
3122 vik_coord_to_latlon ( vik_viewport_get_center ( vw->viking_vvp ), &ll );
3124 /* Apply to preferences */
3125 VikLayerParamData vlp_data;
3126 vlp_data.d = ll.lat;
3127 a_preferences_run_setparam (vlp_data, pref_lat);
3128 vlp_data.d = ll.lon;
3129 a_preferences_run_setparam (vlp_data, pref_lon);
3130 /* Remember to save */
3131 a_preferences_save_to_file();
3134 static void clear_cb ( GtkAction *a, VikWindow *vw )
3136 vik_layers_panel_clear ( vw->viking_vlp );
3137 window_set_filename ( vw, NULL );
3141 static void window_close ( GtkAction *a, VikWindow *vw )
3143 if ( ! delete_event ( vw ) )
3144 gtk_widget_destroy ( GTK_WIDGET(vw) );
3147 static gboolean save_file_and_exit ( GtkAction *a, VikWindow *vw )
3149 if (save_file( NULL, vw)) {
3150 window_close( NULL, vw);
3157 static void zoom_to_cb ( GtkAction *a, VikWindow *vw )
3159 gdouble xmpp = vik_viewport_get_xmpp ( vw->viking_vvp ), ympp = vik_viewport_get_ympp ( vw->viking_vvp );
3160 if ( a_dialog_custom_zoom ( GTK_WINDOW(vw), &xmpp, &ympp ) )
3162 vik_viewport_set_xmpp ( vw->viking_vvp, xmpp );
3163 vik_viewport_set_ympp ( vw->viking_vvp, ympp );
3168 static void save_image_file ( VikWindow *vw, const gchar *fn, guint w, guint h, gdouble zoom, gboolean save_as_png )
3170 /* more efficient way: stuff draws directly to pixbuf (fork viewport) */
3171 GdkPixbuf *pixbuf_to_save;
3172 gdouble old_xmpp, old_ympp;
3173 GError *error = NULL;
3175 GtkWidget *msgbox = gtk_message_dialog_new ( GTK_WINDOW(vw),
3176 GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
3179 _("Generating image file...") );
3181 g_signal_connect_swapped (msgbox, "response", G_CALLBACK (gtk_widget_destroy), msgbox);
3182 // Ensure dialog shown
3183 gtk_widget_show_all ( msgbox );
3185 vik_statusbar_set_message ( vw->viking_vs, VIK_STATUSBAR_INFO, _("Generating image file...") );
3186 while ( gtk_events_pending() )
3187 gtk_main_iteration ();
3188 // Despite many efforts & variations, GTK on my Linux system doesn't show the actual msgbox contents :(
3189 // At least the empty box can give a clue something's going on + the statusbar msg...
3190 // Windows version under Wine OK!
3192 /* backup old zoom & set new */
3193 old_xmpp = vik_viewport_get_xmpp ( vw->viking_vvp );
3194 old_ympp = vik_viewport_get_ympp ( vw->viking_vvp );
3195 vik_viewport_set_zoom ( vw->viking_vvp, zoom );
3197 /* reset width and height: */
3198 vik_viewport_configure_manually ( vw->viking_vvp, w, h );
3200 /* draw all layers */
3203 /* save buffer as file. */
3204 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);
3205 if ( !pixbuf_to_save ) {
3206 g_warning("Failed to generate internal pixmap size: %d x %d", w, h);
3207 gtk_message_dialog_set_markup ( GTK_MESSAGE_DIALOG(msgbox), _("Failed to generate internal image.\n\nTry creating a smaller image.") );
3211 gdk_pixbuf_save ( pixbuf_to_save, fn, save_as_png ? "png" : "jpeg", &error, NULL );
3214 g_warning("Unable to write to file %s: %s", fn, error->message );
3215 gtk_message_dialog_set_markup ( GTK_MESSAGE_DIALOG(msgbox), _("Failed to generate image file.") );
3216 g_error_free (error);
3220 gtk_message_dialog_set_markup ( GTK_MESSAGE_DIALOG(msgbox), _("Image file generated.") );
3222 g_object_unref ( G_OBJECT(pixbuf_to_save) );
3225 vik_statusbar_set_message ( vw->viking_vs, VIK_STATUSBAR_INFO, "" );
3226 gtk_dialog_add_button ( GTK_DIALOG(msgbox), GTK_STOCK_OK, GTK_RESPONSE_OK );
3227 gtk_dialog_run ( GTK_DIALOG(msgbox) ); // Don't care about the result
3229 /* pretend like nothing happened ;) */
3230 vik_viewport_set_xmpp ( vw->viking_vvp, old_xmpp );
3231 vik_viewport_set_ympp ( vw->viking_vvp, old_ympp );
3232 vik_viewport_configure ( vw->viking_vvp );
3236 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 )
3238 gulong size = sizeof(gchar) * (strlen(fn) + 15);
3239 gchar *name_of_file = g_malloc ( size );
3241 struct UTM utm_orig, utm;
3243 /* *** copied from above *** */
3244 GdkPixbuf *pixbuf_to_save;
3245 gdouble old_xmpp, old_ympp;
3246 GError *error = NULL;
3248 /* backup old zoom & set new */
3249 old_xmpp = vik_viewport_get_xmpp ( vw->viking_vvp );
3250 old_ympp = vik_viewport_get_ympp ( vw->viking_vvp );
3251 vik_viewport_set_zoom ( vw->viking_vvp, zoom );
3253 /* reset width and height: do this only once for all images (same size) */
3254 vik_viewport_configure_manually ( vw->viking_vvp, w, h );
3255 /* *** end copy from above *** */
3257 g_assert ( vik_viewport_get_coord_mode ( vw->viking_vvp ) == VIK_COORD_UTM );
3261 utm_orig = *((const struct UTM *)vik_viewport_get_center ( vw->viking_vvp ));
3263 for ( y = 1; y <= tiles_h; y++ )
3265 for ( x = 1; x <= tiles_w; x++ )
3267 g_snprintf ( name_of_file, size, "%s%cy%d-x%d.%s", fn, G_DIR_SEPARATOR, y, x, save_as_png ? "png" : "jpg" );
3269 if ( tiles_w & 0x1 )
3270 utm.easting += ((gdouble)x - ceil(((gdouble)tiles_w)/2)) * (w*zoom);
3272 utm.easting += ((gdouble)x - (((gdouble)tiles_w)+1)/2) * (w*zoom);
3273 if ( tiles_h & 0x1 ) /* odd */
3274 utm.northing -= ((gdouble)y - ceil(((gdouble)tiles_h)/2)) * (h*zoom);
3276 utm.northing -= ((gdouble)y - (((gdouble)tiles_h)+1)/2) * (h*zoom);
3278 /* move to correct place. */
3279 vik_viewport_set_center_utm ( vw->viking_vvp, &utm );
3283 /* save buffer as file. */
3284 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);
3285 gdk_pixbuf_save ( pixbuf_to_save, name_of_file, save_as_png ? "png" : "jpeg", &error, NULL );
3288 g_warning("Unable to write to file %s: %s", name_of_file, error->message );
3289 g_error_free (error);
3292 g_object_unref ( G_OBJECT(pixbuf_to_save) );
3296 vik_viewport_set_center_utm ( vw->viking_vvp, &utm_orig );
3297 vik_viewport_set_xmpp ( vw->viking_vvp, old_xmpp );
3298 vik_viewport_set_ympp ( vw->viking_vvp, old_ympp );
3299 vik_viewport_configure ( vw->viking_vvp );
3302 g_free ( name_of_file );
3305 static void draw_to_image_file_current_window_cb(GtkWidget* widget,GdkEventButton *event,gpointer *pass_along)
3307 VikWindow *vw = VIK_WINDOW(pass_along[0]);
3308 GtkSpinButton *width_spin = GTK_SPIN_BUTTON(pass_along[1]), *height_spin = GTK_SPIN_BUTTON(pass_along[2]);
3310 gint active = gtk_combo_box_get_active ( GTK_COMBO_BOX(pass_along[3]) );
3311 gdouble zoom = pow (2, active-2 );
3313 gdouble width_min, width_max, height_min, height_max;
3316 gtk_spin_button_get_range ( width_spin, &width_min, &width_max );
3317 gtk_spin_button_get_range ( height_spin, &height_min, &height_max );
3319 /* TODO: support for xzoom and yzoom values */
3320 width = vik_viewport_get_width ( vw->viking_vvp ) * vik_viewport_get_xmpp ( vw->viking_vvp ) / zoom;
3321 height = vik_viewport_get_height ( vw->viking_vvp ) * vik_viewport_get_xmpp ( vw->viking_vvp ) / zoom;
3323 if ( width > width_max || width < width_min || height > height_max || height < height_min )
3324 a_dialog_info_msg ( GTK_WINDOW(vw), _("Viewable region outside allowable pixel size bounds for image. Clipping width/height values.") );
3326 gtk_spin_button_set_value ( width_spin, width );
3327 gtk_spin_button_set_value ( height_spin, height );
3330 static void draw_to_image_file_total_area_cb (GtkSpinButton *spinbutton, gpointer *pass_along)
3332 GtkSpinButton *width_spin = GTK_SPIN_BUTTON(pass_along[1]), *height_spin = GTK_SPIN_BUTTON(pass_along[2]);
3334 gint active = gtk_combo_box_get_active ( GTK_COMBO_BOX(pass_along[3]) );
3335 gdouble zoom = pow (2, active-2 );
3339 w = gtk_spin_button_get_value(width_spin) * zoom;
3340 h = gtk_spin_button_get_value(height_spin) * zoom;
3341 if (pass_along[4]) /* save many images; find TOTAL area covered */
3343 w *= gtk_spin_button_get_value(GTK_SPIN_BUTTON(pass_along[4]));
3344 h *= gtk_spin_button_get_value(GTK_SPIN_BUTTON(pass_along[5]));
3346 vik_units_distance_t dist_units = a_vik_get_units_distance ();
3347 switch (dist_units) {
3348 case VIK_UNITS_DISTANCE_KILOMETRES:
3349 label_text = g_strdup_printf ( _("Total area: %ldm x %ldm (%.3f sq. km)"), (glong)w, (glong)h, (w*h/1000000));
3351 case VIK_UNITS_DISTANCE_MILES:
3352 label_text = g_strdup_printf ( _("Total area: %ldm x %ldm (%.3f sq. miles)"), (glong)w, (glong)h, (w*h/2589988.11));
3355 label_text = g_strdup_printf ("Just to keep the compiler happy");
3356 g_critical("Houston, we've had a problem. distance=%d", dist_units);
3359 gtk_label_set_text(GTK_LABEL(pass_along[6]), label_text);
3360 g_free ( label_text );
3364 * Get an allocated filename (or directory as specified)
3366 static gchar* draw_image_filename ( VikWindow *vw, gboolean one_image_only )
3369 if ( one_image_only )
3372 if (!vw->save_img_dia) {
3373 vw->save_img_dia = gtk_file_chooser_dialog_new (_("Save Image"),
3375 GTK_FILE_CHOOSER_ACTION_SAVE,
3376 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
3377 GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT,
3380 gchar *cwd = g_get_current_dir();
3382 gtk_file_chooser_set_current_folder ( GTK_FILE_CHOOSER(vw->save_img_dia), cwd );
3386 GtkFileChooser *chooser = GTK_FILE_CHOOSER ( vw->save_img_dia );
3388 GtkFileFilter *filter;
3389 filter = gtk_file_filter_new ();
3390 gtk_file_filter_set_name ( filter, _("All") );
3391 gtk_file_filter_add_pattern ( filter, "*" );
3392 gtk_file_chooser_add_filter ( chooser, filter );
3394 filter = gtk_file_filter_new ();
3395 gtk_file_filter_set_name ( filter, _("JPG") );
3396 gtk_file_filter_add_mime_type ( filter, "image/jpeg");
3397 gtk_file_chooser_add_filter ( chooser, filter );
3399 if ( !vw->draw_image_save_as_png )
3400 gtk_file_chooser_set_filter ( chooser, filter );
3402 filter = gtk_file_filter_new ();
3403 gtk_file_filter_set_name ( filter, _("PNG") );
3404 gtk_file_filter_add_mime_type ( filter, "image/png");
3405 gtk_file_chooser_add_filter ( chooser, filter );
3407 if ( vw->draw_image_save_as_png )
3408 gtk_file_chooser_set_filter ( chooser, filter );
3410 gtk_window_set_transient_for ( GTK_WINDOW(vw->save_img_dia), GTK_WINDOW(vw) );
3411 gtk_window_set_destroy_with_parent ( GTK_WINDOW(vw->save_img_dia), TRUE );
3414 if ( gtk_dialog_run ( GTK_DIALOG(vw->save_img_dia) ) == GTK_RESPONSE_ACCEPT ) {
3415 fn = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER(vw->save_img_dia) );
3416 if ( g_file_test ( fn, G_FILE_TEST_EXISTS ) )
3417 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 ) ) )
3420 gtk_widget_hide ( vw->save_img_dia );
3424 // For some reason this method is only written to work in UTM...
3425 if ( vik_viewport_get_coord_mode(vw->viking_vvp) != VIK_COORD_UTM ) {
3426 a_dialog_error_msg ( GTK_WINDOW(vw), _("You must be in UTM mode to use this feature") );
3430 if (!vw->save_img_dir_dia) {
3431 vw->save_img_dir_dia = gtk_file_chooser_dialog_new (_("Choose a directory to hold images"),
3433 GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER,
3434 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
3435 GTK_STOCK_OK, GTK_RESPONSE_ACCEPT,
3437 gtk_window_set_transient_for ( GTK_WINDOW(vw->save_img_dir_dia), GTK_WINDOW(vw) );
3438 gtk_window_set_destroy_with_parent ( GTK_WINDOW(vw->save_img_dir_dia), TRUE );
3441 if ( gtk_dialog_run ( GTK_DIALOG(vw->save_img_dir_dia) ) == GTK_RESPONSE_ACCEPT ) {
3442 fn = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER(vw->save_img_dir_dia) );
3444 gtk_widget_hide ( vw->save_img_dir_dia );
3449 static void draw_to_image_file ( VikWindow *vw, gboolean one_image_only )
3451 /* todo: default for answers inside VikWindow or static (thruout instance) */
3452 GtkWidget *dialog = gtk_dialog_new_with_buttons ( _("Save to Image File"), GTK_WINDOW(vw),
3453 GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
3455 GTK_RESPONSE_REJECT,
3457 GTK_RESPONSE_ACCEPT,
3459 GtkWidget *width_label, *width_spin, *height_label, *height_spin;
3460 GtkWidget *png_radio, *jpeg_radio;
3461 GtkWidget *current_window_button;
3462 gpointer current_window_pass_along[7];
3463 GtkWidget *zoom_label, *zoom_combo;
3464 GtkWidget *total_size_label;
3466 /* only used if (!one_image_only) */
3467 GtkWidget *tiles_width_spin = NULL, *tiles_height_spin = NULL;
3469 width_label = gtk_label_new ( _("Width (pixels):") );
3470 width_spin = gtk_spin_button_new ( GTK_ADJUSTMENT(gtk_adjustment_new ( vw->draw_image_width, 10, 50000, 10, 100, 0 )), 10, 0 );
3471 height_label = gtk_label_new ( _("Height (pixels):") );
3472 height_spin = gtk_spin_button_new ( GTK_ADJUSTMENT(gtk_adjustment_new ( vw->draw_image_height, 10, 50000, 10, 100, 0 )), 10, 0 );
3474 GtkWidget *win_warning_label = gtk_label_new ( _("WARNING: USING LARGE IMAGES OVER 10000x10000\nMAY CRASH THE PROGRAM!") );
3476 zoom_label = gtk_label_new ( _("Zoom (meters per pixel):") );
3477 /* TODO: separate xzoom and yzoom factors */
3478 zoom_combo = create_zoom_combo_all_levels();
3480 gdouble mpp = vik_viewport_get_xmpp(vw->viking_vvp);
3481 gint active = 2 + round ( log (mpp) / log (2) );
3483 // Can we not hard code size here?
3488 gtk_combo_box_set_active ( GTK_COMBO_BOX(zoom_combo), active );
3490 total_size_label = gtk_label_new ( NULL );
3492 current_window_button = gtk_button_new_with_label ( _("Area in current viewable window") );
3493 current_window_pass_along [0] = vw;
3494 current_window_pass_along [1] = width_spin;
3495 current_window_pass_along [2] = height_spin;
3496 current_window_pass_along [3] = zoom_combo;
3497 current_window_pass_along [4] = NULL; /* used for one_image_only != 1 */
3498 current_window_pass_along [5] = NULL;
3499 current_window_pass_along [6] = total_size_label;
3500 g_signal_connect ( G_OBJECT(current_window_button), "button_press_event", G_CALLBACK(draw_to_image_file_current_window_cb), current_window_pass_along );
3502 png_radio = gtk_radio_button_new_with_label ( NULL, _("Save as PNG") );
3503 jpeg_radio = gtk_radio_button_new_with_label_from_widget ( GTK_RADIO_BUTTON(png_radio), _("Save as JPEG") );
3505 gtk_box_pack_start (GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), png_radio, FALSE, FALSE, 0);
3506 gtk_box_pack_start (GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), jpeg_radio, FALSE, FALSE, 0);
3508 if ( ! vw->draw_image_save_as_png )
3509 gtk_toggle_button_set_active ( GTK_TOGGLE_BUTTON(jpeg_radio), TRUE );
3511 gtk_box_pack_start (GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), width_label, FALSE, FALSE, 0);
3512 gtk_box_pack_start (GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), width_spin, FALSE, FALSE, 0);
3513 gtk_box_pack_start (GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), height_label, FALSE, FALSE, 0);
3514 gtk_box_pack_start (GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), height_spin, FALSE, FALSE, 0);
3516 gtk_box_pack_start (GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), win_warning_label, FALSE, FALSE, 0);
3518 gtk_box_pack_start (GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), current_window_button, FALSE, FALSE, 0);
3519 gtk_box_pack_start (GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), zoom_label, FALSE, FALSE, 0);
3520 gtk_box_pack_start (GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), zoom_combo, FALSE, FALSE, 0);
3522 if ( ! one_image_only )
3524 GtkWidget *tiles_width_label, *tiles_height_label;
3526 tiles_width_label = gtk_label_new ( _("East-west image tiles:") );
3527 tiles_width_spin = gtk_spin_button_new ( GTK_ADJUSTMENT(gtk_adjustment_new ( 5, 1, 10, 1, 100, 0 )), 1, 0 );
3528 tiles_height_label = gtk_label_new ( _("North-south image tiles:") );
3529 tiles_height_spin = gtk_spin_button_new ( GTK_ADJUSTMENT(gtk_adjustment_new ( 5, 1, 10, 1, 100, 0 )), 1, 0 );
3530 gtk_box_pack_start (GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), tiles_width_label, FALSE, FALSE, 0);
3531 gtk_box_pack_start (GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), tiles_width_spin, FALSE, FALSE, 0);
3532 gtk_box_pack_start (GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), tiles_height_label, FALSE, FALSE, 0);
3533 gtk_box_pack_start (GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), tiles_height_spin, FALSE, FALSE, 0);
3535 current_window_pass_along [4] = tiles_width_spin;
3536 current_window_pass_along [5] = tiles_height_spin;
3537 g_signal_connect ( G_OBJECT(tiles_width_spin), "value-changed", G_CALLBACK(draw_to_image_file_total_area_cb), current_window_pass_along );
3538 g_signal_connect ( G_OBJECT(tiles_height_spin), "value-changed", G_CALLBACK(draw_to_image_file_total_area_cb), current_window_pass_along );
3540 gtk_box_pack_start (GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), total_size_label, FALSE, FALSE, 0);
3541 g_signal_connect ( G_OBJECT(width_spin), "value-changed", G_CALLBACK(draw_to_image_file_total_area_cb), current_window_pass_along );
3542 g_signal_connect ( G_OBJECT(height_spin), "value-changed", G_CALLBACK(draw_to_image_file_total_area_cb), current_window_pass_along );
3543 g_signal_connect ( G_OBJECT(zoom_combo), "changed", G_CALLBACK(draw_to_image_file_total_area_cb), current_window_pass_along );
3545 draw_to_image_file_total_area_cb ( NULL, current_window_pass_along ); /* set correct size info now */
3547 gtk_dialog_set_default_response ( GTK_DIALOG(dialog), GTK_RESPONSE_ACCEPT );
3549 gtk_widget_show_all ( gtk_dialog_get_content_area(GTK_DIALOG(dialog)) );
3551 if ( gtk_dialog_run ( GTK_DIALOG(dialog) ) == GTK_RESPONSE_ACCEPT )
3553 gtk_widget_hide ( GTK_WIDGET(dialog) );
3555 gchar *fn = draw_image_filename ( vw, one_image_only );
3559 gint active = gtk_combo_box_get_active ( GTK_COMBO_BOX(zoom_combo) );
3560 gdouble zoom = pow (2, active-2 );
3562 if ( one_image_only )
3563 save_image_file ( vw, fn,
3564 vw->draw_image_width = gtk_spin_button_get_value_as_int ( GTK_SPIN_BUTTON(width_spin) ),
3565 vw->draw_image_height = gtk_spin_button_get_value_as_int ( GTK_SPIN_BUTTON(height_spin) ),
3567 vw->draw_image_save_as_png = gtk_toggle_button_get_active ( GTK_TOGGLE_BUTTON(png_radio) ) );
3569 // NB is in UTM mode ATM
3570 save_image_dir ( vw, fn,
3571 vw->draw_image_width = gtk_spin_button_get_value_as_int ( GTK_SPIN_BUTTON(width_spin) ),
3572 vw->draw_image_height = gtk_spin_button_get_value_as_int ( GTK_SPIN_BUTTON(height_spin) ),
3574 vw->draw_image_save_as_png = gtk_toggle_button_get_active ( GTK_TOGGLE_BUTTON(png_radio) ),
3575 gtk_spin_button_get_value ( GTK_SPIN_BUTTON(tiles_width_spin) ),
3576 gtk_spin_button_get_value ( GTK_SPIN_BUTTON(tiles_height_spin) ) );
3581 gtk_widget_destroy ( GTK_WIDGET(dialog) );
3585 static void draw_to_image_file_cb ( GtkAction *a, VikWindow *vw )
3587 draw_to_image_file ( vw, TRUE );
3590 static void draw_to_image_dir_cb ( GtkAction *a, VikWindow *vw )
3592 draw_to_image_file ( vw, FALSE );
3595 static void print_cb ( GtkAction *a, VikWindow *vw )
3597 a_print(vw, vw->viking_vvp);
3600 /* really a misnomer: changes coord mode (actual coordinates) AND/OR draw mode (viewport only) */
3601 static void window_change_coord_mode_cb ( GtkAction *old_a, GtkAction *a, VikWindow *vw )
3603 VikViewportDrawMode drawmode;
3604 if (!strcmp(gtk_action_get_name(a), "ModeUTM")) {
3605 drawmode = VIK_VIEWPORT_DRAWMODE_UTM;
3607 else if (!strcmp(gtk_action_get_name(a), "ModeLatLon")) {
3608 drawmode = VIK_VIEWPORT_DRAWMODE_LATLON;
3610 else if (!strcmp(gtk_action_get_name(a), "ModeExpedia")) {
3611 drawmode = VIK_VIEWPORT_DRAWMODE_EXPEDIA;
3613 else if (!strcmp(gtk_action_get_name(a), "ModeMercator")) {
3614 drawmode = VIK_VIEWPORT_DRAWMODE_MERCATOR;
3617 g_critical("Houston, we've had a problem.");
3621 if ( !vw->only_updating_coord_mode_ui )
3623 VikViewportDrawMode olddrawmode = vik_viewport_get_drawmode ( vw->viking_vvp );
3624 if ( olddrawmode != drawmode )
3626 /* this takes care of coord mode too */
3627 vik_viewport_set_drawmode ( vw->viking_vvp, drawmode );
3628 if ( drawmode == VIK_VIEWPORT_DRAWMODE_UTM ) {
3629 vik_layers_panel_change_coord_mode ( vw->viking_vlp, VIK_COORD_UTM );
3630 } else if ( olddrawmode == VIK_VIEWPORT_DRAWMODE_UTM ) {
3631 vik_layers_panel_change_coord_mode ( vw->viking_vlp, VIK_COORD_LATLON );
3638 static void set_draw_scale ( GtkAction *a, VikWindow *vw )
3640 GtkWidget *check_box = gtk_ui_manager_get_widget ( vw->uim, "/ui/MainMenu/View/SetShow/ShowScale" );
3641 g_assert(check_box);
3642 gboolean state = gtk_check_menu_item_get_active ( GTK_CHECK_MENU_ITEM(check_box));
3643 vik_viewport_set_draw_scale ( vw->viking_vvp, state );
3647 static void set_draw_centermark ( GtkAction *a, VikWindow *vw )
3649 GtkWidget *check_box = gtk_ui_manager_get_widget ( vw->uim, "/ui/MainMenu/View/SetShow/ShowCenterMark" );
3650 g_assert(check_box);
3651 gboolean state = gtk_check_menu_item_get_active ( GTK_CHECK_MENU_ITEM(check_box));
3652 vik_viewport_set_draw_centermark ( vw->viking_vvp, state );
3656 static void set_draw_highlight ( GtkAction *a, VikWindow *vw )
3658 GtkWidget *check_box = gtk_ui_manager_get_widget ( vw->uim, "/ui/MainMenu/View/SetShow/ShowHighlight" );
3659 g_assert(check_box);
3660 gboolean state = gtk_check_menu_item_get_active ( GTK_CHECK_MENU_ITEM(check_box));
3661 vik_viewport_set_draw_highlight ( vw->viking_vvp, state );
3665 static void set_bg_color ( GtkAction *a, VikWindow *vw )
3667 GtkWidget *colorsd = gtk_color_selection_dialog_new ( _("Choose a background color") );
3668 GdkColor *color = vik_viewport_get_background_gdkcolor ( vw->viking_vvp );
3669 gtk_color_selection_set_previous_color ( GTK_COLOR_SELECTION(gtk_color_selection_dialog_get_color_selection(GTK_COLOR_SELECTION_DIALOG(colorsd))), color );
3670 gtk_color_selection_set_current_color ( GTK_COLOR_SELECTION(gtk_color_selection_dialog_get_color_selection(GTK_COLOR_SELECTION_DIALOG(colorsd))), color );
3671 if ( gtk_dialog_run ( GTK_DIALOG(colorsd) ) == GTK_RESPONSE_OK )
3673 gtk_color_selection_get_current_color ( GTK_COLOR_SELECTION(gtk_color_selection_dialog_get_color_selection(GTK_COLOR_SELECTION_DIALOG(colorsd))), color );
3674 vik_viewport_set_background_gdkcolor ( vw->viking_vvp, color );
3678 gtk_widget_destroy ( colorsd );
3681 static void set_highlight_color ( GtkAction *a, VikWindow *vw )
3683 GtkWidget *colorsd = gtk_color_selection_dialog_new ( _("Choose a track highlight color") );
3684 GdkColor *color = vik_viewport_get_highlight_gdkcolor ( vw->viking_vvp );
3685 gtk_color_selection_set_previous_color ( GTK_COLOR_SELECTION(gtk_color_selection_dialog_get_color_selection(GTK_COLOR_SELECTION_DIALOG(colorsd))), color );
3686 gtk_color_selection_set_current_color ( GTK_COLOR_SELECTION(gtk_color_selection_dialog_get_color_selection(GTK_COLOR_SELECTION_DIALOG(colorsd))), color );
3687 if ( gtk_dialog_run ( GTK_DIALOG(colorsd) ) == GTK_RESPONSE_OK )
3689 gtk_color_selection_get_current_color ( GTK_COLOR_SELECTION(gtk_color_selection_dialog_get_color_selection(GTK_COLOR_SELECTION_DIALOG(colorsd))), color );
3690 vik_viewport_set_highlight_gdkcolor ( vw->viking_vvp, color );
3694 gtk_widget_destroy ( colorsd );
3699 /***********************************************************************************************
3701 ***********************************************************************************************/
3703 static GtkActionEntry entries[] = {
3704 { "File", NULL, N_("_File"), 0, 0, 0 },
3705 { "Edit", NULL, N_("_Edit"), 0, 0, 0 },
3706 { "View", NULL, N_("_View"), 0, 0, 0 },
3707 { "SetShow", NULL, N_("_Show"), 0, 0, 0 },
3708 { "SetZoom", NULL, N_("_Zoom"), 0, 0, 0 },
3709 { "SetPan", NULL, N_("_Pan"), 0, 0, 0 },
3710 { "Layers", NULL, N_("_Layers"), 0, 0, 0 },
3711 { "Tools", NULL, N_("_Tools"), 0, 0, 0 },
3712 { "Exttools", NULL, N_("_Webtools"), 0, 0, 0 },
3713 { "Help", NULL, N_("_Help"), 0, 0, 0 },
3715 { "New", GTK_STOCK_NEW, N_("_New"), "<control>N", N_("New file"), (GCallback)newwindow_cb },
3716 { "Open", GTK_STOCK_OPEN, N_("_Open..."), "<control>O", N_("Open a file"), (GCallback)load_file },
3717 { "OpenRecentFile", NULL, N_("Open _Recent File"), NULL, NULL, (GCallback)NULL },
3718 { "Append", GTK_STOCK_ADD, N_("Append _File..."), NULL, N_("Append data from a different file"), (GCallback)load_file },
3719 { "Export", GTK_STOCK_CONVERT, N_("_Export All"), NULL, N_("Export All TrackWaypoint Layers"), (GCallback)NULL },
3720 { "ExportGPX", NULL, N_("_GPX..."), NULL, N_("Export as GPX"), (GCallback)export_to_gpx },
3721 { "Acquire", GTK_STOCK_GO_DOWN, N_("A_cquire"), NULL, NULL, (GCallback)NULL },
3722 { "AcquireGPS", NULL, N_("From _GPS..."), NULL, N_("Transfer data from a GPS device"), (GCallback)acquire_from_gps },
3723 { "AcquireGPSBabel", NULL, N_("Import File With GPS_Babel..."), NULL, N_("Import file via GPSBabel converter"), (GCallback)acquire_from_file },
3724 { "AcquireRouting", NULL, N_("_Directions..."), NULL, N_("Get driving directions"), (GCallback)acquire_from_routing },
3725 #ifdef VIK_CONFIG_OPENSTREETMAP
3726 { "AcquireOSM", NULL, N_("_OSM Traces..."), NULL, N_("Get traces from OpenStreetMap"), (GCallback)acquire_from_osm },
3727 { "AcquireMyOSM", NULL, N_("_My OSM Traces..."), NULL, N_("Get Your Own Traces from OpenStreetMap"), (GCallback)acquire_from_my_osm },
3729 #ifdef VIK_CONFIG_GEOCACHES
3730 { "AcquireGC", NULL, N_("Geo_caches..."), NULL, N_("Get Geocaches from geocaching.com"), (GCallback)acquire_from_gc },
3732 #ifdef VIK_CONFIG_GEOTAG
3733 { "AcquireGeotag", NULL, N_("From Geotagged _Images..."), NULL, N_("Create waypoints from geotagged images"), (GCallback)acquire_from_geotag },
3735 { "AcquireURL", NULL, N_("From _URL..."), NULL, N_("Get a file from a URL"), (GCallback)acquire_from_url },
3736 #ifdef VIK_CONFIG_GEONAMES
3737 { "AcquireWikipedia", NULL, N_("From _Wikipedia Waypoints"), NULL, N_("Create waypoints from Wikipedia items in the current view"), (GCallback)acquire_from_wikipedia },
3739 { "Save", GTK_STOCK_SAVE, N_("_Save"), "<control>S", N_("Save the file"), (GCallback)save_file },
3740 { "SaveAs", GTK_STOCK_SAVE_AS, N_("Save _As..."), NULL, N_("Save the file under different name"), (GCallback)save_file_as },
3741 { "FileProperties", NULL, N_("Properties..."), NULL, N_("File Properties"), (GCallback)file_properties_cb },
3742 { "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 },
3743 { "GenImgDir", GTK_STOCK_DND_MULTIPLE, N_("Generate _Directory of Images..."), NULL, N_("FIXME:IMGDIR"), (GCallback)draw_to_image_dir_cb },
3744 { "Print", GTK_STOCK_PRINT, N_("_Print..."), NULL, N_("Print maps"), (GCallback)print_cb },
3745 { "Exit", GTK_STOCK_QUIT, N_("E_xit"), "<control>W", N_("Exit the program"), (GCallback)window_close },
3746 { "SaveExit", GTK_STOCK_QUIT, N_("Save and Exit"), NULL, N_("Save and Exit the program"), (GCallback)save_file_and_exit },
3748 { "GotoDefaultLocation", GTK_STOCK_HOME, N_("Go to the _Default Location"), NULL, N_("Go to the default location"), (GCallback)goto_default_location },
3749 { "GotoSearch", GTK_STOCK_JUMP_TO, N_("Go to _Location..."), NULL, N_("Go to address/place using text search"), (GCallback)goto_address },
3750 { "GotoLL", GTK_STOCK_JUMP_TO, N_("_Go to Lat/Lon..."), NULL, N_("Go to arbitrary lat/lon coordinate"), (GCallback)draw_goto_cb },
3751 { "GotoUTM", GTK_STOCK_JUMP_TO, N_("Go to UTM..."), NULL, N_("Go to arbitrary UTM coordinate"), (GCallback)draw_goto_cb },
3752 { "Refresh", GTK_STOCK_REFRESH, N_("_Refresh"), "F5", N_("Refresh any maps displayed"), (GCallback)draw_refresh_cb },
3753 { "SetHLColor",GTK_STOCK_SELECT_COLOR, N_("Set _Highlight Color..."), NULL, NULL, (GCallback)set_highlight_color },
3754 { "SetBGColor",GTK_STOCK_SELECT_COLOR, N_("Set Bac_kground Color..."), NULL, NULL, (GCallback)set_bg_color },
3755 { "ZoomIn", GTK_STOCK_ZOOM_IN, N_("Zoom _In"), "<control>plus", NULL, (GCallback)draw_zoom_cb },
3756 { "ZoomOut", GTK_STOCK_ZOOM_OUT, N_("Zoom _Out"), "<control>minus", NULL, (GCallback)draw_zoom_cb },
3757 { "ZoomTo", GTK_STOCK_ZOOM_FIT, N_("Zoom _To..."), "<control>Z", NULL, (GCallback)zoom_to_cb },
3758 { "PanNorth", NULL, N_("Pan _North"), "<control>Up", NULL, (GCallback)draw_pan_cb },
3759 { "PanEast", NULL, N_("Pan _East"), "<control>Right", NULL, (GCallback)draw_pan_cb },
3760 { "PanSouth", NULL, N_("Pan _South"), "<control>Down", NULL, (GCallback)draw_pan_cb },
3761 { "PanWest", NULL, N_("Pan _West"), "<control>Left", NULL, (GCallback)draw_pan_cb },
3762 { "BGJobs", GTK_STOCK_EXECUTE, N_("Background _Jobs"), NULL, NULL, (GCallback)a_background_show_window },
3764 { "Cut", GTK_STOCK_CUT, N_("Cu_t"), NULL, NULL, (GCallback)menu_cut_layer_cb },
3765 { "Copy", GTK_STOCK_COPY, N_("_Copy"), NULL, NULL, (GCallback)menu_copy_layer_cb },
3766 { "Paste", GTK_STOCK_PASTE, N_("_Paste"), NULL, NULL, (GCallback)menu_paste_layer_cb },
3767 { "Delete", GTK_STOCK_DELETE, N_("_Delete"), NULL, NULL, (GCallback)menu_delete_layer_cb },
3768 { "DeleteAll", NULL, N_("Delete All"), NULL, NULL, (GCallback)clear_cb },
3769 { "MapCacheFlush",NULL, N_("_Flush Map Cache"), NULL, NULL, (GCallback)mapcache_flush_cb },
3770 { "SetDefaultLocation", GTK_STOCK_GO_FORWARD, N_("_Set the Default Location"), NULL, N_("Set the Default Location to the current position"),(GCallback)default_location_cb },
3771 { "Preferences",GTK_STOCK_PREFERENCES, N_("_Preferences"), NULL, NULL, (GCallback)preferences_cb },
3772 { "LayerDefaults",GTK_STOCK_PROPERTIES, N_("_Layer Defaults"), NULL, NULL, NULL },
3773 { "Properties",GTK_STOCK_PROPERTIES, N_("_Properties"), NULL, NULL, (GCallback)menu_properties_cb },
3775 { "HelpEntry", GTK_STOCK_HELP, N_("_Help"), "F1", NULL, (GCallback)help_help_cb },
3776 { "About", GTK_STOCK_ABOUT, N_("_About"), NULL, NULL, (GCallback)help_about_cb },
3779 static GtkActionEntry entries_gpsbabel[] = {
3780 { "ExportKML", NULL, N_("_KML..."), NULL, N_("Export as KML"), (GCallback)export_to_kml },
3784 /* FIXME use VIEWPORT_DRAWMODE values */
3785 static GtkRadioActionEntry mode_entries[] = {
3786 { "ModeUTM", NULL, N_("_UTM Mode"), "<control>u", NULL, 0 },
3787 { "ModeExpedia", NULL, N_("_Expedia Mode"), "<control>e", NULL, 1 },
3788 { "ModeMercator", NULL, N_("_Mercator Mode"), "<control>m", NULL, 4 },
3789 { "ModeLatLon", NULL, N_("Lat_/Lon Mode"), "<control>l", NULL, 5 },
3792 static GtkToggleActionEntry toggle_entries[] = {
3793 { "ShowScale", NULL, N_("Show _Scale"), "<shift>F5", N_("Show Scale"), (GCallback)set_draw_scale, TRUE },
3794 { "ShowCenterMark", NULL, N_("Show _Center Mark"), "F6", N_("Show Center Mark"), (GCallback)set_draw_centermark, TRUE },
3795 { "ShowHighlight", GTK_STOCK_UNDERLINE, N_("Show _Highlight"), "F7", N_("Show Highlight"), (GCallback)set_draw_highlight, TRUE },
3796 { "FullScreen", GTK_STOCK_FULLSCREEN, N_("_Full Screen"), "F11", N_("Activate full screen mode"), (GCallback)full_screen_cb, FALSE },
3797 { "ViewSidePanel", GTK_STOCK_INDEX, N_("Show Side _Panel"), "F9", N_("Show Side Panel"), (GCallback)view_side_panel_cb, TRUE },
3798 { "ViewStatusBar", NULL, N_("Show Status_bar"), "F12", N_("Show Statusbar"), (GCallback)view_statusbar_cb, TRUE },
3799 { "ViewToolbar", NULL, N_("Show _Toolbar"), "F3", N_("Show Toolbar"), (GCallback)view_toolbar_cb, TRUE },
3800 { "ViewMainMenu", NULL, N_("Show _Menu"), "F4", N_("Show Menu"), (GCallback)view_main_menu_cb, TRUE },
3803 #include "menu.xml.h"
3804 static void window_create_ui( VikWindow *window )
3807 GtkActionGroup *action_group;
3808 GtkAccelGroup *accel_group;
3811 GtkIconFactory *icon_factory;
3812 GtkIconSet *icon_set;
3813 GtkRadioActionEntry *tools = NULL, *radio;
3816 uim = gtk_ui_manager_new ();
3819 toolbox_add_tool(window->vt, &ruler_tool, TOOL_LAYER_TYPE_NONE);
3820 toolbox_add_tool(window->vt, &zoom_tool, TOOL_LAYER_TYPE_NONE);
3821 toolbox_add_tool(window->vt, &pan_tool, TOOL_LAYER_TYPE_NONE);
3822 toolbox_add_tool(window->vt, &select_tool, TOOL_LAYER_TYPE_NONE);
3825 if (!(mid = gtk_ui_manager_add_ui_from_string (uim, menu_xml, -1, &error))) {
3826 g_error_free (error);
3830 action_group = gtk_action_group_new ("MenuActions");
3831 gtk_action_group_set_translation_domain(action_group, PACKAGE_NAME);
3832 gtk_action_group_add_actions (action_group, entries, G_N_ELEMENTS (entries), window);
3833 gtk_action_group_add_toggle_actions (action_group, toggle_entries, G_N_ELEMENTS (toggle_entries), window);
3834 gtk_action_group_add_radio_actions (action_group, mode_entries, G_N_ELEMENTS (mode_entries), 4, (GCallback)window_change_coord_mode_cb, window);
3836 // Use this to see if GPSBabel is available:
3837 if ( a_babel_device_list ) {
3838 // If going to add more entries then might be worth creating a menu_gpsbabel.xml.h file
3839 if ( gtk_ui_manager_add_ui_from_string ( uim,
3840 "<ui><menubar name='MainMenu'><menu action='File'><menu action='Export'><menuitem action='ExportKML'/></menu></menu></menubar></ui>",
3842 gtk_action_group_add_actions ( action_group, entries_gpsbabel, G_N_ELEMENTS (entries_gpsbabel), window );
3845 icon_factory = gtk_icon_factory_new ();
3846 gtk_icon_factory_add_default (icon_factory);
3848 register_vik_icons(icon_factory);
3850 // Copy the tool RadioActionEntries out of the main Window structure into an extending array 'tools'
3851 // so that it can be applied to the UI in one action group add function call below
3853 for (i=0; i<window->vt->n_tools; i++) {
3854 tools = g_renew(GtkRadioActionEntry, tools, ntools+1);
3855 radio = &tools[ntools];
3857 *radio = window->vt->tools[i].ti.radioActionEntry;
3858 radio->value = ntools;
3861 for (i=0; i<VIK_LAYER_NUM_TYPES; i++) {
3862 GtkActionEntry action;
3863 gtk_ui_manager_add_ui(uim, mid, "/ui/MainMenu/Layers/",
3864 vik_layer_get_interface(i)->name,
3865 vik_layer_get_interface(i)->name,
3866 GTK_UI_MANAGER_MENUITEM, FALSE);
3868 icon_set = gtk_icon_set_new_from_pixbuf (gdk_pixbuf_from_pixdata (vik_layer_get_interface(i)->icon, FALSE, NULL ));
3869 gtk_icon_factory_add (icon_factory, vik_layer_get_interface(i)->name, icon_set);
3870 gtk_icon_set_unref (icon_set);
3872 action.name = vik_layer_get_interface(i)->name;
3873 action.stock_id = vik_layer_get_interface(i)->name;
3874 action.label = g_strdup_printf( _("New _%s Layer"), vik_layer_get_interface(i)->name);
3875 action.accelerator = vik_layer_get_interface(i)->accelerator;
3876 action.tooltip = NULL;
3877 action.callback = (GCallback)menu_addlayer_cb;
3878 gtk_action_group_add_actions(action_group, &action, 1, window);
3880 g_free ( (gchar*)action.label );
3882 if ( vik_layer_get_interface(i)->tools_count ) {
3883 gtk_ui_manager_add_ui(uim, mid, "/ui/MainMenu/Tools/", vik_layer_get_interface(i)->name, NULL, GTK_UI_MANAGER_SEPARATOR, FALSE);
3884 gtk_ui_manager_add_ui(uim, mid, "/ui/MainToolbar/ToolItems/", vik_layer_get_interface(i)->name, NULL, GTK_UI_MANAGER_SEPARATOR, FALSE);
3887 // Further tool copying for to apply to the UI, also apply menu UI setup
3888 for ( j = 0; j < vik_layer_get_interface(i)->tools_count; j++ ) {
3889 tools = g_renew(GtkRadioActionEntry, tools, ntools+1);
3890 radio = &tools[ntools];
3893 gtk_ui_manager_add_ui(uim, mid, "/ui/MainMenu/Tools",
3894 vik_layer_get_interface(i)->tools[j].radioActionEntry.label,
3895 vik_layer_get_interface(i)->tools[j].radioActionEntry.name,
3896 GTK_UI_MANAGER_MENUITEM, FALSE);
3897 gtk_ui_manager_add_ui(uim, mid, "/ui/MainToolbar/ToolItems",
3898 vik_layer_get_interface(i)->tools[j].radioActionEntry.label,
3899 vik_layer_get_interface(i)->tools[j].radioActionEntry.name,
3900 GTK_UI_MANAGER_TOOLITEM, FALSE);
3902 toolbox_add_tool(window->vt, &(vik_layer_get_interface(i)->tools[j]), i);
3904 *radio = vik_layer_get_interface(i)->tools[j].radioActionEntry;
3905 // Overwrite with actual number to use
3906 radio->value = ntools;
3909 GtkActionEntry action_dl;
3910 gchar *layername = g_strdup_printf ( "Layer%s", vik_layer_get_interface(i)->fixed_layer_name );
3911 gtk_ui_manager_add_ui(uim, mid, "/ui/MainMenu/Edit/LayerDefaults",
3912 vik_layer_get_interface(i)->name,
3914 GTK_UI_MANAGER_MENUITEM, FALSE);
3917 // For default layers use action names of the form 'Layer<LayerName>'
3918 // This is to avoid clashing with just the layer name used above for the tool actions
3919 action_dl.name = g_strconcat("Layer", vik_layer_get_interface(i)->fixed_layer_name, NULL);
3920 action_dl.stock_id = NULL;
3921 action_dl.label = g_strconcat("_", vik_layer_get_interface(i)->name, "...", NULL); // Prepend marker for keyboard accelerator
3922 action_dl.accelerator = NULL;
3923 action_dl.tooltip = NULL;
3924 action_dl.callback = (GCallback)layer_defaults_cb;
3925 gtk_action_group_add_actions(action_group, &action_dl, 1, window);
3926 g_free ( (gchar*)action_dl.name );
3927 g_free ( (gchar*)action_dl.label );
3929 g_object_unref (icon_factory);
3931 gtk_action_group_add_radio_actions(action_group, tools, ntools, 0, (GCallback)menu_tool_cb, window);
3934 gtk_ui_manager_insert_action_group (uim, action_group, 0);
3936 for (i=0; i<VIK_LAYER_NUM_TYPES; i++) {
3937 for ( j = 0; j < vik_layer_get_interface(i)->tools_count; j++ ) {
3938 GtkAction *action = gtk_action_group_get_action(action_group,
3939 vik_layer_get_interface(i)->tools[j].radioActionEntry.name);
3940 g_object_set(action, "sensitive", FALSE, NULL);
3944 // This is done last so we don't need to track the value of mid anymore
3945 vik_ext_tools_add_action_items ( window, window->uim, action_group, mid );
3947 window->action_group = action_group;
3949 accel_group = gtk_ui_manager_get_accel_group (uim);
3950 gtk_window_add_accel_group (GTK_WINDOW (window), accel_group);
3951 gtk_ui_manager_ensure_update (uim);
3953 setup_recent_files(window);
3957 // TODO - add method to add tool icons defined from outside this file
3958 // and remove the reverse dependency on icon definition from this file
3960 const GdkPixdata *data;
3963 { &mover_22_pixbuf, "vik-icon-pan" },
3964 { &zoom_18_pixbuf, "vik-icon-zoom" },
3965 { &ruler_18_pixbuf, "vik-icon-ruler" },
3966 { &select_18_pixbuf, "vik-icon-select" },
3967 { &vik_new_route_18_pixbuf, "vik-icon-Create Route" },
3968 { &route_finder_18_pixbuf, "vik-icon-Route Finder" },
3969 { &demdl_18_pixbuf, "vik-icon-DEM Download" },
3970 { &showpic_18_pixbuf, "vik-icon-Show Picture" },
3971 { &addtr_18_pixbuf, "vik-icon-Create Track" },
3972 { &edtr_18_pixbuf, "vik-icon-Edit Trackpoint" },
3973 { &addwp_18_pixbuf, "vik-icon-Create Waypoint" },
3974 { &edwp_18_pixbuf, "vik-icon-Edit Waypoint" },
3975 { &geozoom_18_pixbuf, "vik-icon-Georef Zoom Tool" },
3976 { &geomove_18_pixbuf, "vik-icon-Georef Move Map" },
3977 { &mapdl_18_pixbuf, "vik-icon-Maps Download" },
3980 static gint n_stock_icons = G_N_ELEMENTS (stock_icons);
3983 register_vik_icons (GtkIconFactory *icon_factory)
3985 GtkIconSet *icon_set;
3988 for (i = 0; i < n_stock_icons; i++) {
3989 icon_set = gtk_icon_set_new_from_pixbuf (gdk_pixbuf_from_pixdata (
3990 stock_icons[i].data, FALSE, NULL ));
3991 gtk_icon_factory_add (icon_factory, stock_icons[i].stock_id, icon_set);
3992 gtk_icon_set_unref (icon_set);
3996 gpointer vik_window_get_selected_trw_layer ( VikWindow *vw )
3998 return vw->selected_vtl;
4001 void vik_window_set_selected_trw_layer ( VikWindow *vw, gpointer vtl )
4003 vw->selected_vtl = vtl;
4004 vw->containing_vtl = vtl;
4006 vw->selected_track = NULL;
4007 vw->selected_tracks = NULL;
4008 vw->selected_waypoint = NULL;
4009 vw->selected_waypoints = NULL;
4010 // Set highlight thickness
4011 vik_viewport_set_highlight_thickness ( vw->viking_vvp, vik_trw_layer_get_property_tracks_line_thickness (vw->containing_vtl) );
4014 GHashTable *vik_window_get_selected_tracks ( VikWindow *vw )
4016 return vw->selected_tracks;
4019 void vik_window_set_selected_tracks ( VikWindow *vw, GHashTable *ght, gpointer vtl )
4021 vw->selected_tracks = ght;
4022 vw->containing_vtl = vtl;
4024 vw->selected_vtl = NULL;
4025 vw->selected_track = NULL;
4026 vw->selected_waypoint = NULL;
4027 vw->selected_waypoints = NULL;
4028 // Set highlight thickness
4029 vik_viewport_set_highlight_thickness ( vw->viking_vvp, vik_trw_layer_get_property_tracks_line_thickness (vw->containing_vtl) );
4032 gpointer vik_window_get_selected_track ( VikWindow *vw )
4034 return vw->selected_track;
4037 void vik_window_set_selected_track ( VikWindow *vw, gpointer *vt, gpointer vtl )
4039 vw->selected_track = vt;
4040 vw->containing_vtl = vtl;
4042 vw->selected_vtl = NULL;
4043 vw->selected_tracks = NULL;
4044 vw->selected_waypoint = NULL;
4045 vw->selected_waypoints = NULL;
4046 // Set highlight thickness
4047 vik_viewport_set_highlight_thickness ( vw->viking_vvp, vik_trw_layer_get_property_tracks_line_thickness (vw->containing_vtl) );
4050 GHashTable *vik_window_get_selected_waypoints ( VikWindow *vw )
4052 return vw->selected_waypoints;
4055 void vik_window_set_selected_waypoints ( VikWindow *vw, GHashTable *ght, gpointer vtl )
4057 vw->selected_waypoints = ght;
4058 vw->containing_vtl = vtl;
4060 vw->selected_vtl = NULL;
4061 vw->selected_track = NULL;
4062 vw->selected_tracks = NULL;
4063 vw->selected_waypoint = NULL;
4066 gpointer vik_window_get_selected_waypoint ( VikWindow *vw )
4068 return vw->selected_waypoint;
4071 void vik_window_set_selected_waypoint ( VikWindow *vw, gpointer *vwp, gpointer vtl )
4073 vw->selected_waypoint = vwp;
4074 vw->containing_vtl = vtl;
4076 vw->selected_vtl = NULL;
4077 vw->selected_track = NULL;
4078 vw->selected_tracks = NULL;
4079 vw->selected_waypoints = NULL;
4082 gboolean vik_window_clear_highlight ( VikWindow *vw )
4084 gboolean need_redraw = FALSE;
4085 if ( vw->selected_vtl != NULL ) {
4086 vw->selected_vtl = NULL;
4089 if ( vw->selected_track != NULL ) {
4090 vw->selected_track = NULL;
4093 if ( vw->selected_tracks != NULL ) {
4094 vw->selected_tracks = NULL;
4097 if ( vw->selected_waypoint != NULL ) {
4098 vw->selected_waypoint = NULL;
4101 if ( vw->selected_waypoints != NULL ) {
4102 vw->selected_waypoints = NULL;
4108 GThread *vik_window_get_thread ( VikWindow *vw )