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