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