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