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