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