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