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