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