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