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