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