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