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