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