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