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