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