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