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