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