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