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