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