]> git.street.me.uk Git - andy/viking.git/blob - src/vikwindow.c
Use the last selected date when initializing the date search.
[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   gtk_recent_chooser_set_limit (GTK_RECENT_CHOOSER (menu), a_vik_get_recent_number_files() );
2643
2644   menu_item = gtk_ui_manager_get_widget (self->uim, "/ui/MainMenu/File/OpenRecentFile");
2645   gtk_menu_item_set_submenu (GTK_MENU_ITEM (menu_item), menu);
2646
2647   g_signal_connect (G_OBJECT (menu), "item-activated",
2648                     G_CALLBACK (on_activate_recent_item), (gpointer) self);
2649 }
2650
2651 static void update_recently_used_document(const gchar *filename)
2652 {
2653   /* Update Recently Used Document framework */
2654   GtkRecentManager *manager = gtk_recent_manager_get_default();
2655   GtkRecentData *recent_data = g_slice_new (GtkRecentData);
2656   gchar *groups[] = {"viking", NULL};
2657   GFile *file = g_file_new_for_commandline_arg(filename);
2658   gchar *uri = g_file_get_uri(file);
2659   gchar *basename = g_path_get_basename(filename);
2660   g_object_unref(file);
2661   file = NULL;
2662
2663   recent_data->display_name   = basename;
2664   recent_data->description    = NULL;
2665   recent_data->mime_type      = "text/x-gps-data";
2666   recent_data->app_name       = (gchar *) g_get_application_name ();
2667   recent_data->app_exec       = g_strjoin (" ", g_get_prgname (), "%f", NULL);
2668   recent_data->groups         = groups;
2669   recent_data->is_private     = FALSE;
2670   if (!gtk_recent_manager_add_full (manager, uri, recent_data))
2671   {
2672     g_warning (_("Unable to add '%s' to the list of recently used documents"), uri);
2673   }
2674
2675   g_free (uri);
2676   g_free (basename);
2677   g_free (recent_data->app_exec);
2678   g_slice_free (GtkRecentData, recent_data);
2679 }
2680
2681 /**
2682  * Call this before doing things that may take a long time and otherwise not show any other feedback
2683  *  such as loading and saving files
2684  */
2685 void vik_window_set_busy_cursor ( VikWindow *vw )
2686 {
2687   gdk_window_set_cursor ( gtk_widget_get_window(GTK_WIDGET(vw)), vw->busy_cursor );
2688   // Viewport has a separate cursor
2689   gdk_window_set_cursor ( gtk_widget_get_window(GTK_WIDGET(vw->viking_vvp)), vw->busy_cursor );
2690   // Ensure cursor updated before doing stuff
2691   while( gtk_events_pending() )
2692     gtk_main_iteration();
2693 }
2694
2695 void vik_window_clear_busy_cursor ( VikWindow *vw )
2696 {
2697   gdk_window_set_cursor ( gtk_widget_get_window(GTK_WIDGET(vw)), NULL );
2698   // Restore viewport cursor
2699   gdk_window_set_cursor ( gtk_widget_get_window(GTK_WIDGET(vw->viking_vvp)), vw->viewport_cursor );
2700 }
2701
2702 void vik_window_open_file ( VikWindow *vw, const gchar *filename, gboolean change_filename )
2703 {
2704   vik_window_set_busy_cursor ( vw );
2705
2706   // Enable the *new* filename to be accessible by the Layers codez
2707   gchar *original_filename = g_strdup ( vw->filename );
2708   g_free ( vw->filename );
2709   vw->filename = g_strdup ( filename );
2710   gboolean success = FALSE;
2711   gboolean restore_original_filename = FALSE;
2712
2713   vw->loaded_type = a_file_load ( vik_layers_panel_get_top_layer(vw->viking_vlp), vw->viking_vvp, filename );
2714   switch ( vw->loaded_type )
2715   {
2716     case LOAD_TYPE_READ_FAILURE:
2717       a_dialog_error_msg ( GTK_WINDOW(vw), _("The file you requested could not be opened.") );
2718       break;
2719     case LOAD_TYPE_GPSBABEL_FAILURE:
2720       a_dialog_error_msg ( GTK_WINDOW(vw), _("GPSBabel is required to load files of this type or GPSBabel encountered problems.") );
2721       break;
2722     case LOAD_TYPE_GPX_FAILURE:
2723       a_dialog_error_msg_extra ( GTK_WINDOW(vw), _("Unable to load malformed GPX file %s"), filename );
2724       break;
2725     case LOAD_TYPE_UNSUPPORTED_FAILURE:
2726       a_dialog_error_msg_extra ( GTK_WINDOW(vw), _("Unsupported file type for %s"), filename );
2727       break;
2728     case LOAD_TYPE_VIK_FAILURE_NON_FATAL:
2729     {
2730       // Since we can process .vik files with issues just show a warning in the status bar
2731       // Not that a user can do much about it... or tells them what this issue is yet...
2732       gchar *msg = g_strdup_printf (_("WARNING: issues encountered loading %s"), a_file_basename (filename) );
2733       vik_statusbar_set_message ( vw->viking_vs, VIK_STATUSBAR_INFO, msg );
2734       g_free ( msg );
2735     }
2736       // No break, carry on to show any data
2737     case LOAD_TYPE_VIK_SUCCESS:
2738     {
2739       restore_original_filename = TRUE; // NB Will actually get inverted by the 'success' component below
2740       GtkWidget *mode_button;
2741       /* Update UI */
2742       if ( change_filename )
2743         window_set_filename ( vw, filename );
2744       mode_button = vik_window_get_drawmode_button ( vw, vik_viewport_get_drawmode ( vw->viking_vvp ) );
2745       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. */
2746       gtk_check_menu_item_set_active ( GTK_CHECK_MENU_ITEM(mode_button), TRUE );
2747       vw->only_updating_coord_mode_ui = FALSE;
2748
2749       vik_layers_panel_change_coord_mode ( vw->viking_vlp, vik_viewport_get_coord_mode ( vw->viking_vvp ) );
2750       
2751       mode_button = gtk_ui_manager_get_widget ( vw->uim, "/ui/MainMenu/View/SetShow/ShowScale" );
2752       g_assert ( mode_button );
2753       gtk_check_menu_item_set_active ( GTK_CHECK_MENU_ITEM(mode_button),vik_viewport_get_draw_scale(vw->viking_vvp) );
2754
2755       mode_button = gtk_ui_manager_get_widget ( vw->uim, "/ui/MainMenu/View/SetShow/ShowCenterMark" );
2756       g_assert ( mode_button );
2757       gtk_check_menu_item_set_active ( GTK_CHECK_MENU_ITEM(mode_button),vik_viewport_get_draw_centermark(vw->viking_vvp) );
2758       
2759       mode_button = gtk_ui_manager_get_widget ( vw->uim, "/ui/MainMenu/View/SetShow/ShowHighlight" );
2760       g_assert ( mode_button );
2761       gtk_check_menu_item_set_active ( GTK_CHECK_MENU_ITEM(mode_button),vik_viewport_get_draw_highlight (vw->viking_vvp) );
2762     }
2763       // NB No break, carry on to redraw
2764     //case LOAD_TYPE_OTHER_SUCCESS:
2765     default:
2766       success = TRUE;
2767       // When LOAD_TYPE_OTHER_SUCCESS *only*, this will maintain the existing Viking project
2768       restore_original_filename = ! restore_original_filename;
2769       update_recently_used_document(filename);
2770       draw_update ( vw );
2771       break;
2772   }
2773
2774   if ( ! success || restore_original_filename )
2775     // Load didn't work or want to keep as the existing Viking project, keep using the original name
2776     window_set_filename ( vw, original_filename );
2777   g_free ( original_filename );
2778
2779   vik_window_clear_busy_cursor ( vw );
2780 }
2781
2782 static void load_file ( GtkAction *a, VikWindow *vw )
2783 {
2784   GSList *files = NULL;
2785   GSList *cur_file = NULL;
2786   gboolean newwindow;
2787   if (!strcmp(gtk_action_get_name(a), "Open")) {
2788     newwindow = TRUE;
2789   } 
2790   else if (!strcmp(gtk_action_get_name(a), "Append")) {
2791     newwindow = FALSE;
2792   } 
2793   else {
2794     g_critical("Houston, we've had a problem.");
2795     return;
2796   }
2797     
2798   if ( ! vw->open_dia )
2799   {
2800     vw->open_dia = gtk_file_chooser_dialog_new (_("Please select a GPS data file to open. "),
2801                                                 GTK_WINDOW(vw),
2802                                                 GTK_FILE_CHOOSER_ACTION_OPEN,
2803                                                 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
2804                                                 GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT,
2805                                                 NULL);
2806     gchar *cwd = g_get_current_dir();
2807     if ( cwd ) {
2808       gtk_file_chooser_set_current_folder ( GTK_FILE_CHOOSER(vw->open_dia), cwd );
2809       g_free ( cwd );
2810     }
2811
2812     GtkFileFilter *filter;
2813     // NB file filters are listed this way for alphabetical ordering
2814 #ifdef VIK_CONFIG_GEOCACHES
2815     filter = gtk_file_filter_new ();
2816     gtk_file_filter_set_name( filter, _("Geocaching") );
2817     gtk_file_filter_add_pattern ( filter, "*.loc" ); // No MIME type available
2818     gtk_file_chooser_add_filter (GTK_FILE_CHOOSER(vw->open_dia), filter);
2819 #endif
2820
2821     filter = gtk_file_filter_new ();
2822     gtk_file_filter_set_name( filter, _("Google Earth") );
2823     gtk_file_filter_add_mime_type ( filter, "application/vnd.google-earth.kml+xml");
2824     gtk_file_chooser_add_filter (GTK_FILE_CHOOSER(vw->open_dia), filter);
2825
2826     filter = gtk_file_filter_new ();
2827     gtk_file_filter_set_name( filter, _("GPX") );
2828     gtk_file_filter_add_pattern ( filter, "*.gpx" ); // No MIME type available
2829     gtk_file_chooser_add_filter (GTK_FILE_CHOOSER(vw->open_dia), filter);
2830
2831     filter = gtk_file_filter_new ();
2832     gtk_file_filter_set_name ( filter, _("JPG") );
2833     gtk_file_filter_add_mime_type ( filter, "image/jpeg");
2834     gtk_file_chooser_add_filter (GTK_FILE_CHOOSER(vw->open_dia), filter);
2835
2836     filter = gtk_file_filter_new ();
2837     gtk_file_filter_set_name( filter, _("Viking") );
2838     gtk_file_filter_add_pattern ( filter, "*.vik" );
2839     gtk_file_filter_add_pattern ( filter, "*.viking" );
2840     gtk_file_chooser_add_filter (GTK_FILE_CHOOSER(vw->open_dia), filter);
2841
2842     // NB could have filters for gpspoint (*.gps,*.gpsoint?) + gpsmapper (*.gsm,*.gpsmapper?)
2843     // However assume this are barely used and thus not worthy of inclusion
2844     //   as they'll just make the options too many and have no clear file pattern
2845     //   one can always use the all option
2846     filter = gtk_file_filter_new ();
2847     gtk_file_filter_set_name( filter, _("All") );
2848     gtk_file_filter_add_pattern ( filter, "*" );
2849     gtk_file_chooser_add_filter (GTK_FILE_CHOOSER(vw->open_dia), filter);
2850     // Default to any file - same as before open filters were added
2851     gtk_file_chooser_set_filter (GTK_FILE_CHOOSER(vw->open_dia), filter);
2852
2853     gtk_file_chooser_set_select_multiple ( GTK_FILE_CHOOSER(vw->open_dia), TRUE );
2854     gtk_window_set_transient_for ( GTK_WINDOW(vw->open_dia), GTK_WINDOW(vw) );
2855     gtk_window_set_destroy_with_parent ( GTK_WINDOW(vw->open_dia), TRUE );
2856   }
2857   if ( gtk_dialog_run ( GTK_DIALOG(vw->open_dia) ) == GTK_RESPONSE_ACCEPT )
2858   {
2859     gtk_widget_hide ( vw->open_dia );
2860 #ifdef VIKING_PROMPT_IF_MODIFIED
2861     if ( (vw->modified || vw->filename) && newwindow )
2862 #else
2863     if ( vw->filename && newwindow )
2864 #endif
2865       g_signal_emit ( G_OBJECT(vw), window_signals[VW_OPENWINDOW_SIGNAL], 0, gtk_file_chooser_get_filenames (GTK_FILE_CHOOSER(vw->open_dia) ) );
2866     else {
2867       files = gtk_file_chooser_get_filenames (GTK_FILE_CHOOSER(vw->open_dia) );
2868       gboolean change_fn = newwindow && (g_slist_length(files)==1); /* only change fn if one file */
2869       gboolean first_vik_file = TRUE;
2870       cur_file = files;
2871       while ( cur_file ) {
2872
2873         gchar *file_name = cur_file->data;
2874         if ( newwindow && check_file_magic_vik ( file_name ) ) {
2875           // Load first of many .vik files in current window
2876           if ( first_vik_file ) {
2877             vik_window_open_file ( vw, file_name, TRUE );
2878             first_vik_file = FALSE;
2879           }
2880           else {
2881             // Load each subsequent .vik file in a separate window
2882             VikWindow *newvw = vik_window_new_window ();
2883             if (newvw)
2884               vik_window_open_file ( newvw, file_name, TRUE );
2885           }
2886         }
2887         else
2888           // Other file types
2889           vik_window_open_file ( vw, file_name, change_fn );
2890
2891         g_free (file_name);
2892         cur_file = g_slist_next (cur_file);
2893       }
2894       g_slist_free (files);
2895     }
2896   }
2897   else
2898     gtk_widget_hide ( vw->open_dia );
2899 }
2900
2901 static gboolean save_file_as ( GtkAction *a, VikWindow *vw )
2902 {
2903   gboolean rv = FALSE;
2904   const gchar *fn;
2905   if ( ! vw->save_dia )
2906   {
2907     vw->save_dia = gtk_file_chooser_dialog_new (_("Save as Viking File."),
2908                                                 GTK_WINDOW(vw),
2909                                                 GTK_FILE_CHOOSER_ACTION_SAVE,
2910                                                 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
2911                                                 GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT,
2912                                                 NULL);
2913     gchar *cwd = g_get_current_dir();
2914     if ( cwd ) {
2915       gtk_file_chooser_set_current_folder ( GTK_FILE_CHOOSER(vw->save_dia), cwd );
2916       g_free ( cwd );
2917     }
2918
2919     GtkFileFilter *filter;
2920     filter = gtk_file_filter_new ();
2921     gtk_file_filter_set_name( filter, _("All") );
2922     gtk_file_filter_add_pattern ( filter, "*" );
2923     gtk_file_chooser_add_filter (GTK_FILE_CHOOSER(vw->save_dia), filter);
2924
2925     filter = gtk_file_filter_new ();
2926     gtk_file_filter_set_name( filter, _("Viking") );
2927     gtk_file_filter_add_pattern ( filter, "*.vik" );
2928     gtk_file_filter_add_pattern ( filter, "*.viking" );
2929     gtk_file_chooser_add_filter (GTK_FILE_CHOOSER(vw->save_dia), filter);
2930     // Default to a Viking file
2931     gtk_file_chooser_set_filter (GTK_FILE_CHOOSER(vw->save_dia), filter);
2932
2933     gtk_window_set_transient_for ( GTK_WINDOW(vw->save_dia), GTK_WINDOW(vw) );
2934     gtk_window_set_destroy_with_parent ( GTK_WINDOW(vw->save_dia), TRUE );
2935   }
2936   // Auto append / replace extension with '.vik' to the suggested file name as it's going to be a Viking File
2937   gchar* auto_save_name = g_strdup ( window_get_filename ( vw ) );
2938   if ( ! a_file_check_ext ( auto_save_name, ".vik" ) )
2939     auto_save_name = g_strconcat ( auto_save_name, ".vik", NULL );
2940
2941   gtk_file_chooser_set_current_name (GTK_FILE_CHOOSER(vw->save_dia), auto_save_name);
2942
2943   while ( gtk_dialog_run ( GTK_DIALOG(vw->save_dia) ) == GTK_RESPONSE_ACCEPT )
2944   {
2945     fn = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER(vw->save_dia) );
2946     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 ) ) )
2947     {
2948       window_set_filename ( vw, fn );
2949       rv = window_save ( vw );
2950       vw->modified = FALSE;
2951       break;
2952     }
2953   }
2954   g_free ( auto_save_name );
2955   gtk_widget_hide ( vw->save_dia );
2956   return rv;
2957 }
2958
2959 static gboolean window_save ( VikWindow *vw )
2960 {
2961   vik_window_set_busy_cursor ( vw );
2962   gboolean success = TRUE;
2963
2964   if ( a_file_save ( vik_layers_panel_get_top_layer ( vw->viking_vlp ), vw->viking_vvp, vw->filename ) )
2965   {
2966     update_recently_used_document ( vw->filename );
2967   }
2968   else
2969   {
2970     a_dialog_error_msg ( GTK_WINDOW(vw), _("The filename you requested could not be opened for writing.") );
2971     success = FALSE;
2972   }
2973   vik_window_clear_busy_cursor ( vw );
2974   return success;
2975 }
2976
2977 static gboolean save_file ( GtkAction *a, VikWindow *vw )
2978 {
2979   if ( ! vw->filename )
2980     return save_file_as ( NULL, vw );
2981   else
2982   {
2983     vw->modified = FALSE;
2984     return window_save ( vw );
2985   }
2986 }
2987
2988 /**
2989  * export_to:
2990  *
2991  * Export all TRW Layers in the list to individual files in the specified directory
2992  *
2993  * Returns: %TRUE on success
2994  */
2995 static gboolean export_to ( VikWindow *vw, GList *gl, VikFileType_t vft, const gchar *dir, const gchar *extension )
2996 {
2997   gboolean success = TRUE;
2998
2999   gint export_count = 0;
3000
3001   vik_window_set_busy_cursor ( vw );
3002
3003   while ( gl ) {
3004
3005     gchar *fn = g_strconcat ( dir, G_DIR_SEPARATOR_S, VIK_LAYER(gl->data)->name, extension, NULL );
3006
3007     // Some protection in attempting to write too many same named files
3008     // As this will get horribly slow...
3009     gboolean safe = FALSE;
3010     gint ii = 2;
3011     while ( ii < 5000 ) {
3012       if ( g_file_test ( fn, G_FILE_TEST_EXISTS ) ) {
3013         // Try rename
3014         g_free ( fn );
3015         fn = g_strdup_printf ( "%s%s%s#%03d%s", dir, G_DIR_SEPARATOR_S, VIK_LAYER(gl->data)->name, ii, extension );
3016           }
3017           else {
3018                   safe = TRUE;
3019                   break;
3020           }
3021           ii++;
3022     }
3023     if ( ii == 5000 )
3024       success = FALSE;
3025
3026     // NB: We allow exporting empty layers
3027     if ( safe ) {
3028       gboolean this_success = a_file_export ( VIK_TRW_LAYER(gl->data), fn, vft, NULL, TRUE );
3029
3030       // Show some progress
3031       if ( this_success ) {
3032         export_count++;
3033         gchar *message = g_strdup_printf ( _("Exporting to file: %s"), fn );
3034         vik_statusbar_set_message ( vw->viking_vs, VIK_STATUSBAR_INFO, message );
3035         while ( gtk_events_pending() )
3036           gtk_main_iteration ();
3037         g_free ( message );
3038       }
3039       
3040       success = success && this_success;
3041     }
3042
3043     g_free ( fn );
3044     gl = g_list_next ( gl );
3045   }
3046
3047   vik_window_clear_busy_cursor ( vw );
3048
3049   // Confirm what happened.
3050   gchar *message = g_strdup_printf ( _("Exported files: %d"), export_count );
3051   vik_statusbar_set_message ( vw->viking_vs, VIK_STATUSBAR_INFO, message );
3052   g_free ( message );
3053
3054   return success;
3055 }
3056
3057 static void export_to_common ( VikWindow *vw, VikFileType_t vft, const gchar *extension )
3058 {
3059   GList *gl = vik_layers_panel_get_all_layers_of_type ( vw->viking_vlp, VIK_LAYER_TRW, TRUE );
3060
3061   if ( !gl ) {
3062     a_dialog_info_msg ( GTK_WINDOW(vw), _("Nothing to Export!") );
3063     return;
3064   }
3065
3066   GtkWidget *dialog = gtk_file_chooser_dialog_new ( _("Export to directory"),
3067                                                     GTK_WINDOW(vw),
3068                                                     GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER,
3069                                                     GTK_STOCK_CANCEL,
3070                                                     GTK_RESPONSE_REJECT,
3071                                                     GTK_STOCK_OK,
3072                                                     GTK_RESPONSE_ACCEPT,
3073                                                     NULL );
3074   gtk_window_set_transient_for ( GTK_WINDOW(dialog), GTK_WINDOW(vw) );
3075   gtk_window_set_destroy_with_parent ( GTK_WINDOW(dialog), TRUE );
3076   gtk_window_set_modal ( GTK_WINDOW(dialog), TRUE );
3077
3078   gtk_widget_show_all ( dialog );
3079
3080   if ( gtk_dialog_run ( GTK_DIALOG(dialog) ) == GTK_RESPONSE_ACCEPT ) {
3081     gchar *dir = gtk_file_chooser_get_filename ( GTK_FILE_CHOOSER(dialog) );
3082     gtk_widget_destroy ( dialog );
3083     if ( dir ) {
3084       if ( !export_to ( vw, gl, vft, dir, extension ) )
3085         a_dialog_error_msg ( GTK_WINDOW(vw),_("Could not convert all files") );
3086       g_free ( dir );
3087     }
3088   }
3089   else
3090     gtk_widget_destroy ( dialog );
3091
3092   g_list_free ( gl );
3093 }
3094
3095 static void export_to_gpx ( GtkAction *a, VikWindow *vw )
3096 {
3097   export_to_common ( vw, FILE_TYPE_GPX, ".gpx" );
3098 }
3099
3100 static void export_to_kml ( GtkAction *a, VikWindow *vw )
3101 {
3102   export_to_common ( vw, FILE_TYPE_KML, ".kml" );
3103 }
3104
3105 #if !GLIB_CHECK_VERSION(2,26,0)
3106 typedef struct stat GStatBuf;
3107 #endif
3108
3109 static void file_properties_cb ( GtkAction *a, VikWindow *vw )
3110 {
3111   gchar *message = NULL;
3112   if ( vw->filename ) {
3113     if ( g_file_test ( vw->filename, G_FILE_TEST_EXISTS ) ) {
3114       // Get some timestamp information of the file
3115       GStatBuf stat_buf;
3116       if ( g_stat ( vw->filename, &stat_buf ) == 0 ) {
3117         gchar time_buf[64];
3118         strftime ( time_buf, sizeof(time_buf), "%c", gmtime((const time_t *)&stat_buf.st_mtime) );
3119         gchar *size = NULL;
3120         gint byte_size = stat_buf.st_size;
3121         // See http://en.wikipedia.org/wiki/Megabyte (and Kilobyte)
3122         //  hence using 1000 rather than 1024
3123         //  so get output as per 'ls' or the Gtk file open dialog
3124         if ( byte_size < 1000 )
3125           size = g_strdup_printf ( _("%d bytes"), byte_size );
3126         else if ( byte_size < 1000*1000 )
3127           size = g_strdup_printf ( _("%3.1f kB"), (gdouble)byte_size / 1000 );
3128         else
3129           size = g_strdup_printf ( _("%3.1f MB"), (gdouble)byte_size / (1000*1000) );
3130         message = g_strdup_printf ( _("%s\n\n%s\n\n%s"), vw->filename, time_buf, size );
3131         g_free (size);
3132       }
3133     }
3134     else
3135       message = g_strdup ( _("File not accessible") );
3136   }
3137   else
3138     message = g_strdup ( _("No Viking File") );
3139
3140   // Show the info
3141   a_dialog_info_msg ( GTK_WINDOW(vw), message );
3142   g_free ( message );
3143 }
3144
3145 static void acquire_from_gps ( GtkAction *a, VikWindow *vw )
3146 {
3147   // Via the file menu, acquiring from a GPS makes a new layer
3148   //  this has always been the way (not entirely sure if this was the real intention!)
3149   //  thus maintain the behaviour ATM.
3150   // Hence explicit setting here (as the value may be changed elsewhere)
3151   vik_datasource_gps_interface.mode = VIK_DATASOURCE_CREATENEWLAYER;
3152   a_acquire(vw, vw->viking_vlp, vw->viking_vvp, &vik_datasource_gps_interface, NULL, NULL );
3153 }
3154
3155 static void acquire_from_file ( GtkAction *a, VikWindow *vw )
3156 {
3157   a_acquire(vw, vw->viking_vlp, vw->viking_vvp, &vik_datasource_file_interface, NULL, NULL );
3158 }
3159
3160 static void acquire_from_routing ( GtkAction *a, VikWindow *vw )
3161 {
3162   a_acquire(vw, vw->viking_vlp, vw->viking_vvp, &vik_datasource_routing_interface, NULL, NULL );
3163 }
3164
3165 #ifdef VIK_CONFIG_OPENSTREETMAP
3166 static void acquire_from_osm ( GtkAction *a, VikWindow *vw )
3167 {
3168   a_acquire(vw, vw->viking_vlp, vw->viking_vvp, &vik_datasource_osm_interface, NULL, NULL );
3169 }
3170
3171 static void acquire_from_my_osm ( GtkAction *a, VikWindow *vw )
3172 {
3173   a_acquire(vw, vw->viking_vlp, vw->viking_vvp, &vik_datasource_osm_my_traces_interface, NULL, NULL );
3174 }
3175 #endif
3176
3177 #ifdef VIK_CONFIG_GEOCACHES
3178 static void acquire_from_gc ( GtkAction *a, VikWindow *vw )
3179 {
3180   a_acquire(vw, vw->viking_vlp, vw->viking_vvp, &vik_datasource_gc_interface, NULL, NULL );
3181 }
3182 #endif
3183
3184 #ifdef VIK_CONFIG_GEOTAG
3185 static void acquire_from_geotag ( GtkAction *a, VikWindow *vw )
3186 {
3187   vik_datasource_geotag_interface.mode = VIK_DATASOURCE_CREATENEWLAYER;
3188   a_acquire(vw, vw->viking_vlp, vw->viking_vvp, &vik_datasource_geotag_interface, NULL, NULL );
3189 }
3190 #endif
3191
3192 #ifdef VIK_CONFIG_GEONAMES
3193 static void acquire_from_wikipedia ( GtkAction *a, VikWindow *vw )
3194 {
3195   a_acquire(vw, vw->viking_vlp, vw->viking_vvp, &vik_datasource_wikipedia_interface, NULL, NULL );
3196 }
3197 #endif
3198
3199 static void acquire_from_url ( GtkAction *a, VikWindow *vw )
3200 {
3201   vik_datasource_url_interface.mode = VIK_DATASOURCE_CREATENEWLAYER;
3202   a_acquire(vw, vw->viking_vlp, vw->viking_vvp, &vik_datasource_url_interface, NULL, NULL );
3203 }
3204
3205 static void goto_default_location( GtkAction *a, VikWindow *vw)
3206 {
3207   struct LatLon ll;
3208   ll.lat = a_vik_get_default_lat();
3209   ll.lon = a_vik_get_default_long();
3210   vik_viewport_set_center_latlon(vw->viking_vvp, &ll, TRUE);
3211   vik_layers_panel_emit_update(vw->viking_vlp);
3212 }
3213
3214
3215 static void goto_address( GtkAction *a, VikWindow *vw)
3216 {
3217   a_vik_goto ( vw, vw->viking_vvp );
3218   vik_layers_panel_emit_update ( vw->viking_vlp );
3219 }
3220
3221 static void mapcache_flush_cb ( GtkAction *a, VikWindow *vw )
3222 {
3223   a_mapcache_flush();
3224 }
3225
3226 static void layer_defaults_cb ( GtkAction *a, VikWindow *vw )
3227 {
3228   gchar **texts = g_strsplit ( gtk_action_get_name(a), "Layer", 0 );
3229
3230   if ( !texts[1] )
3231     return; // Internally broken :(
3232
3233   if ( ! a_layer_defaults_show_window ( GTK_WINDOW(vw), texts[1] ) )
3234     a_dialog_info_msg ( GTK_WINDOW(vw), _("This layer has no configurable properties.") );
3235   // NB no update needed
3236
3237   g_strfreev ( texts );
3238 }
3239
3240 static void preferences_change_update ( VikWindow *vw, gpointer data )
3241 {
3242   // Want to update all TrackWaypoint layers
3243   GList *layers = vik_layers_panel_get_all_layers_of_type ( vw->viking_vlp, VIK_LAYER_TRW, TRUE );
3244
3245   if ( !layers )
3246     return;
3247
3248   while ( layers ) {
3249     // Reset the individual waypoints themselves due to the preferences change
3250     VikTrwLayer *vtl = VIK_TRW_LAYER(layers->data);
3251     vik_trw_layer_reset_waypoints ( vtl );
3252     layers = g_list_next ( layers );
3253   }
3254
3255   g_list_free ( layers );
3256
3257   draw_update ( vw );
3258 }
3259
3260 static void preferences_cb ( GtkAction *a, VikWindow *vw )
3261 {
3262   gboolean wp_icon_size = a_vik_get_use_large_waypoint_icons();
3263
3264   a_preferences_show_window ( GTK_WINDOW(vw) );
3265
3266   // Has the waypoint size setting changed?
3267   if (wp_icon_size != a_vik_get_use_large_waypoint_icons()) {
3268     // Delete icon indexing 'cache' and so automatically regenerates with the new setting when changed
3269     clear_garmin_icon_syms ();
3270
3271     // Update all windows
3272     g_slist_foreach ( window_list, (GFunc) preferences_change_update, NULL );
3273   }
3274 }
3275
3276 static void default_location_cb ( GtkAction *a, VikWindow *vw )
3277 {
3278   /* Simplistic repeat of preference setting
3279      Only the name & type are important for setting the preference via this 'external' way */
3280   VikLayerParam pref_lat[] = {
3281     { VIK_LAYER_NUM_TYPES,
3282       VIKING_PREFERENCES_NAMESPACE "default_latitude",
3283       VIK_LAYER_PARAM_DOUBLE,
3284       VIK_LOCATION_LAT,
3285       NULL,
3286       VIK_LAYER_WIDGET_SPINBUTTON,
3287       NULL,
3288       NULL,
3289       NULL,
3290       NULL,
3291       NULL,
3292     },
3293   };
3294   VikLayerParam pref_lon[] = {
3295     { VIK_LAYER_NUM_TYPES,
3296       VIKING_PREFERENCES_NAMESPACE "default_longitude",
3297       VIK_LAYER_PARAM_DOUBLE,
3298       VIK_LOCATION_LONG,
3299       NULL,
3300       VIK_LAYER_WIDGET_SPINBUTTON,
3301       NULL,
3302       NULL,
3303       NULL,
3304       NULL,
3305       NULL,
3306     },
3307   };
3308
3309   /* Get current center */
3310   struct LatLon ll;
3311   vik_coord_to_latlon ( vik_viewport_get_center ( vw->viking_vvp ), &ll );
3312
3313   /* Apply to preferences */
3314   VikLayerParamData vlp_data;
3315   vlp_data.d = ll.lat;
3316   a_preferences_run_setparam (vlp_data, pref_lat);
3317   vlp_data.d = ll.lon;
3318   a_preferences_run_setparam (vlp_data, pref_lon);
3319   /* Remember to save */
3320   a_preferences_save_to_file();
3321 }
3322
3323 static void clear_cb ( GtkAction *a, VikWindow *vw )
3324 {
3325   vik_layers_panel_clear ( vw->viking_vlp );
3326   window_set_filename ( vw, NULL );
3327   draw_update ( vw );
3328 }
3329
3330 static void window_close ( GtkAction *a, VikWindow *vw )
3331 {
3332   if ( ! delete_event ( vw ) )
3333     gtk_widget_destroy ( GTK_WIDGET(vw) );
3334 }
3335
3336 static gboolean save_file_and_exit ( GtkAction *a, VikWindow *vw )
3337 {
3338   if (save_file( NULL, vw)) {
3339     window_close( NULL, vw);
3340     return(TRUE);
3341   }
3342   else
3343     return(FALSE);
3344 }
3345
3346 static void zoom_to_cb ( GtkAction *a, VikWindow *vw )
3347 {
3348   gdouble xmpp = vik_viewport_get_xmpp ( vw->viking_vvp ), ympp = vik_viewport_get_ympp ( vw->viking_vvp );
3349   if ( a_dialog_custom_zoom ( GTK_WINDOW(vw), &xmpp, &ympp ) )
3350   {
3351     vik_viewport_set_xmpp ( vw->viking_vvp, xmpp );
3352     vik_viewport_set_ympp ( vw->viking_vvp, ympp );
3353     draw_update ( vw );
3354   }
3355 }
3356
3357 static void save_image_file ( VikWindow *vw, const gchar *fn, guint w, guint h, gdouble zoom, gboolean save_as_png )
3358 {
3359   /* more efficient way: stuff draws directly to pixbuf (fork viewport) */
3360   GdkPixbuf *pixbuf_to_save;
3361   gdouble old_xmpp, old_ympp;
3362   GError *error = NULL;
3363
3364   GtkWidget *msgbox = gtk_message_dialog_new ( GTK_WINDOW(vw),
3365                                                GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
3366                                                GTK_MESSAGE_INFO,
3367                                                GTK_BUTTONS_NONE,
3368                                                _("Generating image file...") );
3369
3370   g_signal_connect_swapped (msgbox, "response", G_CALLBACK (gtk_widget_destroy), msgbox);
3371   // Ensure dialog shown
3372   gtk_widget_show_all ( msgbox );
3373   // Try harder...
3374   vik_statusbar_set_message ( vw->viking_vs, VIK_STATUSBAR_INFO, _("Generating image file...") );
3375   while ( gtk_events_pending() )
3376     gtk_main_iteration ();
3377   // Despite many efforts & variations, GTK on my Linux system doesn't show the actual msgbox contents :(
3378   // At least the empty box can give a clue something's going on + the statusbar msg...
3379   // Windows version under Wine OK!
3380
3381   /* backup old zoom & set new */
3382   old_xmpp = vik_viewport_get_xmpp ( vw->viking_vvp );
3383   old_ympp = vik_viewport_get_ympp ( vw->viking_vvp );
3384   vik_viewport_set_zoom ( vw->viking_vvp, zoom );
3385
3386   /* reset width and height: */
3387   vik_viewport_configure_manually ( vw->viking_vvp, w, h );
3388
3389   /* draw all layers */
3390   draw_redraw ( vw );
3391
3392   /* save buffer as file. */
3393   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);
3394   if ( !pixbuf_to_save ) {
3395     g_warning("Failed to generate internal pixmap size: %d x %d", w, h);
3396     gtk_message_dialog_set_markup ( GTK_MESSAGE_DIALOG(msgbox), _("Failed to generate internal image.\n\nTry creating a smaller image.") );
3397     goto cleanup;
3398   }
3399
3400   gdk_pixbuf_save ( pixbuf_to_save, fn, save_as_png ? "png" : "jpeg", &error, NULL );
3401   if (error)
3402   {
3403     g_warning("Unable to write to file %s: %s", fn, error->message );
3404     gtk_message_dialog_set_markup ( GTK_MESSAGE_DIALOG(msgbox), _("Failed to generate image file.") );
3405     g_error_free (error);
3406   }
3407   else {
3408     // Success
3409     gtk_message_dialog_set_markup ( GTK_MESSAGE_DIALOG(msgbox), _("Image file generated.") );
3410   }
3411   g_object_unref ( G_OBJECT(pixbuf_to_save) );
3412
3413  cleanup:
3414   vik_statusbar_set_message ( vw->viking_vs, VIK_STATUSBAR_INFO, "" );
3415   gtk_dialog_add_button ( GTK_DIALOG(msgbox), GTK_STOCK_OK, GTK_RESPONSE_OK );
3416   gtk_dialog_run ( GTK_DIALOG(msgbox) ); // Don't care about the result
3417
3418   /* pretend like nothing happened ;) */
3419   vik_viewport_set_xmpp ( vw->viking_vvp, old_xmpp );
3420   vik_viewport_set_ympp ( vw->viking_vvp, old_ympp );
3421   vik_viewport_configure ( vw->viking_vvp );
3422   draw_update ( vw );
3423 }
3424
3425 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 )
3426 {
3427   gulong size = sizeof(gchar) * (strlen(fn) + 15);
3428   gchar *name_of_file = g_malloc ( size );
3429   guint x = 1, y = 1;
3430   struct UTM utm_orig, utm;
3431
3432   /* *** copied from above *** */
3433   GdkPixbuf *pixbuf_to_save;
3434   gdouble old_xmpp, old_ympp;
3435   GError *error = NULL;
3436
3437   /* backup old zoom & set new */
3438   old_xmpp = vik_viewport_get_xmpp ( vw->viking_vvp );
3439   old_ympp = vik_viewport_get_ympp ( vw->viking_vvp );
3440   vik_viewport_set_zoom ( vw->viking_vvp, zoom );
3441
3442   /* reset width and height: do this only once for all images (same size) */
3443   vik_viewport_configure_manually ( vw->viking_vvp, w, h );
3444   /* *** end copy from above *** */
3445
3446   g_assert ( vik_viewport_get_coord_mode ( vw->viking_vvp ) == VIK_COORD_UTM );
3447
3448   g_mkdir(fn,0777);
3449
3450   utm_orig = *((const struct UTM *)vik_viewport_get_center ( vw->viking_vvp ));
3451
3452   for ( y = 1; y <= tiles_h; y++ )
3453   {
3454     for ( x = 1; x <= tiles_w; x++ )
3455     {
3456       g_snprintf ( name_of_file, size, "%s%cy%d-x%d.%s", fn, G_DIR_SEPARATOR, y, x, save_as_png ? "png" : "jpg" );
3457       utm = utm_orig;
3458       if ( tiles_w & 0x1 )
3459         utm.easting += ((gdouble)x - ceil(((gdouble)tiles_w)/2)) * (w*zoom);
3460       else
3461         utm.easting += ((gdouble)x - (((gdouble)tiles_w)+1)/2) * (w*zoom);
3462       if ( tiles_h & 0x1 ) /* odd */
3463         utm.northing -= ((gdouble)y - ceil(((gdouble)tiles_h)/2)) * (h*zoom);
3464       else /* even */
3465         utm.northing -= ((gdouble)y - (((gdouble)tiles_h)+1)/2) * (h*zoom);
3466
3467       /* move to correct place. */
3468       vik_viewport_set_center_utm ( vw->viking_vvp, &utm, FALSE );
3469
3470       draw_redraw ( vw );
3471
3472       /* save buffer as file. */
3473       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);
3474       gdk_pixbuf_save ( pixbuf_to_save, name_of_file, save_as_png ? "png" : "jpeg", &error, NULL );
3475       if (error)
3476       {
3477         g_warning("Unable to write to file %s: %s", name_of_file, error->message );
3478         g_error_free (error);
3479       }
3480
3481       g_object_unref ( G_OBJECT(pixbuf_to_save) );
3482     }
3483   }
3484
3485   vik_viewport_set_center_utm ( vw->viking_vvp, &utm_orig, FALSE );
3486   vik_viewport_set_xmpp ( vw->viking_vvp, old_xmpp );
3487   vik_viewport_set_ympp ( vw->viking_vvp, old_ympp );
3488   vik_viewport_configure ( vw->viking_vvp );
3489   draw_update ( vw );
3490
3491   g_free ( name_of_file );
3492 }
3493
3494 static void draw_to_image_file_current_window_cb(GtkWidget* widget,GdkEventButton *event,gpointer *pass_along)
3495 {
3496   VikWindow *vw = VIK_WINDOW(pass_along[0]);
3497   GtkSpinButton *width_spin = GTK_SPIN_BUTTON(pass_along[1]), *height_spin = GTK_SPIN_BUTTON(pass_along[2]);
3498
3499   gint active = gtk_combo_box_get_active ( GTK_COMBO_BOX(pass_along[3]) );
3500   gdouble zoom = pow (2, active-2 );
3501
3502   gdouble width_min, width_max, height_min, height_max;
3503   gint width, height;
3504
3505   gtk_spin_button_get_range ( width_spin, &width_min, &width_max );
3506   gtk_spin_button_get_range ( height_spin, &height_min, &height_max );
3507
3508   /* TODO: support for xzoom and yzoom values */
3509   width = vik_viewport_get_width ( vw->viking_vvp ) * vik_viewport_get_xmpp ( vw->viking_vvp ) / zoom;
3510   height = vik_viewport_get_height ( vw->viking_vvp ) * vik_viewport_get_xmpp ( vw->viking_vvp ) / zoom;
3511
3512   if ( width > width_max || width < width_min || height > height_max || height < height_min )
3513     a_dialog_info_msg ( GTK_WINDOW(vw), _("Viewable region outside allowable pixel size bounds for image. Clipping width/height values.") );
3514
3515   gtk_spin_button_set_value ( width_spin, width );
3516   gtk_spin_button_set_value ( height_spin, height );
3517 }
3518
3519 static void draw_to_image_file_total_area_cb (GtkSpinButton *spinbutton, gpointer *pass_along)
3520 {
3521   GtkSpinButton *width_spin = GTK_SPIN_BUTTON(pass_along[1]), *height_spin = GTK_SPIN_BUTTON(pass_along[2]);
3522
3523   gint active = gtk_combo_box_get_active ( GTK_COMBO_BOX(pass_along[3]) );
3524   gdouble zoom = pow (2, active-2 );
3525
3526   gchar *label_text;
3527   gdouble w, h;
3528   w = gtk_spin_button_get_value(width_spin) * zoom;
3529   h = gtk_spin_button_get_value(height_spin) * zoom;
3530   if (pass_along[4]) /* save many images; find TOTAL area covered */
3531   {
3532     w *= gtk_spin_button_get_value(GTK_SPIN_BUTTON(pass_along[4]));
3533     h *= gtk_spin_button_get_value(GTK_SPIN_BUTTON(pass_along[5]));
3534   }
3535   vik_units_distance_t dist_units = a_vik_get_units_distance ();
3536   switch (dist_units) {
3537   case VIK_UNITS_DISTANCE_KILOMETRES:
3538     label_text = g_strdup_printf ( _("Total area: %ldm x %ldm (%.3f sq. km)"), (glong)w, (glong)h, (w*h/1000000));
3539     break;
3540   case VIK_UNITS_DISTANCE_MILES:
3541     label_text = g_strdup_printf ( _("Total area: %ldm x %ldm (%.3f sq. miles)"), (glong)w, (glong)h, (w*h/2589988.11));
3542     break;
3543   default:
3544     label_text = g_strdup_printf ("Just to keep the compiler happy");
3545     g_critical("Houston, we've had a problem. distance=%d", dist_units);
3546   }
3547
3548   gtk_label_set_text(GTK_LABEL(pass_along[6]), label_text);
3549   g_free ( label_text );
3550 }
3551
3552 /*
3553  * Get an allocated filename (or directory as specified)
3554  */
3555 static gchar* draw_image_filename ( VikWindow *vw, gboolean one_image_only )
3556 {
3557   gchar *fn = NULL;
3558   if ( one_image_only )
3559   {
3560     // Single file
3561     if (!vw->save_img_dia) {
3562       vw->save_img_dia = gtk_file_chooser_dialog_new (_("Save Image"),
3563                                                       GTK_WINDOW(vw),
3564                                                       GTK_FILE_CHOOSER_ACTION_SAVE,
3565                                                       GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
3566                                                       GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT,
3567                                                       NULL);
3568
3569       gchar *cwd = g_get_current_dir();
3570       if ( cwd ) {
3571         gtk_file_chooser_set_current_folder ( GTK_FILE_CHOOSER(vw->save_img_dia), cwd );
3572         g_free ( cwd );
3573       }
3574
3575       GtkFileChooser *chooser = GTK_FILE_CHOOSER ( vw->save_img_dia );
3576       /* Add filters */
3577       GtkFileFilter *filter;
3578       filter = gtk_file_filter_new ();
3579       gtk_file_filter_set_name ( filter, _("All") );
3580       gtk_file_filter_add_pattern ( filter, "*" );
3581       gtk_file_chooser_add_filter ( chooser, filter );
3582
3583       filter = gtk_file_filter_new ();
3584       gtk_file_filter_set_name ( filter, _("JPG") );
3585       gtk_file_filter_add_mime_type ( filter, "image/jpeg");
3586       gtk_file_chooser_add_filter ( chooser, filter );
3587
3588       if ( !vw->draw_image_save_as_png )
3589         gtk_file_chooser_set_filter ( chooser, filter );
3590
3591       filter = gtk_file_filter_new ();
3592       gtk_file_filter_set_name ( filter, _("PNG") );
3593       gtk_file_filter_add_mime_type ( filter, "image/png");
3594       gtk_file_chooser_add_filter ( chooser, filter );
3595
3596       if ( vw->draw_image_save_as_png )
3597         gtk_file_chooser_set_filter ( chooser, filter );
3598
3599       gtk_window_set_transient_for ( GTK_WINDOW(vw->save_img_dia), GTK_WINDOW(vw) );
3600       gtk_window_set_destroy_with_parent ( GTK_WINDOW(vw->save_img_dia), TRUE );
3601     }
3602
3603     if ( gtk_dialog_run ( GTK_DIALOG(vw->save_img_dia) ) == GTK_RESPONSE_ACCEPT ) {
3604       fn = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER(vw->save_img_dia) );
3605       if ( g_file_test ( fn, G_FILE_TEST_EXISTS ) )
3606         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 ) ) )
3607           fn = NULL;
3608     }
3609     gtk_widget_hide ( vw->save_img_dia );
3610   }
3611   else {
3612     // A directory
3613     // For some reason this method is only written to work in UTM...
3614     if ( vik_viewport_get_coord_mode(vw->viking_vvp) != VIK_COORD_UTM ) {
3615       a_dialog_error_msg ( GTK_WINDOW(vw), _("You must be in UTM mode to use this feature") );
3616       return fn;
3617     }
3618
3619     if (!vw->save_img_dir_dia) {
3620       vw->save_img_dir_dia = gtk_file_chooser_dialog_new (_("Choose a directory to hold images"),
3621                                                           GTK_WINDOW(vw),
3622                                                           GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER,
3623                                                           GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
3624                                                           GTK_STOCK_OK, GTK_RESPONSE_ACCEPT,
3625                                                           NULL);
3626       gtk_window_set_transient_for ( GTK_WINDOW(vw->save_img_dir_dia), GTK_WINDOW(vw) );
3627       gtk_window_set_destroy_with_parent ( GTK_WINDOW(vw->save_img_dir_dia), TRUE );
3628     }
3629
3630     if ( gtk_dialog_run ( GTK_DIALOG(vw->save_img_dir_dia) ) == GTK_RESPONSE_ACCEPT ) {
3631       fn = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER(vw->save_img_dir_dia) );
3632     }
3633     gtk_widget_hide ( vw->save_img_dir_dia );
3634   }
3635   return fn;
3636 }
3637
3638 static void draw_to_image_file ( VikWindow *vw, gboolean one_image_only )
3639 {
3640   /* todo: default for answers inside VikWindow or static (thruout instance) */
3641   GtkWidget *dialog = gtk_dialog_new_with_buttons ( _("Save to Image File"), GTK_WINDOW(vw),
3642                                                   GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
3643                                                   GTK_STOCK_CANCEL,
3644                                                   GTK_RESPONSE_REJECT,
3645                                                   GTK_STOCK_OK,
3646                                                   GTK_RESPONSE_ACCEPT,
3647                                                   NULL );
3648   GtkWidget *width_label, *width_spin, *height_label, *height_spin;
3649   GtkWidget *png_radio, *jpeg_radio;
3650   GtkWidget *current_window_button;
3651   gpointer current_window_pass_along[7];
3652   GtkWidget *zoom_label, *zoom_combo;
3653   GtkWidget *total_size_label;
3654
3655   /* only used if (!one_image_only) */
3656   GtkWidget *tiles_width_spin = NULL, *tiles_height_spin = NULL;
3657
3658   width_label = gtk_label_new ( _("Width (pixels):") );
3659   width_spin = gtk_spin_button_new ( GTK_ADJUSTMENT(gtk_adjustment_new ( vw->draw_image_width, 10, 50000, 10, 100, 0 )), 10, 0 );
3660   height_label = gtk_label_new ( _("Height (pixels):") );
3661   height_spin = gtk_spin_button_new ( GTK_ADJUSTMENT(gtk_adjustment_new ( vw->draw_image_height, 10, 50000, 10, 100, 0 )), 10, 0 );
3662 #ifdef WINDOWS
3663   GtkWidget *win_warning_label = gtk_label_new ( _("WARNING: USING LARGE IMAGES OVER 10000x10000\nMAY CRASH THE PROGRAM!") );
3664 #endif
3665   zoom_label = gtk_label_new ( _("Zoom (meters per pixel):") );
3666   /* TODO: separate xzoom and yzoom factors */
3667   zoom_combo = create_zoom_combo_all_levels();
3668
3669   gdouble mpp = vik_viewport_get_xmpp(vw->viking_vvp);
3670   gint active = 2 + round ( log (mpp) / log (2) );
3671
3672   // Can we not hard code size here?
3673   if ( active > 17 )
3674     active = 17;
3675   if ( active < 0 )
3676     active = 0;
3677   gtk_combo_box_set_active ( GTK_COMBO_BOX(zoom_combo), active );
3678
3679   total_size_label = gtk_label_new ( NULL );
3680
3681   current_window_button = gtk_button_new_with_label ( _("Area in current viewable window") );
3682   current_window_pass_along [0] = vw;
3683   current_window_pass_along [1] = width_spin;
3684   current_window_pass_along [2] = height_spin;
3685   current_window_pass_along [3] = zoom_combo;
3686   current_window_pass_along [4] = NULL; /* used for one_image_only != 1 */
3687   current_window_pass_along [5] = NULL;
3688   current_window_pass_along [6] = total_size_label;
3689   g_signal_connect ( G_OBJECT(current_window_button), "button_press_event", G_CALLBACK(draw_to_image_file_current_window_cb), current_window_pass_along );
3690
3691   png_radio = gtk_radio_button_new_with_label ( NULL, _("Save as PNG") );
3692   jpeg_radio = gtk_radio_button_new_with_label_from_widget ( GTK_RADIO_BUTTON(png_radio), _("Save as JPEG") );
3693
3694   gtk_box_pack_start (GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), png_radio, FALSE, FALSE, 0);
3695   gtk_box_pack_start (GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), jpeg_radio, FALSE, FALSE, 0);
3696
3697   if ( ! vw->draw_image_save_as_png )
3698     gtk_toggle_button_set_active ( GTK_TOGGLE_BUTTON(jpeg_radio), TRUE );
3699
3700   gtk_box_pack_start (GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), width_label, FALSE, FALSE, 0);
3701   gtk_box_pack_start (GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), width_spin, FALSE, FALSE, 0);
3702   gtk_box_pack_start (GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), height_label, FALSE, FALSE, 0);
3703   gtk_box_pack_start (GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), height_spin, FALSE, FALSE, 0);
3704 #ifdef WINDOWS
3705   gtk_box_pack_start (GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), win_warning_label, FALSE, FALSE, 0);
3706 #endif
3707   gtk_box_pack_start (GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), current_window_button, FALSE, FALSE, 0);
3708   gtk_box_pack_start (GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), zoom_label, FALSE, FALSE, 0);
3709   gtk_box_pack_start (GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), zoom_combo, FALSE, FALSE, 0);
3710
3711   if ( ! one_image_only )
3712   {
3713     GtkWidget *tiles_width_label, *tiles_height_label;
3714
3715     tiles_width_label = gtk_label_new ( _("East-west image tiles:") );
3716     tiles_width_spin = gtk_spin_button_new ( GTK_ADJUSTMENT(gtk_adjustment_new ( 5, 1, 10, 1, 100, 0 )), 1, 0 );
3717     tiles_height_label = gtk_label_new ( _("North-south image tiles:") );
3718     tiles_height_spin = gtk_spin_button_new ( GTK_ADJUSTMENT(gtk_adjustment_new ( 5, 1, 10, 1, 100, 0 )), 1, 0 );
3719     gtk_box_pack_start (GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), tiles_width_label, FALSE, FALSE, 0);
3720     gtk_box_pack_start (GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), tiles_width_spin, FALSE, FALSE, 0);
3721     gtk_box_pack_start (GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), tiles_height_label, FALSE, FALSE, 0);
3722     gtk_box_pack_start (GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), tiles_height_spin, FALSE, FALSE, 0);
3723
3724     current_window_pass_along [4] = tiles_width_spin;
3725     current_window_pass_along [5] = tiles_height_spin;
3726     g_signal_connect ( G_OBJECT(tiles_width_spin), "value-changed", G_CALLBACK(draw_to_image_file_total_area_cb), current_window_pass_along );
3727     g_signal_connect ( G_OBJECT(tiles_height_spin), "value-changed", G_CALLBACK(draw_to_image_file_total_area_cb), current_window_pass_along );
3728   }
3729   gtk_box_pack_start (GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), total_size_label, FALSE, FALSE, 0);
3730   g_signal_connect ( G_OBJECT(width_spin), "value-changed", G_CALLBACK(draw_to_image_file_total_area_cb), current_window_pass_along );
3731   g_signal_connect ( G_OBJECT(height_spin), "value-changed", G_CALLBACK(draw_to_image_file_total_area_cb), current_window_pass_along );
3732   g_signal_connect ( G_OBJECT(zoom_combo), "changed", G_CALLBACK(draw_to_image_file_total_area_cb), current_window_pass_along );
3733
3734   draw_to_image_file_total_area_cb ( NULL, current_window_pass_along ); /* set correct size info now */
3735
3736   gtk_dialog_set_default_response ( GTK_DIALOG(dialog), GTK_RESPONSE_ACCEPT );
3737
3738   gtk_widget_show_all ( gtk_dialog_get_content_area(GTK_DIALOG(dialog)) );
3739
3740   if ( gtk_dialog_run ( GTK_DIALOG(dialog) ) == GTK_RESPONSE_ACCEPT )
3741   {
3742     gtk_widget_hide ( GTK_WIDGET(dialog) );
3743
3744     gchar *fn = draw_image_filename ( vw, one_image_only );
3745     if ( !fn )
3746       return;
3747
3748     gint active = gtk_combo_box_get_active ( GTK_COMBO_BOX(zoom_combo) );
3749     gdouble zoom = pow (2, active-2 );
3750
3751     if ( one_image_only )
3752       save_image_file ( vw, fn, 
3753                       vw->draw_image_width = gtk_spin_button_get_value_as_int ( GTK_SPIN_BUTTON(width_spin) ),
3754                       vw->draw_image_height = gtk_spin_button_get_value_as_int ( GTK_SPIN_BUTTON(height_spin) ),
3755                       zoom,
3756                       vw->draw_image_save_as_png = gtk_toggle_button_get_active ( GTK_TOGGLE_BUTTON(png_radio) ) );
3757     else {
3758       // NB is in UTM mode ATM
3759       save_image_dir ( vw, fn,
3760                        vw->draw_image_width = gtk_spin_button_get_value_as_int ( GTK_SPIN_BUTTON(width_spin) ),
3761                        vw->draw_image_height = gtk_spin_button_get_value_as_int ( GTK_SPIN_BUTTON(height_spin) ),
3762                        zoom,
3763                        vw->draw_image_save_as_png = gtk_toggle_button_get_active ( GTK_TOGGLE_BUTTON(png_radio) ),
3764                        gtk_spin_button_get_value ( GTK_SPIN_BUTTON(tiles_width_spin) ),
3765                        gtk_spin_button_get_value ( GTK_SPIN_BUTTON(tiles_height_spin) ) );
3766     }
3767
3768     g_free ( fn );
3769   }
3770   gtk_widget_destroy ( GTK_WIDGET(dialog) );
3771 }
3772
3773
3774 static void draw_to_image_file_cb ( GtkAction *a, VikWindow *vw )
3775 {
3776   draw_to_image_file ( vw, TRUE );
3777 }
3778
3779 static void draw_to_image_dir_cb ( GtkAction *a, VikWindow *vw )
3780 {
3781   draw_to_image_file ( vw, FALSE );
3782 }
3783
3784 static void print_cb ( GtkAction *a, VikWindow *vw )
3785 {
3786   a_print(vw, vw->viking_vvp);
3787 }
3788
3789 /* really a misnomer: changes coord mode (actual coordinates) AND/OR draw mode (viewport only) */
3790 static void window_change_coord_mode_cb ( GtkAction *old_a, GtkAction *a, VikWindow *vw )
3791 {
3792   VikViewportDrawMode drawmode;
3793   if (!strcmp(gtk_action_get_name(a), "ModeUTM")) {
3794     drawmode = VIK_VIEWPORT_DRAWMODE_UTM;
3795   }
3796   else if (!strcmp(gtk_action_get_name(a), "ModeLatLon")) {
3797     drawmode = VIK_VIEWPORT_DRAWMODE_LATLON;
3798   }
3799   else if (!strcmp(gtk_action_get_name(a), "ModeExpedia")) {
3800     drawmode = VIK_VIEWPORT_DRAWMODE_EXPEDIA;
3801   }
3802   else if (!strcmp(gtk_action_get_name(a), "ModeMercator")) {
3803     drawmode = VIK_VIEWPORT_DRAWMODE_MERCATOR;
3804   }
3805   else {
3806     g_critical("Houston, we've had a problem.");
3807     return;
3808   }
3809
3810   if ( !vw->only_updating_coord_mode_ui )
3811   {
3812     VikViewportDrawMode olddrawmode = vik_viewport_get_drawmode ( vw->viking_vvp );
3813     if ( olddrawmode != drawmode )
3814     {
3815       /* this takes care of coord mode too */
3816       vik_viewport_set_drawmode ( vw->viking_vvp, drawmode );
3817       if ( drawmode == VIK_VIEWPORT_DRAWMODE_UTM ) {
3818         vik_layers_panel_change_coord_mode ( vw->viking_vlp, VIK_COORD_UTM );
3819       } else if ( olddrawmode == VIK_VIEWPORT_DRAWMODE_UTM ) {
3820         vik_layers_panel_change_coord_mode ( vw->viking_vlp, VIK_COORD_LATLON );
3821       }
3822       draw_update ( vw );
3823     }
3824   }
3825 }
3826
3827 static void set_draw_scale ( GtkAction *a, VikWindow *vw )
3828 {
3829   GtkWidget *check_box = gtk_ui_manager_get_widget ( vw->uim, "/ui/MainMenu/View/SetShow/ShowScale" );
3830   g_assert(check_box);
3831   gboolean state = gtk_check_menu_item_get_active ( GTK_CHECK_MENU_ITEM(check_box));
3832   vik_viewport_set_draw_scale ( vw->viking_vvp, state );
3833   draw_update ( vw );
3834 }
3835
3836 static void set_draw_centermark ( GtkAction *a, VikWindow *vw )
3837 {
3838   GtkWidget *check_box = gtk_ui_manager_get_widget ( vw->uim, "/ui/MainMenu/View/SetShow/ShowCenterMark" );
3839   g_assert(check_box);
3840   gboolean state = gtk_check_menu_item_get_active ( GTK_CHECK_MENU_ITEM(check_box));
3841   vik_viewport_set_draw_centermark ( vw->viking_vvp, state );
3842   draw_update ( vw );
3843 }
3844
3845 static void set_draw_highlight ( GtkAction *a, VikWindow *vw )
3846 {
3847   GtkWidget *check_box = gtk_ui_manager_get_widget ( vw->uim, "/ui/MainMenu/View/SetShow/ShowHighlight" );
3848   g_assert(check_box);
3849   gboolean state = gtk_check_menu_item_get_active ( GTK_CHECK_MENU_ITEM(check_box));
3850   vik_viewport_set_draw_highlight (  vw->viking_vvp, state );
3851   draw_update ( vw );
3852 }
3853
3854 static void set_bg_color ( GtkAction *a, VikWindow *vw )
3855 {
3856   GtkWidget *colorsd = gtk_color_selection_dialog_new ( _("Choose a background color") );
3857   GdkColor *color = vik_viewport_get_background_gdkcolor ( vw->viking_vvp );
3858   gtk_color_selection_set_previous_color ( GTK_COLOR_SELECTION(gtk_color_selection_dialog_get_color_selection(GTK_COLOR_SELECTION_DIALOG(colorsd))), color );
3859   gtk_color_selection_set_current_color ( GTK_COLOR_SELECTION(gtk_color_selection_dialog_get_color_selection(GTK_COLOR_SELECTION_DIALOG(colorsd))), color );
3860   if ( gtk_dialog_run ( GTK_DIALOG(colorsd) ) == GTK_RESPONSE_OK )
3861   {
3862     gtk_color_selection_get_current_color ( GTK_COLOR_SELECTION(gtk_color_selection_dialog_get_color_selection(GTK_COLOR_SELECTION_DIALOG(colorsd))), color );
3863     vik_viewport_set_background_gdkcolor ( vw->viking_vvp, color );
3864     draw_update ( vw );
3865   }
3866   g_free ( color );
3867   gtk_widget_destroy ( colorsd );
3868 }
3869
3870 static void set_highlight_color ( GtkAction *a, VikWindow *vw )
3871 {
3872   GtkWidget *colorsd = gtk_color_selection_dialog_new ( _("Choose a track highlight color") );
3873   GdkColor *color = vik_viewport_get_highlight_gdkcolor ( vw->viking_vvp );
3874   gtk_color_selection_set_previous_color ( GTK_COLOR_SELECTION(gtk_color_selection_dialog_get_color_selection(GTK_COLOR_SELECTION_DIALOG(colorsd))), color );
3875   gtk_color_selection_set_current_color ( GTK_COLOR_SELECTION(gtk_color_selection_dialog_get_color_selection(GTK_COLOR_SELECTION_DIALOG(colorsd))), color );
3876   if ( gtk_dialog_run ( GTK_DIALOG(colorsd) ) == GTK_RESPONSE_OK )
3877   {
3878     gtk_color_selection_get_current_color ( GTK_COLOR_SELECTION(gtk_color_selection_dialog_get_color_selection(GTK_COLOR_SELECTION_DIALOG(colorsd))), color );
3879     vik_viewport_set_highlight_gdkcolor ( vw->viking_vvp, color );
3880     draw_update ( vw );
3881   }
3882   g_free ( color );
3883   gtk_widget_destroy ( colorsd );
3884 }
3885
3886
3887
3888 /***********************************************************************************************
3889  ** GUI Creation
3890  ***********************************************************************************************/
3891
3892 static GtkActionEntry entries[] = {
3893   { "File", NULL, N_("_File"), 0, 0, 0 },
3894   { "Edit", NULL, N_("_Edit"), 0, 0, 0 },
3895   { "View", NULL, N_("_View"), 0, 0, 0 },
3896   { "SetShow", NULL, N_("_Show"), 0, 0, 0 },
3897   { "SetZoom", NULL, N_("_Zoom"), 0, 0, 0 },
3898   { "SetPan", NULL, N_("_Pan"), 0, 0, 0 },
3899   { "Layers", NULL, N_("_Layers"), 0, 0, 0 },
3900   { "Tools", NULL, N_("_Tools"), 0, 0, 0 },
3901   { "Exttools", NULL, N_("_Webtools"), 0, 0, 0 },
3902   { "Help", NULL, N_("_Help"), 0, 0, 0 },
3903
3904   { "New",       GTK_STOCK_NEW,          N_("_New"),                          "<control>N", N_("New file"),                                     (GCallback)newwindow_cb          },
3905   { "Open",      GTK_STOCK_OPEN,         N_("_Open..."),                         "<control>O", N_("Open a file"),                                  (GCallback)load_file             },
3906   { "OpenRecentFile", NULL,              N_("Open _Recent File"),         NULL,         NULL,                                               (GCallback)NULL },
3907   { "Append",    GTK_STOCK_ADD,          N_("Append _File..."),           NULL,         N_("Append data from a different file"),            (GCallback)load_file             },
3908   { "Export",    GTK_STOCK_CONVERT,      N_("_Export All"),               NULL,         N_("Export All TrackWaypoint Layers"),              (GCallback)NULL                  },
3909   { "ExportGPX", NULL,                   N_("_GPX..."),                       NULL,         N_("Export as GPX"),                                (GCallback)export_to_gpx         },
3910   { "Acquire",   GTK_STOCK_GO_DOWN,      N_("A_cquire"),                  NULL,         NULL,                                               (GCallback)NULL },
3911   { "AcquireGPS",   NULL,                N_("From _GPS..."),              NULL,         N_("Transfer data from a GPS device"),              (GCallback)acquire_from_gps      },
3912   { "AcquireGPSBabel",   NULL,                N_("Import File With GPS_Babel..."),                NULL,         N_("Import file via GPSBabel converter"),              (GCallback)acquire_from_file      },
3913   { "AcquireRouting",   NULL,             N_("_Directions..."),     NULL,         N_("Get driving directions"),           (GCallback)acquire_from_routing   },
3914 #ifdef VIK_CONFIG_OPENSTREETMAP
3915   { "AcquireOSM",   NULL,                 N_("_OSM Traces..."),           NULL,         N_("Get traces from OpenStreetMap"),            (GCallback)acquire_from_osm       },
3916   { "AcquireMyOSM", NULL,                 N_("_My OSM Traces..."),        NULL,         N_("Get Your Own Traces from OpenStreetMap"),   (GCallback)acquire_from_my_osm    },
3917 #endif
3918 #ifdef VIK_CONFIG_GEOCACHES
3919   { "AcquireGC",   NULL,                 N_("Geo_caches..."),             NULL,         N_("Get Geocaches from geocaching.com"),            (GCallback)acquire_from_gc       },
3920 #endif
3921 #ifdef VIK_CONFIG_GEOTAG
3922   { "AcquireGeotag", NULL,               N_("From Geotagged _Images..."), NULL,         N_("Create waypoints from geotagged images"),       (GCallback)acquire_from_geotag   },
3923 #endif
3924   { "AcquireURL", NULL,                  N_("From _URL..."),              NULL,         N_("Get a file from a URL"),                        (GCallback)acquire_from_url },
3925 #ifdef VIK_CONFIG_GEONAMES
3926   { "AcquireWikipedia", NULL,            N_("From _Wikipedia Waypoints"), NULL,         N_("Create waypoints from Wikipedia items in the current view"), (GCallback)acquire_from_wikipedia },
3927 #endif
3928   { "Save",      GTK_STOCK_SAVE,         N_("_Save"),                         "<control>S", N_("Save the file"),                                (GCallback)save_file             },
3929   { "SaveAs",    GTK_STOCK_SAVE_AS,      N_("Save _As..."),                      NULL,  N_("Save the file under different name"),           (GCallback)save_file_as          },
3930   { "FileProperties", NULL,              N_("Properties..."),                    NULL,  N_("File Properties"),                              (GCallback)file_properties_cb },
3931   { "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 },
3932   { "GenImgDir", GTK_STOCK_DND_MULTIPLE, N_("Generate _Directory of Images..."), NULL,  N_("FIXME:IMGDIR"),                                 (GCallback)draw_to_image_dir_cb  },
3933   { "Print",    GTK_STOCK_PRINT,        N_("_Print..."),          NULL,         N_("Print maps"), (GCallback)print_cb },
3934   { "Exit",      GTK_STOCK_QUIT,         N_("E_xit"),                         "<control>W", N_("Exit the program"),                             (GCallback)window_close          },
3935   { "SaveExit",  GTK_STOCK_QUIT,         N_("Save and Exit"),                 NULL, N_("Save and Exit the program"),                             (GCallback)save_file_and_exit          },
3936
3937   { "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 },
3938   { "GoForward", GTK_STOCK_GO_FORWARD,   N_("Go to the _Next Location"),      NULL,         N_("Go to the next location"),                  (GCallback)draw_goto_back_and_forth },
3939   { "GotoDefaultLocation", GTK_STOCK_HOME, N_("Go to the _Default Location"),  NULL,         N_("Go to the default location"),                     (GCallback)goto_default_location },
3940   { "GotoSearch", GTK_STOCK_JUMP_TO,     N_("Go to _Location..."),            NULL,         N_("Go to address/place using text search"),        (GCallback)goto_address       },
3941   { "GotoLL",    GTK_STOCK_JUMP_TO,      N_("_Go to Lat/Lon..."),           NULL,         N_("Go to arbitrary lat/lon coordinate"),         (GCallback)draw_goto_cb          },
3942   { "GotoUTM",   GTK_STOCK_JUMP_TO,      N_("Go to UTM..."),                  NULL,         N_("Go to arbitrary UTM coordinate"),               (GCallback)draw_goto_cb          },
3943   { "Refresh",   GTK_STOCK_REFRESH,      N_("_Refresh"),                      "F5",         N_("Refresh any maps displayed"),               (GCallback)draw_refresh_cb       },
3944   { "SetHLColor",GTK_STOCK_SELECT_COLOR, N_("Set _Highlight Color..."),       NULL,         NULL,                                           (GCallback)set_highlight_color   },
3945   { "SetBGColor",GTK_STOCK_SELECT_COLOR, N_("Set Bac_kground Color..."),      NULL,         NULL,                                           (GCallback)set_bg_color          },
3946   { "ZoomIn",    GTK_STOCK_ZOOM_IN,      N_("Zoom _In"),                   "<control>plus", NULL,                                           (GCallback)draw_zoom_cb          },
3947   { "ZoomOut",   GTK_STOCK_ZOOM_OUT,     N_("Zoom _Out"),                 "<control>minus", NULL,                                           (GCallback)draw_zoom_cb          },
3948   { "ZoomTo",    GTK_STOCK_ZOOM_FIT,     N_("Zoom _To..."),               "<control>Z", NULL,                                           (GCallback)zoom_to_cb            },
3949   { "PanNorth",  NULL,                   N_("Pan _North"),                "<control>Up",    NULL,                                           (GCallback)draw_pan_cb },
3950   { "PanEast",   NULL,                   N_("Pan _East"),                 "<control>Right", NULL,                                           (GCallback)draw_pan_cb },
3951   { "PanSouth",  NULL,                   N_("Pan _South"),                "<control>Down",  NULL,                                           (GCallback)draw_pan_cb },
3952   { "PanWest",   NULL,                   N_("Pan _West"),                 "<control>Left",  NULL,                                           (GCallback)draw_pan_cb },
3953   { "BGJobs",    GTK_STOCK_EXECUTE,      N_("Background _Jobs"),              NULL,         NULL,                                           (GCallback)a_background_show_window },
3954
3955   { "Cut",       GTK_STOCK_CUT,          N_("Cu_t"),                          NULL,         NULL,                                           (GCallback)menu_cut_layer_cb     },
3956   { "Copy",      GTK_STOCK_COPY,         N_("_Copy"),                         NULL,         NULL,                                           (GCallback)menu_copy_layer_cb    },
3957   { "Paste",     GTK_STOCK_PASTE,        N_("_Paste"),                        NULL,         NULL,                                           (GCallback)menu_paste_layer_cb   },
3958   { "Delete",    GTK_STOCK_DELETE,       N_("_Delete"),                       NULL,         NULL,                                           (GCallback)menu_delete_layer_cb  },
3959   { "DeleteAll", NULL,                   N_("Delete All"),                    NULL,         NULL,                                           (GCallback)clear_cb              },
3960   { "MapCacheFlush",NULL,                N_("_Flush Map Cache"),              NULL,         NULL,                                           (GCallback)mapcache_flush_cb     },
3961   { "SetDefaultLocation", GTK_STOCK_GO_FORWARD, N_("_Set the Default Location"), NULL, N_("Set the Default Location to the current position"),(GCallback)default_location_cb },
3962   { "Preferences",GTK_STOCK_PREFERENCES, N_("_Preferences"),                  NULL,         NULL,                                           (GCallback)preferences_cb              },
3963   { "LayerDefaults",GTK_STOCK_PROPERTIES, N_("_Layer Defaults"),             NULL,         NULL,                                           NULL },
3964   { "Properties",GTK_STOCK_PROPERTIES,   N_("_Properties"),                   NULL,         NULL,                                           (GCallback)menu_properties_cb    },
3965
3966   { "HelpEntry", GTK_STOCK_HELP,         N_("_Help"),                         "F1",         NULL,                                           (GCallback)help_help_cb     },
3967   { "About",     GTK_STOCK_ABOUT,        N_("_About"),                        NULL,         NULL,                                           (GCallback)help_about_cb    },
3968 };
3969
3970 static GtkActionEntry entries_gpsbabel[] = {
3971   { "ExportKML", NULL,                   N_("_KML..."),                       NULL,         N_("Export as KML"),                                (GCallback)export_to_kml },
3972 };
3973
3974 /* Radio items */
3975 /* FIXME use VIEWPORT_DRAWMODE values */
3976 static GtkRadioActionEntry mode_entries[] = {
3977   { "ModeUTM",         NULL,         N_("_UTM Mode"),               "<control>u", NULL, 0 },
3978   { "ModeExpedia",     NULL,         N_("_Expedia Mode"),           "<control>e", NULL, 1 },
3979   { "ModeMercator",    NULL,         N_("_Mercator Mode"),            "<control>m", NULL, 4 },
3980   { "ModeLatLon",      NULL,         N_("Lat_/Lon Mode"),           "<control>l", NULL, 5 },
3981 };
3982
3983 static GtkToggleActionEntry toggle_entries[] = {
3984   { "ShowScale",      NULL,                 N_("Show _Scale"),               "<shift>F5",  N_("Show Scale"),                              (GCallback)set_draw_scale, TRUE },
3985   { "ShowCenterMark", NULL,                 N_("Show _Center Mark"),         "F6",         N_("Show Center Mark"),                        (GCallback)set_draw_centermark, TRUE },
3986   { "ShowHighlight",  GTK_STOCK_UNDERLINE,  N_("Show _Highlight"),           "F7",         N_("Show Highlight"),                          (GCallback)set_draw_highlight, TRUE },
3987   { "FullScreen",     GTK_STOCK_FULLSCREEN, N_("_Full Screen"),              "F11",        N_("Activate full screen mode"),               (GCallback)full_screen_cb, FALSE },
3988   { "ViewSidePanel",  GTK_STOCK_INDEX,      N_("Show Side _Panel"),          "F9",         N_("Show Side Panel"),                         (GCallback)view_side_panel_cb, TRUE },
3989   { "ViewStatusBar",  NULL,                 N_("Show Status_bar"),           "F12",        N_("Show Statusbar"),                          (GCallback)view_statusbar_cb, TRUE },
3990   { "ViewToolbar",    NULL,                 N_("Show _Toolbar"),             "F3",         N_("Show Toolbar"),                            (GCallback)view_toolbar_cb, TRUE },
3991   { "ViewMainMenu",   NULL,                 N_("Show _Menu"),                "F4",         N_("Show Menu"),                               (GCallback)view_main_menu_cb, TRUE },
3992 };
3993
3994 #include "menu.xml.h"
3995 static void window_create_ui( VikWindow *window )
3996 {
3997   GtkUIManager *uim;
3998   GtkActionGroup *action_group;
3999   GtkAccelGroup *accel_group;
4000   GError *error;
4001   guint i, j, mid;
4002   GtkIconFactory *icon_factory;
4003   GtkIconSet *icon_set; 
4004   GtkRadioActionEntry *tools = NULL, *radio;
4005   guint ntools;
4006   
4007   uim = gtk_ui_manager_new ();
4008   window->uim = uim;
4009
4010   toolbox_add_tool(window->vt, &ruler_tool, TOOL_LAYER_TYPE_NONE);
4011   toolbox_add_tool(window->vt, &zoom_tool, TOOL_LAYER_TYPE_NONE);
4012   toolbox_add_tool(window->vt, &pan_tool, TOOL_LAYER_TYPE_NONE);
4013   toolbox_add_tool(window->vt, &select_tool, TOOL_LAYER_TYPE_NONE);
4014
4015   error = NULL;
4016   if (!(mid = gtk_ui_manager_add_ui_from_string (uim, menu_xml, -1, &error))) {
4017     g_error_free (error);
4018     exit (1);
4019   }
4020
4021   action_group = gtk_action_group_new ("MenuActions");
4022   gtk_action_group_set_translation_domain(action_group, PACKAGE_NAME);
4023   gtk_action_group_add_actions (action_group, entries, G_N_ELEMENTS (entries), window);
4024   gtk_action_group_add_toggle_actions (action_group, toggle_entries, G_N_ELEMENTS (toggle_entries), window);
4025   gtk_action_group_add_radio_actions (action_group, mode_entries, G_N_ELEMENTS (mode_entries), 4, (GCallback)window_change_coord_mode_cb, window);
4026
4027   // Use this to see if GPSBabel is available:
4028   if ( a_babel_available () ) {
4029         // If going to add more entries then might be worth creating a menu_gpsbabel.xml.h file
4030         if ( gtk_ui_manager_add_ui_from_string ( uim,
4031           "<ui><menubar name='MainMenu'><menu action='File'><menu action='Export'><menuitem action='ExportKML'/></menu></menu></menubar></ui>",
4032           -1, &error ) )
4033       gtk_action_group_add_actions ( action_group, entries_gpsbabel, G_N_ELEMENTS (entries_gpsbabel), window );
4034   }
4035
4036   icon_factory = gtk_icon_factory_new ();
4037   gtk_icon_factory_add_default (icon_factory); 
4038
4039   register_vik_icons(icon_factory);
4040
4041   // Copy the tool RadioActionEntries out of the main Window structure into an extending array 'tools'
4042   //  so that it can be applied to the UI in one action group add function call below
4043   ntools = 0;
4044   for (i=0; i<window->vt->n_tools; i++) {
4045       tools = g_renew(GtkRadioActionEntry, tools, ntools+1);
4046       radio = &tools[ntools];
4047       ntools++;
4048       *radio = window->vt->tools[i].ti.radioActionEntry;
4049       radio->value = ntools;
4050   }
4051
4052   for (i=0; i<VIK_LAYER_NUM_TYPES; i++) {
4053     GtkActionEntry action;
4054     gtk_ui_manager_add_ui(uim, mid,  "/ui/MainMenu/Layers/", 
4055                           vik_layer_get_interface(i)->name,
4056                           vik_layer_get_interface(i)->name,
4057                           GTK_UI_MANAGER_MENUITEM, FALSE);
4058
4059     icon_set = gtk_icon_set_new_from_pixbuf (gdk_pixbuf_from_pixdata (vik_layer_get_interface(i)->icon, FALSE, NULL ));
4060     gtk_icon_factory_add (icon_factory, vik_layer_get_interface(i)->name, icon_set);
4061     gtk_icon_set_unref (icon_set);
4062
4063     action.name = vik_layer_get_interface(i)->name;
4064     action.stock_id = vik_layer_get_interface(i)->name;
4065     action.label = g_strdup_printf( _("New _%s Layer"), vik_layer_get_interface(i)->name);
4066     action.accelerator = vik_layer_get_interface(i)->accelerator;
4067     action.tooltip = NULL;
4068     action.callback = (GCallback)menu_addlayer_cb;
4069     gtk_action_group_add_actions(action_group, &action, 1, window);
4070
4071     g_free ( (gchar*)action.label );
4072
4073     if ( vik_layer_get_interface(i)->tools_count ) {
4074       gtk_ui_manager_add_ui(uim, mid,  "/ui/MainMenu/Tools/", vik_layer_get_interface(i)->name, NULL, GTK_UI_MANAGER_SEPARATOR, FALSE);
4075       gtk_ui_manager_add_ui(uim, mid,  "/ui/MainToolbar/ToolItems/", vik_layer_get_interface(i)->name, NULL, GTK_UI_MANAGER_SEPARATOR, FALSE);
4076     }
4077
4078     // Further tool copying for to apply to the UI, also apply menu UI setup
4079     for ( j = 0; j < vik_layer_get_interface(i)->tools_count; j++ ) {
4080       tools = g_renew(GtkRadioActionEntry, tools, ntools+1);
4081       radio = &tools[ntools];
4082       ntools++;
4083       
4084       gtk_ui_manager_add_ui(uim, mid,  "/ui/MainMenu/Tools", 
4085                             vik_layer_get_interface(i)->tools[j].radioActionEntry.label,
4086                             vik_layer_get_interface(i)->tools[j].radioActionEntry.name,
4087                             GTK_UI_MANAGER_MENUITEM, FALSE);
4088       gtk_ui_manager_add_ui(uim, mid,  "/ui/MainToolbar/ToolItems", 
4089                             vik_layer_get_interface(i)->tools[j].radioActionEntry.label,
4090                             vik_layer_get_interface(i)->tools[j].radioActionEntry.name,
4091                             GTK_UI_MANAGER_TOOLITEM, FALSE);
4092
4093       toolbox_add_tool(window->vt, &(vik_layer_get_interface(i)->tools[j]), i);
4094
4095       *radio = vik_layer_get_interface(i)->tools[j].radioActionEntry;
4096       // Overwrite with actual number to use
4097       radio->value = ntools;
4098     }
4099
4100     GtkActionEntry action_dl;
4101     gchar *layername = g_strdup_printf ( "Layer%s", vik_layer_get_interface(i)->fixed_layer_name );
4102     gtk_ui_manager_add_ui(uim, mid,  "/ui/MainMenu/Edit/LayerDefaults",
4103                           vik_layer_get_interface(i)->name,
4104                           layername,
4105                           GTK_UI_MANAGER_MENUITEM, FALSE);
4106     g_free (layername);
4107
4108     // For default layers use action names of the form 'Layer<LayerName>'
4109     // This is to avoid clashing with just the layer name used above for the tool actions
4110     action_dl.name = g_strconcat("Layer", vik_layer_get_interface(i)->fixed_layer_name, NULL);
4111     action_dl.stock_id = NULL;
4112     action_dl.label = g_strconcat("_", vik_layer_get_interface(i)->name, "...", NULL); // Prepend marker for keyboard accelerator
4113     action_dl.accelerator = NULL;
4114     action_dl.tooltip = NULL;
4115     action_dl.callback = (GCallback)layer_defaults_cb;
4116     gtk_action_group_add_actions(action_group, &action_dl, 1, window);
4117     g_free ( (gchar*)action_dl.name );
4118     g_free ( (gchar*)action_dl.label );
4119   }
4120   g_object_unref (icon_factory);
4121
4122   gtk_action_group_add_radio_actions(action_group, tools, ntools, 0, (GCallback)menu_tool_cb, window);
4123   g_free(tools);
4124
4125   gtk_ui_manager_insert_action_group (uim, action_group, 0);
4126
4127   for (i=0; i<VIK_LAYER_NUM_TYPES; i++) {
4128     for ( j = 0; j < vik_layer_get_interface(i)->tools_count; j++ ) {
4129       GtkAction *action = gtk_action_group_get_action(action_group,
4130                             vik_layer_get_interface(i)->tools[j].radioActionEntry.name);
4131       g_object_set(action, "sensitive", FALSE, NULL);
4132     }
4133   }
4134
4135   // This is done last so we don't need to track the value of mid anymore
4136   vik_ext_tools_add_action_items ( window, window->uim, action_group, mid );
4137
4138   window->action_group = action_group;
4139
4140   accel_group = gtk_ui_manager_get_accel_group (uim);
4141   gtk_window_add_accel_group (GTK_WINDOW (window), accel_group);
4142   gtk_ui_manager_ensure_update (uim);
4143   
4144   setup_recent_files(window);
4145 }
4146
4147
4148 // TODO - add method to add tool icons defined from outside this file
4149 //  and remove the reverse dependency on icon definition from this file
4150 static struct { 
4151   const GdkPixdata *data;
4152   gchar *stock_id;
4153 } stock_icons[] = {
4154   { &mover_22_pixbuf,           "vik-icon-pan"      },
4155   { &zoom_18_pixbuf,            "vik-icon-zoom"     },
4156   { &ruler_18_pixbuf,           "vik-icon-ruler"    },
4157   { &select_18_pixbuf,          "vik-icon-select"   },
4158   { &vik_new_route_18_pixbuf,   "vik-icon-Create Route"     },
4159   { &route_finder_18_pixbuf,    "vik-icon-Route Finder"     },
4160   { &demdl_18_pixbuf,           "vik-icon-DEM Download"     },
4161   { &showpic_18_pixbuf,         "vik-icon-Show Picture"     },
4162   { &addtr_18_pixbuf,           "vik-icon-Create Track"     },
4163   { &edtr_18_pixbuf,            "vik-icon-Edit Trackpoint"  },
4164   { &addwp_18_pixbuf,           "vik-icon-Create Waypoint"  },
4165   { &edwp_18_pixbuf,            "vik-icon-Edit Waypoint"    },
4166   { &geozoom_18_pixbuf,         "vik-icon-Georef Zoom Tool" },
4167   { &geomove_18_pixbuf,         "vik-icon-Georef Move Map"  },
4168   { &mapdl_18_pixbuf,           "vik-icon-Maps Download"    },
4169 };
4170  
4171 static gint n_stock_icons = G_N_ELEMENTS (stock_icons);
4172
4173 static void
4174 register_vik_icons (GtkIconFactory *icon_factory)
4175 {
4176   GtkIconSet *icon_set; 
4177   gint i;
4178
4179   for (i = 0; i < n_stock_icons; i++) {
4180     icon_set = gtk_icon_set_new_from_pixbuf (gdk_pixbuf_from_pixdata (
4181                    stock_icons[i].data, FALSE, NULL ));
4182     gtk_icon_factory_add (icon_factory, stock_icons[i].stock_id, icon_set);
4183     gtk_icon_set_unref (icon_set);
4184   }
4185 }
4186
4187 gpointer vik_window_get_selected_trw_layer ( VikWindow *vw )
4188 {
4189   return vw->selected_vtl;
4190 }
4191
4192 void vik_window_set_selected_trw_layer ( VikWindow *vw, gpointer vtl )
4193 {
4194   vw->selected_vtl   = vtl;
4195   vw->containing_vtl = vtl;
4196   /* Clear others */
4197   vw->selected_track     = NULL;
4198   vw->selected_tracks    = NULL;
4199   vw->selected_waypoint  = NULL;
4200   vw->selected_waypoints = NULL;
4201   // Set highlight thickness
4202   vik_viewport_set_highlight_thickness ( vw->viking_vvp, vik_trw_layer_get_property_tracks_line_thickness (vw->containing_vtl) );
4203 }
4204
4205 GHashTable *vik_window_get_selected_tracks ( VikWindow *vw )
4206 {
4207   return vw->selected_tracks;
4208 }
4209
4210 void vik_window_set_selected_tracks ( VikWindow *vw, GHashTable *ght, gpointer vtl )
4211 {
4212   vw->selected_tracks = ght;
4213   vw->containing_vtl  = vtl;
4214   /* Clear others */
4215   vw->selected_vtl       = NULL;
4216   vw->selected_track     = NULL;
4217   vw->selected_waypoint  = NULL;
4218   vw->selected_waypoints = NULL;
4219   // Set highlight thickness
4220   vik_viewport_set_highlight_thickness ( vw->viking_vvp, vik_trw_layer_get_property_tracks_line_thickness (vw->containing_vtl) );
4221 }
4222
4223 gpointer vik_window_get_selected_track ( VikWindow *vw )
4224 {
4225   return vw->selected_track;
4226 }
4227
4228 void vik_window_set_selected_track ( VikWindow *vw, gpointer *vt, gpointer vtl )
4229 {
4230   vw->selected_track = vt;
4231   vw->containing_vtl = vtl;
4232   /* Clear others */
4233   vw->selected_vtl       = NULL;
4234   vw->selected_tracks    = NULL;
4235   vw->selected_waypoint  = NULL;
4236   vw->selected_waypoints = NULL;
4237   // Set highlight thickness
4238   vik_viewport_set_highlight_thickness ( vw->viking_vvp, vik_trw_layer_get_property_tracks_line_thickness (vw->containing_vtl) );
4239 }
4240
4241 GHashTable *vik_window_get_selected_waypoints ( VikWindow *vw )
4242 {
4243   return vw->selected_waypoints;
4244 }
4245
4246 void vik_window_set_selected_waypoints ( VikWindow *vw, GHashTable *ght, gpointer vtl )
4247 {
4248   vw->selected_waypoints = ght;
4249   vw->containing_vtl     = vtl;
4250   /* Clear others */
4251   vw->selected_vtl       = NULL;
4252   vw->selected_track     = NULL;
4253   vw->selected_tracks    = NULL;
4254   vw->selected_waypoint  = NULL;
4255 }
4256
4257 gpointer vik_window_get_selected_waypoint ( VikWindow *vw )
4258 {
4259   return vw->selected_waypoint;
4260 }
4261
4262 void vik_window_set_selected_waypoint ( VikWindow *vw, gpointer *vwp, gpointer vtl )
4263 {
4264   vw->selected_waypoint = vwp;
4265   vw->containing_vtl    = vtl;
4266   /* Clear others */
4267   vw->selected_vtl       = NULL;
4268   vw->selected_track     = NULL;
4269   vw->selected_tracks    = NULL;
4270   vw->selected_waypoints = NULL;
4271 }
4272
4273 gboolean vik_window_clear_highlight ( VikWindow *vw )
4274 {
4275   gboolean need_redraw = FALSE;
4276   if ( vw->selected_vtl != NULL ) {
4277     vw->selected_vtl = NULL;
4278     need_redraw = TRUE;
4279   }
4280   if ( vw->selected_track != NULL ) {
4281     vw->selected_track = NULL;
4282     need_redraw = TRUE;
4283   }
4284   if ( vw->selected_tracks != NULL ) {
4285     vw->selected_tracks = NULL;
4286     need_redraw = TRUE;
4287   }
4288   if ( vw->selected_waypoint != NULL ) {
4289     vw->selected_waypoint = NULL;
4290     need_redraw = TRUE;
4291   }
4292   if ( vw->selected_waypoints != NULL ) {
4293     vw->selected_waypoints = NULL;
4294     need_redraw = TRUE;
4295   }
4296   return need_redraw;
4297 }
4298
4299 GThread *vik_window_get_thread ( VikWindow *vw )
4300 {
4301   return vw->thread;
4302 }