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