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