]> git.street.me.uk Git - andy/viking.git/blob - src/vikwindow.c
XDG_CACHE_HOME compliant thumbnail usage
[andy/viking.git] / src / vikwindow.c
1 /*
2  * viking -- GPS Data and Topo Analyzer, Explorer, and Manager
3  *
4  * Copyright (C) 2003-2005, Evan Battaglia <gtoevan@gmx.net>
5  * Copyright (C) 2005-2006, Alex Foobarian <foobarian@gmail.com>
6  * Copyright (C) 2012-2015, Rob Norris <rw_norris@hotmail.com>
7  *
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.
12  *
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.
17  *
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
21  *
22  */
23
24 #ifdef HAVE_CONFIG_H
25 #include "config.h"
26 #endif
27
28 #include "viking.h"
29 #include "background.h"
30 #include "acquire.h"
31 #include "datasources.h"
32 #include "geojson.h"
33 #include "vikgoto.h"
34 #include "dems.h"
35 #include "mapcache.h"
36 #include "print.h"
37 #include "preferences.h"
38 #include "toolbar.h"
39 #include "viklayer_defaults.h"
40 #include "icons/icons.h"
41 #include "vikexttools.h"
42 #include "vikexttool_datasources.h"
43 #include "garminsymbols.h"
44 #include "vikmapslayer.h"
45 #include "vikrouting.h"
46 #include "geonamessearch.h"
47 #include "dir.h"
48 #include "kmz.h"
49
50 #ifdef HAVE_STDLIB_H
51 #include <stdlib.h>
52 #endif
53 #ifdef HAVE_MATH_H
54 #include <math.h>
55 #endif
56 #ifdef HAVE_STRING_H
57 #include <string.h>
58 #endif
59 #include <ctype.h>
60 #include <glib.h>
61 #include <glib/gstdio.h>
62 #include <glib/gprintf.h>
63 #include <glib/gi18n.h>
64 #include <gio/gio.h>
65 #include <gdk/gdkkeysyms.h>
66
67 // This seems rather arbitary, quite large and pointless
68 //  I mean, if you have a thousand windows open;
69 //   why not be allowed to open a thousand more...
70 #define MAX_WINDOWS 1024
71 static guint window_count = 0;
72 static GSList *window_list = NULL;
73
74 #define VIKING_WINDOW_WIDTH      1000
75 #define VIKING_WINDOW_HEIGHT     800
76 #define DRAW_IMAGE_DEFAULT_WIDTH 1280
77 #define DRAW_IMAGE_DEFAULT_HEIGHT 1024
78 #define DRAW_IMAGE_DEFAULT_SAVE_AS_PNG TRUE
79
80 // The last used directories
81 static gchar *last_folder_files_uri = NULL;
82 static gchar *last_folder_images_uri = NULL;
83
84 static void window_finalize ( GObject *gob );
85 static GObjectClass *parent_class;
86
87 static void window_set_filename ( VikWindow *vw, const gchar *filename );
88 static const gchar *window_get_filename ( VikWindow *vw );
89
90 static VikWindow *window_new ();
91
92 static void draw_update ( VikWindow *vw );
93
94 static void newwindow_cb ( GtkAction *a, VikWindow *vw );
95
96 // Signals
97 static void open_window ( VikWindow *vw, GSList *files );
98 static void destroy_window ( GtkWidget *widget,
99                              gpointer   data );
100
101 /* Drawing & stuff */
102
103 static gboolean delete_event( VikWindow *vw );
104
105 static gboolean key_press_event( VikWindow *vw, GdkEventKey *event, gpointer data );
106
107 static void center_changed_cb ( VikWindow *vw );
108 static void window_configure_event ( VikWindow *vw );
109 static void draw_sync ( VikWindow *vw );
110 static void draw_redraw ( VikWindow *vw );
111 static void draw_scroll  ( VikWindow *vw, GdkEventScroll *event );
112 static void draw_click  ( VikWindow *vw, GdkEventButton *event );
113 static void draw_release ( VikWindow *vw, GdkEventButton *event );
114 static void draw_mouse_motion ( VikWindow *vw, GdkEventMotion *event );
115 static void draw_zoom_cb ( GtkAction *a, VikWindow *vw );
116 static void draw_goto_cb ( GtkAction *a, VikWindow *vw );
117 static void draw_refresh_cb ( GtkAction *a, VikWindow *vw );
118
119 static void draw_status ( VikWindow *vw );
120
121 /* End Drawing Functions */
122
123 static void toggle_draw_scale ( GtkAction *a, VikWindow *vw );
124 static void toggle_draw_centermark ( GtkAction *a, VikWindow *vw );
125 static void toggle_draw_highlight ( GtkAction *a, VikWindow *vw );
126
127 static void menu_addlayer_cb ( GtkAction *a, VikWindow *vw );
128 static void menu_properties_cb ( GtkAction *a, VikWindow *vw );
129 static void menu_delete_layer_cb ( GtkAction *a, VikWindow *vw );
130
131 /* tool management */
132 typedef struct {
133   VikToolInterface ti;
134   gpointer state;
135   gint layer_type;
136 } toolbox_tool_t;
137 #define TOOL_LAYER_TYPE_NONE -1
138
139 typedef struct {
140   int                   active_tool;
141   int                   n_tools;
142   toolbox_tool_t        *tools;
143   VikWindow *vw;
144 } toolbox_tools_t;
145
146 static void menu_cb ( GtkAction *old, GtkAction *a, VikWindow *vw );
147 static void window_change_coord_mode_cb ( GtkAction *old, GtkAction *a, VikWindow *vw );
148 static toolbox_tools_t* toolbox_create(VikWindow *vw);
149 static void toolbox_add_tool(toolbox_tools_t *vt, VikToolInterface *vti, gint layer_type );
150 static int toolbox_get_tool(toolbox_tools_t *vt, const gchar *tool_name);
151 static void toolbox_activate(toolbox_tools_t *vt, const gchar *tool_name);
152 static const GdkCursor *toolbox_get_cursor(toolbox_tools_t *vt, const gchar *tool_name);
153 static void toolbox_click (toolbox_tools_t *vt, GdkEventButton *event);
154 static void toolbox_move (toolbox_tools_t *vt, GdkEventMotion *event);
155 static void toolbox_release (toolbox_tools_t *vt, GdkEventButton *event);
156
157
158 /* ui creation */
159 static void window_create_ui( VikWindow *window );
160 static void register_vik_icons (GtkIconFactory *icon_factory);
161
162 /* i/o */
163 static void load_file ( GtkAction *a, VikWindow *vw );
164 static gboolean save_file_as ( GtkAction *a, VikWindow *vw );
165 static gboolean save_file ( GtkAction *a, VikWindow *vw );
166 static gboolean save_file_and_exit ( GtkAction *a, VikWindow *vw );
167 static gboolean window_save ( VikWindow *vw );
168
169 struct _VikWindow {
170   GtkWindow gtkwindow;
171   GtkWidget *hpaned;
172   VikViewport *viking_vvp;
173   VikLayersPanel *viking_vlp;
174   VikStatusbar *viking_vs;
175   VikToolbar *viking_vtb;
176
177   GtkWidget *main_vbox;
178   GtkWidget *menu_hbox;
179
180   GdkCursor *busy_cursor;
181   GdkCursor *viewport_cursor; // only a reference
182
183   /* tool management state */
184   guint current_tool;
185   toolbox_tools_t *vt;
186   guint16 tool_layer_id;
187   guint16 tool_tool_id;
188
189   GtkActionGroup *action_group;
190
191   // Display controls
192   // NB scale, centermark and highlight are in viewport.
193   gboolean show_full_screen;
194   gboolean show_side_panel;
195   gboolean show_statusbar;
196   gboolean show_toolbar;
197   gboolean show_main_menu;
198
199   gboolean select_move;
200   gboolean pan_move;
201   gint pan_x, pan_y;
202   gint delayed_pan_x, delayed_pan_y; // Temporary storage
203   gboolean single_click_pending;
204
205   guint draw_image_width, draw_image_height;
206   gboolean draw_image_save_as_png;
207
208   gchar *filename;
209   gboolean modified;
210   VikLoadType_t loaded_type;
211
212   gboolean only_updating_coord_mode_ui; /* hack for a bug in GTK */
213   GtkUIManager *uim;
214
215   GThread  *thread;
216   /* half-drawn update */
217   VikLayer *trigger;
218   VikCoord trigger_center;
219
220   /* Store at this level for highlighted selection drawing since it applies to the viewport and the layers panel */
221   /* Only one of these items can be selected at the same time */
222   gpointer selected_vtl; /* notionally VikTrwLayer */
223   GHashTable *selected_tracks;
224   gpointer selected_track; /* notionally VikTrack */
225   GHashTable *selected_waypoints;
226   gpointer selected_waypoint; /* notionally VikWaypoint */
227   /* only use for individual track or waypoint */
228   /* For track(s) & waypoint(s) it is the layer they are in - this helps refering to the individual item easier */
229   gpointer containing_vtl; /* notionally VikTrwLayer */
230 };
231
232 enum {
233  TOOL_PAN = 0,
234  TOOL_ZOOM,
235  TOOL_RULER,
236  TOOL_SELECT,
237  TOOL_LAYER,
238  NUMBER_OF_TOOLS
239 };
240
241 enum {
242   VW_NEWWINDOW_SIGNAL,
243   VW_OPENWINDOW_SIGNAL,
244   VW_LAST_SIGNAL
245 };
246
247 static guint window_signals[VW_LAST_SIGNAL] = { 0 };
248
249 // TODO get rid of this as this is unnecessary duplication...
250 static gchar *tool_names[NUMBER_OF_TOOLS] = { N_("Pan"), N_("Zoom"), N_("Ruler"), N_("Select") };
251
252 G_DEFINE_TYPE (VikWindow, vik_window, GTK_TYPE_WINDOW)
253
254 VikViewport * vik_window_viewport(VikWindow *vw)
255 {
256   return(vw->viking_vvp);
257 }
258
259 VikLayersPanel * vik_window_layers_panel(VikWindow *vw)
260 {
261   return(vw->viking_vlp);
262 }
263
264 /**
265  *  Returns the statusbar for the window
266  */
267 VikStatusbar * vik_window_get_statusbar ( VikWindow *vw )
268 {
269   return vw->viking_vs;
270 }
271
272 /**
273  *  Returns the 'project' filename
274  */
275 const gchar *vik_window_get_filename (VikWindow *vw)
276 {
277   return vw->filename;
278 }
279
280 typedef struct {
281   VikStatusbar *vs;
282   vik_statusbar_type_t vs_type;
283   gchar* message; // Always make a copy of this data
284 } statusbar_idle_data;
285
286 /**
287  * For the actual statusbar update!
288  */
289 static gboolean statusbar_idle_update ( statusbar_idle_data *sid )
290 {
291   vik_statusbar_set_message ( sid->vs, sid->vs_type, sid->message );
292   g_free ( sid->message );
293   g_free ( sid );
294   return FALSE;
295 }
296
297 /**
298  * vik_window_statusbar_update:
299  * @vw:      The main window in which the statusbar will be updated.
300  * @message: The string to be displayed. This is copied.
301  * @vs_type: The part of the statusbar to be updated.
302  *
303  * This updates any part of the statusbar with the new string.
304  * It handles calling from the main thread or any background thread
305  * ATM this mostly used from background threads - as from the main thread
306  *  one may use the vik_statusbar_set_message() directly.
307  */
308 void vik_window_statusbar_update ( VikWindow *vw, const gchar* message, vik_statusbar_type_t vs_type )
309 {
310   GThread *thread = vik_window_get_thread ( vw );
311   if ( !thread )
312     // Do nothing
313     return;
314
315   statusbar_idle_data *sid = g_malloc ( sizeof (statusbar_idle_data) );
316   sid->vs = vw->viking_vs;
317   sid->vs_type = vs_type;
318   sid->message = g_strdup ( message );
319
320   if ( g_thread_self() == thread ) {
321     g_idle_add ( (GSourceFunc) statusbar_idle_update, sid );
322   }
323   else {
324     // From a background thread
325     gdk_threads_add_idle ( (GSourceFunc) statusbar_idle_update, sid );
326   }
327 }
328
329 // Actual signal handlers
330 static void destroy_window ( GtkWidget *widget,
331                              gpointer   data )
332 {
333     g_debug ( "%s", __FUNCTION__ );
334     if ( ! --window_count ) {
335       g_free ( last_folder_files_uri );
336       g_free ( last_folder_images_uri );
337       gtk_main_quit ();
338     }
339 }
340
341 #define VIK_SETTINGS_WIN_SIDEPANEL "window_sidepanel"
342 #define VIK_SETTINGS_WIN_STATUSBAR "window_statusbar"
343 #define VIK_SETTINGS_WIN_TOOLBAR "window_toolbar"
344 // Menubar setting to off is never auto saved in case it's accidentally turned off
345 // It's not so obvious so to recover the menu visibility.
346 // Thus this value is for setting manually via editting the settings file directly
347 #define VIK_SETTINGS_WIN_MENUBAR "window_menubar"
348
349 VikWindow *vik_window_new_window ()
350 {
351   if ( window_count < MAX_WINDOWS )
352   {
353     VikWindow *vw = window_new ();
354
355     if ( window_count == 0 ) {
356        vik_window_statusbar_update ( vw, _("This is Viking "VIKING_VERSION), VIK_STATUSBAR_INFO );
357     }
358
359     g_signal_connect (G_OBJECT (vw), "destroy",
360                       G_CALLBACK (destroy_window), NULL);
361     g_signal_connect (G_OBJECT (vw), "newwindow",
362                       G_CALLBACK (vik_window_new_window), NULL);
363     g_signal_connect (G_OBJECT (vw), "openwindow",
364                       G_CALLBACK (open_window), NULL);
365
366     gtk_widget_show_all ( GTK_WIDGET(vw) );
367
368     if ( a_vik_get_restore_window_state() ) {
369       // These settings are applied after the show all as these options hide widgets
370       gboolean sidepanel;
371       if ( a_settings_get_boolean ( VIK_SETTINGS_WIN_SIDEPANEL, &sidepanel ) )
372         if ( ! sidepanel ) {
373           gtk_widget_hide ( GTK_WIDGET(vw->viking_vlp) );
374           GtkWidget *check_box = gtk_ui_manager_get_widget ( vw->uim, "/ui/MainMenu/View/SetShow/ViewSidePanel" );
375           gtk_check_menu_item_set_active ( GTK_CHECK_MENU_ITEM(check_box), FALSE );
376         }
377
378       gboolean statusbar;
379       if ( a_settings_get_boolean ( VIK_SETTINGS_WIN_STATUSBAR, &statusbar ) )
380         if ( ! statusbar ) {
381           gtk_widget_hide ( GTK_WIDGET(vw->viking_vs) );
382           GtkWidget *check_box = gtk_ui_manager_get_widget ( vw->uim, "/ui/MainMenu/View/SetShow/ViewStatusBar" );
383           gtk_check_menu_item_set_active ( GTK_CHECK_MENU_ITEM(check_box), FALSE );
384         }
385
386       gboolean toolbar;
387       if ( a_settings_get_boolean ( VIK_SETTINGS_WIN_TOOLBAR, &toolbar ) )
388         if ( ! toolbar ) {
389           gtk_widget_hide ( toolbar_get_widget (vw->viking_vtb) );
390           GtkWidget *check_box = gtk_ui_manager_get_widget ( vw->uim, "/ui/MainMenu/View/SetShow/ViewToolBar" );
391           gtk_check_menu_item_set_active ( GTK_CHECK_MENU_ITEM(check_box), FALSE );
392         }
393
394       gboolean menubar;
395       if ( a_settings_get_boolean ( VIK_SETTINGS_WIN_MENUBAR, &menubar ) )
396         if ( ! menubar ) {
397           gtk_widget_hide ( gtk_ui_manager_get_widget ( vw->uim, "/ui/MainMenu" ) );
398           GtkWidget *check_box = gtk_ui_manager_get_widget ( vw->uim, "/ui/MainMenu/View/SetShow/ViewMainMenu" );
399           gtk_check_menu_item_set_active ( GTK_CHECK_MENU_ITEM(check_box), FALSE );
400         }
401     }
402     window_count++;
403
404     return vw;
405   }
406   return NULL;
407 }
408
409 /**
410  * determine_location_thread:
411  * @vw:         The window that will get updated
412  * @threaddata: Data used by our background thread mechanism
413  *
414  * Use the features in vikgoto to determine where we are
415  * Then set up the viewport:
416  *  1. To goto the location
417  *  2. Set an appropriate level zoom for the location type
418  *  3. Some statusbar message feedback
419  */
420 static int determine_location_thread ( VikWindow *vw, gpointer threaddata )
421 {
422   struct LatLon ll;
423   gchar *name = NULL;
424   gint ans = a_vik_goto_where_am_i ( vw->viking_vvp, &ll, &name );
425
426   int result = a_background_thread_progress ( threaddata, 1.0 );
427   if ( result != 0 ) {
428     vik_window_statusbar_update ( vw, _("Location lookup aborted"), VIK_STATUSBAR_INFO );
429     return -1; /* Abort thread */
430   }
431
432   if ( ans ) {
433     // Zoom out a little
434     gdouble zoom = 16.0;
435
436     if ( ans == 2 ) {
437       // Position found with city precision - so zoom out more
438       zoom = 128.0;
439     }
440     else if ( ans == 3 ) {
441       // Position found via country name search - so zoom wayyyy out
442       zoom = 2048.0;
443     }
444
445     vik_viewport_set_zoom ( vw->viking_vvp, zoom );
446     vik_viewport_set_center_latlon ( vw->viking_vvp, &ll, FALSE );
447
448     gchar *message = g_strdup_printf ( _("Location found: %s"), name );
449     vik_window_statusbar_update ( vw, message, VIK_STATUSBAR_INFO );
450     g_free ( name );
451     g_free ( message );
452
453     // Signal to redraw from the background
454     vik_layers_panel_emit_update ( vw->viking_vlp );
455   }
456   else
457     vik_window_statusbar_update ( vw, _("Unable to determine location"), VIK_STATUSBAR_INFO );
458
459   return 0;
460 }
461
462 /**
463  * Steps to be taken once initial loading has completed
464  */
465 void vik_window_new_window_finish ( VikWindow *vw )
466 {
467   // Don't add a map if we've loaded a Viking file already
468   if ( vw->filename )
469     return;
470
471   // Maybe add a default map layer
472   if ( a_vik_get_add_default_map_layer () ) {
473     VikMapsLayer *vml = VIK_MAPS_LAYER ( vik_layer_create(VIK_LAYER_MAPS, vw->viking_vvp, FALSE) );
474     vik_layer_rename ( VIK_LAYER(vml), _("Default Map") );
475     vik_aggregate_layer_add_layer ( vik_layers_panel_get_top_layer(vw->viking_vlp), VIK_LAYER(vml), TRUE );
476
477     draw_update ( vw );
478   }
479
480   // If not loaded any file, maybe try the location lookup
481   if ( vw->loaded_type == LOAD_TYPE_READ_FAILURE ) {
482     if ( a_vik_get_startup_method ( ) == VIK_STARTUP_METHOD_AUTO_LOCATION ) {
483
484       vik_statusbar_set_message ( vw->viking_vs, VIK_STATUSBAR_INFO, _("Trying to determine location...") );
485
486       a_background_thread ( BACKGROUND_POOL_REMOTE,
487                             GTK_WINDOW(vw),
488                             _("Determining location"),
489                             (vik_thr_func) determine_location_thread,
490                             vw,
491                             NULL,
492                             NULL,
493                             1 );
494     }
495   }
496 }
497
498 static void open_window ( VikWindow *vw, GSList *files )
499 {
500   if ( !vw  )
501     return;
502   gboolean change_fn = (g_slist_length(files) == 1); /* only change fn if one file */
503   GSList *cur_file = files;
504   while ( cur_file ) {
505     // Only open a new window if a viking file
506     gchar *file_name = cur_file->data;
507     if (vw->filename && check_file_magic_vik ( file_name ) ) {
508       VikWindow *newvw = vik_window_new_window ();
509       if (newvw)
510         vik_window_open_file ( newvw, file_name, TRUE );
511     }
512     else {
513       vik_window_open_file ( vw, file_name, change_fn );
514     }
515     g_free (file_name);
516     cur_file = g_slist_next (cur_file);
517   }
518   g_slist_free (files);
519 }
520 // End signals
521
522 void vik_window_selected_layer(VikWindow *vw, VikLayer *vl)
523 {
524   int i, j, tool_count;
525   VikLayerInterface *layer_interface;
526
527   if (!vw->action_group) return;
528
529   for (i=0; i<VIK_LAYER_NUM_TYPES; i++) {
530     GtkAction *action;
531     layer_interface = vik_layer_get_interface(i);
532     tool_count = layer_interface->tools_count;
533
534     for (j = 0; j < tool_count; j++) {
535       action = gtk_action_group_get_action(vw->action_group,
536                                            layer_interface->tools[j].radioActionEntry.name);
537       g_object_set(action, "sensitive", i == vl->type, NULL);
538       toolbar_action_set_sensitive ( vw->viking_vtb, vik_layer_get_interface(i)->tools[j].radioActionEntry.name, i == vl->type );
539     }
540   }
541 }
542
543 static void window_finalize ( GObject *gob )
544 {
545   VikWindow *vw = VIK_WINDOW(gob);
546   g_return_if_fail ( vw != NULL );
547
548   a_background_remove_window ( vw );
549
550   window_list = g_slist_remove ( window_list, vw );
551
552   gdk_cursor_unref ( vw->busy_cursor );
553   int tt;
554   for (tt = 0; tt < vw->vt->n_tools; tt++ )
555     if ( vw->vt->tools[tt].ti.destroy )
556       vw->vt->tools[tt].ti.destroy ( vw->vt->tools[tt].state );
557   g_free ( vw->vt->tools );
558   g_free ( vw->vt );
559
560   vik_toolbar_finalize ( vw->viking_vtb );
561
562   G_OBJECT_CLASS(parent_class)->finalize(gob);
563 }
564
565
566 static void vik_window_class_init ( VikWindowClass *klass )
567 {
568   /* destructor */
569   GObjectClass *object_class;
570
571   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);
572   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);
573
574   object_class = G_OBJECT_CLASS (klass);
575
576   object_class->finalize = window_finalize;
577
578   parent_class = g_type_class_peek_parent (klass);
579
580 }
581
582 static void zoom_changed (GtkMenuShell *menushell,
583               gpointer      user_data)
584 {
585   VikWindow *vw = VIK_WINDOW (user_data);
586
587   GtkWidget *aw = gtk_menu_get_active ( GTK_MENU (menushell) );
588   gint active = GPOINTER_TO_INT(g_object_get_data ( G_OBJECT (aw), "position" ));
589
590   gdouble zoom_request = pow (2, active-5 );
591
592   // But has it really changed?
593   gdouble current_zoom = vik_viewport_get_zoom ( vw->viking_vvp );
594   if ( current_zoom != 0.0 && zoom_request != current_zoom ) {
595     vik_viewport_set_zoom ( vw->viking_vvp, zoom_request );
596     // Force drawing update
597     draw_update ( vw );
598   }
599 }
600
601 /**
602  * @mpp: The initial zoom level
603  */
604 static GtkWidget *create_zoom_menu_all_levels ( gdouble mpp )
605 {
606   GtkWidget *menu = gtk_menu_new ();
607   char *itemLabels[] = { "0.031", "0.063", "0.125", "0.25", "0.5", "1", "2", "4", "8", "16", "32", "64", "128", "256", "512", "1024", "2048", "4096", "8192", "16384", "32768" };
608
609   int i;
610   for (i = 0 ; i < G_N_ELEMENTS(itemLabels) ; i++)
611     {
612       GtkWidget *item = gtk_menu_item_new_with_label (itemLabels[i]);
613       gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
614       gtk_widget_show (item);
615       g_object_set_data (G_OBJECT (item), "position", GINT_TO_POINTER(i));
616     }
617
618   gint active = 5 + round ( log (mpp) / log (2) );
619   // Ensure value derived from mpp is in bounds of the menu
620   if ( active >= G_N_ELEMENTS(itemLabels) )
621     active = G_N_ELEMENTS(itemLabels) - 1;
622   if ( active < 0 )
623     active = 0;
624   gtk_menu_set_active ( GTK_MENU(menu), active );
625
626   return menu;
627 }
628
629 static GtkWidget *create_zoom_combo_all_levels ()
630 {
631   GtkWidget *combo = vik_combo_box_text_new();
632   vik_combo_box_text_append ( combo, "0.25");
633   vik_combo_box_text_append ( combo, "0.5");
634   vik_combo_box_text_append ( combo, "1");
635   vik_combo_box_text_append ( combo, "2");
636   vik_combo_box_text_append ( combo, "4");
637   vik_combo_box_text_append ( combo, "8");
638   vik_combo_box_text_append ( combo, "16");
639   vik_combo_box_text_append ( combo, "32");
640   vik_combo_box_text_append ( combo, "64");
641   vik_combo_box_text_append ( combo, "128");
642   vik_combo_box_text_append ( combo, "256");
643   vik_combo_box_text_append ( combo, "512");
644   vik_combo_box_text_append ( combo, "1024");
645   vik_combo_box_text_append ( combo, "2048");
646   vik_combo_box_text_append ( combo, "4096");
647   vik_combo_box_text_append ( combo, "8192");
648   vik_combo_box_text_append ( combo, "16384");
649   vik_combo_box_text_append ( combo, "32768");
650   /* Create tooltip */
651   gtk_widget_set_tooltip_text (combo, _("Select zoom level"));
652   return combo;
653 }
654
655 static gint zoom_popup_handler (GtkWidget *widget)
656 {
657   GtkMenu *menu;
658
659   g_return_val_if_fail (widget != NULL, FALSE);
660   g_return_val_if_fail (GTK_IS_MENU (widget), FALSE);
661
662   /* The "widget" is the menu that was supplied when
663    * g_signal_connect_swapped() was called.
664    */
665   menu = GTK_MENU (widget);
666
667   gtk_menu_popup (menu, NULL, NULL, NULL, NULL,
668                   1, gtk_get_current_event_time());
669   return TRUE;
670 }
671
672 enum {
673   TARGET_URIS,
674 };
675
676 static void drag_data_received_cb ( GtkWidget *widget,
677                                     GdkDragContext *context,
678                                     gint x,
679                                     gint y,
680                                     GtkSelectionData *selection_data,
681                                     guint target_type,
682                                     guint time,
683                                     gpointer data )
684 {
685   gboolean success = FALSE;
686
687   if ( (selection_data != NULL) && (gtk_selection_data_get_length(selection_data) > 0) ) {
688     switch (target_type) {
689     case TARGET_URIS: {
690       gchar *str = (gchar*)gtk_selection_data_get_data(selection_data);
691       g_debug ("drag received string:%s \n", str);
692
693       // Convert string into GSList of individual entries for use with our open signal
694       gchar **entries = g_strsplit(str, "\r\n", 0);
695       GSList *filenames = NULL;
696       gint entry_runner = 0;
697       gchar *entry = entries[entry_runner];
698       while (entry) {
699         if ( g_strcmp0 ( entry, "" ) ) {
700           // Drag+Drop gives URIs. And so in particular, %20 in place of spaces in filenames
701           //  thus need to convert the text into a plain string
702           gchar *filename = g_filename_from_uri ( entry, NULL, NULL );
703           if ( filename )
704             filenames = g_slist_append ( filenames, filename );
705         }
706         entry_runner++;
707         entry = entries[entry_runner];
708       }
709
710       if ( filenames )
711         g_signal_emit ( G_OBJECT(VIK_WINDOW_FROM_WIDGET(widget)), window_signals[VW_OPENWINDOW_SIGNAL], 0, filenames );
712         // NB: GSList & contents are freed by main.open_window
713
714       success = TRUE;
715       break;
716     }
717     default: break;
718     }
719   }
720
721   gtk_drag_finish ( context, success, FALSE, time );
722 }
723
724 static void toolbar_tool_cb ( GtkAction *old, GtkAction *current, gpointer gp )
725 {
726   VikWindow *vw = (VikWindow*)gp;
727   GtkAction *action = gtk_action_group_get_action ( vw->action_group, gtk_action_get_name(current) );
728   if ( action )
729     gtk_action_activate ( action );
730 }
731
732 static void toolbar_reload_cb ( GtkActionGroup *grp, gpointer gp )
733 {
734   VikWindow *vw = (VikWindow*)gp;
735   center_changed_cb ( vw );
736 }
737
738 #define VIK_SETTINGS_WIN_MAX "window_maximized"
739 #define VIK_SETTINGS_WIN_FULLSCREEN "window_fullscreen"
740 #define VIK_SETTINGS_WIN_WIDTH "window_width"
741 #define VIK_SETTINGS_WIN_HEIGHT "window_height"
742 #define VIK_SETTINGS_WIN_PANE_POSITION "window_horizontal_pane_position"
743 #define VIK_SETTINGS_WIN_SAVE_IMAGE_WIDTH "window_save_image_width"
744 #define VIK_SETTINGS_WIN_SAVE_IMAGE_HEIGHT "window_save_image_height"
745 #define VIK_SETTINGS_WIN_SAVE_IMAGE_PNG "window_save_image_as_png"
746 #define VIK_SETTINGS_WIN_COPY_CENTRE_FULL_FORMAT "window_copy_centre_full_format"
747
748 #define VIKING_ACCELERATOR_KEY_FILE "keys.rc"
749
750 static void vik_window_init ( VikWindow *vw )
751 {
752   vw->action_group = NULL;
753
754   vw->viking_vvp = vik_viewport_new();
755   vw->viking_vlp = vik_layers_panel_new();
756   vik_layers_panel_set_viewport ( vw->viking_vlp, vw->viking_vvp );
757   vw->viking_vs = vik_statusbar_new();
758
759   vw->vt = toolbox_create(vw);
760   vw->viking_vtb = vik_toolbar_new ();
761   window_create_ui(vw);
762   window_set_filename (vw, NULL);
763
764   vw->busy_cursor = gdk_cursor_new ( GDK_WATCH );
765
766   vw->filename = NULL;
767   vw->loaded_type = LOAD_TYPE_READ_FAILURE; //AKA none
768   vw->modified = FALSE;
769   vw->only_updating_coord_mode_ui = FALSE;
770
771   vw->select_move = FALSE;
772   vw->pan_move = FALSE; 
773   vw->pan_x = vw->pan_y = -1;
774   vw->single_click_pending = FALSE;
775
776   gint draw_image_width;
777   if ( a_settings_get_integer ( VIK_SETTINGS_WIN_SAVE_IMAGE_WIDTH, &draw_image_width ) )
778     vw->draw_image_width = draw_image_width;
779   else
780     vw->draw_image_width = DRAW_IMAGE_DEFAULT_WIDTH;
781   gint draw_image_height;
782   if ( a_settings_get_integer ( VIK_SETTINGS_WIN_SAVE_IMAGE_HEIGHT, &draw_image_height ) )
783     vw->draw_image_height = draw_image_height;
784   else
785     vw->draw_image_height = DRAW_IMAGE_DEFAULT_HEIGHT;
786   gboolean draw_image_save_as_png;
787   if ( a_settings_get_boolean ( VIK_SETTINGS_WIN_SAVE_IMAGE_PNG, &draw_image_save_as_png ) )
788     vw->draw_image_save_as_png = draw_image_save_as_png;
789   else
790     vw->draw_image_save_as_png = DRAW_IMAGE_DEFAULT_SAVE_AS_PNG;
791
792   vw->main_vbox = gtk_vbox_new(FALSE, 1);
793   gtk_container_add (GTK_CONTAINER (vw), vw->main_vbox);
794   vw->menu_hbox = gtk_hbox_new(FALSE, 1);
795   GtkWidget *menu_bar = gtk_ui_manager_get_widget (vw->uim, "/MainMenu");
796   gtk_box_pack_start (GTK_BOX(vw->menu_hbox), menu_bar, FALSE, TRUE, 0);
797   gtk_box_pack_start (GTK_BOX(vw->main_vbox), vw->menu_hbox, FALSE, TRUE, 0);
798
799   toolbar_init(vw->viking_vtb,
800                &vw->gtkwindow,
801                vw->main_vbox,
802                vw->menu_hbox,
803                toolbar_tool_cb,
804                toolbar_reload_cb,
805                (gpointer)vw); // This auto packs toolbar into the vbox
806   // Must be performed post toolbar init
807   gint i,j;
808   for (i=0; i<VIK_LAYER_NUM_TYPES; i++) {
809     for ( j = 0; j < vik_layer_get_interface(i)->tools_count; j++ ) {
810       toolbar_action_set_sensitive ( vw->viking_vtb, vik_layer_get_interface(i)->tools[j].radioActionEntry.name, FALSE );
811     }
812   }
813
814   vik_ext_tool_datasources_add_menu_items ( vw, vw->uim );
815
816   GtkWidget * zoom_levels = gtk_ui_manager_get_widget (vw->uim, "/MainMenu/View/SetZoom");
817   GtkWidget * zoom_levels_menu = create_zoom_menu_all_levels ( vik_viewport_get_zoom(vw->viking_vvp) );
818   gtk_menu_item_set_submenu (GTK_MENU_ITEM (zoom_levels), zoom_levels_menu);
819   g_signal_connect ( G_OBJECT(zoom_levels_menu), "selection-done", G_CALLBACK(zoom_changed), vw);
820   g_signal_connect_swapped ( G_OBJECT(vw->viking_vs), "clicked", G_CALLBACK(zoom_popup_handler), zoom_levels_menu );
821
822   g_signal_connect (G_OBJECT (vw), "delete_event", G_CALLBACK (delete_event), NULL);
823
824   // Own signals
825   g_signal_connect_swapped (G_OBJECT(vw->viking_vvp), "updated_center", G_CALLBACK(center_changed_cb), vw);
826   // Signals from GTK
827   g_signal_connect_swapped (G_OBJECT(vw->viking_vvp), "expose_event", G_CALLBACK(draw_sync), vw);
828   g_signal_connect_swapped (G_OBJECT(vw->viking_vvp), "configure_event", G_CALLBACK(window_configure_event), vw);
829   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 );
830   g_signal_connect_swapped (G_OBJECT(vw->viking_vvp), "scroll_event", G_CALLBACK(draw_scroll), vw);
831   g_signal_connect_swapped (G_OBJECT(vw->viking_vvp), "button_press_event", G_CALLBACK(draw_click), vw);
832   g_signal_connect_swapped (G_OBJECT(vw->viking_vvp), "button_release_event", G_CALLBACK(draw_release), vw);
833   g_signal_connect_swapped (G_OBJECT(vw->viking_vvp), "motion_notify_event", G_CALLBACK(draw_mouse_motion), vw);
834
835   g_signal_connect_swapped (G_OBJECT(vw->viking_vlp), "update", G_CALLBACK(draw_update), vw);
836   g_signal_connect_swapped (G_OBJECT(vw->viking_vlp), "delete_layer", G_CALLBACK(vik_window_clear_highlight), vw);
837
838   // Allow key presses to be processed anywhere
839   g_signal_connect_swapped (G_OBJECT (vw), "key_press_event", G_CALLBACK (key_press_event), vw);
840
841   // Set initial button sensitivity
842   center_changed_cb ( vw );
843
844   vw->hpaned = gtk_hpaned_new ();
845   gtk_paned_pack1 ( GTK_PANED(vw->hpaned), GTK_WIDGET (vw->viking_vlp), FALSE, TRUE );
846   gtk_paned_pack2 ( GTK_PANED(vw->hpaned), GTK_WIDGET (vw->viking_vvp), TRUE, TRUE );
847
848   /* This packs the button into the window (a gtk container). */
849   gtk_box_pack_start (GTK_BOX(vw->main_vbox), vw->hpaned, TRUE, TRUE, 0);
850
851   gtk_box_pack_end (GTK_BOX(vw->main_vbox), GTK_WIDGET(vw->viking_vs), FALSE, TRUE, 0);
852
853   a_background_add_window ( vw );
854
855   window_list = g_slist_prepend ( window_list, vw);
856
857   gint height = VIKING_WINDOW_HEIGHT;
858   gint width = VIKING_WINDOW_WIDTH;
859
860   if ( a_vik_get_restore_window_state() ) {
861     if ( a_settings_get_integer ( VIK_SETTINGS_WIN_HEIGHT, &height ) ) {
862       // Enforce a basic minimum size
863       if ( height < 160 )
864         height = 160;
865     }
866     else
867       // No setting - so use default
868       height = VIKING_WINDOW_HEIGHT;
869
870     if ( a_settings_get_integer ( VIK_SETTINGS_WIN_WIDTH, &width ) ) {
871       // Enforce a basic minimum size
872       if ( width < 320 )
873         width = 320;
874     }
875     else
876       // No setting - so use default
877       width = VIKING_WINDOW_WIDTH;
878
879     gboolean maxed;
880     if ( a_settings_get_boolean ( VIK_SETTINGS_WIN_MAX, &maxed ) )
881       if ( maxed )
882         gtk_window_maximize ( GTK_WINDOW(vw) );
883
884     gboolean full;
885     if ( a_settings_get_boolean ( VIK_SETTINGS_WIN_FULLSCREEN, &full ) ) {
886       if ( full ) {
887         vw->show_full_screen = TRUE;
888         gtk_window_fullscreen ( GTK_WINDOW(vw) );
889         GtkWidget *check_box = gtk_ui_manager_get_widget ( vw->uim, "/ui/MainMenu/View/FullScreen" );
890         if ( check_box )
891           gtk_check_menu_item_set_active ( GTK_CHECK_MENU_ITEM(check_box), TRUE );
892       }
893     }
894
895     gint position = -1; // Let GTK determine default positioning
896     if ( !a_settings_get_integer ( VIK_SETTINGS_WIN_PANE_POSITION, &position ) ) {
897       position = -1;
898     }
899     gtk_paned_set_position ( GTK_PANED(vw->hpaned), position );
900   }
901
902   gtk_window_set_default_size ( GTK_WINDOW(vw), width, height );
903
904   vw->show_side_panel = TRUE;
905   vw->show_statusbar = TRUE;
906   vw->show_toolbar = TRUE;
907   vw->show_main_menu = TRUE;
908
909   // Only accept Drag and Drop of files onto the viewport
910   gtk_drag_dest_set ( GTK_WIDGET(vw->viking_vvp), GTK_DEST_DEFAULT_ALL, NULL, 0, GDK_ACTION_COPY );
911   gtk_drag_dest_add_uri_targets ( GTK_WIDGET(vw->viking_vvp) );
912   g_signal_connect ( GTK_WIDGET(vw->viking_vvp), "drag-data-received", G_CALLBACK(drag_data_received_cb), NULL );
913
914   // Store the thread value so comparisons can be made to determine the gdk update method
915   // Hopefully we are storing the main thread value here :)
916   //  [ATM any window initialization is always be performed by the main thread]
917   vw->thread = g_thread_self();
918
919   // Set the default tool + mode
920   gtk_action_activate ( gtk_action_group_get_action ( vw->action_group, "Pan" ) );
921   gtk_action_activate ( gtk_action_group_get_action ( vw->action_group, "ModeMercator" ) );
922
923   gchar *accel_file_name = g_build_filename ( a_get_viking_dir(), VIKING_ACCELERATOR_KEY_FILE, NULL );
924   gtk_accel_map_load ( accel_file_name );
925   g_free ( accel_file_name );
926 }
927
928 static VikWindow *window_new ()
929 {
930   return VIK_WINDOW ( g_object_new ( VIK_WINDOW_TYPE, NULL ) );
931 }
932
933 /**
934  * Update the displayed map
935  *  Only update the top most visible map layer
936  *  ATM this assumes (as per defaults) the top most map has full alpha setting
937  *   such that other other maps even though they may be active will not be seen
938  *  It's more complicated to work out which maps are actually visible due to alpha settings
939  *   and overkill for this simple refresh method.
940  */
941 static void simple_map_update ( VikWindow *vw, gboolean only_new )
942 {
943   // Find the most relevent single map layer to operate on
944   VikLayer *vl = vik_aggregate_layer_get_top_visible_layer_of_type (vik_layers_panel_get_top_layer(vw->viking_vlp), VIK_LAYER_MAPS);
945   if ( vl )
946         vik_maps_layer_download ( VIK_MAPS_LAYER(vl), vw->viking_vvp, only_new );
947 }
948
949 /**
950  * This is the global key press handler
951  *  Global shortcuts are available at any time and hence are not restricted to when a certain tool is enabled
952  */
953 static gboolean key_press_event( VikWindow *vw, GdkEventKey *event, gpointer data )
954 {
955   // The keys handled here are not in the menuing system for a couple of reasons:
956   //  . Keeps the menu size compact (alebit at expense of discoverably)
957   //  . Allows differing key bindings to perform the same actions
958
959   // First decide if key events are related to the maps layer
960   gboolean map_download = FALSE;
961   gboolean map_download_only_new = TRUE; // Only new or reload
962
963   GdkModifierType modifiers = gtk_accelerator_get_default_mod_mask();
964
965   // Standard 'Refresh' keys: F5 or Ctrl+r
966   // Note 'F5' is actually handled via draw_refresh_cb() later on
967   //  (not 'R' it's 'r' notice the case difference!!)
968   if ( event->keyval == GDK_r && (event->state & modifiers) == GDK_CONTROL_MASK ) {
969         map_download = TRUE;
970         map_download_only_new = TRUE;
971   }
972   // Full cache reload with Ctrl+F5 or Ctrl+Shift+r [This is not in the menu system]
973   // Note the use of uppercase R here since shift key has been pressed
974   else if ( (event->keyval == GDK_F5 && (event->state & modifiers) == GDK_CONTROL_MASK ) ||
975            ( event->keyval == GDK_R && (event->state & modifiers) == (GDK_CONTROL_MASK + GDK_SHIFT_MASK) ) ) {
976         map_download = TRUE;
977         map_download_only_new = FALSE;
978   }
979   // Standard Ctrl+KP+ / Ctrl+KP- to zoom in/out respectively
980   else if ( event->keyval == GDK_KEY_KP_Add && (event->state & modifiers) == GDK_CONTROL_MASK ) {
981     vik_viewport_zoom_in ( vw->viking_vvp );
982     draw_update(vw);
983     return TRUE; // handled keypress
984   }
985   else if ( event->keyval == GDK_KEY_KP_Subtract && (event->state & modifiers) == GDK_CONTROL_MASK ) {
986     vik_viewport_zoom_out ( vw->viking_vvp );
987     draw_update(vw);
988     return TRUE; // handled keypress
989   }
990
991   if ( map_download ) {
992     simple_map_update ( vw, map_download_only_new );
993     return TRUE; // handled keypress
994   }
995
996   VikLayer *vl = vik_layers_panel_get_selected ( vw->viking_vlp );
997   if (vl && vw->vt->active_tool != -1 && vw->vt->tools[vw->vt->active_tool].ti.key_press ) {
998     gint ltype = vw->vt->tools[vw->vt->active_tool].layer_type;
999     if ( vl && ltype == vl->type )
1000       return vw->vt->tools[vw->vt->active_tool].ti.key_press(vl, event, vw->vt->tools[vw->vt->active_tool].state);
1001   }
1002
1003   // Ensure called only on window tools (i.e. not on any of the Layer tools since the layer is NULL)
1004   if ( vw->current_tool < TOOL_LAYER ) {
1005     // No layer - but enable window tool keypress processing - these should be able to handle a NULL layer
1006     if ( vw->vt->tools[vw->vt->active_tool].ti.key_press ) {
1007       return vw->vt->tools[vw->vt->active_tool].ti.key_press ( vl, event, vw->vt->tools[vw->vt->active_tool].state );
1008     }
1009   }
1010
1011   /* Restore Main Menu via Escape key if the user has hidden it */
1012   /* This key is more likely to be used as they may not remember the function key */
1013   if ( event->keyval == GDK_Escape ) {
1014     GtkWidget *check_box = gtk_ui_manager_get_widget ( vw->uim, "/ui/MainMenu/View/SetShow/ViewMainMenu" );
1015     if ( check_box ) {
1016       gboolean state = gtk_check_menu_item_get_active ( GTK_CHECK_MENU_ITEM(check_box) );
1017       if ( !state ) {
1018         gtk_widget_show ( gtk_ui_manager_get_widget ( vw->uim, "/ui/MainMenu" ) );
1019         gtk_check_menu_item_set_active ( GTK_CHECK_MENU_ITEM(check_box), TRUE );
1020         return TRUE; /* handled keypress */
1021       }
1022     }
1023   }
1024
1025   return FALSE; /* don't handle the keypress */
1026 }
1027
1028 static gboolean delete_event( VikWindow *vw )
1029 {
1030 #ifdef VIKING_PROMPT_IF_MODIFIED
1031   if ( vw->modified )
1032 #else
1033   if (0)
1034 #endif
1035   {
1036     GtkDialog *dia;
1037     dia = GTK_DIALOG ( gtk_message_dialog_new ( GTK_WINDOW(vw), GTK_DIALOG_DESTROY_WITH_PARENT, GTK_MESSAGE_QUESTION, GTK_BUTTONS_NONE,
1038       _("Do you want to save the changes you made to the document \"%s\"?\n"
1039         "\n"
1040         "Your changes will be lost if you don't save them."),
1041       window_get_filename ( vw ) ) );
1042     gtk_dialog_add_buttons ( dia, _("Don't Save"), GTK_RESPONSE_NO, GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, GTK_STOCK_SAVE, GTK_RESPONSE_YES, NULL );
1043     switch ( gtk_dialog_run ( dia ) )
1044     {
1045       case GTK_RESPONSE_NO: gtk_widget_destroy ( GTK_WIDGET(dia) ); return FALSE;
1046       case GTK_RESPONSE_CANCEL: gtk_widget_destroy ( GTK_WIDGET(dia) ); return TRUE;
1047       default: gtk_widget_destroy ( GTK_WIDGET(dia) ); return ! save_file(NULL, vw);
1048     }
1049   }
1050
1051   if ( window_count == 1 ) {
1052     // On the final window close - save latest state - if it's wanted...
1053     if ( a_vik_get_restore_window_state() ) {
1054       gint state = gdk_window_get_state ( GTK_WIDGET(vw)->window );
1055       gboolean state_max = state & GDK_WINDOW_STATE_MAXIMIZED;
1056       a_settings_set_boolean ( VIK_SETTINGS_WIN_MAX, state_max );
1057
1058       gboolean state_fullscreen = state & GDK_WINDOW_STATE_FULLSCREEN;
1059       a_settings_set_boolean ( VIK_SETTINGS_WIN_FULLSCREEN, state_fullscreen );
1060
1061       a_settings_set_boolean ( VIK_SETTINGS_WIN_SIDEPANEL, GTK_WIDGET_VISIBLE (GTK_WIDGET(vw->viking_vlp)) );
1062
1063       a_settings_set_boolean ( VIK_SETTINGS_WIN_STATUSBAR, GTK_WIDGET_VISIBLE (GTK_WIDGET(vw->viking_vs)) );
1064
1065       a_settings_set_boolean ( VIK_SETTINGS_WIN_TOOLBAR, GTK_WIDGET_VISIBLE (toolbar_get_widget(vw->viking_vtb)) );
1066
1067       // If supersized - no need to save the enlarged width+height values
1068       if ( ! (state_fullscreen || state_max) ) {
1069         gint width, height;
1070         gtk_window_get_size ( GTK_WINDOW (vw), &width, &height );
1071         a_settings_set_integer ( VIK_SETTINGS_WIN_WIDTH, width );
1072         a_settings_set_integer ( VIK_SETTINGS_WIN_HEIGHT, height );
1073       }
1074
1075       a_settings_set_integer ( VIK_SETTINGS_WIN_PANE_POSITION, gtk_paned_get_position (GTK_PANED(vw->hpaned)) );
1076     }
1077
1078     a_settings_set_integer ( VIK_SETTINGS_WIN_SAVE_IMAGE_WIDTH, vw->draw_image_width );
1079     a_settings_set_integer ( VIK_SETTINGS_WIN_SAVE_IMAGE_HEIGHT, vw->draw_image_height );
1080     a_settings_set_boolean ( VIK_SETTINGS_WIN_SAVE_IMAGE_PNG, vw->draw_image_save_as_png );
1081
1082     gchar *accel_file_name = g_build_filename ( a_get_viking_dir(), VIKING_ACCELERATOR_KEY_FILE, NULL );
1083     gtk_accel_map_save ( accel_file_name );
1084     g_free ( accel_file_name );
1085   }
1086
1087   return FALSE;
1088 }
1089
1090 /* Drawing stuff */
1091 static void newwindow_cb ( GtkAction *a, VikWindow *vw )
1092 {
1093   g_signal_emit ( G_OBJECT(vw), window_signals[VW_NEWWINDOW_SIGNAL], 0 );
1094 }
1095
1096 static void draw_update ( VikWindow *vw )
1097 {
1098   draw_redraw (vw);
1099   draw_sync (vw);
1100 }
1101
1102 static void draw_sync ( VikWindow *vw )
1103 {
1104   vik_viewport_sync(vw->viking_vvp);
1105   draw_status ( vw );
1106 }
1107
1108 /*
1109  * Split the status update, as sometimes only need to update the tool part
1110  *  also on initialization the zoom related stuff is not ready to be used
1111  */
1112 static void draw_status_tool ( VikWindow *vw )
1113 {
1114   if ( vw->current_tool == TOOL_LAYER )
1115     // Use tooltip rather than the internal name as the tooltip is i8n
1116     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 );
1117   else
1118     vik_statusbar_set_message ( vw->viking_vs, VIK_STATUSBAR_TOOL, _(tool_names[vw->current_tool]) );
1119 }
1120
1121 static void draw_status ( VikWindow *vw )
1122 {
1123   static gchar zoom_level[22];
1124   gdouble xmpp = vik_viewport_get_xmpp (vw->viking_vvp);
1125   gdouble ympp = vik_viewport_get_ympp(vw->viking_vvp);
1126   gchar *unit = vik_viewport_get_coord_mode(vw->viking_vvp) == VIK_COORD_UTM ? _("mpp") : _("pixelfact");
1127   if (xmpp != ympp)
1128     g_snprintf ( zoom_level, 22, "%.3f/%.3f %s", xmpp, ympp, unit );
1129   else
1130     if ( (int)xmpp - xmpp < 0.0 )
1131       g_snprintf ( zoom_level, 22, "%.3f %s", xmpp, unit );
1132     else
1133       /* xmpp should be a whole number so don't show useless .000 bit */
1134       g_snprintf ( zoom_level, 22, "%d %s", (int)xmpp, unit );
1135
1136   vik_statusbar_set_message ( vw->viking_vs, VIK_STATUSBAR_ZOOM, zoom_level );
1137
1138   draw_status_tool ( vw );  
1139 }
1140
1141 void vik_window_set_redraw_trigger(VikLayer *vl)
1142 {
1143   VikWindow *vw = VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vl));
1144   if (NULL != vw)
1145     vw->trigger = vl;
1146 }
1147
1148 static void window_configure_event ( VikWindow *vw )
1149 {
1150   static int first = 1;
1151   draw_redraw ( vw );
1152   if (first) {
1153     // This is a hack to set the cursor corresponding to the first tool
1154     // FIXME find the correct way to initialize both tool and its cursor
1155     first = 0;
1156     vw->viewport_cursor = (GdkCursor *)toolbox_get_cursor(vw->vt, "Pan");
1157     /* We set cursor, even if it is NULL: it resets to default */
1158     gdk_window_set_cursor ( gtk_widget_get_window(GTK_WIDGET(vw->viking_vvp)), vw->viewport_cursor );
1159   }
1160 }
1161
1162 static void draw_redraw ( VikWindow *vw )
1163 {
1164   VikCoord old_center = vw->trigger_center;
1165   vw->trigger_center = *(vik_viewport_get_center(vw->viking_vvp));
1166   VikLayer *new_trigger = vw->trigger;
1167   vw->trigger = NULL;
1168   VikLayer *old_trigger = VIK_LAYER(vik_viewport_get_trigger(vw->viking_vvp));
1169
1170   if ( ! new_trigger )
1171     ; /* do nothing -- have to redraw everything. */
1172   else if ( (old_trigger != new_trigger) || !vik_coord_equals(&old_center, &vw->trigger_center) || (new_trigger->type == VIK_LAYER_AGGREGATE) )
1173     vik_viewport_set_trigger ( vw->viking_vvp, new_trigger ); /* todo: set to half_drawn mode if new trigger is above old */
1174   else
1175     vik_viewport_set_half_drawn ( vw->viking_vvp, TRUE );
1176
1177   /* actually draw */
1178   vik_viewport_clear ( vw->viking_vvp);
1179   // Main layer drawing
1180   vik_layers_panel_draw_all ( vw->viking_vlp );
1181   // Draw highlight (possibly again but ensures it is on top - especially for when tracks overlap)
1182   if ( vik_viewport_get_draw_highlight (vw->viking_vvp) ) {
1183     if ( vw->containing_vtl && (vw->selected_tracks || vw->selected_waypoints ) ) {
1184       vik_trw_layer_draw_highlight_items ( vw->containing_vtl, vw->selected_tracks, vw->selected_waypoints, vw->viking_vvp );
1185     }
1186     else if ( vw->containing_vtl && (vw->selected_track || vw->selected_waypoint) ) {
1187       vik_trw_layer_draw_highlight_item ( vw->containing_vtl, vw->selected_track, vw->selected_waypoint, vw->viking_vvp );
1188     }
1189     else if ( vw->selected_vtl ) {
1190       vik_trw_layer_draw_highlight ( vw->selected_vtl, vw->viking_vvp );
1191     }
1192   }
1193   // Other viewport decoration items on top if they are enabled/in use
1194   vik_viewport_draw_scale ( vw->viking_vvp );
1195   vik_viewport_draw_copyright ( vw->viking_vvp );
1196   vik_viewport_draw_centermark ( vw->viking_vvp );
1197   vik_viewport_draw_logo ( vw->viking_vvp );
1198
1199   vik_viewport_set_half_drawn ( vw->viking_vvp, FALSE ); /* just in case. */
1200 }
1201
1202 gboolean draw_buf_done = TRUE;
1203
1204 static gboolean draw_buf(gpointer data)
1205 {
1206   gpointer *pass_along = data;
1207   gdk_draw_drawable (pass_along[0], pass_along[1],
1208                      pass_along[2], 0, 0, 0, 0, -1, -1);
1209   draw_buf_done = TRUE;
1210   return FALSE;
1211 }
1212
1213
1214 /* Mouse event handlers ************************************************************************/
1215
1216 static void vik_window_pan_click (VikWindow *vw, GdkEventButton *event)
1217 {
1218   /* set panning origin */
1219   vw->pan_move = FALSE;
1220   vw->pan_x = (gint) event->x;
1221   vw->pan_y = (gint) event->y;
1222 }
1223
1224 static void draw_click (VikWindow *vw, GdkEventButton *event)
1225 {
1226   gtk_widget_grab_focus ( GTK_WIDGET(vw->viking_vvp) );
1227
1228   /* middle button pressed.  we reserve all middle button and scroll events
1229    * for panning and zooming; tools only get left/right/movement 
1230    */
1231   if ( event->button == 2) {
1232     if ( vw->vt->tools[vw->vt->active_tool].ti.pan_handler )
1233       // Tool still may need to do something (such as disable something)
1234       toolbox_click(vw->vt, event);
1235     vik_window_pan_click ( vw, event );
1236   } 
1237   else {
1238     toolbox_click(vw->vt, event);
1239   }
1240 }
1241
1242 static void vik_window_pan_move (VikWindow *vw, GdkEventMotion *event)
1243 {
1244   if ( vw->pan_x != -1 ) {
1245     vik_viewport_set_center_screen ( vw->viking_vvp, vik_viewport_get_width(vw->viking_vvp)/2 - event->x + vw->pan_x,
1246                                      vik_viewport_get_height(vw->viking_vvp)/2 - event->y + vw->pan_y );
1247     vw->pan_move = TRUE;
1248     vw->pan_x = event->x;
1249     vw->pan_y = event->y;
1250     draw_update ( vw );
1251   }
1252 }
1253
1254 /**
1255  * get_location_strings:
1256  *
1257  * Utility function to get positional strings for the given location
1258  * lat and lon strings will get allocated and so need to be freed after use
1259  */
1260 static void get_location_strings ( VikWindow *vw, struct UTM utm, gchar **lat, gchar **lon )
1261 {
1262   if ( vik_viewport_get_drawmode ( vw->viking_vvp ) == VIK_VIEWPORT_DRAWMODE_UTM ) {
1263     // Reuse lat for the first part (Zone + N or S, and lon for the second part (easting and northing) of a UTM format:
1264     //  ZONE[N|S] EASTING NORTHING
1265     *lat = g_malloc(4*sizeof(gchar));
1266     // NB zone is stored in a char but is an actual number
1267     g_snprintf (*lat, 4, "%d%c", utm.zone, utm.letter);
1268     *lon = g_malloc(16*sizeof(gchar));
1269     g_snprintf (*lon, 16, "%d %d", (gint)utm.easting, (gint)utm.northing);
1270   }
1271   else {
1272     struct LatLon ll;
1273     a_coords_utm_to_latlon ( &utm, &ll );
1274     a_coords_latlon_to_string ( &ll, lat, lon );
1275   }
1276 }
1277
1278 static void draw_mouse_motion (VikWindow *vw, GdkEventMotion *event)
1279 {
1280   static VikCoord coord;
1281   static struct UTM utm;
1282   #define BUFFER_SIZE 50
1283   static char pointer_buf[BUFFER_SIZE];
1284   gchar *lat = NULL, *lon = NULL;
1285   gint16 alt;
1286   gdouble zoom;
1287   VikDemInterpol interpol_method;
1288
1289   /* This is a hack, but work far the best, at least for single pointer systems.
1290    * See http://bugzilla.gnome.org/show_bug.cgi?id=587714 for more. */
1291   gint x, y;
1292   gdk_window_get_pointer (event->window, &x, &y, NULL);
1293   event->x = x;
1294   event->y = y;
1295
1296   toolbox_move(vw->vt, event);
1297
1298   vik_viewport_screen_to_coord ( vw->viking_vvp, event->x, event->y, &coord );
1299   vik_coord_to_utm ( &coord, &utm );
1300
1301   get_location_strings ( vw, utm, &lat, &lon );
1302
1303   /* Change interpolate method according to scale */
1304   zoom = vik_viewport_get_zoom(vw->viking_vvp);
1305   if (zoom > 2.0)
1306     interpol_method = VIK_DEM_INTERPOL_NONE;
1307   else if (zoom >= 1.0)
1308     interpol_method = VIK_DEM_INTERPOL_SIMPLE;
1309   else
1310     interpol_method = VIK_DEM_INTERPOL_BEST;
1311   if ((alt = a_dems_get_elev_by_coord(&coord, interpol_method)) != VIK_DEM_INVALID_ELEVATION) {
1312     if ( a_vik_get_units_height () == VIK_UNITS_HEIGHT_METRES )
1313       g_snprintf ( pointer_buf, BUFFER_SIZE, _("%s %s %dm"), lat, lon, alt );
1314     else
1315       g_snprintf ( pointer_buf, BUFFER_SIZE, _("%s %s %dft"), lat, lon, (int)VIK_METERS_TO_FEET(alt) );
1316   }
1317   else
1318     g_snprintf ( pointer_buf, BUFFER_SIZE, _("%s %s"), lat, lon );
1319   g_free (lat);
1320   lat = NULL;
1321   g_free (lon);
1322   lon = NULL;
1323   vik_statusbar_set_message ( vw->viking_vs, VIK_STATUSBAR_POSITION, pointer_buf );
1324
1325   vik_window_pan_move ( vw, event );
1326
1327   /* This is recommended by the GTK+ documentation, but does not work properly.
1328    * Use deprecated way until GTK+ gets a solution for correct motion hint handling:
1329    * http://bugzilla.gnome.org/show_bug.cgi?id=587714
1330   */
1331   /* gdk_event_request_motions ( event ); */
1332 }
1333
1334 /**
1335  * Action the single click after a small timeout
1336  * If a double click has occurred then this will do nothing
1337  */
1338 static gboolean vik_window_pan_timeout (VikWindow *vw)
1339 {
1340   if ( ! vw->single_click_pending ) {
1341     // Double click happened, so don't do anything
1342     return FALSE;
1343   }
1344
1345   /* set panning origin */
1346   vw->pan_move = FALSE;
1347   vw->single_click_pending = FALSE;
1348   vik_viewport_set_center_screen ( vw->viking_vvp, vw->delayed_pan_x, vw->delayed_pan_y );
1349   draw_update ( vw );
1350
1351   // Really turn off the pan moving!!
1352   vw->pan_x = vw->pan_y = -1;
1353   return FALSE;
1354 }
1355
1356 static void vik_window_pan_release ( VikWindow *vw, GdkEventButton *event )
1357 {
1358   gboolean do_draw = TRUE;
1359
1360   if ( vw->pan_move == FALSE ) {
1361     vw->single_click_pending = !vw->single_click_pending;
1362
1363     if ( vw->single_click_pending ) {
1364       // Store offset to use
1365       vw->delayed_pan_x = vw->pan_x;
1366       vw->delayed_pan_y = vw->pan_y;
1367       // Get double click time
1368       GtkSettings *gs = gtk_widget_get_settings ( GTK_WIDGET(vw) );
1369       GValue dct = { 0 }; // = G_VALUE_INIT; // GLIB 2.30+ only
1370       g_value_init ( &dct, G_TYPE_INT );
1371       g_object_get_property ( G_OBJECT(gs), "gtk-double-click-time", &dct );
1372       // Give chance for a double click to occur
1373       gint timer = g_value_get_int ( &dct ) + 50;
1374       g_timeout_add ( timer, (GSourceFunc)vik_window_pan_timeout, vw );
1375       do_draw = FALSE;
1376     }
1377     else {
1378       vik_viewport_set_center_screen ( vw->viking_vvp, vw->pan_x, vw->pan_y );
1379     }
1380   }
1381   else {
1382      vik_viewport_set_center_screen ( vw->viking_vvp, vik_viewport_get_width(vw->viking_vvp)/2 - event->x + vw->pan_x,
1383                                       vik_viewport_get_height(vw->viking_vvp)/2 - event->y + vw->pan_y );
1384   }
1385
1386   vw->pan_move = FALSE;
1387   vw->pan_x = vw->pan_y = -1;
1388   if ( do_draw )
1389     draw_update ( vw );
1390 }
1391
1392 static void draw_release ( VikWindow *vw, GdkEventButton *event )
1393 {
1394   gtk_widget_grab_focus ( GTK_WIDGET(vw->viking_vvp) );
1395
1396   if ( event->button == 2 ) {  /* move / pan */
1397     if ( vw->vt->tools[vw->vt->active_tool].ti.pan_handler )
1398       // Tool still may need to do something (such as reenable something)
1399       toolbox_release(vw->vt, event);
1400     vik_window_pan_release ( vw, event );
1401   }
1402   else {
1403     toolbox_release(vw->vt, event);
1404   }
1405 }
1406
1407 static void draw_scroll (VikWindow *vw, GdkEventScroll *event)
1408 {
1409   guint modifiers = event->state & (GDK_SHIFT_MASK | GDK_CONTROL_MASK);
1410   if ( modifiers == GDK_CONTROL_MASK ) {
1411     /* control == pan up & down */
1412     if ( event->direction == GDK_SCROLL_UP )
1413       vik_viewport_set_center_screen ( vw->viking_vvp, vik_viewport_get_width(vw->viking_vvp)/2, vik_viewport_get_height(vw->viking_vvp)/3 );
1414     else
1415       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 );
1416   } else if ( modifiers == GDK_SHIFT_MASK ) {
1417     /* shift == pan left & right */
1418     if ( event->direction == GDK_SCROLL_UP )
1419       vik_viewport_set_center_screen ( vw->viking_vvp, vik_viewport_get_width(vw->viking_vvp)/3, vik_viewport_get_height(vw->viking_vvp)/2 );
1420     else
1421       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 );
1422   } else if ( modifiers == (GDK_CONTROL_MASK | GDK_SHIFT_MASK) ) {
1423     // This zoom is on the center position
1424     if ( event->direction == GDK_SCROLL_UP )
1425       vik_viewport_zoom_in (vw->viking_vvp);
1426     else
1427       vik_viewport_zoom_out (vw->viking_vvp);
1428   } else {
1429     /* make sure mouse is still over the same point on the map when we zoom */
1430     VikCoord coord;
1431     gint x, y;
1432     gint center_x = vik_viewport_get_width ( vw->viking_vvp ) / 2;
1433     gint center_y = vik_viewport_get_height ( vw->viking_vvp ) / 2;
1434     vik_viewport_screen_to_coord ( vw->viking_vvp, event->x, event->y, &coord );
1435     if ( event->direction == GDK_SCROLL_UP )
1436       vik_viewport_zoom_in (vw->viking_vvp);
1437     else
1438       vik_viewport_zoom_out(vw->viking_vvp);
1439     vik_viewport_coord_to_screen ( vw->viking_vvp, &coord, &x, &y );
1440     vik_viewport_set_center_screen ( vw->viking_vvp, center_x + (x - event->x),
1441                                      center_y + (y - event->y) );
1442   }
1443
1444   draw_update(vw);
1445 }
1446
1447
1448
1449 /********************************************************************************
1450  ** Ruler tool code
1451  ********************************************************************************/
1452 static void draw_ruler(VikViewport *vvp, GdkDrawable *d, GdkGC *gc, gint x1, gint y1, gint x2, gint y2, gdouble distance)
1453 {
1454   PangoLayout *pl;
1455   gchar str[128];
1456   GdkGC *labgc = vik_viewport_new_gc ( vvp, "#cccccc", 1);
1457   GdkGC *thickgc = gdk_gc_new(d);
1458   
1459   gdouble len = sqrt((x1-x2)*(x1-x2) + (y1-y2)*(y1-y2));
1460   gdouble dx = (x2-x1)/len*10; 
1461   gdouble dy = (y2-y1)/len*10;
1462   gdouble c = cos(DEG2RAD(15.0));
1463   gdouble s = sin(DEG2RAD(15.0));
1464   gdouble angle;
1465   gdouble baseangle = 0;
1466   gint i;
1467
1468   /* draw line with arrow ends */
1469   {
1470     gint tmp_x1=x1, tmp_y1=y1, tmp_x2=x2, tmp_y2=y2;
1471     a_viewport_clip_line(&tmp_x1, &tmp_y1, &tmp_x2, &tmp_y2);
1472     gdk_draw_line(d, gc, tmp_x1, tmp_y1, tmp_x2, tmp_y2);
1473   }
1474
1475   a_viewport_clip_line(&x1, &y1, &x2, &y2);
1476   gdk_draw_line(d, gc, x1, y1, x2, y2);
1477
1478   gdk_draw_line(d, gc, x1 - dy, y1 + dx, x1 + dy, y1 - dx);
1479   gdk_draw_line(d, gc, x2 - dy, y2 + dx, x2 + dy, y2 - dx);
1480   gdk_draw_line(d, gc, x2, y2, x2 - (dx * c + dy * s), y2 - (dy * c - dx * s));
1481   gdk_draw_line(d, gc, x2, y2, x2 - (dx * c - dy * s), y2 - (dy * c + dx * s));
1482   gdk_draw_line(d, gc, x1, y1, x1 + (dx * c + dy * s), y1 + (dy * c - dx * s));
1483   gdk_draw_line(d, gc, x1, y1, x1 + (dx * c - dy * s), y1 + (dy * c + dx * s));
1484
1485   /* draw compass */
1486 #define CR 80
1487 #define CW 4
1488
1489   vik_viewport_compute_bearing ( vvp, x1, y1, x2, y2, &angle, &baseangle );
1490
1491   {
1492     GdkColor color;
1493     gdk_gc_copy(thickgc, gc);
1494     gdk_gc_set_line_attributes(thickgc, CW, GDK_LINE_SOLID, GDK_CAP_BUTT, GDK_JOIN_MITER);
1495     gdk_color_parse("#2255cc", &color);
1496     gdk_gc_set_rgb_fg_color(thickgc, &color);
1497   }
1498   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);
1499
1500
1501   gdk_gc_copy(thickgc, gc);
1502   gdk_gc_set_line_attributes(thickgc, 2, GDK_LINE_SOLID, GDK_CAP_BUTT, GDK_JOIN_MITER);
1503   for (i=0; i<180; i++) {
1504     c = cos(DEG2RAD(i)*2 + baseangle);
1505     s = sin(DEG2RAD(i)*2 + baseangle);
1506
1507     if (i%5) {
1508       gdk_draw_line (d, gc, x1 + CR*c, y1 + CR*s, x1 + (CR+CW)*c, y1 + (CR+CW)*s);
1509     } else {
1510       gdouble ticksize = 2*CW;
1511       gdk_draw_line (d, thickgc, x1 + (CR-CW)*c, y1 + (CR-CW)*s, x1 + (CR+ticksize)*c, y1 + (CR+ticksize)*s);
1512     }
1513   }
1514
1515   gdk_draw_arc (d, gc, FALSE, x1-CR, y1-CR, 2*CR, 2*CR, 0, 64*360);
1516   gdk_draw_arc (d, gc, FALSE, x1-CR-CW, y1-CR-CW, 2*(CR+CW), 2*(CR+CW), 0, 64*360);
1517   gdk_draw_arc (d, gc, FALSE, x1-CR+CW, y1-CR+CW, 2*(CR-CW), 2*(CR-CW), 0, 64*360);
1518   c = (CR+CW*2)*cos(baseangle);
1519   s = (CR+CW*2)*sin(baseangle);
1520   gdk_draw_line (d, gc, x1-c, y1-s, x1+c, y1+s);
1521   gdk_draw_line (d, gc, x1+s, y1-c, x1-s, y1+c);
1522
1523   /* draw labels */
1524 #define LABEL(x, y, w, h) { \
1525     gdk_draw_rectangle(d, labgc, TRUE, (x)-2, (y)-1, (w)+4, (h)+1); \
1526     gdk_draw_rectangle(d, gc, FALSE, (x)-2, (y)-1, (w)+4, (h)+1); \
1527     gdk_draw_layout(d, gc, (x), (y), pl); } 
1528   {
1529     gint wd, hd, xd, yd;
1530     gint wb, hb, xb, yb;
1531
1532     pl = gtk_widget_create_pango_layout (GTK_WIDGET(vvp), NULL);
1533     pango_layout_set_font_description (pl, gtk_widget_get_style(GTK_WIDGET(vvp))->font_desc);
1534     pango_layout_set_text(pl, "N", -1);
1535     gdk_draw_layout(d, gc, x1-5, y1-CR-3*CW-8, pl);
1536
1537     /* draw label with distance */
1538     vik_units_distance_t dist_units = a_vik_get_units_distance ();
1539     switch (dist_units) {
1540     case VIK_UNITS_DISTANCE_KILOMETRES:
1541       if (distance >= 1000 && distance < 100000) {
1542         g_sprintf(str, "%3.2f km", distance/1000.0);
1543       } else if (distance < 1000) {
1544         g_sprintf(str, "%d m", (int)distance);
1545       } else {
1546         g_sprintf(str, "%d km", (int)distance/1000);
1547       }
1548       break;
1549     case VIK_UNITS_DISTANCE_MILES:
1550       if (distance >= VIK_MILES_TO_METERS(1) && distance < VIK_MILES_TO_METERS(100)) {
1551         g_sprintf(str, "%3.2f miles", VIK_METERS_TO_MILES(distance));
1552       } else if (distance < VIK_MILES_TO_METERS(1)) {
1553         g_sprintf(str, "%d yards", (int)(distance*1.0936133));
1554       } else {
1555         g_sprintf(str, "%d miles", (int)VIK_METERS_TO_MILES(distance));
1556       }
1557       break;
1558     case VIK_UNITS_DISTANCE_NAUTICAL_MILES:
1559       if (distance >= VIK_NAUTICAL_MILES_TO_METERS(1) && distance < VIK_NAUTICAL_MILES_TO_METERS(100)) {
1560         g_sprintf(str, "%3.2f NM", VIK_METERS_TO_NAUTICAL_MILES(distance));
1561       } else if (distance < VIK_NAUTICAL_MILES_TO_METERS(1)) {
1562         g_sprintf(str, "%d yards", (int)(distance*1.0936133));
1563       } else {
1564         g_sprintf(str, "%d NM", (int)VIK_METERS_TO_NAUTICAL_MILES(distance));
1565       }
1566       break;
1567     default:
1568       g_critical("Houston, we've had a problem. distance=%d", dist_units);
1569     }
1570
1571     pango_layout_set_text(pl, str, -1);
1572
1573     pango_layout_get_pixel_size ( pl, &wd, &hd );
1574     if (dy>0) {
1575       xd = (x1+x2)/2 + dy;
1576       yd = (y1+y2)/2 - hd/2 - dx;
1577     } else {
1578       xd = (x1+x2)/2 - dy;
1579       yd = (y1+y2)/2 - hd/2 + dx;
1580     }
1581
1582     if ( xd < -5 || yd < -5 || xd > vik_viewport_get_width(vvp)+5 || yd > vik_viewport_get_height(vvp)+5 ) {
1583       xd = x2 + 10;
1584       yd = y2 - 5;
1585     }
1586
1587     LABEL(xd, yd, wd, hd);
1588
1589     /* draw label with bearing */
1590     g_sprintf(str, "%3.1f°", RAD2DEG(angle));
1591     pango_layout_set_text(pl, str, -1);
1592     pango_layout_get_pixel_size ( pl, &wb, &hb );
1593     xb = x1 + CR*cos(angle-M_PI_2);
1594     yb = y1 + CR*sin(angle-M_PI_2);
1595
1596     if ( xb < -5 || yb < -5 || xb > vik_viewport_get_width(vvp)+5 || yb > vik_viewport_get_height(vvp)+5 ) {
1597       xb = x2 + 10;
1598       yb = y2 + 10;
1599     }
1600
1601     {
1602       GdkRectangle r1 = {xd-2, yd-1, wd+4, hd+1}, r2 = {xb-2, yb-1, wb+4, hb+1};
1603       if (gdk_rectangle_intersect(&r1, &r2, &r2)) {
1604         xb = xd + wd + 5;
1605       }
1606     }
1607     LABEL(xb, yb, wb, hb);
1608   }
1609 #undef LABEL
1610
1611   g_object_unref ( G_OBJECT ( pl ) );
1612   g_object_unref ( G_OBJECT ( labgc ) );
1613   g_object_unref ( G_OBJECT ( thickgc ) );
1614 }
1615
1616 typedef struct {
1617   VikWindow *vw;
1618   VikViewport *vvp;
1619   gboolean has_oldcoord;
1620   VikCoord oldcoord;
1621 } ruler_tool_state_t;
1622
1623 static gpointer ruler_create (VikWindow *vw, VikViewport *vvp) 
1624 {
1625   ruler_tool_state_t *s = g_new(ruler_tool_state_t, 1);
1626   s->vw = vw;
1627   s->vvp = vvp;
1628   s->has_oldcoord = FALSE;
1629   return s;
1630 }
1631
1632 static void ruler_destroy (ruler_tool_state_t *s)
1633 {
1634   g_free(s);
1635 }
1636
1637 static VikLayerToolFuncStatus ruler_click (VikLayer *vl, GdkEventButton *event, ruler_tool_state_t *s)
1638 {
1639   struct LatLon ll;
1640   VikCoord coord;
1641   gchar *temp;
1642   if ( event->button == 1 ) {
1643     gchar *lat=NULL, *lon=NULL;
1644     vik_viewport_screen_to_coord ( s->vvp, (gint) event->x, (gint) event->y, &coord );
1645     vik_coord_to_latlon ( &coord, &ll );
1646     a_coords_latlon_to_string ( &ll, &lat, &lon );
1647     if ( s->has_oldcoord ) {
1648       vik_units_distance_t dist_units = a_vik_get_units_distance ();
1649       switch (dist_units) {
1650       case VIK_UNITS_DISTANCE_KILOMETRES:
1651         temp = g_strdup_printf ( "%s %s DIFF %f meters", lat, lon, vik_coord_diff( &coord, &(s->oldcoord) ) );
1652         break;
1653       case VIK_UNITS_DISTANCE_MILES:
1654         temp = g_strdup_printf ( "%s %s DIFF %f miles", lat, lon, VIK_METERS_TO_MILES(vik_coord_diff( &coord, &(s->oldcoord) )) );
1655         break;
1656       case VIK_UNITS_DISTANCE_NAUTICAL_MILES:
1657         temp = g_strdup_printf ( "%s %s DIFF %f NM", lat, lon, VIK_METERS_TO_NAUTICAL_MILES(vik_coord_diff( &coord, &(s->oldcoord) )) );
1658         break;
1659       default:
1660         temp = g_strdup_printf ("Just to keep the compiler happy");
1661         g_critical("Houston, we've had a problem. distance=%d", dist_units);
1662       }
1663
1664       s->has_oldcoord = FALSE;
1665     }
1666     else {
1667       temp = g_strdup_printf ( "%s %s", lat, lon );
1668       s->has_oldcoord = TRUE;
1669     }
1670
1671     vik_statusbar_set_message ( s->vw->viking_vs, VIK_STATUSBAR_INFO, temp );
1672     g_free ( temp );
1673
1674     s->oldcoord = coord;
1675   }
1676   else {
1677     vik_viewport_set_center_screen ( s->vvp, (gint) event->x, (gint) event->y );
1678     draw_update ( s->vw );
1679   }
1680   return VIK_LAYER_TOOL_ACK;
1681 }
1682
1683 static VikLayerToolFuncStatus ruler_move (VikLayer *vl, GdkEventMotion *event, ruler_tool_state_t *s)
1684 {
1685   VikViewport *vvp = s->vvp;
1686   VikWindow *vw = s->vw;
1687
1688   struct LatLon ll;
1689   VikCoord coord;
1690   gchar *temp;
1691
1692   if ( s->has_oldcoord ) {
1693     int oldx, oldy, w1, h1, w2, h2;
1694     static GdkPixmap *buf = NULL;
1695     gchar *lat=NULL, *lon=NULL;
1696     w1 = vik_viewport_get_width(vvp); 
1697     h1 = vik_viewport_get_height(vvp);
1698     if (!buf) {
1699       buf = gdk_pixmap_new ( gtk_widget_get_window(GTK_WIDGET(vvp)), w1, h1, -1 );
1700     }
1701     gdk_drawable_get_size(buf, &w2, &h2);
1702     if (w1 != w2 || h1 != h2) {
1703       g_object_unref ( G_OBJECT ( buf ) );
1704       buf = gdk_pixmap_new ( gtk_widget_get_window(GTK_WIDGET(vvp)), w1, h1, -1 );
1705     }
1706
1707     vik_viewport_screen_to_coord ( vvp, (gint) event->x, (gint) event->y, &coord );
1708     vik_coord_to_latlon ( &coord, &ll );
1709     vik_viewport_coord_to_screen ( vvp, &s->oldcoord, &oldx, &oldy );
1710
1711     gdk_draw_drawable (buf, gtk_widget_get_style(GTK_WIDGET(vvp))->black_gc,
1712                        vik_viewport_get_pixmap(vvp), 0, 0, 0, 0, -1, -1);
1713     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)) );
1714     if (draw_buf_done) {
1715       static gpointer pass_along[3];
1716       pass_along[0] = gtk_widget_get_window(GTK_WIDGET(vvp));
1717       pass_along[1] = gtk_widget_get_style(GTK_WIDGET(vvp))->black_gc;
1718       pass_along[2] = buf;
1719       g_idle_add_full (G_PRIORITY_HIGH_IDLE + 10, draw_buf, pass_along, NULL);
1720       draw_buf_done = FALSE;
1721     }
1722     a_coords_latlon_to_string(&ll, &lat, &lon);
1723     vik_units_distance_t dist_units = a_vik_get_units_distance ();
1724     switch (dist_units) {
1725     case VIK_UNITS_DISTANCE_KILOMETRES:
1726       temp = g_strdup_printf ( "%s %s DIFF %f meters", lat, lon, vik_coord_diff( &coord, &(s->oldcoord) ) );
1727       break;
1728     case VIK_UNITS_DISTANCE_MILES:
1729       temp = g_strdup_printf ( "%s %s DIFF %f miles", lat, lon, VIK_METERS_TO_MILES (vik_coord_diff( &coord, &(s->oldcoord) )) );
1730       break;
1731     case VIK_UNITS_DISTANCE_NAUTICAL_MILES:
1732       temp = g_strdup_printf ( "%s %s DIFF %f NM", lat, lon, VIK_METERS_TO_NAUTICAL_MILES (vik_coord_diff( &coord, &(s->oldcoord) )) );
1733       break;
1734     default:
1735       temp = g_strdup_printf ("Just to keep the compiler happy");
1736       g_critical("Houston, we've had a problem. distance=%d", dist_units);
1737     }
1738     vik_statusbar_set_message ( vw->viking_vs, VIK_STATUSBAR_INFO, temp );
1739     g_free ( temp );
1740   }
1741   return VIK_LAYER_TOOL_ACK;
1742 }
1743
1744 static VikLayerToolFuncStatus ruler_release (VikLayer *vl, GdkEventButton *event, ruler_tool_state_t *s)
1745 {
1746   return VIK_LAYER_TOOL_ACK;
1747 }
1748
1749 static void ruler_deactivate (VikLayer *vl, ruler_tool_state_t *s)
1750 {
1751   draw_update ( s->vw );
1752 }
1753
1754 static gboolean ruler_key_press (VikLayer *vl, GdkEventKey *event, ruler_tool_state_t *s)
1755 {
1756   if (event->keyval == GDK_Escape) {
1757     s->has_oldcoord = FALSE;
1758     ruler_deactivate ( vl, s );
1759     return TRUE;
1760   }
1761   // Regardless of whether we used it, return false so other GTK things may use it
1762   return FALSE;
1763 }
1764
1765 static VikToolInterface ruler_tool =
1766   // NB Ctrl+Shift+R is used for Refresh (deemed more important), so use 'U' instead
1767   { &ruler_18_pixbuf,
1768     { "Ruler", "vik-icon-ruler", N_("_Ruler"), "<control><shift>U", N_("Ruler Tool"), 2 },
1769     (VikToolConstructorFunc) ruler_create,
1770     (VikToolDestructorFunc) ruler_destroy,
1771     (VikToolActivationFunc) NULL,
1772     (VikToolActivationFunc) ruler_deactivate, 
1773     (VikToolMouseFunc) ruler_click, 
1774     (VikToolMouseMoveFunc) ruler_move, 
1775     (VikToolMouseFunc) ruler_release,
1776     (VikToolKeyFunc) ruler_key_press,
1777     FALSE,
1778     GDK_CURSOR_IS_PIXMAP,
1779     &cursor_ruler_pixbuf,
1780     NULL };
1781 /*** end ruler code ********************************************************/
1782
1783
1784
1785 /********************************************************************************
1786  ** Zoom tool code
1787  ********************************************************************************/
1788
1789 typedef struct {
1790   VikWindow *vw;
1791   GdkPixmap *pixmap;
1792   // Track zoom bounds for zoom tool with shift modifier:
1793   gboolean bounds_active;
1794   gint start_x;
1795   gint start_y;
1796 } zoom_tool_state_t;
1797
1798 /*
1799  * In case the screen size has changed
1800  */
1801 static void zoomtool_resize_pixmap (zoom_tool_state_t *zts)
1802 {
1803     int w1, h1, w2, h2;
1804
1805     // Allocate a drawing area the size of the viewport
1806     w1 = vik_viewport_get_width ( zts->vw->viking_vvp );
1807     h1 = vik_viewport_get_height ( zts->vw->viking_vvp );
1808
1809     if ( !zts->pixmap ) {
1810       // Totally new
1811       zts->pixmap = gdk_pixmap_new ( gtk_widget_get_window(GTK_WIDGET(zts->vw->viking_vvp)), w1, h1, -1 );
1812     }
1813
1814     gdk_drawable_get_size ( zts->pixmap, &w2, &h2 );
1815
1816     if ( w1 != w2 || h1 != h2 ) {
1817       // Has changed - delete and recreate with new values
1818       g_object_unref ( G_OBJECT ( zts->pixmap ) );
1819       zts->pixmap = gdk_pixmap_new ( gtk_widget_get_window(GTK_WIDGET(zts->vw->viking_vvp)), w1, h1, -1 );
1820     }
1821 }
1822
1823 static gpointer zoomtool_create (VikWindow *vw, VikViewport *vvp)
1824 {
1825   zoom_tool_state_t *zts = g_new(zoom_tool_state_t, 1);
1826   zts->vw = vw;
1827   zts->pixmap = NULL;
1828   zts->start_x = 0;
1829   zts->start_y = 0;
1830   zts->bounds_active = FALSE;
1831   return zts;
1832 }
1833
1834 static void zoomtool_destroy ( zoom_tool_state_t *zts)
1835 {
1836   if ( zts->pixmap )
1837     g_object_unref ( G_OBJECT ( zts->pixmap ) );
1838   g_free(zts);
1839 }
1840
1841 static VikLayerToolFuncStatus zoomtool_click (VikLayer *vl, GdkEventButton *event, zoom_tool_state_t *zts)
1842 {
1843   zts->vw->modified = TRUE;
1844   guint modifiers = event->state & (GDK_SHIFT_MASK | GDK_CONTROL_MASK);
1845
1846   VikCoord coord;
1847   gint x, y;
1848   gint center_x = vik_viewport_get_width ( zts->vw->viking_vvp ) / 2;
1849   gint center_y = vik_viewport_get_height ( zts->vw->viking_vvp ) / 2;
1850
1851   gboolean skip_update = FALSE;
1852
1853   zts->bounds_active = FALSE;
1854
1855   if ( modifiers == (GDK_CONTROL_MASK | GDK_SHIFT_MASK) ) {
1856     // This zoom is on the center position
1857     vik_viewport_set_center_screen ( zts->vw->viking_vvp, center_x, center_y );
1858     if ( event->button == 1 )
1859       vik_viewport_zoom_in (zts->vw->viking_vvp);
1860     else if ( event->button == 3 )
1861       vik_viewport_zoom_out (zts->vw->viking_vvp);
1862   }
1863   else if ( modifiers == GDK_CONTROL_MASK ) {
1864     // This zoom is to recenter on the mouse position
1865     vik_viewport_set_center_screen ( zts->vw->viking_vvp, (gint) event->x, (gint) event->y );
1866     if ( event->button == 1 )
1867       vik_viewport_zoom_in (zts->vw->viking_vvp);
1868     else if ( event->button == 3 )
1869       vik_viewport_zoom_out (zts->vw->viking_vvp);
1870   }
1871   else if ( modifiers == GDK_SHIFT_MASK ) {
1872     // Get start of new zoom bounds
1873     if ( event->button == 1 ) {
1874       zts->bounds_active = TRUE;
1875       zts->start_x = (gint) event->x;
1876       zts->start_y = (gint) event->y;
1877       skip_update = TRUE;
1878     }
1879   }
1880   else {
1881     /* make sure mouse is still over the same point on the map when we zoom */
1882     vik_viewport_screen_to_coord ( zts->vw->viking_vvp, event->x, event->y, &coord );
1883     if ( event->button == 1 )
1884       vik_viewport_zoom_in (zts->vw->viking_vvp);
1885     else if ( event->button == 3 )
1886       vik_viewport_zoom_out(zts->vw->viking_vvp);
1887     vik_viewport_coord_to_screen ( zts->vw->viking_vvp, &coord, &x, &y );
1888     vik_viewport_set_center_screen ( zts->vw->viking_vvp,
1889                                      center_x + (x - event->x),
1890                                      center_y + (y - event->y) );
1891   }
1892
1893   if ( !skip_update )
1894     draw_update ( zts->vw );
1895
1896   return VIK_LAYER_TOOL_ACK;
1897 }
1898
1899 static VikLayerToolFuncStatus zoomtool_move (VikLayer *vl, GdkEventMotion *event, zoom_tool_state_t *zts)
1900 {
1901   guint modifiers = event->state & (GDK_SHIFT_MASK | GDK_CONTROL_MASK);
1902
1903   if ( zts->bounds_active && modifiers == GDK_SHIFT_MASK ) {
1904     zoomtool_resize_pixmap ( zts );
1905
1906     // Blank out currently drawn area
1907     gdk_draw_drawable ( zts->pixmap,
1908                         gtk_widget_get_style(GTK_WIDGET(zts->vw->viking_vvp))->black_gc,
1909                         vik_viewport_get_pixmap(zts->vw->viking_vvp),
1910                         0, 0, 0, 0, -1, -1);
1911
1912     // Calculate new box starting point & size in pixels
1913     int xx, yy, width, height;
1914     if ( event->y > zts->start_y ) {
1915       yy = zts->start_y;
1916       height = event->y-zts->start_y;
1917     }
1918     else {
1919       yy = event->y;
1920       height = zts->start_y-event->y;
1921     }
1922     if ( event->x > zts->start_x ) {
1923       xx = zts->start_x;
1924       width = event->x-zts->start_x;
1925     }
1926     else {
1927       xx = event->x;
1928       width = zts->start_x-event->x;
1929     }
1930
1931     // Draw the box
1932     gdk_draw_rectangle (zts->pixmap, gtk_widget_get_style(GTK_WIDGET(zts->vw->viking_vvp))->black_gc, FALSE, xx, yy, width, height);
1933
1934     // Only actually draw when there's time to do so
1935     if (draw_buf_done) {
1936       static gpointer pass_along[3];
1937       pass_along[0] = gtk_widget_get_window(GTK_WIDGET(zts->vw->viking_vvp));
1938       pass_along[1] = gtk_widget_get_style(GTK_WIDGET(zts->vw->viking_vvp))->black_gc;
1939       pass_along[2] = zts->pixmap;
1940       g_idle_add_full (G_PRIORITY_HIGH_IDLE + 10, draw_buf, pass_along, NULL);
1941       draw_buf_done = FALSE;
1942     }
1943   }
1944   else
1945     zts->bounds_active = FALSE;
1946
1947   return VIK_LAYER_TOOL_ACK;
1948 }
1949
1950 static VikLayerToolFuncStatus zoomtool_release (VikLayer *vl, GdkEventButton *event, zoom_tool_state_t *zts)
1951 {
1952   guint modifiers = event->state & (GDK_SHIFT_MASK | GDK_CONTROL_MASK);
1953
1954   // Ensure haven't just released on the exact same position
1955   //  i.e. probably haven't moved the mouse at all
1956   if ( zts->bounds_active && modifiers == GDK_SHIFT_MASK &&
1957      ( event->x < zts->start_x-5 || event->x > zts->start_x+5 ) &&
1958      ( event->y < zts->start_y-5 || event->y > zts->start_y+5 ) ) {
1959
1960     VikCoord coord1, coord2;
1961     vik_viewport_screen_to_coord ( zts->vw->viking_vvp, zts->start_x, zts->start_y, &coord1);
1962     vik_viewport_screen_to_coord ( zts->vw->viking_vvp, event->x, event->y, &coord2);
1963
1964     // From the extend of the bounds pick the best zoom level
1965     // c.f. trw_layer_zoom_to_show_latlons()
1966     // Maybe refactor...
1967     struct LatLon ll1, ll2;
1968     vik_coord_to_latlon(&coord1, &ll1);
1969     vik_coord_to_latlon(&coord2, &ll2);
1970     struct LatLon average = { (ll1.lat+ll2.lat)/2,
1971                               (ll1.lon+ll2.lon)/2 };
1972
1973     VikCoord new_center;
1974     vik_coord_load_from_latlon ( &new_center, vik_viewport_get_coord_mode ( zts->vw->viking_vvp ), &average );
1975     vik_viewport_set_center_coord ( zts->vw->viking_vvp, &new_center, FALSE );
1976
1977     /* Convert into definite 'smallest' and 'largest' positions */
1978     struct LatLon minmin;
1979     if ( ll1.lat < ll2.lat )
1980       minmin.lat = ll1.lat;
1981     else
1982       minmin.lat = ll2.lat;
1983
1984     struct LatLon maxmax;
1985     if ( ll1.lon > ll2.lon )
1986       maxmax.lon = ll1.lon;
1987     else
1988       maxmax.lon = ll2.lon;
1989
1990     /* Always recalculate the 'best' zoom level */
1991     gdouble zoom = VIK_VIEWPORT_MIN_ZOOM;
1992     vik_viewport_set_zoom ( zts->vw->viking_vvp, zoom );
1993
1994     gdouble min_lat, max_lat, min_lon, max_lon;
1995     /* Should only be a maximum of about 18 iterations from min to max zoom levels */
1996     while ( zoom <= VIK_VIEWPORT_MAX_ZOOM ) {
1997       vik_viewport_get_min_max_lat_lon ( zts->vw->viking_vvp, &min_lat, &max_lat, &min_lon, &max_lon );
1998       /* NB I think the logic used in this test to determine if the bounds is within view
1999          fails if track goes across 180 degrees longitude.
2000          Hopefully that situation is not too common...
2001          Mind you viking doesn't really do edge locations to well anyway */
2002       if ( min_lat < minmin.lat &&
2003            max_lat > minmin.lat &&
2004            min_lon < maxmax.lon &&
2005            max_lon > maxmax.lon )
2006         /* Found within zoom level */
2007         break;
2008
2009       /* Try next */
2010       zoom = zoom * 2;
2011       vik_viewport_set_zoom ( zts->vw->viking_vvp, zoom );
2012     }
2013   }
2014   else {
2015      // When pressing shift and clicking for zoom, then jump three levels
2016      if ( modifiers == GDK_SHIFT_MASK ) {
2017        // Zoom in/out by three if possible
2018        vik_viewport_set_center_screen ( zts->vw->viking_vvp, event->x, event->y );
2019        if ( event->button == 1 ) {
2020           vik_viewport_zoom_in ( zts->vw->viking_vvp );
2021           vik_viewport_zoom_in ( zts->vw->viking_vvp );
2022           vik_viewport_zoom_in ( zts->vw->viking_vvp );
2023        }
2024        else if ( event->button == 3 ) {
2025           vik_viewport_zoom_out ( zts->vw->viking_vvp );
2026           vik_viewport_zoom_out ( zts->vw->viking_vvp );
2027           vik_viewport_zoom_out ( zts->vw->viking_vvp );
2028        }
2029      }
2030   }
2031
2032   draw_update ( zts->vw );
2033
2034   // Reset
2035   zts->bounds_active = FALSE;
2036
2037   return VIK_LAYER_TOOL_ACK;
2038 }
2039
2040 static VikToolInterface zoom_tool = 
2041   { &zoom_18_pixbuf,
2042     { "Zoom", "vik-icon-zoom", N_("_Zoom"), "<control><shift>Z", N_("Zoom Tool"), 1 },
2043     (VikToolConstructorFunc) zoomtool_create,
2044     (VikToolDestructorFunc) zoomtool_destroy,
2045     (VikToolActivationFunc) NULL,
2046     (VikToolActivationFunc) NULL,
2047     (VikToolMouseFunc) zoomtool_click, 
2048     (VikToolMouseMoveFunc) zoomtool_move,
2049     (VikToolMouseFunc) zoomtool_release,
2050     NULL,
2051     FALSE,
2052     GDK_CURSOR_IS_PIXMAP,
2053     &cursor_zoom_pixbuf,
2054     NULL };
2055 /*** end zoom code ********************************************************/
2056
2057 /********************************************************************************
2058  ** Pan tool code
2059  ********************************************************************************/
2060 static gpointer pantool_create (VikWindow *vw, VikViewport *vvp)
2061 {
2062   return vw;
2063 }
2064
2065 // NB Double clicking means this gets called THREE times!!!
2066 static VikLayerToolFuncStatus pantool_click (VikLayer *vl, GdkEventButton *event, VikWindow *vw)
2067 {
2068   vw->modified = TRUE;
2069
2070   if ( event->type == GDK_2BUTTON_PRESS ) {
2071     // Zoom in / out on double click
2072     // No need to change the center as that has already occurred in the first click of a double click occurrence
2073     if ( event->button == 1 ) {
2074       guint modifier = event->state & GDK_SHIFT_MASK;
2075       if ( modifier )
2076         vik_viewport_zoom_out ( vw->viking_vvp );
2077       else
2078         vik_viewport_zoom_in ( vw->viking_vvp );
2079     }
2080     else if ( event->button == 3 )
2081       vik_viewport_zoom_out ( vw->viking_vvp );
2082
2083     draw_update ( vw );
2084   }
2085   else
2086     // Standard pan click
2087     if ( event->button == 1 )
2088       vik_window_pan_click ( vw, event );
2089
2090   return VIK_LAYER_TOOL_ACK;
2091 }
2092
2093 static VikLayerToolFuncStatus pantool_move (VikLayer *vl, GdkEventMotion *event, VikWindow *vw)
2094 {
2095   vik_window_pan_move ( vw, event );
2096   return VIK_LAYER_TOOL_ACK;
2097 }
2098
2099 static VikLayerToolFuncStatus pantool_release (VikLayer *vl, GdkEventButton *event, VikWindow *vw)
2100 {
2101   if ( event->button == 1 )
2102     vik_window_pan_release ( vw, event );
2103   return VIK_LAYER_TOOL_ACK;
2104 }
2105
2106 static VikToolInterface pan_tool = 
2107   { &mover_22_pixbuf,
2108     { "Pan", "vik-icon-pan", N_("_Pan"), "<control><shift>P", N_("Pan Tool"), 0 },
2109     (VikToolConstructorFunc) pantool_create,
2110     (VikToolDestructorFunc) NULL,
2111     (VikToolActivationFunc) NULL,
2112     (VikToolActivationFunc) NULL,
2113     (VikToolMouseFunc) pantool_click, 
2114     (VikToolMouseMoveFunc) pantool_move,
2115     (VikToolMouseFunc) pantool_release,
2116     NULL,
2117     FALSE,
2118     GDK_FLEUR,
2119     NULL,
2120     NULL };
2121 /*** end pan code ********************************************************/
2122
2123 /********************************************************************************
2124  ** Select tool code
2125  ********************************************************************************/
2126 static gpointer selecttool_create (VikWindow *vw, VikViewport *vvp)
2127 {
2128   tool_ed_t *t = g_new(tool_ed_t, 1);
2129   t->vw = vw;
2130   t->vvp = vvp;
2131   t->vtl = NULL;
2132   t->is_waypoint = FALSE;
2133   return t;
2134 }
2135
2136 static void selecttool_destroy (tool_ed_t *t)
2137 {
2138   g_free(t);
2139 }
2140
2141 typedef struct {
2142   gboolean cont;
2143   VikViewport *vvp;
2144   GdkEventButton *event;
2145   tool_ed_t *tool_edit;
2146 } clicker;
2147
2148 static void click_layer_selected (VikLayer *vl, clicker *ck)
2149 {
2150   /* Do nothing when function call returns true; */
2151   /* i.e. stop on first found item */
2152   if ( ck->cont )
2153     if ( vl->visible )
2154       if ( vik_layer_get_interface(vl->type)->select_click )
2155         ck->cont = !vik_layer_get_interface(vl->type)->select_click ( vl, ck->event, ck->vvp, ck->tool_edit );
2156 }
2157
2158 #ifdef WINDOWS
2159 // Hopefully Alt keys by default
2160 #define VIK_MOVE_MODIFIER GDK_MOD1_MASK
2161 #else
2162 // Alt+mouse on Linux desktops tend to be used by the desktop manager
2163 // Thus use an alternate modifier - you may need to set something into this group
2164 #define VIK_MOVE_MODIFIER GDK_MOD5_MASK
2165 #endif
2166
2167 static VikLayerToolFuncStatus selecttool_click (VikLayer *vl, GdkEventButton *event, tool_ed_t *t)
2168 {
2169   t->vw->select_move = FALSE;
2170   /* Only allow selection on primary button */
2171   if ( event->button == 1 ) {
2172
2173     if ( event->state & VIK_MOVE_MODIFIER )
2174       vik_window_pan_click ( t->vw, event );
2175     else {
2176       /* Enable click to apply callback to potentially all track/waypoint layers */
2177       /* Useful as we can find things that aren't necessarily in the currently selected layer */
2178       GList* gl = vik_layers_panel_get_all_layers_of_type ( t->vw->viking_vlp, VIK_LAYER_TRW, FALSE ); // Don't get invisible layers
2179       clicker ck;
2180       ck.cont = TRUE;
2181       ck.vvp = t->vw->viking_vvp;
2182       ck.event = event;
2183       ck.tool_edit = t;
2184       g_list_foreach ( gl, (GFunc) click_layer_selected, &ck );
2185       g_list_free ( gl );
2186
2187       // If nothing found then deselect & redraw screen if necessary to remove the highlight
2188       if ( ck.cont ) {
2189         GtkTreeIter iter;
2190         VikTreeview *vtv = vik_layers_panel_get_treeview ( t->vw->viking_vlp );
2191
2192         if ( vik_treeview_get_selected_iter ( vtv, &iter ) ) {
2193           // Only clear if selected thing is a TrackWaypoint layer or a sublayer
2194           gint type = vik_treeview_item_get_type ( vtv, &iter );
2195           if ( type == VIK_TREEVIEW_TYPE_SUBLAYER ||
2196             VIK_LAYER(vik_treeview_item_get_pointer ( vtv, &iter ))->type == VIK_LAYER_TRW ) {
2197    
2198             vik_treeview_item_unselect ( vtv, &iter );
2199             if ( vik_window_clear_highlight ( t->vw ) )
2200               draw_update ( t->vw );
2201           }
2202         }
2203       }
2204       else {
2205         // Something found - so enable movement
2206         t->vw->select_move = TRUE;
2207       }
2208     }
2209   }
2210   else if ( ( event->button == 3 ) && ( vl && ( vl->type == VIK_LAYER_TRW ) ) ) {
2211     if ( vl->visible )
2212       /* Act on currently selected item to show menu */
2213       if ( t->vw->selected_track || t->vw->selected_waypoint )
2214         if ( vik_layer_get_interface(vl->type)->show_viewport_menu )
2215           vik_layer_get_interface(vl->type)->show_viewport_menu ( vl, event, t->vw->viking_vvp );
2216   }
2217
2218   return VIK_LAYER_TOOL_ACK;
2219 }
2220
2221 static VikLayerToolFuncStatus selecttool_move (VikLayer *vl, GdkEventMotion *event, tool_ed_t *t)
2222 {
2223   if ( t->vw->select_move ) {
2224     // Don't care about vl here
2225     if ( t->vtl )
2226       if ( vik_layer_get_interface(VIK_LAYER_TRW)->select_move )
2227         vik_layer_get_interface(VIK_LAYER_TRW)->select_move ( vl, event, t->vvp, t );
2228   }
2229   else
2230     // Optional Panning
2231     if ( event->state & VIK_MOVE_MODIFIER )
2232       vik_window_pan_move ( t->vw, event );
2233
2234   return VIK_LAYER_TOOL_ACK;
2235 }
2236
2237 static VikLayerToolFuncStatus selecttool_release (VikLayer *vl, GdkEventButton *event, tool_ed_t *t)
2238 {
2239   if ( t->vw->select_move ) {
2240     // Don't care about vl here
2241     if ( t->vtl )
2242       if ( vik_layer_get_interface(VIK_LAYER_TRW)->select_release )
2243         vik_layer_get_interface(VIK_LAYER_TRW)->select_release ( (VikLayer*)t->vtl, event, t->vvp, t );
2244   }
2245
2246   if ( event->button == 1 && (event->state & VIK_MOVE_MODIFIER) )
2247       vik_window_pan_release ( t->vw, event );
2248
2249   // Force pan off incase it was on
2250   t->vw->pan_move = FALSE;
2251   t->vw->pan_x = t->vw->pan_y = -1;
2252
2253   // End of this select movement
2254   t->vw->select_move = FALSE;
2255
2256   return VIK_LAYER_TOOL_ACK;
2257 }
2258
2259 static VikToolInterface select_tool =
2260   { &select_18_pixbuf,
2261     { "Select", "vik-icon-select", N_("_Select"), "<control><shift>S", N_("Select Tool"), 3 },
2262     (VikToolConstructorFunc) selecttool_create,
2263     (VikToolDestructorFunc) selecttool_destroy,
2264     (VikToolActivationFunc) NULL,
2265     (VikToolActivationFunc) NULL,
2266     (VikToolMouseFunc) selecttool_click,
2267     (VikToolMouseMoveFunc) selecttool_move,
2268     (VikToolMouseFunc) selecttool_release,
2269     (VikToolKeyFunc) NULL,
2270     FALSE,
2271     GDK_LEFT_PTR,
2272     NULL,
2273     NULL };
2274 /*** end select tool code ********************************************************/
2275
2276 static void draw_pan_cb ( GtkAction *a, VikWindow *vw )
2277 {
2278   // Since the treeview cell editting intercepts standard keyboard handlers, it means we can receive events here
2279   // Thus if currently editting, ensure we don't move the viewport when Ctrl+<arrow> is received
2280   VikLayer *sel = vik_layers_panel_get_selected ( vw->viking_vlp );
2281   if ( sel && vik_treeview_get_editing ( sel->vt ) )
2282     return;
2283
2284   if (!strcmp(gtk_action_get_name(a), "PanNorth")) {
2285     vik_viewport_set_center_screen ( vw->viking_vvp, vik_viewport_get_width(vw->viking_vvp)/2, 0 );
2286   } else if (!strcmp(gtk_action_get_name(a), "PanEast")) {
2287     vik_viewport_set_center_screen ( vw->viking_vvp, vik_viewport_get_width(vw->viking_vvp), vik_viewport_get_height(vw->viking_vvp)/2 );
2288   } else if (!strcmp(gtk_action_get_name(a), "PanSouth")) {
2289     vik_viewport_set_center_screen ( vw->viking_vvp, vik_viewport_get_width(vw->viking_vvp)/2, vik_viewport_get_height(vw->viking_vvp) );
2290   } else if (!strcmp(gtk_action_get_name(a), "PanWest")) {
2291     vik_viewport_set_center_screen ( vw->viking_vvp, 0, vik_viewport_get_height(vw->viking_vvp)/2 );
2292   }
2293   draw_update ( vw );
2294 }
2295
2296 static void draw_zoom_cb ( GtkAction *a, VikWindow *vw )
2297 {
2298   guint what = 128;
2299
2300   if (!strcmp(gtk_action_get_name(a), "ZoomIn")) {
2301     what = -3;
2302   } 
2303   else if (!strcmp(gtk_action_get_name(a), "ZoomOut")) {
2304     what = -4;
2305   }
2306   else if (!strcmp(gtk_action_get_name(a), "Zoom0.25")) {
2307     what = -2;
2308   }
2309   else if (!strcmp(gtk_action_get_name(a), "Zoom0.5")) {
2310     what = -1;
2311   }
2312   else {
2313     gchar *s = (gchar *)gtk_action_get_name(a);
2314     what = atoi(s+4);
2315   }
2316
2317   switch (what)
2318   {
2319     case -3: vik_viewport_zoom_in ( vw->viking_vvp ); break;
2320     case -4: vik_viewport_zoom_out ( vw->viking_vvp ); break;
2321     case -1: vik_viewport_set_zoom ( vw->viking_vvp, 0.5 ); break;
2322     case -2: vik_viewport_set_zoom ( vw->viking_vvp, 0.25 ); break;
2323     default: vik_viewport_set_zoom ( vw->viking_vvp, what );
2324   }
2325   draw_update ( vw );
2326 }
2327
2328 static void draw_goto_cb ( GtkAction *a, VikWindow *vw )
2329 {
2330   VikCoord new_center;
2331
2332   if (!strcmp(gtk_action_get_name(a), "GotoLL")) {
2333     struct LatLon ll, llold;
2334     vik_coord_to_latlon ( vik_viewport_get_center ( vw->viking_vvp ), &llold );
2335     if ( a_dialog_goto_latlon ( GTK_WINDOW(vw), &ll, &llold ) )
2336       vik_coord_load_from_latlon ( &new_center, vik_viewport_get_coord_mode(vw->viking_vvp), &ll );
2337     else
2338       return;
2339   }
2340   else if (!strcmp(gtk_action_get_name(a), "GotoUTM")) {
2341     struct UTM utm, utmold;
2342     vik_coord_to_utm ( vik_viewport_get_center ( vw->viking_vvp ), &utmold );
2343     if ( a_dialog_goto_utm ( GTK_WINDOW(vw), &utm, &utmold ) )
2344       vik_coord_load_from_utm ( &new_center, vik_viewport_get_coord_mode(vw->viking_vvp), &utm );
2345     else
2346      return;
2347   }
2348   else {
2349     g_critical("Houston, we've had a problem.");
2350     return;
2351   }
2352
2353   vik_viewport_set_center_coord ( vw->viking_vvp, &new_center, TRUE );
2354   draw_update ( vw );
2355 }
2356
2357 /**
2358  * center_changed_cb:
2359  */
2360 static void center_changed_cb ( VikWindow *vw )
2361 {
2362 // ATM Keep back always available, so when we pan - we can jump to the last requested position
2363 /*
2364   GtkAction* action_back = gtk_action_group_get_action ( vw->action_group, "GoBack" );
2365   if ( action_back ) {
2366     gtk_action_set_sensitive ( action_back, vik_viewport_back_available(vw->viking_vvp) );
2367   }
2368 */
2369   GtkAction* action_forward = gtk_action_group_get_action ( vw->action_group, "GoForward" );
2370   if ( action_forward ) {
2371     gtk_action_set_sensitive ( action_forward, vik_viewport_forward_available(vw->viking_vvp) );
2372   }
2373
2374   toolbar_action_set_sensitive ( vw->viking_vtb, "GoForward", vik_viewport_forward_available(vw->viking_vvp) );
2375 }
2376
2377 /**
2378  * draw_goto_back_and_forth:
2379  */
2380 static void draw_goto_back_and_forth ( GtkAction *a, VikWindow *vw )
2381 {
2382   gboolean changed = FALSE;
2383   if (!strcmp(gtk_action_get_name(a), "GoBack")) {
2384     changed = vik_viewport_go_back ( vw->viking_vvp );
2385   }
2386   else if (!strcmp(gtk_action_get_name(a), "GoForward")) {
2387     changed = vik_viewport_go_forward ( vw->viking_vvp );
2388   }
2389   else {
2390     return;
2391   }
2392
2393   // Recheck buttons sensitivities, as the center changed signal is not sent on back/forward changes
2394   //  (otherwise we would get stuck in an infinite loop!)
2395   center_changed_cb ( vw );
2396
2397   if ( changed )
2398     draw_update ( vw );
2399 }
2400
2401 /**
2402  * Refresh maps displayed
2403  */
2404 static void draw_refresh_cb ( GtkAction *a, VikWindow *vw )
2405 {
2406   // Only get 'new' maps
2407   simple_map_update ( vw, TRUE );
2408 }
2409
2410 static void menu_addlayer_cb ( GtkAction *a, VikWindow *vw )
2411 {
2412  VikLayerTypeEnum type;
2413   for ( type = 0; type < VIK_LAYER_NUM_TYPES; type++ ) {
2414     if (!strcmp(vik_layer_get_interface(type)->fixed_layer_name, gtk_action_get_name(a))) {
2415       if ( vik_layers_panel_new_layer ( vw->viking_vlp, type ) ) {
2416         draw_update ( vw );
2417         vw->modified = TRUE;
2418       }
2419     }
2420   }
2421 }
2422
2423 static void menu_copy_layer_cb ( GtkAction *a, VikWindow *vw )
2424 {
2425   a_clipboard_copy_selected ( vw->viking_vlp );
2426 }
2427
2428 static void menu_cut_layer_cb ( GtkAction *a, VikWindow *vw )
2429 {
2430   vik_layers_panel_cut_selected ( vw->viking_vlp );
2431   vw->modified = TRUE;
2432 }
2433
2434 static void menu_paste_layer_cb ( GtkAction *a, VikWindow *vw )
2435 {
2436   if ( vik_layers_panel_paste_selected ( vw->viking_vlp ) )
2437   {
2438     vw->modified = TRUE;
2439   }
2440 }
2441
2442 static void menu_properties_cb ( GtkAction *a, VikWindow *vw )
2443 {
2444   if ( ! vik_layers_panel_properties ( vw->viking_vlp ) )
2445     a_dialog_info_msg ( GTK_WINDOW(vw), _("You must select a layer to show its properties.") );
2446 }
2447
2448 static void help_help_cb ( GtkAction *a, VikWindow *vw )
2449 {
2450 #ifdef WINDOWS
2451   ShellExecute(NULL, "open", ""PACKAGE".pdf", NULL, NULL, SW_SHOWNORMAL);
2452 #else /* WINDOWS */
2453   gchar *uri;
2454   uri = g_strdup_printf("ghelp:%s", PACKAGE);
2455   GError *error = NULL;
2456   gboolean show = gtk_show_uri (NULL, uri, GDK_CURRENT_TIME, &error);
2457   if ( !show && !error )
2458     // No error to show, so unlikely this will get called
2459     a_dialog_error_msg ( GTK_WINDOW(vw), _("The help system is not available.") );
2460   else if ( error ) {
2461     // Main error path
2462     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 );
2463     g_error_free ( error );
2464   }
2465   g_free(uri);
2466 #endif /* WINDOWS */
2467 }
2468
2469 static void toggle_side_panel ( VikWindow *vw )
2470 {
2471   vw->show_side_panel = !vw->show_side_panel;
2472   if ( vw->show_side_panel )
2473     gtk_widget_show(GTK_WIDGET(vw->viking_vlp));
2474   else
2475     gtk_widget_hide(GTK_WIDGET(vw->viking_vlp));
2476 }
2477
2478 static void toggle_full_screen ( VikWindow *vw )
2479 {
2480   vw->show_full_screen = !vw->show_full_screen;
2481   if ( vw->show_full_screen )
2482     gtk_window_fullscreen ( GTK_WINDOW(vw) );
2483   else
2484     gtk_window_unfullscreen ( GTK_WINDOW(vw) );
2485 }
2486
2487 static void toggle_statusbar ( VikWindow *vw )
2488 {
2489   vw->show_statusbar = !vw->show_statusbar;
2490   if ( vw->show_statusbar )
2491     gtk_widget_show ( GTK_WIDGET(vw->viking_vs) );
2492   else
2493     gtk_widget_hide ( GTK_WIDGET(vw->viking_vs) );
2494 }
2495
2496 static void toggle_toolbar ( VikWindow *vw )
2497 {
2498   vw->show_toolbar = !vw->show_toolbar;
2499   if ( vw->show_toolbar )
2500     gtk_widget_show ( toolbar_get_widget (vw->viking_vtb) );
2501   else
2502     gtk_widget_hide ( toolbar_get_widget (vw->viking_vtb) );
2503 }
2504
2505 static void toggle_main_menu ( VikWindow *vw )
2506 {
2507   vw->show_main_menu = !vw->show_main_menu;
2508   if ( vw->show_main_menu )
2509     gtk_widget_show ( gtk_ui_manager_get_widget ( vw->uim, "/ui/MainMenu" ) );
2510   else
2511     gtk_widget_hide ( gtk_ui_manager_get_widget ( vw->uim, "/ui/MainMenu" ) );
2512 }
2513
2514 // Only for 'view' toggle menu widgets ATM.
2515 GtkWidget *get_show_widget_by_name(VikWindow *vw, const gchar *name)
2516 {
2517   g_return_val_if_fail(name != NULL, NULL);
2518
2519   // ATM only FullScreen is *not* in SetShow path
2520   gchar *path;
2521   if ( g_strcmp0 ("FullScreen", name ) )
2522     path = g_strconcat("/ui/MainMenu/View/SetShow/", name, NULL);
2523   else
2524     path = g_strconcat("/ui/MainMenu/View/", name, NULL);
2525
2526   GtkWidget *widget = gtk_ui_manager_get_widget(vw->uim, path);
2527   g_free(path);
2528
2529   return widget;
2530 }
2531
2532 static void tb_view_side_panel_cb ( GtkAction *a, VikWindow *vw )
2533 {
2534   gboolean next_state = !vw->show_side_panel;
2535   GtkWidget *check_box = get_show_widget_by_name ( vw, gtk_action_get_name(a) );
2536   gboolean menu_state = gtk_check_menu_item_get_active ( GTK_CHECK_MENU_ITEM(check_box) );
2537   if ( next_state != menu_state )
2538     gtk_check_menu_item_set_active ( GTK_CHECK_MENU_ITEM(check_box), next_state );
2539   else
2540     toggle_side_panel ( vw );
2541 }
2542
2543 static void tb_full_screen_cb ( GtkAction *a, VikWindow *vw )
2544 {
2545   gboolean next_state = !vw->show_full_screen;
2546   GtkWidget *check_box = get_show_widget_by_name ( vw, gtk_action_get_name(a) );
2547   gboolean menu_state = gtk_check_menu_item_get_active ( GTK_CHECK_MENU_ITEM(check_box) );
2548   if ( next_state != menu_state )
2549     gtk_check_menu_item_set_active ( GTK_CHECK_MENU_ITEM(check_box), next_state );
2550   else
2551     toggle_full_screen ( vw );
2552 }
2553
2554 static void tb_view_statusbar_cb ( GtkAction *a, VikWindow *vw )
2555 {
2556   gboolean next_state = !vw->show_statusbar;
2557   GtkWidget *check_box = get_show_widget_by_name ( vw, gtk_action_get_name(a) );
2558   gboolean menu_state = gtk_check_menu_item_get_active ( GTK_CHECK_MENU_ITEM(check_box) );
2559   if ( next_state != menu_state )
2560     gtk_check_menu_item_set_active ( GTK_CHECK_MENU_ITEM(check_box), next_state );
2561   else
2562     toggle_statusbar ( vw );
2563 }
2564
2565 static void tb_view_toolbar_cb ( GtkAction *a, VikWindow *vw )
2566 {
2567   gboolean next_state = !vw->show_toolbar;
2568   GtkWidget *check_box = get_show_widget_by_name ( vw, gtk_action_get_name(a) );
2569   gboolean menu_state = gtk_check_menu_item_get_active ( GTK_CHECK_MENU_ITEM(check_box) );
2570   if ( next_state != menu_state )
2571     gtk_check_menu_item_set_active ( GTK_CHECK_MENU_ITEM(check_box), next_state );
2572   else
2573     toggle_toolbar ( vw );
2574 }
2575
2576 static void tb_view_main_menu_cb ( GtkAction *a, VikWindow *vw )
2577 {
2578   gboolean next_state = !vw->show_main_menu;
2579   GtkWidget *check_box = get_show_widget_by_name ( vw, gtk_action_get_name(a) );
2580   gboolean menu_state = gtk_check_menu_item_get_active ( GTK_CHECK_MENU_ITEM(check_box) );
2581   if ( next_state != menu_state )
2582     gtk_check_menu_item_set_active ( GTK_CHECK_MENU_ITEM(check_box), next_state );
2583   else
2584     toggle_main_menu ( vw );
2585 }
2586
2587 static void tb_set_draw_scale ( GtkAction *a, VikWindow *vw )
2588 {
2589   gboolean next_state = !vik_viewport_get_draw_scale ( vw->viking_vvp );
2590   GtkWidget *check_box = get_show_widget_by_name ( vw, gtk_action_get_name(a) );
2591   gboolean menu_state = gtk_check_menu_item_get_active ( GTK_CHECK_MENU_ITEM(check_box) );
2592   if ( next_state != menu_state )
2593     gtk_check_menu_item_set_active ( GTK_CHECK_MENU_ITEM(check_box), next_state );
2594   else {
2595     vik_viewport_set_draw_scale ( vw->viking_vvp, next_state );
2596     draw_update ( vw );
2597   }
2598 }
2599
2600 static void tb_set_draw_centermark ( GtkAction *a, VikWindow *vw )
2601 {
2602   gboolean next_state = !vik_viewport_get_draw_centermark ( vw->viking_vvp );
2603   GtkWidget *check_box = get_show_widget_by_name ( vw, gtk_action_get_name(a) );
2604   gboolean menu_state = gtk_check_menu_item_get_active ( GTK_CHECK_MENU_ITEM(check_box) );
2605   if ( next_state != menu_state )
2606     gtk_check_menu_item_set_active ( GTK_CHECK_MENU_ITEM(check_box), next_state );
2607   else {
2608     vik_viewport_set_draw_centermark ( vw->viking_vvp, next_state );
2609     draw_update ( vw );
2610   }
2611 }
2612
2613 static void tb_set_draw_highlight ( GtkAction *a, VikWindow *vw )
2614 {
2615   gboolean next_state = !vik_viewport_get_draw_highlight ( vw->viking_vvp );
2616   GtkWidget *check_box = get_show_widget_by_name ( vw, gtk_action_get_name(a) );
2617   gboolean menu_state = gtk_check_menu_item_get_active ( GTK_CHECK_MENU_ITEM(check_box) );
2618   if ( next_state != menu_state )
2619     gtk_check_menu_item_set_active ( GTK_CHECK_MENU_ITEM(check_box), next_state );
2620   else {
2621     vik_viewport_set_draw_highlight ( vw->viking_vvp, next_state );
2622     draw_update ( vw );
2623   }
2624 }
2625
2626 static void help_about_cb ( GtkAction *a, VikWindow *vw )
2627 {
2628   a_dialog_about(GTK_WINDOW(vw));
2629 }
2630
2631 static void help_cache_info_cb ( GtkAction *a, VikWindow *vw )
2632 {
2633   // NB: No i18n as this is just for debug
2634   gint byte_size = a_mapcache_get_size();
2635   gchar *msg_sz = NULL;
2636   gchar *msg = NULL;
2637 #if GLIB_CHECK_VERSION(2,30,0)
2638   msg_sz = g_format_size_full ( byte_size, G_FORMAT_SIZE_LONG_FORMAT );
2639 #else
2640   msg_sz = g_format_size_for_display ( byte_size );
2641 #endif
2642   msg = g_strdup_printf ( "Map Cache size is %s with %d items", msg_sz, a_mapcache_get_count());
2643   a_dialog_info_msg_extra ( GTK_WINDOW(vw), "%s", msg );
2644   g_free ( msg_sz );
2645   g_free ( msg );
2646 }
2647
2648 static void back_forward_info_cb ( GtkAction *a, VikWindow *vw )
2649 {
2650   vik_viewport_show_centers ( vw->viking_vvp, GTK_WINDOW(vw) );
2651 }
2652
2653 static void menu_delete_layer_cb ( GtkAction *a, VikWindow *vw )
2654 {
2655   if ( vik_layers_panel_get_selected ( vw->viking_vlp ) )
2656   {
2657     vik_layers_panel_delete_selected ( vw->viking_vlp );
2658     vw->modified = TRUE;
2659   }
2660   else
2661     a_dialog_info_msg ( GTK_WINDOW(vw), _("You must select a layer to delete.") );
2662 }
2663
2664 static void full_screen_cb ( GtkAction *a, VikWindow *vw )
2665 {
2666   gboolean next_state = !vw->show_full_screen;
2667   GtkToggleToolButton *tbutton = (GtkToggleToolButton *)toolbar_get_widget_by_name ( vw->viking_vtb, gtk_action_get_name(a) );
2668   if ( tbutton ) {
2669     gboolean tb_state = gtk_toggle_tool_button_get_active ( tbutton );
2670     if ( next_state != tb_state )
2671       gtk_toggle_tool_button_set_active ( tbutton, next_state );
2672     else
2673       toggle_full_screen ( vw );
2674   }
2675   else
2676     toggle_full_screen ( vw );
2677 }
2678
2679 static void view_side_panel_cb ( GtkAction *a, VikWindow *vw )
2680 {
2681   gboolean next_state = !vw->show_side_panel;
2682   GtkToggleToolButton *tbutton = (GtkToggleToolButton *)toolbar_get_widget_by_name ( vw->viking_vtb, gtk_action_get_name(a) );
2683   if ( tbutton ) {
2684     gboolean tb_state = gtk_toggle_tool_button_get_active ( tbutton );
2685     if ( next_state != tb_state )
2686       gtk_toggle_tool_button_set_active ( tbutton, next_state );
2687     else
2688       toggle_side_panel ( vw );
2689   }
2690   else
2691     toggle_side_panel ( vw );
2692 }
2693
2694 static void view_statusbar_cb ( GtkAction *a, VikWindow *vw )
2695 {
2696   gboolean next_state = !vw->show_statusbar;
2697   GtkToggleToolButton *tbutton = (GtkToggleToolButton *)toolbar_get_widget_by_name ( vw->viking_vtb, gtk_action_get_name(a) );
2698   if ( tbutton ) {
2699     gboolean tb_state = gtk_toggle_tool_button_get_active ( tbutton );
2700     if ( next_state != tb_state )
2701       gtk_toggle_tool_button_set_active ( tbutton, next_state );
2702     else
2703       toggle_statusbar ( vw );
2704   }
2705   else
2706     toggle_statusbar ( vw );
2707 }
2708
2709 static void view_toolbar_cb ( GtkAction *a, VikWindow *vw )
2710 {
2711   gboolean next_state = !vw->show_toolbar;
2712   GtkToggleToolButton *tbutton = (GtkToggleToolButton *)toolbar_get_widget_by_name ( vw->viking_vtb, gtk_action_get_name(a) );
2713   if ( tbutton ) {
2714     gboolean tb_state = gtk_toggle_tool_button_get_active ( tbutton );
2715     if ( next_state != tb_state )
2716       gtk_toggle_tool_button_set_active ( tbutton, next_state );
2717     else
2718       toggle_toolbar ( vw );
2719   }
2720   else
2721     toggle_toolbar ( vw );
2722 }
2723
2724 static void view_main_menu_cb ( GtkAction *a, VikWindow *vw )
2725 {
2726   gboolean next_state = !vw->show_main_menu;
2727   GtkToggleToolButton *tbutton = (GtkToggleToolButton *)toolbar_get_widget_by_name ( vw->viking_vtb, gtk_action_get_name(a) );
2728   if ( tbutton ) {
2729     gboolean tb_state = gtk_toggle_tool_button_get_active ( tbutton );
2730     if ( next_state != tb_state )
2731       gtk_toggle_tool_button_set_active ( tbutton, next_state );
2732     else
2733       toggle_main_menu ( vw );
2734   }
2735   else
2736     toggle_main_menu ( vw );
2737 }
2738
2739 /***************************************
2740  ** tool management routines
2741  **
2742  ***************************************/
2743
2744 static toolbox_tools_t* toolbox_create(VikWindow *vw)
2745 {
2746   toolbox_tools_t *vt = g_new(toolbox_tools_t, 1);
2747   vt->tools = NULL;
2748   vt->n_tools = 0;
2749   vt->active_tool = -1;
2750   vt->vw = vw;
2751   return vt;
2752 }
2753
2754 static void toolbox_add_tool(toolbox_tools_t *vt, VikToolInterface *vti, gint layer_type )
2755 {
2756   vt->tools = g_renew(toolbox_tool_t, vt->tools, vt->n_tools+1);
2757   vt->tools[vt->n_tools].ti = *vti;
2758   vt->tools[vt->n_tools].layer_type = layer_type;
2759   if (vti->create) {
2760     vt->tools[vt->n_tools].state = vti->create(vt->vw, vt->vw->viking_vvp);
2761   } 
2762   else {
2763     vt->tools[vt->n_tools].state = NULL;
2764   }
2765   vt->n_tools++;
2766 }
2767
2768 static int toolbox_get_tool(toolbox_tools_t *vt, const gchar *tool_name)
2769 {
2770   int i;
2771   for (i=0; i<vt->n_tools; i++) {
2772     if (!strcmp(tool_name, vt->tools[i].ti.radioActionEntry.name)) {
2773       break;
2774     }
2775   }
2776   return i;
2777 }
2778
2779 static void toolbox_activate(toolbox_tools_t *vt, const gchar *tool_name)
2780 {
2781   int tool = toolbox_get_tool(vt, tool_name);
2782   toolbox_tool_t *t = &vt->tools[tool];
2783   VikLayer *vl = vik_layers_panel_get_selected ( vt->vw->viking_vlp );
2784
2785   if (tool == vt->n_tools) {
2786     g_critical("trying to activate a non-existent tool...");
2787     return;
2788   }
2789   /* is the tool already active? */
2790   if (vt->active_tool == tool) {
2791     return;
2792   }
2793
2794   if (vt->active_tool != -1) {
2795     if (vt->tools[vt->active_tool].ti.deactivate) {
2796       vt->tools[vt->active_tool].ti.deactivate(NULL, vt->tools[vt->active_tool].state);
2797     }
2798   }
2799   if (t->ti.activate) {
2800     t->ti.activate(vl, t->state);
2801   }
2802   vt->active_tool = tool;
2803 }
2804
2805 static const GdkCursor *toolbox_get_cursor(toolbox_tools_t *vt, const gchar *tool_name)
2806 {
2807   int tool = toolbox_get_tool(vt, tool_name);
2808   toolbox_tool_t *t = &vt->tools[tool];
2809   if (t->ti.cursor == NULL) {
2810     if (t->ti.cursor_type == GDK_CURSOR_IS_PIXMAP && t->ti.cursor_data != NULL) {
2811       GdkPixbuf *cursor_pixbuf = gdk_pixbuf_from_pixdata (t->ti.cursor_data, FALSE, NULL);
2812       /* TODO: settable offeset */
2813       t->ti.cursor = gdk_cursor_new_from_pixbuf ( gdk_display_get_default(), cursor_pixbuf, 3, 3 );
2814       g_object_unref ( G_OBJECT(cursor_pixbuf) );
2815     } else {
2816       t->ti.cursor = gdk_cursor_new ( t->ti.cursor_type );
2817     }
2818   }
2819   return t->ti.cursor;
2820 }
2821
2822 static void toolbox_click (toolbox_tools_t *vt, GdkEventButton *event)
2823 {
2824   VikLayer *vl = vik_layers_panel_get_selected ( vt->vw->viking_vlp );
2825   if (vt->active_tool != -1 && vt->tools[vt->active_tool].ti.click) {
2826     gint ltype = vt->tools[vt->active_tool].layer_type;
2827     if ( ltype == TOOL_LAYER_TYPE_NONE || (vl && ltype == vl->type) )
2828       vt->tools[vt->active_tool].ti.click(vl, event, vt->tools[vt->active_tool].state);
2829   }
2830 }
2831
2832 static void toolbox_move (toolbox_tools_t *vt, GdkEventMotion *event)
2833 {
2834   VikLayer *vl = vik_layers_panel_get_selected ( vt->vw->viking_vlp );
2835   if (vt->active_tool != -1 && vt->tools[vt->active_tool].ti.move) {
2836     gint ltype = vt->tools[vt->active_tool].layer_type;
2837     if ( ltype == TOOL_LAYER_TYPE_NONE || (vl && ltype == vl->type) )
2838       if ( VIK_LAYER_TOOL_ACK_GRAB_FOCUS == vt->tools[vt->active_tool].ti.move(vl, event, vt->tools[vt->active_tool].state) )
2839         gtk_widget_grab_focus ( GTK_WIDGET(vt->vw->viking_vvp) );
2840   }
2841 }
2842
2843 static void toolbox_release (toolbox_tools_t *vt, GdkEventButton *event)
2844 {
2845   VikLayer *vl = vik_layers_panel_get_selected ( vt->vw->viking_vlp );
2846   if (vt->active_tool != -1 && vt->tools[vt->active_tool].ti.release ) {
2847     gint ltype = vt->tools[vt->active_tool].layer_type;
2848     if ( ltype == TOOL_LAYER_TYPE_NONE || (vl && ltype == vl->type) )
2849       vt->tools[vt->active_tool].ti.release(vl, event, vt->tools[vt->active_tool].state);
2850   }
2851 }
2852 /** End tool management ************************************/
2853
2854 void vik_window_enable_layer_tool ( VikWindow *vw, gint layer_id, gint tool_id )
2855 {
2856   gtk_action_activate ( gtk_action_group_get_action ( vw->action_group, vik_layer_get_interface(layer_id)->tools[tool_id].radioActionEntry.name ) );
2857 }
2858
2859 // Be careful with usage - as it may trigger actions being continually alternately by the menu and toolbar items
2860 // DON'T Use this from menu callback with toggle toolbar items!!
2861 static void toolbar_sync ( VikWindow *vw, const gchar *name, gboolean state )
2862 {
2863   GtkToggleToolButton *tbutton = (GtkToggleToolButton *)toolbar_get_widget_by_name ( vw->viking_vtb, name );
2864   if ( tbutton ) {
2865     // Causes toggle signal action to be raised.
2866     gtk_toggle_tool_button_set_active ( tbutton, state );
2867   }
2868 }
2869
2870 /* this function gets called whenever a menu is clicked */
2871 // Note old is not used
2872 static void menu_cb ( GtkAction *old, GtkAction *a, VikWindow *vw )
2873 {
2874   // Ensure Toolbar kept in sync
2875   const gchar *name = gtk_action_get_name(a);
2876   toolbar_sync ( vw, name, TRUE );
2877
2878   /* White Magic, my friends ... White Magic... */
2879   gint tool_id;
2880   toolbox_activate(vw->vt, name);
2881
2882   vw->viewport_cursor = (GdkCursor *)toolbox_get_cursor(vw->vt, name);
2883
2884   if ( gtk_widget_get_window(GTK_WIDGET(vw->viking_vvp)) )
2885     /* We set cursor, even if it is NULL: it resets to default */
2886     gdk_window_set_cursor ( gtk_widget_get_window(GTK_WIDGET(vw->viking_vvp)), vw->viewport_cursor );
2887
2888   if (!g_strcmp0(name, "Pan")) {
2889     vw->current_tool = TOOL_PAN;
2890   } 
2891   else if (!g_strcmp0(name, "Zoom")) {
2892     vw->current_tool = TOOL_ZOOM;
2893   } 
2894   else if (!g_strcmp0(name, "Ruler")) {
2895     vw->current_tool = TOOL_RULER;
2896   }
2897   else if (!g_strcmp0(name, "Select")) {
2898     vw->current_tool = TOOL_SELECT;
2899   }
2900   else {
2901     VikLayerTypeEnum layer_id;
2902     for (layer_id=0; layer_id<VIK_LAYER_NUM_TYPES; layer_id++) {
2903       for ( tool_id = 0; tool_id < vik_layer_get_interface(layer_id)->tools_count; tool_id++ ) {
2904         if (!g_strcmp0(vik_layer_get_interface(layer_id)->tools[tool_id].radioActionEntry.name, name)) {
2905            vw->current_tool = TOOL_LAYER;
2906            vw->tool_layer_id = layer_id;
2907            vw->tool_tool_id = tool_id;
2908         }
2909       }
2910     }
2911   }
2912   draw_status_tool ( vw );
2913 }
2914
2915 static void window_set_filename ( VikWindow *vw, const gchar *filename )
2916 {
2917   gchar *title;
2918   const gchar *file;
2919   if ( vw->filename )
2920     g_free ( vw->filename );
2921   if ( filename == NULL )
2922   {
2923     vw->filename = NULL;
2924   }
2925   else
2926   {
2927     vw->filename = g_strdup(filename);
2928   }
2929
2930   /* Refresh window's title */
2931   file = window_get_filename ( vw );
2932   title = g_strdup_printf( "%s - Viking", file );
2933   gtk_window_set_title ( GTK_WINDOW(vw), title );
2934   g_free ( title );
2935 }
2936
2937 static const gchar *window_get_filename ( VikWindow *vw )
2938 {
2939   return vw->filename ? a_file_basename ( vw->filename ) : _("Untitled");
2940 }
2941
2942 GtkWidget *vik_window_get_drawmode_button ( VikWindow *vw, VikViewportDrawMode mode )
2943 {
2944   GtkWidget *mode_button;
2945   gchar *buttonname;
2946   switch ( mode ) {
2947 #ifdef VIK_CONFIG_EXPEDIA
2948     case VIK_VIEWPORT_DRAWMODE_EXPEDIA: buttonname = "/ui/MainMenu/View/ModeExpedia"; break;
2949 #endif
2950     case VIK_VIEWPORT_DRAWMODE_MERCATOR: buttonname = "/ui/MainMenu/View/ModeMercator"; break;
2951     case VIK_VIEWPORT_DRAWMODE_LATLON: buttonname = "/ui/MainMenu/View/ModeLatLon"; break;
2952     default: buttonname = "/ui/MainMenu/View/ModeUTM";
2953   }
2954   mode_button = gtk_ui_manager_get_widget ( vw->uim, buttonname );
2955   g_assert ( mode_button );
2956   return mode_button;
2957 }
2958
2959 /**
2960  * vik_window_get_pan_move:
2961  * @vw: some VikWindow
2962  *
2963  * Retrieves @vw's pan_move.
2964  *
2965  * Should be removed as soon as possible.
2966  *
2967  * Returns: @vw's pan_move
2968  *
2969  * Since: 0.9.96
2970  **/
2971 gboolean vik_window_get_pan_move ( VikWindow *vw )
2972 {
2973   return vw->pan_move;
2974 }
2975
2976 /**
2977  * Remove the potentially auto added map
2978  */
2979 static void remove_default_map_layer ( VikWindow *vw )
2980 {
2981   if ( a_vik_get_add_default_map_layer () ) {
2982     VikAggregateLayer *top = vik_layers_panel_get_top_layer(vw->viking_vlp);
2983     if ( vik_aggregate_layer_count(top) == 1 ) {
2984       VikLayer *vl = vik_aggregate_layer_get_top_visible_layer_of_type (top, VIK_LAYER_MAPS);
2985       // Could try to compare name vs _("Default Map") but this might have i8n issues if not translated
2986       if ( vl )
2987         vik_aggregate_layer_delete_layer ( top, vl );
2988     }
2989   }
2990 }
2991
2992 static void on_activate_recent_item (GtkRecentChooser *chooser,
2993                                      VikWindow *self)
2994 {
2995   gchar *filename;
2996
2997   filename = gtk_recent_chooser_get_current_uri (chooser);
2998   if (filename != NULL)
2999   {
3000     GFile *file = g_file_new_for_uri ( filename );
3001     gchar *path = g_file_get_path ( file );
3002     g_object_unref ( file );
3003     if ( self->filename )
3004     {
3005       GSList *filenames = NULL;
3006       filenames = g_slist_append ( filenames, path );
3007       g_signal_emit ( G_OBJECT(self), window_signals[VW_OPENWINDOW_SIGNAL], 0, filenames );
3008       // NB: GSList & contents are freed by main.open_window
3009     }
3010     else {
3011       remove_default_map_layer ( self );
3012       vik_window_open_file ( self, path, TRUE );
3013       g_free ( path );
3014     }
3015   }
3016
3017   g_free (filename);
3018 }
3019
3020 static void setup_recent_files (VikWindow *self)
3021 {
3022   GtkRecentManager *manager;
3023   GtkRecentFilter *filter;
3024   GtkWidget *menu, *menu_item;
3025
3026   filter = gtk_recent_filter_new ();
3027   /* gtk_recent_filter_add_application (filter, g_get_application_name()); */
3028   gtk_recent_filter_add_group(filter, "viking");
3029
3030   manager = gtk_recent_manager_get_default ();
3031   menu = gtk_recent_chooser_menu_new_for_manager (manager);
3032   gtk_recent_chooser_set_sort_type (GTK_RECENT_CHOOSER (menu), GTK_RECENT_SORT_MRU);
3033   gtk_recent_chooser_add_filter (GTK_RECENT_CHOOSER (menu), filter);
3034   gtk_recent_chooser_set_limit (GTK_RECENT_CHOOSER (menu), a_vik_get_recent_number_files() );
3035
3036   menu_item = gtk_ui_manager_get_widget (self->uim, "/ui/MainMenu/File/OpenRecentFile");
3037   gtk_menu_item_set_submenu (GTK_MENU_ITEM (menu_item), menu);
3038
3039   g_signal_connect (G_OBJECT (menu), "item-activated",
3040                     G_CALLBACK (on_activate_recent_item), (gpointer) self);
3041 }
3042
3043 /*
3044  *
3045  */
3046 static void update_recently_used_document (VikWindow *vw, const gchar *filename)
3047 {
3048   /* Update Recently Used Document framework */
3049   GtkRecentManager *manager = gtk_recent_manager_get_default();
3050   GtkRecentData *recent_data = g_slice_new (GtkRecentData);
3051   gchar *groups[] = {"viking", NULL};
3052   GFile *file = g_file_new_for_commandline_arg(filename);
3053   gchar *uri = g_file_get_uri(file);
3054   gchar *basename = g_path_get_basename(filename);
3055   g_object_unref(file);
3056   file = NULL;
3057
3058   recent_data->display_name   = basename;
3059   recent_data->description    = NULL;
3060   recent_data->mime_type      = "text/x-gps-data";
3061   recent_data->app_name       = (gchar *) g_get_application_name ();
3062   recent_data->app_exec       = g_strjoin (" ", g_get_prgname (), "%f", NULL);
3063   recent_data->groups         = groups;
3064   recent_data->is_private     = FALSE;
3065   if (!gtk_recent_manager_add_full (manager, uri, recent_data))
3066   {
3067     gchar *msg = g_strdup_printf (_("Unable to add '%s' to the list of recently used documents"), uri);
3068     vik_statusbar_set_message ( vw->viking_vs, VIK_STATUSBAR_INFO, msg );
3069     g_free ( msg );
3070   }
3071
3072   g_free (uri);
3073   g_free (basename);
3074   g_free (recent_data->app_exec);
3075   g_slice_free (GtkRecentData, recent_data);
3076 }
3077
3078 /**
3079  * Call this before doing things that may take a long time and otherwise not show any other feedback
3080  *  such as loading and saving files
3081  */
3082 void vik_window_set_busy_cursor ( VikWindow *vw )
3083 {
3084   gdk_window_set_cursor ( gtk_widget_get_window(GTK_WIDGET(vw)), vw->busy_cursor );
3085   // Viewport has a separate cursor
3086   gdk_window_set_cursor ( gtk_widget_get_window(GTK_WIDGET(vw->viking_vvp)), vw->busy_cursor );
3087   // Ensure cursor updated before doing stuff
3088   while( gtk_events_pending() )
3089     gtk_main_iteration();
3090 }
3091
3092 void vik_window_clear_busy_cursor ( VikWindow *vw )
3093 {
3094   gdk_window_set_cursor ( gtk_widget_get_window(GTK_WIDGET(vw)), NULL );
3095   // Restore viewport cursor
3096   gdk_window_set_cursor ( gtk_widget_get_window(GTK_WIDGET(vw->viking_vvp)), vw->viewport_cursor );
3097 }
3098
3099 void vik_window_open_file ( VikWindow *vw, const gchar *filename, gboolean change_filename )
3100 {
3101   vik_window_set_busy_cursor ( vw );
3102
3103   // Enable the *new* filename to be accessible by the Layers codez
3104   gchar *original_filename = g_strdup ( vw->filename );
3105   g_free ( vw->filename );
3106   vw->filename = g_strdup ( filename );
3107   gboolean success = FALSE;
3108   gboolean restore_original_filename = FALSE;
3109   vw->loaded_type = a_file_load ( vik_layers_panel_get_top_layer(vw->viking_vlp), vw->viking_vvp, vw->containing_vtl, filename );
3110   switch ( vw->loaded_type )
3111   {
3112     case LOAD_TYPE_READ_FAILURE:
3113       a_dialog_error_msg ( GTK_WINDOW(vw), _("The file you requested could not be opened.") );
3114       break;
3115     case LOAD_TYPE_GPSBABEL_FAILURE:
3116       a_dialog_error_msg ( GTK_WINDOW(vw), _("GPSBabel is required to load files of this type or GPSBabel encountered problems.") );
3117       break;
3118     case LOAD_TYPE_GPX_FAILURE:
3119       a_dialog_error_msg_extra ( GTK_WINDOW(vw), _("Unable to load malformed GPX file %s"), filename );
3120       break;
3121     case LOAD_TYPE_UNSUPPORTED_FAILURE:
3122       a_dialog_error_msg_extra ( GTK_WINDOW(vw), _("Unsupported file type for %s"), filename );
3123       break;
3124     case LOAD_TYPE_VIK_FAILURE_NON_FATAL:
3125     {
3126       // Since we can process .vik files with issues just show a warning in the status bar
3127       // Not that a user can do much about it... or tells them what this issue is yet...
3128       gchar *msg = g_strdup_printf (_("WARNING: issues encountered loading %s"), a_file_basename (filename) );
3129       vik_statusbar_set_message ( vw->viking_vs, VIK_STATUSBAR_INFO, msg );
3130       g_free ( msg );
3131     }
3132       // No break, carry on to show any data
3133     case LOAD_TYPE_VIK_SUCCESS:
3134     {
3135       restore_original_filename = TRUE; // NB Will actually get inverted by the 'success' component below
3136       GtkWidget *mode_button;
3137       /* Update UI */
3138       if ( change_filename )
3139         window_set_filename ( vw, filename );
3140       mode_button = vik_window_get_drawmode_button ( vw, vik_viewport_get_drawmode ( vw->viking_vvp ) );
3141       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. */
3142       gtk_check_menu_item_set_active ( GTK_CHECK_MENU_ITEM(mode_button), TRUE );
3143       vw->only_updating_coord_mode_ui = FALSE;
3144
3145       vik_layers_panel_change_coord_mode ( vw->viking_vlp, vik_viewport_get_coord_mode ( vw->viking_vvp ) );
3146
3147       // Slightly long winded methods to align loaded viewport settings with the UI
3148       //  Since the rewrite for toolbar + menu actions
3149       //  there no longer exists a simple way to directly change the UI to a value for toggle settings
3150       //  it only supports toggling the existing setting (otherwise get infinite loops in trying to align tb+menu elements)
3151       // Thus get state, compare them, if different then invert viewport setting and (re)sync the setting (via toggling)
3152       gboolean vp_state_scale = vik_viewport_get_draw_scale ( vw->viking_vvp );
3153       gboolean ui_state_scale = gtk_check_menu_item_get_active ( GTK_CHECK_MENU_ITEM(get_show_widget_by_name(vw, "ShowScale")) );
3154       if ( vp_state_scale != ui_state_scale ) {
3155         vik_viewport_set_draw_scale ( vw->viking_vvp, !vp_state_scale );
3156         toggle_draw_scale ( NULL, vw );
3157       }
3158       gboolean vp_state_centermark = vik_viewport_get_draw_centermark ( vw->viking_vvp );
3159       gboolean ui_state_centermark = gtk_check_menu_item_get_active ( GTK_CHECK_MENU_ITEM(get_show_widget_by_name(vw, "ShowCenterMark")) );
3160       if ( vp_state_centermark != ui_state_centermark ) {
3161         vik_viewport_set_draw_centermark ( vw->viking_vvp, !vp_state_centermark );
3162         toggle_draw_centermark ( NULL, vw );
3163       }
3164       gboolean vp_state_highlight = vik_viewport_get_draw_highlight ( vw->viking_vvp );
3165       gboolean ui_state_highlight = gtk_check_menu_item_get_active ( GTK_CHECK_MENU_ITEM(get_show_widget_by_name(vw, "ShowHighlight")) );
3166       if ( vp_state_highlight != ui_state_highlight ) {
3167         vik_viewport_set_draw_highlight ( vw->viking_vvp, !vp_state_highlight );
3168         toggle_draw_highlight ( NULL, vw );
3169       }
3170     }
3171       // NB No break, carry on to redraw
3172     //case LOAD_TYPE_OTHER_SUCCESS:
3173     default:
3174       success = TRUE;
3175       // When LOAD_TYPE_OTHER_SUCCESS *only*, this will maintain the existing Viking project
3176       restore_original_filename = ! restore_original_filename;
3177       update_recently_used_document (vw, filename);
3178       draw_update ( vw );
3179       break;
3180   }
3181
3182   if ( ! success || restore_original_filename )
3183     // Load didn't work or want to keep as the existing Viking project, keep using the original name
3184     window_set_filename ( vw, original_filename );
3185   g_free ( original_filename );
3186
3187   vik_window_clear_busy_cursor ( vw );
3188 }
3189
3190 static void load_file ( GtkAction *a, VikWindow *vw )
3191 {
3192   GSList *files = NULL;
3193   GSList *cur_file = NULL;
3194   gboolean newwindow;
3195   if (!strcmp(gtk_action_get_name(a), "Open")) {
3196     newwindow = TRUE;
3197   } 
3198   else if (!strcmp(gtk_action_get_name(a), "Append")) {
3199     newwindow = FALSE;
3200   } 
3201   else {
3202     g_critical("Houston, we've had a problem.");
3203     return;
3204   }
3205
3206   GtkWidget *dialog = gtk_file_chooser_dialog_new (_("Please select a GPS data file to open. "),
3207                                                    GTK_WINDOW(vw),
3208                                                    GTK_FILE_CHOOSER_ACTION_OPEN,
3209                                                    GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
3210                                                    GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT,
3211                                                    NULL);
3212   if ( last_folder_files_uri )
3213     gtk_file_chooser_set_current_folder_uri ( GTK_FILE_CHOOSER(dialog), last_folder_files_uri );
3214
3215   GtkFileFilter *filter;
3216   // NB file filters are listed this way for alphabetical ordering
3217 #ifdef VIK_CONFIG_GEOCACHES
3218   filter = gtk_file_filter_new ();
3219   gtk_file_filter_set_name( filter, _("Geocaching") );
3220   gtk_file_filter_add_pattern ( filter, "*.loc" ); // No MIME type available
3221   gtk_file_chooser_add_filter (GTK_FILE_CHOOSER(dialog), filter);
3222 #endif
3223
3224   filter = gtk_file_filter_new ();
3225   gtk_file_filter_set_name( filter, _("Google Earth") );
3226   gtk_file_filter_add_mime_type ( filter, "application/vnd.google-earth.kml+xml");
3227   gtk_file_chooser_add_filter (GTK_FILE_CHOOSER(dialog), filter);
3228
3229   filter = gtk_file_filter_new ();
3230   gtk_file_filter_set_name( filter, _("GPX") );
3231   gtk_file_filter_add_pattern ( filter, "*.gpx" ); // No MIME type available
3232   gtk_file_chooser_add_filter (GTK_FILE_CHOOSER(dialog), filter);
3233
3234   filter = gtk_file_filter_new ();
3235   gtk_file_filter_set_name ( filter, _("JPG") );
3236   gtk_file_filter_add_mime_type ( filter, "image/jpeg");
3237   gtk_file_chooser_add_filter (GTK_FILE_CHOOSER(dialog), filter);
3238
3239   filter = gtk_file_filter_new ();
3240   gtk_file_filter_set_name( filter, _("Viking") );
3241   gtk_file_filter_add_pattern ( filter, "*.vik" );
3242   gtk_file_filter_add_pattern ( filter, "*.viking" );
3243   gtk_file_chooser_add_filter (GTK_FILE_CHOOSER(dialog), filter);
3244
3245   // NB could have filters for gpspoint (*.gps,*.gpsoint?) + gpsmapper (*.gsm,*.gpsmapper?)
3246   // However assume this are barely used and thus not worthy of inclusion
3247   //   as they'll just make the options too many and have no clear file pattern
3248   //   one can always use the all option
3249   filter = gtk_file_filter_new ();
3250   gtk_file_filter_set_name( filter, _("All") );
3251   gtk_file_filter_add_pattern ( filter, "*" );
3252   gtk_file_chooser_add_filter (GTK_FILE_CHOOSER(dialog), filter);
3253   // Default to any file - same as before open filters were added
3254   gtk_file_chooser_set_filter (GTK_FILE_CHOOSER(dialog), filter);
3255
3256   gtk_file_chooser_set_select_multiple ( GTK_FILE_CHOOSER(dialog), TRUE );
3257   gtk_window_set_transient_for ( GTK_WINDOW(dialog), GTK_WINDOW(vw) );
3258   gtk_window_set_destroy_with_parent ( GTK_WINDOW(dialog), TRUE );
3259
3260   if ( gtk_dialog_run ( GTK_DIALOG(dialog) ) == GTK_RESPONSE_ACCEPT )
3261   {
3262     g_free ( last_folder_files_uri );
3263     last_folder_files_uri = gtk_file_chooser_get_current_folder_uri ( GTK_FILE_CHOOSER(dialog) );
3264
3265 #ifdef VIKING_PROMPT_IF_MODIFIED
3266     if ( (vw->modified || vw->filename) && newwindow )
3267 #else
3268     if ( vw->filename && newwindow )
3269 #endif
3270       g_signal_emit ( G_OBJECT(vw), window_signals[VW_OPENWINDOW_SIGNAL], 0, gtk_file_chooser_get_filenames (GTK_FILE_CHOOSER(dialog) ) );
3271     else {
3272
3273       files = gtk_file_chooser_get_filenames (GTK_FILE_CHOOSER(dialog) );
3274       gboolean change_fn = newwindow && (g_slist_length(files)==1); /* only change fn if one file */
3275       gboolean first_vik_file = TRUE;
3276       cur_file = files;
3277       while ( cur_file ) {
3278
3279         gchar *file_name = cur_file->data;
3280         if ( newwindow && check_file_magic_vik ( file_name ) ) {
3281           // Load first of many .vik files in current window
3282           if ( first_vik_file ) {
3283             remove_default_map_layer ( vw );
3284             vik_window_open_file ( vw, file_name, TRUE );
3285             first_vik_file = FALSE;
3286           }
3287           else {
3288             // Load each subsequent .vik file in a separate window
3289             VikWindow *newvw = vik_window_new_window ();
3290             if (newvw)
3291               vik_window_open_file ( newvw, file_name, TRUE );
3292           }
3293         }
3294         else
3295           // Other file types
3296           vik_window_open_file ( vw, file_name, change_fn );
3297
3298         g_free (file_name);
3299         cur_file = g_slist_next (cur_file);
3300       }
3301       g_slist_free (files);
3302     }
3303   }
3304   gtk_widget_destroy ( dialog );
3305 }
3306
3307 static gboolean save_file_as ( GtkAction *a, VikWindow *vw )
3308 {
3309   gboolean rv = FALSE;
3310   const gchar *fn;
3311
3312   GtkWidget *dialog = gtk_file_chooser_dialog_new (_("Save as Viking File."),
3313                                                    GTK_WINDOW(vw),
3314                                                    GTK_FILE_CHOOSER_ACTION_SAVE,
3315                                                    GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
3316                                                    GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT,
3317                                                    NULL);
3318   if ( last_folder_files_uri )
3319     gtk_file_chooser_set_current_folder_uri ( GTK_FILE_CHOOSER(dialog), last_folder_files_uri );
3320
3321   GtkFileFilter *filter;
3322   filter = gtk_file_filter_new ();
3323   gtk_file_filter_set_name( filter, _("All") );
3324   gtk_file_filter_add_pattern ( filter, "*" );
3325   gtk_file_chooser_add_filter (GTK_FILE_CHOOSER(dialog), filter);
3326
3327   filter = gtk_file_filter_new ();
3328   gtk_file_filter_set_name( filter, _("Viking") );
3329   gtk_file_filter_add_pattern ( filter, "*.vik" );
3330   gtk_file_filter_add_pattern ( filter, "*.viking" );
3331   gtk_file_chooser_add_filter (GTK_FILE_CHOOSER(dialog), filter);
3332   // Default to a Viking file
3333   gtk_file_chooser_set_filter (GTK_FILE_CHOOSER(dialog), filter);
3334
3335   gtk_window_set_transient_for ( GTK_WINDOW(dialog), GTK_WINDOW(vw) );
3336   gtk_window_set_destroy_with_parent ( GTK_WINDOW(dialog), TRUE );
3337
3338   // Auto append / replace extension with '.vik' to the suggested file name as it's going to be a Viking File
3339   gchar* auto_save_name = g_strdup ( window_get_filename ( vw ) );
3340   if ( ! a_file_check_ext ( auto_save_name, ".vik" ) )
3341     auto_save_name = g_strconcat ( auto_save_name, ".vik", NULL );
3342
3343   gtk_file_chooser_set_current_name (GTK_FILE_CHOOSER(dialog), auto_save_name);
3344
3345   while ( gtk_dialog_run ( GTK_DIALOG(dialog) ) == GTK_RESPONSE_ACCEPT )
3346   {
3347     fn = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER(dialog) );
3348     if ( g_file_test ( fn, G_FILE_TEST_EXISTS ) == FALSE || a_dialog_yes_or_no ( GTK_WINDOW(dialog), _("The file \"%s\" exists, do you wish to overwrite it?"), a_file_basename ( fn ) ) )
3349     {
3350       window_set_filename ( vw, fn );
3351       rv = window_save ( vw );
3352       if ( rv ) {
3353         vw->modified = FALSE;
3354         g_free ( last_folder_files_uri );
3355         last_folder_files_uri = gtk_file_chooser_get_current_folder_uri ( GTK_FILE_CHOOSER(dialog) );
3356       }
3357       break;
3358     }
3359   }
3360   g_free ( auto_save_name );
3361   gtk_widget_destroy ( dialog );
3362   return rv;
3363 }
3364
3365 static gboolean window_save ( VikWindow *vw )
3366 {
3367   vik_window_set_busy_cursor ( vw );
3368   gboolean success = TRUE;
3369
3370   if ( a_file_save ( vik_layers_panel_get_top_layer ( vw->viking_vlp ), vw->viking_vvp, vw->filename ) )
3371   {
3372     update_recently_used_document ( vw, vw->filename );
3373   }
3374   else
3375   {
3376     a_dialog_error_msg ( GTK_WINDOW(vw), _("The filename you requested could not be opened for writing.") );
3377     success = FALSE;
3378   }
3379   vik_window_clear_busy_cursor ( vw );
3380   return success;
3381 }
3382
3383 static gboolean save_file ( GtkAction *a, VikWindow *vw )
3384 {
3385   if ( ! vw->filename )
3386     return save_file_as ( NULL, vw );
3387   else
3388   {
3389     vw->modified = FALSE;
3390     return window_save ( vw );
3391   }
3392 }
3393
3394 /**
3395  * export_to:
3396  *
3397  * Export all TRW Layers in the list to individual files in the specified directory
3398  *
3399  * Returns: %TRUE on success
3400  */
3401 static gboolean export_to ( VikWindow *vw, GList *gl, VikFileType_t vft, const gchar *dir, const gchar *extension )
3402 {
3403   gboolean success = TRUE;
3404
3405   gint export_count = 0;
3406
3407   vik_window_set_busy_cursor ( vw );
3408
3409   while ( gl ) {
3410
3411     gchar *fn = g_strconcat ( dir, G_DIR_SEPARATOR_S, VIK_LAYER(gl->data)->name, extension, NULL );
3412
3413     // Some protection in attempting to write too many same named files
3414     // As this will get horribly slow...
3415     gboolean safe = FALSE;
3416     gint ii = 2;
3417     while ( ii < 5000 ) {
3418       if ( g_file_test ( fn, G_FILE_TEST_EXISTS ) ) {
3419         // Try rename
3420         g_free ( fn );
3421         fn = g_strdup_printf ( "%s%s%s#%03d%s", dir, G_DIR_SEPARATOR_S, VIK_LAYER(gl->data)->name, ii, extension );
3422           }
3423           else {
3424                   safe = TRUE;
3425                   break;
3426           }
3427           ii++;
3428     }
3429     if ( ii == 5000 )
3430       success = FALSE;
3431
3432     // NB: We allow exporting empty layers
3433     if ( safe ) {
3434       gboolean this_success = a_file_export ( VIK_TRW_LAYER(gl->data), fn, vft, NULL, TRUE );
3435
3436       // Show some progress
3437       if ( this_success ) {
3438         export_count++;
3439         gchar *message = g_strdup_printf ( _("Exporting to file: %s"), fn );
3440         vik_statusbar_set_message ( vw->viking_vs, VIK_STATUSBAR_INFO, message );
3441         while ( gtk_events_pending() )
3442           gtk_main_iteration ();
3443         g_free ( message );
3444       }
3445       
3446       success = success && this_success;
3447     }
3448
3449     g_free ( fn );
3450     gl = g_list_next ( gl );
3451   }
3452
3453   vik_window_clear_busy_cursor ( vw );
3454
3455   // Confirm what happened.
3456   gchar *message = g_strdup_printf ( _("Exported files: %d"), export_count );
3457   vik_statusbar_set_message ( vw->viking_vs, VIK_STATUSBAR_INFO, message );
3458   g_free ( message );
3459
3460   return success;
3461 }
3462
3463 static void export_to_common ( VikWindow *vw, VikFileType_t vft, const gchar *extension )
3464 {
3465   GList *gl = vik_layers_panel_get_all_layers_of_type ( vw->viking_vlp, VIK_LAYER_TRW, TRUE );
3466
3467   if ( !gl ) {
3468     a_dialog_info_msg ( GTK_WINDOW(vw), _("Nothing to Export!") );
3469     return;
3470   }
3471
3472   GtkWidget *dialog = gtk_file_chooser_dialog_new ( _("Export to directory"),
3473                                                     GTK_WINDOW(vw),
3474                                                     GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER,
3475                                                     GTK_STOCK_CANCEL,
3476                                                     GTK_RESPONSE_REJECT,
3477                                                     GTK_STOCK_OK,
3478                                                     GTK_RESPONSE_ACCEPT,
3479                                                     NULL );
3480   gtk_window_set_transient_for ( GTK_WINDOW(dialog), GTK_WINDOW(vw) );
3481   gtk_window_set_destroy_with_parent ( GTK_WINDOW(dialog), TRUE );
3482   gtk_window_set_modal ( GTK_WINDOW(dialog), TRUE );
3483
3484   gtk_widget_show_all ( dialog );
3485
3486   if ( gtk_dialog_run ( GTK_DIALOG(dialog) ) == GTK_RESPONSE_ACCEPT ) {
3487     gchar *dir = gtk_file_chooser_get_filename ( GTK_FILE_CHOOSER(dialog) );
3488     gtk_widget_destroy ( dialog );
3489     if ( dir ) {
3490       if ( !export_to ( vw, gl, vft, dir, extension ) )
3491         a_dialog_error_msg ( GTK_WINDOW(vw),_("Could not convert all files") );
3492       g_free ( dir );
3493     }
3494   }
3495   else
3496     gtk_widget_destroy ( dialog );
3497
3498   g_list_free ( gl );
3499 }
3500
3501 static void export_to_gpx ( GtkAction *a, VikWindow *vw )
3502 {
3503   export_to_common ( vw, FILE_TYPE_GPX, ".gpx" );
3504 }
3505
3506 static void export_to_kml ( GtkAction *a, VikWindow *vw )
3507 {
3508   export_to_common ( vw, FILE_TYPE_KML, ".kml" );
3509 }
3510
3511 static void file_properties_cb ( GtkAction *a, VikWindow *vw )
3512 {
3513   gchar *message = NULL;
3514   if ( vw->filename ) {
3515     if ( g_file_test ( vw->filename, G_FILE_TEST_EXISTS ) ) {
3516       // Get some timestamp information of the file
3517       GStatBuf stat_buf;
3518       if ( g_stat ( vw->filename, &stat_buf ) == 0 ) {
3519         gchar time_buf[64];
3520         strftime ( time_buf, sizeof(time_buf), "%c", gmtime((const time_t *)&stat_buf.st_mtime) );
3521         gchar *size = NULL;
3522         gint byte_size = stat_buf.st_size;
3523 #if GLIB_CHECK_VERSION(2,30,0)
3524         size = g_format_size_full ( byte_size, G_FORMAT_SIZE_DEFAULT );
3525 #else
3526         size = g_format_size_for_display ( byte_size );
3527 #endif
3528         message = g_strdup_printf ( "%s\n\n%s\n\n%s", vw->filename, time_buf, size );
3529         g_free (size);
3530       }
3531     }
3532     else
3533       message = g_strdup ( _("File not accessible") );
3534   }
3535   else
3536     message = g_strdup ( _("No Viking File") );
3537
3538   // Show the info
3539   a_dialog_info_msg ( GTK_WINDOW(vw), message );
3540   g_free ( message );
3541 }
3542
3543 static void my_acquire ( VikWindow *vw, VikDataSourceInterface *datasource )
3544 {
3545   vik_datasource_mode_t mode = datasource->mode;
3546   if ( mode == VIK_DATASOURCE_AUTO_LAYER_MANAGEMENT )
3547     mode = VIK_DATASOURCE_CREATENEWLAYER;
3548   a_acquire ( vw, vw->viking_vlp, vw->viking_vvp, mode, datasource, NULL, NULL );
3549 }
3550
3551 static void acquire_from_gps ( GtkAction *a, VikWindow *vw )
3552 {
3553   my_acquire ( vw, &vik_datasource_gps_interface );
3554 }
3555
3556 static void acquire_from_file ( GtkAction *a, VikWindow *vw )
3557 {
3558   my_acquire ( vw, &vik_datasource_file_interface );
3559 }
3560
3561 static void acquire_from_geojson ( GtkAction *a, VikWindow *vw )
3562 {
3563   my_acquire ( vw, &vik_datasource_geojson_interface );
3564 }
3565
3566 static void acquire_from_routing ( GtkAction *a, VikWindow *vw )
3567 {
3568   my_acquire ( vw, &vik_datasource_routing_interface );
3569 }
3570
3571 #ifdef VIK_CONFIG_OPENSTREETMAP
3572 static void acquire_from_osm ( GtkAction *a, VikWindow *vw )
3573 {
3574   my_acquire ( vw, &vik_datasource_osm_interface );
3575 }
3576
3577 static void acquire_from_my_osm ( GtkAction *a, VikWindow *vw )
3578 {
3579   my_acquire ( vw, &vik_datasource_osm_my_traces_interface );
3580 }
3581 #endif
3582
3583 #ifdef VIK_CONFIG_GEOCACHES
3584 static void acquire_from_gc ( GtkAction *a, VikWindow *vw )
3585 {
3586   my_acquire ( vw, &vik_datasource_gc_interface );
3587 }
3588 #endif
3589
3590 #ifdef VIK_CONFIG_GEOTAG
3591 static void acquire_from_geotag ( GtkAction *a, VikWindow *vw )
3592 {
3593   my_acquire ( vw, &vik_datasource_geotag_interface );
3594 }
3595 #endif
3596
3597 #ifdef VIK_CONFIG_GEONAMES
3598 static void acquire_from_wikipedia ( GtkAction *a, VikWindow *vw )
3599 {
3600   my_acquire ( vw, &vik_datasource_wikipedia_interface );
3601 }
3602 #endif
3603
3604 static void acquire_from_url ( GtkAction *a, VikWindow *vw )
3605 {
3606   my_acquire ( vw, &vik_datasource_url_interface );
3607 }
3608
3609 static void goto_default_location( GtkAction *a, VikWindow *vw)
3610 {
3611   struct LatLon ll;
3612   ll.lat = a_vik_get_default_lat();
3613   ll.lon = a_vik_get_default_long();
3614   vik_viewport_set_center_latlon(vw->viking_vvp, &ll, TRUE);
3615   vik_layers_panel_emit_update(vw->viking_vlp);
3616 }
3617
3618
3619 static void goto_address( GtkAction *a, VikWindow *vw)
3620 {
3621   a_vik_goto ( vw, vw->viking_vvp );
3622   vik_layers_panel_emit_update ( vw->viking_vlp );
3623 }
3624
3625 static void mapcache_flush_cb ( GtkAction *a, VikWindow *vw )
3626 {
3627   a_mapcache_flush();
3628 }
3629
3630 static void menu_copy_centre_cb ( GtkAction *a, VikWindow *vw )
3631 {
3632   const VikCoord* coord;
3633   struct UTM utm;
3634   gchar *lat = NULL, *lon = NULL;
3635
3636   coord = vik_viewport_get_center ( vw->viking_vvp );
3637   vik_coord_to_utm ( coord, &utm );
3638
3639   gboolean full_format = FALSE;
3640   (void)a_settings_get_boolean ( VIK_SETTINGS_WIN_COPY_CENTRE_FULL_FORMAT, &full_format );
3641
3642   if ( full_format )
3643     // Bells & Whistles - may include degrees, minutes and second symbols
3644     get_location_strings ( vw, utm, &lat, &lon );
3645   else {
3646     // Simple x.xx y.yy format
3647     struct LatLon ll;
3648     a_coords_utm_to_latlon ( &utm, &ll );
3649     lat = g_strdup_printf ( "%.6f", ll.lat );
3650     lon = g_strdup_printf ( "%.6f", ll.lon );
3651   }
3652
3653   gchar *msg = g_strdup_printf ( "%s %s", lat, lon );
3654   g_free (lat);
3655   g_free (lon);
3656
3657   a_clipboard_copy ( VIK_CLIPBOARD_DATA_TEXT, 0, 0, 0, msg, NULL );
3658
3659   g_free ( msg );
3660 }
3661
3662 static void layer_defaults_cb ( GtkAction *a, VikWindow *vw )
3663 {
3664   gchar **texts = g_strsplit ( gtk_action_get_name(a), "Layer", 0 );
3665
3666   if ( !texts[1] )
3667     return; // Internally broken :(
3668
3669   if ( ! a_layer_defaults_show_window ( GTK_WINDOW(vw), texts[1] ) )
3670     a_dialog_info_msg ( GTK_WINDOW(vw), _("This layer has no configurable properties.") );
3671   // NB no update needed
3672
3673   g_strfreev ( texts );
3674 }
3675
3676 static void preferences_change_update ( VikWindow *vw, gpointer data )
3677 {
3678   // Want to update all TrackWaypoint layers
3679   GList *layers = vik_layers_panel_get_all_layers_of_type ( vw->viking_vlp, VIK_LAYER_TRW, TRUE );
3680
3681   if ( !layers )
3682     return;
3683
3684   while ( layers ) {
3685     // Reset the individual waypoints themselves due to the preferences change
3686     VikTrwLayer *vtl = VIK_TRW_LAYER(layers->data);
3687     vik_trw_layer_reset_waypoints ( vtl );
3688     layers = g_list_next ( layers );
3689   }
3690
3691   g_list_free ( layers );
3692
3693   draw_update ( vw );
3694 }
3695
3696 static void preferences_cb ( GtkAction *a, VikWindow *vw )
3697 {
3698   gboolean wp_icon_size = a_vik_get_use_large_waypoint_icons();
3699
3700   a_preferences_show_window ( GTK_WINDOW(vw) );
3701
3702   // Has the waypoint size setting changed?
3703   if (wp_icon_size != a_vik_get_use_large_waypoint_icons()) {
3704     // Delete icon indexing 'cache' and so automatically regenerates with the new setting when changed
3705     clear_garmin_icon_syms ();
3706
3707     // Update all windows
3708     g_slist_foreach ( window_list, (GFunc) preferences_change_update, NULL );
3709   }
3710
3711   // Ensure TZ Lookup initialized
3712   if ( a_vik_get_time_ref_frame() == VIK_TIME_REF_WORLD )
3713     vu_setup_lat_lon_tz_lookup();
3714
3715   toolbar_apply_settings ( vw->viking_vtb, vw->main_vbox, vw->menu_hbox, TRUE );
3716 }
3717
3718 static void default_location_cb ( GtkAction *a, VikWindow *vw )
3719 {
3720   /* Simplistic repeat of preference setting
3721      Only the name & type are important for setting the preference via this 'external' way */
3722   VikLayerParam pref_lat[] = {
3723     { VIK_LAYER_NUM_TYPES,
3724       VIKING_PREFERENCES_NAMESPACE "default_latitude",
3725       VIK_LAYER_PARAM_DOUBLE,
3726       VIK_LAYER_GROUP_NONE,
3727       NULL,
3728       VIK_LAYER_WIDGET_SPINBUTTON,
3729       NULL,
3730       NULL,
3731       NULL,
3732       NULL,
3733       NULL,
3734       NULL,
3735     },
3736   };
3737   VikLayerParam pref_lon[] = {
3738     { VIK_LAYER_NUM_TYPES,
3739       VIKING_PREFERENCES_NAMESPACE "default_longitude",
3740       VIK_LAYER_PARAM_DOUBLE,
3741       VIK_LAYER_GROUP_NONE,
3742       NULL,
3743       VIK_LAYER_WIDGET_SPINBUTTON,
3744       NULL,
3745       NULL,
3746       NULL,
3747       NULL,
3748       NULL,
3749       NULL,
3750     },
3751   };
3752
3753   /* Get current center */
3754   struct LatLon ll;
3755   vik_coord_to_latlon ( vik_viewport_get_center ( vw->viking_vvp ), &ll );
3756
3757   /* Apply to preferences */
3758   VikLayerParamData vlp_data;
3759   vlp_data.d = ll.lat;
3760   a_preferences_run_setparam (vlp_data, pref_lat);
3761   vlp_data.d = ll.lon;
3762   a_preferences_run_setparam (vlp_data, pref_lon);
3763   /* Remember to save */
3764   a_preferences_save_to_file();
3765 }
3766
3767 /**
3768  * Delete All
3769  */
3770 static void clear_cb ( GtkAction *a, VikWindow *vw )
3771 {
3772   // Do nothing if empty
3773   VikAggregateLayer *top = vik_layers_panel_get_top_layer(vw->viking_vlp);
3774   if ( ! vik_aggregate_layer_is_empty(top) ) {
3775     if ( a_dialog_yes_or_no ( GTK_WINDOW(vw), _("Are you sure you wish to delete all layers?"), NULL ) ) {
3776       vik_layers_panel_clear ( vw->viking_vlp );
3777       window_set_filename ( vw, NULL );
3778       draw_update ( vw );
3779     }
3780   }
3781 }
3782
3783 static void window_close ( GtkAction *a, VikWindow *vw )
3784 {
3785   if ( ! delete_event ( vw ) )
3786     gtk_widget_destroy ( GTK_WIDGET(vw) );
3787 }
3788
3789 static gboolean save_file_and_exit ( GtkAction *a, VikWindow *vw )
3790 {
3791   if (save_file( NULL, vw)) {
3792     window_close( NULL, vw);
3793     return(TRUE);
3794   }
3795   else
3796     return(FALSE);
3797 }
3798
3799 static void zoom_to_cb ( GtkAction *a, VikWindow *vw )
3800 {
3801   gdouble xmpp = vik_viewport_get_xmpp ( vw->viking_vvp ), ympp = vik_viewport_get_ympp ( vw->viking_vvp );
3802   if ( a_dialog_custom_zoom ( GTK_WINDOW(vw), &xmpp, &ympp ) )
3803   {
3804     vik_viewport_set_xmpp ( vw->viking_vvp, xmpp );
3805     vik_viewport_set_ympp ( vw->viking_vvp, ympp );
3806     draw_update ( vw );
3807   }
3808 }
3809
3810 static void save_image_file ( VikWindow *vw, const gchar *fn, guint w, guint h, gdouble zoom, gboolean save_as_png, gboolean save_kmz )
3811 {
3812   /* more efficient way: stuff draws directly to pixbuf (fork viewport) */
3813   GdkPixbuf *pixbuf_to_save;
3814   gdouble old_xmpp, old_ympp;
3815   GError *error = NULL;
3816
3817   GtkWidget *msgbox = gtk_message_dialog_new ( GTK_WINDOW(vw),
3818                                                GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
3819                                                GTK_MESSAGE_INFO,
3820                                                GTK_BUTTONS_NONE,
3821                                                _("Generating image file...") );
3822
3823   g_signal_connect_swapped (msgbox, "response", G_CALLBACK (gtk_widget_destroy), msgbox);
3824   // Ensure dialog shown
3825   gtk_widget_show_all ( msgbox );
3826   // Try harder...
3827   vik_statusbar_set_message ( vw->viking_vs, VIK_STATUSBAR_INFO, _("Generating image file...") );
3828   while ( gtk_events_pending() )
3829     gtk_main_iteration ();
3830   // Despite many efforts & variations, GTK on my Linux system doesn't show the actual msgbox contents :(
3831   // At least the empty box can give a clue something's going on + the statusbar msg...
3832   // Windows version under Wine OK!
3833
3834   /* backup old zoom & set new */
3835   old_xmpp = vik_viewport_get_xmpp ( vw->viking_vvp );
3836   old_ympp = vik_viewport_get_ympp ( vw->viking_vvp );
3837   vik_viewport_set_zoom ( vw->viking_vvp, zoom );
3838
3839   /* reset width and height: */
3840   vik_viewport_configure_manually ( vw->viking_vvp, w, h );
3841
3842   /* draw all layers */
3843   draw_redraw ( vw );
3844
3845   /* save buffer as file. */
3846   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);
3847   if ( !pixbuf_to_save ) {
3848     g_warning("Failed to generate internal pixmap size: %d x %d", w, h);
3849     gtk_message_dialog_set_markup ( GTK_MESSAGE_DIALOG(msgbox), _("Failed to generate internal image.\n\nTry creating a smaller image.") );
3850     goto cleanup;
3851   }
3852
3853   int ans = 0; // Default to success
3854
3855   if ( save_kmz ) {
3856     gdouble north, east, south, west;
3857     vik_viewport_get_min_max_lat_lon ( vw->viking_vvp, &south, &north, &west, &east );
3858     ans = kmz_save_file ( pixbuf_to_save, fn, north, east, south, west );
3859   }
3860   else {
3861     gdk_pixbuf_save ( pixbuf_to_save, fn, save_as_png ? "png" : "jpeg", &error, NULL );
3862     if (error) {
3863       g_warning("Unable to write to file %s: %s", fn, error->message );
3864       g_error_free (error);
3865       ans = 42;
3866     }
3867   }
3868
3869   if ( ans == 0 )
3870     gtk_message_dialog_set_markup ( GTK_MESSAGE_DIALOG(msgbox), _("Image file generated.") );
3871   else
3872     gtk_message_dialog_set_markup ( GTK_MESSAGE_DIALOG(msgbox), _("Failed to generate image file.") );
3873
3874   g_object_unref ( G_OBJECT(pixbuf_to_save) );
3875
3876  cleanup:
3877   vik_statusbar_set_message ( vw->viking_vs, VIK_STATUSBAR_INFO, "" );
3878   gtk_dialog_add_button ( GTK_DIALOG(msgbox), GTK_STOCK_OK, GTK_RESPONSE_OK );
3879   gtk_dialog_run ( GTK_DIALOG(msgbox) ); // Don't care about the result
3880
3881   /* pretend like nothing happened ;) */
3882   vik_viewport_set_xmpp ( vw->viking_vvp, old_xmpp );
3883   vik_viewport_set_ympp ( vw->viking_vvp, old_ympp );
3884   vik_viewport_configure ( vw->viking_vvp );
3885   draw_update ( vw );
3886 }
3887
3888 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 )
3889 {
3890   gulong size = sizeof(gchar) * (strlen(fn) + 15);
3891   gchar *name_of_file = g_malloc ( size );
3892   guint x = 1, y = 1;
3893   struct UTM utm_orig, utm;
3894
3895   /* *** copied from above *** */
3896   GdkPixbuf *pixbuf_to_save;
3897   gdouble old_xmpp, old_ympp;
3898   GError *error = NULL;
3899
3900   /* backup old zoom & set new */
3901   old_xmpp = vik_viewport_get_xmpp ( vw->viking_vvp );
3902   old_ympp = vik_viewport_get_ympp ( vw->viking_vvp );
3903   vik_viewport_set_zoom ( vw->viking_vvp, zoom );
3904
3905   /* reset width and height: do this only once for all images (same size) */
3906   vik_viewport_configure_manually ( vw->viking_vvp, w, h );
3907   /* *** end copy from above *** */
3908
3909   g_assert ( vik_viewport_get_coord_mode ( vw->viking_vvp ) == VIK_COORD_UTM );
3910
3911   if ( g_mkdir(fn,0777) != 0 )
3912     g_warning ( "%s: Failed to create directory %s", __FUNCTION__, fn );
3913
3914   utm_orig = *((const struct UTM *)vik_viewport_get_center ( vw->viking_vvp ));
3915
3916   for ( y = 1; y <= tiles_h; y++ )
3917   {
3918     for ( x = 1; x <= tiles_w; x++ )
3919     {
3920       g_snprintf ( name_of_file, size, "%s%cy%d-x%d.%s", fn, G_DIR_SEPARATOR, y, x, save_as_png ? "png" : "jpg" );
3921       utm = utm_orig;
3922       if ( tiles_w & 0x1 )
3923         utm.easting += ((gdouble)x - ceil(((gdouble)tiles_w)/2)) * (w*zoom);
3924       else
3925         utm.easting += ((gdouble)x - (((gdouble)tiles_w)+1)/2) * (w*zoom);
3926       if ( tiles_h & 0x1 ) /* odd */
3927         utm.northing -= ((gdouble)y - ceil(((gdouble)tiles_h)/2)) * (h*zoom);
3928       else /* even */
3929         utm.northing -= ((gdouble)y - (((gdouble)tiles_h)+1)/2) * (h*zoom);
3930
3931       /* move to correct place. */
3932       vik_viewport_set_center_utm ( vw->viking_vvp, &utm, FALSE );
3933
3934       draw_redraw ( vw );
3935
3936       /* save buffer as file. */
3937       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);
3938       gdk_pixbuf_save ( pixbuf_to_save, name_of_file, save_as_png ? "png" : "jpeg", &error, NULL );
3939       if (error)
3940       {
3941         gchar *msg = g_strdup_printf (_("Unable to write to file %s: %s"), name_of_file, error->message );
3942         vik_statusbar_set_message ( vw->viking_vs, VIK_STATUSBAR_INFO, msg );
3943         g_free ( msg );
3944         g_error_free (error);
3945       }
3946
3947       g_object_unref ( G_OBJECT(pixbuf_to_save) );
3948     }
3949   }
3950
3951   vik_viewport_set_center_utm ( vw->viking_vvp, &utm_orig, FALSE );
3952   vik_viewport_set_xmpp ( vw->viking_vvp, old_xmpp );
3953   vik_viewport_set_ympp ( vw->viking_vvp, old_ympp );
3954   vik_viewport_configure ( vw->viking_vvp );
3955   draw_update ( vw );
3956
3957   g_free ( name_of_file );
3958 }
3959
3960 static void draw_to_image_file_current_window_cb(GtkWidget* widget,GdkEventButton *event,gpointer *pass_along)
3961 {
3962   VikWindow *vw = VIK_WINDOW(pass_along[0]);
3963   GtkSpinButton *width_spin = GTK_SPIN_BUTTON(pass_along[1]), *height_spin = GTK_SPIN_BUTTON(pass_along[2]);
3964
3965   gint active = gtk_combo_box_get_active ( GTK_COMBO_BOX(pass_along[3]) );
3966   gdouble zoom = pow (2, active-2 );
3967
3968   gdouble width_min, width_max, height_min, height_max;
3969   gint width, height;
3970
3971   gtk_spin_button_get_range ( width_spin, &width_min, &width_max );
3972   gtk_spin_button_get_range ( height_spin, &height_min, &height_max );
3973
3974   /* TODO: support for xzoom and yzoom values */
3975   width = vik_viewport_get_width ( vw->viking_vvp ) * vik_viewport_get_xmpp ( vw->viking_vvp ) / zoom;
3976   height = vik_viewport_get_height ( vw->viking_vvp ) * vik_viewport_get_xmpp ( vw->viking_vvp ) / zoom;
3977
3978   if ( width > width_max || width < width_min || height > height_max || height < height_min )
3979     a_dialog_info_msg ( GTK_WINDOW(vw), _("Viewable region outside allowable pixel size bounds for image. Clipping width/height values.") );
3980
3981   gtk_spin_button_set_value ( width_spin, width );
3982   gtk_spin_button_set_value ( height_spin, height );
3983 }
3984
3985 static void draw_to_image_file_total_area_cb (GtkSpinButton *spinbutton, gpointer *pass_along)
3986 {
3987   GtkSpinButton *width_spin = GTK_SPIN_BUTTON(pass_along[1]), *height_spin = GTK_SPIN_BUTTON(pass_along[2]);
3988
3989   gint active = gtk_combo_box_get_active ( GTK_COMBO_BOX(pass_along[3]) );
3990   gdouble zoom = pow (2, active-2 );
3991
3992   gchar *label_text;
3993   gdouble w, h;
3994   w = gtk_spin_button_get_value(width_spin) * zoom;
3995   h = gtk_spin_button_get_value(height_spin) * zoom;
3996   if (pass_along[4]) /* save many images; find TOTAL area covered */
3997   {
3998     w *= gtk_spin_button_get_value(GTK_SPIN_BUTTON(pass_along[4]));
3999     h *= gtk_spin_button_get_value(GTK_SPIN_BUTTON(pass_along[5]));
4000   }
4001   vik_units_distance_t dist_units = a_vik_get_units_distance ();
4002   switch (dist_units) {
4003   case VIK_UNITS_DISTANCE_KILOMETRES:
4004     label_text = g_strdup_printf ( _("Total area: %ldm x %ldm (%.3f sq. km)"), (glong)w, (glong)h, (w*h/1000000));
4005     break;
4006   case VIK_UNITS_DISTANCE_MILES:
4007     label_text = g_strdup_printf ( _("Total area: %ldm x %ldm (%.3f sq. miles)"), (glong)w, (glong)h, (w*h/2589988.11));
4008     break;
4009   case VIK_UNITS_DISTANCE_NAUTICAL_MILES:
4010     label_text = g_strdup_printf ( _("Total area: %ldm x %ldm (%.3f sq. NM)"), (glong)w, (glong)h, (w*h/(1852.0*1852.0)));
4011     break;
4012   default:
4013     label_text = g_strdup_printf ("Just to keep the compiler happy");
4014     g_critical("Houston, we've had a problem. distance=%d", dist_units);
4015   }
4016
4017   gtk_label_set_text(GTK_LABEL(pass_along[6]), label_text);
4018   g_free ( label_text );
4019 }
4020
4021 typedef enum {
4022   VW_GEN_SINGLE_IMAGE,
4023   VW_GEN_DIRECTORY_OF_IMAGES,
4024   VW_GEN_KMZ_FILE,
4025 } img_generation_t;
4026
4027 /*
4028  * Get an allocated filename (or directory as specified)
4029  */
4030 static gchar* draw_image_filename ( VikWindow *vw, img_generation_t img_gen )
4031 {
4032   gchar *fn = NULL;
4033   if ( img_gen != VW_GEN_DIRECTORY_OF_IMAGES )
4034   {
4035     // Single file
4036     GtkWidget *dialog = gtk_file_chooser_dialog_new (_("Save Image"),
4037                                                      GTK_WINDOW(vw),
4038                                                      GTK_FILE_CHOOSER_ACTION_SAVE,
4039                                                      GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
4040                                                      GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT,
4041                                                      NULL);
4042     if ( last_folder_images_uri )
4043       gtk_file_chooser_set_current_folder_uri ( GTK_FILE_CHOOSER(dialog), last_folder_images_uri );
4044
4045     GtkFileChooser *chooser = GTK_FILE_CHOOSER ( dialog );
4046     /* Add filters */
4047     GtkFileFilter *filter;
4048     filter = gtk_file_filter_new ();
4049     gtk_file_filter_set_name ( filter, _("All") );
4050     gtk_file_filter_add_pattern ( filter, "*" );
4051     gtk_file_chooser_add_filter ( chooser, filter );
4052
4053     if ( img_gen == VW_GEN_KMZ_FILE ) {
4054       filter = gtk_file_filter_new ();
4055       gtk_file_filter_set_name ( filter, _("KMZ") );
4056       gtk_file_filter_add_mime_type ( filter, "vnd.google-earth.kmz");
4057       gtk_file_filter_add_pattern ( filter, "*.kmz" );
4058       gtk_file_chooser_add_filter ( chooser, filter );
4059       gtk_file_chooser_set_filter ( chooser, filter );
4060     }
4061     else {
4062       filter = gtk_file_filter_new ();
4063       gtk_file_filter_set_name ( filter, _("JPG") );
4064       gtk_file_filter_add_mime_type ( filter, "image/jpeg");
4065       gtk_file_chooser_add_filter ( chooser, filter );
4066
4067       if ( !vw->draw_image_save_as_png )
4068         gtk_file_chooser_set_filter ( chooser, filter );
4069
4070       filter = gtk_file_filter_new ();
4071       gtk_file_filter_set_name ( filter, _("PNG") );
4072       gtk_file_filter_add_mime_type ( filter, "image/png");
4073       gtk_file_chooser_add_filter ( chooser, filter );
4074
4075       if ( vw->draw_image_save_as_png )
4076         gtk_file_chooser_set_filter ( chooser, filter );
4077     }
4078
4079     gtk_window_set_transient_for ( GTK_WINDOW(dialog), GTK_WINDOW(vw) );
4080     gtk_window_set_destroy_with_parent ( GTK_WINDOW(dialog), TRUE );
4081
4082     if ( gtk_dialog_run ( GTK_DIALOG(dialog) ) == GTK_RESPONSE_ACCEPT ) {
4083       g_free ( last_folder_images_uri );
4084       last_folder_images_uri = gtk_file_chooser_get_current_folder_uri ( GTK_FILE_CHOOSER(dialog) );
4085
4086       fn = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER(dialog) );
4087       if ( g_file_test ( fn, G_FILE_TEST_EXISTS ) )
4088         if ( ! a_dialog_yes_or_no ( GTK_WINDOW(dialog), _("The file \"%s\" exists, do you wish to overwrite it?"), a_file_basename ( fn ) ) )
4089           fn = NULL;
4090     }
4091     gtk_widget_destroy ( dialog );
4092   }
4093   else {
4094     // A directory
4095     // For some reason this method is only written to work in UTM...
4096     if ( vik_viewport_get_coord_mode(vw->viking_vvp) != VIK_COORD_UTM ) {
4097       a_dialog_error_msg ( GTK_WINDOW(vw), _("You must be in UTM mode to use this feature") );
4098       return fn;
4099     }
4100
4101     GtkWidget *dialog = gtk_file_chooser_dialog_new (_("Choose a directory to hold images"),
4102                                                      GTK_WINDOW(vw),
4103                                                      GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER,
4104                                                      GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
4105                                                      GTK_STOCK_OK, GTK_RESPONSE_ACCEPT,
4106                                                      NULL);
4107     gtk_window_set_transient_for ( GTK_WINDOW(dialog), GTK_WINDOW(vw) );
4108     gtk_window_set_destroy_with_parent ( GTK_WINDOW(dialog), TRUE );
4109
4110     if ( gtk_dialog_run ( GTK_DIALOG(dialog) ) == GTK_RESPONSE_ACCEPT ) {
4111       fn = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER(dialog) );
4112     }
4113     gtk_widget_destroy ( dialog );
4114   }
4115   return fn;
4116 }
4117
4118 static void draw_to_image_file ( VikWindow *vw, img_generation_t img_gen )
4119 {
4120   /* todo: default for answers inside VikWindow or static (thruout instance) */
4121   GtkWidget *dialog = gtk_dialog_new_with_buttons ( _("Save to Image File"), GTK_WINDOW(vw),
4122                                                   GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
4123                                                   GTK_STOCK_CANCEL,
4124                                                   GTK_RESPONSE_REJECT,
4125                                                   GTK_STOCK_OK,
4126                                                   GTK_RESPONSE_ACCEPT,
4127                                                   NULL );
4128   GtkWidget *width_label, *width_spin, *height_label, *height_spin;
4129   GtkWidget *current_window_button;
4130   gpointer current_window_pass_along[7];
4131   GtkWidget *zoom_label, *zoom_combo;
4132   GtkWidget *total_size_label;
4133
4134   // only used for VW_GEN_DIRECTORY_OF_IMAGES
4135   GtkWidget *tiles_width_spin = NULL, *tiles_height_spin = NULL;
4136
4137   width_label = gtk_label_new ( _("Width (pixels):") );
4138   width_spin = gtk_spin_button_new ( GTK_ADJUSTMENT(gtk_adjustment_new ( vw->draw_image_width, 10, 50000, 10, 100, 0 )), 10, 0 );
4139   height_label = gtk_label_new ( _("Height (pixels):") );
4140   height_spin = gtk_spin_button_new ( GTK_ADJUSTMENT(gtk_adjustment_new ( vw->draw_image_height, 10, 50000, 10, 100, 0 )), 10, 0 );
4141 #ifdef WINDOWS
4142   GtkWidget *win_warning_label = gtk_label_new ( _("WARNING: USING LARGE IMAGES OVER 10000x10000\nMAY CRASH THE PROGRAM!") );
4143 #endif
4144   zoom_label = gtk_label_new ( _("Zoom (meters per pixel):") );
4145   /* TODO: separate xzoom and yzoom factors */
4146   zoom_combo = create_zoom_combo_all_levels();
4147
4148   gdouble mpp = vik_viewport_get_xmpp(vw->viking_vvp);
4149   gint active = 2 + round ( log (mpp) / log (2) );
4150
4151   // Can we not hard code size here?
4152   if ( active > 17 )
4153     active = 17;
4154   if ( active < 0 )
4155     active = 0;
4156   gtk_combo_box_set_active ( GTK_COMBO_BOX(zoom_combo), active );
4157
4158   total_size_label = gtk_label_new ( NULL );
4159
4160   current_window_button = gtk_button_new_with_label ( _("Area in current viewable window") );
4161   current_window_pass_along [0] = vw;
4162   current_window_pass_along [1] = width_spin;
4163   current_window_pass_along [2] = height_spin;
4164   current_window_pass_along [3] = zoom_combo;
4165   current_window_pass_along [4] = NULL; // Only for directory of tiles: width
4166   current_window_pass_along [5] = NULL; // Only for directory of tiles: height
4167   current_window_pass_along [6] = total_size_label;
4168   g_signal_connect ( G_OBJECT(current_window_button), "button_press_event", G_CALLBACK(draw_to_image_file_current_window_cb), current_window_pass_along );
4169
4170   GtkWidget *png_radio = gtk_radio_button_new_with_label ( NULL, _("Save as PNG") );
4171   GtkWidget *jpeg_radio = gtk_radio_button_new_with_label_from_widget ( GTK_RADIO_BUTTON(png_radio), _("Save as JPEG") );
4172
4173   if ( img_gen == VW_GEN_KMZ_FILE ) {
4174     // Don't show image type selection if creating a KMZ (always JPG internally)
4175     // Start with viewable area by default
4176     draw_to_image_file_current_window_cb ( current_window_button, NULL, current_window_pass_along );
4177   } else {
4178     gtk_box_pack_start (GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), jpeg_radio, FALSE, FALSE, 0);
4179     gtk_box_pack_start (GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), png_radio, FALSE, FALSE, 0);
4180   }
4181
4182   if ( ! vw->draw_image_save_as_png )
4183     gtk_toggle_button_set_active ( GTK_TOGGLE_BUTTON(jpeg_radio), TRUE );
4184
4185   gtk_box_pack_start (GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), width_label, FALSE, FALSE, 0);
4186   gtk_box_pack_start (GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), width_spin, FALSE, FALSE, 0);
4187   gtk_box_pack_start (GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), height_label, FALSE, FALSE, 0);
4188   gtk_box_pack_start (GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), height_spin, FALSE, FALSE, 0);
4189 #ifdef WINDOWS
4190   gtk_box_pack_start (GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), win_warning_label, FALSE, FALSE, 0);
4191 #endif
4192   gtk_box_pack_start (GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), current_window_button, FALSE, FALSE, 0);
4193   gtk_box_pack_start (GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), zoom_label, FALSE, FALSE, 0);
4194   gtk_box_pack_start (GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), zoom_combo, FALSE, FALSE, 0);
4195
4196   if ( img_gen == VW_GEN_DIRECTORY_OF_IMAGES )
4197   {
4198     GtkWidget *tiles_width_label, *tiles_height_label;
4199
4200     tiles_width_label = gtk_label_new ( _("East-west image tiles:") );
4201     tiles_width_spin = gtk_spin_button_new ( GTK_ADJUSTMENT(gtk_adjustment_new ( 5, 1, 10, 1, 100, 0 )), 1, 0 );
4202     tiles_height_label = gtk_label_new ( _("North-south image tiles:") );
4203     tiles_height_spin = gtk_spin_button_new ( GTK_ADJUSTMENT(gtk_adjustment_new ( 5, 1, 10, 1, 100, 0 )), 1, 0 );
4204     gtk_box_pack_start (GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), tiles_width_label, FALSE, FALSE, 0);
4205     gtk_box_pack_start (GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), tiles_width_spin, FALSE, FALSE, 0);
4206     gtk_box_pack_start (GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), tiles_height_label, FALSE, FALSE, 0);
4207     gtk_box_pack_start (GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), tiles_height_spin, FALSE, FALSE, 0);
4208
4209     current_window_pass_along [4] = tiles_width_spin;
4210     current_window_pass_along [5] = tiles_height_spin;
4211     g_signal_connect ( G_OBJECT(tiles_width_spin), "value-changed", G_CALLBACK(draw_to_image_file_total_area_cb), current_window_pass_along );
4212     g_signal_connect ( G_OBJECT(tiles_height_spin), "value-changed", G_CALLBACK(draw_to_image_file_total_area_cb), current_window_pass_along );
4213   }
4214   gtk_box_pack_start (GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), total_size_label, FALSE, FALSE, 0);
4215   g_signal_connect ( G_OBJECT(width_spin), "value-changed", G_CALLBACK(draw_to_image_file_total_area_cb), current_window_pass_along );
4216   g_signal_connect ( G_OBJECT(height_spin), "value-changed", G_CALLBACK(draw_to_image_file_total_area_cb), current_window_pass_along );
4217   g_signal_connect ( G_OBJECT(zoom_combo), "changed", G_CALLBACK(draw_to_image_file_total_area_cb), current_window_pass_along );
4218
4219   draw_to_image_file_total_area_cb ( NULL, current_window_pass_along ); /* set correct size info now */
4220
4221   gtk_dialog_set_default_response ( GTK_DIALOG(dialog), GTK_RESPONSE_ACCEPT );
4222
4223   gtk_widget_show_all ( gtk_dialog_get_content_area(GTK_DIALOG(dialog)) );
4224
4225   if ( gtk_dialog_run ( GTK_DIALOG(dialog) ) == GTK_RESPONSE_ACCEPT )
4226   {
4227     gtk_widget_hide ( GTK_WIDGET(dialog) );
4228
4229     gchar *fn = draw_image_filename ( vw, img_gen );
4230     if ( !fn )
4231       return;
4232
4233     gint active_z = gtk_combo_box_get_active ( GTK_COMBO_BOX(zoom_combo) );
4234     gdouble zoom = pow (2, active_z-2 );
4235
4236     if ( img_gen == VW_GEN_SINGLE_IMAGE )
4237       save_image_file ( vw, fn, 
4238                       vw->draw_image_width = gtk_spin_button_get_value_as_int ( GTK_SPIN_BUTTON(width_spin) ),
4239                       vw->draw_image_height = gtk_spin_button_get_value_as_int ( GTK_SPIN_BUTTON(height_spin) ),
4240                       zoom,
4241                       vw->draw_image_save_as_png = gtk_toggle_button_get_active ( GTK_TOGGLE_BUTTON(png_radio) ),
4242                       FALSE );
4243     else if ( img_gen == VW_GEN_KMZ_FILE ) {
4244       // Remove some viewport overlays as these aren't useful in KMZ file.
4245       gboolean restore_xhair = vik_viewport_get_draw_centermark ( vw->viking_vvp );
4246       if ( restore_xhair )
4247         vik_viewport_set_draw_centermark ( vw->viking_vvp, FALSE );
4248       gboolean restore_scale = vik_viewport_get_draw_scale ( vw->viking_vvp );
4249       if ( restore_scale )
4250         vik_viewport_set_draw_scale ( vw->viking_vvp, FALSE );
4251
4252       save_image_file ( vw,
4253                         fn,
4254                         gtk_spin_button_get_value_as_int ( GTK_SPIN_BUTTON(width_spin) ),
4255                         gtk_spin_button_get_value_as_int ( GTK_SPIN_BUTTON(height_spin) ),
4256                         zoom,
4257                         FALSE, // JPG
4258                         TRUE );
4259
4260       if ( restore_xhair )
4261         vik_viewport_set_draw_centermark ( vw->viking_vvp, TRUE );
4262       if ( restore_scale )
4263         vik_viewport_set_draw_scale ( vw->viking_vvp, TRUE );
4264       if ( restore_xhair || restore_scale )
4265         draw_update ( vw );
4266     }
4267     else {
4268       // NB is in UTM mode ATM
4269       save_image_dir ( vw, fn,
4270                        vw->draw_image_width = gtk_spin_button_get_value_as_int ( GTK_SPIN_BUTTON(width_spin) ),
4271                        vw->draw_image_height = gtk_spin_button_get_value_as_int ( GTK_SPIN_BUTTON(height_spin) ),
4272                        zoom,
4273                        vw->draw_image_save_as_png = gtk_toggle_button_get_active ( GTK_TOGGLE_BUTTON(png_radio) ),
4274                        gtk_spin_button_get_value ( GTK_SPIN_BUTTON(tiles_width_spin) ),
4275                        gtk_spin_button_get_value ( GTK_SPIN_BUTTON(tiles_height_spin) ) );
4276     }
4277
4278     g_free ( fn );
4279   }
4280   gtk_widget_destroy ( GTK_WIDGET(dialog) );
4281 }
4282
4283 static void draw_to_kmz_file_cb ( GtkAction *a, VikWindow *vw )
4284 {
4285   if ( vik_viewport_get_coord_mode(vw->viking_vvp) == VIK_COORD_UTM ) {
4286     a_dialog_error_msg ( GTK_WINDOW(vw), _("This feature is not available in UTM mode") );
4287     return;
4288   }
4289   // NB ATM This only generates a KMZ file with the current viewport image - intended mostly for map images [but will include any lines/icons from track & waypoints that are drawn]
4290   // (it does *not* include a full KML dump of every track, waypoint etc...)
4291   draw_to_image_file ( vw, VW_GEN_KMZ_FILE );
4292 }
4293
4294 static void draw_to_image_file_cb ( GtkAction *a, VikWindow *vw )
4295 {
4296   draw_to_image_file ( vw, VW_GEN_SINGLE_IMAGE );
4297 }
4298
4299 static void draw_to_image_dir_cb ( GtkAction *a, VikWindow *vw )
4300 {
4301   draw_to_image_file ( vw, VW_GEN_DIRECTORY_OF_IMAGES );
4302 }
4303
4304 /**
4305  *
4306  */
4307 static void import_kmz_file_cb ( GtkAction *a, VikWindow *vw )
4308 {
4309   GtkWidget *dialog = gtk_file_chooser_dialog_new (_("Open File"),
4310                                                    GTK_WINDOW(vw),
4311                                                    GTK_FILE_CHOOSER_ACTION_OPEN,
4312                                                    GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
4313                                                    GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT,
4314                                                    NULL);
4315
4316   GtkFileFilter *filter;
4317   filter = gtk_file_filter_new ();
4318   gtk_file_filter_set_name ( filter, _("KMZ") );
4319   gtk_file_filter_add_mime_type ( filter, "vnd.google-earth.kmz");
4320   gtk_file_filter_add_pattern ( filter, "*.kmz" );
4321   gtk_file_chooser_add_filter ( GTK_FILE_CHOOSER(dialog), filter );
4322   gtk_file_chooser_set_filter ( GTK_FILE_CHOOSER(dialog), filter );
4323
4324   filter = gtk_file_filter_new ();
4325   gtk_file_filter_set_name( filter, _("All") );
4326   gtk_file_filter_add_pattern ( filter, "*" );
4327   gtk_file_chooser_add_filter ( GTK_FILE_CHOOSER(dialog), filter );
4328   // Default to any file - same as before open filters were added
4329   gtk_file_chooser_set_filter ( GTK_FILE_CHOOSER(dialog), filter );
4330
4331   if ( gtk_dialog_run ( GTK_DIALOG(dialog) ) == GTK_RESPONSE_ACCEPT )  {
4332     gchar *fn = gtk_file_chooser_get_filename ( GTK_FILE_CHOOSER(dialog) );
4333     // TODO convert ans value into readable explaination of failure...
4334     int ans = kmz_open_file ( fn, vw->viking_vvp, vw->viking_vlp );
4335     if ( ans )
4336       a_dialog_error_msg_extra ( GTK_WINDOW(vw), _("Unable to import %s."), fn );
4337
4338     draw_update ( vw );
4339   }
4340   gtk_widget_destroy ( dialog );
4341 }
4342
4343 static void print_cb ( GtkAction *a, VikWindow *vw )
4344 {
4345   a_print(vw, vw->viking_vvp);
4346 }
4347
4348 /* really a misnomer: changes coord mode (actual coordinates) AND/OR draw mode (viewport only) */
4349 static void window_change_coord_mode_cb ( GtkAction *old_a, GtkAction *a, VikWindow *vw )
4350 {
4351   const gchar *name = gtk_action_get_name(a);
4352   GtkToggleToolButton *tbutton = (GtkToggleToolButton *)toolbar_get_widget_by_name ( vw->viking_vtb, name );
4353   if ( tbutton )
4354     gtk_toggle_tool_button_set_active ( tbutton, TRUE );
4355
4356   VikViewportDrawMode drawmode;
4357   if (!g_strcmp0(name, "ModeUTM")) {
4358     drawmode = VIK_VIEWPORT_DRAWMODE_UTM;
4359   }
4360   else if (!g_strcmp0(name, "ModeLatLon")) {
4361     drawmode = VIK_VIEWPORT_DRAWMODE_LATLON;
4362   }
4363   else if (!g_strcmp0(name, "ModeExpedia")) {
4364     drawmode = VIK_VIEWPORT_DRAWMODE_EXPEDIA;
4365   }
4366   else if (!g_strcmp0(name, "ModeMercator")) {
4367     drawmode = VIK_VIEWPORT_DRAWMODE_MERCATOR;
4368   }
4369   else {
4370     g_critical("Houston, we've had a problem.");
4371     return;
4372   }
4373
4374   if ( !vw->only_updating_coord_mode_ui )
4375   {
4376     VikViewportDrawMode olddrawmode = vik_viewport_get_drawmode ( vw->viking_vvp );
4377     if ( olddrawmode != drawmode )
4378     {
4379       /* this takes care of coord mode too */
4380       vik_viewport_set_drawmode ( vw->viking_vvp, drawmode );
4381       if ( drawmode == VIK_VIEWPORT_DRAWMODE_UTM ) {
4382         vik_layers_panel_change_coord_mode ( vw->viking_vlp, VIK_COORD_UTM );
4383       } else if ( olddrawmode == VIK_VIEWPORT_DRAWMODE_UTM ) {
4384         vik_layers_panel_change_coord_mode ( vw->viking_vlp, VIK_COORD_LATLON );
4385       }
4386       draw_update ( vw );
4387     }
4388   }
4389 }
4390
4391 static void toggle_draw_scale ( GtkAction *a, VikWindow *vw )
4392 {
4393   gboolean state = !vik_viewport_get_draw_scale ( vw->viking_vvp );
4394   GtkWidget *check_box = gtk_ui_manager_get_widget ( vw->uim, "/ui/MainMenu/View/SetShow/ShowScale" );
4395   if ( !check_box )
4396     return;
4397   gtk_check_menu_item_set_active ( GTK_CHECK_MENU_ITEM(check_box), state );
4398   vik_viewport_set_draw_scale ( vw->viking_vvp, state );
4399   draw_update ( vw );
4400 }
4401
4402 static void toggle_draw_centermark ( GtkAction *a, VikWindow *vw )
4403 {
4404   gboolean state = !vik_viewport_get_draw_centermark ( vw->viking_vvp );
4405   GtkWidget *check_box = gtk_ui_manager_get_widget ( vw->uim, "/ui/MainMenu/View/SetShow/ShowCenterMark" );
4406   if ( !check_box )
4407     return;
4408   gtk_check_menu_item_set_active ( GTK_CHECK_MENU_ITEM(check_box), state );
4409   vik_viewport_set_draw_centermark ( vw->viking_vvp, state );
4410   draw_update ( vw );
4411 }
4412
4413 static void toggle_draw_highlight ( GtkAction *a, VikWindow *vw )
4414 {
4415   gboolean state = !vik_viewport_get_draw_highlight ( vw->viking_vvp );
4416   GtkWidget *check_box = gtk_ui_manager_get_widget ( vw->uim, "/ui/MainMenu/View/SetShow/ShowHighlight" );
4417   if ( !check_box )
4418     return;
4419   gtk_check_menu_item_set_active ( GTK_CHECK_MENU_ITEM(check_box), state );
4420   vik_viewport_set_draw_highlight ( vw->viking_vvp, state );
4421   draw_update ( vw );
4422 }
4423
4424 static void set_bg_color ( GtkAction *a, VikWindow *vw )
4425 {
4426   GtkWidget *colorsd = gtk_color_selection_dialog_new ( _("Choose a background color") );
4427   GdkColor *color = vik_viewport_get_background_gdkcolor ( vw->viking_vvp );
4428   gtk_color_selection_set_previous_color ( GTK_COLOR_SELECTION(gtk_color_selection_dialog_get_color_selection(GTK_COLOR_SELECTION_DIALOG(colorsd))), color );
4429   gtk_color_selection_set_current_color ( GTK_COLOR_SELECTION(gtk_color_selection_dialog_get_color_selection(GTK_COLOR_SELECTION_DIALOG(colorsd))), color );
4430   if ( gtk_dialog_run ( GTK_DIALOG(colorsd) ) == GTK_RESPONSE_OK )
4431   {
4432     gtk_color_selection_get_current_color ( GTK_COLOR_SELECTION(gtk_color_selection_dialog_get_color_selection(GTK_COLOR_SELECTION_DIALOG(colorsd))), color );
4433     vik_viewport_set_background_gdkcolor ( vw->viking_vvp, color );
4434     draw_update ( vw );
4435   }
4436   g_free ( color );
4437   gtk_widget_destroy ( colorsd );
4438 }
4439
4440 static void set_highlight_color ( GtkAction *a, VikWindow *vw )
4441 {
4442   GtkWidget *colorsd = gtk_color_selection_dialog_new ( _("Choose a track highlight color") );
4443   GdkColor *color = vik_viewport_get_highlight_gdkcolor ( vw->viking_vvp );
4444   gtk_color_selection_set_previous_color ( GTK_COLOR_SELECTION(gtk_color_selection_dialog_get_color_selection(GTK_COLOR_SELECTION_DIALOG(colorsd))), color );
4445   gtk_color_selection_set_current_color ( GTK_COLOR_SELECTION(gtk_color_selection_dialog_get_color_selection(GTK_COLOR_SELECTION_DIALOG(colorsd))), color );
4446   if ( gtk_dialog_run ( GTK_DIALOG(colorsd) ) == GTK_RESPONSE_OK )
4447   {
4448     gtk_color_selection_get_current_color ( GTK_COLOR_SELECTION(gtk_color_selection_dialog_get_color_selection(GTK_COLOR_SELECTION_DIALOG(colorsd))), color );
4449     vik_viewport_set_highlight_gdkcolor ( vw->viking_vvp, color );
4450     draw_update ( vw );
4451   }
4452   g_free ( color );
4453   gtk_widget_destroy ( colorsd );
4454 }
4455
4456
4457 /***********************************************************************************************
4458  ** GUI Creation
4459  ***********************************************************************************************/
4460 typedef struct {
4461   const GdkPixdata *data;
4462   const gchar *name;
4463 } icon_definition_t;
4464
4465 static void
4466 a_register_icon ( GtkIconFactory *icon_factory, icon_definition_t icon )
4467 {
4468   if ( !icon.data ) return;
4469   GtkIconSet *icon_set = gtk_icon_set_new_from_pixbuf (gdk_pixbuf_from_pixdata ( icon.data, FALSE, NULL ));
4470   gtk_icon_factory_add ( icon_factory, icon.name, icon_set );
4471   gtk_icon_set_unref ( icon_set );
4472 }
4473
4474
4475 static GtkActionEntry entries[] = {
4476   { "File", NULL, N_("_File"), 0, 0, 0 },
4477   { "Edit", NULL, N_("_Edit"), 0, 0, 0 },
4478   { "View", NULL, N_("_View"), 0, 0, 0 },
4479   { "SetShow", NULL, N_("_Show"), 0, 0, 0 },
4480   { "SetZoom", NULL, N_("_Zoom"), 0, 0, 0 },
4481   { "SetPan", NULL, N_("_Pan"), 0, 0, 0 },
4482   { "Layers", NULL, N_("_Layers"), 0, 0, 0 },
4483   { "Tools", NULL, N_("_Tools"), 0, 0, 0 },
4484   { "Exttools", NULL, N_("_Webtools"), 0, 0, 0 },
4485   { "Help", NULL, N_("_Help"), 0, 0, 0 },
4486
4487   { "New",       GTK_STOCK_NEW,          N_("_New"),                          "<control>N", N_("New file"),                                     (GCallback)newwindow_cb          },
4488   { "Open",      GTK_STOCK_OPEN,         N_("_Open..."),                         "<control>O", N_("Open a file"),                                  (GCallback)load_file             },
4489   { "OpenRecentFile", NULL,              N_("Open _Recent File"),         NULL,         NULL,                                               (GCallback)NULL },
4490   { "Append",    GTK_STOCK_ADD,          N_("Append _File..."),           NULL,         N_("Append data from a different file"),            (GCallback)load_file             },
4491   { "Export",    GTK_STOCK_CONVERT,      N_("_Export All"),               NULL,         N_("Export All TrackWaypoint Layers"),              (GCallback)NULL                  },
4492   { "ExportGPX", NULL,                   N_("_GPX..."),                       NULL,         N_("Export as GPX"),                                (GCallback)export_to_gpx         },
4493   { "Acquire",   GTK_STOCK_GO_DOWN,      N_("A_cquire"),                  NULL,         NULL,                                               (GCallback)NULL },
4494   { "AcquireRouting",   NULL,             N_("_Directions..."),     NULL,         N_("Get driving directions"),           (GCallback)acquire_from_routing   },
4495 #ifdef VIK_CONFIG_OPENSTREETMAP
4496   { "AcquireOSM",   NULL,                 N_("_OSM Traces..."),           NULL,         N_("Get traces from OpenStreetMap"),            (GCallback)acquire_from_osm       },
4497   { "AcquireMyOSM", NULL,                 N_("_My OSM Traces..."),        NULL,         N_("Get Your Own Traces from OpenStreetMap"),   (GCallback)acquire_from_my_osm    },
4498 #endif
4499 #ifdef VIK_CONFIG_GEOCACHES
4500   { "AcquireGC",   NULL,                 N_("Geo_caches..."),             NULL,         N_("Get Geocaches from geocaching.com"),            (GCallback)acquire_from_gc       },
4501 #endif
4502 #ifdef VIK_CONFIG_GEOTAG
4503   { "AcquireGeotag", NULL,               N_("From Geotagged _Images..."), NULL,         N_("Create waypoints from geotagged images"),       (GCallback)acquire_from_geotag   },
4504 #endif
4505   { "AcquireURL", NULL,                  N_("From _URL..."),              NULL,         N_("Get a file from a URL"),                        (GCallback)acquire_from_url },
4506 #ifdef VIK_CONFIG_GEONAMES
4507   { "AcquireWikipedia", NULL,            N_("From _Wikipedia Waypoints"), NULL,         N_("Create waypoints from Wikipedia items in the current view"), (GCallback)acquire_from_wikipedia },
4508 #endif
4509   { "Save",      GTK_STOCK_SAVE,         N_("_Save"),                         "<control>S", N_("Save the file"),                                (GCallback)save_file             },
4510   { "SaveAs",    GTK_STOCK_SAVE_AS,      N_("Save _As..."),                      NULL,  N_("Save the file under different name"),           (GCallback)save_file_as          },
4511   { "FileProperties", NULL,              N_("Properties..."),                    NULL,  N_("File Properties"),                              (GCallback)file_properties_cb },
4512 #ifdef HAVE_ZIP_H
4513   { "ImportKMZ", GTK_STOCK_CONVERT,      N_("Import KMZ _Map File..."),        NULL,  N_("Import a KMZ file"), (GCallback)import_kmz_file_cb },
4514   { "GenKMZ",    GTK_STOCK_DND,          N_("Generate _KMZ Map File..."),        NULL,  N_("Generate a KMZ file with an overlay of the current view"), (GCallback)draw_to_kmz_file_cb },
4515 #endif
4516   { "GenImg",    GTK_STOCK_FILE,         N_("_Generate Image File..."),          NULL,  N_("Save a snapshot of the workspace into a file"), (GCallback)draw_to_image_file_cb },
4517   { "GenImgDir", GTK_STOCK_DND_MULTIPLE, N_("Generate _Directory of Images..."), NULL,  N_("Generate _Directory of Images"),                (GCallback)draw_to_image_dir_cb },
4518   { "Print",    GTK_STOCK_PRINT,        N_("_Print..."),          NULL,         N_("Print maps"), (GCallback)print_cb },
4519   { "Exit",      GTK_STOCK_QUIT,         N_("E_xit"),                         "<control>W", N_("Exit the program"),                             (GCallback)window_close          },
4520   { "SaveExit",  GTK_STOCK_QUIT,         N_("Save and Exit"),                 NULL, N_("Save and Exit the program"),                             (GCallback)save_file_and_exit          },
4521
4522   { "GoBack",    GTK_STOCK_GO_BACK,      N_("Go to the Pre_vious Location"),  NULL,         N_("Go to the previous location"),              (GCallback)draw_goto_back_and_forth },
4523   { "GoForward", GTK_STOCK_GO_FORWARD,   N_("Go to the _Next Location"),      NULL,         N_("Go to the next location"),                  (GCallback)draw_goto_back_and_forth },
4524   { "GotoDefaultLocation", GTK_STOCK_HOME, N_("Go to the _Default Location"),  NULL,         N_("Go to the default location"),                     (GCallback)goto_default_location },
4525   { "GotoSearch", GTK_STOCK_JUMP_TO,     N_("Go to _Location..."),            NULL,         N_("Go to address/place using text search"),        (GCallback)goto_address       },
4526   { "GotoLL",    GTK_STOCK_JUMP_TO,      N_("_Go to Lat/Lon..."),           NULL,         N_("Go to arbitrary lat/lon coordinate"),         (GCallback)draw_goto_cb          },
4527   { "GotoUTM",   GTK_STOCK_JUMP_TO,      N_("Go to UTM..."),                  NULL,         N_("Go to arbitrary UTM coordinate"),               (GCallback)draw_goto_cb          },
4528   { "Refresh",   GTK_STOCK_REFRESH,      N_("_Refresh"),                      "F5",         N_("Refresh any maps displayed"),               (GCallback)draw_refresh_cb       },
4529   { "SetHLColor",GTK_STOCK_SELECT_COLOR, N_("Set _Highlight Color..."),       NULL,         N_("Set Highlight Color"),                      (GCallback)set_highlight_color },
4530   { "SetBGColor",GTK_STOCK_SELECT_COLOR, N_("Set Bac_kground Color..."),      NULL,         N_("Set Background Color"),                     (GCallback)set_bg_color },
4531   { "ZoomIn",    GTK_STOCK_ZOOM_IN,      N_("Zoom _In"),                  "<control>plus",  N_("Zoom In"),                                  (GCallback)draw_zoom_cb },
4532   { "ZoomOut",   GTK_STOCK_ZOOM_OUT,     N_("Zoom _Out"),                 "<control>minus", N_("Zoom Out"),                                 (GCallback)draw_zoom_cb },
4533   { "ZoomTo",    GTK_STOCK_ZOOM_FIT,     N_("Zoom _To..."),               "<control>Z",     N_("Zoom To"),                                  (GCallback)zoom_to_cb },
4534   { "PanNorth",  NULL,                   N_("Pan _North"),                "<control>Up",    NULL,                                           (GCallback)draw_pan_cb },
4535   { "PanEast",   NULL,                   N_("Pan _East"),                 "<control>Right", NULL,                                           (GCallback)draw_pan_cb },
4536   { "PanSouth",  NULL,                   N_("Pan _South"),                "<control>Down",  NULL,                                           (GCallback)draw_pan_cb },
4537   { "PanWest",   NULL,                   N_("Pan _West"),                 "<control>Left",  NULL,                                           (GCallback)draw_pan_cb },
4538   { "BGJobs",    GTK_STOCK_EXECUTE,      N_("Background _Jobs"),              NULL,         N_("Background Jobs"),                          (GCallback)a_background_show_window },
4539
4540   { "Cut",       GTK_STOCK_CUT,          N_("Cu_t"),                          NULL,         N_("Cut selected layer"),                       (GCallback)menu_cut_layer_cb     },
4541   { "Copy",      GTK_STOCK_COPY,         N_("_Copy"),                         NULL,         N_("Copy selected layer"),                      (GCallback)menu_copy_layer_cb    },
4542   { "Paste",     GTK_STOCK_PASTE,        N_("_Paste"),                        NULL,         N_("Paste layer into selected container layer or otherwise above selected layer"), (GCallback)menu_paste_layer_cb },
4543   { "Delete",    GTK_STOCK_DELETE,       N_("_Delete"),                       NULL,         N_("Remove selected layer"),                    (GCallback)menu_delete_layer_cb  },
4544   { "DeleteAll", NULL,                   N_("Delete All"),                    NULL,         NULL,                                           (GCallback)clear_cb              },
4545   { "CopyCentre",NULL,                   N_("Copy Centre _Location"),     "<control>h",     NULL,                                           (GCallback)menu_copy_centre_cb   },
4546   { "MapCacheFlush",NULL,                N_("_Flush Map Cache"),              NULL,         NULL,                                           (GCallback)mapcache_flush_cb     },
4547   { "SetDefaultLocation", GTK_STOCK_GO_FORWARD, N_("_Set the Default Location"), NULL, N_("Set the Default Location to the current position"),(GCallback)default_location_cb },
4548   { "Preferences",GTK_STOCK_PREFERENCES, N_("_Preferences"),                  NULL,         N_("Program Preferences"),                      (GCallback)preferences_cb },
4549   { "LayerDefaults",GTK_STOCK_PROPERTIES, N_("_Layer Defaults"),             NULL,         NULL,                                           NULL },
4550   { "Properties",GTK_STOCK_PROPERTIES,   N_("_Properties"),                   NULL,         N_("Layer Properties"),                         (GCallback)menu_properties_cb },
4551
4552   { "HelpEntry", GTK_STOCK_HELP,         N_("_Help"),                         "F1",         N_("Help"),                                     (GCallback)help_help_cb },
4553   { "About",     GTK_STOCK_ABOUT,        N_("_About"),                        NULL,         N_("About"),                                    (GCallback)help_about_cb },
4554 };
4555
4556 static GtkActionEntry debug_entries[] = {
4557   { "MapCacheInfo", NULL,                "_Map Cache Info",                   NULL,         NULL,                                           (GCallback)help_cache_info_cb    },
4558   { "BackForwardInfo", NULL,             "_Back/Forward Info",                NULL,         NULL,                                           (GCallback)back_forward_info_cb  },
4559 };
4560
4561 static GtkActionEntry entries_gpsbabel[] = {
4562   { "ExportKML", NULL,                   N_("_KML..."),                       NULL,         N_("Export as KML"),                                (GCallback)export_to_kml },
4563   { "AcquireGPS",   NULL,                N_("From _GPS..."),              NULL,         N_("Transfer data from a GPS device"),              (GCallback)acquire_from_gps      },
4564   { "AcquireGPSBabel", NULL,             N_("Import File With GPS_Babel..."), NULL,     N_("Import file via GPSBabel converter"),           (GCallback)acquire_from_file },
4565 };
4566
4567 static GtkActionEntry entries_geojson[] = {
4568   { "AcquireGeoJSON",   NULL,            N_("Import Geo_JSON File..."),   NULL,         N_("Import GeoJSON file"),                          (GCallback)acquire_from_geojson },
4569 };
4570
4571 /* Radio items */
4572 static GtkRadioActionEntry mode_entries[] = {
4573   { "ModeUTM",         NULL,         N_("_UTM Mode"),               "<control>u", NULL, VIK_VIEWPORT_DRAWMODE_UTM },
4574   { "ModeExpedia",     NULL,         N_("_Expedia Mode"),           "<control>e", NULL, VIK_VIEWPORT_DRAWMODE_EXPEDIA },
4575   { "ModeMercator",    NULL,         N_("_Mercator Mode"),          "<control>m", NULL, VIK_VIEWPORT_DRAWMODE_MERCATOR },
4576   { "ModeLatLon",      NULL,         N_("Lat_/Lon Mode"),           "<control>l", NULL, VIK_VIEWPORT_DRAWMODE_LATLON },
4577 };
4578
4579 static GtkToggleActionEntry toggle_entries[] = {
4580   { "ShowScale",      NULL,                 N_("Show _Scale"),               "<shift>F5",  N_("Show Scale"),                              (GCallback)toggle_draw_scale, TRUE },
4581   { "ShowCenterMark", NULL,                 N_("Show _Center Mark"),         "F6",         N_("Show Center Mark"),                        (GCallback)toggle_draw_centermark, TRUE },
4582   { "ShowHighlight",  GTK_STOCK_UNDERLINE,  N_("Show _Highlight"),           "F7",         N_("Show Highlight"),                          (GCallback)toggle_draw_highlight, TRUE },
4583   { "FullScreen",     GTK_STOCK_FULLSCREEN, N_("_Full Screen"),              "F11",        N_("Activate full screen mode"),               (GCallback)full_screen_cb, FALSE },
4584   { "ViewSidePanel",  GTK_STOCK_INDEX,      N_("Show Side _Panel"),          "F9",         N_("Show Side Panel"),                         (GCallback)view_side_panel_cb, TRUE },
4585   { "ViewStatusBar",  NULL,                 N_("Show Status_bar"),           "F12",        N_("Show Statusbar"),                          (GCallback)view_statusbar_cb, TRUE },
4586   { "ViewToolbar",    NULL,                 N_("Show _Toolbar"),             "F3",         N_("Show Toolbar"),                            (GCallback)view_toolbar_cb, TRUE },
4587   { "ViewMainMenu",   NULL,                 N_("Show _Menu"),                "F4",         N_("Show Menu"),                               (GCallback)view_main_menu_cb, TRUE },
4588 };
4589
4590 // This must match the toggle entries order above
4591 static gpointer toggle_entries_toolbar_cb[] = {
4592   (GCallback)tb_set_draw_scale,
4593   (GCallback)tb_set_draw_centermark,
4594   (GCallback)tb_set_draw_highlight,
4595   (GCallback)tb_full_screen_cb,
4596   (GCallback)tb_view_side_panel_cb,
4597   (GCallback)tb_view_statusbar_cb,
4598   (GCallback)tb_view_toolbar_cb,
4599   (GCallback)tb_view_main_menu_cb,
4600 };
4601
4602 #include "menu.xml.h"
4603 static void window_create_ui( VikWindow *window )
4604 {
4605   GtkUIManager *uim;
4606   GtkActionGroup *action_group;
4607   GtkAccelGroup *accel_group;
4608   GError *error;
4609   guint i, j, mid;
4610   GtkIconFactory *icon_factory;
4611   GtkRadioActionEntry *tools = NULL, *radio;
4612   guint ntools;
4613   
4614   uim = gtk_ui_manager_new ();
4615   window->uim = uim;
4616
4617   toolbox_add_tool(window->vt, &ruler_tool, TOOL_LAYER_TYPE_NONE);
4618   toolbox_add_tool(window->vt, &zoom_tool, TOOL_LAYER_TYPE_NONE);
4619   toolbox_add_tool(window->vt, &pan_tool, TOOL_LAYER_TYPE_NONE);
4620   toolbox_add_tool(window->vt, &select_tool, TOOL_LAYER_TYPE_NONE);
4621
4622   toolbar_action_tool_entry_register ( window->viking_vtb, &pan_tool.radioActionEntry );
4623   toolbar_action_tool_entry_register ( window->viking_vtb, &zoom_tool.radioActionEntry );
4624   toolbar_action_tool_entry_register ( window->viking_vtb, &ruler_tool.radioActionEntry );
4625   toolbar_action_tool_entry_register ( window->viking_vtb, &select_tool.radioActionEntry );
4626
4627   error = NULL;
4628   if (!(mid = gtk_ui_manager_add_ui_from_string (uim, menu_xml, -1, &error))) {
4629     g_error_free (error);
4630     exit (1);
4631   }
4632
4633   action_group = gtk_action_group_new ("MenuActions");
4634   gtk_action_group_set_translation_domain(action_group, PACKAGE_NAME);
4635   gtk_action_group_add_actions (action_group, entries, G_N_ELEMENTS (entries), window);
4636   gtk_action_group_add_toggle_actions (action_group, toggle_entries, G_N_ELEMENTS (toggle_entries), window);
4637   gtk_action_group_add_radio_actions (action_group, mode_entries, G_N_ELEMENTS (mode_entries), 4, (GCallback)window_change_coord_mode_cb, window);
4638   if ( vik_debug ) {
4639     if ( gtk_ui_manager_add_ui_from_string ( uim,
4640          "<ui><menubar name='MainMenu'><menu action='Help'>"
4641            "<menuitem action='MapCacheInfo'/>"
4642            "<menuitem action='BackForwardInfo'/>"
4643          "</menu></menubar></ui>",
4644          -1, NULL ) ) {
4645       gtk_action_group_add_actions (action_group, debug_entries, G_N_ELEMENTS (debug_entries), window);
4646     }
4647   }
4648
4649   for ( i=0; i < G_N_ELEMENTS (entries); i++ ) {
4650     if ( entries[i].callback )
4651       toolbar_action_entry_register ( window->viking_vtb, &entries[i] );
4652   }
4653
4654   if ( G_N_ELEMENTS (toggle_entries) !=  G_N_ELEMENTS (toggle_entries_toolbar_cb) ) {
4655     g_print ( "Broken entries definitions\n" );
4656     exit (1);
4657   }
4658   for ( i=0; i < G_N_ELEMENTS (toggle_entries); i++ ) {
4659     if ( toggle_entries_toolbar_cb[i] )
4660       toolbar_action_toggle_entry_register ( window->viking_vtb, &toggle_entries[i], toggle_entries_toolbar_cb[i] );
4661   }
4662
4663   for ( i=0; i < G_N_ELEMENTS (mode_entries); i++ ) {
4664     toolbar_action_mode_entry_register ( window->viking_vtb, &mode_entries[i] );
4665   }
4666
4667   // Use this to see if GPSBabel is available:
4668   if ( a_babel_available () ) {
4669     // If going to add more entries then might be worth creating a menu_gpsbabel.xml.h file
4670     if ( gtk_ui_manager_add_ui_from_string ( uim,
4671          "<ui>" \
4672          "<menubar name='MainMenu'>" \
4673          "<menu action='File'><menu action='Export'><menuitem action='ExportKML'/></menu></menu>" \
4674          "<menu action='File'><menu action='Acquire'><menuitem action='AcquireGPS'/></menu></menu>" \
4675          "<menu action='File'><menu action='Acquire'><menuitem action='AcquireGPSBabel'/></menu></menu>" \
4676          "</menubar>" \
4677          "</ui>",
4678          -1, &error ) )
4679       gtk_action_group_add_actions ( action_group, entries_gpsbabel, G_N_ELEMENTS (entries_gpsbabel), window );
4680   }
4681
4682   // GeoJSON import capability
4683   if ( g_find_program_in_path ( a_geojson_program_import() ) ) {
4684     if ( gtk_ui_manager_add_ui_from_string ( uim,
4685          "<ui><menubar name='MainMenu'><menu action='File'><menu action='Acquire'><menuitem action='AcquireGeoJSON'/></menu></menu></menubar></ui>",
4686          -1, &error ) )
4687       gtk_action_group_add_actions ( action_group, entries_geojson, G_N_ELEMENTS (entries_geojson), window );
4688   }
4689
4690   icon_factory = gtk_icon_factory_new ();
4691   gtk_icon_factory_add_default (icon_factory); 
4692
4693   register_vik_icons(icon_factory);
4694
4695   // Copy the tool RadioActionEntries out of the main Window structure into an extending array 'tools'
4696   //  so that it can be applied to the UI in one action group add function call below
4697   ntools = 0;
4698   for (i=0; i<window->vt->n_tools; i++) {
4699       tools = g_renew(GtkRadioActionEntry, tools, ntools+1);
4700       radio = &tools[ntools];
4701       ntools++;
4702       *radio = window->vt->tools[i].ti.radioActionEntry;
4703       radio->value = ntools;
4704   }
4705
4706   for (i=0; i<VIK_LAYER_NUM_TYPES; i++) {
4707     GtkActionEntry action;
4708     gtk_ui_manager_add_ui(uim, mid,  "/ui/MainMenu/Layers/", 
4709                           vik_layer_get_interface(i)->fixed_layer_name,
4710                           vik_layer_get_interface(i)->fixed_layer_name,
4711                           GTK_UI_MANAGER_MENUITEM, FALSE);
4712
4713     icon_definition_t icon = { vik_layer_get_interface(i)->icon,
4714                                vik_layer_get_interface(i)->fixed_layer_name };
4715     a_register_icon ( icon_factory, icon );
4716
4717     action.name = vik_layer_get_interface(i)->fixed_layer_name;
4718     action.stock_id = vik_layer_get_interface(i)->fixed_layer_name;
4719     action.label = g_strdup_printf( _("New _%s Layer"), _(vik_layer_get_interface(i)->name));
4720     action.accelerator = vik_layer_get_interface(i)->accelerator;
4721     action.tooltip = NULL;
4722     action.callback = (GCallback)menu_addlayer_cb;
4723     gtk_action_group_add_actions(action_group, &action, 1, window);
4724
4725     g_free ( (gchar*)action.label );
4726
4727     if ( vik_layer_get_interface(i)->tools_count ) {
4728       gtk_ui_manager_add_ui ( uim, mid,  "/ui/MainMenu/Tools/",
4729                               vik_layer_get_interface(i)->fixed_layer_name,
4730                               vik_layer_get_interface(i)->fixed_layer_name,
4731                               GTK_UI_MANAGER_SEPARATOR, FALSE );
4732     }
4733
4734     // Further tool copying for to apply to the UI, also apply menu UI setup
4735     for ( j = 0; j < vik_layer_get_interface(i)->tools_count; j++ ) {
4736       tools = g_renew(GtkRadioActionEntry, tools, ntools+1);
4737       radio = &tools[ntools];
4738       ntools++;
4739
4740       icon_definition_t icon = { vik_layer_get_interface(i)->tools[j].icon,
4741                                  vik_layer_get_interface(i)->tools[j].radioActionEntry.stock_id };
4742       a_register_icon ( icon_factory, icon );
4743
4744       gtk_ui_manager_add_ui(uim, mid,  "/ui/MainMenu/Tools", 
4745                             vik_layer_get_interface(i)->tools[j].radioActionEntry.label,
4746                             vik_layer_get_interface(i)->tools[j].radioActionEntry.name,
4747                             GTK_UI_MANAGER_MENUITEM, FALSE);
4748
4749       toolbox_add_tool(window->vt, &(vik_layer_get_interface(i)->tools[j]), i);
4750       toolbar_action_tool_entry_register ( window->viking_vtb, &(vik_layer_get_interface(i)->tools[j].radioActionEntry) );
4751
4752       *radio = vik_layer_get_interface(i)->tools[j].radioActionEntry;
4753       // Overwrite with actual number to use
4754       radio->value = ntools;
4755     }
4756
4757     GtkActionEntry action_dl;
4758     gchar *layername = g_strdup_printf ( "Layer%s", vik_layer_get_interface(i)->fixed_layer_name );
4759     gtk_ui_manager_add_ui(uim, mid,  "/ui/MainMenu/Edit/LayerDefaults",
4760                           vik_layer_get_interface(i)->fixed_layer_name,
4761                           layername,
4762                           GTK_UI_MANAGER_MENUITEM, FALSE);
4763     g_free (layername);
4764
4765     // For default layers use action names of the form 'Layer<LayerName>'
4766     // This is to avoid clashing with just the layer name used above for the tool actions
4767     action_dl.name = g_strconcat("Layer", vik_layer_get_interface(i)->fixed_layer_name, NULL);
4768     action_dl.stock_id = vik_layer_get_interface(i)->fixed_layer_name;
4769     action_dl.label = g_strconcat(_(vik_layer_get_interface(i)->name), _("..."), NULL);
4770     action_dl.accelerator = NULL;
4771     action_dl.tooltip = NULL;
4772     action_dl.callback = (GCallback)layer_defaults_cb;
4773     gtk_action_group_add_actions(action_group, &action_dl, 1, window);
4774     g_free ( (gchar*)action_dl.name );
4775     g_free ( (gchar*)action_dl.label );
4776   }
4777   g_object_unref (icon_factory);
4778
4779   gtk_action_group_add_radio_actions(action_group, tools, ntools, 0, (GCallback)menu_cb, window);
4780   g_free(tools);
4781
4782   gtk_ui_manager_insert_action_group (uim, action_group, 0);
4783
4784   for (i=0; i<VIK_LAYER_NUM_TYPES; i++) {
4785     for ( j = 0; j < vik_layer_get_interface(i)->tools_count; j++ ) {
4786       GtkAction *action = gtk_action_group_get_action(action_group,
4787                             vik_layer_get_interface(i)->tools[j].radioActionEntry.name);
4788       g_object_set(action, "sensitive", FALSE, NULL);
4789     }
4790   }
4791
4792   // This is done last so we don't need to track the value of mid anymore
4793   vik_ext_tools_add_action_items ( window, window->uim, action_group, mid );
4794
4795   window->action_group = action_group;
4796
4797   accel_group = gtk_ui_manager_get_accel_group (uim);
4798   gtk_window_add_accel_group (GTK_WINDOW (window), accel_group);
4799   gtk_ui_manager_ensure_update (uim);
4800   
4801   setup_recent_files(window);
4802
4803   if ( vik_routing_number_of_engines(VIK_ROUTING_METHOD_DIRECTIONS) == 0 ) {
4804     GtkWidget *widget = gtk_ui_manager_get_widget ( uim, "/ui/MainMenu/File/Acquire/AcquireRouting" );
4805     if ( widget ) {
4806       g_object_set ( widget, "sensitive", FALSE, NULL );
4807     }
4808     g_warning ( "No direction routing engines available" );
4809   }
4810 }
4811
4812 static icon_definition_t vik_icons[] = {
4813   // Vikwindow specific icons:
4814   { &mover_22_pixbuf,           "vik-icon-pan"      },
4815   { &zoom_18_pixbuf,            "vik-icon-zoom"     },
4816   { &ruler_18_pixbuf,           "vik-icon-ruler"    },
4817   { &select_18_pixbuf,          "vik-icon-select"   },
4818   // Generic icons for use anywhere:
4819   { &showpic_18_pixbuf,         VIK_ICON_SHOW_PICTURE },
4820 };
4821
4822 static void
4823 register_vik_icons ( GtkIconFactory *icon_factory )
4824 {
4825   for (guint i = 0; i < G_N_ELEMENTS(vik_icons); i++) {
4826     a_register_icon ( icon_factory, vik_icons[i] );
4827   }
4828 }
4829
4830 gpointer vik_window_get_selected_trw_layer ( VikWindow *vw )
4831 {
4832   return vw->selected_vtl;
4833 }
4834
4835 void vik_window_set_selected_trw_layer ( VikWindow *vw, gpointer vtl )
4836 {
4837   vw->selected_vtl   = vtl;
4838   vw->containing_vtl = vtl;
4839   /* Clear others */
4840   vw->selected_track     = NULL;
4841   vw->selected_tracks    = NULL;
4842   vw->selected_waypoint  = NULL;
4843   vw->selected_waypoints = NULL;
4844   // Set highlight thickness
4845   vik_viewport_set_highlight_thickness ( vw->viking_vvp, vik_trw_layer_get_property_tracks_line_thickness (vw->containing_vtl) );
4846 }
4847
4848 GHashTable *vik_window_get_selected_tracks ( VikWindow *vw )
4849 {
4850   return vw->selected_tracks;
4851 }
4852
4853 void vik_window_set_selected_tracks ( VikWindow *vw, GHashTable *ght, gpointer vtl )
4854 {
4855   vw->selected_tracks = ght;
4856   vw->containing_vtl  = vtl;
4857   /* Clear others */
4858   vw->selected_vtl       = NULL;
4859   vw->selected_track     = NULL;
4860   vw->selected_waypoint  = NULL;
4861   vw->selected_waypoints = NULL;
4862   // Set highlight thickness
4863   vik_viewport_set_highlight_thickness ( vw->viking_vvp, vik_trw_layer_get_property_tracks_line_thickness (vw->containing_vtl) );
4864 }
4865
4866 gpointer vik_window_get_selected_track ( VikWindow *vw )
4867 {
4868   return vw->selected_track;
4869 }
4870
4871 void vik_window_set_selected_track ( VikWindow *vw, gpointer *vt, gpointer vtl )
4872 {
4873   vw->selected_track = vt;
4874   vw->containing_vtl = vtl;
4875   /* Clear others */
4876   vw->selected_vtl       = NULL;
4877   vw->selected_tracks    = NULL;
4878   vw->selected_waypoint  = NULL;
4879   vw->selected_waypoints = NULL;
4880   // Set highlight thickness
4881   vik_viewport_set_highlight_thickness ( vw->viking_vvp, vik_trw_layer_get_property_tracks_line_thickness (vw->containing_vtl) );
4882 }
4883
4884 GHashTable *vik_window_get_selected_waypoints ( VikWindow *vw )
4885 {
4886   return vw->selected_waypoints;
4887 }
4888
4889 void vik_window_set_selected_waypoints ( VikWindow *vw, GHashTable *ght, gpointer vtl )
4890 {
4891   vw->selected_waypoints = ght;
4892   vw->containing_vtl     = vtl;
4893   /* Clear others */
4894   vw->selected_vtl       = NULL;
4895   vw->selected_track     = NULL;
4896   vw->selected_tracks    = NULL;
4897   vw->selected_waypoint  = NULL;
4898 }
4899
4900 gpointer vik_window_get_selected_waypoint ( VikWindow *vw )
4901 {
4902   return vw->selected_waypoint;
4903 }
4904
4905 void vik_window_set_selected_waypoint ( VikWindow *vw, gpointer *vwp, gpointer vtl )
4906 {
4907   vw->selected_waypoint = vwp;
4908   vw->containing_vtl    = vtl;
4909   /* Clear others */
4910   vw->selected_vtl       = NULL;
4911   vw->selected_track     = NULL;
4912   vw->selected_tracks    = NULL;
4913   vw->selected_waypoints = NULL;
4914 }
4915
4916 gboolean vik_window_clear_highlight ( VikWindow *vw )
4917 {
4918   gboolean need_redraw = FALSE;
4919   vw->containing_vtl = NULL;
4920   if ( vw->selected_vtl != NULL ) {
4921     vw->selected_vtl = NULL;
4922     need_redraw = TRUE;
4923   }
4924   if ( vw->selected_track != NULL ) {
4925     vw->selected_track = NULL;
4926     need_redraw = TRUE;
4927   }
4928   if ( vw->selected_tracks != NULL ) {
4929     vw->selected_tracks = NULL;
4930     need_redraw = TRUE;
4931   }
4932   if ( vw->selected_waypoint != NULL ) {
4933     vw->selected_waypoint = NULL;
4934     need_redraw = TRUE;
4935   }
4936   if ( vw->selected_waypoints != NULL ) {
4937     vw->selected_waypoints = NULL;
4938     need_redraw = TRUE;
4939   }
4940   return need_redraw;
4941 }
4942
4943 /**
4944  * May return NULL if the window no longer exists
4945  */
4946 GThread *vik_window_get_thread ( VikWindow *vw )
4947 {
4948   if ( vw )
4949     return vw->thread;
4950   return NULL;
4951 }