]> git.street.me.uk Git - andy/viking.git/blob - src/viktrwlayer.c
Extend Routing functions for UI reuse.
[andy/viking.git] / src / viktrwlayer.c
1 /*
2  * viking -- GPS Data and Topo Analyzer, Explorer, and Manager
3  *
4  * Copyright (C) 2003-2007, Evan Battaglia <gtoevan@gmx.net>
5  * Copyright (C) 2005-2008, Alex Foobarian <foobarian@gmail.com>
6  * Copyright (C) 2007, Quy Tonthat <qtonthat@gmail.com>
7  * Copyright (C) 2009, Hein Ragas <viking@ragas.nl>
8  * Copyright (c) 2012, Rob Norris <rw_norris@hotmail.com>
9  * Copyright (c) 2012-2013, Guilhem Bonnefille <guilhem.bonnefille@gmail.com>
10  *
11  * This program is free software; you can redistribute it and/or modify
12  * it under the terms of the GNU General Public License as published by
13  * the Free Software Foundation; either version 2 of the License, or
14  * (at your option) any later version.
15  *
16  * This program is distributed in the hope that it will be useful,
17  * but WITHOUT ANY WARRANTY; without even the implied warranty of
18  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19  * GNU General Public License for more details.
20  *
21  * You should have received a copy of the GNU General Public License
22  * along with this program; if not, write to the Free Software
23  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
24  *
25  */
26 /* WARNING: If you go beyond this point, we are NOT responsible for any ill effects on your sanity */
27 /* viktrwlayer.c -- 8000+ lines can make a difference in the state of things */
28
29 #ifdef HAVE_CONFIG_H
30 #include "config.h"
31 #endif
32
33 #include "viking.h"
34 #include "vikmapslayer.h"
35 #include "vikgpslayer.h"
36 #include "viktrwlayer_export.h"
37 #include "viktrwlayer_tpwin.h"
38 #include "viktrwlayer_propwin.h"
39 #include "viktrwlayer_analysis.h"
40 #include "viktrwlayer_tracklist.h"
41 #include "viktrwlayer_waypointlist.h"
42 #ifdef VIK_CONFIG_GEOTAG
43 #include "viktrwlayer_geotag.h"
44 #include "geotag_exif.h"
45 #endif
46 #include "garminsymbols.h"
47 #include "thumbnails.h"
48 #include "background.h"
49 #include "gpx.h"
50 #include "babel.h"
51 #include "dem.h"
52 #include "dems.h"
53 #include "geonamessearch.h"
54 #ifdef VIK_CONFIG_OPENSTREETMAP
55 #include "osm-traces.h"
56 #endif
57 #include "acquire.h"
58 #include "datasources.h"
59 #include "datasource_gps.h"
60 #include "vikexttool_datasources.h"
61 #include "util.h"
62 #include "vikutils.h"
63
64 #include "vikrouting.h"
65
66 #include "icons/icons.h"
67
68 #ifdef HAVE_MATH_H
69 #include <math.h>
70 #endif
71 #ifdef HAVE_STRING_H
72 #include <string.h>
73 #endif
74 #ifdef HAVE_STDLIB_H
75 #include <stdlib.h>
76 #endif
77 #include <stdio.h>
78 #include <ctype.h>
79
80 #include <gdk/gdkkeysyms.h>
81 #include <glib.h>
82 #include <glib/gstdio.h>
83 #include <glib/gi18n.h>
84
85 #define VIK_TRW_LAYER_TRACK_GC 6
86 #define VIK_TRW_LAYER_TRACK_GCS 10
87 #define VIK_TRW_LAYER_TRACK_GC_BLACK 0
88 #define VIK_TRW_LAYER_TRACK_GC_SLOW 1
89 #define VIK_TRW_LAYER_TRACK_GC_AVER 2
90 #define VIK_TRW_LAYER_TRACK_GC_FAST 3
91 #define VIK_TRW_LAYER_TRACK_GC_STOP 4
92 #define VIK_TRW_LAYER_TRACK_GC_SINGLE 5
93
94 #define DRAWMODE_BY_TRACK 0
95 #define DRAWMODE_BY_SPEED 1
96 #define DRAWMODE_ALL_SAME_COLOR 2
97 // Note using DRAWMODE_BY_SPEED may be slow especially for vast numbers of trackpoints
98 //  as we are (re)calculating the colour for every point
99
100 #define POINTS 1
101 #define LINES 2
102
103 /* this is how it knows when you click if you are clicking close to a trackpoint. */
104 #define TRACKPOINT_SIZE_APPROX 5
105 #define WAYPOINT_SIZE_APPROX 5
106
107 #define MIN_STOP_LENGTH 15
108 #define MAX_STOP_LENGTH 86400
109 #define DRAW_ELEVATION_FACTOR 30 /* height of elevation plotting, sort of relative to zoom level ("mpp" that isn't mpp necessarily) */
110                                  /* this is multiplied by user-inputted value from 1-100. */
111
112 enum { WP_SYMBOL_FILLED_SQUARE, WP_SYMBOL_SQUARE, WP_SYMBOL_CIRCLE, WP_SYMBOL_X, WP_NUM_SYMBOLS };
113
114 // See http://developer.gnome.org/pango/stable/PangoMarkupFormat.html
115 typedef enum {
116   FS_XX_SMALL = 0, // 'xx-small'
117   FS_X_SMALL,
118   FS_SMALL,
119   FS_MEDIUM, // DEFAULT
120   FS_LARGE,
121   FS_X_LARGE,
122   FS_XX_LARGE,
123   FS_NUM_SIZES
124 } font_size_t;
125
126 struct _VikTrwLayer {
127   VikLayer vl;
128   GHashTable *tracks;
129   GHashTable *tracks_iters;
130   GHashTable *routes;
131   GHashTable *routes_iters;
132   GHashTable *waypoints_iters;
133   GHashTable *waypoints;
134   GtkTreeIter tracks_iter, routes_iter, waypoints_iter;
135   gboolean tracks_visible, routes_visible, waypoints_visible;
136   LatLonBBox waypoints_bbox;
137
138   gboolean track_draw_labels;
139   guint8 drawmode;
140   guint8 drawpoints;
141   guint8 drawpoints_size;
142   guint8 drawelevation;
143   guint8 elevation_factor;
144   guint8 drawstops;
145   guint32 stop_length;
146   guint8 drawlines;
147   guint8 drawdirections;
148   guint8 drawdirections_size;
149   guint8 line_thickness;
150   guint8 bg_line_thickness;
151   vik_layer_sort_order_t track_sort_order;
152
153   // Metadata
154   VikTRWMetadata *metadata;
155
156   PangoLayout *tracklabellayout;
157   font_size_t track_font_size;
158   gchar *track_fsize_str;
159
160   guint8 wp_symbol;
161   guint8 wp_size;
162   gboolean wp_draw_symbols;
163   font_size_t wp_font_size;
164   gchar *wp_fsize_str;
165   vik_layer_sort_order_t wp_sort_order;
166
167   gdouble track_draw_speed_factor;
168   GArray *track_gc;
169   GdkGC *track_1color_gc;
170   GdkColor track_color;
171   GdkGC *current_track_gc;
172   // Separate GC for a track's potential new point as drawn via separate method
173   //  (compared to the actual track points drawn in the main trw_layer_draw_track function)
174   GdkGC *current_track_newpoint_gc;
175   GdkGC *track_bg_gc; GdkColor track_bg_color;
176   GdkGC *waypoint_gc; GdkColor waypoint_color;
177   GdkGC *waypoint_text_gc; GdkColor waypoint_text_color;
178   GdkGC *waypoint_bg_gc; GdkColor waypoint_bg_color;
179   gboolean wpbgand;
180   GdkFont *waypoint_font;
181   VikTrack *current_track; // ATM shared between new tracks and new routes
182   guint16 ct_x1, ct_y1, ct_x2, ct_y2;
183   gboolean draw_sync_done;
184   gboolean draw_sync_do;
185
186   VikCoordMode coord_mode;
187
188   /* wp editing tool */
189   VikWaypoint *current_wp;
190   gpointer current_wp_id;
191   gboolean moving_wp;
192   gboolean waypoint_rightclick;
193
194   /* track editing tool */
195   GList *current_tpl;
196   VikTrack *current_tp_track;
197   gpointer current_tp_id;
198   VikTrwLayerTpwin *tpwin;
199
200   /* track editing tool -- more specifically, moving tps */
201   gboolean moving_tp;
202
203   /* route finder tool */
204   gboolean route_finder_started;
205   VikCoord route_finder_coord;
206   gboolean route_finder_check_added_track;
207   VikTrack *route_finder_added_track;
208   VikTrack *route_finder_current_track;
209   gboolean route_finder_append;
210
211   gboolean drawlabels;
212   gboolean drawimages;
213   guint8 image_alpha;
214   GQueue *image_cache;
215   guint8 image_size;
216   guint16 image_cache_size;
217
218   /* for waypoint text */
219   PangoLayout *wplabellayout;
220
221   gboolean has_verified_thumbnails;
222
223   GtkMenu *wp_right_click_menu;
224   GtkMenu *track_right_click_menu;
225
226   /* menu */
227   VikStdLayerMenuItem menu_selection;
228
229   gint highest_wp_number;
230
231   // One per layer
232   GtkWidget *tracks_analysis_dialog;
233 };
234
235 /* A caached waypoint image. */
236 typedef struct {
237   GdkPixbuf *pixbuf;
238   gchar *image; /* filename */
239 } CachedPixbuf;
240
241 struct DrawingParams {
242   VikViewport *vp;
243   VikTrwLayer *vtl;
244   VikWindow *vw;
245   gdouble xmpp, ympp;
246   guint16 width, height;
247   gdouble cc; // Cosine factor in track directions
248   gdouble ss; // Sine factor in track directions
249   const VikCoord *center;
250   gboolean one_zone, lat_lon;
251   gdouble ce1, ce2, cn1, cn2;
252   LatLonBBox bbox;
253   gboolean highlight;
254 };
255
256 static gboolean trw_layer_delete_waypoint ( VikTrwLayer *vtl, VikWaypoint *wp );
257
258 typedef enum {
259   MA_VTL = 0,
260   MA_VLP,
261   MA_SUBTYPE, // OR END for Layer only
262   MA_SUBLAYER_ID,
263   MA_CONFIRM,
264   MA_VVP,
265   MA_TV_ITER,
266   MA_MISC,
267   MA_LAST,
268 } menu_array_index;
269
270 typedef gpointer menu_array_layer[2];
271 typedef gpointer menu_array_sublayer[MA_LAST];
272
273 static void trw_layer_delete_item ( menu_array_sublayer values );
274 static void trw_layer_copy_item_cb ( menu_array_sublayer values );
275 static void trw_layer_cut_item_cb ( menu_array_sublayer values );
276
277 static void trw_layer_find_maxmin_tracks ( const gpointer id, const VikTrack *trk, struct LatLon maxmin[2] );
278 static void trw_layer_find_maxmin (VikTrwLayer *vtl, struct LatLon maxmin[2]);
279
280 static void trw_layer_new_track_gcs ( VikTrwLayer *vtl, VikViewport *vp );
281 static void trw_layer_free_track_gcs ( VikTrwLayer *vtl );
282
283 static void trw_layer_draw_track_cb ( const gpointer id, VikTrack *track, struct DrawingParams *dp );
284 static void trw_layer_draw_waypoint ( const gpointer id, VikWaypoint *wp, struct DrawingParams *dp );
285
286 static void goto_coord ( gpointer *vlp, gpointer vvp, gpointer vl, const VikCoord *coord );
287 static void trw_layer_goto_track_startpoint ( menu_array_sublayer values );
288 static void trw_layer_goto_track_endpoint ( menu_array_sublayer values );
289 static void trw_layer_goto_track_max_speed ( menu_array_sublayer values );
290 static void trw_layer_goto_track_max_alt ( menu_array_sublayer values );
291 static void trw_layer_goto_track_min_alt ( menu_array_sublayer values );
292 static void trw_layer_goto_track_center ( menu_array_sublayer values );
293 static void trw_layer_merge_by_segment ( menu_array_sublayer values );
294 static void trw_layer_merge_by_timestamp ( menu_array_sublayer values );
295 static void trw_layer_merge_with_other ( menu_array_sublayer values );
296 static void trw_layer_append_track ( menu_array_sublayer values );
297 static void trw_layer_split_by_timestamp ( menu_array_sublayer values );
298 static void trw_layer_split_by_n_points ( menu_array_sublayer values );
299 static void trw_layer_split_at_trackpoint ( menu_array_sublayer values );
300 static void trw_layer_split_segments ( menu_array_sublayer values );
301 static void trw_layer_delete_point_selected ( menu_array_sublayer values );
302 static void trw_layer_delete_points_same_position ( menu_array_sublayer values );
303 static void trw_layer_delete_points_same_time ( menu_array_sublayer values );
304 static void trw_layer_reverse ( menu_array_sublayer values );
305 static void trw_layer_download_map_along_track_cb ( menu_array_sublayer values );
306 static void trw_layer_edit_trackpoint ( menu_array_sublayer values );
307 static void trw_layer_show_picture ( menu_array_sublayer values );
308 static void trw_layer_gps_upload_any ( menu_array_sublayer values );
309
310 static void trw_layer_centerize ( menu_array_layer values );
311 static void trw_layer_auto_view ( menu_array_layer values );
312 static void trw_layer_goto_wp ( menu_array_layer values );
313 static void trw_layer_new_wp ( menu_array_layer values );
314 static void trw_layer_new_track ( menu_array_layer values );
315 static void trw_layer_new_route ( menu_array_layer values );
316 static void trw_layer_finish_track ( menu_array_layer values );
317 static void trw_layer_auto_waypoints_view ( menu_array_layer values );
318 static void trw_layer_auto_tracks_view ( menu_array_layer values );
319 static void trw_layer_delete_all_tracks ( menu_array_layer values );
320 static void trw_layer_delete_tracks_from_selection ( menu_array_layer values );
321 static void trw_layer_delete_all_waypoints ( menu_array_layer values );
322 static void trw_layer_delete_waypoints_from_selection ( menu_array_layer values );
323 static void trw_layer_new_wikipedia_wp_viewport ( menu_array_layer values );
324 static void trw_layer_new_wikipedia_wp_layer ( menu_array_layer values );
325 #ifdef VIK_CONFIG_GEOTAG
326 static void trw_layer_geotagging_waypoint_mtime_keep ( menu_array_sublayer values );
327 static void trw_layer_geotagging_waypoint_mtime_update ( menu_array_sublayer values );
328 static void trw_layer_geotagging_track ( menu_array_sublayer values );
329 static void trw_layer_geotagging ( menu_array_layer values );
330 #endif
331 static void trw_layer_acquire_gps_cb ( menu_array_layer values );
332 static void trw_layer_acquire_routing_cb ( menu_array_layer values );
333 static void trw_layer_acquire_url_cb ( menu_array_layer values );
334 #ifdef VIK_CONFIG_OPENSTREETMAP
335 static void trw_layer_acquire_osm_cb ( menu_array_layer values );
336 static void trw_layer_acquire_osm_my_traces_cb ( menu_array_layer values );
337 #endif
338 #ifdef VIK_CONFIG_GEOCACHES
339 static void trw_layer_acquire_geocache_cb ( menu_array_layer values );
340 #endif
341 #ifdef VIK_CONFIG_GEOTAG
342 static void trw_layer_acquire_geotagged_cb ( menu_array_layer values );
343 #endif
344 static void trw_layer_acquire_file_cb ( menu_array_layer values );
345 static void trw_layer_gps_upload ( menu_array_layer values );
346
347 static void trw_layer_track_list_dialog_single ( menu_array_sublayer values );
348 static void trw_layer_track_list_dialog ( menu_array_layer values );
349 static void trw_layer_waypoint_list_dialog ( menu_array_layer values );
350
351 // Specific route versions:
352 //  Most track handling functions can handle operating on the route list
353 //  However these ones are easier in separate functions
354 static void trw_layer_auto_routes_view ( menu_array_layer values );
355 static void trw_layer_delete_all_routes ( menu_array_layer values );
356 static void trw_layer_delete_routes_from_selection ( menu_array_layer values );
357
358 /* pop-up items */
359 static void trw_layer_properties_item ( gpointer pass_along[7] ); //TODO??
360 static void trw_layer_goto_waypoint ( menu_array_sublayer values );
361 static void trw_layer_waypoint_gc_webpage ( menu_array_sublayer values );
362 static void trw_layer_waypoint_webpage ( menu_array_sublayer values );
363
364 static void trw_layer_realize_waypoint ( gpointer id, VikWaypoint *wp, gpointer pass_along[5] );
365 static void trw_layer_realize_track ( gpointer id, VikTrack *track, gpointer pass_along[5] );
366
367 static void trw_layer_insert_tp_beside_current_tp ( VikTrwLayer *vtl, gboolean );
368 static void trw_layer_cancel_current_tp ( VikTrwLayer *vtl, gboolean destroy );
369 static void trw_layer_tpwin_response ( VikTrwLayer *vtl, gint response );
370 static void trw_layer_tpwin_init ( VikTrwLayer *vtl );
371
372 static gpointer tool_edit_trackpoint_create ( VikWindow *vw, VikViewport *vvp);
373 static void tool_edit_trackpoint_destroy ( tool_ed_t *t );
374 static gboolean tool_edit_trackpoint_click ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data );
375 static gboolean tool_edit_trackpoint_move ( VikTrwLayer *vtl, GdkEventMotion *event, gpointer data );
376 static gboolean tool_edit_trackpoint_release ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data );
377 static gpointer tool_show_picture_create ( VikWindow *vw, VikViewport *vvp);
378 static gboolean tool_show_picture_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp ); 
379 static gpointer tool_edit_waypoint_create ( VikWindow *vw, VikViewport *vvp);
380 static void tool_edit_waypoint_destroy ( tool_ed_t *t );
381 static gboolean tool_edit_waypoint_click ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data );
382 static gboolean tool_edit_waypoint_move ( VikTrwLayer *vtl, GdkEventMotion *event, gpointer data );
383 static gboolean tool_edit_waypoint_release ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data );
384 static gpointer tool_new_route_create ( VikWindow *vw, VikViewport *vvp);
385 static gboolean tool_new_route_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp );
386 static gpointer tool_new_track_create ( VikWindow *vw, VikViewport *vvp);
387 static gboolean tool_new_track_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp ); 
388 static VikLayerToolFuncStatus tool_new_track_move ( VikTrwLayer *vtl, GdkEventMotion *event, VikViewport *vvp ); 
389 static void tool_new_track_release ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp );
390 static gboolean tool_new_track_key_press ( VikTrwLayer *vtl, GdkEventKey *event, VikViewport *vvp ); 
391 static gpointer tool_new_waypoint_create ( VikWindow *vw, VikViewport *vvp);
392 static gboolean tool_new_waypoint_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp );
393 static gpointer tool_route_finder_create ( VikWindow *vw, VikViewport *vvp);
394 static gboolean tool_route_finder_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp );
395
396 static void cached_pixbuf_free ( CachedPixbuf *cp );
397 static gint cached_pixbuf_cmp ( CachedPixbuf *cp, const gchar *name );
398
399 static VikTrackpoint *closest_tp_in_five_pixel_interval ( VikTrwLayer *vtl, VikViewport *vvp, gint x, gint y );
400 static VikWaypoint *closest_wp_in_five_pixel_interval ( VikTrwLayer *vtl, VikViewport *vvp, gint x, gint y );
401
402 static void waypoint_convert ( const gpointer id, VikWaypoint *wp, VikCoordMode *dest_mode );
403 static void track_convert ( const gpointer id, VikTrack *tr, VikCoordMode *dest_mode );
404
405 static gchar *highest_wp_number_get(VikTrwLayer *vtl);
406 static void highest_wp_number_reset(VikTrwLayer *vtl);
407 static void highest_wp_number_add_wp(VikTrwLayer *vtl, const gchar *new_wp_name);
408 static void highest_wp_number_remove_wp(VikTrwLayer *vtl, const gchar *old_wp_name);
409
410 // Note for the following tool GtkRadioActionEntry texts:
411 //  the very first text value is an internal name not displayed anywhere
412 //  the first N_ text value is the name used for menu entries - hence has an underscore for the keyboard accelerator
413 //    * remember not to clash with the values used for VikWindow level tools (Pan, Zoom, Ruler + Select)
414 //  the second N_ text value is used for the button tooltip (i.e. generally don't want an underscore here)
415 //  the value is always set to 0 and the tool loader in VikWindow will set the actual appropriate value used
416 static VikToolInterface trw_layer_tools[] = {
417   { { "CreateWaypoint", "vik-icon-Create Waypoint", N_("Create _Waypoint"), "<control><shift>W", N_("Create Waypoint"), 0 },
418     (VikToolConstructorFunc) tool_new_waypoint_create,    NULL, NULL, NULL,
419     (VikToolMouseFunc) tool_new_waypoint_click,    NULL, NULL, (VikToolKeyFunc) NULL,
420     FALSE,
421     GDK_CURSOR_IS_PIXMAP, &cursor_addwp_pixbuf, NULL },
422
423   { { "CreateTrack", "vik-icon-Create Track", N_("Create _Track"), "<control><shift>T", N_("Create Track"), 0 },
424     (VikToolConstructorFunc) tool_new_track_create,       NULL, NULL, NULL,
425     (VikToolMouseFunc) tool_new_track_click,
426     (VikToolMouseMoveFunc) tool_new_track_move,
427     (VikToolMouseFunc) tool_new_track_release,
428     (VikToolKeyFunc) tool_new_track_key_press,
429     TRUE, // Still need to handle clicks when in PAN mode to disable the potential trackpoint drawing
430     GDK_CURSOR_IS_PIXMAP, &cursor_addtr_pixbuf, NULL },
431
432   { { "CreateRoute", "vik-icon-Create Route", N_("Create _Route"), "<control><shift>B", N_("Create Route"), 0 },
433     (VikToolConstructorFunc) tool_new_route_create,       NULL, NULL, NULL,
434     (VikToolMouseFunc) tool_new_route_click,
435     (VikToolMouseMoveFunc) tool_new_track_move, // -\#
436     (VikToolMouseFunc) tool_new_track_release,  //   -> Reuse these track methods on a route
437     (VikToolKeyFunc) tool_new_track_key_press,  // -/#
438     TRUE, // Still need to handle clicks when in PAN mode to disable the potential trackpoint drawing
439     GDK_CURSOR_IS_PIXMAP, &cursor_new_route_pixbuf, NULL },
440
441   { { "EditWaypoint", "vik-icon-Edit Waypoint", N_("_Edit Waypoint"), "<control><shift>E", N_("Edit Waypoint"), 0 },
442     (VikToolConstructorFunc) tool_edit_waypoint_create,
443     (VikToolDestructorFunc) tool_edit_waypoint_destroy,
444     NULL, NULL,
445     (VikToolMouseFunc) tool_edit_waypoint_click,   
446     (VikToolMouseMoveFunc) tool_edit_waypoint_move,
447     (VikToolMouseFunc) tool_edit_waypoint_release, (VikToolKeyFunc) NULL,
448     FALSE,
449     GDK_CURSOR_IS_PIXMAP, &cursor_edwp_pixbuf, NULL },
450
451   { { "EditTrackpoint", "vik-icon-Edit Trackpoint", N_("Edit Trac_kpoint"), "<control><shift>K", N_("Edit Trackpoint"), 0 },
452     (VikToolConstructorFunc) tool_edit_trackpoint_create,
453     (VikToolDestructorFunc) tool_edit_trackpoint_destroy,
454     NULL, NULL,
455     (VikToolMouseFunc) tool_edit_trackpoint_click,
456     (VikToolMouseMoveFunc) tool_edit_trackpoint_move,
457     (VikToolMouseFunc) tool_edit_trackpoint_release, (VikToolKeyFunc) NULL,
458     FALSE,
459     GDK_CURSOR_IS_PIXMAP, &cursor_edtr_pixbuf, NULL },
460
461   { { "ShowPicture", "vik-icon-Show Picture", N_("Show P_icture"), "<control><shift>I", N_("Show Picture"), 0 },
462     (VikToolConstructorFunc) tool_show_picture_create,    NULL, NULL, NULL,
463     (VikToolMouseFunc) tool_show_picture_click,    NULL, NULL, (VikToolKeyFunc) NULL,
464     FALSE,
465     GDK_CURSOR_IS_PIXMAP, &cursor_showpic_pixbuf, NULL },
466
467   { { "RouteFinder", "vik-icon-Route Finder", N_("Route _Finder"), "<control><shift>F", N_("Route Finder"), 0 },
468     (VikToolConstructorFunc) tool_route_finder_create,  NULL, NULL, NULL,
469     (VikToolMouseFunc) tool_route_finder_click, NULL, NULL, (VikToolKeyFunc) NULL,
470     FALSE,
471     GDK_CURSOR_IS_PIXMAP, &cursor_route_finder_pixbuf, NULL },
472 };
473
474 enum {
475   TOOL_CREATE_WAYPOINT=0,
476   TOOL_CREATE_TRACK,
477   TOOL_CREATE_ROUTE,
478   TOOL_EDIT_WAYPOINT,
479   TOOL_EDIT_TRACKPOINT,
480   TOOL_SHOW_PICTURE,
481   TOOL_ROUTE_FINDER,
482   NUM_TOOLS
483 };
484
485 /****** PARAMETERS ******/
486
487 static gchar *params_groups[] = { N_("Waypoints"), N_("Tracks"), N_("Waypoint Images"), N_("Tracks Advanced"), N_("Metadata") };
488 enum { GROUP_WAYPOINTS, GROUP_TRACKS, GROUP_IMAGES, GROUP_TRACKS_ADV, GROUP_METADATA };
489
490 static gchar *params_drawmodes[] = { N_("Draw by Track"), N_("Draw by Speed"), N_("All Tracks Same Color"), NULL };
491 static gchar *params_wpsymbols[] = { N_("Filled Square"), N_("Square"), N_("Circle"), N_("X"), 0 };
492
493 #define MIN_POINT_SIZE 2
494 #define MAX_POINT_SIZE 10
495
496 #define MIN_ARROW_SIZE 3
497 #define MAX_ARROW_SIZE 20
498
499 static VikLayerParamScale params_scales[] = {
500  /* min  max    step digits */
501  {  1,   10,    1,   0 }, /* line_thickness */
502  {  0,   100,   1,   0 }, /* track draw speed factor */
503  {  1.0, 100.0, 1.0, 2 }, /* UNUSED */
504                 /* 5 * step == how much to turn */
505  {  16,   128,  4,   0 }, // 3: image_size - NB step size ignored when an HSCALE used
506  {   0,   255,  5,   0 }, // 4: image alpha -    "     "      "            "
507  {   5,   500,  5,   0 }, // 5: image cache_size -     "      "
508  {   0,   8,    1,   0 }, // 6: Background line thickness
509  {   1,  64,    1,   0 }, /* wpsize */
510  {   MIN_STOP_LENGTH, MAX_STOP_LENGTH, 1,   0 }, /* stop_length */
511  {   1, 100, 1,   0 }, // 9: elevation factor
512  {   MIN_POINT_SIZE,  MAX_POINT_SIZE,  1,   0 }, // 10: track point size
513  {   MIN_ARROW_SIZE,  MAX_ARROW_SIZE,  1,   0 }, // 11: direction arrow size
514 };
515
516 static gchar* params_font_sizes[] = {
517   N_("Extra Extra Small"),
518   N_("Extra Small"),
519   N_("Small"),
520   N_("Medium"),
521   N_("Large"),
522   N_("Extra Large"),
523   N_("Extra Extra Large"),
524   NULL };
525
526 // Needs to align with vik_layer_sort_order_t
527 static gchar* params_sort_order[] = {
528   N_("None"),
529   N_("Name Ascending"),
530   N_("Name Descending"),
531   NULL
532 };
533
534 static VikLayerParamData black_color_default ( void ) {
535   VikLayerParamData data; gdk_color_parse ( "#000000", &data.c ); return data; // Black
536 }
537 static VikLayerParamData drawmode_default ( void ) { return VIK_LPD_UINT ( DRAWMODE_BY_TRACK ); }
538 static VikLayerParamData line_thickness_default ( void ) { return VIK_LPD_UINT ( 1 ); }
539 static VikLayerParamData trkpointsize_default ( void ) { return VIK_LPD_UINT ( MIN_POINT_SIZE ); }
540 static VikLayerParamData trkdirectionsize_default ( void ) { return VIK_LPD_UINT ( 5 ); }
541 static VikLayerParamData bg_line_thickness_default ( void ) { return VIK_LPD_UINT ( 0 ); }
542 static VikLayerParamData trackbgcolor_default ( void ) {
543   VikLayerParamData data; gdk_color_parse ( "#FFFFFF", &data.c ); return data; // White
544 }
545 static VikLayerParamData elevation_factor_default ( void ) { return VIK_LPD_UINT ( 30 ); }
546 static VikLayerParamData stop_length_default ( void ) { return VIK_LPD_UINT ( 60 ); }
547 static VikLayerParamData speed_factor_default ( void ) { return VIK_LPD_DOUBLE ( 30.0 ); }
548
549 static VikLayerParamData tnfontsize_default ( void ) { return VIK_LPD_UINT ( FS_MEDIUM ); }
550 static VikLayerParamData wpfontsize_default ( void ) { return VIK_LPD_UINT ( FS_MEDIUM ); }
551 static VikLayerParamData wptextcolor_default ( void ) {
552   VikLayerParamData data; gdk_color_parse ( "#FFFFFF", &data.c ); return data; // White
553 }
554 static VikLayerParamData wpbgcolor_default ( void ) {
555   VikLayerParamData data; gdk_color_parse ( "#8383C4", &data.c ); return data; // Kind of Blue
556 }
557 static VikLayerParamData wpsize_default ( void ) { return VIK_LPD_UINT ( 4 ); }
558 static VikLayerParamData wpsymbol_default ( void ) { return VIK_LPD_UINT ( WP_SYMBOL_FILLED_SQUARE ); }
559
560 static VikLayerParamData image_size_default ( void ) { return VIK_LPD_UINT ( 64 ); }
561 static VikLayerParamData image_alpha_default ( void ) { return VIK_LPD_UINT ( 255 ); }
562 static VikLayerParamData image_cache_size_default ( void ) { return VIK_LPD_UINT ( 300 ); }
563
564 static VikLayerParamData sort_order_default ( void ) { return VIK_LPD_UINT ( 0 ); }
565
566 static VikLayerParamData string_default ( void )
567 {
568   VikLayerParamData data;
569   data.s = "";
570   return data;
571 }
572
573 VikLayerParam trw_layer_params[] = {
574   { VIK_LAYER_TRW, "tracks_visible", VIK_LAYER_PARAM_BOOLEAN, VIK_LAYER_NOT_IN_PROPERTIES, NULL, 0, NULL, NULL, NULL, vik_lpd_true_default, NULL, NULL },
575   { VIK_LAYER_TRW, "waypoints_visible", VIK_LAYER_PARAM_BOOLEAN, VIK_LAYER_NOT_IN_PROPERTIES, NULL, 0, NULL, NULL, NULL, vik_lpd_true_default, NULL, NULL },
576   { VIK_LAYER_TRW, "routes_visible", VIK_LAYER_PARAM_BOOLEAN, VIK_LAYER_NOT_IN_PROPERTIES, NULL, 0, NULL, NULL, NULL, vik_lpd_true_default, NULL, NULL },
577
578   { VIK_LAYER_TRW, "trackdrawlabels", VIK_LAYER_PARAM_BOOLEAN, GROUP_TRACKS, N_("Draw Labels"), VIK_LAYER_WIDGET_CHECKBUTTON, NULL, NULL,
579     N_("Note: the individual track controls what labels may be displayed"), vik_lpd_true_default, NULL, NULL },
580   { VIK_LAYER_TRW, "trackfontsize", VIK_LAYER_PARAM_UINT, GROUP_TRACKS_ADV, N_("Track Labels Font Size:"), VIK_LAYER_WIDGET_COMBOBOX, params_font_sizes, NULL, NULL, tnfontsize_default, NULL, NULL },
581   { VIK_LAYER_TRW, "drawmode", VIK_LAYER_PARAM_UINT, GROUP_TRACKS, N_("Track Drawing Mode:"), VIK_LAYER_WIDGET_COMBOBOX, params_drawmodes, NULL, NULL, drawmode_default, NULL, NULL },
582   { VIK_LAYER_TRW, "trackcolor", VIK_LAYER_PARAM_COLOR, GROUP_TRACKS, N_("All Tracks Color:"), VIK_LAYER_WIDGET_COLOR, NULL, NULL,
583     N_("The color used when 'All Tracks Same Color' drawing mode is selected"), black_color_default, NULL, NULL },
584   { VIK_LAYER_TRW, "drawlines", VIK_LAYER_PARAM_BOOLEAN, GROUP_TRACKS, N_("Draw Track Lines"), VIK_LAYER_WIDGET_CHECKBUTTON, NULL, NULL, NULL, vik_lpd_true_default, NULL, NULL },
585   { VIK_LAYER_TRW, "line_thickness", VIK_LAYER_PARAM_UINT, GROUP_TRACKS_ADV, N_("Track Thickness:"), VIK_LAYER_WIDGET_SPINBUTTON, &params_scales[0], NULL, NULL, line_thickness_default, NULL, NULL },
586   { VIK_LAYER_TRW, "drawdirections", VIK_LAYER_PARAM_BOOLEAN, GROUP_TRACKS, N_("Draw Track Direction"), VIK_LAYER_WIDGET_CHECKBUTTON, NULL, NULL, NULL, vik_lpd_false_default, NULL, NULL },
587   { VIK_LAYER_TRW, "trkdirectionsize", VIK_LAYER_PARAM_UINT, GROUP_TRACKS_ADV, N_("Direction Size:"), VIK_LAYER_WIDGET_SPINBUTTON, &params_scales[11], NULL, NULL, trkdirectionsize_default, NULL, NULL },
588   { VIK_LAYER_TRW, "drawpoints", VIK_LAYER_PARAM_BOOLEAN, GROUP_TRACKS, N_("Draw Trackpoints"), VIK_LAYER_WIDGET_CHECKBUTTON, NULL, NULL, NULL, vik_lpd_true_default, NULL, NULL },
589   { VIK_LAYER_TRW, "trkpointsize", VIK_LAYER_PARAM_UINT, GROUP_TRACKS_ADV, N_("Trackpoint Size:"), VIK_LAYER_WIDGET_SPINBUTTON, &params_scales[10], NULL, NULL, trkpointsize_default, NULL, NULL },
590   { VIK_LAYER_TRW, "drawelevation", VIK_LAYER_PARAM_BOOLEAN, GROUP_TRACKS, N_("Draw Elevation"), VIK_LAYER_WIDGET_CHECKBUTTON, NULL, NULL, NULL, vik_lpd_false_default, NULL, NULL },
591   { VIK_LAYER_TRW, "elevation_factor", VIK_LAYER_PARAM_UINT, GROUP_TRACKS_ADV, N_("Draw Elevation Height %:"), VIK_LAYER_WIDGET_HSCALE, &params_scales[9], NULL, NULL, elevation_factor_default, NULL, NULL },
592   { VIK_LAYER_TRW, "drawstops", VIK_LAYER_PARAM_BOOLEAN, GROUP_TRACKS, N_("Draw Stops"), VIK_LAYER_WIDGET_CHECKBUTTON, NULL, NULL,
593     N_("Whether to draw a marker when trackpoints are at the same position but over the minimum stop length apart in time"), vik_lpd_false_default, NULL, NULL },
594   { VIK_LAYER_TRW, "stop_length", VIK_LAYER_PARAM_UINT, GROUP_TRACKS_ADV, N_("Min Stop Length (seconds):"), VIK_LAYER_WIDGET_SPINBUTTON, &params_scales[8], NULL, NULL, stop_length_default, NULL, NULL },
595
596   { VIK_LAYER_TRW, "bg_line_thickness", VIK_LAYER_PARAM_UINT, GROUP_TRACKS_ADV, N_("Track BG Thickness:"), VIK_LAYER_WIDGET_SPINBUTTON, &params_scales[6], NULL, NULL, bg_line_thickness_default, NULL, NULL },
597   { VIK_LAYER_TRW, "trackbgcolor", VIK_LAYER_PARAM_COLOR, GROUP_TRACKS_ADV, N_("Track Background Color"), VIK_LAYER_WIDGET_COLOR, NULL, NULL, NULL, trackbgcolor_default, NULL, NULL },
598   { VIK_LAYER_TRW, "speed_factor", VIK_LAYER_PARAM_DOUBLE, GROUP_TRACKS_ADV, N_("Draw by Speed Factor (%):"), VIK_LAYER_WIDGET_HSCALE, &params_scales[1], NULL,
599     N_("The percentage factor away from the average speed determining the color used"), speed_factor_default, NULL, NULL },
600   { VIK_LAYER_TRW, "tracksortorder", VIK_LAYER_PARAM_UINT, GROUP_TRACKS_ADV, N_("Track Sort Order:"), VIK_LAYER_WIDGET_COMBOBOX, params_sort_order, NULL, NULL, sort_order_default, NULL, NULL },
601
602   { VIK_LAYER_TRW, "drawlabels", VIK_LAYER_PARAM_BOOLEAN, GROUP_WAYPOINTS, N_("Draw Labels"), VIK_LAYER_WIDGET_CHECKBUTTON, NULL, NULL, NULL, vik_lpd_true_default, NULL, NULL },
603   { VIK_LAYER_TRW, "wpfontsize", VIK_LAYER_PARAM_UINT, GROUP_WAYPOINTS, N_("Waypoint Font Size:"), VIK_LAYER_WIDGET_COMBOBOX, params_font_sizes, NULL, NULL, wpfontsize_default, NULL, NULL },
604   { VIK_LAYER_TRW, "wpcolor", VIK_LAYER_PARAM_COLOR, GROUP_WAYPOINTS, N_("Waypoint Color:"), VIK_LAYER_WIDGET_COLOR, NULL, NULL, NULL, black_color_default, NULL, NULL },
605   { VIK_LAYER_TRW, "wptextcolor", VIK_LAYER_PARAM_COLOR, GROUP_WAYPOINTS, N_("Waypoint Text:"), VIK_LAYER_WIDGET_COLOR, NULL, NULL, NULL, wptextcolor_default, NULL, NULL },
606   { VIK_LAYER_TRW, "wpbgcolor", VIK_LAYER_PARAM_COLOR, GROUP_WAYPOINTS, N_("Background:"), VIK_LAYER_WIDGET_COLOR, NULL, NULL, NULL, wpbgcolor_default, NULL, NULL },
607   { VIK_LAYER_TRW, "wpbgand", VIK_LAYER_PARAM_BOOLEAN, GROUP_WAYPOINTS, N_("Fake BG Color Translucency:"), VIK_LAYER_WIDGET_CHECKBUTTON, NULL, NULL, NULL, vik_lpd_false_default, NULL, NULL },
608   { VIK_LAYER_TRW, "wpsymbol", VIK_LAYER_PARAM_UINT, GROUP_WAYPOINTS, N_("Waypoint marker:"), VIK_LAYER_WIDGET_COMBOBOX, params_wpsymbols, NULL, NULL, wpsymbol_default, NULL, NULL },
609   { VIK_LAYER_TRW, "wpsize", VIK_LAYER_PARAM_UINT, GROUP_WAYPOINTS, N_("Waypoint size:"), VIK_LAYER_WIDGET_SPINBUTTON, &params_scales[7], NULL, NULL, wpsize_default, NULL, NULL },
610   { VIK_LAYER_TRW, "wpsyms", VIK_LAYER_PARAM_BOOLEAN, GROUP_WAYPOINTS, N_("Draw Waypoint Symbols:"), VIK_LAYER_WIDGET_CHECKBUTTON, NULL, NULL, NULL, vik_lpd_true_default, NULL, NULL },
611   { VIK_LAYER_TRW, "wpsortorder", VIK_LAYER_PARAM_UINT, GROUP_WAYPOINTS, N_("Waypoint Sort Order:"), VIK_LAYER_WIDGET_COMBOBOX, params_sort_order, NULL, NULL, sort_order_default, NULL, NULL },
612
613   { VIK_LAYER_TRW, "drawimages", VIK_LAYER_PARAM_BOOLEAN, GROUP_IMAGES, N_("Draw Waypoint Images"), VIK_LAYER_WIDGET_CHECKBUTTON, NULL, NULL, NULL, vik_lpd_true_default, NULL, NULL },
614   { VIK_LAYER_TRW, "image_size", VIK_LAYER_PARAM_UINT, GROUP_IMAGES, N_("Image Size (pixels):"), VIK_LAYER_WIDGET_HSCALE, &params_scales[3], NULL, NULL, image_size_default, NULL, NULL },
615   { VIK_LAYER_TRW, "image_alpha", VIK_LAYER_PARAM_UINT, GROUP_IMAGES, N_("Image Alpha:"), VIK_LAYER_WIDGET_HSCALE, &params_scales[4], NULL, NULL, image_alpha_default, NULL, NULL },
616   { VIK_LAYER_TRW, "image_cache_size", VIK_LAYER_PARAM_UINT, GROUP_IMAGES, N_("Image Memory Cache Size:"), VIK_LAYER_WIDGET_HSCALE, &params_scales[5], NULL, NULL, image_cache_size_default, NULL, NULL },
617
618   { VIK_LAYER_TRW, "metadatadesc", VIK_LAYER_PARAM_STRING, GROUP_METADATA, N_("Description"), VIK_LAYER_WIDGET_ENTRY, NULL, NULL, NULL, string_default, NULL, NULL },
619   { VIK_LAYER_TRW, "metadataauthor", VIK_LAYER_PARAM_STRING, GROUP_METADATA, N_("Author"), VIK_LAYER_WIDGET_ENTRY, NULL, NULL, NULL, string_default, NULL, NULL },
620   { VIK_LAYER_TRW, "metadatatime", VIK_LAYER_PARAM_STRING, GROUP_METADATA, N_("Creation Time"), VIK_LAYER_WIDGET_ENTRY, NULL, NULL, NULL, string_default, NULL, NULL },
621   { VIK_LAYER_TRW, "metadatakeywords", VIK_LAYER_PARAM_STRING, GROUP_METADATA, N_("Keywords"), VIK_LAYER_WIDGET_ENTRY, NULL, NULL, NULL, string_default, NULL, NULL },
622 };
623
624 // ENUMERATION MUST BE IN THE SAME ORDER AS THE NAMED PARAMS ABOVE
625 enum {
626   // Sublayer visibilities
627   PARAM_TV,
628   PARAM_WV,
629   PARAM_RV,
630   // Tracks
631   PARAM_TDL,
632   PARAM_TLFONTSIZE,
633   PARAM_DM,
634   PARAM_TC,
635   PARAM_DL,
636   PARAM_LT,
637   PARAM_DD,
638   PARAM_DDS,
639   PARAM_DP,
640   PARAM_DPS,
641   PARAM_DE,
642   PARAM_EF,
643   PARAM_DS,
644   PARAM_SL,
645   PARAM_BLT,
646   PARAM_TBGC,
647   PARAM_TDSF,
648   PARAM_TSO,
649   // Waypoints
650   PARAM_DLA,
651   PARAM_WPFONTSIZE,
652   PARAM_WPC,
653   PARAM_WPTC,
654   PARAM_WPBC,
655   PARAM_WPBA,
656   PARAM_WPSYM,
657   PARAM_WPSIZE,
658   PARAM_WPSYMS,
659   PARAM_WPSO,
660   // WP images
661   PARAM_DI,
662   PARAM_IS,
663   PARAM_IA,
664   PARAM_ICS,
665   // Metadata
666   PARAM_MDDESC,
667   PARAM_MDAUTH,
668   PARAM_MDTIME,
669   PARAM_MDKEYS,
670   NUM_PARAMS
671 };
672
673 /*** TO ADD A PARAM:
674  *** 1) Add to trw_layer_params and enumeration
675  *** 2) Handle in get_param & set_param (presumably adding on to VikTrwLayer struct)
676  ***/
677
678 /****** END PARAMETERS ******/
679
680 /* Layer Interface function definitions */
681 static VikTrwLayer* trw_layer_create ( VikViewport *vp );
682 static void trw_layer_realize ( VikTrwLayer *vtl, VikTreeview *vt, GtkTreeIter *layer_iter );
683 static void trw_layer_post_read ( VikTrwLayer *vtl, GtkWidget *vvp, gboolean from_file );
684 static void trw_layer_free ( VikTrwLayer *trwlayer );
685 static void trw_layer_draw ( VikTrwLayer *l, gpointer data );
686 static void trw_layer_change_coord_mode ( VikTrwLayer *vtl, VikCoordMode dest_mode );
687 static void trw_layer_set_menu_selection ( VikTrwLayer *vtl, guint16 );
688 static guint16 trw_layer_get_menu_selection ( VikTrwLayer *vtl );
689 static void trw_layer_add_menu_items ( VikTrwLayer *vtl, GtkMenu *menu, gpointer vlp );
690 static gboolean trw_layer_sublayer_add_menu_items ( VikTrwLayer *l, GtkMenu *menu, gpointer vlp, gint subtype, gpointer sublayer, GtkTreeIter *iter, VikViewport *vvp );
691 static const gchar* trw_layer_sublayer_rename_request ( VikTrwLayer *l, const gchar *newname, gpointer vlp, gint subtype, gpointer sublayer, GtkTreeIter *iter );
692 static gboolean trw_layer_sublayer_toggle_visible ( VikTrwLayer *l, gint subtype, gpointer sublayer );
693 static const gchar* trw_layer_layer_tooltip ( VikTrwLayer *vtl );
694 static const gchar* trw_layer_sublayer_tooltip ( VikTrwLayer *l, gint subtype, gpointer sublayer );
695 static gboolean trw_layer_selected ( VikTrwLayer *l, gint subtype, gpointer sublayer, gint type, gpointer vlp );
696 static void trw_layer_marshall ( VikTrwLayer *vtl, guint8 **data, gint *len );
697 static VikTrwLayer *trw_layer_unmarshall ( guint8 *data, gint len, VikViewport *vvp );
698 static gboolean trw_layer_set_param ( VikTrwLayer *vtl, guint16 id, VikLayerParamData data, VikViewport *vp, gboolean is_file_operation );
699 static VikLayerParamData trw_layer_get_param ( VikTrwLayer *vtl, guint16 id, gboolean is_file_operation );
700 static void trw_layer_change_param ( GtkWidget *widget, ui_change_values values );
701 static void trw_layer_del_item ( VikTrwLayer *vtl, gint subtype, gpointer sublayer );
702 static void trw_layer_cut_item ( VikTrwLayer *vtl, gint subtype, gpointer sublayer );
703 static void trw_layer_copy_item ( VikTrwLayer *vtl, gint subtype, gpointer sublayer, guint8 **item, guint *len );
704 static gboolean trw_layer_paste_item ( VikTrwLayer *vtl, gint subtype, guint8 *item, guint len );
705 static void trw_layer_free_copied_item ( gint subtype, gpointer item );
706 static void trw_layer_drag_drop_request ( VikTrwLayer *vtl_src, VikTrwLayer *vtl_dest, GtkTreeIter *src_item_iter, GtkTreePath *dest_path );
707 static gboolean trw_layer_select_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp, tool_ed_t *t );
708 static gboolean trw_layer_select_move ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp, tool_ed_t *t );
709 static gboolean trw_layer_select_release ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp, tool_ed_t *t );
710 static gboolean trw_layer_show_selected_viewport_menu ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp );
711 /* End Layer Interface function definitions */
712
713 VikLayerInterface vik_trw_layer_interface = {
714   "TrackWaypoint",
715   N_("TrackWaypoint"),
716   "<control><shift>Y",
717   &viktrwlayer_pixbuf,
718
719   trw_layer_tools,
720   sizeof(trw_layer_tools) / sizeof(VikToolInterface),
721
722   trw_layer_params,
723   NUM_PARAMS,
724   params_groups, /* params_groups */
725   sizeof(params_groups)/sizeof(params_groups[0]),    /* number of groups */
726
727   VIK_MENU_ITEM_ALL,
728
729   (VikLayerFuncCreate)                  trw_layer_create,
730   (VikLayerFuncRealize)                 trw_layer_realize,
731   (VikLayerFuncPostRead)                trw_layer_post_read,
732   (VikLayerFuncFree)                    trw_layer_free,
733
734   (VikLayerFuncProperties)              NULL,
735   (VikLayerFuncDraw)                    trw_layer_draw,
736   (VikLayerFuncChangeCoordMode)         trw_layer_change_coord_mode,
737
738   (VikLayerFuncSetMenuItemsSelection)   trw_layer_set_menu_selection,
739   (VikLayerFuncGetMenuItemsSelection)   trw_layer_get_menu_selection,
740
741   (VikLayerFuncAddMenuItems)            trw_layer_add_menu_items,
742   (VikLayerFuncSublayerAddMenuItems)    trw_layer_sublayer_add_menu_items,
743
744   (VikLayerFuncSublayerRenameRequest)   trw_layer_sublayer_rename_request,
745   (VikLayerFuncSublayerToggleVisible)   trw_layer_sublayer_toggle_visible,
746   (VikLayerFuncSublayerTooltip)         trw_layer_sublayer_tooltip,
747   (VikLayerFuncLayerTooltip)            trw_layer_layer_tooltip,
748   (VikLayerFuncLayerSelected)           trw_layer_selected,
749
750   (VikLayerFuncMarshall)                trw_layer_marshall,
751   (VikLayerFuncUnmarshall)              trw_layer_unmarshall,
752
753   (VikLayerFuncSetParam)                trw_layer_set_param,
754   (VikLayerFuncGetParam)                trw_layer_get_param,
755   (VikLayerFuncChangeParam)             trw_layer_change_param,
756
757   (VikLayerFuncReadFileData)            a_gpspoint_read_file,
758   (VikLayerFuncWriteFileData)           a_gpspoint_write_file,
759
760   (VikLayerFuncDeleteItem)              trw_layer_del_item,
761   (VikLayerFuncCutItem)                 trw_layer_cut_item,
762   (VikLayerFuncCopyItem)                trw_layer_copy_item,
763   (VikLayerFuncPasteItem)               trw_layer_paste_item,
764   (VikLayerFuncFreeCopiedItem)          trw_layer_free_copied_item,
765   
766   (VikLayerFuncDragDropRequest)         trw_layer_drag_drop_request,
767
768   (VikLayerFuncSelectClick)             trw_layer_select_click,
769   (VikLayerFuncSelectMove)              trw_layer_select_move,
770   (VikLayerFuncSelectRelease)           trw_layer_select_release,
771   (VikLayerFuncSelectedViewportMenu)    trw_layer_show_selected_viewport_menu,
772 };
773
774 static gboolean have_diary_program = FALSE;
775
776 // NB Only performed once per program run
777 static void vik_trwlayer_class_init ( VikTrwLayerClass *klass )
778 {
779   if ( g_find_program_in_path( "rednotebook" ) ) {
780     gchar *stdout = NULL;
781     gchar *stderr = NULL;
782     // Needs RedNotebook 1.7.3+ for support of opening on a specified date
783     if ( g_spawn_command_line_sync ( "rednotebook --version", &stdout, &stderr, NULL, NULL ) ) {
784       // Annoyingly 1.7.1|2|3 versions of RedNotebook prints the version to stderr!!
785       if ( stdout )
786         g_debug ("Diary: %s", stdout ); // Should be something like 'RedNotebook 1.4'
787       if ( stderr )
788         g_warning ("Diary: stderr: %s", stderr );
789
790       gchar **tokens = NULL;
791       if ( stdout && g_strcmp0(stdout, "") )
792         tokens = g_strsplit(stdout, " ", 0);
793       else if ( stderr )
794         tokens = g_strsplit(stderr, " ", 0);
795
796       gint num = 0;
797       gchar *token = tokens[num];
798       while ( token && num < 2 ) {
799         if (num == 1) {
800           if ( viking_version_to_number(token) >= viking_version_to_number("1.7.3") )
801             have_diary_program = TRUE;
802         }
803         num++;
804         token = tokens[num];
805       }
806       g_strfreev ( tokens );
807     }
808     g_free ( stdout );
809     g_free ( stderr );
810   }
811 }
812
813 GType vik_trw_layer_get_type ()
814 {
815   static GType vtl_type = 0;
816
817   if (!vtl_type)
818   {
819     static const GTypeInfo vtl_info =
820     {
821       sizeof (VikTrwLayerClass),
822       NULL, /* base_init */
823       NULL, /* base_finalize */
824       (GClassInitFunc) vik_trwlayer_class_init, /* class init */
825       NULL, /* class_finalize */
826       NULL, /* class_data */
827       sizeof (VikTrwLayer),
828       0,
829       NULL /* instance init */
830     };
831     vtl_type = g_type_register_static ( VIK_LAYER_TYPE, "VikTrwLayer", &vtl_info, 0 );
832   }
833   return vtl_type;
834 }
835
836 VikTRWMetadata *vik_trw_metadata_new()
837 {
838   return (VikTRWMetadata*)g_malloc0(sizeof(VikTRWMetadata));
839 }
840
841 void vik_trw_metadata_free ( VikTRWMetadata *metadata)
842 {
843   g_free (metadata);
844 }
845
846 VikTRWMetadata *vik_trw_layer_get_metadata ( VikTrwLayer *vtl )
847 {
848   return vtl->metadata;
849 }
850
851 void vik_trw_layer_set_metadata ( VikTrwLayer *vtl, VikTRWMetadata *metadata)
852 {
853   if ( vtl->metadata )
854     vik_trw_metadata_free ( vtl->metadata );
855   vtl->metadata = metadata;
856 }
857
858 typedef struct {
859   gboolean found;
860   const gchar *date_str;
861   const VikTrack *trk;
862   const VikWaypoint *wpt;
863   gpointer *trk_id;
864   gpointer *wpt_id;
865 } date_finder_type;
866
867 static gboolean trw_layer_find_date_track ( const gpointer id, const VikTrack *trk, date_finder_type *df )
868 {
869   gchar date_buf[20];
870   date_buf[0] = '\0';
871   // Might be an easier way to compare dates rather than converting the strings all the time...
872   if ( trk->trackpoints && VIK_TRACKPOINT(trk->trackpoints->data)->has_timestamp ) {
873     strftime (date_buf, sizeof(date_buf), "%Y-%m-%d", gmtime(&(VIK_TRACKPOINT(trk->trackpoints->data)->timestamp)));
874
875     if ( ! g_strcmp0 ( df->date_str, date_buf ) ) {
876       df->found = TRUE;
877       df->trk = trk;
878       df->trk_id = id;
879     }
880   }
881   return df->found;
882 }
883
884 static gboolean trw_layer_find_date_waypoint ( const gpointer id, const VikWaypoint *wpt, date_finder_type *df )
885 {
886   gchar date_buf[20];
887   date_buf[0] = '\0';
888   // Might be an easier way to compare dates rather than converting the strings all the time...
889   if ( wpt->has_timestamp ) {
890     strftime (date_buf, sizeof(date_buf), "%Y-%m-%d", gmtime(&(wpt->timestamp)));
891
892     if ( ! g_strcmp0 ( df->date_str, date_buf ) ) {
893       df->found = TRUE;
894       df->wpt = wpt;
895       df->wpt_id = id;
896     }
897   }
898   return df->found;
899 }
900
901 /**
902  * Find an item by date
903  */
904 gboolean vik_trw_layer_find_date ( VikTrwLayer *vtl, const gchar *date_str, VikCoord *position, VikViewport *vvp, gboolean do_tracks, gboolean select )
905 {
906   date_finder_type df;
907   df.found = FALSE;
908   df.date_str = date_str;
909   df.trk = NULL;
910   df.wpt = NULL;
911   // Only tracks ATM
912   if ( do_tracks )
913     g_hash_table_find ( vtl->tracks, (GHRFunc) trw_layer_find_date_track, &df );
914   else
915     g_hash_table_find ( vtl->waypoints, (GHRFunc) trw_layer_find_date_waypoint, &df );
916
917   if ( select && df.found ) {
918     if ( do_tracks && df.trk ) {
919       struct LatLon maxmin[2] = { {0,0}, {0,0} };
920       trw_layer_find_maxmin_tracks ( NULL, df.trk, maxmin );
921       trw_layer_zoom_to_show_latlons ( vtl, vvp, maxmin );
922       vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, g_hash_table_lookup (vtl->tracks_iters, df.trk_id), TRUE );
923     }
924     else if ( df.wpt ) {
925       vik_viewport_set_center_coord ( vvp, &(df.wpt->coord), TRUE );
926       vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, g_hash_table_lookup (vtl->waypoints_iters, df.wpt_id), TRUE );
927     }
928     vik_layer_emit_update ( VIK_LAYER(vtl) );
929   }
930   return df.found;
931 }
932
933 static void trw_layer_del_item ( VikTrwLayer *vtl, gint subtype, gpointer sublayer )
934 {
935   static menu_array_sublayer values;
936   if (!sublayer) {
937     return;
938   }
939
940   gint ii;
941   for ( ii = MA_VTL; ii < MA_LAST; ii++ )
942     values[ii] = NULL;
943
944   values[MA_VTL]         = vtl;
945   values[MA_SUBTYPE]     = GINT_TO_POINTER (subtype);
946   values[MA_SUBLAYER_ID] = sublayer;
947   values[MA_CONFIRM]     = GINT_TO_POINTER (1); // Confirm delete request
948
949   trw_layer_delete_item ( values );
950 }
951
952 static void trw_layer_cut_item ( VikTrwLayer *vtl, gint subtype, gpointer sublayer )
953 {
954   static menu_array_sublayer values;
955   if (!sublayer) {
956     return;
957   }
958
959   gint ii;
960   for ( ii = MA_VTL; ii < MA_LAST; ii++ )
961     values[ii] = NULL;
962
963   values[MA_VTL]         = vtl;
964   values[MA_SUBTYPE]     = GINT_TO_POINTER (subtype);
965   values[MA_SUBLAYER_ID] = sublayer;
966   values[MA_CONFIRM]     = GINT_TO_POINTER (1); // Confirm delete request
967
968   trw_layer_copy_item_cb(values);
969   trw_layer_cut_item_cb(values);
970 }
971
972 static void trw_layer_copy_item_cb ( menu_array_sublayer values)
973 {
974   VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
975   gint subtype = GPOINTER_TO_INT (values[MA_SUBTYPE]);
976   gpointer * sublayer = values[MA_SUBLAYER_ID];
977   guint8 *data = NULL;
978   guint len;
979
980   trw_layer_copy_item( vtl, subtype, sublayer, &data, &len);
981
982   if (data) {
983     const gchar* name;
984     if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT ) {
985       VikWaypoint *wp = g_hash_table_lookup ( vtl->waypoints, sublayer);
986       if ( wp && wp->name )
987         name = wp->name;
988       else
989         name = NULL; // Broken :(
990     }
991     else if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK ) {
992       VikTrack *trk = g_hash_table_lookup ( vtl->tracks, sublayer);
993       if ( trk && trk->name )
994         name = trk->name;
995       else
996         name = NULL; // Broken :(
997     }
998     else {
999       VikTrack *trk = g_hash_table_lookup ( vtl->routes, sublayer);
1000       if ( trk && trk->name )
1001         name = trk->name;
1002       else
1003         name = NULL; // Broken :(
1004     }
1005
1006     a_clipboard_copy( VIK_CLIPBOARD_DATA_SUBLAYER, VIK_LAYER_TRW,
1007                       subtype, len, name, data);
1008   }
1009 }
1010
1011 static void trw_layer_cut_item_cb ( menu_array_sublayer values)
1012 {
1013   trw_layer_copy_item_cb(values);
1014   values[MA_CONFIRM] = GINT_TO_POINTER (0); // Never need to confirm automatic delete
1015   trw_layer_delete_item(values);
1016 }
1017
1018 static void trw_layer_paste_item_cb ( menu_array_sublayer values)
1019 {
1020   // Slightly cheating method, routing via the panels capability
1021   a_clipboard_paste (VIK_LAYERS_PANEL(values[MA_VLP]));
1022 }
1023
1024 static void trw_layer_copy_item ( VikTrwLayer *vtl, gint subtype, gpointer sublayer, guint8 **item, guint *len )
1025 {
1026   guint8 *id;
1027   guint il;
1028
1029   if (!sublayer) {
1030     *item = NULL;
1031     return;
1032   }
1033
1034   GByteArray *ba = g_byte_array_new ();
1035
1036   if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT ) {
1037     vik_waypoint_marshall ( g_hash_table_lookup ( vtl->waypoints, sublayer ), &id, &il );
1038   } else if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK ) {
1039     vik_track_marshall ( g_hash_table_lookup ( vtl->tracks, sublayer ), &id, &il );
1040   } else {
1041     vik_track_marshall ( g_hash_table_lookup ( vtl->routes, sublayer ), &id, &il );
1042   }
1043
1044   g_byte_array_append ( ba, id, il );
1045
1046   g_free(id);
1047
1048   *len = ba->len;
1049   *item = ba->data;
1050 }
1051
1052 static gboolean trw_layer_paste_item ( VikTrwLayer *vtl, gint subtype, guint8 *item, guint len )
1053 {
1054   if ( !item )
1055     return FALSE;
1056
1057   gchar *name;
1058
1059   if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT )
1060   {
1061     VikWaypoint *w;
1062
1063     w = vik_waypoint_unmarshall ( item, len );
1064     // When copying - we'll create a new name based on the original
1065     name = trw_layer_new_unique_sublayer_name(vtl, VIK_TRW_LAYER_SUBLAYER_WAYPOINT, w->name);
1066     vik_trw_layer_add_waypoint ( vtl, name, w );
1067     waypoint_convert (NULL, w, &vtl->coord_mode);
1068     g_free ( name );
1069
1070     trw_layer_calculate_bounds_waypoints ( vtl );
1071
1072     // Consider if redraw necessary for the new item
1073     if ( vtl->vl.visible && vtl->waypoints_visible && w->visible )
1074       vik_layer_emit_update ( VIK_LAYER(vtl) );
1075     return TRUE;
1076   }
1077   if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
1078   {
1079     VikTrack *t;
1080
1081     t = vik_track_unmarshall ( item, len );
1082     // When copying - we'll create a new name based on the original
1083     name = trw_layer_new_unique_sublayer_name(vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, t->name);
1084     vik_trw_layer_add_track ( vtl, name, t );
1085     vik_track_convert (t, vtl->coord_mode);
1086     g_free ( name );
1087
1088     // Consider if redraw necessary for the new item
1089     if ( vtl->vl.visible && vtl->tracks_visible && t->visible )
1090       vik_layer_emit_update ( VIK_LAYER(vtl) );
1091     return TRUE;
1092   }
1093   if ( subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE )
1094   {
1095     VikTrack *t;
1096
1097     t = vik_track_unmarshall ( item, len );
1098     // When copying - we'll create a new name based on the original
1099     name = trw_layer_new_unique_sublayer_name(vtl, VIK_TRW_LAYER_SUBLAYER_ROUTE, t->name);
1100     vik_trw_layer_add_route ( vtl, name, t );
1101     vik_track_convert (t, vtl->coord_mode);
1102     g_free ( name );
1103
1104     // Consider if redraw necessary for the new item
1105     if ( vtl->vl.visible && vtl->routes_visible && t->visible )
1106       vik_layer_emit_update ( VIK_LAYER(vtl) );
1107     return TRUE;
1108   }
1109   return FALSE;
1110 }
1111
1112 static void trw_layer_free_copied_item ( gint subtype, gpointer item )
1113 {
1114   if (item) {
1115     g_free(item);
1116   }
1117 }
1118
1119 static gboolean trw_layer_set_param ( VikTrwLayer *vtl, guint16 id, VikLayerParamData data, VikViewport *vp, gboolean is_file_operation )
1120 {
1121   switch ( id )
1122   {
1123     case PARAM_TV: vtl->tracks_visible = data.b; break;
1124     case PARAM_WV: vtl->waypoints_visible = data.b; break;
1125     case PARAM_RV: vtl->routes_visible = data.b; break;
1126     case PARAM_TDL: vtl->track_draw_labels = data.b; break;
1127     case PARAM_TLFONTSIZE:
1128       if ( data.u < FS_NUM_SIZES ) {
1129         vtl->track_font_size = data.u;
1130         g_free ( vtl->track_fsize_str );
1131         switch ( vtl->track_font_size ) {
1132           case FS_XX_SMALL: vtl->track_fsize_str = g_strdup ( "xx-small" ); break;
1133           case FS_X_SMALL: vtl->track_fsize_str = g_strdup ( "x-small" ); break;
1134           case FS_SMALL: vtl->track_fsize_str = g_strdup ( "small" ); break;
1135           case FS_LARGE: vtl->track_fsize_str = g_strdup ( "large" ); break;
1136           case FS_X_LARGE: vtl->track_fsize_str = g_strdup ( "x-large" ); break;
1137           case FS_XX_LARGE: vtl->track_fsize_str = g_strdup ( "xx-large" ); break;
1138           default: vtl->track_fsize_str = g_strdup ( "medium" ); break;
1139         }
1140       }
1141       break;
1142     case PARAM_DM: vtl->drawmode = data.u; break;
1143     case PARAM_TC:
1144       vtl->track_color = data.c;
1145       if ( vp ) trw_layer_new_track_gcs ( vtl, vp );
1146       break;
1147     case PARAM_DP: vtl->drawpoints = data.b; break;
1148     case PARAM_DPS:
1149       if ( data.u >= MIN_POINT_SIZE && data.u <= MAX_POINT_SIZE )
1150         vtl->drawpoints_size = data.u;
1151       break;
1152     case PARAM_DE: vtl->drawelevation = data.b; break;
1153     case PARAM_DS: vtl->drawstops = data.b; break;
1154     case PARAM_DL: vtl->drawlines = data.b; break;
1155     case PARAM_DD: vtl->drawdirections = data.b; break;
1156     case PARAM_DDS:
1157       if ( data.u >= MIN_ARROW_SIZE && data.u <= MAX_ARROW_SIZE )
1158         vtl->drawdirections_size = data.u;
1159       break;
1160     case PARAM_SL: if ( data.u >= MIN_STOP_LENGTH && data.u <= MAX_STOP_LENGTH )
1161                      vtl->stop_length = data.u;
1162                    break;
1163     case PARAM_EF: if ( data.u >= 1 && data.u <= 100 )
1164                      vtl->elevation_factor = data.u;
1165                    break;
1166     case PARAM_LT: if ( data.u > 0 && data.u < 15 && data.u != vtl->line_thickness )
1167                    {
1168                      vtl->line_thickness = data.u;
1169                      if ( vp ) trw_layer_new_track_gcs ( vtl, vp );
1170                    }
1171                    break;
1172     case PARAM_BLT: if ( data.u <= 8 && data.u != vtl->bg_line_thickness )
1173                    {
1174                      vtl->bg_line_thickness = data.u;
1175                      if ( vp ) trw_layer_new_track_gcs ( vtl, vp );
1176                    }
1177                    break;
1178     case PARAM_TBGC:
1179       vtl->track_bg_color = data.c;
1180       if ( vtl->track_bg_gc )
1181         gdk_gc_set_rgb_fg_color(vtl->track_bg_gc, &(vtl->track_bg_color));
1182       break;
1183     case PARAM_TDSF: vtl->track_draw_speed_factor = data.d; break;
1184     case PARAM_TSO: if ( data.u < VL_SO_LAST ) vtl->track_sort_order = data.u; break;
1185     case PARAM_DLA: vtl->drawlabels = data.b; break;
1186     case PARAM_DI: vtl->drawimages = data.b; break;
1187     case PARAM_IS: if ( data.u != vtl->image_size )
1188       {
1189         vtl->image_size = data.u;
1190         g_list_foreach ( vtl->image_cache->head, (GFunc) cached_pixbuf_free, NULL );
1191         g_queue_free ( vtl->image_cache );
1192         vtl->image_cache = g_queue_new ();
1193       }
1194       break;
1195     case PARAM_IA: vtl->image_alpha = data.u; break;
1196     case PARAM_ICS: vtl->image_cache_size = data.u;
1197       while ( vtl->image_cache->length > vtl->image_cache_size ) /* if shrinking cache_size, free pixbuf ASAP */
1198           cached_pixbuf_free ( g_queue_pop_tail ( vtl->image_cache ) );
1199       break;
1200     case PARAM_WPC:
1201       vtl->waypoint_color = data.c;
1202       if ( vtl->waypoint_gc )
1203         gdk_gc_set_rgb_fg_color(vtl->waypoint_gc, &(vtl->waypoint_color));
1204       break;
1205     case PARAM_WPTC:
1206       vtl->waypoint_text_color = data.c;
1207       if ( vtl->waypoint_text_gc )
1208         gdk_gc_set_rgb_fg_color(vtl->waypoint_text_gc, &(vtl->waypoint_text_color));
1209       break;
1210     case PARAM_WPBC:
1211       vtl->waypoint_bg_color = data.c;
1212       if ( vtl->waypoint_bg_gc )
1213         gdk_gc_set_rgb_fg_color(vtl->waypoint_bg_gc, &(vtl->waypoint_bg_color));
1214       break;
1215     case PARAM_WPBA:
1216       vtl->wpbgand = data.b;
1217       if ( vtl->waypoint_bg_gc )
1218         gdk_gc_set_function(vtl->waypoint_bg_gc, data.b ? GDK_AND : GDK_COPY );
1219       break;
1220     case PARAM_WPSYM: if ( data.u < WP_NUM_SYMBOLS ) vtl->wp_symbol = data.u; break;
1221     case PARAM_WPSIZE: if ( data.u > 0 && data.u <= 64 ) vtl->wp_size = data.u; break;
1222     case PARAM_WPSYMS: vtl->wp_draw_symbols = data.b; break;
1223     case PARAM_WPFONTSIZE:
1224       if ( data.u < FS_NUM_SIZES ) {
1225         vtl->wp_font_size = data.u;
1226         g_free ( vtl->wp_fsize_str );
1227         switch ( vtl->wp_font_size ) {
1228           case FS_XX_SMALL: vtl->wp_fsize_str = g_strdup ( "xx-small" ); break;
1229           case FS_X_SMALL: vtl->wp_fsize_str = g_strdup ( "x-small" ); break;
1230           case FS_SMALL: vtl->wp_fsize_str = g_strdup ( "small" ); break;
1231           case FS_LARGE: vtl->wp_fsize_str = g_strdup ( "large" ); break;
1232           case FS_X_LARGE: vtl->wp_fsize_str = g_strdup ( "x-large" ); break;
1233           case FS_XX_LARGE: vtl->wp_fsize_str = g_strdup ( "xx-large" ); break;
1234           default: vtl->wp_fsize_str = g_strdup ( "medium" ); break;
1235         }
1236       }
1237       break;
1238     case PARAM_WPSO: if ( data.u < VL_SO_LAST ) vtl->wp_sort_order = data.u; break;
1239     // Metadata
1240     case PARAM_MDDESC: if ( data.s && vtl->metadata ) vtl->metadata->description = g_strdup (data.s); break;
1241     case PARAM_MDAUTH: if ( data.s && vtl->metadata ) vtl->metadata->author = g_strdup (data.s); break;
1242     case PARAM_MDTIME: if ( data.s && vtl->metadata ) vtl->metadata->timestamp = g_strdup (data.s); break;
1243     case PARAM_MDKEYS: if ( data.s && vtl->metadata ) vtl->metadata->keywords = g_strdup (data.s); break;
1244     default: break;
1245   }
1246   return TRUE;
1247 }
1248
1249 static VikLayerParamData trw_layer_get_param ( VikTrwLayer *vtl, guint16 id, gboolean is_file_operation )
1250 {
1251   VikLayerParamData rv;
1252   switch ( id )
1253   {
1254     case PARAM_TV: rv.b = vtl->tracks_visible; break;
1255     case PARAM_WV: rv.b = vtl->waypoints_visible; break;
1256     case PARAM_RV: rv.b = vtl->routes_visible; break;
1257     case PARAM_TDL: rv.b = vtl->track_draw_labels; break;
1258     case PARAM_TLFONTSIZE: rv.u = vtl->track_font_size; break;
1259     case PARAM_DM: rv.u = vtl->drawmode; break;
1260     case PARAM_TC: rv.c = vtl->track_color; break;
1261     case PARAM_DP: rv.b = vtl->drawpoints; break;
1262     case PARAM_DPS: rv.u = vtl->drawpoints_size; break;
1263     case PARAM_DE: rv.b = vtl->drawelevation; break;
1264     case PARAM_EF: rv.u = vtl->elevation_factor; break;
1265     case PARAM_DS: rv.b = vtl->drawstops; break;
1266     case PARAM_SL: rv.u = vtl->stop_length; break;
1267     case PARAM_DL: rv.b = vtl->drawlines; break;
1268     case PARAM_DD: rv.b = vtl->drawdirections; break;
1269     case PARAM_DDS: rv.u = vtl->drawdirections_size; break;
1270     case PARAM_LT: rv.u = vtl->line_thickness; break;
1271     case PARAM_BLT: rv.u = vtl->bg_line_thickness; break;
1272     case PARAM_DLA: rv.b = vtl->drawlabels; break;
1273     case PARAM_DI: rv.b = vtl->drawimages; break;
1274     case PARAM_TBGC: rv.c = vtl->track_bg_color; break;
1275     case PARAM_TDSF: rv.d = vtl->track_draw_speed_factor; break;
1276     case PARAM_TSO: rv.u = vtl->track_sort_order; break;
1277     case PARAM_IS: rv.u = vtl->image_size; break;
1278     case PARAM_IA: rv.u = vtl->image_alpha; break;
1279     case PARAM_ICS: rv.u = vtl->image_cache_size; break;
1280     case PARAM_WPC: rv.c = vtl->waypoint_color; break;
1281     case PARAM_WPTC: rv.c = vtl->waypoint_text_color; break;
1282     case PARAM_WPBC: rv.c = vtl->waypoint_bg_color; break;
1283     case PARAM_WPBA: rv.b = vtl->wpbgand; break;
1284     case PARAM_WPSYM: rv.u = vtl->wp_symbol; break;
1285     case PARAM_WPSIZE: rv.u = vtl->wp_size; break;
1286     case PARAM_WPSYMS: rv.b = vtl->wp_draw_symbols; break;
1287     case PARAM_WPFONTSIZE: rv.u = vtl->wp_font_size; break;
1288     case PARAM_WPSO: rv.u = vtl->wp_sort_order; break;
1289     // Metadata
1290     case PARAM_MDDESC: if (vtl->metadata) { rv.s = vtl->metadata->description; } break;
1291     case PARAM_MDAUTH: if (vtl->metadata) { rv.s = vtl->metadata->author; } break;
1292     case PARAM_MDTIME: if (vtl->metadata) { rv.s = vtl->metadata->timestamp; } break;
1293     case PARAM_MDKEYS: if (vtl->metadata) { rv.s = vtl->metadata->keywords; } break;
1294     default: break;
1295   }
1296   return rv;
1297 }
1298
1299 static void trw_layer_change_param ( GtkWidget *widget, ui_change_values values )
1300 {
1301   // This '-3' is to account for the first few parameters not in the properties
1302   const gint OFFSET = -3;
1303
1304   switch ( GPOINTER_TO_INT(values[UI_CHG_PARAM_ID]) ) {
1305     // Alter sensitivity of waypoint draw image related widgets according to the draw image setting.
1306     case PARAM_DI: {
1307       // Get new value
1308       VikLayerParamData vlpd = a_uibuilder_widget_get_value ( widget, values[UI_CHG_PARAM] );
1309       GtkWidget **ww1 = values[UI_CHG_WIDGETS];
1310       GtkWidget **ww2 = values[UI_CHG_LABELS];
1311       GtkWidget *w1 = ww1[OFFSET + PARAM_IS];
1312       GtkWidget *w2 = ww2[OFFSET + PARAM_IS];
1313       GtkWidget *w3 = ww1[OFFSET + PARAM_IA];
1314       GtkWidget *w4 = ww2[OFFSET + PARAM_IA];
1315       GtkWidget *w5 = ww1[OFFSET + PARAM_ICS];
1316       GtkWidget *w6 = ww2[OFFSET + PARAM_ICS];
1317       if ( w1 ) gtk_widget_set_sensitive ( w1, vlpd.b );
1318       if ( w2 ) gtk_widget_set_sensitive ( w2, vlpd.b );
1319       if ( w3 ) gtk_widget_set_sensitive ( w3, vlpd.b );
1320       if ( w4 ) gtk_widget_set_sensitive ( w4, vlpd.b );
1321       if ( w5 ) gtk_widget_set_sensitive ( w5, vlpd.b );
1322       if ( w6 ) gtk_widget_set_sensitive ( w6, vlpd.b );
1323       break;
1324     }
1325     // Alter sensitivity of waypoint label related widgets according to the draw label setting.
1326     case PARAM_DLA: {
1327       // Get new value
1328       VikLayerParamData vlpd = a_uibuilder_widget_get_value ( widget, values[UI_CHG_PARAM] );
1329       GtkWidget **ww1 = values[UI_CHG_WIDGETS];
1330       GtkWidget **ww2 = values[UI_CHG_LABELS];
1331       GtkWidget *w1 = ww1[OFFSET + PARAM_WPTC];
1332       GtkWidget *w2 = ww2[OFFSET + PARAM_WPTC];
1333       GtkWidget *w3 = ww1[OFFSET + PARAM_WPBC];
1334       GtkWidget *w4 = ww2[OFFSET + PARAM_WPBC];
1335       GtkWidget *w5 = ww1[OFFSET + PARAM_WPBA];
1336       GtkWidget *w6 = ww2[OFFSET + PARAM_WPBA];
1337       GtkWidget *w7 = ww1[OFFSET + PARAM_WPFONTSIZE];
1338       GtkWidget *w8 = ww2[OFFSET + PARAM_WPFONTSIZE];
1339       if ( w1 ) gtk_widget_set_sensitive ( w1, vlpd.b );
1340       if ( w2 ) gtk_widget_set_sensitive ( w2, vlpd.b );
1341       if ( w3 ) gtk_widget_set_sensitive ( w3, vlpd.b );
1342       if ( w4 ) gtk_widget_set_sensitive ( w4, vlpd.b );
1343       if ( w5 ) gtk_widget_set_sensitive ( w5, vlpd.b );
1344       if ( w6 ) gtk_widget_set_sensitive ( w6, vlpd.b );
1345       if ( w7 ) gtk_widget_set_sensitive ( w7, vlpd.b );
1346       if ( w8 ) gtk_widget_set_sensitive ( w8, vlpd.b );
1347       break;
1348     }
1349     // Alter sensitivity of all track colours according to the draw track mode.
1350     case PARAM_DM: {
1351       // Get new value
1352       VikLayerParamData vlpd = a_uibuilder_widget_get_value ( widget, values[UI_CHG_PARAM] );
1353       gboolean sensitive = ( vlpd.u == DRAWMODE_ALL_SAME_COLOR );
1354       GtkWidget **ww1 = values[UI_CHG_WIDGETS];
1355       GtkWidget **ww2 = values[UI_CHG_LABELS];
1356       GtkWidget *w1 = ww1[OFFSET + PARAM_TC];
1357       GtkWidget *w2 = ww2[OFFSET + PARAM_TC];
1358       if ( w1 ) gtk_widget_set_sensitive ( w1, sensitive );
1359       if ( w2 ) gtk_widget_set_sensitive ( w2, sensitive );
1360       break;
1361     }
1362     case PARAM_MDTIME: {
1363       // Force metadata->timestamp to be always read-only for now.
1364       GtkWidget **ww = values[UI_CHG_WIDGETS];
1365       GtkWidget *w1 = ww[OFFSET + PARAM_MDTIME];
1366       if ( w1 ) gtk_widget_set_sensitive ( w1, FALSE );
1367     }
1368     // NB Since other track settings have been split across tabs,
1369     // I don't think it's useful to set sensitivities on widgets you can't immediately see
1370     default: break;
1371   }
1372 }
1373
1374 static void trw_layer_marshall( VikTrwLayer *vtl, guint8 **data, gint *len )
1375 {
1376   guint8 *pd;
1377   gint pl;
1378
1379   *data = NULL;
1380
1381   // Use byte arrays to store sublayer data
1382   // much like done elsewhere e.g. vik_layer_marshall_params()
1383   GByteArray *ba = g_byte_array_new ( );
1384
1385   guint8 *sl_data;
1386   guint sl_len;
1387
1388   guint object_length;
1389   guint subtype;
1390   // store:
1391   // the length of the item
1392   // the sublayer type of item
1393   // the the actual item
1394 #define tlm_append(object_pointer, size, type)  \
1395   subtype = (type); \
1396   object_length = (size); \
1397   g_byte_array_append ( ba, (guint8 *)&object_length, sizeof(object_length) ); \
1398   g_byte_array_append ( ba, (guint8 *)&subtype, sizeof(subtype) ); \
1399   g_byte_array_append ( ba, (object_pointer), object_length );
1400
1401   // Layer parameters first
1402   vik_layer_marshall_params(VIK_LAYER(vtl), &pd, &pl);
1403   g_byte_array_append ( ba, (guint8 *)&pl, sizeof(pl) ); \
1404   g_byte_array_append ( ba, pd, pl );
1405   g_free ( pd );
1406
1407   // Now sublayer data
1408   GHashTableIter iter;
1409   gpointer key, value;
1410
1411   // Waypoints
1412   g_hash_table_iter_init ( &iter, vtl->waypoints );
1413   while ( g_hash_table_iter_next (&iter, &key, &value) ) {
1414     vik_waypoint_marshall ( VIK_WAYPOINT(value), &sl_data, &sl_len );
1415     tlm_append ( sl_data, sl_len, VIK_TRW_LAYER_SUBLAYER_WAYPOINT );
1416     g_free ( sl_data );
1417   }
1418
1419   // Tracks
1420   g_hash_table_iter_init ( &iter, vtl->tracks );
1421   while ( g_hash_table_iter_next (&iter, &key, &value) ) {
1422     vik_track_marshall ( VIK_TRACK(value), &sl_data, &sl_len );
1423     tlm_append ( sl_data, sl_len, VIK_TRW_LAYER_SUBLAYER_TRACK );
1424     g_free ( sl_data );
1425   }
1426
1427   // Routes
1428   g_hash_table_iter_init ( &iter, vtl->routes );
1429   while ( g_hash_table_iter_next (&iter, &key, &value) ) {
1430     vik_track_marshall ( VIK_TRACK(value), &sl_data, &sl_len );
1431     tlm_append ( sl_data, sl_len, VIK_TRW_LAYER_SUBLAYER_ROUTE );
1432     g_free ( sl_data );
1433   }
1434
1435 #undef tlm_append
1436
1437   *data = ba->data;
1438   *len = ba->len;
1439 }
1440
1441 static VikTrwLayer *trw_layer_unmarshall( guint8 *data, gint len, VikViewport *vvp )
1442 {
1443   VikTrwLayer *vtl = VIK_TRW_LAYER(vik_layer_create ( VIK_LAYER_TRW, vvp, FALSE ));
1444   gint pl;
1445   gint consumed_length;
1446
1447   // First the overall layer parameters
1448   memcpy(&pl, data, sizeof(pl));
1449   data += sizeof(pl);
1450   vik_layer_unmarshall_params ( VIK_LAYER(vtl), data, pl, vvp );
1451   data += pl;
1452
1453   consumed_length = pl;
1454   const gint sizeof_len_and_subtype = sizeof(gint) + sizeof(gint);
1455
1456 #define tlm_size (*(gint *)data)
1457   // See marshalling above for order of how this is written
1458 #define tlm_next \
1459   data += sizeof_len_and_subtype + tlm_size;
1460
1461   // Now the individual sublayers:
1462
1463   while ( *data && consumed_length < len ) {
1464     // Normally four extra bytes at the end of the datastream
1465     //  (since it's a GByteArray and that's where it's length is stored)
1466     //  So only attempt read when there's an actual block of sublayer data
1467     if ( consumed_length + tlm_size < len ) {
1468
1469       // Reuse pl to read the subtype from the data stream
1470       memcpy(&pl, data+sizeof(gint), sizeof(pl));
1471
1472       if ( pl == VIK_TRW_LAYER_SUBLAYER_TRACK ) {
1473         VikTrack *trk = vik_track_unmarshall ( data + sizeof_len_and_subtype, 0 );
1474         gchar *name = g_strdup ( trk->name );
1475         vik_trw_layer_add_track ( vtl, name, trk );
1476         g_free ( name );
1477       }
1478       if ( pl == VIK_TRW_LAYER_SUBLAYER_WAYPOINT ) {
1479         VikWaypoint *wp = vik_waypoint_unmarshall ( data + sizeof_len_and_subtype, 0 );
1480         gchar *name = g_strdup ( wp->name );
1481         vik_trw_layer_add_waypoint ( vtl, name, wp );
1482         g_free ( name );
1483       }
1484       if ( pl == VIK_TRW_LAYER_SUBLAYER_ROUTE ) {
1485         VikTrack *trk = vik_track_unmarshall ( data + sizeof_len_and_subtype, 0 );
1486         gchar *name = g_strdup ( trk->name );
1487         vik_trw_layer_add_route ( vtl, name, trk );
1488         g_free ( name );
1489       }
1490     }
1491     consumed_length += tlm_size + sizeof_len_and_subtype;
1492     tlm_next;
1493   }
1494   //g_debug ("consumed_length %d vs len %d", consumed_length, len);
1495
1496   // Not stored anywhere else so need to regenerate
1497   trw_layer_calculate_bounds_waypoints ( vtl );
1498
1499   return vtl;
1500 }
1501
1502 // Keep interesting hash function at least visible
1503 /*
1504 static guint strcase_hash(gconstpointer v)
1505 {
1506   // 31 bit hash function
1507   int i;
1508   const gchar *t = v;
1509   gchar s[128];   // malloc is too slow for reading big files
1510   gchar *p = s;
1511
1512   for (i = 0; (i < (sizeof(s)- 1)) && t[i]; i++)
1513       p[i] = toupper(t[i]);
1514   p[i] = '\0';
1515
1516   p = s;
1517   guint32 h = *p;
1518   if (h) {
1519     for (p += 1; *p != '\0'; p++)
1520       h = (h << 5) - h + *p;
1521   }
1522
1523   return h;  
1524 }
1525 */
1526
1527 // Stick a 1 at the end of the function name to make it more unique
1528 //  thus more easily searchable in a simple text editor
1529 static VikTrwLayer* trw_layer_new1 ( VikViewport *vvp )
1530 {
1531   VikTrwLayer *rv = VIK_TRW_LAYER ( g_object_new ( VIK_TRW_LAYER_TYPE, NULL ) );
1532   vik_layer_set_type ( VIK_LAYER(rv), VIK_LAYER_TRW );
1533
1534   // It's not entirely clear the benefits of hash tables usage here - possibly the simplicity of first implementation for unique names
1535   // Now with the name of the item stored as part of the item - these tables are effectively straightforward lists
1536
1537   // For this reworking I've choosen to keep the use of hash tables since for the expected data sizes
1538   // - even many hundreds of waypoints and tracks is quite small in the grand scheme of things,
1539   //  and with normal PC processing capabilities - it has negligibile performance impact
1540   // This also minimized the amount of rework - as the management of the hash tables already exists.
1541
1542   // The hash tables are indexed by simple integers acting as a UUID hash, which again shouldn't affect performance much
1543   //   we have to maintain a uniqueness (as before when multiple names where not allowed),
1544   //   this is to ensure it refers to the same item in the data structures used on the viewport and on the layers panel
1545
1546   rv->waypoints = g_hash_table_new_full ( g_direct_hash, g_direct_equal, NULL, (GDestroyNotify) vik_waypoint_free );
1547   rv->waypoints_iters = g_hash_table_new_full ( g_direct_hash, g_direct_equal, NULL, g_free );
1548   rv->tracks = g_hash_table_new_full ( g_direct_hash, g_direct_equal, NULL, (GDestroyNotify) vik_track_free );
1549   rv->tracks_iters = g_hash_table_new_full ( g_direct_hash, g_direct_equal, NULL, g_free );
1550   rv->routes = g_hash_table_new_full ( g_direct_hash, g_direct_equal, NULL, (GDestroyNotify) vik_track_free );
1551   rv->routes_iters = g_hash_table_new_full ( g_direct_hash, g_direct_equal, NULL, g_free );
1552
1553   rv->image_cache = g_queue_new(); // Must be performed before set_params via set_defaults
1554
1555   vik_layer_set_defaults ( VIK_LAYER(rv), vvp );
1556
1557   // Param settings that are not available via the GUI
1558   // Force to on after processing params (which defaults them to off with a zero value)
1559   rv->waypoints_visible = rv->tracks_visible = rv->routes_visible = TRUE;
1560
1561   rv->metadata = vik_trw_metadata_new ();
1562   rv->draw_sync_done = TRUE;
1563   rv->draw_sync_do = TRUE;
1564   // Everything else is 0, FALSE or NULL
1565
1566   return rv;
1567 }
1568
1569
1570 static void trw_layer_free ( VikTrwLayer *trwlayer )
1571 {
1572   g_hash_table_destroy(trwlayer->waypoints);
1573   g_hash_table_destroy(trwlayer->waypoints_iters);
1574   g_hash_table_destroy(trwlayer->tracks);
1575   g_hash_table_destroy(trwlayer->tracks_iters);
1576   g_hash_table_destroy(trwlayer->routes);
1577   g_hash_table_destroy(trwlayer->routes_iters);
1578
1579   /* ODC: replace with GArray */
1580   trw_layer_free_track_gcs ( trwlayer );
1581
1582   if ( trwlayer->wp_right_click_menu )
1583     g_object_ref_sink ( G_OBJECT(trwlayer->wp_right_click_menu) );
1584
1585   if ( trwlayer->track_right_click_menu )
1586     g_object_ref_sink ( G_OBJECT(trwlayer->track_right_click_menu) );
1587
1588   if ( trwlayer->tracklabellayout != NULL)
1589     g_object_unref ( G_OBJECT ( trwlayer->tracklabellayout ) );
1590
1591   if ( trwlayer->wplabellayout != NULL)
1592     g_object_unref ( G_OBJECT ( trwlayer->wplabellayout ) );
1593
1594   if ( trwlayer->waypoint_gc != NULL )
1595     g_object_unref ( G_OBJECT ( trwlayer->waypoint_gc ) );
1596
1597   if ( trwlayer->waypoint_text_gc != NULL )
1598     g_object_unref ( G_OBJECT ( trwlayer->waypoint_text_gc ) );
1599
1600   if ( trwlayer->waypoint_bg_gc != NULL )
1601     g_object_unref ( G_OBJECT ( trwlayer->waypoint_bg_gc ) );
1602
1603   g_free ( trwlayer->wp_fsize_str );
1604   g_free ( trwlayer->track_fsize_str );
1605
1606   if ( trwlayer->tpwin != NULL )
1607     gtk_widget_destroy ( GTK_WIDGET(trwlayer->tpwin) );
1608
1609   if ( trwlayer->tracks_analysis_dialog != NULL )
1610     gtk_widget_destroy ( GTK_WIDGET(trwlayer->tracks_analysis_dialog) );
1611
1612   g_list_foreach ( trwlayer->image_cache->head, (GFunc) cached_pixbuf_free, NULL );
1613   g_queue_free ( trwlayer->image_cache );
1614 }
1615
1616 static void init_drawing_params ( struct DrawingParams *dp, VikTrwLayer *vtl, VikViewport *vp, gboolean highlight )
1617 {
1618   dp->vtl = vtl;
1619   dp->vp = vp;
1620   dp->highlight = highlight;
1621   dp->vw = (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(dp->vtl);
1622   dp->xmpp = vik_viewport_get_xmpp ( vp );
1623   dp->ympp = vik_viewport_get_ympp ( vp );
1624   dp->width = vik_viewport_get_width ( vp );
1625   dp->height = vik_viewport_get_height ( vp );
1626   dp->cc = vtl->drawdirections_size*cos(DEG2RAD(45)); // Calculate once per vtl update - even if not used
1627   dp->ss = vtl->drawdirections_size*sin(DEG2RAD(45)); // Calculate once per vtl update - even if not used
1628
1629   dp->center = vik_viewport_get_center ( vp );
1630   dp->one_zone = vik_viewport_is_one_zone ( vp ); /* false if some other projection besides UTM */
1631   dp->lat_lon = vik_viewport_get_coord_mode ( vp ) == VIK_COORD_LATLON;
1632
1633   if ( dp->one_zone )
1634   {
1635     gint w2, h2;
1636     w2 = dp->xmpp * (dp->width / 2) + 1600 / dp->xmpp; 
1637     h2 = dp->ympp * (dp->height / 2) + 1600 / dp->ympp;
1638     /* leniency -- for tracks. Obviously for waypoints this SHOULD be a lot smaller */
1639  
1640     dp->ce1 = dp->center->east_west-w2; 
1641     dp->ce2 = dp->center->east_west+w2;
1642     dp->cn1 = dp->center->north_south-h2;
1643     dp->cn2 = dp->center->north_south+h2;
1644   } else if ( dp->lat_lon ) {
1645     VikCoord upperleft, bottomright;
1646     /* quick & dirty calculation; really want to check all corners due to lat/lon smaller at top in northern hemisphere */
1647     /* this also DOESN'T WORK if you are crossing 180/-180 lon. I don't plan to in the near future...  */
1648     vik_viewport_screen_to_coord ( vp, -500, -500, &upperleft );
1649     vik_viewport_screen_to_coord ( vp, dp->width+500, dp->height+500, &bottomright );
1650     dp->ce1 = upperleft.east_west;
1651     dp->ce2 = bottomright.east_west;
1652     dp->cn1 = bottomright.north_south;
1653     dp->cn2 = upperleft.north_south;
1654   }
1655
1656   vik_viewport_get_min_max_lat_lon ( vp, &(dp->bbox.south), &(dp->bbox.north), &(dp->bbox.west), &(dp->bbox.east) );
1657 }
1658
1659 /*
1660  * Determine the colour of the trackpoint (and/or trackline) relative to the average speed
1661  * Here a simple traffic like light colour system is used:
1662  *  . slow points are red
1663  *  . average is yellow
1664  *  . fast points are green
1665  */
1666 static gint track_section_colour_by_speed ( VikTrwLayer *vtl, VikTrackpoint *tp1, VikTrackpoint *tp2, gdouble average_speed, gdouble low_speed, gdouble high_speed )
1667 {
1668   gdouble rv = 0;
1669   if ( tp1->has_timestamp && tp2->has_timestamp ) {
1670     if ( average_speed > 0 ) {
1671       rv = ( vik_coord_diff ( &(tp1->coord), &(tp2->coord) ) / (tp1->timestamp - tp2->timestamp) );
1672       if ( rv < low_speed )
1673         return VIK_TRW_LAYER_TRACK_GC_SLOW;
1674       else if ( rv > high_speed )
1675         return VIK_TRW_LAYER_TRACK_GC_FAST;
1676       else
1677         return VIK_TRW_LAYER_TRACK_GC_AVER;
1678     }
1679   }
1680   return VIK_TRW_LAYER_TRACK_GC_BLACK;
1681 }
1682
1683 static void draw_utm_skip_insignia ( VikViewport *vvp, GdkGC *gc, gint x, gint y )
1684 {
1685   vik_viewport_draw_line ( vvp, gc, x+5, y, x-5, y );
1686   vik_viewport_draw_line ( vvp, gc, x, y+5, x, y-5 );
1687   vik_viewport_draw_line ( vvp, gc, x+5, y+5, x-5, y-5 );
1688   vik_viewport_draw_line ( vvp, gc, x+5, y-5, x-5, y+5 );
1689 }
1690
1691
1692 static void trw_layer_draw_track_label ( gchar *name, gchar *fgcolour, gchar *bgcolour, struct DrawingParams *dp, VikCoord *coord )
1693 {
1694   gchar *label_markup = g_strdup_printf ( "<span foreground=\"%s\" background=\"%s\" size=\"%s\">%s</span>", fgcolour, bgcolour, dp->vtl->track_fsize_str, name );
1695
1696   if ( pango_parse_markup ( label_markup, -1, 0, NULL, NULL, NULL, NULL ) )
1697     pango_layout_set_markup ( dp->vtl->tracklabellayout, label_markup, -1 );
1698   else
1699     // Fallback if parse failure
1700     pango_layout_set_text ( dp->vtl->tracklabellayout, name, -1 );
1701
1702   g_free ( label_markup );
1703
1704   gint label_x, label_y;
1705   gint width, height;
1706   pango_layout_get_pixel_size ( dp->vtl->tracklabellayout, &width, &height );
1707
1708   vik_viewport_coord_to_screen ( dp->vp, coord, &label_x, &label_y );
1709   vik_viewport_draw_layout ( dp->vp, dp->vtl->track_bg_gc, label_x-width/2, label_y-height/2, dp->vtl->tracklabellayout );
1710 }
1711
1712 /**
1713  * distance_in_preferred_units:
1714  * @dist: The source distance in standard SI Units (i.e. metres)
1715  *
1716  * TODO: This is a generic function that could be moved into globals.c or utils.c
1717  *
1718  * Probably best used if you have a only few conversions to perform.
1719  * However if doing many points (such as on all points along a track) then this may be a bit slow,
1720  *  since it will be doing the preference check on each call
1721  *
1722  * Returns: The distance in the units as specified by the preferences
1723  */
1724 static gdouble distance_in_preferred_units ( gdouble dist )
1725 {
1726   gdouble mydist;
1727   vik_units_distance_t dist_units = a_vik_get_units_distance ();
1728   switch (dist_units) {
1729   case VIK_UNITS_DISTANCE_MILES:
1730     mydist = VIK_METERS_TO_MILES(dist);
1731     break;
1732   // VIK_UNITS_DISTANCE_KILOMETRES:
1733   default:
1734     mydist = dist/1000.0;
1735     break;
1736   }
1737   return mydist;
1738 }
1739
1740 /**
1741  * trw_layer_draw_dist_labels:
1742  *
1743  * Draw a few labels along a track at nicely seperated distances
1744  * This might slow things down if there's many tracks being displayed with this on.
1745  */
1746 static void trw_layer_draw_dist_labels ( struct DrawingParams *dp, VikTrack *trk, gboolean drawing_highlight )
1747 {
1748   static const gdouble chunksd[] = {0.25, 0.5, 1.0, 2.0, 5.0, 10.0, 15.0, 20.0,
1749                                     25.0, 40.0, 50.0, 75.0, 100.0,
1750                                     150.0, 200.0, 250.0, 500.0, 1000.0};
1751
1752   gdouble dist = vik_track_get_length_including_gaps ( trk ) / (trk->max_number_dist_labels+1);
1753
1754   // Convert to specified unit to find the friendly breakdown value
1755   dist = distance_in_preferred_units ( dist );
1756
1757   gint index = 0;
1758   gint i=0;
1759   for ( i = 0; i < G_N_ELEMENTS(chunksd); i++ ) {
1760     if ( chunksd[i] > dist ) {
1761       index = i;
1762       dist = chunksd[index];
1763       break;
1764     }
1765   }
1766
1767   vik_units_distance_t dist_units = a_vik_get_units_distance ();
1768
1769   for ( i = 1; i < trk->max_number_dist_labels+1; i++ ) {
1770     gdouble dist_i = dist * i;
1771
1772     // Convert distance back into metres for use in finding a trackpoint
1773     switch (dist_units) {
1774     case VIK_UNITS_DISTANCE_MILES:
1775       dist_i = VIK_MILES_TO_METERS(dist_i);
1776       break;
1777       // VIK_UNITS_DISTANCE_KILOMETRES:
1778     default:
1779       dist_i = dist_i*1000.0;
1780       break;
1781     }
1782
1783     gdouble dist_current = 0.0;
1784     VikTrackpoint *tp_current = vik_track_get_tp_by_dist ( trk, dist_i, FALSE, &dist_current );
1785     gdouble dist_next = 0.0;
1786     VikTrackpoint *tp_next = vik_track_get_tp_by_dist ( trk, dist_i, TRUE, &dist_next );
1787
1788     gdouble dist_between_tps = fabs (dist_next - dist_current);
1789     gdouble ratio = 0.0;
1790     // Prevent division by 0 errors
1791     if ( dist_between_tps > 0.0 )
1792       ratio = fabs(dist_i-dist_current)/dist_between_tps;
1793
1794     if ( tp_current && tp_next ) {
1795       // Construct the name based on the distance value
1796       gchar *name;
1797       gchar *units;
1798       switch (dist_units) {
1799       case VIK_UNITS_DISTANCE_MILES:
1800         units = g_strdup ( _("miles") );
1801         break;
1802         // VIK_UNITS_DISTANCE_KILOMETRES:
1803       default:
1804         units = g_strdup ( _("km") );
1805         break;
1806       }
1807
1808       // Convert for display
1809       dist_i = distance_in_preferred_units ( dist_i );
1810
1811       // Make the precision of the output related to the unit size.
1812       if ( index == 0 )
1813         name = g_strdup_printf ( "%.2f %s", dist_i, units);
1814       else if ( index == 1 )
1815         name = g_strdup_printf ( "%.1f %s", dist_i, units);
1816       else
1817         name = g_strdup_printf ( "%d %s", (gint)round(dist_i), units); // TODO single vs plurals
1818       g_free ( units );
1819
1820       struct LatLon ll_current, ll_next;
1821       vik_coord_to_latlon ( &tp_current->coord, &ll_current );
1822       vik_coord_to_latlon ( &tp_next->coord, &ll_next );
1823
1824       // positional interpolation
1825       // Using a simple ratio - may not be perfectly correct due to lat/long projections
1826       //  but should be good enough over the small scale that I anticipate usage on
1827       struct LatLon ll_new = { ll_current.lat + (ll_next.lat-ll_current.lat)*ratio,
1828                                ll_current.lon + (ll_next.lon-ll_current.lon)*ratio };
1829       VikCoord coord;
1830       vik_coord_load_from_latlon ( &coord, dp->vtl->coord_mode, &ll_new );
1831
1832       gchar *fgcolour;
1833       if ( dp->vtl->drawmode == DRAWMODE_BY_TRACK )
1834         fgcolour = gdk_color_to_string ( &(trk->color) );
1835       else
1836         fgcolour = gdk_color_to_string ( &(dp->vtl->track_color) );
1837
1838       // if highlight mode on, then colour the background in the highlight colour
1839       gchar *bgcolour;
1840       if ( drawing_highlight )
1841         bgcolour = g_strdup ( vik_viewport_get_highlight_color ( dp->vp ) );
1842       else
1843         bgcolour = gdk_color_to_string ( &(dp->vtl->track_bg_color) );
1844
1845       trw_layer_draw_track_label ( name, fgcolour, bgcolour, dp, &coord );
1846
1847       g_free ( fgcolour );
1848       g_free ( bgcolour );
1849       g_free ( name );
1850     }
1851   }
1852 }
1853
1854 /**
1855  * trw_layer_draw_track_name_labels:
1856  *
1857  * Draw a label (or labels) for the track name somewhere depending on the track's properties
1858  */
1859 static void trw_layer_draw_track_name_labels ( struct DrawingParams *dp, VikTrack *trk, gboolean drawing_highlight )
1860 {
1861   gchar *fgcolour;
1862   if ( dp->vtl->drawmode == DRAWMODE_BY_TRACK )
1863     fgcolour = gdk_color_to_string ( &(trk->color) );
1864   else
1865     fgcolour = gdk_color_to_string ( &(dp->vtl->track_color) );
1866
1867   // if highlight mode on, then colour the background in the highlight colour
1868   gchar *bgcolour;
1869   if ( drawing_highlight )
1870     bgcolour = g_strdup ( vik_viewport_get_highlight_color ( dp->vp ) );
1871   else
1872     bgcolour = gdk_color_to_string ( &(dp->vtl->track_bg_color) );
1873
1874   gchar *ename = g_markup_escape_text ( trk->name, -1 );
1875
1876   if ( trk->draw_name_mode == TRACK_DRAWNAME_START_END_CENTRE ||
1877        trk->draw_name_mode == TRACK_DRAWNAME_CENTRE ) {
1878     struct LatLon average, maxmin[2] = { {0,0}, {0,0} };
1879     trw_layer_find_maxmin_tracks ( NULL, trk, maxmin );
1880     average.lat = (maxmin[0].lat+maxmin[1].lat)/2;
1881     average.lon = (maxmin[0].lon+maxmin[1].lon)/2;
1882     VikCoord coord;
1883     vik_coord_load_from_latlon ( &coord, dp->vtl->coord_mode, &average );
1884
1885     trw_layer_draw_track_label ( ename, fgcolour, bgcolour, dp, &coord );
1886   }
1887
1888   if ( trk->draw_name_mode == TRACK_DRAWNAME_CENTRE )
1889     // No other labels to draw
1890     return;
1891
1892   VikTrackpoint *tp_end = vik_track_get_tp_last ( trk );
1893   if ( !tp_end )
1894     return;
1895   VikTrackpoint *tp_begin = vik_track_get_tp_first ( trk );
1896   if ( !tp_begin )
1897     return;
1898   VikCoord begin_coord = tp_begin->coord;
1899   VikCoord end_coord = tp_end->coord;
1900
1901   gboolean done_start_end = FALSE;
1902
1903   if ( trk->draw_name_mode == TRACK_DRAWNAME_START_END ||
1904        trk->draw_name_mode == TRACK_DRAWNAME_START_END_CENTRE ) {
1905
1906     // This number can be configured via the settings if you really want to change it
1907     gdouble distance_diff;
1908     if ( ! a_settings_get_double ( "trackwaypoint_start_end_distance_diff", &distance_diff ) )
1909       distance_diff = 100.0; // Metres
1910
1911     if ( vik_coord_diff ( &begin_coord, &end_coord ) < distance_diff ) {
1912       // Start and end 'close' together so only draw one label at an average location
1913       gint x1, x2, y1, y2;
1914       vik_viewport_coord_to_screen ( dp->vp, &begin_coord, &x1, &y1);
1915       vik_viewport_coord_to_screen ( dp->vp, &end_coord, &x2, &y2);
1916       VikCoord av_coord;
1917       vik_viewport_screen_to_coord ( dp->vp, (x1 + x2) / 2, (y1 + y2) / 2, &av_coord );
1918
1919       gchar *name = g_strdup_printf ( "%s: %s", ename, _("start/end") );
1920       trw_layer_draw_track_label ( name, fgcolour, bgcolour, dp, &av_coord );
1921       g_free ( name );
1922
1923       done_start_end = TRUE;
1924     }
1925   }
1926
1927   if ( ! done_start_end ) {
1928     if ( trk->draw_name_mode == TRACK_DRAWNAME_START ||
1929          trk->draw_name_mode == TRACK_DRAWNAME_START_END ||
1930          trk->draw_name_mode == TRACK_DRAWNAME_START_END_CENTRE ) {
1931       gchar *name_start = g_strdup_printf ( "%s: %s", ename, _("start") );
1932       trw_layer_draw_track_label ( name_start, fgcolour, bgcolour, dp, &begin_coord );
1933       g_free ( name_start );
1934     }
1935     // Don't draw end label if this is the one being created
1936     if ( trk != dp->vtl->current_track ) {
1937       if ( trk->draw_name_mode == TRACK_DRAWNAME_END ||
1938            trk->draw_name_mode == TRACK_DRAWNAME_START_END ||
1939            trk->draw_name_mode == TRACK_DRAWNAME_START_END_CENTRE ) {
1940         gchar *name_end = g_strdup_printf ( "%s: %s", ename, _("end") );
1941         trw_layer_draw_track_label ( name_end, fgcolour, bgcolour, dp, &end_coord );
1942         g_free ( name_end );
1943       }
1944     }
1945   }
1946
1947   g_free ( fgcolour );
1948   g_free ( bgcolour );
1949   g_free ( ename );
1950 }
1951
1952 static void trw_layer_draw_track ( const gpointer id, VikTrack *track, struct DrawingParams *dp, gboolean draw_track_outline )
1953 {
1954   if ( ! track->visible )
1955     return;
1956
1957   /* TODO: this function is a mess, get rid of any redundancy */
1958   GList *list = track->trackpoints;
1959   GdkGC *main_gc;
1960   gboolean useoldvals = TRUE;
1961
1962   gboolean drawpoints;
1963   gboolean drawstops;
1964   gboolean drawelevation;
1965   gdouble min_alt, max_alt, alt_diff = 0;
1966
1967   const guint8 tp_size_reg = dp->vtl->drawpoints_size;
1968   const guint8 tp_size_cur = dp->vtl->drawpoints_size*2;
1969   guint8 tp_size;
1970
1971   if ( dp->vtl->drawelevation )
1972   {
1973     /* assume if it has elevation at the beginning, it has it throughout. not ness a true good assumption */
1974     if ( ( drawelevation = vik_track_get_minmax_alt ( track, &min_alt, &max_alt ) ) )
1975       alt_diff = max_alt - min_alt;
1976   }
1977
1978   /* admittedly this is not an efficient way to do it because we go through the whole GC thing all over... */
1979   if ( dp->vtl->bg_line_thickness && !draw_track_outline )
1980     trw_layer_draw_track ( id, track, dp, TRUE );
1981
1982   if ( draw_track_outline )
1983     drawpoints = drawstops = FALSE;
1984   else {
1985     drawpoints = dp->vtl->drawpoints;
1986     drawstops = dp->vtl->drawstops;
1987   }
1988
1989   gboolean drawing_highlight = FALSE;
1990   /* Current track - used for creation */
1991   if ( track == dp->vtl->current_track )
1992     main_gc = dp->vtl->current_track_gc;
1993   else {
1994     if ( dp->highlight ) {
1995       /* Draw all tracks of the layer in special colour
1996          NB this supercedes the drawmode */
1997       main_gc = vik_viewport_get_gc_highlight (dp->vp);
1998       drawing_highlight = TRUE;
1999     }
2000     if ( !drawing_highlight ) {
2001       // Still need to figure out the gc according to the drawing mode:
2002       switch ( dp->vtl->drawmode ) {
2003       case DRAWMODE_BY_TRACK:
2004         if ( dp->vtl->track_1color_gc )
2005           g_object_unref ( dp->vtl->track_1color_gc );
2006         dp->vtl->track_1color_gc = vik_viewport_new_gc_from_color ( dp->vp, &track->color, dp->vtl->line_thickness );
2007         main_gc = dp->vtl->track_1color_gc;
2008         break;
2009       default:
2010         // Mostly for DRAWMODE_ALL_SAME_COLOR
2011         // but includes DRAWMODE_BY_SPEED, main_gc is set later on as necessary
2012         main_gc = g_array_index(dp->vtl->track_gc, GdkGC *, VIK_TRW_LAYER_TRACK_GC_SINGLE);
2013         break;
2014       }
2015     }
2016   }
2017
2018   if (list) {
2019     int x, y, oldx, oldy;
2020     VikTrackpoint *tp = VIK_TRACKPOINT(list->data);
2021   
2022     tp_size = (list == dp->vtl->current_tpl) ? tp_size_cur : tp_size_reg;
2023
2024     vik_viewport_coord_to_screen ( dp->vp, &(tp->coord), &x, &y );
2025
2026     // Draw the first point as something a bit different from the normal points
2027     // ATM it's slightly bigger and a triangle
2028     if ( drawpoints ) {
2029       GdkPoint trian[3] = { { x, y-(3*tp_size) }, { x-(2*tp_size), y+(2*tp_size) }, {x+(2*tp_size), y+(2*tp_size)} };
2030       vik_viewport_draw_polygon ( dp->vp, main_gc, TRUE, trian, 3 );
2031     }
2032
2033     oldx = x;
2034     oldy = y;
2035
2036     gdouble average_speed = 0.0;
2037     gdouble low_speed = 0.0;
2038     gdouble high_speed = 0.0;
2039     // If necessary calculate these values - which is done only once per track redraw
2040     if ( dp->vtl->drawmode == DRAWMODE_BY_SPEED ) {
2041       // the percentage factor away from the average speed determines transistions between the levels
2042       average_speed = vik_track_get_average_speed_moving(track, dp->vtl->stop_length);
2043       low_speed = average_speed - (average_speed*(dp->vtl->track_draw_speed_factor/100.0));
2044       high_speed = average_speed + (average_speed*(dp->vtl->track_draw_speed_factor/100.0));
2045     }
2046
2047     while ((list = g_list_next(list)))
2048     {
2049       tp = VIK_TRACKPOINT(list->data);
2050       tp_size = (list == dp->vtl->current_tpl) ? tp_size_cur : tp_size_reg;
2051
2052       /* check some stuff -- but only if we're in UTM and there's only ONE ZONE; or lat lon */
2053       if ( (!dp->one_zone && !dp->lat_lon) ||     /* UTM & zones; do everything */
2054              ( ((!dp->one_zone) || tp->coord.utm_zone == dp->center->utm_zone) &&   /* only check zones if UTM & one_zone */
2055              tp->coord.east_west < dp->ce2 && tp->coord.east_west > dp->ce1 &&  /* both UTM and lat lon */
2056              tp->coord.north_south > dp->cn1 && tp->coord.north_south < dp->cn2 ) )
2057       {
2058         vik_viewport_coord_to_screen ( dp->vp, &(tp->coord), &x, &y );
2059
2060         /*
2061          * If points are the same in display coordinates, don't draw.
2062          */
2063         if ( useoldvals && x == oldx && y == oldy )
2064         {
2065           // Still need to process points to ensure 'stops' are drawn if required
2066           if ( drawstops && drawpoints && ! draw_track_outline && list->next &&
2067                (VIK_TRACKPOINT(list->next->data)->timestamp - VIK_TRACKPOINT(list->data)->timestamp > dp->vtl->stop_length) )
2068             vik_viewport_draw_arc ( dp->vp, g_array_index(dp->vtl->track_gc, GdkGC *, 11), TRUE, x-(3*tp_size), y-(3*tp_size), 6*tp_size, 6*tp_size, 0, 360*64 );
2069
2070           goto skip;
2071         }
2072
2073         VikTrackpoint *tp2 = VIK_TRACKPOINT(list->prev->data);
2074         if ( drawpoints || dp->vtl->drawlines ) {
2075           // setup main_gc for both point and line drawing
2076           if ( !drawing_highlight && (dp->vtl->drawmode == DRAWMODE_BY_SPEED) ) {
2077             main_gc = g_array_index(dp->vtl->track_gc, GdkGC *, track_section_colour_by_speed ( dp->vtl, tp, tp2, average_speed, low_speed, high_speed ) );
2078           }
2079         }
2080
2081         if ( drawpoints && ! draw_track_outline )
2082         {
2083
2084           if ( list->next ) {
2085             /*
2086              * The concept of drawing stops is that a trackpoint
2087              * that is if the next trackpoint has a timestamp far into
2088              * the future, we draw a circle of 6x trackpoint size,
2089              * instead of a rectangle of 2x trackpoint size.
2090              * This is drawn first so the trackpoint will be drawn on top
2091              */
2092             /* stops */
2093             if ( drawstops && VIK_TRACKPOINT(list->next->data)->timestamp - VIK_TRACKPOINT(list->data)->timestamp > dp->vtl->stop_length )
2094               /* Stop point.  Draw 6x circle. Always in redish colour */
2095               vik_viewport_draw_arc ( dp->vp, g_array_index(dp->vtl->track_gc, GdkGC *, VIK_TRW_LAYER_TRACK_GC_STOP), TRUE, x-(3*tp_size), y-(3*tp_size), 6*tp_size, 6*tp_size, 0, 360*64 );
2096
2097             /* Regular point - draw 2x square. */
2098             vik_viewport_draw_rectangle ( dp->vp, main_gc, TRUE, x-tp_size, y-tp_size, 2*tp_size, 2*tp_size );
2099           }
2100           else
2101             /* Final point - draw 4x circle. */
2102             vik_viewport_draw_arc ( dp->vp, main_gc, TRUE, x-(2*tp_size), y-(2*tp_size), 4*tp_size, 4*tp_size, 0, 360*64 );
2103         }
2104
2105         if ((!tp->newsegment) && (dp->vtl->drawlines))
2106         {
2107
2108           /* UTM only: zone check */
2109           if ( drawpoints && dp->vtl->coord_mode == VIK_COORD_UTM && tp->coord.utm_zone != dp->center->utm_zone )
2110             draw_utm_skip_insignia (  dp->vp, main_gc, x, y);
2111
2112           if (!useoldvals)
2113             vik_viewport_coord_to_screen ( dp->vp, &(tp2->coord), &oldx, &oldy );
2114
2115           if ( draw_track_outline ) {
2116             vik_viewport_draw_line ( dp->vp, dp->vtl->track_bg_gc, oldx, oldy, x, y);
2117           }
2118           else {
2119
2120             vik_viewport_draw_line ( dp->vp, main_gc, oldx, oldy, x, y);
2121
2122             if ( dp->vtl->drawelevation && list->next && VIK_TRACKPOINT(list->next->data)->altitude != VIK_DEFAULT_ALTITUDE ) {
2123               GdkPoint tmp[4];
2124               #define FIXALTITUDE(what) ((VIK_TRACKPOINT((what))->altitude-min_alt)/alt_diff*DRAW_ELEVATION_FACTOR*dp->vtl->elevation_factor/dp->xmpp)
2125
2126               tmp[0].x = oldx;
2127               tmp[0].y = oldy;
2128               tmp[1].x = oldx;
2129               tmp[1].y = oldy-FIXALTITUDE(list->data);
2130               tmp[2].x = x;
2131               tmp[2].y = y-FIXALTITUDE(list->next->data);
2132               tmp[3].x = x;
2133               tmp[3].y = y;
2134
2135               GdkGC *tmp_gc;
2136               if ( ((oldx - x) > 0 && (oldy - y) > 0) || ((oldx - x) < 0 && (oldy - y) < 0))
2137                 tmp_gc = gtk_widget_get_style(GTK_WIDGET(dp->vp))->light_gc[3];
2138               else
2139                 tmp_gc = gtk_widget_get_style(GTK_WIDGET(dp->vp))->dark_gc[0];
2140               vik_viewport_draw_polygon ( dp->vp, tmp_gc, TRUE, tmp, 4);
2141
2142               vik_viewport_draw_line ( dp->vp, main_gc, oldx, oldy-FIXALTITUDE(list->data), x, y-FIXALTITUDE(list->next->data));
2143             }
2144           }
2145         }
2146
2147         if ( (!tp->newsegment) && dp->vtl->drawdirections ) {
2148           // Draw an arrow at the mid point to show the direction of the track
2149           // Code is a rework from vikwindow::draw_ruler()
2150           gint midx = (oldx + x) / 2;
2151           gint midy = (oldy + y) / 2;
2152
2153           gdouble len = sqrt ( ((midx-oldx) * (midx-oldx)) + ((midy-oldy) * (midy-oldy)) );
2154           // Avoid divide by zero and ensure at least 1 pixel big
2155           if ( len > 1 ) {
2156             gdouble dx = (oldx - midx) / len;
2157             gdouble dy = (oldy - midy) / len;
2158             vik_viewport_draw_line ( dp->vp, main_gc, midx, midy, midx + (dx * dp->cc + dy * dp->ss), midy + (dy * dp->cc - dx * dp->ss) );
2159             vik_viewport_draw_line ( dp->vp, main_gc, midx, midy, midx + (dx * dp->cc - dy * dp->ss), midy + (dy * dp->cc + dx * dp->ss) );
2160           }
2161         }
2162
2163       skip:
2164         oldx = x;
2165         oldy = y;
2166         useoldvals = TRUE;
2167       }
2168       else {
2169         if (useoldvals && dp->vtl->drawlines && (!tp->newsegment))
2170         {
2171           VikTrackpoint *tp2 = VIK_TRACKPOINT(list->prev->data);
2172           if ( dp->vtl->coord_mode != VIK_COORD_UTM || tp->coord.utm_zone == dp->center->utm_zone )
2173           {
2174             vik_viewport_coord_to_screen ( dp->vp, &(tp->coord), &x, &y );
2175
2176             if ( !drawing_highlight && (dp->vtl->drawmode == DRAWMODE_BY_SPEED) ) {
2177               main_gc = g_array_index(dp->vtl->track_gc, GdkGC *, track_section_colour_by_speed ( dp->vtl, tp, tp2, average_speed, low_speed, high_speed ));
2178             }
2179
2180             /*
2181              * If points are the same in display coordinates, don't draw.
2182              */
2183             if ( x != oldx || y != oldy )
2184               {
2185                 if ( draw_track_outline )
2186                   vik_viewport_draw_line ( dp->vp, dp->vtl->track_bg_gc, oldx, oldy, x, y);
2187                 else
2188                   vik_viewport_draw_line ( dp->vp, main_gc, oldx, oldy, x, y);
2189               }
2190           }
2191           else 
2192           {
2193             /*
2194              * If points are the same in display coordinates, don't draw.
2195              */
2196             if ( x != oldx && y != oldy )
2197               {
2198                 vik_viewport_coord_to_screen ( dp->vp, &(tp2->coord), &x, &y );
2199                 draw_utm_skip_insignia ( dp->vp, main_gc, x, y );
2200               }
2201           }
2202         }
2203         useoldvals = FALSE;
2204       }
2205     }
2206
2207     // Labels drawn after the trackpoints, so the labels are on top
2208     if ( dp->vtl->track_draw_labels ) {
2209       if ( track->max_number_dist_labels > 0 ) {
2210         trw_layer_draw_dist_labels ( dp, track, drawing_highlight );
2211       }
2212
2213       if ( track->draw_name_mode != TRACK_DRAWNAME_NO ) {
2214         trw_layer_draw_track_name_labels ( dp, track, drawing_highlight );
2215       }
2216     }
2217   }
2218 }
2219
2220 static void trw_layer_draw_track_cb ( const gpointer id, VikTrack *track, struct DrawingParams *dp )
2221 {
2222   if ( BBOX_INTERSECT ( track->bbox, dp->bbox ) ) {
2223     trw_layer_draw_track ( id, track, dp, FALSE );
2224   }
2225 }
2226
2227 static void cached_pixbuf_free ( CachedPixbuf *cp )
2228 {
2229   g_object_unref ( G_OBJECT(cp->pixbuf) );
2230   g_free ( cp->image );
2231 }
2232
2233 static gint cached_pixbuf_cmp ( CachedPixbuf *cp, const gchar *name )
2234 {
2235   return strcmp ( cp->image, name );
2236 }
2237
2238 static void trw_layer_draw_waypoint ( const gpointer id, VikWaypoint *wp, struct DrawingParams *dp )
2239 {
2240   if ( wp->visible )
2241   if ( (!dp->one_zone && !dp->lat_lon) || ( ( dp->lat_lon || wp->coord.utm_zone == dp->center->utm_zone ) && 
2242              wp->coord.east_west < dp->ce2 && wp->coord.east_west > dp->ce1 && 
2243              wp->coord.north_south > dp->cn1 && wp->coord.north_south < dp->cn2 ) )
2244   {
2245     gint x, y;
2246     vik_viewport_coord_to_screen ( dp->vp, &(wp->coord), &x, &y );
2247
2248     /* if in shrunken_cache, get that. If not, get and add to shrunken_cache */
2249
2250     if ( wp->image && dp->vtl->drawimages )
2251     {
2252       GdkPixbuf *pixbuf = NULL;
2253       GList *l;
2254
2255       if ( dp->vtl->image_alpha == 0)
2256         return;
2257
2258       l = g_list_find_custom ( dp->vtl->image_cache->head, wp->image, (GCompareFunc) cached_pixbuf_cmp );
2259       if ( l )
2260         pixbuf = ((CachedPixbuf *) l->data)->pixbuf;
2261       else
2262       {
2263         gchar *image = wp->image;
2264         GdkPixbuf *regularthumb = a_thumbnails_get ( wp->image );
2265         if ( ! regularthumb )
2266         {
2267           regularthumb = a_thumbnails_get_default (); /* cache one 'not yet loaded' for all thumbs not loaded */
2268           image = "\x12\x00"; /* this shouldn't occur naturally. */
2269         }
2270         if ( regularthumb )
2271         {
2272           CachedPixbuf *cp = NULL;
2273           cp = g_malloc ( sizeof ( CachedPixbuf ) );
2274           if ( dp->vtl->image_size == 128 )
2275             cp->pixbuf = regularthumb;
2276           else
2277           {
2278             cp->pixbuf = a_thumbnails_scale_pixbuf(regularthumb, dp->vtl->image_size, dp->vtl->image_size);
2279             g_assert ( cp->pixbuf );
2280             g_object_unref ( G_OBJECT(regularthumb) );
2281           }
2282           cp->image = g_strdup ( image );
2283
2284           /* needed so 'click picture' tool knows how big the pic is; we don't
2285            * store it in cp because they may have been freed already. */
2286           wp->image_width = gdk_pixbuf_get_width ( cp->pixbuf );
2287           wp->image_height = gdk_pixbuf_get_height ( cp->pixbuf );
2288
2289           g_queue_push_head ( dp->vtl->image_cache, cp );
2290           if ( dp->vtl->image_cache->length > dp->vtl->image_cache_size )
2291             cached_pixbuf_free ( g_queue_pop_tail ( dp->vtl->image_cache ) );
2292
2293           pixbuf = cp->pixbuf;
2294         }
2295         else
2296         {
2297           pixbuf = a_thumbnails_get_default (); /* thumbnail not yet loaded */
2298         }
2299       }
2300       if ( pixbuf )
2301       {
2302         gint w, h;
2303         w = gdk_pixbuf_get_width ( pixbuf );
2304         h = gdk_pixbuf_get_height ( pixbuf );
2305
2306         if ( x+(w/2) > 0 && y+(h/2) > 0 && x-(w/2) < dp->width && y-(h/2) < dp->height ) /* always draw within boundaries */
2307         {
2308           if ( dp->highlight ) {
2309             // Highlighted - so draw a little border around the chosen one
2310             // single line seems a little weak so draw 2 of them
2311             vik_viewport_draw_rectangle (dp->vp, vik_viewport_get_gc_highlight (dp->vp), FALSE,
2312                                          x - (w/2) - 1, y - (h/2) - 1, w + 2, h + 2 );
2313             vik_viewport_draw_rectangle (dp->vp, vik_viewport_get_gc_highlight (dp->vp), FALSE,
2314                                          x - (w/2) - 2, y - (h/2) - 2, w + 4, h + 4 );
2315           }
2316
2317           if ( dp->vtl->image_alpha == 255 )
2318             vik_viewport_draw_pixbuf ( dp->vp, pixbuf, 0, 0, x - (w/2), y - (h/2), w, h );
2319           else
2320             vik_viewport_draw_pixbuf_with_alpha ( dp->vp, pixbuf, dp->vtl->image_alpha, 0, 0, x - (w/2), y - (h/2), w, h );
2321         }
2322         return; /* if failed to draw picture, default to drawing regular waypoint (below) */
2323       }
2324     }
2325
2326     // Draw appropriate symbol - either symbol image or simple types
2327     if ( dp->vtl->wp_draw_symbols && wp->symbol && wp->symbol_pixbuf ) {
2328       vik_viewport_draw_pixbuf ( dp->vp, wp->symbol_pixbuf, 0, 0, x - gdk_pixbuf_get_width(wp->symbol_pixbuf)/2, y - gdk_pixbuf_get_height(wp->symbol_pixbuf)/2, -1, -1 );
2329     } 
2330     else if ( wp == dp->vtl->current_wp ) {
2331       switch ( dp->vtl->wp_symbol ) {
2332         case WP_SYMBOL_FILLED_SQUARE: vik_viewport_draw_rectangle ( dp->vp, dp->vtl->waypoint_gc, TRUE, x - (dp->vtl->wp_size), y - (dp->vtl->wp_size), dp->vtl->wp_size*2, dp->vtl->wp_size*2 ); break;
2333         case WP_SYMBOL_SQUARE: vik_viewport_draw_rectangle ( dp->vp, dp->vtl->waypoint_gc, FALSE, x - (dp->vtl->wp_size), y - (dp->vtl->wp_size), dp->vtl->wp_size*2, dp->vtl->wp_size*2 ); break;
2334         case WP_SYMBOL_CIRCLE: vik_viewport_draw_arc ( dp->vp, dp->vtl->waypoint_gc, TRUE, x - dp->vtl->wp_size, y - dp->vtl->wp_size, dp->vtl->wp_size, dp->vtl->wp_size, 0, 360*64 ); break;
2335         case WP_SYMBOL_X: vik_viewport_draw_line ( dp->vp, dp->vtl->waypoint_gc, x - dp->vtl->wp_size*2, y - dp->vtl->wp_size*2, x + dp->vtl->wp_size*2, y + dp->vtl->wp_size*2 );
2336                           vik_viewport_draw_line ( dp->vp, dp->vtl->waypoint_gc, x - dp->vtl->wp_size*2, y + dp->vtl->wp_size*2, x + dp->vtl->wp_size*2, y - dp->vtl->wp_size*2 );
2337         default: break;
2338       }
2339     }
2340     else {
2341       switch ( dp->vtl->wp_symbol ) {
2342         case WP_SYMBOL_FILLED_SQUARE: vik_viewport_draw_rectangle ( dp->vp, dp->vtl->waypoint_gc, TRUE, x - dp->vtl->wp_size/2, y - dp->vtl->wp_size/2, dp->vtl->wp_size, dp->vtl->wp_size ); break;
2343         case WP_SYMBOL_SQUARE: vik_viewport_draw_rectangle ( dp->vp, dp->vtl->waypoint_gc, FALSE, x - dp->vtl->wp_size/2, y - dp->vtl->wp_size/2, dp->vtl->wp_size, dp->vtl->wp_size ); break;
2344         case WP_SYMBOL_CIRCLE: vik_viewport_draw_arc ( dp->vp, dp->vtl->waypoint_gc, TRUE, x-dp->vtl->wp_size/2, y-dp->vtl->wp_size/2, dp->vtl->wp_size, dp->vtl->wp_size, 0, 360*64 ); break;
2345         case WP_SYMBOL_X: vik_viewport_draw_line ( dp->vp, dp->vtl->waypoint_gc, x-dp->vtl->wp_size, y-dp->vtl->wp_size, x+dp->vtl->wp_size, y+dp->vtl->wp_size );
2346                           vik_viewport_draw_line ( dp->vp, dp->vtl->waypoint_gc, x-dp->vtl->wp_size, y+dp->vtl->wp_size, x+dp->vtl->wp_size, y-dp->vtl->wp_size ); break;
2347         default: break;
2348       }
2349     }
2350
2351     if ( dp->vtl->drawlabels )
2352     {
2353       /* thanks to the GPSDrive people (Fritz Ganter et al.) for hints on this part ... yah, I'm too lazy to study documentation */
2354       gint label_x, label_y;
2355       gint width, height;
2356       // Hopefully name won't break the markup (may need to sanitize - g_markup_escape_text())
2357
2358       // Could this stored in the waypoint rather than recreating each pass?
2359       gchar *wp_label_markup = g_strdup_printf ( "<span size=\"%s\">%s</span>", dp->vtl->wp_fsize_str, wp->name );
2360
2361       if ( pango_parse_markup ( wp_label_markup, -1, 0, NULL, NULL, NULL, NULL ) )
2362         pango_layout_set_markup ( dp->vtl->wplabellayout, wp_label_markup, -1 );
2363       else
2364         // Fallback if parse failure
2365         pango_layout_set_text ( dp->vtl->wplabellayout, wp->name, -1 );
2366
2367       g_free ( wp_label_markup );
2368
2369       pango_layout_get_pixel_size ( dp->vtl->wplabellayout, &width, &height );
2370       label_x = x - width/2;
2371       if ( wp->symbol_pixbuf )
2372         label_y = y - height - 2 - gdk_pixbuf_get_height(wp->symbol_pixbuf)/2;
2373       else
2374         label_y = y - dp->vtl->wp_size - height - 2;
2375
2376       /* if highlight mode on, then draw background text in highlight colour */
2377       if ( dp->highlight )
2378         vik_viewport_draw_rectangle ( dp->vp, vik_viewport_get_gc_highlight (dp->vp), TRUE, label_x - 1, label_y-1,width+2,height+2);
2379       else
2380         vik_viewport_draw_rectangle ( dp->vp, dp->vtl->waypoint_bg_gc, TRUE, label_x - 1, label_y-1,width+2,height+2);
2381       vik_viewport_draw_layout ( dp->vp, dp->vtl->waypoint_text_gc, label_x, label_y, dp->vtl->wplabellayout );
2382     }
2383   }
2384 }
2385
2386 static void trw_layer_draw_waypoint_cb ( gpointer id, VikWaypoint *wp, struct DrawingParams *dp )
2387 {
2388   if ( BBOX_INTERSECT ( dp->vtl->waypoints_bbox, dp->bbox ) ) {
2389     trw_layer_draw_waypoint ( id, wp, dp );
2390   }
2391 }
2392
2393 static void trw_layer_draw_with_highlight ( VikTrwLayer *l, gpointer data, gboolean highlight )
2394 {
2395   static struct DrawingParams dp;
2396   g_assert ( l != NULL );
2397
2398   init_drawing_params ( &dp, l, VIK_VIEWPORT(data), highlight );
2399
2400   if ( l->tracks_visible )
2401     g_hash_table_foreach ( l->tracks, (GHFunc) trw_layer_draw_track_cb, &dp );
2402
2403   if ( l->routes_visible )
2404     g_hash_table_foreach ( l->routes, (GHFunc) trw_layer_draw_track_cb, &dp );
2405
2406   if (l->waypoints_visible)
2407     g_hash_table_foreach ( l->waypoints, (GHFunc) trw_layer_draw_waypoint_cb, &dp );
2408 }
2409
2410 static void trw_layer_draw ( VikTrwLayer *l, gpointer data )
2411 {
2412   // If this layer is to be highlighted - then don't draw now - as it will be drawn later on in the specific highlight draw stage
2413   // This may seem slightly inefficient to test each time for every layer
2414   //  but for a layer with *lots* of tracks & waypoints this can save some effort by not drawing the items twice
2415   if ( vik_viewport_get_draw_highlight ( (VikViewport*)data ) &&
2416        vik_window_get_selected_trw_layer ((VikWindow*)VIK_GTK_WINDOW_FROM_LAYER((VikLayer*)l)) == l )
2417     return;
2418   trw_layer_draw_with_highlight ( l, data, FALSE );
2419 }
2420
2421 void vik_trw_layer_draw_highlight ( VikTrwLayer *vtl, VikViewport *vvp )
2422 {
2423   // Check the layer for visibility (including all the parents visibilities)
2424   if ( !vik_treeview_item_get_visible_tree (VIK_LAYER(vtl)->vt, &(VIK_LAYER(vtl)->iter)) )
2425     return;
2426   trw_layer_draw_with_highlight ( vtl, vvp, TRUE );
2427 }
2428
2429 /**
2430  * vik_trw_layer_draw_highlight_item:
2431  *
2432  * Only handles a single track or waypoint ATM
2433  * It assumes the track or waypoint belongs to the TRW Layer (it doesn't check this is the case)
2434  */
2435 void vik_trw_layer_draw_highlight_item ( VikTrwLayer *vtl, VikTrack *trk, VikWaypoint *wpt, VikViewport *vvp )
2436 {
2437   // Check the layer for visibility (including all the parents visibilities)
2438   if ( !vik_treeview_item_get_visible_tree (VIK_LAYER(vtl)->vt, &(VIK_LAYER(vtl)->iter)) )
2439     return;
2440
2441   static struct DrawingParams dp;
2442   init_drawing_params ( &dp, vtl, vvp, TRUE );
2443
2444   if ( trk ) {
2445     gboolean draw = ( trk->is_route && vtl->routes_visible ) || ( !trk->is_route && vtl->tracks_visible );
2446     if ( draw )
2447       trw_layer_draw_track_cb ( NULL, trk, &dp );
2448   }
2449   if ( vtl->waypoints_visible && wpt ) {
2450     trw_layer_draw_waypoint_cb ( NULL, wpt, &dp );
2451   }
2452 }
2453
2454 /**
2455  * vik_trw_layer_draw_highlight_item:
2456  *
2457  * Generally for drawing all tracks or routes or waypoints
2458  * trks may be actually routes
2459  * It assumes they belong to the TRW Layer (it doesn't check this is the case)
2460  */
2461 void vik_trw_layer_draw_highlight_items ( VikTrwLayer *vtl, GHashTable *trks, GHashTable *wpts, VikViewport *vvp )
2462 {
2463   // Check the layer for visibility (including all the parents visibilities)
2464   if ( !vik_treeview_item_get_visible_tree (VIK_LAYER(vtl)->vt, &(VIK_LAYER(vtl)->iter)) )
2465     return;
2466
2467   static struct DrawingParams dp;
2468   init_drawing_params ( &dp, vtl, vvp, TRUE );
2469
2470   if ( trks ) {
2471     gboolean is_routes = (trks == vtl->routes);
2472     gboolean draw = ( is_routes && vtl->routes_visible ) || ( !is_routes && vtl->tracks_visible );
2473     if ( draw )
2474       g_hash_table_foreach ( trks, (GHFunc) trw_layer_draw_track_cb, &dp );
2475   }
2476
2477   if ( vtl->waypoints_visible && wpts )
2478     g_hash_table_foreach ( wpts, (GHFunc) trw_layer_draw_waypoint_cb, &dp );
2479 }
2480
2481 static void trw_layer_free_track_gcs ( VikTrwLayer *vtl )
2482 {
2483   int i;
2484   if ( vtl->track_bg_gc ) 
2485   {
2486     g_object_unref ( vtl->track_bg_gc );
2487     vtl->track_bg_gc = NULL;
2488   }
2489   if ( vtl->track_1color_gc )
2490   {
2491     g_object_unref ( vtl->track_1color_gc );
2492     vtl->track_1color_gc = NULL;
2493   }
2494   if ( vtl->current_track_gc ) 
2495   {
2496     g_object_unref ( vtl->current_track_gc );
2497     vtl->current_track_gc = NULL;
2498   }
2499   if ( vtl->current_track_newpoint_gc )
2500   {
2501     g_object_unref ( vtl->current_track_newpoint_gc );
2502     vtl->current_track_newpoint_gc = NULL;
2503   }
2504
2505   if ( ! vtl->track_gc )
2506     return;
2507   for ( i = vtl->track_gc->len - 1; i >= 0; i-- )
2508     g_object_unref ( g_array_index ( vtl->track_gc, GObject *, i ) );
2509   g_array_free ( vtl->track_gc, TRUE );
2510   vtl->track_gc = NULL;
2511 }
2512
2513 static void trw_layer_new_track_gcs ( VikTrwLayer *vtl, VikViewport *vp )
2514 {
2515   GdkGC *gc[ VIK_TRW_LAYER_TRACK_GC ];
2516   gint width = vtl->line_thickness;
2517
2518   if ( vtl->track_gc )
2519     trw_layer_free_track_gcs ( vtl );
2520
2521   if ( vtl->track_bg_gc )
2522     g_object_unref ( vtl->track_bg_gc );
2523   vtl->track_bg_gc = vik_viewport_new_gc_from_color ( vp, &(vtl->track_bg_color), width + vtl->bg_line_thickness );
2524
2525   // Ensure new track drawing heeds line thickness setting
2526   //  however always have a minium of 2, as 1 pixel is really narrow
2527   gint new_track_width = (vtl->line_thickness < 2) ? 2 : vtl->line_thickness;
2528   
2529   if ( vtl->current_track_gc )
2530     g_object_unref ( vtl->current_track_gc );
2531   vtl->current_track_gc = vik_viewport_new_gc ( vp, "#FF0000", new_track_width );
2532   gdk_gc_set_line_attributes ( vtl->current_track_gc, new_track_width, GDK_LINE_ON_OFF_DASH, GDK_CAP_ROUND, GDK_JOIN_ROUND );
2533
2534   // 'newpoint' gc is exactly the same as the current track gc
2535   if ( vtl->current_track_newpoint_gc )
2536     g_object_unref ( vtl->current_track_newpoint_gc );
2537   vtl->current_track_newpoint_gc = vik_viewport_new_gc ( vp, "#FF0000", new_track_width );
2538   gdk_gc_set_line_attributes ( vtl->current_track_newpoint_gc, new_track_width, GDK_LINE_ON_OFF_DASH, GDK_CAP_ROUND, GDK_JOIN_ROUND );
2539
2540   vtl->track_gc = g_array_sized_new ( FALSE, FALSE, sizeof ( GdkGC * ), VIK_TRW_LAYER_TRACK_GC );
2541
2542   gc[VIK_TRW_LAYER_TRACK_GC_STOP] = vik_viewport_new_gc ( vp, "#874200", width );
2543   gc[VIK_TRW_LAYER_TRACK_GC_BLACK] = vik_viewport_new_gc ( vp, "#000000", width ); // black
2544
2545   gc[VIK_TRW_LAYER_TRACK_GC_SLOW] = vik_viewport_new_gc ( vp, "#E6202E", width ); // red-ish
2546   gc[VIK_TRW_LAYER_TRACK_GC_AVER] = vik_viewport_new_gc ( vp, "#D2CD26", width ); // yellow-ish
2547   gc[VIK_TRW_LAYER_TRACK_GC_FAST] = vik_viewport_new_gc ( vp, "#2B8700", width ); // green-ish
2548
2549   gc[VIK_TRW_LAYER_TRACK_GC_SINGLE] = vik_viewport_new_gc_from_color ( vp, &(vtl->track_color), width );
2550
2551   g_array_append_vals ( vtl->track_gc, gc, VIK_TRW_LAYER_TRACK_GC );
2552 }
2553
2554 static VikTrwLayer* trw_layer_create ( VikViewport *vp )
2555 {
2556   VikTrwLayer *rv = trw_layer_new1 ( vp );
2557   vik_layer_rename ( VIK_LAYER(rv), vik_trw_layer_interface.name );
2558
2559   if ( vp == NULL || gtk_widget_get_window(GTK_WIDGET(vp)) == NULL ) {
2560     /* early exit, as the rest is GUI related */
2561     return rv;
2562   }
2563
2564   rv->wplabellayout = gtk_widget_create_pango_layout (GTK_WIDGET(vp), NULL);
2565   pango_layout_set_font_description (rv->wplabellayout, gtk_widget_get_style(GTK_WIDGET(vp))->font_desc);
2566
2567   rv->tracklabellayout = gtk_widget_create_pango_layout (GTK_WIDGET(vp), NULL);
2568   pango_layout_set_font_description (rv->tracklabellayout, gtk_widget_get_style(GTK_WIDGET(vp))->font_desc);
2569
2570   trw_layer_new_track_gcs ( rv, vp );
2571
2572   rv->waypoint_gc = vik_viewport_new_gc_from_color ( vp, &(rv->waypoint_color), 2 );
2573   rv->waypoint_text_gc = vik_viewport_new_gc_from_color ( vp, &(rv->waypoint_text_color), 1 );
2574   rv->waypoint_bg_gc = vik_viewport_new_gc_from_color ( vp, &(rv->waypoint_bg_color), 1 );
2575   gdk_gc_set_function ( rv->waypoint_bg_gc, rv->wpbgand );
2576
2577   rv->coord_mode = vik_viewport_get_coord_mode ( vp );
2578
2579   rv->menu_selection = vik_layer_get_interface(VIK_LAYER(rv)->type)->menu_items_selection;
2580
2581   return rv;
2582 }
2583
2584 #define SMALL_ICON_SIZE 18
2585 /*
2586  * Can accept a null symbol, and may return null value
2587  */
2588 GdkPixbuf* get_wp_sym_small ( gchar *symbol )
2589 {
2590   GdkPixbuf* wp_icon = a_get_wp_sym (symbol);
2591   // ATM a_get_wp_sym returns a cached icon, with the size dependent on the preferences.
2592   //  So needing a small icon for the treeview may need some resizing:
2593   if ( wp_icon && gdk_pixbuf_get_width ( wp_icon ) != SMALL_ICON_SIZE )
2594     wp_icon = gdk_pixbuf_scale_simple ( wp_icon, SMALL_ICON_SIZE, SMALL_ICON_SIZE, GDK_INTERP_BILINEAR );
2595   return wp_icon;
2596 }
2597
2598 static void trw_layer_realize_track ( gpointer id, VikTrack *track, gpointer pass_along[5] )
2599 {
2600   GtkTreeIter *new_iter = g_malloc(sizeof(GtkTreeIter));
2601
2602   GdkPixbuf *pixbuf = NULL;
2603
2604   if ( track->has_color ) {
2605     pixbuf = gdk_pixbuf_new ( GDK_COLORSPACE_RGB, FALSE, 8, SMALL_ICON_SIZE, SMALL_ICON_SIZE );
2606     // Annoyingly the GdkColor.pixel does not give the correct color when passed to gdk_pixbuf_fill (even when alloc'ed)
2607     // Here is some magic found to do the conversion
2608     // http://www.cs.binghamton.edu/~sgreene/cs360-2011s/topics/gtk+-2.20.1/gtk/gtkcolorbutton.c
2609     guint32 pixel = ((track->color.red & 0xff00) << 16) |
2610       ((track->color.green & 0xff00) << 8) |
2611       (track->color.blue & 0xff00);
2612
2613     gdk_pixbuf_fill ( pixbuf, pixel );
2614   }
2615
2616   vik_treeview_add_sublayer ( (VikTreeview *) pass_along[3], (GtkTreeIter *) pass_along[0], (GtkTreeIter *) pass_along[1], track->name, pass_along[2], id, GPOINTER_TO_INT (pass_along[4]), pixbuf, TRUE, TRUE );
2617
2618   if ( pixbuf )
2619     g_object_unref (pixbuf);
2620
2621   *new_iter = *((GtkTreeIter *) pass_along[1]);
2622   if ( track->is_route )
2623     g_hash_table_insert ( VIK_TRW_LAYER(pass_along[2])->routes_iters, id, new_iter );
2624   else
2625     g_hash_table_insert ( VIK_TRW_LAYER(pass_along[2])->tracks_iters, id, new_iter );
2626
2627   if ( ! track->visible )
2628     vik_treeview_item_set_visible ( (VikTreeview *) pass_along[3], (GtkTreeIter *) pass_along[1], FALSE );
2629 }
2630
2631 static void trw_layer_realize_waypoint ( gpointer id, VikWaypoint *wp, gpointer pass_along[5] )
2632 {
2633   GtkTreeIter *new_iter = g_malloc(sizeof(GtkTreeIter));
2634
2635   vik_treeview_add_sublayer ( (VikTreeview *) pass_along[3], (GtkTreeIter *) pass_along[0], (GtkTreeIter *) pass_along[1], wp->name, pass_along[2], id, GPOINTER_TO_UINT (pass_along[4]), get_wp_sym_small (wp->symbol), TRUE, TRUE );
2636
2637   *new_iter = *((GtkTreeIter *) pass_along[1]);
2638   g_hash_table_insert ( VIK_TRW_LAYER(pass_along[2])->waypoints_iters, id, new_iter );
2639
2640   if ( ! wp->visible )
2641     vik_treeview_item_set_visible ( (VikTreeview *) pass_along[3], (GtkTreeIter *) pass_along[1], FALSE );
2642 }
2643
2644 static void trw_layer_add_sublayer_tracks ( VikTrwLayer *vtl, VikTreeview *vt, GtkTreeIter *layer_iter )
2645 {
2646   vik_treeview_add_sublayer ( (VikTreeview *) vt, layer_iter, &(vtl->tracks_iter), _("Tracks"), vtl, NULL, VIK_TRW_LAYER_SUBLAYER_TRACKS, NULL, TRUE, FALSE );
2647 }
2648
2649 static void trw_layer_add_sublayer_waypoints ( VikTrwLayer *vtl, VikTreeview *vt, GtkTreeIter *layer_iter )
2650 {
2651   vik_treeview_add_sublayer ( (VikTreeview *) vt, layer_iter, &(vtl->waypoints_iter), _("Waypoints"), vtl, NULL, VIK_TRW_LAYER_SUBLAYER_WAYPOINTS, NULL, TRUE, FALSE );
2652 }
2653
2654 static void trw_layer_add_sublayer_routes ( VikTrwLayer *vtl, VikTreeview *vt, GtkTreeIter *layer_iter )
2655 {
2656   vik_treeview_add_sublayer ( (VikTreeview *) vt, layer_iter, &(vtl->routes_iter), _("Routes"), vtl, NULL, VIK_TRW_LAYER_SUBLAYER_ROUTES, NULL, TRUE, FALSE );
2657 }
2658
2659 static void trw_layer_realize ( VikTrwLayer *vtl, VikTreeview *vt, GtkTreeIter *layer_iter )
2660 {
2661   GtkTreeIter iter2;
2662   gpointer pass_along[5] = { &(vtl->tracks_iter), &iter2, vtl, vt, GINT_TO_POINTER(VIK_TRW_LAYER_SUBLAYER_TRACK) };
2663
2664   if ( g_hash_table_size (vtl->tracks) > 0 ) {
2665     trw_layer_add_sublayer_tracks ( vtl, vt , layer_iter );
2666
2667     g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_realize_track, pass_along );
2668
2669     vik_treeview_item_set_visible ( vt, &(vtl->tracks_iter), vtl->tracks_visible );
2670   }
2671
2672   if ( g_hash_table_size (vtl->routes) > 0 ) {
2673     trw_layer_add_sublayer_routes ( vtl, vt, layer_iter );
2674
2675     pass_along[0] = &(vtl->routes_iter);
2676     pass_along[4] = GINT_TO_POINTER(VIK_TRW_LAYER_SUBLAYER_ROUTE);
2677
2678     g_hash_table_foreach ( vtl->routes, (GHFunc) trw_layer_realize_track, pass_along );
2679
2680     vik_treeview_item_set_visible ( (VikTreeview *) vt, &(vtl->routes_iter), vtl->routes_visible );
2681   }
2682
2683   if ( g_hash_table_size (vtl->waypoints) > 0 ) {
2684     trw_layer_add_sublayer_waypoints ( vtl, vt, layer_iter );
2685
2686     pass_along[0] = &(vtl->waypoints_iter);
2687     pass_along[4] = GINT_TO_POINTER(VIK_TRW_LAYER_SUBLAYER_WAYPOINT);
2688
2689     g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_realize_waypoint, pass_along );
2690
2691     vik_treeview_item_set_visible ( (VikTreeview *) vt, &(vtl->waypoints_iter), vtl->waypoints_visible );
2692   }
2693
2694 }
2695
2696 static gboolean trw_layer_sublayer_toggle_visible ( VikTrwLayer *l, gint subtype, gpointer sublayer )
2697 {
2698   switch ( subtype )
2699   {
2700     case VIK_TRW_LAYER_SUBLAYER_TRACKS: return (l->tracks_visible ^= 1);
2701     case VIK_TRW_LAYER_SUBLAYER_WAYPOINTS: return (l->waypoints_visible ^= 1);
2702     case VIK_TRW_LAYER_SUBLAYER_ROUTES: return (l->routes_visible ^= 1);
2703     case VIK_TRW_LAYER_SUBLAYER_TRACK:
2704     {
2705       VikTrack *t = g_hash_table_lookup ( l->tracks, sublayer );
2706       if (t)
2707         return (t->visible ^= 1);
2708       else
2709         return TRUE;
2710     }
2711     case VIK_TRW_LAYER_SUBLAYER_WAYPOINT:
2712     {
2713       VikWaypoint *t = g_hash_table_lookup ( l->waypoints, sublayer );
2714       if (t)
2715         return (t->visible ^= 1);
2716       else
2717         return TRUE;
2718     }
2719     case VIK_TRW_LAYER_SUBLAYER_ROUTE:
2720     {
2721       VikTrack *t = g_hash_table_lookup ( l->routes, sublayer );
2722       if (t)
2723         return (t->visible ^= 1);
2724       else
2725         return TRUE;
2726     }
2727     default: break;
2728   }
2729   return TRUE;
2730 }
2731
2732 /*
2733  * Return a property about tracks for this layer
2734  */
2735 gint vik_trw_layer_get_property_tracks_line_thickness ( VikTrwLayer *vtl )
2736 {
2737   return vtl->line_thickness;
2738 }
2739
2740 // Structure to hold multiple track information for a layer
2741 typedef struct {
2742   gdouble length;
2743   time_t  start_time;
2744   time_t  end_time;
2745   gint    duration;
2746 } tooltip_tracks;
2747
2748 /*
2749  * Build up layer multiple track information via updating the tooltip_tracks structure
2750  */
2751 static void trw_layer_tracks_tooltip ( const gchar *name, VikTrack *tr, tooltip_tracks *tt )
2752 {
2753   tt->length = tt->length + vik_track_get_length (tr);
2754
2755   // Ensure times are available
2756   if ( tr->trackpoints &&
2757        vik_track_get_tp_first(tr)->has_timestamp &&
2758        vik_track_get_tp_last(tr)->has_timestamp ) {
2759
2760     time_t t1, t2;
2761     t1 = vik_track_get_tp_first(tr)->timestamp;
2762     t2 = vik_track_get_tp_last(tr)->timestamp;
2763
2764     // Assume never actually have a track with a time of 0 (1st Jan 1970)
2765     // Hence initialize to the first 'proper' value
2766     if ( tt->start_time == 0 )
2767         tt->start_time = t1;
2768     if ( tt->end_time == 0 )
2769         tt->end_time = t2;
2770
2771     // Update find the earliest / last times
2772     if ( t1 < tt->start_time )
2773         tt->start_time = t1;
2774     if ( t2 > tt->end_time )
2775         tt->end_time = t2;
2776
2777     // Keep track of total time
2778     //  there maybe gaps within a track (eg segments)
2779     //  but this should be generally good enough for a simple indicator
2780     tt->duration = tt->duration + (int)(t2-t1);
2781   }
2782 }
2783
2784 /*
2785  * Generate tooltip text for the layer.
2786  * This is relatively complicated as it considers information for
2787  *   no tracks, a single track or multiple tracks
2788  *     (which may or may not have timing information)
2789  */
2790 static const gchar* trw_layer_layer_tooltip ( VikTrwLayer *vtl )
2791 {
2792   gchar tbuf1[32];
2793   gchar tbuf2[64];
2794   gchar tbuf3[64];
2795   gchar tbuf4[10];
2796   tbuf1[0] = '\0';
2797   tbuf2[0] = '\0';
2798   tbuf3[0] = '\0';
2799   tbuf4[0] = '\0';
2800
2801   static gchar tmp_buf[128];
2802   tmp_buf[0] = '\0';
2803
2804   // For compact date format I'm using '%x'     [The preferred date representation for the current locale without the time.]
2805
2806   // Safety check - I think these should always be valid
2807   if ( vtl->tracks && vtl->waypoints ) {
2808     tooltip_tracks tt = { 0.0, 0, 0, 0 };
2809     g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_tracks_tooltip, &tt );
2810
2811     GDate* gdate_start = g_date_new ();
2812     g_date_set_time_t (gdate_start, tt.start_time);
2813
2814     GDate* gdate_end = g_date_new ();
2815     g_date_set_time_t (gdate_end, tt.end_time);
2816
2817     if ( g_date_compare (gdate_start, gdate_end) ) {
2818       // Dates differ so print range on separate line
2819       g_date_strftime (tbuf1, sizeof(tbuf1), "%x", gdate_start);
2820       g_date_strftime (tbuf2, sizeof(tbuf2), "%x", gdate_end);
2821       g_snprintf (tbuf3, sizeof(tbuf3), "%s to %s\n", tbuf1, tbuf2);
2822     }
2823     else {
2824       // Same date so just show it and keep rest of text on the same line - provided it's a valid time!
2825       if ( tt.start_time != 0 )
2826         g_date_strftime (tbuf3, sizeof(tbuf3), "%x: ", gdate_start);
2827     }
2828
2829     tbuf2[0] = '\0';
2830     if ( tt.length > 0.0 ) {
2831       gdouble len_in_units;
2832
2833       // Setup info dependent on distance units
2834       if ( a_vik_get_units_distance() == VIK_UNITS_DISTANCE_MILES ) {
2835         g_snprintf (tbuf4, sizeof(tbuf4), "miles");
2836         len_in_units = VIK_METERS_TO_MILES(tt.length);
2837       }
2838       else {
2839         g_snprintf (tbuf4, sizeof(tbuf4), "kms");
2840         len_in_units = tt.length/1000.0;
2841       }
2842
2843       // Timing information if available
2844       tbuf1[0] = '\0';
2845       if ( tt.duration > 0 ) {
2846         g_snprintf (tbuf1, sizeof(tbuf1),
2847                     _(" in %d:%02d hrs:mins"),
2848                     (int)round(tt.duration/3600), (int)round((tt.duration/60)%60));
2849       }
2850       g_snprintf (tbuf2, sizeof(tbuf2),
2851                   _("\n%sTotal Length %.1f %s%s"),
2852                   tbuf3, len_in_units, tbuf4, tbuf1);
2853     }
2854
2855     // Put together all the elements to form compact tooltip text
2856     g_snprintf (tmp_buf, sizeof(tmp_buf),
2857                 _("Tracks: %d - Waypoints: %d - Routes: %d%s"),
2858                 g_hash_table_size (vtl->tracks), g_hash_table_size (vtl->waypoints), g_hash_table_size (vtl->routes), tbuf2);
2859
2860     g_date_free (gdate_start);
2861     g_date_free (gdate_end);
2862
2863   }
2864
2865   return tmp_buf;
2866 }
2867
2868 static const gchar* trw_layer_sublayer_tooltip ( VikTrwLayer *l, gint subtype, gpointer sublayer )
2869 {
2870   switch ( subtype )
2871   {
2872     case VIK_TRW_LAYER_SUBLAYER_TRACKS:
2873     {
2874       // Very simple tooltip - may expand detail in the future...
2875       static gchar tmp_buf[32];
2876       g_snprintf (tmp_buf, sizeof(tmp_buf),
2877                   _("Tracks: %d"),
2878                   g_hash_table_size (l->tracks));
2879       return tmp_buf;
2880     }
2881     break;
2882     case VIK_TRW_LAYER_SUBLAYER_ROUTES:
2883     {
2884       // Very simple tooltip - may expand detail in the future...
2885       static gchar tmp_buf[32];
2886       g_snprintf (tmp_buf, sizeof(tmp_buf),
2887                   _("Routes: %d"),
2888                   g_hash_table_size (l->routes));
2889       return tmp_buf;
2890     }
2891     break;
2892
2893     case VIK_TRW_LAYER_SUBLAYER_ROUTE:
2894       // Same tooltip for a route
2895     case VIK_TRW_LAYER_SUBLAYER_TRACK:
2896     {
2897       VikTrack *tr;
2898       if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
2899         tr = g_hash_table_lookup ( l->tracks, sublayer );
2900       else
2901         tr = g_hash_table_lookup ( l->routes, sublayer );
2902
2903       if ( tr ) {
2904         // Could be a better way of handling strings - but this works...
2905         gchar time_buf1[20];
2906         gchar time_buf2[20];
2907         time_buf1[0] = '\0';
2908         time_buf2[0] = '\0';
2909         static gchar tmp_buf[100];
2910         // Compact info: Short date eg (11/20/99), duration and length
2911         // Hopefully these are the things that are most useful and so promoted into the tooltip
2912         if ( tr->trackpoints && vik_track_get_tp_first(tr)->has_timestamp ) {
2913           // %x     The preferred date representation for the current locale without the time.
2914           strftime (time_buf1, sizeof(time_buf1), "%x: ", gmtime(&(vik_track_get_tp_first(tr)->timestamp)));
2915           if ( vik_track_get_tp_last(tr)->has_timestamp ) {
2916             gint dur = ( (vik_track_get_tp_last(tr)->timestamp) - (vik_track_get_tp_first(tr)->timestamp) );
2917             if ( dur > 0 )
2918               g_snprintf ( time_buf2, sizeof(time_buf2), _("- %d:%02d hrs:mins"), (int)round(dur/3600), (int)round((dur/60)%60) );
2919           }
2920         }
2921         // Get length and consider the appropriate distance units
2922         gdouble tr_len = vik_track_get_length(tr);
2923         vik_units_distance_t dist_units = a_vik_get_units_distance ();
2924         switch (dist_units) {
2925         case VIK_UNITS_DISTANCE_KILOMETRES:
2926           g_snprintf (tmp_buf, sizeof(tmp_buf), _("%s%.1f km %s"), time_buf1, tr_len/1000.0, time_buf2);
2927           break;
2928         case VIK_UNITS_DISTANCE_MILES:
2929           g_snprintf (tmp_buf, sizeof(tmp_buf), _("%s%.1f miles %s"), time_buf1, VIK_METERS_TO_MILES(tr_len), time_buf2);
2930           break;
2931         default:
2932           break;
2933         }
2934         return tmp_buf;
2935       }
2936     }
2937     break;
2938     case VIK_TRW_LAYER_SUBLAYER_WAYPOINTS:
2939     {
2940       // Very simple tooltip - may expand detail in the future...
2941       static gchar tmp_buf[32];
2942       g_snprintf (tmp_buf, sizeof(tmp_buf),
2943                   _("Waypoints: %d"),
2944                   g_hash_table_size (l->waypoints));
2945       return tmp_buf;
2946     }
2947     break;
2948     case VIK_TRW_LAYER_SUBLAYER_WAYPOINT:
2949     {
2950       VikWaypoint *w = g_hash_table_lookup ( l->waypoints, sublayer );
2951       // NB It's OK to return NULL
2952       if ( w ) {
2953         if ( w->comment )
2954           return w->comment;
2955         else
2956           return w->description;
2957       }
2958     }
2959     break;
2960     default: break;
2961   }
2962   return NULL;
2963 }
2964
2965 #define VIK_SETTINGS_TRKPT_SELECTED_STATUSBAR_FORMAT "trkpt_selected_statusbar_format"
2966
2967 /**
2968  * set_statusbar_msg_info_trkpt:
2969  *
2970  * Function to show track point information on the statusbar
2971  *  Items displayed is controlled by the settings format code
2972  */
2973 static void set_statusbar_msg_info_trkpt ( VikTrwLayer *vtl, VikTrackpoint *trkpt )
2974 {
2975   gchar *statusbar_format_code = NULL;
2976   gboolean need2free = FALSE;
2977   if ( !a_settings_get_string ( VIK_SETTINGS_TRKPT_SELECTED_STATUSBAR_FORMAT, &statusbar_format_code ) ) {
2978     // Otherwise use default
2979     statusbar_format_code = g_strdup ( "KEATDN" );
2980     need2free = TRUE;
2981   }
2982
2983   gchar *msg = vu_trackpoint_formatted_message ( statusbar_format_code, trkpt, NULL, vtl->current_tp_track );
2984   vik_statusbar_set_message ( vik_window_get_statusbar (VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl))), VIK_STATUSBAR_INFO, msg );
2985   g_free ( msg );
2986
2987   if ( need2free )
2988     g_free ( statusbar_format_code );
2989 }
2990
2991 /*
2992  * Function to show basic waypoint information on the statusbar
2993  */
2994 static void set_statusbar_msg_info_wpt ( VikTrwLayer *vtl, VikWaypoint *wpt )
2995 {
2996   gchar tmp_buf1[64];
2997   switch (a_vik_get_units_height ()) {
2998   case VIK_UNITS_HEIGHT_FEET:
2999     g_snprintf(tmp_buf1, sizeof(tmp_buf1), _("Wpt: Alt %dft"), (int)round(VIK_METERS_TO_FEET(wpt->altitude)));
3000     break;
3001   default:
3002     //VIK_UNITS_HEIGHT_METRES:
3003     g_snprintf(tmp_buf1, sizeof(tmp_buf1), _("Wpt: Alt %dm"), (int)round(wpt->altitude));
3004   }
3005   
3006   // Position part
3007   // Position is put last, as this bit is most likely not to be seen if the display is not big enough,
3008   //   one can easily use the current pointer position to see this if needed
3009   gchar *lat = NULL, *lon = NULL;
3010   static struct LatLon ll;
3011   vik_coord_to_latlon (&(wpt->coord), &ll);
3012   a_coords_latlon_to_string ( &ll, &lat, &lon );
3013
3014   // Combine parts to make overall message
3015   gchar *msg;
3016   if ( wpt->comment )
3017     // Add comment if available
3018     msg = g_strdup_printf ( _("%s | %s %s | Comment: %s"), tmp_buf1, lat, lon, wpt->comment );
3019   else
3020     msg = g_strdup_printf ( _("%s | %s %s"), tmp_buf1, lat, lon );
3021   vik_statusbar_set_message ( vik_window_get_statusbar (VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl))), VIK_STATUSBAR_INFO, msg );
3022   g_free ( lat );
3023   g_free ( lon );
3024   g_free ( msg );
3025 }
3026
3027 /**
3028  * General layer selection function, find out which bit is selected and take appropriate action
3029  */
3030 static gboolean trw_layer_selected ( VikTrwLayer *l, gint subtype, gpointer sublayer, gint type, gpointer vlp )
3031 {
3032   // Reset
3033   l->current_wp    = NULL;
3034   l->current_wp_id = NULL;
3035   trw_layer_cancel_current_tp ( l, FALSE );
3036
3037   // Clear statusbar
3038   vik_statusbar_set_message ( vik_window_get_statusbar (VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(l))), VIK_STATUSBAR_INFO, "" );
3039
3040   switch ( type )
3041     {
3042     case VIK_TREEVIEW_TYPE_LAYER:
3043       {
3044         vik_window_set_selected_trw_layer ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l), l );
3045         /* Mark for redraw */
3046         return TRUE;
3047       }
3048       break;
3049
3050     case VIK_TREEVIEW_TYPE_SUBLAYER:
3051       {
3052         switch ( subtype )
3053           {
3054           case VIK_TRW_LAYER_SUBLAYER_TRACKS:
3055             {
3056               vik_window_set_selected_tracks ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l), l->tracks, l );
3057               /* Mark for redraw */
3058               return TRUE;
3059             }
3060             break;
3061           case VIK_TRW_LAYER_SUBLAYER_TRACK:
3062             {
3063               VikTrack *track = g_hash_table_lookup ( l->tracks, sublayer );
3064               vik_window_set_selected_track ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l), (gpointer)track, l );
3065               /* Mark for redraw */
3066               return TRUE;
3067             }
3068             break;
3069           case VIK_TRW_LAYER_SUBLAYER_ROUTES:
3070             {
3071               vik_window_set_selected_tracks ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l), l->routes, l );
3072               /* Mark for redraw */
3073               return TRUE;
3074             }
3075             break;
3076           case VIK_TRW_LAYER_SUBLAYER_ROUTE:
3077             {
3078               VikTrack *track = g_hash_table_lookup ( l->routes, sublayer );
3079               vik_window_set_selected_track ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l), (gpointer)track, l );
3080               /* Mark for redraw */
3081               return TRUE;
3082             }
3083             break;
3084           case VIK_TRW_LAYER_SUBLAYER_WAYPOINTS:
3085             {
3086               vik_window_set_selected_waypoints ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l), l->waypoints, l );
3087               /* Mark for redraw */
3088               return TRUE;
3089             }
3090             break;
3091           case VIK_TRW_LAYER_SUBLAYER_WAYPOINT:
3092             {
3093               VikWaypoint *wpt = g_hash_table_lookup ( l->waypoints, sublayer );
3094               if ( wpt ) {
3095                 vik_window_set_selected_waypoint ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l), (gpointer)wpt, l );
3096                 // Show some waypoint info
3097                 set_statusbar_msg_info_wpt ( l, wpt );
3098                 /* Mark for redraw */
3099                 return TRUE;
3100               }
3101             }
3102             break;
3103           default:
3104             {
3105               return vik_window_clear_highlight ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l) );
3106             }
3107             break;
3108           }
3109         return FALSE;
3110       }
3111       break;
3112
3113     default:
3114       return vik_window_clear_highlight ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l) );
3115       break;
3116     }
3117 }
3118
3119 GHashTable *vik_trw_layer_get_tracks ( VikTrwLayer *l )
3120 {
3121   return l->tracks;
3122 }
3123
3124 GHashTable *vik_trw_layer_get_routes ( VikTrwLayer *l )
3125 {
3126   return l->routes;
3127 }
3128
3129 GHashTable *vik_trw_layer_get_waypoints ( VikTrwLayer *l )
3130 {
3131   return l->waypoints;
3132 }
3133
3134 GHashTable *vik_trw_layer_get_tracks_iters ( VikTrwLayer *vtl )
3135 {
3136   return vtl->tracks_iters;
3137 }
3138
3139 GHashTable *vik_trw_layer_get_routes_iters ( VikTrwLayer *vtl )
3140 {
3141   return vtl->routes_iters;
3142 }
3143
3144 GHashTable *vik_trw_layer_get_waypoints_iters ( VikTrwLayer *vtl )
3145 {
3146   return vtl->waypoints;
3147 }
3148
3149 gboolean vik_trw_layer_is_empty ( VikTrwLayer *vtl )
3150 {
3151   return ! ( g_hash_table_size ( vtl->tracks ) ||
3152              g_hash_table_size ( vtl->routes ) ||
3153              g_hash_table_size ( vtl->waypoints ) );
3154 }
3155
3156 gboolean vik_trw_layer_get_tracks_visibility ( VikTrwLayer *vtl )
3157 {
3158   return vtl->tracks_visible;
3159 }
3160
3161 gboolean vik_trw_layer_get_routes_visibility ( VikTrwLayer *vtl )
3162 {
3163   return vtl->routes_visible;
3164 }
3165
3166 gboolean vik_trw_layer_get_waypoints_visibility ( VikTrwLayer *vtl )
3167 {
3168   return vtl->waypoints_visible;
3169 }
3170
3171 /*
3172  * ATM use a case sensitive find
3173  * Finds the first one
3174  */
3175 static gboolean trw_layer_waypoint_find ( const gpointer id, const VikWaypoint *wp, const gchar *name )
3176 {
3177   if ( wp && wp->name )
3178     if ( ! strcmp ( wp->name, name ) )
3179       return TRUE;
3180   return FALSE;
3181 }
3182
3183 /*
3184  * Get waypoint by name - not guaranteed to be unique
3185  * Finds the first one
3186  */
3187 VikWaypoint *vik_trw_layer_get_waypoint ( VikTrwLayer *vtl, const gchar *name )
3188 {
3189   return g_hash_table_find ( vtl->waypoints, (GHRFunc) trw_layer_waypoint_find, (gpointer) name );
3190 }
3191
3192 /*
3193  * ATM use a case sensitive find
3194  * Finds the first one
3195  */
3196 static gboolean trw_layer_track_find ( const gpointer id, const VikTrack *trk, const gchar *name )
3197 {
3198   if ( trk && trk->name )
3199     if ( ! strcmp ( trk->name, name ) )
3200       return TRUE;
3201   return FALSE;
3202 }
3203
3204 /*
3205  * Get track by name - not guaranteed to be unique
3206  * Finds the first one
3207  */
3208 VikTrack *vik_trw_layer_get_track ( VikTrwLayer *vtl, const gchar *name )
3209 {
3210   return g_hash_table_find ( vtl->tracks, (GHRFunc) trw_layer_track_find, (gpointer) name );
3211 }
3212
3213 /*
3214  * Get route by name - not guaranteed to be unique
3215  * Finds the first one
3216  */
3217 VikTrack *vik_trw_layer_get_route ( VikTrwLayer *vtl, const gchar *name )
3218 {
3219   return g_hash_table_find ( vtl->routes, (GHRFunc) trw_layer_track_find, (gpointer) name );
3220 }
3221
3222 static void trw_layer_find_maxmin_tracks ( const gpointer id, const VikTrack *trk, struct LatLon maxmin[2] )
3223 {
3224   if ( trk->bbox.north > maxmin[0].lat || maxmin[0].lat == 0.0 )
3225     maxmin[0].lat = trk->bbox.north;
3226   if ( trk->bbox.south < maxmin[1].lat || maxmin[1].lat == 0.0 )
3227     maxmin[1].lat = trk->bbox.south;
3228   if ( trk->bbox.east > maxmin[0].lon || maxmin[0].lon == 0.0 )
3229     maxmin[0].lon = trk->bbox.east;
3230   if ( trk->bbox.west < maxmin[1].lon || maxmin[1].lon == 0.0 )
3231     maxmin[1].lon = trk->bbox.west;
3232 }
3233
3234 static void trw_layer_find_maxmin (VikTrwLayer *vtl, struct LatLon maxmin[2])
3235 {
3236   // Continually reuse maxmin to find the latest maximum and minimum values
3237   // First set to waypoints bounds
3238   maxmin[0].lat = vtl->waypoints_bbox.north;
3239   maxmin[1].lat = vtl->waypoints_bbox.south;
3240   maxmin[0].lon = vtl->waypoints_bbox.east;
3241   maxmin[1].lon = vtl->waypoints_bbox.west;
3242   g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_find_maxmin_tracks, maxmin );
3243   g_hash_table_foreach ( vtl->routes, (GHFunc) trw_layer_find_maxmin_tracks, maxmin );
3244 }
3245
3246 gboolean vik_trw_layer_find_center ( VikTrwLayer *vtl, VikCoord *dest )
3247 {
3248   /* TODO: what if there's only one waypoint @ 0,0, it will think nothing found. like I don't have more important things to worry about... */
3249   struct LatLon maxmin[2] = { {0.0,0.0}, {0.0,0.0} };
3250   trw_layer_find_maxmin (vtl, maxmin);
3251   if (maxmin[0].lat == 0.0 && maxmin[0].lon == 0.0 && maxmin[1].lat == 0.0 && maxmin[1].lon == 0.0)
3252     return FALSE;
3253   else
3254   {
3255     struct LatLon average = { (maxmin[0].lat+maxmin[1].lat)/2, (maxmin[0].lon+maxmin[1].lon)/2 };
3256     vik_coord_load_from_latlon ( dest, vtl->coord_mode, &average );
3257     return TRUE;
3258   }
3259 }
3260
3261 static void trw_layer_centerize ( menu_array_layer values )
3262 {
3263   VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
3264   VikCoord coord;
3265   if ( vik_trw_layer_find_center ( vtl, &coord ) )
3266     goto_coord ( values[MA_VLP], NULL, NULL, &coord );
3267   else
3268     a_dialog_info_msg ( VIK_GTK_WINDOW_FROM_LAYER(vtl), _("This layer has no waypoints or trackpoints.") );
3269 }
3270
3271 void trw_layer_zoom_to_show_latlons ( VikTrwLayer *vtl, VikViewport *vvp, struct LatLon maxmin[2] )
3272 {
3273   /* First set the center [in case previously viewing from elsewhere] */
3274   /* Then loop through zoom levels until provided positions are in view */
3275   /* This method is not particularly fast - but should work well enough */
3276   struct LatLon average = { (maxmin[0].lat+maxmin[1].lat)/2, (maxmin[0].lon+maxmin[1].lon)/2 };
3277   VikCoord coord;
3278   vik_coord_load_from_latlon ( &coord, vtl->coord_mode, &average );
3279   vik_viewport_set_center_coord ( vvp, &coord, TRUE );
3280
3281   /* Convert into definite 'smallest' and 'largest' positions */
3282   struct LatLon minmin;
3283   if ( maxmin[0].lat < maxmin[1].lat )
3284     minmin.lat = maxmin[0].lat;
3285   else
3286     minmin.lat = maxmin[1].lat;
3287
3288   struct LatLon maxmax;
3289   if ( maxmin[0].lon > maxmin[1].lon )
3290     maxmax.lon = maxmin[0].lon;
3291   else
3292     maxmax.lon = maxmin[1].lon;
3293
3294   /* Never zoom in too far - generally not that useful, as too close ! */
3295   /* Always recalculate the 'best' zoom level */
3296   gdouble zoom = 1.0;
3297   vik_viewport_set_zoom ( vvp, zoom );
3298
3299   gdouble min_lat, max_lat, min_lon, max_lon;
3300   /* Should only be a maximum of about 18 iterations from min to max zoom levels */
3301   while ( zoom <= VIK_VIEWPORT_MAX_ZOOM ) {
3302     vik_viewport_get_min_max_lat_lon ( vvp, &min_lat, &max_lat, &min_lon, &max_lon );
3303     /* NB I think the logic used in this test to determine if the bounds is within view
3304        fails if track goes across 180 degrees longitude.
3305        Hopefully that situation is not too common...
3306        Mind you viking doesn't really do edge locations to well anyway */
3307     if ( min_lat < minmin.lat &&
3308          max_lat > minmin.lat &&
3309          min_lon < maxmax.lon &&
3310          max_lon > maxmax.lon )
3311       /* Found within zoom level */
3312       break;
3313
3314     /* Try next */
3315     zoom = zoom * 2;
3316     vik_viewport_set_zoom ( vvp, zoom );
3317   }
3318 }
3319
3320 gboolean vik_trw_layer_auto_set_view ( VikTrwLayer *vtl, VikViewport *vvp )
3321 {
3322   /* TODO: what if there's only one waypoint @ 0,0, it will think nothing found. */
3323   struct LatLon maxmin[2] = { {0.0,0.0}, {0.0,0.0} };
3324   trw_layer_find_maxmin (vtl, maxmin);
3325   if (maxmin[0].lat == 0.0 && maxmin[0].lon == 0.0 && maxmin[1].lat == 0.0 && maxmin[1].lon == 0.0)
3326     return FALSE;
3327   else {
3328     trw_layer_zoom_to_show_latlons ( vtl, vvp, maxmin );
3329     return TRUE;
3330   }
3331 }
3332
3333 static void trw_layer_auto_view ( menu_array_layer values )
3334 {
3335   VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
3336   VikLayersPanel *vlp = VIK_LAYERS_PANEL(values[MA_VLP]);
3337   if ( vik_trw_layer_auto_set_view ( vtl, vik_layers_panel_get_viewport (vlp) ) ) {
3338     vik_layers_panel_emit_update ( vlp );
3339   }
3340   else
3341     a_dialog_info_msg ( VIK_GTK_WINDOW_FROM_LAYER(vtl), _("This layer has no waypoints or trackpoints.") );
3342 }
3343
3344 static void trw_layer_export_gpspoint ( menu_array_layer values )
3345 {
3346   gchar *auto_save_name = append_file_ext ( vik_layer_get_name(VIK_LAYER(values[MA_VTL])), FILE_TYPE_GPSPOINT );
3347
3348   vik_trw_layer_export ( VIK_TRW_LAYER (values[MA_VTL]), _("Export Layer"), auto_save_name, NULL, FILE_TYPE_GPSPOINT );
3349
3350   g_free ( auto_save_name );
3351 }
3352
3353 static void trw_layer_export_gpsmapper ( menu_array_layer values )
3354 {
3355   gchar *auto_save_name = append_file_ext ( vik_layer_get_name(VIK_LAYER(values[MA_VTL])), FILE_TYPE_GPSMAPPER );
3356
3357   vik_trw_layer_export ( VIK_TRW_LAYER (values[MA_VTL]), _("Export Layer"), auto_save_name, NULL, FILE_TYPE_GPSMAPPER );
3358
3359   g_free ( auto_save_name );
3360 }
3361
3362 static void trw_layer_export_gpx ( menu_array_layer values )
3363 {
3364   gchar *auto_save_name = append_file_ext ( vik_layer_get_name(VIK_LAYER(values[MA_VTL])), FILE_TYPE_GPX );
3365
3366   vik_trw_layer_export ( VIK_TRW_LAYER (values[MA_VTL]), _("Export Layer"), auto_save_name, NULL, FILE_TYPE_GPX );
3367
3368   g_free ( auto_save_name );
3369 }
3370
3371 static void trw_layer_export_kml ( menu_array_layer values )
3372 {
3373   gchar *auto_save_name = append_file_ext ( vik_layer_get_name(VIK_LAYER(values[MA_VTL])), FILE_TYPE_KML );
3374
3375   vik_trw_layer_export ( VIK_TRW_LAYER (values[MA_VTL]), _("Export Layer"), auto_save_name, NULL, FILE_TYPE_KML );
3376
3377   g_free ( auto_save_name );
3378 }
3379
3380 static void trw_layer_export_babel ( gpointer layer_and_vlp[2] )
3381 {
3382   const gchar *auto_save_name = vik_layer_get_name(VIK_LAYER(layer_and_vlp[0]));
3383   vik_trw_layer_export_gpsbabel ( VIK_TRW_LAYER (layer_and_vlp[0]), _("Export Layer"), auto_save_name );
3384 }
3385
3386 static void trw_layer_export_external_gpx_1 ( menu_array_layer values )
3387 {
3388   vik_trw_layer_export_external_gpx ( VIK_TRW_LAYER (values[MA_VTL]), a_vik_get_external_gpx_program_1() );
3389 }
3390
3391 static void trw_layer_export_external_gpx_2 ( menu_array_layer values )
3392 {
3393   vik_trw_layer_export_external_gpx ( VIK_TRW_LAYER (values[MA_VTL]), a_vik_get_external_gpx_program_2() );
3394 }
3395
3396 static void trw_layer_export_gpx_track ( menu_array_sublayer values )
3397 {
3398   VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
3399   VikTrack *trk;
3400   if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
3401     trk = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
3402   else
3403     trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
3404
3405   if ( !trk || !trk->name )
3406     return;
3407
3408   gchar *auto_save_name = append_file_ext ( trk->name, FILE_TYPE_GPX );
3409
3410   gchar *label = NULL;
3411   if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
3412     label = _("Export Route as GPX");
3413   else
3414     label = _("Export Track as GPX");
3415   vik_trw_layer_export ( VIK_TRW_LAYER (values[MA_VTL]), label, auto_save_name, trk, FILE_TYPE_GPX );
3416
3417   g_free ( auto_save_name );
3418 }
3419
3420 gboolean trw_layer_waypoint_find_uuid ( const gpointer id, const VikWaypoint *wp, gpointer udata )
3421 {
3422   wpu_udata *user_data = udata;
3423   if ( wp == user_data->wp ) {
3424     user_data->uuid = id;
3425     return TRUE;
3426   }
3427   return FALSE;
3428 }
3429
3430 static void trw_layer_goto_wp ( menu_array_layer values )
3431 {
3432   VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
3433   VikLayersPanel *vlp = VIK_LAYERS_PANEL(values[MA_VLP]);
3434   GtkWidget *dia = gtk_dialog_new_with_buttons (_("Find"),
3435                                                  VIK_GTK_WINDOW_FROM_LAYER(vtl),
3436                                                  GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
3437                                                  GTK_STOCK_CANCEL,
3438                                                  GTK_RESPONSE_REJECT,
3439                                                  GTK_STOCK_OK,
3440                                                  GTK_RESPONSE_ACCEPT,
3441                                                  NULL);
3442
3443   GtkWidget *label, *entry;
3444   label = gtk_label_new(_("Waypoint Name:"));
3445   entry = gtk_entry_new();
3446
3447   gtk_box_pack_start(GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dia))), label, FALSE, FALSE, 0);
3448   gtk_box_pack_start(GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dia))), entry, FALSE, FALSE, 0);
3449   gtk_widget_show_all ( label );
3450   gtk_widget_show_all ( entry );
3451
3452   gtk_dialog_set_default_response ( GTK_DIALOG(dia), GTK_RESPONSE_ACCEPT );
3453
3454   while ( gtk_dialog_run ( GTK_DIALOG(dia) ) == GTK_RESPONSE_ACCEPT )
3455   {
3456     gchar *name = g_strdup(gtk_entry_get_text(GTK_ENTRY(entry)));
3457     // Find *first* wp with the given name
3458     VikWaypoint *wp = vik_trw_layer_get_waypoint ( vtl, name );
3459
3460     if ( !wp )
3461       a_dialog_error_msg ( VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Waypoint not found in this layer.") );
3462     else
3463     {
3464       vik_viewport_set_center_coord ( vik_layers_panel_get_viewport(vlp), &(wp->coord), TRUE );
3465       vik_layers_panel_emit_update ( vlp );
3466
3467       // Find and select on the side panel
3468       wpu_udata udata;
3469       udata.wp   = wp;
3470       udata.uuid = NULL;
3471
3472       // Hmmm, want key of it
3473       gpointer *wpf = g_hash_table_find ( vtl->waypoints, (GHRFunc) trw_layer_waypoint_find_uuid, (gpointer) &udata );
3474
3475       if ( wpf && udata.uuid ) {
3476         GtkTreeIter *it = g_hash_table_lookup ( vtl->waypoints_iters, udata.uuid );
3477         vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, it, TRUE );
3478       }
3479
3480       break;
3481     }
3482
3483     g_free ( name );
3484
3485   }
3486   gtk_widget_destroy ( dia );
3487 }
3488
3489 gboolean vik_trw_layer_new_waypoint ( VikTrwLayer *vtl, GtkWindow *w, const VikCoord *def_coord )
3490 {
3491   gchar *default_name = highest_wp_number_get(vtl);
3492   VikWaypoint *wp = vik_waypoint_new();
3493   gchar *returned_name;
3494   gboolean updated;
3495   wp->coord = *def_coord;
3496   
3497   // Attempt to auto set height if DEM data is available
3498   vik_waypoint_apply_dem_data ( wp, TRUE );
3499
3500   returned_name = a_dialog_waypoint ( w, default_name, vtl, wp, vtl->coord_mode, TRUE, &updated );
3501
3502   if ( returned_name )
3503   {
3504     wp->visible = TRUE;
3505     vik_trw_layer_add_waypoint ( vtl, returned_name, wp );
3506     g_free (default_name);
3507     g_free (returned_name);
3508     return TRUE;
3509   }
3510   g_free (default_name);
3511   vik_waypoint_free(wp);
3512   return FALSE;
3513 }
3514
3515 static void trw_layer_new_wikipedia_wp_viewport ( menu_array_layer values )
3516 {
3517   struct LatLon maxmin[2] = { {0.0,0.0}, {0.0,0.0} };
3518   VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
3519   VikLayersPanel *vlp = VIK_LAYERS_PANEL(values[MA_VLP]);
3520   VikWindow *vw = (VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl));
3521   VikViewport *vvp =  vik_window_viewport(vw);
3522
3523   // Note the order is max part first then min part - thus reverse order of use in min_max function:
3524   vik_viewport_get_min_max_lat_lon ( vvp, &maxmin[1].lat, &maxmin[0].lat, &maxmin[1].lon, &maxmin[0].lon );
3525   a_geonames_wikipedia_box((VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl)), vtl, maxmin);
3526   trw_layer_calculate_bounds_waypoints ( vtl );
3527   vik_layers_panel_emit_update ( vlp );
3528 }
3529
3530 static void trw_layer_new_wikipedia_wp_layer ( menu_array_layer values )
3531 {
3532   VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
3533   VikLayersPanel *vlp = VIK_LAYERS_PANEL(values[MA_VLP]);
3534   struct LatLon maxmin[2] = { {0.0,0.0}, {0.0,0.0} };
3535   
3536   trw_layer_find_maxmin (vtl, maxmin);
3537   a_geonames_wikipedia_box((VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl)), vtl, maxmin);
3538   trw_layer_calculate_bounds_waypoints ( vtl );
3539   vik_layers_panel_emit_update ( vlp );
3540 }
3541
3542 #ifdef VIK_CONFIG_GEOTAG
3543 static void trw_layer_geotagging_waypoint_mtime_keep ( menu_array_sublayer values )
3544 {
3545   VikWaypoint *wp = g_hash_table_lookup ( VIK_TRW_LAYER(values[MA_VTL])->waypoints, values[MA_SUBLAYER_ID] );
3546   if ( wp )
3547     // Update directly - not changing the mtime
3548     a_geotag_write_exif_gps ( wp->image, wp->coord, wp->altitude, TRUE );
3549 }
3550
3551 static void trw_layer_geotagging_waypoint_mtime_update ( menu_array_sublayer values )
3552 {
3553   VikWaypoint *wp = g_hash_table_lookup ( VIK_TRW_LAYER(values[MA_VTL])->waypoints, values[MA_SUBLAYER_ID] );
3554   if ( wp )
3555     // Update directly
3556     a_geotag_write_exif_gps ( wp->image, wp->coord, wp->altitude, FALSE );
3557 }
3558
3559 /*
3560  * Use code in separate file for this feature as reasonably complex
3561  */
3562 static void trw_layer_geotagging_track ( menu_array_sublayer values )
3563 {
3564   VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
3565   VikTrack *track = g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
3566   // Unset so can be reverified later if necessary
3567   vtl->has_verified_thumbnails = FALSE;
3568
3569   trw_layer_geotag_dialog ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
3570                             vtl,
3571                             NULL,
3572                             track );
3573 }
3574
3575 static void trw_layer_geotagging_waypoint ( menu_array_sublayer values )
3576 {
3577   VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
3578   VikWaypoint *wpt = g_hash_table_lookup ( vtl->waypoints, values[MA_SUBLAYER_ID] );
3579
3580   trw_layer_geotag_dialog ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
3581                             vtl,
3582                             wpt,
3583                             NULL );
3584 }
3585
3586 static void trw_layer_geotagging ( menu_array_layer values )
3587 {
3588   VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
3589   // Unset so can be reverified later if necessary
3590   vtl->has_verified_thumbnails = FALSE;
3591
3592   trw_layer_geotag_dialog ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
3593                             vtl,
3594                             NULL,
3595                             NULL );
3596 }
3597 #endif
3598
3599 // 'Acquires' - Same as in File Menu -> Acquire - applies into the selected TRW Layer //
3600
3601 static void trw_layer_acquire ( menu_array_layer values, VikDataSourceInterface *datasource )
3602 {
3603   VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
3604   VikLayersPanel *vlp = VIK_LAYERS_PANEL(values[MA_VLP]);
3605   VikWindow *vw = (VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl));
3606   VikViewport *vvp =  vik_window_viewport(vw);
3607
3608   a_acquire ( vw, vlp, vvp, datasource, NULL, NULL );
3609 }
3610
3611 /*
3612  * Acquire into this TRW Layer straight from GPS Device
3613  */
3614 static void trw_layer_acquire_gps_cb ( menu_array_layer values )
3615 {
3616   vik_datasource_gps_interface.mode = VIK_DATASOURCE_ADDTOLAYER;
3617   trw_layer_acquire ( values, &vik_datasource_gps_interface );
3618 }
3619
3620 /*
3621  * Acquire into this TRW Layer from Directions
3622  */
3623 static void trw_layer_acquire_routing_cb ( menu_array_layer values )
3624 {
3625   trw_layer_acquire ( values, &vik_datasource_routing_interface );
3626 }
3627
3628 /*
3629  * Acquire into this TRW Layer from an entered URL
3630  */
3631 static void trw_layer_acquire_url_cb ( menu_array_layer values )
3632 {
3633   vik_datasource_url_interface.mode = VIK_DATASOURCE_ADDTOLAYER;
3634   trw_layer_acquire ( values, &vik_datasource_url_interface );
3635 }
3636
3637 #ifdef VIK_CONFIG_OPENSTREETMAP
3638 /*
3639  * Acquire into this TRW Layer from OSM
3640  */
3641 static void trw_layer_acquire_osm_cb ( menu_array_layer values )
3642 {
3643   trw_layer_acquire ( values, &vik_datasource_osm_interface );
3644 }
3645
3646 /**
3647  * Acquire into this TRW Layer from OSM for 'My' Traces
3648  */
3649 static void trw_layer_acquire_osm_my_traces_cb ( menu_array_layer values )
3650 {
3651   trw_layer_acquire ( values, &vik_datasource_osm_my_traces_interface );
3652 }
3653 #endif
3654
3655 #ifdef VIK_CONFIG_GEOCACHES
3656 /*
3657  * Acquire into this TRW Layer from Geocaching.com
3658  */
3659 static void trw_layer_acquire_geocache_cb ( menu_array_layer values )
3660 {
3661   trw_layer_acquire ( values, &vik_datasource_gc_interface );
3662 }
3663 #endif
3664
3665 #ifdef VIK_CONFIG_GEOTAG
3666 /*
3667  * Acquire into this TRW Layer from images
3668  */
3669 static void trw_layer_acquire_geotagged_cb ( menu_array_layer values )
3670 {
3671   VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
3672
3673   vik_datasource_geotag_interface.mode = VIK_DATASOURCE_ADDTOLAYER;
3674   trw_layer_acquire ( values, &vik_datasource_geotag_interface );
3675
3676   // Reverify thumbnails as they may have changed
3677   vtl->has_verified_thumbnails = FALSE;
3678   trw_layer_verify_thumbnails ( vtl, NULL );
3679 }
3680 #endif
3681
3682 static void trw_layer_gps_upload ( menu_array_layer values )
3683 {
3684   menu_array_sublayer data;
3685   gint ii;
3686   for ( ii = MA_VTL; ii < MA_LAST; ii++ )
3687     data[ii] = NULL;
3688   data[MA_VTL] = values[MA_VTL];
3689   data[MA_VLP] = values[MA_VLP];
3690
3691   trw_layer_gps_upload_any ( data );
3692 }
3693
3694 /**
3695  * If pass_along[3] is defined that this will upload just that track
3696  */
3697 static void trw_layer_gps_upload_any ( menu_array_sublayer values )
3698 {
3699   VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
3700   VikLayersPanel *vlp = VIK_LAYERS_PANEL(values[MA_VLP]);
3701
3702   // May not actually get a track here as values[2&3] can be null
3703   VikTrack *track = NULL;
3704   vik_gps_xfer_type xfer_type = TRK; // VIK_TRW_LAYER_SUBLAYER_TRACKS = 0 so hard to test different from NULL!
3705   gboolean xfer_all = FALSE;
3706
3707   if ( values[MA_SUBTYPE] ) {
3708     xfer_all = FALSE;
3709     if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE ) {
3710       track = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
3711       xfer_type = RTE;
3712     }
3713     else if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_TRACK ) {
3714       track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
3715       xfer_type = TRK;
3716     }
3717     else if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_WAYPOINTS ) {
3718       xfer_type = WPT;
3719     }
3720     else if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTES ) {
3721       xfer_type = RTE;
3722     }
3723   }
3724   else if ( !values[MA_CONFIRM] )
3725     xfer_all = TRUE; // i.e. whole layer
3726
3727   if (track && !track->visible) {
3728     a_dialog_error_msg ( VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Can not upload invisible track.") );
3729     return;
3730   }
3731
3732   GtkWidget *dialog = gtk_dialog_new_with_buttons ( _("GPS Upload"),
3733                                                     VIK_GTK_WINDOW_FROM_LAYER(vtl),
3734                                                     GTK_DIALOG_DESTROY_WITH_PARENT,
3735                                                     GTK_STOCK_OK,
3736                                                     GTK_RESPONSE_ACCEPT,
3737                                                     GTK_STOCK_CANCEL,
3738                                                     GTK_RESPONSE_REJECT,
3739                                                     NULL );
3740
3741   gtk_dialog_set_default_response ( GTK_DIALOG(dialog), GTK_RESPONSE_ACCEPT );
3742   GtkWidget *response_w = NULL;
3743 #if GTK_CHECK_VERSION (2, 20, 0)
3744   response_w = gtk_dialog_get_widget_for_response ( GTK_DIALOG(dialog), GTK_RESPONSE_ACCEPT );
3745 #endif
3746
3747   if ( response_w )
3748     gtk_widget_grab_focus ( response_w );
3749
3750   gpointer dgs = datasource_gps_setup ( dialog, xfer_type, xfer_all );
3751
3752   if ( gtk_dialog_run ( GTK_DIALOG(dialog) ) != GTK_RESPONSE_ACCEPT ) {
3753     datasource_gps_clean_up ( dgs );
3754     gtk_widget_destroy ( dialog );
3755     return;
3756   }
3757
3758   // Get info from reused datasource dialog widgets
3759   gchar* protocol = datasource_gps_get_protocol ( dgs );
3760   gchar* port = datasource_gps_get_descriptor ( dgs );
3761   // NB don't free the above strings as they're references to values held elsewhere
3762   gboolean do_tracks = datasource_gps_get_do_tracks ( dgs );
3763   gboolean do_routes = datasource_gps_get_do_routes ( dgs );
3764   gboolean do_waypoints = datasource_gps_get_do_waypoints ( dgs );
3765   gboolean turn_off = datasource_gps_get_off ( dgs );
3766
3767   gtk_widget_destroy ( dialog );
3768
3769   // When called from the viewport - work the corresponding layerspanel:
3770   if ( !vlp ) {
3771     vlp = vik_window_layers_panel ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)) );
3772   }
3773
3774   // Apply settings to transfer to the GPS device
3775   vik_gps_comm ( vtl,
3776                  track,
3777                  GPS_UP,
3778                  protocol,
3779                  port,
3780                  FALSE,
3781                  vik_layers_panel_get_viewport (vlp),
3782                  vlp,
3783                  do_tracks,
3784                  do_routes,
3785                  do_waypoints,
3786                  turn_off );
3787 }
3788
3789 /*
3790  * Acquire into this TRW Layer from any GPS Babel supported file
3791  */
3792 static void trw_layer_acquire_file_cb ( menu_array_layer values )
3793 {
3794   VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
3795   VikLayersPanel *vlp = VIK_LAYERS_PANEL(values[MA_VLP]);
3796   VikWindow *vw = (VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl));
3797   VikViewport *vvp =  vik_window_viewport(vw);
3798
3799   a_acquire ( vw, vlp, vvp, &vik_datasource_file_interface, NULL, NULL );
3800 }
3801
3802 static void trw_layer_new_wp ( menu_array_layer values )
3803 {
3804   VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
3805   VikLayersPanel *vlp = VIK_LAYERS_PANEL(values[MA_VLP]);
3806   /* TODO longone: okay, if layer above (aggregate) is invisible but vtl->visible is true, this redraws for no reason.
3807      instead return true if you want to update. */
3808   if ( vik_trw_layer_new_waypoint ( vtl, VIK_GTK_WINDOW_FROM_LAYER(vtl), vik_viewport_get_center(vik_layers_panel_get_viewport(vlp))) && VIK_LAYER(vtl)->visible ) {
3809     trw_layer_calculate_bounds_waypoints ( vtl );
3810     vik_layers_panel_emit_update ( vlp );
3811   }
3812 }
3813
3814 static void new_track_create_common ( VikTrwLayer *vtl, gchar *name )
3815 {
3816   vtl->current_track = vik_track_new();
3817   vik_track_set_defaults ( vtl->current_track );
3818   vtl->current_track->visible = TRUE;
3819   if ( vtl->drawmode == DRAWMODE_ALL_SAME_COLOR )
3820     // Create track with the preferred colour from the layer properties
3821     vtl->current_track->color = vtl->track_color;
3822   else
3823     gdk_color_parse ( "#000000", &(vtl->current_track->color) );
3824   vtl->current_track->has_color = TRUE;
3825   vik_trw_layer_add_track ( vtl, name, vtl->current_track );
3826 }
3827
3828 static void trw_layer_new_track ( menu_array_layer values )
3829 {
3830   VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
3831
3832   if ( ! vtl->current_track ) {
3833     gchar *name = trw_layer_new_unique_sublayer_name ( vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, _("Track")) ;
3834     new_track_create_common ( vtl, name );
3835     g_free ( name );
3836
3837     vik_window_enable_layer_tool ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)), VIK_LAYER_TRW, TOOL_CREATE_TRACK );
3838   }
3839 }
3840
3841 static void new_route_create_common ( VikTrwLayer *vtl, gchar *name )
3842 {
3843   vtl->current_track = vik_track_new();
3844   vik_track_set_defaults ( vtl->current_track );
3845   vtl->current_track->visible = TRUE;
3846   vtl->current_track->is_route = TRUE;
3847   // By default make all routes red
3848   vtl->current_track->has_color = TRUE;
3849   gdk_color_parse ( "red", &vtl->current_track->color );
3850   vik_trw_layer_add_route ( vtl, name, vtl->current_track );
3851 }
3852
3853 static void trw_layer_new_route ( menu_array_layer values )
3854 {
3855   VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
3856
3857   if ( ! vtl->current_track ) {
3858     gchar *name = trw_layer_new_unique_sublayer_name ( vtl, VIK_TRW_LAYER_SUBLAYER_ROUTE, _("Route")) ;
3859     new_route_create_common ( vtl, name );
3860     g_free ( name );
3861     vik_window_enable_layer_tool ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)), VIK_LAYER_TRW, TOOL_CREATE_ROUTE );
3862   }
3863 }
3864
3865 static void trw_layer_auto_routes_view ( menu_array_layer values )
3866 {
3867   VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
3868   VikLayersPanel *vlp = VIK_LAYERS_PANEL(values[MA_VLP]);
3869
3870   if ( g_hash_table_size (vtl->routes) > 0 ) {
3871     struct LatLon maxmin[2] = { {0,0}, {0,0} };
3872     g_hash_table_foreach ( vtl->routes, (GHFunc) trw_layer_find_maxmin_tracks, maxmin );
3873     trw_layer_zoom_to_show_latlons ( vtl, vik_layers_panel_get_viewport (vlp), maxmin );
3874     vik_layers_panel_emit_update ( vlp );
3875   }
3876 }
3877
3878
3879 static void trw_layer_finish_track ( menu_array_layer values )
3880 {
3881   VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
3882   vtl->current_track = NULL;
3883   vik_layer_emit_update ( VIK_LAYER(vtl) );
3884 }
3885
3886 static void trw_layer_auto_tracks_view ( menu_array_layer values )
3887 {
3888   VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
3889   VikLayersPanel *vlp = VIK_LAYERS_PANEL(values[MA_VLP]);
3890
3891   if ( g_hash_table_size (vtl->tracks) > 0 ) {
3892     struct LatLon maxmin[2] = { {0,0}, {0,0} };
3893     g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_find_maxmin_tracks, maxmin );
3894     trw_layer_zoom_to_show_latlons ( vtl, vik_layers_panel_get_viewport (vlp), maxmin );
3895     vik_layers_panel_emit_update ( vlp );
3896   }
3897 }
3898
3899 static void trw_layer_single_waypoint_jump ( const gpointer id, const VikWaypoint *wp, gpointer vvp )
3900 {
3901   /* NB do not care if wp is visible or not */
3902   vik_viewport_set_center_coord ( VIK_VIEWPORT(vvp), &(wp->coord), TRUE );
3903 }
3904
3905 static void trw_layer_auto_waypoints_view ( menu_array_layer values )
3906 {
3907   VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
3908   VikLayersPanel *vlp = VIK_LAYERS_PANEL(values[MA_VLP]);
3909
3910   /* Only 1 waypoint - jump straight to it */
3911   if ( g_hash_table_size (vtl->waypoints) == 1 ) {
3912     VikViewport *vvp = vik_layers_panel_get_viewport (vlp);
3913     g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_single_waypoint_jump, (gpointer) vvp );
3914   }
3915   /* If at least 2 waypoints - find center and then zoom to fit */
3916   else if ( g_hash_table_size (vtl->waypoints) > 1 )
3917   {
3918     struct LatLon maxmin[2] = { {0,0}, {0,0} };
3919     maxmin[0].lat = vtl->waypoints_bbox.north;
3920     maxmin[1].lat = vtl->waypoints_bbox.south;
3921     maxmin[0].lon = vtl->waypoints_bbox.east;
3922     maxmin[1].lon = vtl->waypoints_bbox.west;
3923     trw_layer_zoom_to_show_latlons ( vtl, vik_layers_panel_get_viewport (vlp), maxmin );
3924   }
3925
3926   vik_layers_panel_emit_update ( vlp );
3927 }
3928
3929 void trw_layer_osm_traces_upload_cb ( menu_array_layer values )
3930 {
3931   osm_traces_upload_viktrwlayer(VIK_TRW_LAYER(values[MA_VTL]), NULL);
3932 }
3933
3934 void trw_layer_osm_traces_upload_track_cb ( menu_array_sublayer values )
3935 {
3936   if ( values[MA_MISC] ) {
3937     VikTrack *trk = VIK_TRACK(values[MA_MISC]);
3938     osm_traces_upload_viktrwlayer(VIK_TRW_LAYER(values[MA_VTL]), trk);
3939   }
3940 }
3941
3942 static void trw_layer_add_menu_items ( VikTrwLayer *vtl, GtkMenu *menu, gpointer vlp )
3943 {
3944   static menu_array_layer pass_along;
3945   GtkWidget *item;
3946   GtkWidget *export_submenu;
3947   pass_along[MA_VTL] = vtl;
3948   pass_along[MA_VLP] = vlp;
3949
3950   item = gtk_menu_item_new();
3951   gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
3952   gtk_widget_show ( item );
3953
3954   if ( vtl->current_track ) {
3955     if ( vtl->current_track->is_route )
3956       item = gtk_menu_item_new_with_mnemonic ( _("_Finish Route") );
3957     else
3958       item = gtk_menu_item_new_with_mnemonic ( _("_Finish Track") );
3959     g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_finish_track), pass_along );
3960     gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3961     gtk_widget_show ( item );
3962
3963     // Add separator
3964     item = gtk_menu_item_new ();
3965     gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
3966     gtk_widget_show ( item );
3967   }
3968
3969   /* Now with icons */
3970   item = gtk_image_menu_item_new_with_mnemonic ( _("_View Layer") );
3971   gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ZOOM_FIT, GTK_ICON_SIZE_MENU) );
3972   g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_view), pass_along );
3973   gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3974   gtk_widget_show ( item );
3975
3976   GtkWidget *view_submenu = gtk_menu_new();
3977   item = gtk_image_menu_item_new_with_mnemonic ( _("V_iew") );
3978   gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_FIND, GTK_ICON_SIZE_MENU) );
3979   gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3980   gtk_widget_show ( item );
3981   gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), view_submenu );
3982
3983   item = gtk_menu_item_new_with_mnemonic ( _("View All _Tracks") );
3984   g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_tracks_view), pass_along );
3985   gtk_menu_shell_append (GTK_MENU_SHELL (view_submenu), item);
3986   gtk_widget_show ( item );
3987
3988   item = gtk_menu_item_new_with_mnemonic ( _("View All _Routes") );
3989   g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_routes_view), pass_along );
3990   gtk_menu_shell_append (GTK_MENU_SHELL (view_submenu), item);
3991   gtk_widget_show ( item );
3992
3993   item = gtk_menu_item_new_with_mnemonic ( _("View All _Waypoints") );
3994   g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_waypoints_view), pass_along );
3995   gtk_menu_shell_append (GTK_MENU_SHELL (view_submenu), item);
3996   gtk_widget_show ( item );
3997
3998   item = gtk_image_menu_item_new_with_mnemonic ( _("_Goto Center of Layer") );
3999   gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_JUMP_TO, GTK_ICON_SIZE_MENU) );
4000   g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_centerize), pass_along );
4001   gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
4002   gtk_widget_show ( item );
4003
4004   item = gtk_menu_item_new_with_mnemonic ( _("Goto _Waypoint...") );
4005   g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_wp), pass_along );
4006   gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
4007   gtk_widget_show ( item );
4008
4009   export_submenu = gtk_menu_new ();
4010   item = gtk_image_menu_item_new_with_mnemonic ( _("_Export Layer") );
4011   gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_HARDDISK, GTK_ICON_SIZE_MENU) );
4012   gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
4013   gtk_widget_show ( item );
4014   gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), export_submenu );
4015   
4016   item = gtk_menu_item_new_with_mnemonic ( _("Export as GPS_Point...") );
4017   g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_gpspoint), pass_along );
4018   gtk_menu_shell_append (GTK_MENU_SHELL (export_submenu), item);
4019   gtk_widget_show ( item );
4020
4021   item = gtk_menu_item_new_with_mnemonic ( _("Export as GPS_Mapper...") );
4022   g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_gpsmapper), pass_along );
4023   gtk_menu_shell_append (GTK_MENU_SHELL (export_submenu), item);
4024   gtk_widget_show ( item );
4025
4026   item = gtk_menu_item_new_with_mnemonic ( _("Export as _GPX...") );
4027   g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_gpx), pass_along );
4028   gtk_menu_shell_append (GTK_MENU_SHELL (export_submenu), item);
4029   gtk_widget_show ( item );
4030
4031   item = gtk_menu_item_new_with_mnemonic ( _("Export as _KML...") );
4032   g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_kml), pass_along );
4033   gtk_menu_shell_append (GTK_MENU_SHELL (export_submenu), item);
4034   gtk_widget_show ( item );
4035
4036   item = gtk_menu_item_new_with_mnemonic ( _("Export via GPSbabel...") );
4037   g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_babel), pass_along );
4038   gtk_menu_shell_append (GTK_MENU_SHELL (export_submenu), item);
4039   gtk_widget_show ( item );
4040
4041   gchar* external1 = g_strdup_printf ( _("Open with External Program_1: %s"), a_vik_get_external_gpx_program_1() );
4042   item = gtk_menu_item_new_with_mnemonic ( external1 );
4043   g_free ( external1 );
4044   g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_external_gpx_1), pass_along );
4045   gtk_menu_shell_append (GTK_MENU_SHELL (export_submenu), item);
4046   gtk_widget_show ( item );
4047
4048   gchar* external2 = g_strdup_printf ( _("Open with External Program_2: %s"), a_vik_get_external_gpx_program_2() );
4049   item = gtk_menu_item_new_with_mnemonic ( external2 );
4050   g_free ( external2 );
4051   g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_external_gpx_2), pass_along );
4052   gtk_menu_shell_append (GTK_MENU_SHELL (export_submenu), item);
4053   gtk_widget_show ( item );
4054
4055   GtkWidget *new_submenu = gtk_menu_new();
4056   item = gtk_image_menu_item_new_with_mnemonic ( _("_New") );
4057   gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NEW, GTK_ICON_SIZE_MENU) );
4058   gtk_menu_shell_append(GTK_MENU_SHELL (menu), item);
4059   gtk_widget_show(item);
4060   gtk_menu_item_set_submenu(GTK_MENU_ITEM(item), new_submenu);
4061
4062   item = gtk_image_menu_item_new_with_mnemonic ( _("New _Waypoint...") );
4063   gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NEW, GTK_ICON_SIZE_MENU) );
4064   g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_wp), pass_along );
4065   gtk_menu_shell_append (GTK_MENU_SHELL (new_submenu), item);
4066   gtk_widget_show ( item );
4067
4068   item = gtk_image_menu_item_new_with_mnemonic ( _("New _Track") );
4069   gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NEW, GTK_ICON_SIZE_MENU) );
4070   g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_track), pass_along );
4071   gtk_menu_shell_append (GTK_MENU_SHELL (new_submenu), item);
4072   gtk_widget_show ( item );
4073   // Make it available only when a new track *not* already in progress
4074   gtk_widget_set_sensitive ( item, ! (gboolean)GPOINTER_TO_INT(vtl->current_track) );
4075
4076   item = gtk_image_menu_item_new_with_mnemonic ( _("New _Route") );
4077   gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NEW, GTK_ICON_SIZE_MENU) );
4078   g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_route), pass_along );
4079   gtk_menu_shell_append (GTK_MENU_SHELL (new_submenu), item);
4080   gtk_widget_show ( item );
4081   // Make it available only when a new track *not* already in progress
4082   gtk_widget_set_sensitive ( item, ! (gboolean)GPOINTER_TO_INT(vtl->current_track) );
4083
4084 #ifdef VIK_CONFIG_GEOTAG
4085   item = gtk_menu_item_new_with_mnemonic ( _("Geotag _Images...") );
4086   g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_geotagging), pass_along );
4087   gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
4088   gtk_widget_show ( item );
4089 #endif
4090
4091   GtkWidget *acquire_submenu = gtk_menu_new ();
4092   item = gtk_image_menu_item_new_with_mnemonic ( _("_Acquire") );
4093   gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_DOWN, GTK_ICON_SIZE_MENU) );
4094   gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
4095   gtk_widget_show ( item );
4096   gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), acquire_submenu );
4097   
4098   item = gtk_menu_item_new_with_mnemonic ( _("From _GPS...") );
4099   g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_acquire_gps_cb), pass_along );
4100   gtk_menu_shell_append (GTK_MENU_SHELL (acquire_submenu), item);
4101   gtk_widget_show ( item );
4102
4103   /* FIXME: only add menu when at least a routing engine has support for Directions */
4104   item = gtk_menu_item_new_with_mnemonic ( _("From _Directions...") );
4105   g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_acquire_routing_cb), pass_along );
4106   gtk_menu_shell_append (GTK_MENU_SHELL (acquire_submenu), item);
4107   gtk_widget_show ( item );
4108
4109 #ifdef VIK_CONFIG_OPENSTREETMAP
4110   item = gtk_menu_item_new_with_mnemonic ( _("From _OSM Traces...") );
4111   g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_acquire_osm_cb), pass_along );
4112   gtk_menu_shell_append (GTK_MENU_SHELL (acquire_submenu), item);
4113   gtk_widget_show ( item );
4114
4115   item = gtk_menu_item_new_with_mnemonic ( _("From _My OSM Traces...") );
4116   g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_acquire_osm_my_traces_cb), pass_along );
4117   gtk_menu_shell_append (GTK_MENU_SHELL (acquire_submenu), item);
4118   gtk_widget_show ( item );
4119 #endif
4120
4121   item = gtk_menu_item_new_with_mnemonic ( _("From _URL...") );
4122   g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_acquire_url_cb), pass_along );
4123   gtk_menu_shell_append (GTK_MENU_SHELL (acquire_submenu), item);
4124   gtk_widget_show ( item );
4125
4126 #ifdef VIK_CONFIG_GEONAMES
4127   GtkWidget *wikipedia_submenu = gtk_menu_new();
4128   item = gtk_image_menu_item_new_with_mnemonic ( _("From _Wikipedia Waypoints") );
4129   gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ADD, GTK_ICON_SIZE_MENU) );
4130   gtk_menu_shell_append(GTK_MENU_SHELL (acquire_submenu), item);
4131   gtk_widget_show(item);
4132   gtk_menu_item_set_submenu(GTK_MENU_ITEM(item), wikipedia_submenu);
4133
4134   item = gtk_image_menu_item_new_with_mnemonic ( _("Within _Layer Bounds") );
4135   gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ZOOM_FIT, GTK_ICON_SIZE_MENU) );
4136   g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_wikipedia_wp_layer), pass_along );
4137   gtk_menu_shell_append (GTK_MENU_SHELL (wikipedia_submenu), item);
4138   gtk_widget_show ( item );
4139
4140   item = gtk_image_menu_item_new_with_mnemonic ( _("Within _Current View") );
4141   gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ZOOM_100, GTK_ICON_SIZE_MENU) );
4142   g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_wikipedia_wp_viewport), pass_along );
4143   gtk_menu_shell_append (GTK_MENU_SHELL (wikipedia_submenu), item);
4144   gtk_widget_show ( item );
4145 #endif
4146
4147 #ifdef VIK_CONFIG_GEOCACHES
4148   item = gtk_menu_item_new_with_mnemonic ( _("From Geo_caching...") );
4149   g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_acquire_geocache_cb), pass_along );
4150   gtk_menu_shell_append (GTK_MENU_SHELL (acquire_submenu), item);
4151   gtk_widget_show ( item );
4152 #endif
4153
4154 #ifdef VIK_CONFIG_GEOTAG
4155   item = gtk_menu_item_new_with_mnemonic ( _("From Geotagged _Images...") );
4156   g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_acquire_geotagged_cb), pass_along );
4157   gtk_menu_shell_append (GTK_MENU_SHELL (acquire_submenu), item);
4158   gtk_widget_show ( item );
4159 #endif
4160
4161   item = gtk_menu_item_new_with_mnemonic ( _("From _File...") );
4162   g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_acquire_file_cb), pass_along );
4163   gtk_menu_shell_append (GTK_MENU_SHELL (acquire_submenu), item);
4164   gtk_widget_set_tooltip_text (item, _("Import File With GPS_Babel..."));
4165   gtk_widget_show ( item );
4166
4167   vik_ext_tool_datasources_add_menu_items_to_menu ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)), GTK_MENU (acquire_submenu) );
4168
4169   GtkWidget *upload_submenu = gtk_menu_new ();
4170   item = gtk_image_menu_item_new_with_mnemonic ( _("_Upload") );
4171   gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_UP, GTK_ICON_SIZE_MENU) );
4172   gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
4173   gtk_widget_show ( item );
4174   gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), upload_submenu );
4175
4176   item = gtk_image_menu_item_new_with_mnemonic ( _("Upload to _GPS...") );
4177   gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_FORWARD, GTK_ICON_SIZE_MENU) );
4178   g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_gps_upload), pass_along );
4179   gtk_menu_shell_append (GTK_MENU_SHELL (upload_submenu), item);
4180   gtk_widget_show ( item );
4181
4182 #ifdef VIK_CONFIG_OPENSTREETMAP 
4183   item = gtk_image_menu_item_new_with_mnemonic ( _("Upload to _OSM...") );
4184   gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_UP, GTK_ICON_SIZE_MENU) );
4185   g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_osm_traces_upload_cb), pass_along );
4186   gtk_menu_shell_append (GTK_MENU_SHELL (upload_submenu), item);
4187   gtk_widget_show ( item );
4188 #endif
4189
4190   GtkWidget *delete_submenu = gtk_menu_new ();
4191   item = gtk_image_menu_item_new_with_mnemonic ( _("De_lete") );
4192   gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU) );
4193   gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
4194   gtk_widget_show ( item );
4195   gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), delete_submenu );
4196   
4197   item = gtk_image_menu_item_new_with_mnemonic ( _("Delete All _Tracks") );
4198   gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU) );
4199   g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_all_tracks), pass_along );
4200   gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
4201   gtk_widget_show ( item );
4202   
4203   item = gtk_image_menu_item_new_with_mnemonic ( _("Delete Tracks _From Selection...") );
4204   gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
4205   g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_tracks_from_selection), pass_along );
4206   gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
4207   gtk_widget_show ( item );
4208
4209   item = gtk_image_menu_item_new_with_mnemonic ( _("Delete _All Routes") );
4210   gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU) );
4211   g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_all_routes), pass_along );
4212   gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
4213   gtk_widget_show ( item );
4214
4215   item = gtk_image_menu_item_new_with_mnemonic ( _("_Delete Routes From Selection...") );
4216   gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
4217   g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_routes_from_selection), pass_along );
4218   gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
4219   gtk_widget_show ( item );
4220   
4221   item = gtk_image_menu_item_new_with_mnemonic ( _("Delete All _Waypoints") );
4222   gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU) );
4223   g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_all_waypoints), pass_along );
4224   gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
4225   gtk_widget_show ( item );
4226   
4227   item = gtk_image_menu_item_new_with_mnemonic ( _("Delete Waypoints From _Selection...") );
4228   gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
4229   g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_waypoints_from_selection), pass_along );
4230   gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
4231   gtk_widget_show ( item );
4232   
4233   item = a_acquire_trwlayer_menu ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)), vlp,
4234                                    vik_layers_panel_get_viewport(VIK_LAYERS_PANEL(vlp)), vtl );
4235   if ( item ) {
4236     gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
4237     gtk_widget_show ( item );
4238   }  
4239
4240   item = a_acquire_trwlayer_track_menu ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)), vlp,
4241                                          vik_layers_panel_get_viewport(VIK_LAYERS_PANEL(vlp)), vtl );
4242   if ( item ) {
4243     gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
4244     gtk_widget_show ( item );
4245   }
4246
4247   item = gtk_image_menu_item_new_with_mnemonic ( _("Track _List...") );
4248   gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
4249   g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_track_list_dialog), pass_along );
4250   gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
4251   gtk_widget_show ( item );
4252   gtk_widget_set_sensitive ( item, (gboolean)(g_hash_table_size (vtl->tracks)+g_hash_table_size (vtl->routes)) );
4253
4254   item = gtk_image_menu_item_new_with_mnemonic ( _("_Waypoint List...") );
4255   gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
4256   g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_waypoint_list_dialog), pass_along );
4257   gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
4258   gtk_widget_show ( item );
4259   gtk_widget_set_sensitive ( item, (gboolean)(g_hash_table_size (vtl->waypoints)) );
4260 }
4261
4262 // Fake Waypoint UUIDs vi simple increasing integer
4263 static guint wp_uuid = 0;
4264
4265 void vik_trw_layer_add_waypoint ( VikTrwLayer *vtl, gchar *name, VikWaypoint *wp )
4266 {
4267   wp_uuid++;
4268
4269   vik_waypoint_set_name (wp, name);
4270
4271   if ( VIK_LAYER(vtl)->realized )
4272   {
4273     // Do we need to create the sublayer:
4274     if ( g_hash_table_size (vtl->waypoints) == 0 ) {
4275       trw_layer_add_sublayer_waypoints ( vtl, VIK_LAYER(vtl)->vt, &(VIK_LAYER(vtl)->iter) );
4276     }
4277
4278     GtkTreeIter *iter = g_malloc(sizeof(GtkTreeIter));
4279
4280     // Visibility column always needed for waypoints
4281     vik_treeview_add_sublayer ( VIK_LAYER(vtl)->vt, &(vtl->waypoints_iter), iter, name, vtl, GUINT_TO_POINTER(wp_uuid), VIK_TRW_LAYER_SUBLAYER_WAYPOINT, get_wp_sym_small (wp->symbol), TRUE, TRUE );
4282
4283     // Actual setting of visibility dependent on the waypoint
4284     vik_treeview_item_set_visible ( VIK_LAYER(vtl)->vt, iter, wp->visible );
4285
4286     g_hash_table_insert ( vtl->waypoints_iters, GUINT_TO_POINTER(wp_uuid), iter );
4287
4288     // Sort now as post_read is not called on a realized waypoint
4289     vik_treeview_sort_children ( VIK_LAYER(vtl)->vt, &(vtl->waypoints_iter), vtl->wp_sort_order );
4290   }
4291
4292   highest_wp_number_add_wp(vtl, name);
4293   g_hash_table_insert ( vtl->waypoints, GUINT_TO_POINTER(wp_uuid), wp );
4294  
4295 }
4296
4297 // Fake Track UUIDs vi simple increasing integer
4298 static guint tr_uuid = 0;
4299
4300 void vik_trw_layer_add_track ( VikTrwLayer *vtl, gchar *name, VikTrack *t )
4301 {
4302   tr_uuid++;
4303
4304   vik_track_set_name (t, name);
4305
4306   if ( VIK_LAYER(vtl)->realized )
4307   {
4308     // Do we need to create the sublayer:
4309     if ( g_hash_table_size (vtl->tracks) == 0 ) {
4310       trw_layer_add_sublayer_tracks ( vtl, VIK_LAYER(vtl)->vt, &(VIK_LAYER(vtl)->iter) );
4311     }
4312
4313     GtkTreeIter *iter = g_malloc(sizeof(GtkTreeIter));
4314     // Visibility column always needed for tracks
4315     vik_treeview_add_sublayer ( VIK_LAYER(vtl)->vt, &(vtl->tracks_iter), iter, name, vtl, GUINT_TO_POINTER(tr_uuid), VIK_TRW_LAYER_SUBLAYER_TRACK, NULL, TRUE, TRUE );
4316
4317     // Actual setting of visibility dependent on the track
4318     vik_treeview_item_set_visible ( VIK_LAYER(vtl)->vt, iter, t->visible );
4319
4320     g_hash_table_insert ( vtl->tracks_iters, GUINT_TO_POINTER(tr_uuid), iter );
4321
4322     // Sort now as post_read is not called on a realized track
4323     vik_treeview_sort_children ( VIK_LAYER(vtl)->vt, &(vtl->tracks_iter), vtl->track_sort_order );
4324   }
4325
4326   g_hash_table_insert ( vtl->tracks, GUINT_TO_POINTER(tr_uuid), t );
4327
4328   trw_layer_update_treeview ( vtl, t );
4329 }
4330
4331 // Fake Route UUIDs vi simple increasing integer
4332 static guint rt_uuid = 0;
4333
4334 void vik_trw_layer_add_route ( VikTrwLayer *vtl, gchar *name, VikTrack *t )
4335 {
4336   rt_uuid++;
4337
4338   vik_track_set_name (t, name);
4339
4340   if ( VIK_LAYER(vtl)->realized )
4341   {
4342     // Do we need to create the sublayer:
4343     if ( g_hash_table_size (vtl->routes) == 0 ) {
4344       trw_layer_add_sublayer_routes ( vtl, VIK_LAYER(vtl)->vt, &(VIK_LAYER(vtl)->iter) );
4345     }
4346
4347     GtkTreeIter *iter = g_malloc(sizeof(GtkTreeIter));
4348     // Visibility column always needed for routes
4349     vik_treeview_add_sublayer ( VIK_LAYER(vtl)->vt, &(vtl->routes_iter), iter, name, vtl, GUINT_TO_POINTER(rt_uuid), VIK_TRW_LAYER_SUBLAYER_ROUTE, NULL, TRUE, TRUE );
4350     // Actual setting of visibility dependent on the route
4351     vik_treeview_item_set_visible ( VIK_LAYER(vtl)->vt, iter, t->visible );
4352
4353     g_hash_table_insert ( vtl->routes_iters, GUINT_TO_POINTER(rt_uuid), iter );
4354
4355     // Sort now as post_read is not called on a realized route
4356     vik_treeview_sort_children ( VIK_LAYER(vtl)->vt, &(vtl->routes_iter), vtl->track_sort_order );
4357   }
4358
4359   g_hash_table_insert ( vtl->routes, GUINT_TO_POINTER(rt_uuid), t );
4360
4361   trw_layer_update_treeview ( vtl, t );
4362 }
4363
4364 /* to be called whenever a track has been deleted or may have been changed. */
4365 void trw_layer_cancel_tps_of_track ( VikTrwLayer *vtl, VikTrack *trk )
4366 {
4367   if (vtl->current_tp_track == trk )
4368     trw_layer_cancel_current_tp ( vtl, FALSE );
4369 }
4370
4371 /**
4372  * Normally this is done to due the waypoint size preference having changed
4373  */
4374 void vik_trw_layer_reset_waypoints ( VikTrwLayer *vtl )
4375 {
4376   GHashTableIter iter;
4377   gpointer key, value;
4378
4379   // Foreach waypoint
4380   g_hash_table_iter_init ( &iter, vtl->waypoints );
4381   while ( g_hash_table_iter_next ( &iter, &key, &value ) ) {
4382     VikWaypoint *wp = VIK_WAYPOINT(value);
4383     if ( wp->symbol ) {
4384       // Reapply symbol setting to update the pixbuf
4385       gchar *tmp_symbol = g_strdup ( wp->symbol );
4386       vik_waypoint_set_symbol ( wp, tmp_symbol );
4387       g_free ( tmp_symbol );
4388     }
4389   }
4390 }
4391
4392 /**
4393  * trw_layer_new_unique_sublayer_name:
4394  *
4395  * Allocates a unique new name
4396  */
4397 gchar *trw_layer_new_unique_sublayer_name (VikTrwLayer *vtl, gint sublayer_type, const gchar *name)
4398 {
4399   gint i = 2;
4400   gchar *newname = g_strdup(name);
4401
4402   gpointer id = NULL;
4403   do {
4404     id = NULL;
4405     switch ( sublayer_type ) {
4406     case VIK_TRW_LAYER_SUBLAYER_TRACK:
4407       id = (gpointer) vik_trw_layer_get_track ( vtl, newname );
4408       break;
4409     case VIK_TRW_LAYER_SUBLAYER_WAYPOINT:
4410       id = (gpointer) vik_trw_layer_get_waypoint ( vtl, newname );
4411       break;
4412     default:
4413       id = (gpointer) vik_trw_layer_get_route ( vtl, newname );
4414       break;
4415     }
4416     // If found a name already in use try adding 1 to it and we try again
4417     if ( id ) {
4418       gchar *new_newname = g_strdup_printf("%s#%d", name, i);
4419       g_free(newname);
4420       newname = new_newname;
4421       i++;
4422     }
4423   } while ( id != NULL);
4424
4425   return newname;
4426 }
4427
4428 void vik_trw_layer_filein_add_waypoint ( VikTrwLayer *vtl, gchar *name, VikWaypoint *wp )
4429 {
4430   // No more uniqueness of name forced when loading from a file
4431   // This now makes this function a little redunant as we just flow the parameters through
4432   vik_trw_layer_add_waypoint ( vtl, name, wp );
4433 }
4434
4435 void vik_trw_layer_filein_add_track ( VikTrwLayer *vtl, gchar *name, VikTrack *tr )
4436 {
4437   if ( vtl->route_finder_append && vtl->route_finder_current_track ) {
4438     vik_track_remove_dup_points ( tr ); /* make "double point" track work to undo */
4439     vik_track_steal_and_append_trackpoints ( vtl->route_finder_current_track, tr );
4440     vik_track_free ( tr );
4441     vtl->route_finder_append = FALSE; /* this means we have added it */
4442   } else {
4443
4444     // No more uniqueness of name forced when loading from a file
4445     if ( tr->is_route )
4446       vik_trw_layer_add_route ( vtl, name, tr );
4447     else
4448       vik_trw_layer_add_track ( vtl, name, tr );
4449
4450     if ( vtl->route_finder_check_added_track ) {
4451       vik_track_remove_dup_points ( tr ); /* make "double point" track work to undo */
4452       vtl->route_finder_added_track = tr;
4453     }
4454   }
4455 }
4456
4457 static void trw_layer_enum_item ( gpointer id, GList **tr, GList **l )
4458 {
4459   *l = g_list_append(*l, id);
4460 }
4461
4462 /*
4463  * Move an item from one TRW layer to another TRW layer
4464  */
4465 static void trw_layer_move_item ( VikTrwLayer *vtl_src, VikTrwLayer *vtl_dest, gpointer id, gint type )
4466 {
4467   // TODO reconsider strategy when moving within layer (if anything...)
4468   gboolean rename = ( vtl_src != vtl_dest );
4469   if ( ! rename )
4470     return;
4471
4472   if (type == VIK_TRW_LAYER_SUBLAYER_TRACK) {
4473     VikTrack *trk = g_hash_table_lookup ( vtl_src->tracks, id );
4474
4475     gchar *newname;
4476     if ( rename )
4477       newname = trw_layer_new_unique_sublayer_name ( vtl_dest, type, trk->name );
4478     else
4479       newname = g_strdup ( trk->name );
4480
4481     VikTrack *trk2 = vik_track_copy ( trk, TRUE );
4482     vik_trw_layer_add_track ( vtl_dest, newname, trk2 );
4483     g_free ( newname );
4484     vik_trw_layer_delete_track ( vtl_src, trk );
4485   }
4486
4487   if (type == VIK_TRW_LAYER_SUBLAYER_ROUTE) {
4488     VikTrack *trk = g_hash_table_lookup ( vtl_src->routes, id );
4489
4490     gchar *newname;
4491     if ( rename )
4492       newname = trw_layer_new_unique_sublayer_name ( vtl_dest, type, trk->name );
4493     else
4494       newname = g_strdup ( trk->name );
4495
4496     VikTrack *trk2 = vik_track_copy ( trk, TRUE );
4497     vik_trw_layer_add_route ( vtl_dest, newname, trk2 );
4498     g_free ( newname );
4499     vik_trw_layer_delete_route ( vtl_src, trk );
4500   }
4501
4502   if (type == VIK_TRW_LAYER_SUBLAYER_WAYPOINT) {
4503     VikWaypoint *wp = g_hash_table_lookup ( vtl_src->waypoints, id );
4504
4505     gchar *newname;
4506     if ( rename )
4507       newname = trw_layer_new_unique_sublayer_name ( vtl_dest, type, wp->name );
4508     else
4509       newname = g_strdup ( wp->name );
4510
4511     VikWaypoint *wp2 = vik_waypoint_copy ( wp );
4512     vik_trw_layer_add_waypoint ( vtl_dest, newname, wp2 );
4513     g_free ( newname );
4514     trw_layer_delete_waypoint ( vtl_src, wp );
4515
4516     // Recalculate bounds even if not renamed as maybe dragged between layers
4517     trw_layer_calculate_bounds_waypoints ( vtl_dest );
4518     trw_layer_calculate_bounds_waypoints ( vtl_src );
4519   }
4520 }
4521
4522 static void trw_layer_drag_drop_request ( VikTrwLayer *vtl_src, VikTrwLayer *vtl_dest, GtkTreeIter *src_item_iter, GtkTreePath *dest_path )
4523 {
4524   VikTreeview *vt = VIK_LAYER(vtl_src)->vt;
4525   gint type = vik_treeview_item_get_data(vt, src_item_iter);
4526
4527   if (!vik_treeview_item_get_pointer(vt, src_item_iter)) {
4528     GList *items = NULL;
4529     GList *iter;
4530
4531     if (type==VIK_TRW_LAYER_SUBLAYER_TRACKS) {
4532       g_hash_table_foreach ( vtl_src->tracks, (GHFunc)trw_layer_enum_item, &items);
4533     } 
4534     if (type==VIK_TRW_LAYER_SUBLAYER_WAYPOINTS) {
4535       g_hash_table_foreach ( vtl_src->waypoints, (GHFunc)trw_layer_enum_item, &items);
4536     }    
4537     if (type==VIK_TRW_LAYER_SUBLAYER_ROUTES) {
4538       g_hash_table_foreach ( vtl_src->routes, (GHFunc)trw_layer_enum_item, &items);
4539     }
4540
4541     iter = items;
4542     while (iter) {
4543       if (type==VIK_TRW_LAYER_SUBLAYER_TRACKS) {
4544         trw_layer_move_item ( vtl_src, vtl_dest, iter->data, VIK_TRW_LAYER_SUBLAYER_TRACK);
4545       } else if (type==VIK_TRW_LAYER_SUBLAYER_ROUTES) {
4546         trw_layer_move_item ( vtl_src, vtl_dest, iter->data, VIK_TRW_LAYER_SUBLAYER_ROUTE);
4547       } else {
4548         trw_layer_move_item ( vtl_src, vtl_dest, iter->data, VIK_TRW_LAYER_SUBLAYER_WAYPOINT);
4549       }
4550       iter = iter->next;
4551     }
4552     if (items) 
4553       g_list_free(items);
4554   } else {
4555     gchar *name = vik_treeview_item_get_pointer(vt, src_item_iter);
4556     trw_layer_move_item(vtl_src, vtl_dest, name, type);
4557   }
4558 }
4559
4560 gboolean trw_layer_track_find_uuid ( const gpointer id, const VikTrack *trk, gpointer udata )
4561 {
4562   trku_udata *user_data = udata;
4563   if ( trk == user_data->trk ) {
4564     user_data->uuid = id;
4565     return TRUE;
4566   }
4567   return FALSE;
4568 }
4569
4570 gboolean vik_trw_layer_delete_track ( VikTrwLayer *vtl, VikTrack *trk )
4571 {
4572   gboolean was_visible = FALSE;
4573   if ( trk && trk->name ) {
4574
4575     if ( trk == vtl->current_track ) {
4576       vtl->current_track = NULL;
4577       vtl->current_tp_track = NULL;
4578       vtl->current_tp_id = NULL;
4579       vtl->moving_tp = FALSE;
4580     }
4581
4582     was_visible = trk->visible;
4583
4584     if ( trk == vtl->route_finder_current_track )
4585       vtl->route_finder_current_track = NULL;
4586
4587     if ( trk == vtl->route_finder_added_track )
4588       vtl->route_finder_added_track = NULL;
4589
4590     trku_udata udata;
4591     udata.trk  = trk;
4592     udata.uuid = NULL;
4593
4594     // Hmmm, want key of it
4595     gpointer *trkf = g_hash_table_find ( vtl->tracks, (GHRFunc) trw_layer_track_find_uuid, &udata );
4596
4597     if ( trkf && udata.uuid ) {
4598       /* could be current_tp, so we have to check */
4599       trw_layer_cancel_tps_of_track ( vtl, trk );
4600
4601       GtkTreeIter *it = g_hash_table_lookup ( vtl->tracks_iters, udata.uuid );
4602
4603       if ( it ) {
4604         vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, it );
4605         g_hash_table_remove ( vtl->tracks_iters, udata.uuid );
4606         g_hash_table_remove ( vtl->tracks, udata.uuid );
4607
4608         // If last sublayer, then remove sublayer container
4609         if ( g_hash_table_size (vtl->tracks) == 0 ) {
4610           vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, &(vtl->tracks_iter) );
4611         }
4612       }
4613       // Incase it was selected (no item delete signal ATM)
4614       vik_window_clear_highlight ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)) );
4615     }
4616   }
4617   return was_visible;
4618 }
4619
4620 gboolean vik_trw_layer_delete_route ( VikTrwLayer *vtl, VikTrack *trk )
4621 {
4622   gboolean was_visible = FALSE;
4623
4624   if ( trk && trk->name ) {
4625
4626     if ( trk == vtl->current_track ) {
4627       vtl->current_track = NULL;
4628       vtl->current_tp_track = NULL;
4629       vtl->current_tp_id = NULL;
4630       vtl->moving_tp = FALSE;
4631     }
4632
4633     was_visible = trk->visible;
4634
4635     if ( trk == vtl->route_finder_current_track )
4636       vtl->route_finder_current_track = NULL;
4637
4638     if ( trk == vtl->route_finder_added_track )
4639       vtl->route_finder_added_track = NULL;
4640
4641     trku_udata udata;
4642     udata.trk  = trk;
4643     udata.uuid = NULL;
4644
4645     // Hmmm, want key of it
4646     gpointer *trkf = g_hash_table_find ( vtl->routes, (GHRFunc) trw_layer_track_find_uuid, &udata );
4647
4648     if ( trkf && udata.uuid ) {
4649       /* could be current_tp, so we have to check */
4650       trw_layer_cancel_tps_of_track ( vtl, trk );
4651
4652       GtkTreeIter *it = g_hash_table_lookup ( vtl->routes_iters, udata.uuid );
4653
4654       if ( it ) {
4655         vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, it );
4656         g_hash_table_remove ( vtl->routes_iters, udata.uuid );
4657         g_hash_table_remove ( vtl->routes, udata.uuid );
4658
4659         // If last sublayer, then remove sublayer container
4660         if ( g_hash_table_size (vtl->routes) == 0 ) {
4661           vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, &(vtl->routes_iter) );
4662         }
4663       }
4664       // Incase it was selected (no item delete signal ATM)
4665       vik_window_clear_highlight ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)) );
4666     }
4667   }
4668   return was_visible;
4669 }
4670
4671 static gboolean trw_layer_delete_waypoint ( VikTrwLayer *vtl, VikWaypoint *wp )
4672 {
4673   gboolean was_visible = FALSE;
4674
4675   if ( wp && wp->name ) {
4676
4677     if ( wp == vtl->current_wp ) {
4678       vtl->current_wp = NULL;
4679       vtl->current_wp_id = NULL;
4680       vtl->moving_wp = FALSE;
4681     }
4682
4683     was_visible = wp->visible;
4684     
4685     wpu_udata udata;
4686     udata.wp   = wp;
4687     udata.uuid = NULL;
4688
4689     // Hmmm, want key of it
4690     gpointer *wpf = g_hash_table_find ( vtl->waypoints, (GHRFunc) trw_layer_waypoint_find_uuid, (gpointer) &udata );
4691
4692     if ( wpf && udata.uuid ) {
4693       GtkTreeIter *it = g_hash_table_lookup ( vtl->waypoints_iters, udata.uuid );
4694     
4695       if ( it ) {
4696         vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, it );
4697         g_hash_table_remove ( vtl->waypoints_iters, udata.uuid );
4698
4699         highest_wp_number_remove_wp(vtl, wp->name);
4700         g_hash_table_remove ( vtl->waypoints, udata.uuid ); // last because this frees the name
4701
4702         // If last sublayer, then remove sublayer container
4703         if ( g_hash_table_size (vtl->waypoints) == 0 ) {
4704           vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, &(vtl->waypoints_iter) );
4705         }
4706       }
4707       // Incase it was selected (no item delete signal ATM)
4708       vik_window_clear_highlight ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)) );
4709     }
4710
4711   }
4712
4713   return was_visible;
4714 }
4715
4716 // Only for temporary use by trw_layer_delete_waypoint_by_name
4717 static gboolean trw_layer_waypoint_find_uuid_by_name ( const gpointer id, const VikWaypoint *wp, gpointer udata )
4718 {
4719   wpu_udata *user_data = udata;
4720   if ( ! strcmp ( wp->name, user_data->wp->name ) ) {
4721     user_data->uuid = id;
4722     return TRUE;
4723   }
4724   return FALSE;
4725 }
4726
4727 /*
4728  * Delete a waypoint by the given name
4729  * NOTE: ATM this will delete the first encountered Waypoint with the specified name
4730  *   as there be multiple waypoints with the same name
4731  */
4732 static gboolean trw_layer_delete_waypoint_by_name ( VikTrwLayer *vtl, const gchar *name )
4733 {
4734   wpu_udata udata;
4735   // Fake a waypoint with the given name
4736   udata.wp   = vik_waypoint_new ();
4737   vik_waypoint_set_name (udata.wp, name);
4738   // Currently only the name is used in this waypoint find function
4739   udata.uuid = NULL;
4740
4741   // Hmmm, want key of it
4742   gpointer *wpf = g_hash_table_find ( vtl->waypoints, (GHRFunc) trw_layer_waypoint_find_uuid_by_name, (gpointer) &udata );
4743
4744   vik_waypoint_free (udata.wp);
4745
4746   if ( wpf && udata.uuid )
4747     return trw_layer_delete_waypoint (vtl, g_hash_table_lookup ( vtl->waypoints, udata.uuid ));
4748   else
4749     return FALSE;
4750 }
4751
4752 typedef struct {
4753   VikTrack *trk; // input
4754   gpointer uuid; // output
4755 } tpu_udata;
4756
4757 // Only for temporary use by trw_layer_delete_track_by_name
4758 static gboolean trw_layer_track_find_uuid_by_name ( const gpointer id, const VikTrack *trk, gpointer udata )
4759 {
4760   tpu_udata *user_data = udata;
4761   if ( ! strcmp ( trk->name, user_data->trk->name ) ) {
4762     user_data->uuid = id;
4763     return TRUE;
4764   }
4765   return FALSE;
4766 }
4767
4768 /*
4769  * Delete a track by the given name
4770  * NOTE: ATM this will delete the first encountered Track with the specified name
4771  *   as there may be multiple tracks with the same name within the specified hash table
4772  */
4773 static gboolean trw_layer_delete_track_by_name ( VikTrwLayer *vtl, const gchar *name, GHashTable *ht_tracks )
4774 {
4775   tpu_udata udata;
4776   // Fake a track with the given name
4777   udata.trk   = vik_track_new ();
4778   vik_track_set_name (udata.trk, name);
4779   // Currently only the name is used in this waypoint find function
4780   udata.uuid = NULL;
4781
4782   // Hmmm, want key of it
4783   gpointer *trkf = g_hash_table_find ( ht_tracks, (GHRFunc) trw_layer_track_find_uuid_by_name, &udata );
4784
4785   vik_track_free (udata.trk);
4786
4787   if ( trkf && udata.uuid ) {
4788     // This could be a little better written...
4789     if ( vtl->tracks == ht_tracks )
4790       return vik_trw_layer_delete_track (vtl, g_hash_table_lookup ( ht_tracks, udata.uuid ));
4791     if ( vtl->routes == ht_tracks )
4792       return vik_trw_layer_delete_route (vtl, g_hash_table_lookup ( ht_tracks, udata.uuid ));
4793     return FALSE;
4794   }
4795   else
4796     return FALSE;
4797 }
4798
4799 static void remove_item_from_treeview ( const gpointer id, GtkTreeIter *it, VikTreeview * vt )
4800 {
4801     vik_treeview_item_delete (vt, it );
4802 }
4803
4804 void vik_trw_layer_delete_all_routes ( VikTrwLayer *vtl )
4805 {
4806
4807   vtl->current_track = NULL;
4808   vtl->route_finder_current_track = NULL;
4809   vtl->route_finder_added_track = NULL;
4810   if (vtl->current_tp_track)
4811     trw_layer_cancel_current_tp(vtl, FALSE);
4812
4813   g_hash_table_foreach(vtl->routes_iters, (GHFunc) remove_item_from_treeview, VIK_LAYER(vtl)->vt);
4814   g_hash_table_remove_all(vtl->routes_iters);
4815   g_hash_table_remove_all(vtl->routes);
4816
4817   vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, &(vtl->routes_iter) );
4818
4819   vik_layer_emit_update ( VIK_LAYER(vtl) );
4820 }
4821
4822 void vik_trw_layer_delete_all_tracks ( VikTrwLayer *vtl )
4823 {
4824
4825   vtl->current_track = NULL;
4826   vtl->route_finder_current_track = NULL;
4827   vtl->route_finder_added_track = NULL;
4828   if (vtl->current_tp_track)
4829     trw_layer_cancel_current_tp(vtl, FALSE);
4830
4831   g_hash_table_foreach(vtl->tracks_iters, (GHFunc) remove_item_from_treeview, VIK_LAYER(vtl)->vt);
4832   g_hash_table_remove_all(vtl->tracks_iters);
4833   g_hash_table_remove_all(vtl->tracks);
4834
4835   vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, &(vtl->tracks_iter) );
4836
4837   vik_layer_emit_update ( VIK_LAYER(vtl) );
4838 }
4839
4840 void vik_trw_layer_delete_all_waypoints ( VikTrwLayer *vtl )
4841 {
4842   vtl->current_wp = NULL;
4843   vtl->current_wp_id = NULL;
4844   vtl->moving_wp = FALSE;
4845
4846   highest_wp_number_reset(vtl);
4847
4848   g_hash_table_foreach(vtl->waypoints_iters, (GHFunc) remove_item_from_treeview, VIK_LAYER(vtl)->vt);
4849   g_hash_table_remove_all(vtl->waypoints_iters);
4850   g_hash_table_remove_all(vtl->waypoints);
4851
4852   vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, &(vtl->waypoints_iter) );
4853
4854   vik_layer_emit_update ( VIK_LAYER(vtl) );
4855 }
4856
4857 static void trw_layer_delete_all_tracks ( menu_array_layer values )
4858 {
4859   VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
4860   // Get confirmation from the user
4861   if ( a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
4862                             _("Are you sure you want to delete all tracks in %s?"),
4863                             vik_layer_get_name ( VIK_LAYER(vtl) ) ) )
4864     vik_trw_layer_delete_all_tracks (vtl);
4865 }
4866
4867 static void trw_layer_delete_all_routes ( menu_array_layer values )
4868 {
4869   VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
4870   // Get confirmation from the user
4871   if ( a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
4872                             _("Are you sure you want to delete all routes in %s?"),
4873                             vik_layer_get_name ( VIK_LAYER(vtl) ) ) )
4874     vik_trw_layer_delete_all_routes (vtl);
4875 }
4876
4877 static void trw_layer_delete_all_waypoints ( menu_array_layer values )
4878 {
4879   VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
4880   // Get confirmation from the user
4881   if ( a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
4882                             _("Are you sure you want to delete all waypoints in %s?"),
4883                             vik_layer_get_name ( VIK_LAYER(vtl) ) ) )
4884     vik_trw_layer_delete_all_waypoints (vtl);
4885 }
4886
4887 static void trw_layer_delete_item ( menu_array_sublayer values )
4888 {
4889   VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
4890   gboolean was_visible = FALSE;
4891   if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_WAYPOINT )
4892   {
4893     VikWaypoint *wp = g_hash_table_lookup ( vtl->waypoints, values[MA_SUBLAYER_ID] );
4894     if ( wp && wp->name ) {
4895       if ( GPOINTER_TO_INT (values[MA_CONFIRM]) )
4896         // Get confirmation from the user
4897         // Maybe this Waypoint Delete should be optional as is it could get annoying...
4898         if ( ! a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
4899             _("Are you sure you want to delete the waypoint \"%s\"?"),
4900             wp->name ) )
4901           return;
4902       was_visible = trw_layer_delete_waypoint ( vtl, wp );
4903     }
4904   }
4905   else if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_TRACK )
4906   {
4907     VikTrack *trk = g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
4908     if ( trk && trk->name ) {
4909       if ( GPOINTER_TO_INT (values[MA_CONFIRM]) )
4910         // Get confirmation from the user
4911         if ( ! a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
4912                                   _("Are you sure you want to delete the track \"%s\"?"),
4913                                   trk->name ) )
4914           return;
4915       was_visible = vik_trw_layer_delete_track ( vtl, trk );
4916     }
4917   }
4918   else
4919   {
4920     VikTrack *trk = g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
4921     if ( trk && trk->name ) {
4922       if ( GPOINTER_TO_INT (values[MA_CONFIRM]) )
4923         // Get confirmation from the user
4924         if ( ! a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
4925                                     _("Are you sure you want to delete the route \"%s\"?"),
4926                                     trk->name ) )
4927           return;
4928       was_visible = vik_trw_layer_delete_route ( vtl, trk );
4929     }
4930   }
4931   if ( was_visible )
4932     vik_layer_emit_update ( VIK_LAYER(vtl) );
4933 }
4934
4935 /**
4936  *  Rename waypoint and maintain corresponding name of waypoint in the treeview
4937  */
4938 void trw_layer_waypoint_rename ( VikTrwLayer *vtl, VikWaypoint *wp, const gchar *new_name )
4939 {
4940   vik_waypoint_set_name ( wp, new_name );
4941
4942   // Now update the treeview as well
4943   wpu_udata udataU;
4944   udataU.wp   = wp;
4945   udataU.uuid = NULL;
4946
4947   // Need key of it for treeview update
4948   gpointer *wpf = g_hash_table_find ( vtl->waypoints, (GHRFunc) trw_layer_waypoint_find_uuid, &udataU );
4949
4950   if ( wpf && udataU.uuid ) {
4951     GtkTreeIter *it = g_hash_table_lookup ( vtl->waypoints_iters, udataU.uuid );
4952
4953     if ( it ) {
4954       vik_treeview_item_set_name ( VIK_LAYER(vtl)->vt, it, new_name );
4955       vik_treeview_sort_children ( VIK_LAYER(vtl)->vt, &(vtl->waypoints_iter), vtl->wp_sort_order );
4956     }
4957   }
4958 }
4959
4960 /**
4961  *  Maintain icon of waypoint in the treeview
4962  */
4963 void trw_layer_waypoint_reset_icon ( VikTrwLayer *vtl, VikWaypoint *wp )
4964 {
4965   // update the treeview
4966   wpu_udata udataU;
4967   udataU.wp   = wp;
4968   udataU.uuid = NULL;
4969
4970   // Need key of it for treeview update
4971   gpointer *wpf = g_hash_table_find ( vtl->waypoints, (GHRFunc) trw_layer_waypoint_find_uuid, &udataU );
4972
4973   if ( wpf && udataU.uuid ) {
4974     GtkTreeIter *it = g_hash_table_lookup ( vtl->waypoints_iters, udataU.uuid );
4975
4976     if ( it ) {
4977       vik_treeview_item_set_icon ( VIK_LAYER(vtl)->vt, it, get_wp_sym_small (wp->symbol) );
4978     }
4979   }
4980 }
4981
4982 static void trw_layer_properties_item ( menu_array_sublayer values )
4983 {
4984   VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
4985   if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_WAYPOINT )
4986   {
4987     VikWaypoint *wp = g_hash_table_lookup ( vtl->waypoints, values[MA_SUBLAYER_ID] );
4988
4989     if ( wp && wp->name )
4990     {
4991       gboolean updated = FALSE;
4992       gchar *new_name = a_dialog_waypoint ( VIK_GTK_WINDOW_FROM_LAYER(vtl), wp->name, vtl, wp, vtl->coord_mode, FALSE, &updated );
4993       if ( new_name )
4994         trw_layer_waypoint_rename ( vtl, wp, new_name );
4995
4996       if ( updated && values[MA_TV_ITER] )
4997         vik_treeview_item_set_icon ( VIK_LAYER(vtl)->vt, values[MA_TV_ITER], get_wp_sym_small (wp->symbol) );
4998
4999       if ( updated && VIK_LAYER(vtl)->visible )
5000         vik_layer_emit_update ( VIK_LAYER(vtl) );
5001     }
5002   }
5003   else
5004   {
5005     VikTrack *tr;
5006     if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_TRACK )
5007       tr = g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
5008     else
5009       tr = g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
5010
5011     if ( tr && tr->name )
5012     {
5013       vik_trw_layer_propwin_run ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
5014                                   vtl,
5015                                   tr,
5016                                   values[MA_VLP],
5017                                   values[MA_VVP],
5018                                   FALSE );
5019     }
5020   }
5021 }
5022
5023 /**
5024  * trw_layer_track_statistics:
5025  *
5026  * Show track statistics.
5027  * ATM jump to the stats page in the properties
5028  * TODO: consider separating the stats into an individual dialog?
5029  */
5030 static void trw_layer_track_statistics ( menu_array_sublayer values )
5031 {
5032   VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
5033   VikTrack *trk;
5034   if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_TRACK )
5035     trk = g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
5036   else
5037     trk = g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
5038
5039   if ( trk && trk->name ) {
5040     vik_trw_layer_propwin_run ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
5041                                 vtl,
5042                                 trk,
5043                                 values[MA_VLP],
5044                                 values[MA_VVP],
5045                                 TRUE );
5046   }
5047 }
5048
5049 /*
5050  * Update the treeview of the track id - primarily to update the icon
5051  */
5052 void trw_layer_update_treeview ( VikTrwLayer *vtl, VikTrack *trk )
5053 {
5054   trku_udata udata;
5055   udata.trk  = trk;
5056   udata.uuid = NULL;
5057
5058   gpointer *trkf = NULL;
5059   if ( trk->is_route )
5060     trkf = g_hash_table_find ( vtl->routes, (GHRFunc) trw_layer_track_find_uuid, &udata );
5061   else
5062     trkf = g_hash_table_find ( vtl->tracks, (GHRFunc) trw_layer_track_find_uuid, &udata );
5063
5064   if ( trkf && udata.uuid ) {
5065
5066     GtkTreeIter *iter = NULL;
5067     if ( trk->is_route )
5068       iter = g_hash_table_lookup ( vtl->routes_iters, udata.uuid );
5069     else
5070       iter = g_hash_table_lookup ( vtl->tracks_iters, udata.uuid );
5071
5072     if ( iter ) {
5073       // TODO: Make this a function
5074       GdkPixbuf *pixbuf = gdk_pixbuf_new ( GDK_COLORSPACE_RGB, FALSE, 8, 18, 18);
5075       guint32 pixel = ((trk->color.red & 0xff00) << 16) |
5076         ((trk->color.green & 0xff00) << 8) |
5077         (trk->color.blue & 0xff00);
5078       gdk_pixbuf_fill ( pixbuf, pixel );
5079       vik_treeview_item_set_icon ( VIK_LAYER(vtl)->vt, iter, pixbuf );
5080       g_object_unref (pixbuf);
5081     }
5082
5083   }
5084 }
5085
5086 /*
5087    Parameter 1 -> VikLayersPanel
5088    Parameter 2 -> VikLayer
5089    Parameter 3 -> VikViewport
5090 */
5091 static void goto_coord ( gpointer *vlp, gpointer vl, gpointer vvp, const VikCoord *coord )
5092 {
5093   if ( vlp ) {
5094     vik_viewport_set_center_coord ( vik_layers_panel_get_viewport (VIK_LAYERS_PANEL(vlp)), coord, TRUE );
5095     vik_layers_panel_emit_update ( VIK_LAYERS_PANEL(vlp) );
5096   }
5097   else {
5098     /* since vlp not set, vl & vvp should be valid instead! */
5099     if ( vl && vvp ) {
5100       vik_viewport_set_center_coord ( VIK_VIEWPORT(vvp), coord, TRUE );
5101       vik_layer_emit_update ( VIK_LAYER(vl) );
5102     }
5103   }
5104 }
5105
5106 static void trw_layer_goto_track_startpoint ( menu_array_sublayer values )
5107 {
5108   VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
5109   VikTrack *track;
5110   if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5111     track = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
5112   else
5113     track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
5114
5115   if ( track && track->trackpoints )
5116     goto_coord ( values[MA_VLP], vtl, values[MA_VVP], &(vik_track_get_tp_first(track)->coord) );
5117 }
5118
5119 static void trw_layer_goto_track_center ( menu_array_sublayer values )
5120 {
5121   VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
5122   VikTrack *track;
5123   if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5124     track = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
5125   else
5126     track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
5127
5128   if ( track && track->trackpoints )
5129   {
5130     struct LatLon average, maxmin[2] = { {0,0}, {0,0} };
5131     VikCoord coord;
5132     trw_layer_find_maxmin_tracks ( NULL, track, maxmin );
5133     average.lat = (maxmin[0].lat+maxmin[1].lat)/2;
5134     average.lon = (maxmin[0].lon+maxmin[1].lon)/2;
5135     vik_coord_load_from_latlon ( &coord, vtl->coord_mode, &average );
5136     goto_coord ( values[MA_VLP], vtl, values[MA_VVP], &coord);
5137   }
5138 }
5139
5140 static void trw_layer_convert_track_route ( menu_array_sublayer values )
5141 {
5142   VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
5143   VikTrack *trk;
5144   if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5145     trk = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
5146   else
5147     trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
5148
5149   if ( !trk )
5150     return;
5151
5152   // Converting a track to a route can be a bit more complicated,
5153   //  so give a chance to change our minds:
5154   if ( !trk->is_route &&
5155        ( ( vik_track_get_segment_count ( trk ) > 1 ) ||
5156          ( vik_track_get_average_speed ( trk ) > 0.0 ) ) ) {
5157
5158     if ( ! a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
5159                                 _("Converting a track to a route removes extra track data such as segments, timestamps, etc...\nDo you want to continue?"), NULL ) )
5160       return;
5161 }
5162
5163   // Copy it
5164   VikTrack *trk_copy = vik_track_copy ( trk, TRUE );
5165
5166   // Convert
5167   trk_copy->is_route = !trk_copy->is_route;
5168
5169   // ATM can't set name to self - so must create temporary copy
5170   gchar *name = g_strdup ( trk_copy->name );
5171
5172   // Delete old one and then add new one
5173   if ( trk->is_route ) {
5174     vik_trw_layer_delete_route ( vtl, trk );
5175     vik_trw_layer_add_track ( vtl, name, trk_copy );
5176   }
5177   else {
5178     // Extra route conversion bits...
5179     vik_track_merge_segments ( trk_copy );
5180     vik_track_to_routepoints ( trk_copy );
5181
5182     vik_trw_layer_delete_track ( vtl, trk );
5183     vik_trw_layer_add_route ( vtl, name, trk_copy );
5184   }
5185   g_free ( name );
5186
5187   // Update in case color of track / route changes when moving between sublayers
5188   vik_layer_emit_update ( VIK_LAYER(vtl) );
5189 }
5190
5191 static void trw_layer_anonymize_times ( menu_array_sublayer values )
5192 {
5193   VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
5194   VikTrack *track;
5195   if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5196     track = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
5197   else
5198     track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
5199
5200   if ( track )
5201     vik_track_anonymize_times ( track );
5202 }
5203
5204 static void trw_layer_extend_track_end ( menu_array_sublayer values )
5205 {
5206   VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
5207   VikTrack *track;
5208   if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5209     track = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
5210   else
5211     track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
5212
5213   if ( !track )
5214     return;
5215
5216   vtl->current_track = track;
5217   vik_window_enable_layer_tool ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)), VIK_LAYER_TRW, track->is_route ? TOOL_CREATE_ROUTE : TOOL_CREATE_TRACK);
5218
5219   if ( track->trackpoints )
5220     goto_coord ( values[MA_VLP], vtl, values[MA_VVP], &(vik_track_get_tp_last(track)->coord) );
5221 }
5222
5223 /**
5224  * extend a track using route finder
5225  */
5226 static void trw_layer_extend_track_end_route_finder ( menu_array_sublayer values )
5227 {
5228   VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
5229   VikTrack *track = g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
5230   if ( !track )
5231     return;
5232   if ( !track->trackpoints )
5233     return;
5234
5235   vik_window_enable_layer_tool ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)), VIK_LAYER_TRW, TOOL_ROUTE_FINDER );
5236   vtl->route_finder_coord = vik_track_get_tp_last(track)->coord;
5237   vtl->route_finder_current_track = track;
5238   vtl->route_finder_started = TRUE;
5239
5240   goto_coord ( values[MA_VLP], vtl, values[MA_VVP], &vtl->route_finder_coord );
5241 }
5242
5243 /**
5244  *
5245  */
5246 static gboolean trw_layer_dem_test ( VikTrwLayer *vtl, VikLayersPanel *vlp )
5247 {
5248   // If have a vlp then perform a basic test to see if any DEM info available...
5249   if ( vlp ) {
5250     GList *dems = vik_layers_panel_get_all_layers_of_type (vlp, VIK_LAYER_DEM, TRUE); // Includes hidden DEM layer types
5251
5252     if ( !g_list_length(dems) ) {
5253       a_dialog_error_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), _("No DEM layers available, thus no DEM values can be applied.") );
5254       return FALSE;
5255     }
5256   }
5257   return TRUE;
5258 }
5259
5260 /**
5261  * apply_dem_data_common:
5262  *
5263  * A common function for applying the DEM values and reporting the results.
5264  */
5265 static void apply_dem_data_common ( VikTrwLayer *vtl, VikLayersPanel *vlp, VikTrack *track, gboolean skip_existing_elevations )
5266 {
5267   if ( !trw_layer_dem_test ( vtl, vlp ) )
5268     return;
5269
5270   gulong changed = vik_track_apply_dem_data ( track, skip_existing_elevations );
5271   // Inform user how much was changed
5272   gchar str[64];
5273   const gchar *tmp_str = ngettext("%ld point adjusted", "%ld points adjusted", changed);
5274   g_snprintf(str, 64, tmp_str, changed);
5275   a_dialog_info_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), str);
5276 }
5277
5278 static void trw_layer_apply_dem_data_all ( menu_array_sublayer values )
5279 {
5280   VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
5281   VikTrack *track;
5282   if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5283     track = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
5284   else
5285     track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
5286
5287   if ( track )
5288     apply_dem_data_common ( vtl, values[MA_VLP], track, FALSE );
5289 }
5290
5291 static void trw_layer_apply_dem_data_only_missing ( menu_array_sublayer values )
5292 {
5293   VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
5294   VikTrack *track;
5295   if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5296     track = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
5297   else
5298     track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
5299
5300   if ( track )
5301     apply_dem_data_common ( vtl, values[MA_VLP], track, TRUE );
5302 }
5303
5304 /**
5305  * smooth_it:
5306  *
5307  * A common function for applying the elevation smoothing and reporting the results.
5308  */
5309 static void smooth_it ( VikTrwLayer *vtl, VikTrack *track, gboolean flat )
5310 {
5311   gulong changed = vik_track_smooth_missing_elevation_data ( track, flat );
5312   // Inform user how much was changed
5313   gchar str[64];
5314   const gchar *tmp_str = ngettext("%ld point adjusted", "%ld points adjusted", changed);
5315   g_snprintf(str, 64, tmp_str, changed);
5316   a_dialog_info_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), str);
5317 }
5318
5319 /**
5320  *
5321  */
5322 static void trw_layer_missing_elevation_data_interp ( menu_array_sublayer values )
5323 {
5324   VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
5325   VikTrack *track;
5326   if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5327     track = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
5328   else
5329     track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
5330
5331   if ( !track )
5332     return;
5333
5334   smooth_it ( vtl, track, FALSE );
5335 }
5336
5337 static void trw_layer_missing_elevation_data_flat ( menu_array_sublayer values )
5338 {
5339   VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
5340   VikTrack *track;
5341   if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5342     track = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
5343   else
5344     track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
5345
5346   if ( !track )
5347     return;
5348
5349   smooth_it ( vtl, track, TRUE );
5350 }
5351
5352 /**
5353  * Commonal helper function
5354  */
5355 static void wp_changed_message ( VikTrwLayer *vtl, gint changed )
5356 {
5357   gchar str[64];
5358   const gchar *tmp_str = ngettext("%ld waypoint changed", "%ld waypoints changed", changed);
5359   g_snprintf(str, 64, tmp_str, changed);
5360   a_dialog_info_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), str);
5361 }
5362
5363 static void trw_layer_apply_dem_data_wpt_all ( menu_array_sublayer values )
5364 {
5365   VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
5366   VikLayersPanel *vlp = (VikLayersPanel *)values[MA_VLP];
5367
5368   if ( !trw_layer_dem_test ( vtl, vlp ) )
5369     return;
5370
5371   gint changed = 0;
5372   if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_WAYPOINT ) {
5373     // Single Waypoint
5374     VikWaypoint *wp = (VikWaypoint *) g_hash_table_lookup ( vtl->waypoints, values[MA_SUBLAYER_ID] );
5375     if ( wp )
5376       changed = (gint)vik_waypoint_apply_dem_data ( wp, FALSE );
5377   }
5378   else {
5379     // All waypoints
5380     GHashTableIter iter;
5381     gpointer key, value;
5382
5383     g_hash_table_iter_init ( &iter, vtl->waypoints );
5384     while ( g_hash_table_iter_next (&iter, &key, &value) ) {
5385       VikWaypoint *wp = VIK_WAYPOINT(value);
5386       changed = changed + (gint)vik_waypoint_apply_dem_data ( wp, FALSE );
5387     }
5388   }
5389   wp_changed_message ( vtl, changed );
5390 }
5391
5392 static void trw_layer_apply_dem_data_wpt_only_missing ( menu_array_sublayer values )
5393 {
5394   VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
5395   VikLayersPanel *vlp = (VikLayersPanel *)values[MA_VLP];
5396
5397   if ( !trw_layer_dem_test ( vtl, vlp ) )
5398     return;
5399
5400   gint changed = 0;
5401   if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_WAYPOINT ) {
5402     // Single Waypoint
5403     VikWaypoint *wp = (VikWaypoint *) g_hash_table_lookup ( vtl->waypoints, values[MA_SUBLAYER_ID] );
5404     if ( wp )
5405       changed = (gint)vik_waypoint_apply_dem_data ( wp, TRUE );
5406   }
5407   else {
5408     // All waypoints
5409     GHashTableIter iter;
5410     gpointer key, value;
5411
5412     g_hash_table_iter_init ( &iter, vtl->waypoints );
5413     while ( g_hash_table_iter_next (&iter, &key, &value) ) {
5414       VikWaypoint *wp = VIK_WAYPOINT(value);
5415       changed = changed + (gint)vik_waypoint_apply_dem_data ( wp, TRUE );
5416     }
5417   }
5418   wp_changed_message ( vtl, changed );
5419 }
5420
5421 static void trw_layer_goto_track_endpoint ( menu_array_sublayer values )
5422 {
5423   VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
5424   VikTrack *track;
5425   if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5426     track = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
5427   else
5428     track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
5429
5430   if ( !track )
5431     return;
5432   if ( !track->trackpoints )
5433     return;
5434   goto_coord ( values[MA_VLP], vtl, values[MA_VVP], &(vik_track_get_tp_last(track)->coord));
5435 }
5436
5437 static void trw_layer_goto_track_max_speed ( menu_array_sublayer values )
5438 {
5439   VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
5440   VikTrack *track;
5441   if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5442     track = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
5443   else
5444     track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
5445
5446   if ( !track )
5447     return;
5448
5449   VikTrackpoint* vtp = vik_track_get_tp_by_max_speed ( track );
5450   if ( !vtp )
5451     return;
5452   goto_coord ( values[MA_VLP], vtl, values[MA_VVP], &(vtp->coord));
5453 }
5454
5455 static void trw_layer_goto_track_max_alt ( menu_array_sublayer values )
5456 {
5457   VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
5458   VikTrack *track;
5459   if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5460     track = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
5461   else
5462     track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
5463
5464   if ( !track )
5465     return;
5466
5467   VikTrackpoint* vtp = vik_track_get_tp_by_max_alt ( track );
5468   if ( !vtp )
5469     return;
5470   goto_coord ( values[MA_VLP], vtl, values[MA_VVP], &(vtp->coord));
5471 }
5472
5473 static void trw_layer_goto_track_min_alt ( menu_array_sublayer values )
5474 {
5475   VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
5476   VikTrack *track;
5477   if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5478     track = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
5479   else
5480     track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
5481
5482   if ( !track )
5483     return;
5484
5485   VikTrackpoint* vtp = vik_track_get_tp_by_min_alt ( track );
5486   if ( !vtp )
5487     return;
5488   goto_coord ( values[MA_VLP], vtl, values[MA_VVP], &(vtp->coord));
5489 }
5490
5491 /*
5492  * Automatically change the viewport to center on the track and zoom to see the extent of the track
5493  */
5494 static void trw_layer_auto_track_view ( menu_array_sublayer values )
5495 {
5496   VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
5497   VikTrack *trk;
5498   if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5499     trk = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
5500   else
5501     trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
5502
5503   if ( trk && trk->trackpoints )
5504   {
5505     struct LatLon maxmin[2] = { {0,0}, {0,0} };
5506     trw_layer_find_maxmin_tracks ( NULL, trk, maxmin );
5507     trw_layer_zoom_to_show_latlons ( vtl, values[MA_VVP], maxmin );
5508     if ( values[MA_VLP] )
5509       vik_layers_panel_emit_update ( VIK_LAYERS_PANEL(values[MA_VLP]) );
5510     else
5511       vik_layer_emit_update ( VIK_LAYER(vtl) );
5512   }
5513 }
5514
5515 /*
5516  * Refine the selected track/route with a routing engine.
5517  * The routing engine is selected by the user, when requestiong the job.
5518  */
5519 static void trw_layer_route_refine ( menu_array_sublayer values )
5520 {
5521   static gint last_engine = 0;
5522   VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
5523   VikTrack *trk;
5524
5525   if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5526     trk = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
5527   else
5528     trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
5529
5530   if ( trk && trk->trackpoints )
5531   {
5532     /* Check size of the route */
5533     int nb = vik_track_get_tp_count(trk);
5534     if (nb > 100) {
5535       GtkWidget *dialog = gtk_message_dialog_new (VIK_GTK_WINDOW_FROM_LAYER (vtl),
5536                                                   GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
5537                                                   GTK_MESSAGE_WARNING,
5538                                                   GTK_BUTTONS_OK_CANCEL,
5539                                                   _("Refining a track with many points (%d) is unlikely to yield sensible results. Do you want to Continue?"),
5540                                                   nb);
5541       gint response = gtk_dialog_run ( GTK_DIALOG(dialog) );
5542       gtk_widget_destroy ( dialog );
5543       if (response != GTK_RESPONSE_OK )
5544         return;
5545     }
5546     /* Select engine from dialog */
5547     GtkWidget *dialog = gtk_dialog_new_with_buttons (_("Refine Route with Routing Engine..."),
5548                                                   VIK_GTK_WINDOW_FROM_LAYER (vtl),
5549                                                   GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
5550                                                   GTK_STOCK_CANCEL,
5551                                                   GTK_RESPONSE_REJECT,
5552                                                   GTK_STOCK_OK,
5553                                                   GTK_RESPONSE_ACCEPT,
5554                                                   NULL);
5555     GtkWidget *label = gtk_label_new ( _("Select routing engine") );
5556     gtk_widget_show_all(label);
5557
5558     gtk_box_pack_start ( GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), label, TRUE, TRUE, 0 );
5559
5560     GtkWidget * combo = vik_routing_ui_selector_new ( (Predicate)vik_routing_engine_supports_refine, NULL );
5561     gtk_combo_box_set_active (GTK_COMBO_BOX (combo), last_engine);
5562     gtk_widget_show_all(combo);
5563
5564     gtk_box_pack_start ( GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), combo, TRUE, TRUE, 0 );
5565
5566     gtk_dialog_set_default_response ( GTK_DIALOG(dialog), GTK_RESPONSE_ACCEPT );
5567
5568     if ( gtk_dialog_run ( GTK_DIALOG(dialog) ) == GTK_RESPONSE_ACCEPT )
5569     {
5570         /* Dialog validated: retrieve selected engine and do the job */
5571         last_engine = gtk_combo_box_get_active ( GTK_COMBO_BOX(combo) );
5572         VikRoutingEngine *routing = vik_routing_ui_selector_get_nth (combo, last_engine);
5573
5574         /* Change cursor */
5575         vik_window_set_busy_cursor ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)) );
5576
5577         /* Force saving track */
5578         /* FIXME: remove or rename this hack */
5579         vtl->route_finder_check_added_track = TRUE;
5580
5581         /* the job */
5582         vik_routing_engine_refine (routing, vtl, trk);
5583
5584         /* FIXME: remove or rename this hack */
5585         if ( vtl->route_finder_added_track )
5586           vik_track_calculate_bounds ( vtl->route_finder_added_track );
5587
5588         vtl->route_finder_added_track = NULL;
5589         vtl->route_finder_check_added_track = FALSE;
5590
5591         vik_layer_emit_update ( VIK_LAYER(vtl) );
5592
5593         /* Restore cursor */
5594         vik_window_clear_busy_cursor ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)) );
5595     }
5596     gtk_widget_destroy ( dialog );
5597   }
5598 }
5599
5600 static void trw_layer_edit_trackpoint ( menu_array_sublayer values )
5601 {
5602   VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
5603   trw_layer_tpwin_init ( vtl );
5604 }
5605
5606 /*************************************
5607  * merge/split by time routines 
5608  *************************************/
5609
5610 /* called for each key in track hash table.
5611  * If the current track has the same time stamp type, add it to the result,
5612  * except the one pointed by "exclude".
5613  * set exclude to NULL if there is no exclude to check.
5614  * Note that the result is in reverse (for performance reasons).
5615  */
5616 typedef struct {
5617   GList **result;
5618   VikTrack *exclude;
5619   gboolean with_timestamps;
5620 } twt_udata;
5621 static void find_tracks_with_timestamp_type(gpointer key, gpointer value, gpointer udata)
5622 {
5623   twt_udata *user_data = udata;
5624   VikTrackpoint *p1, *p2;
5625   VikTrack *trk = VIK_TRACK(value);
5626   if (trk == user_data->exclude) {
5627     return;
5628   }
5629
5630   if (trk->trackpoints) {
5631     p1 = vik_track_get_tp_first(trk);
5632     p2 = vik_track_get_tp_last(trk);
5633
5634     if ( user_data->with_timestamps ) {
5635       if (!p1->has_timestamp || !p2->has_timestamp) {
5636         return;
5637       }
5638     }
5639     else {
5640       // Don't add tracks with timestamps when getting non timestamp tracks
5641       if (p1->has_timestamp || p2->has_timestamp) {
5642         return;
5643       }
5644     }
5645   }
5646
5647   *(user_data->result) = g_list_prepend(*(user_data->result), key);
5648 }
5649
5650 /* called for each key in track hash table. if original track user_data[1] is close enough
5651  * to the passed one, add it to list in user_data[0] 
5652  */
5653 static void find_nearby_tracks_by_time (gpointer key, gpointer value, gpointer user_data)
5654 {
5655   time_t t1, t2;
5656   VikTrackpoint *p1, *p2;
5657   VikTrack *trk = VIK_TRACK(value);
5658
5659   GList **nearby_tracks = ((gpointer *)user_data)[0];
5660
5661   /* outline: 
5662    * detect reasons for not merging, and return
5663    * if no reason is found not to merge, then do it.
5664    */
5665
5666   twt_udata *udata = user_data;
5667   // Exclude the original track from the compiled list
5668   if (trk == udata->exclude) {
5669     return;
5670   }
5671
5672   t1 = vik_track_get_tp_first(trk)->timestamp;
5673   t2 = vik_track_get_tp_last(trk)->timestamp;
5674
5675   if (trk->trackpoints) {
5676     p1 = vik_track_get_tp_first(trk);
5677     p2 = vik_track_get_tp_last(trk);
5678
5679     if (!p1->has_timestamp || !p2->has_timestamp) {
5680       //g_print("no timestamp\n");
5681       return;
5682     }
5683
5684     guint threshold = GPOINTER_TO_UINT (((gpointer *)user_data)[2]);
5685     //g_print("Got track named %s, times %d, %d\n", trk->name, p1->timestamp, p2->timestamp);
5686     if (! (abs(t1 - p2->timestamp) < threshold ||
5687         /*  p1 p2      t1 t2 */
5688            abs(p1->timestamp - t2) < threshold)
5689         /*  t1 t2      p1 p2 */
5690         ) {
5691       return;
5692     }
5693   }
5694
5695   *nearby_tracks = g_list_prepend(*nearby_tracks, value);
5696 }
5697
5698 /* comparison function used to sort tracks; a and b are hash table keys */
5699 /* Not actively used - can be restored if needed
5700 static gint track_compare(gconstpointer a, gconstpointer b, gpointer user_data)
5701 {
5702   GHashTable *tracks = user_data;
5703   time_t t1, t2;
5704
5705   t1 = VIK_TRACKPOINT(VIK_TRACK(g_hash_table_lookup(tracks, a))->trackpoints->data)->timestamp;
5706   t2 = VIK_TRACKPOINT(VIK_TRACK(g_hash_table_lookup(tracks, b))->trackpoints->data)->timestamp;
5707   
5708   if (t1 < t2) return -1;
5709   if (t1 > t2) return 1;
5710   return 0;
5711 }
5712 */
5713
5714 /* comparison function used to sort trackpoints */
5715 static gint trackpoint_compare(gconstpointer a, gconstpointer b)
5716 {
5717   time_t t1 = VIK_TRACKPOINT(a)->timestamp, t2 = VIK_TRACKPOINT(b)->timestamp;
5718   
5719   if (t1 < t2) return -1;
5720   if (t1 > t2) return 1;
5721   return 0;
5722 }
5723
5724 /**
5725  * comparison function which can be used to sort tracks or waypoints by name
5726  */
5727 static gint sort_alphabetically (gconstpointer a, gconstpointer b, gpointer user_data)
5728 {
5729   const gchar* namea = (const gchar*) a;
5730   const gchar* nameb = (const gchar*) b;
5731   if ( namea == NULL || nameb == NULL)
5732     return 0;
5733   else
5734     // Same sort method as used in the vik_treeview_*_alphabetize functions
5735     return strcmp ( namea, nameb );
5736 }
5737
5738 /**
5739  * Attempt to merge selected track with other tracks specified by the user
5740  * Tracks to merge with must be of the same 'type' as the selected track -
5741  *  either all with timestamps, or all without timestamps
5742  */
5743 static void trw_layer_merge_with_other ( menu_array_sublayer values )
5744 {
5745   VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
5746   GList *other_tracks = NULL;
5747   GHashTable *ght_tracks;
5748   if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5749     ght_tracks = vtl->routes;
5750   else
5751     ght_tracks = vtl->tracks;
5752
5753   VikTrack *track = (VikTrack *) g_hash_table_lookup ( ght_tracks, values[MA_SUBLAYER_ID] );
5754
5755   if ( !track )
5756     return;
5757
5758   if ( !track->trackpoints )
5759     return;
5760
5761   twt_udata udata;
5762   udata.result = &other_tracks;
5763   udata.exclude = track;
5764   // Allow merging with 'similar' time type time tracks
5765   // i.e. either those times, or those without
5766   udata.with_timestamps = vik_track_get_tp_first(track)->has_timestamp;
5767
5768   g_hash_table_foreach(ght_tracks, find_tracks_with_timestamp_type, (gpointer)&udata);
5769   other_tracks = g_list_reverse(other_tracks);
5770
5771   if ( !other_tracks ) {
5772     if ( udata.with_timestamps )
5773       a_dialog_error_msg(VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Failed. No other tracks with timestamps in this layer found"));
5774     else
5775       a_dialog_error_msg(VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Failed. No other tracks without timestamps in this layer found"));
5776     return;
5777   }
5778
5779   // Sort alphabetically for user presentation
5780   // Convert into list of names for usage with dialog function
5781   // TODO: Need to consider how to work best when we can have multiple tracks the same name...
5782   GList *other_tracks_names = NULL;
5783   GList *iter = g_list_first ( other_tracks );
5784   while ( iter ) {
5785     other_tracks_names = g_list_append ( other_tracks_names, VIK_TRACK(g_hash_table_lookup (ght_tracks, iter->data))->name );
5786     iter = g_list_next ( iter );
5787   }
5788
5789   other_tracks_names = g_list_sort_with_data (other_tracks_names, sort_alphabetically, NULL);
5790
5791   GList *merge_list = a_dialog_select_from_list(VIK_GTK_WINDOW_FROM_LAYER(vtl),
5792                                                 other_tracks_names,
5793                                                 TRUE,
5794                                                 _("Merge with..."),
5795                                                 track->is_route ? _("Select route to merge with") : _("Select track to merge with"));
5796   g_list_free(other_tracks);
5797   g_list_free(other_tracks_names);
5798
5799   if (merge_list)
5800   {
5801     GList *l;
5802     for (l = merge_list; l != NULL; l = g_list_next(l)) {
5803       VikTrack *merge_track;
5804       if ( track->is_route )
5805         merge_track = vik_trw_layer_get_route ( vtl, l->data );
5806       else
5807         merge_track = vik_trw_layer_get_track ( vtl, l->data );
5808
5809       if (merge_track) {
5810         vik_track_steal_and_append_trackpoints ( track, merge_track );
5811         if ( track->is_route )
5812           vik_trw_layer_delete_route (vtl, merge_track);
5813         else
5814           vik_trw_layer_delete_track (vtl, merge_track);
5815         track->trackpoints = g_list_sort(track->trackpoints, trackpoint_compare);
5816       }
5817     }
5818     for (l = merge_list; l != NULL; l = g_list_next(l))
5819       g_free(l->data);
5820     g_list_free(merge_list);
5821
5822     vik_layer_emit_update( VIK_LAYER(vtl) );
5823   }
5824 }
5825
5826 // c.f. trw_layer_sorted_track_id_by_name_list
5827 //  but don't add the specified track to the list (normally current track)
5828 static void trw_layer_sorted_track_id_by_name_list_exclude_self (const gpointer id, const VikTrack *trk, gpointer udata)
5829 {
5830   twt_udata *user_data = udata;
5831
5832   // Skip self
5833   if (trk == user_data->exclude) {
5834     return;
5835   }
5836
5837   // Sort named list alphabetically
5838   *(user_data->result) = g_list_insert_sorted_with_data (*(user_data->result), trk->name, sort_alphabetically, NULL);
5839 }
5840
5841 /**
5842  * Join - this allows combining 'tracks' and 'track routes'
5843  *  i.e. doesn't care about whether tracks have consistent timestamps
5844  * ATM can only append one track at a time to the currently selected track
5845  */
5846 static void trw_layer_append_track ( menu_array_sublayer values )
5847 {
5848
5849   VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
5850   VikTrack *trk;
5851   GHashTable *ght_tracks;
5852   if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5853     ght_tracks = vtl->routes;
5854   else
5855     ght_tracks = vtl->tracks;
5856
5857   trk = (VikTrack *) g_hash_table_lookup ( ght_tracks, values[MA_SUBLAYER_ID] );
5858
5859   if ( !trk )
5860     return;
5861
5862   GList *other_tracks_names = NULL;
5863
5864   // Sort alphabetically for user presentation
5865   // Convert into list of names for usage with dialog function
5866   // TODO: Need to consider how to work best when we can have multiple tracks the same name...
5867   twt_udata udata;
5868   udata.result = &other_tracks_names;
5869   udata.exclude = trk;
5870
5871   g_hash_table_foreach(ght_tracks, (GHFunc) trw_layer_sorted_track_id_by_name_list_exclude_self, (gpointer)&udata);
5872
5873   // Note the limit to selecting one track only
5874   //  this is to control the ordering of appending tracks, i.e. the selected track always goes after the current track
5875   //  (otherwise with multiple select the ordering would not be controllable by the user - automatically being alphabetically)
5876   GList *append_list = a_dialog_select_from_list(VIK_GTK_WINDOW_FROM_LAYER(vtl),
5877                                                  other_tracks_names,
5878                                                  FALSE,
5879                                                  trk->is_route ? _("Append Route"): _("Append Track"),
5880                                                  trk->is_route ? _("Select the route to append after the current route") :
5881                                                                  _("Select the track to append after the current track") );
5882
5883   g_list_free(other_tracks_names);
5884
5885   // It's a list, but shouldn't contain more than one other track!
5886   if ( append_list ) {
5887     GList *l;
5888     for (l = append_list; l != NULL; l = g_list_next(l)) {
5889       // TODO: at present this uses the first track found by name,
5890       //  which with potential multiple same named tracks may not be the one selected...
5891       VikTrack *append_track;
5892       if ( trk->is_route )
5893         append_track = vik_trw_layer_get_route ( vtl, l->data );
5894       else
5895         append_track = vik_trw_layer_get_track ( vtl, l->data );
5896
5897       if ( append_track ) {
5898         vik_track_steal_and_append_trackpoints ( trk, append_track );
5899         if ( trk->is_route )
5900           vik_trw_layer_delete_route (vtl, append_track);
5901         else
5902           vik_trw_layer_delete_track (vtl, append_track);
5903       }
5904     }
5905     for (l = append_list; l != NULL; l = g_list_next(l))
5906       g_free(l->data);
5907     g_list_free(append_list);
5908
5909     vik_layer_emit_update( VIK_LAYER(vtl) );
5910   }
5911 }
5912
5913 /**
5914  * Very similar to trw_layer_append_track for joining
5915  * but this allows selection from the 'other' list
5916  * If a track is selected, then is shows routes and joins the selected one
5917  * If a route is selected, then is shows tracks and joins the selected one
5918  */
5919 static void trw_layer_append_other ( menu_array_sublayer values )
5920 {
5921
5922   VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
5923   VikTrack *trk;
5924   GHashTable *ght_mykind, *ght_others;
5925   if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE ) {
5926     ght_mykind = vtl->routes;
5927     ght_others = vtl->tracks;
5928   }
5929   else {
5930     ght_mykind = vtl->tracks;
5931     ght_others = vtl->routes;
5932   }
5933
5934   trk = (VikTrack *) g_hash_table_lookup ( ght_mykind, values[MA_SUBLAYER_ID] );
5935
5936   if ( !trk )
5937     return;
5938
5939   GList *other_tracks_names = NULL;
5940
5941   // Sort alphabetically for user presentation
5942   // Convert into list of names for usage with dialog function
5943   // TODO: Need to consider how to work best when we can have multiple tracks the same name...
5944   twt_udata udata;
5945   udata.result = &other_tracks_names;
5946   udata.exclude = trk;
5947
5948   g_hash_table_foreach(ght_others, (GHFunc) trw_layer_sorted_track_id_by_name_list_exclude_self, (gpointer)&udata);
5949
5950   // Note the limit to selecting one track only
5951   //  this is to control the ordering of appending tracks, i.e. the selected track always goes after the current track
5952   //  (otherwise with multiple select the ordering would not be controllable by the user - automatically being alphabetically)
5953   GList *append_list = a_dialog_select_from_list(VIK_GTK_WINDOW_FROM_LAYER(vtl),
5954                                                  other_tracks_names,
5955                                                  FALSE,
5956                                                  trk->is_route ? _("Append Track"): _("Append Route"),
5957                                                  trk->is_route ? _("Select the track to append after the current route") :
5958                                                                  _("Select the route to append after the current track") );
5959
5960   g_list_free(other_tracks_names);
5961
5962   // It's a list, but shouldn't contain more than one other track!
5963   if ( append_list ) {
5964     GList *l;
5965     for (l = append_list; l != NULL; l = g_list_next(l)) {
5966       // TODO: at present this uses the first track found by name,
5967       //  which with potential multiple same named tracks may not be the one selected...
5968
5969       // Get FROM THE OTHER TYPE list
5970       VikTrack *append_track;
5971       if ( trk->is_route )
5972         append_track = vik_trw_layer_get_track ( vtl, l->data );
5973       else
5974         append_track = vik_trw_layer_get_route ( vtl, l->data );
5975
5976       if ( append_track ) {
5977
5978         if ( !append_track->is_route &&
5979              ( ( vik_track_get_segment_count ( append_track ) > 1 ) ||
5980                ( vik_track_get_average_speed ( append_track ) > 0.0 ) ) ) {
5981
5982           if ( a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
5983                                       _("Converting a track to a route removes extra track data such as segments, timestamps, etc...\nDo you want to continue?"), NULL ) ) {
5984             vik_track_merge_segments ( append_track );
5985             vik_track_to_routepoints ( append_track );
5986           }
5987           else {
5988             break;
5989           }
5990         }
5991
5992         vik_track_steal_and_append_trackpoints ( trk, append_track );
5993
5994         // Delete copied which is FROM THE OTHER TYPE list
5995         if ( trk->is_route )
5996           vik_trw_layer_delete_track (vtl, append_track);
5997         else
5998           vik_trw_layer_delete_route (vtl, append_track);
5999       }
6000     }
6001     for (l = append_list; l != NULL; l = g_list_next(l))
6002       g_free(l->data);
6003     g_list_free(append_list);
6004     vik_layer_emit_update( VIK_LAYER(vtl) );
6005   }
6006 }
6007
6008 /* merge by segments */
6009 static void trw_layer_merge_by_segment ( menu_array_sublayer values )
6010 {
6011   VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
6012   VikTrack *trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
6013   guint segments = vik_track_merge_segments ( trk );
6014   // NB currently no need to redraw as segments not actually shown on the display
6015   // However inform the user of what happened:
6016   gchar str[64];
6017   const gchar *tmp_str = ngettext("%d segment merged", "%d segments merged", segments);
6018   g_snprintf(str, 64, tmp_str, segments);
6019   a_dialog_info_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), str );
6020 }
6021
6022 /* merge by time routine */
6023 static void trw_layer_merge_by_timestamp ( menu_array_sublayer values )
6024 {
6025   VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
6026
6027   //time_t t1, t2;
6028
6029   GList *tracks_with_timestamp = NULL;
6030   VikTrack *orig_trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
6031   if (orig_trk->trackpoints &&
6032       !vik_track_get_tp_first(orig_trk)->has_timestamp) {
6033     a_dialog_error_msg(VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Failed. This track does not have timestamp"));
6034     return;
6035   }
6036
6037   twt_udata udata;
6038   udata.result = &tracks_with_timestamp;
6039   udata.exclude = orig_trk;
6040   udata.with_timestamps = TRUE;
6041   g_hash_table_foreach(vtl->tracks, find_tracks_with_timestamp_type, (gpointer)&udata);
6042   tracks_with_timestamp = g_list_reverse(tracks_with_timestamp);
6043
6044   if (!tracks_with_timestamp) {
6045     a_dialog_error_msg(VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Failed. No other track in this layer has timestamp"));
6046     return;
6047   }
6048   g_list_free(tracks_with_timestamp);
6049
6050   static guint threshold_in_minutes = 1;
6051   if (!a_dialog_time_threshold(VIK_GTK_WINDOW_FROM_LAYER(vtl),
6052                                _("Merge Threshold..."),
6053                                _("Merge when time between tracks less than:"),
6054                                &threshold_in_minutes)) {
6055     return;
6056   }
6057
6058   // keep attempting to merge all tracks until no merges within the time specified is possible
6059   gboolean attempt_merge = TRUE;
6060   GList *nearby_tracks = NULL;
6061   GList *trps;
6062   static gpointer params[3];
6063
6064   while ( attempt_merge ) {
6065
6066     // Don't try again unless tracks have changed
6067     attempt_merge = FALSE;
6068
6069     trps = orig_trk->trackpoints;
6070     if ( !trps )
6071       return;
6072
6073     if (nearby_tracks) {
6074       g_list_free(nearby_tracks);
6075       nearby_tracks = NULL;
6076     }
6077
6078     params[0] = &nearby_tracks;
6079     params[1] = (gpointer)trps;
6080     params[2] = GUINT_TO_POINTER (threshold_in_minutes*60); // In seconds
6081
6082     /* get a list of adjacent-in-time tracks */
6083     g_hash_table_foreach(vtl->tracks, find_nearby_tracks_by_time, params);
6084
6085     /* merge them */
6086     GList *l = nearby_tracks;
6087     while ( l ) {
6088       /* remove trackpoints from merged track, delete track */
6089       vik_track_steal_and_append_trackpoints ( orig_trk, VIK_TRACK(l->data) );
6090       vik_trw_layer_delete_track (vtl, VIK_TRACK(l->data));
6091
6092       // Tracks have changed, therefore retry again against all the remaining tracks
6093       attempt_merge = TRUE;
6094
6095       l = g_list_next(l);
6096     }
6097
6098     orig_trk->trackpoints = g_list_sort(orig_trk->trackpoints, trackpoint_compare);
6099   }
6100
6101   g_list_free(nearby_tracks);
6102
6103   vik_layer_emit_update( VIK_LAYER(vtl) );
6104 }
6105
6106 /**
6107  * Split a track at the currently selected trackpoint
6108  */
6109 static void trw_layer_split_at_selected_trackpoint ( VikTrwLayer *vtl, gint subtype )
6110 {
6111   if ( !vtl->current_tpl )
6112     return;
6113
6114   if ( vtl->current_tpl->next && vtl->current_tpl->prev ) {
6115     gchar *name = trw_layer_new_unique_sublayer_name(vtl, subtype, vtl->current_tp_track->name);
6116     if ( name ) {
6117       VikTrack *tr = vik_track_copy ( vtl->current_tp_track, FALSE );
6118       GList *newglist = g_list_alloc ();
6119       newglist->prev = NULL;
6120       newglist->next = vtl->current_tpl->next;
6121       newglist->data = vik_trackpoint_copy(VIK_TRACKPOINT(vtl->current_tpl->data));
6122       tr->trackpoints = newglist;
6123
6124       vtl->current_tpl->next->prev = newglist; /* end old track here */
6125       vtl->current_tpl->next = NULL;
6126
6127       // Bounds of the selected track changed due to the split
6128       vik_track_calculate_bounds ( vtl->current_tp_track );
6129
6130       vtl->current_tpl = newglist; /* change tp to first of new track. */
6131       vtl->current_tp_track = tr;
6132
6133       if ( tr->is_route )
6134         vik_trw_layer_add_route ( vtl, name, tr );
6135       else
6136         vik_trw_layer_add_track ( vtl, name, tr );
6137
6138       // Bounds of the new track created by the split
6139       vik_track_calculate_bounds ( tr );
6140
6141       trku_udata udata;
6142       udata.trk  = tr;
6143       udata.uuid = NULL;
6144
6145       // Also need id of newly created track
6146       gpointer *trkf;
6147       if ( tr->is_route )
6148          trkf = g_hash_table_find ( vtl->routes, (GHRFunc) trw_layer_track_find_uuid, &udata );
6149       else
6150          trkf = g_hash_table_find ( vtl->tracks, (GHRFunc) trw_layer_track_find_uuid, &udata );
6151
6152       if ( trkf && udata.uuid )
6153         vtl->current_tp_id = udata.uuid;
6154       else
6155         vtl->current_tp_id = NULL;
6156
6157       vik_layer_emit_update(VIK_LAYER(vtl));
6158     }
6159     g_free ( name );
6160   }
6161 }
6162
6163 /* split by time routine */
6164 static void trw_layer_split_by_timestamp ( menu_array_sublayer values )
6165 {
6166   VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
6167   VikTrack *track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
6168   GList *trps = track->trackpoints;
6169   GList *iter;
6170   GList *newlists = NULL;
6171   GList *newtps = NULL;
6172   static guint thr = 1;
6173
6174   time_t ts, prev_ts;
6175
6176   if ( !trps )
6177     return;
6178
6179   if (!a_dialog_time_threshold(VIK_GTK_WINDOW_FROM_LAYER(vtl), 
6180                                _("Split Threshold..."), 
6181                                _("Split when time between trackpoints exceeds:"), 
6182                                &thr)) {
6183     return;
6184   }
6185
6186   /* iterate through trackpoints, and copy them into new lists without touching original list */
6187   prev_ts = VIK_TRACKPOINT(trps->data)->timestamp;
6188   iter = trps;
6189
6190   while (iter) {
6191     ts = VIK_TRACKPOINT(iter->data)->timestamp;
6192
6193     // Check for unordered time points - this is quite a rare occurence - unless one has reversed a track.
6194     if (ts < prev_ts) {
6195       gchar tmp_str[64];
6196       strftime ( tmp_str, sizeof(tmp_str), "%c", localtime(&ts) );
6197       if ( a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
6198                                 _("Can not split track due to trackpoints not ordered in time - such as at %s.\n\nGoto this trackpoint?"),
6199                                 tmp_str ) ) {
6200         goto_coord ( values[MA_VLP], vtl, values[MA_VVP], &(VIK_TRACKPOINT(iter->data)->coord) );
6201       }
6202       return;
6203     }
6204
6205     if (ts - prev_ts > thr*60) {
6206       /* flush accumulated trackpoints into new list */
6207       newlists = g_list_append(newlists, g_list_reverse(newtps));
6208       newtps = NULL;
6209     }
6210
6211     /* accumulate trackpoint copies in newtps, in reverse order */
6212     newtps = g_list_prepend(newtps, vik_trackpoint_copy(VIK_TRACKPOINT(iter->data)));
6213     prev_ts = ts;
6214     iter = g_list_next(iter);
6215   }
6216   if (newtps) {
6217       newlists = g_list_append(newlists, g_list_reverse(newtps));
6218   }
6219
6220   /* put lists of trackpoints into tracks */
6221   iter = newlists;
6222   // Only bother updating if the split results in new tracks
6223   if (g_list_length (newlists) > 1) {
6224     while (iter) {
6225       gchar *new_tr_name;
6226       VikTrack *tr;
6227
6228       tr = vik_track_copy ( track, FALSE );
6229       tr->trackpoints = (GList *)(iter->data);
6230
6231       new_tr_name = trw_layer_new_unique_sublayer_name ( vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, track->name);
6232       vik_trw_layer_add_track(vtl, new_tr_name, tr);
6233       g_free ( new_tr_name );
6234       vik_track_calculate_bounds ( tr );
6235       iter = g_list_next(iter);
6236     }
6237     // Remove original track and then update the display
6238     vik_trw_layer_delete_track (vtl, track);
6239     vik_layer_emit_update(VIK_LAYER(vtl));
6240   }
6241   g_list_free(newlists);
6242 }
6243
6244 /**
6245  * Split a track by the number of points as specified by the user
6246  */
6247 static void trw_layer_split_by_n_points ( menu_array_sublayer values )
6248 {
6249   VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
6250   VikTrack *track;
6251   if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
6252     track = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
6253   else
6254     track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
6255
6256   if ( !track )
6257     return;
6258
6259   // Check valid track
6260   GList *trps = track->trackpoints;
6261   if ( !trps )
6262     return;
6263
6264   gint points = a_dialog_get_positive_number(VIK_GTK_WINDOW_FROM_LAYER(vtl),
6265                                              _("Split Every Nth Point"),
6266                                              _("Split on every Nth point:"),
6267                                              250,   // Default value as per typical limited track capacity of various GPS devices
6268                                              2,     // Min
6269                                              65536, // Max
6270                                              5);    // Step
6271   // Was a valid number returned?
6272   if (!points)
6273     return;
6274
6275   // Now split...
6276   GList *iter;
6277   GList *newlists = NULL;
6278   GList *newtps = NULL;
6279   gint count = 0;
6280   iter = trps;
6281
6282   while (iter) {
6283     /* accumulate trackpoint copies in newtps, in reverse order */
6284     newtps = g_list_prepend(newtps, vik_trackpoint_copy(VIK_TRACKPOINT(iter->data)));
6285     count++;
6286     if (count >= points) {
6287       /* flush accumulated trackpoints into new list */
6288       newlists = g_list_append(newlists, g_list_reverse(newtps));
6289       newtps = NULL;
6290       count = 0;
6291     }
6292     iter = g_list_next(iter);
6293   }
6294
6295   // If there is a remaining chunk put that into the new split list
6296   // This may well be the whole track if no split points were encountered
6297   if (newtps) {
6298       newlists = g_list_append(newlists, g_list_reverse(newtps));
6299   }
6300
6301   /* put lists of trackpoints into tracks */
6302   iter = newlists;
6303   // Only bother updating if the split results in new tracks
6304   if (g_list_length (newlists) > 1) {
6305     while (iter) {
6306       gchar *new_tr_name;
6307       VikTrack *tr;
6308
6309       tr = vik_track_copy ( track, FALSE );
6310       tr->trackpoints = (GList *)(iter->data);
6311
6312       if ( track->is_route ) {
6313         new_tr_name = trw_layer_new_unique_sublayer_name ( vtl, VIK_TRW_LAYER_SUBLAYER_ROUTE, track->name);
6314         vik_trw_layer_add_route(vtl, new_tr_name, tr);
6315       }
6316       else {
6317         new_tr_name = trw_layer_new_unique_sublayer_name ( vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, track->name);
6318         vik_trw_layer_add_track(vtl, new_tr_name, tr);
6319       }
6320       g_free ( new_tr_name );
6321       vik_track_calculate_bounds ( tr );
6322
6323       iter = g_list_next(iter);
6324     }
6325     // Remove original track and then update the display
6326     if ( track->is_route )
6327       vik_trw_layer_delete_route (vtl, track);
6328     else
6329       vik_trw_layer_delete_track (vtl, track);
6330     vik_layer_emit_update(VIK_LAYER(vtl));
6331   }
6332   g_list_free(newlists);
6333 }
6334
6335 /**
6336  * Split a track at the currently selected trackpoint
6337  */
6338 static void trw_layer_split_at_trackpoint ( menu_array_sublayer values )
6339 {
6340   VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
6341   gint subtype = GPOINTER_TO_INT (values[MA_SUBTYPE]);
6342   trw_layer_split_at_selected_trackpoint ( vtl, subtype );
6343 }
6344
6345 /**
6346  * Split a track by its segments
6347  * Routes do not have segments so don't call this for routes
6348  */
6349 static void trw_layer_split_segments ( menu_array_sublayer values )
6350 {
6351   VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
6352   VikTrack *trk = g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
6353
6354   if ( !trk )
6355     return;
6356
6357   guint ntracks;
6358
6359   VikTrack **tracks = vik_track_split_into_segments (trk, &ntracks);
6360   gchar *new_tr_name;
6361   guint i;
6362   for ( i = 0; i < ntracks; i++ ) {
6363     if ( tracks[i] ) {
6364       new_tr_name = trw_layer_new_unique_sublayer_name ( vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, trk->name);
6365       vik_trw_layer_add_track ( vtl, new_tr_name, tracks[i] );
6366       g_free ( new_tr_name );
6367     }
6368   }
6369   if ( tracks ) {
6370     g_free ( tracks );
6371     // Remove original track
6372     vik_trw_layer_delete_track ( vtl, trk );
6373     vik_layer_emit_update ( VIK_LAYER(vtl) );
6374   }
6375   else {
6376     a_dialog_error_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Can not split track as it has no segments"));
6377   }
6378 }
6379 /* end of split/merge routines */
6380
6381 static void trw_layer_trackpoint_selected_delete ( VikTrwLayer *vtl, VikTrack *trk )
6382 {
6383   GList *new_tpl;
6384
6385   // Find available adjacent trackpoint
6386   if ( (new_tpl = vtl->current_tpl->next) || (new_tpl = vtl->current_tpl->prev) ) {
6387     if ( VIK_TRACKPOINT(vtl->current_tpl->data)->newsegment && vtl->current_tpl->next )
6388       VIK_TRACKPOINT(vtl->current_tpl->next->data)->newsegment = TRUE; /* don't concat segments on del */
6389
6390     // Delete current trackpoint
6391     vik_trackpoint_free ( vtl->current_tpl->data );
6392     trk->trackpoints = g_list_delete_link ( trk->trackpoints, vtl->current_tpl );
6393
6394     // Set to current to the available adjacent trackpoint
6395     vtl->current_tpl = new_tpl;
6396
6397     if ( vtl->current_tp_track ) {
6398       vik_track_calculate_bounds ( vtl->current_tp_track );
6399     }
6400   }
6401   else {
6402     // Delete current trackpoint
6403     vik_trackpoint_free ( vtl->current_tpl->data );
6404     trk->trackpoints = g_list_delete_link ( trk->trackpoints, vtl->current_tpl );
6405     trw_layer_cancel_current_tp ( vtl, FALSE );
6406   }
6407 }
6408
6409 /**
6410  * Delete the selected point
6411  */
6412 static void trw_layer_delete_point_selected ( menu_array_sublayer values )
6413 {
6414   VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
6415   VikTrack *trk;
6416   if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
6417     trk = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
6418   else
6419     trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
6420
6421   if ( !trk )
6422     return;
6423
6424   if ( !vtl->current_tpl )
6425     return;
6426
6427   trw_layer_trackpoint_selected_delete ( vtl, trk );
6428
6429   // Track has been updated so update tps:
6430   trw_layer_cancel_tps_of_track ( vtl, trk );
6431
6432   vik_layer_emit_update ( VIK_LAYER(vtl) );
6433 }
6434
6435 /**
6436  * Delete adjacent track points at the same position
6437  * AKA Delete Dulplicates on the Properties Window
6438  */
6439 static void trw_layer_delete_points_same_position ( menu_array_sublayer values )
6440 {
6441   VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
6442   VikTrack *trk;
6443   if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
6444     trk = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
6445   else
6446     trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
6447
6448   if ( !trk )
6449     return;
6450
6451   gulong removed = vik_track_remove_dup_points ( trk );
6452
6453   // Track has been updated so update tps:
6454   trw_layer_cancel_tps_of_track ( vtl, trk );
6455
6456   // Inform user how much was deleted as it's not obvious from the normal view
6457   gchar str[64];
6458   const gchar *tmp_str = ngettext("Deleted %ld point", "Deleted %ld points", removed);
6459   g_snprintf(str, 64, tmp_str, removed);
6460   a_dialog_info_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), str);
6461
6462   vik_layer_emit_update ( VIK_LAYER(vtl) );
6463 }
6464
6465 /**
6466  * Delete adjacent track points with the same timestamp
6467  * Normally new tracks that are 'routes' won't have any timestamps so should be OK to clean up the track
6468  */
6469 static void trw_layer_delete_points_same_time ( menu_array_sublayer values )
6470 {
6471   VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
6472   VikTrack *trk;
6473   if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
6474     trk = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
6475   else
6476     trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
6477
6478   if ( !trk )
6479     return;
6480
6481   gulong removed = vik_track_remove_same_time_points ( trk );
6482
6483   // Track has been updated so update tps:
6484   trw_layer_cancel_tps_of_track ( vtl, trk );
6485
6486   // Inform user how much was deleted as it's not obvious from the normal view
6487   gchar str[64];
6488   const gchar *tmp_str = ngettext("Deleted %ld point", "Deleted %ld points", removed);
6489   g_snprintf(str, 64, tmp_str, removed);
6490   a_dialog_info_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), str);
6491
6492   vik_layer_emit_update ( VIK_LAYER(vtl) );
6493 }
6494
6495 /**
6496  * Insert a point
6497  */
6498 static void trw_layer_insert_point_after ( menu_array_sublayer values )
6499 {
6500   VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
6501   VikTrack *track;
6502   if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
6503     track = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
6504   else
6505     track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
6506
6507   if ( ! track )
6508     return;
6509
6510   trw_layer_insert_tp_beside_current_tp ( vtl, FALSE );
6511
6512   vik_layer_emit_update ( VIK_LAYER(vtl) );
6513 }
6514
6515 static void trw_layer_insert_point_before ( menu_array_sublayer values )
6516 {
6517   VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
6518   VikTrack *track;
6519   if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
6520     track = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
6521   else
6522     track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
6523
6524   if ( ! track )
6525     return;
6526
6527   trw_layer_insert_tp_beside_current_tp ( vtl, TRUE );
6528
6529   vik_layer_emit_update ( VIK_LAYER(vtl) );
6530 }
6531
6532 /**
6533  * Reverse a track
6534  */
6535 static void trw_layer_reverse ( menu_array_sublayer values )
6536 {
6537   VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
6538   VikTrack *track;
6539   if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
6540     track = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
6541   else
6542     track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
6543
6544   if ( ! track )
6545     return;
6546
6547   vik_track_reverse ( track );
6548  
6549   vik_layer_emit_update ( VIK_LAYER(vtl) );
6550 }
6551
6552 /**
6553  * Open a diary at the specified date
6554  */
6555 static void trw_layer_diary_open ( VikTrwLayer *vtl, const gchar *date_str )
6556 {
6557   GError *err = NULL;
6558   gchar *cmd = g_strdup_printf ( "%s%s", "rednotebook --date=", date_str );
6559   if ( ! g_spawn_command_line_async ( cmd, &err ) ) {
6560     a_dialog_error_msg_extra ( VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Could not launch %s to open file."), "rednotebook" );
6561     g_error_free ( err );
6562   }
6563   g_free ( cmd );
6564 }
6565
6566 /**
6567  * Open a diary at the date of the track or waypoint
6568  */
6569 static void trw_layer_diary ( menu_array_sublayer values )
6570 {
6571   VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
6572
6573   if ( GPOINTER_TO_INT(values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_TRACK ) {
6574     VikTrack *trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
6575     if ( ! trk )
6576       return;
6577
6578     gchar date_buf[20];
6579     date_buf[0] = '\0';
6580     if ( trk->trackpoints && VIK_TRACKPOINT(trk->trackpoints->data)->has_timestamp ) {
6581       strftime (date_buf, sizeof(date_buf), "%Y-%m-%d", gmtime(&(VIK_TRACKPOINT(trk->trackpoints->data)->timestamp)));
6582       trw_layer_diary_open ( vtl, date_buf );
6583     }
6584     else
6585       a_dialog_info_msg ( VIK_GTK_WINDOW_FROM_LAYER(vtl), _("This track has no date information.") );
6586   }
6587   else if ( GPOINTER_TO_INT(values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_WAYPOINT ) {
6588     VikWaypoint *wpt = (VikWaypoint *) g_hash_table_lookup ( vtl->waypoints, values[MA_SUBLAYER_ID] );
6589     if ( ! wpt )
6590       return;
6591
6592     gchar date_buf[20];
6593     date_buf[0] = '\0';
6594     if ( wpt->has_timestamp ) {
6595       strftime (date_buf, sizeof(date_buf), "%Y-%m-%d", gmtime(&(wpt->timestamp)));
6596       trw_layer_diary_open ( vtl, date_buf );
6597     }
6598     else
6599       a_dialog_info_msg ( VIK_GTK_WINDOW_FROM_LAYER(vtl), _("This waypoint has no date information.") );
6600   }
6601 }
6602
6603 /**
6604  * Similar to trw_layer_enum_item, but this uses a sorted method
6605  */
6606 /* Currently unused
6607 static void trw_layer_sorted_name_list(gpointer key, gpointer value, gpointer udata)
6608 {
6609   GList **list = (GList**)udata;
6610   // *list = g_list_prepend(*all, key); //unsorted method
6611   // Sort named list alphabetically
6612   *list = g_list_insert_sorted_with_data (*list, key, sort_alphabetically, NULL);
6613 }
6614 */
6615
6616 /**
6617  * Now Waypoint specific sort
6618  */
6619 static void trw_layer_sorted_wp_id_by_name_list (const gpointer id, const VikWaypoint *wp, gpointer udata)
6620 {
6621   GList **list = (GList**)udata;
6622   // Sort named list alphabetically
6623   *list = g_list_insert_sorted_with_data (*list, wp->name, sort_alphabetically, NULL);
6624 }
6625
6626 /**
6627  * Track specific sort
6628  */
6629 static void trw_layer_sorted_track_id_by_name_list (const gpointer id, const VikTrack *trk, gpointer udata)
6630 {
6631   GList **list = (GList**)udata;
6632   // Sort named list alphabetically
6633   *list = g_list_insert_sorted_with_data (*list, trk->name, sort_alphabetically, NULL);
6634 }
6635
6636
6637 typedef struct {
6638   gboolean    has_same_track_name;
6639   const gchar *same_track_name;
6640 } same_track_name_udata;
6641
6642 static gint check_tracks_for_same_name ( gconstpointer aa, gconstpointer bb, gpointer udata )
6643 {
6644   const gchar* namea = (const gchar*) aa;
6645   const gchar* nameb = (const gchar*) bb;
6646
6647   // the test
6648   gint result = strcmp ( namea, nameb );
6649
6650   if ( result == 0 ) {
6651     // Found two names the same
6652     same_track_name_udata *user_data = udata;
6653     user_data->has_same_track_name = TRUE;
6654     user_data->same_track_name = namea;
6655   }
6656
6657   // Leave ordering the same
6658   return 0;
6659 }
6660
6661 /**
6662  * Find out if any tracks have the same name in this hash table
6663  */
6664 static gboolean trw_layer_has_same_track_names ( GHashTable *ht_tracks )
6665 {
6666   // Sort items by name, then compare if any next to each other are the same
6667
6668   GList *track_names = NULL;
6669   g_hash_table_foreach ( ht_tracks, (GHFunc) trw_layer_sorted_track_id_by_name_list, &track_names );
6670
6671   // No tracks
6672   if ( ! track_names )
6673     return FALSE;
6674
6675   same_track_name_udata udata;
6676   udata.has_same_track_name = FALSE;
6677
6678   // Use sort routine to traverse list comparing items
6679   // Don't care how this list ends up ordered ( doesn't actually change ) - care about the returned status
6680   GList *dummy_list = g_list_sort_with_data ( track_names, check_tracks_for_same_name, &udata );
6681   // Still no tracks...
6682   if ( ! dummy_list )
6683     return FALSE;
6684
6685   return udata.has_same_track_name;
6686 }
6687
6688 /**
6689  * Force unqiue track names for the track table specified
6690  * Note the panel is a required parameter to enable the update of the names displayed
6691  * Specify if on tracks or else on routes
6692  */
6693 static void vik_trw_layer_uniquify_tracks ( VikTrwLayer *vtl, VikLayersPanel *vlp, GHashTable *track_table, gboolean ontrack )
6694 {
6695   // . Search list for an instance of repeated name
6696   // . get track of this name
6697   // . create new name
6698   // . rename track & update equiv. treeview iter
6699   // . repeat until all different
6700
6701   same_track_name_udata udata;
6702
6703   GList *track_names = NULL;
6704   udata.has_same_track_name = FALSE;
6705   udata.same_track_name = NULL;
6706
6707   g_hash_table_foreach ( track_table, (GHFunc) trw_layer_sorted_track_id_by_name_list, &track_names );
6708
6709   // No tracks
6710   if ( ! track_names )
6711     return;
6712
6713   GList *dummy_list1 = g_list_sort_with_data ( track_names, check_tracks_for_same_name, &udata );
6714
6715   // Still no tracks...
6716   if ( ! dummy_list1 )
6717     return;
6718
6719   while ( udata.has_same_track_name ) {
6720
6721     // Find a track with the same name
6722     VikTrack *trk;
6723     if ( ontrack )
6724       trk = vik_trw_layer_get_track ( vtl, (gpointer) udata.same_track_name );
6725     else
6726       trk = vik_trw_layer_get_route ( vtl, (gpointer) udata.same_track_name );
6727
6728     if ( ! trk ) {
6729       // Broken :(
6730       g_critical("Houston, we've had a problem.");
6731       vik_statusbar_set_message ( vik_window_get_statusbar (VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl))), VIK_STATUSBAR_INFO, 
6732                                   _("Internal Error in vik_trw_layer_uniquify_tracks") );
6733       return;
6734     }
6735
6736     // Rename it
6737     gchar *newname = trw_layer_new_unique_sublayer_name ( vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, udata.same_track_name );
6738     vik_track_set_name ( trk, newname );
6739
6740     trku_udata udataU;
6741     udataU.trk  = trk;
6742     udataU.uuid = NULL;
6743
6744     // Need want key of it for treeview update
6745     gpointer *trkf = g_hash_table_find ( track_table, (GHRFunc) trw_layer_track_find_uuid, &udataU );
6746
6747     if ( trkf && udataU.uuid ) {
6748
6749       GtkTreeIter *it;
6750       if ( ontrack )
6751         it = g_hash_table_lookup ( vtl->tracks_iters, udataU.uuid );
6752       else
6753         it = g_hash_table_lookup ( vtl->routes_iters, udataU.uuid );
6754
6755       if ( it ) {
6756         vik_treeview_item_set_name ( VIK_LAYER(vtl)->vt, it, newname );
6757         if ( ontrack )
6758           vik_treeview_sort_children ( VIK_LAYER(vtl)->vt, &(vtl->tracks_iter), vtl->wp_sort_order );
6759         else
6760           vik_treeview_sort_children ( VIK_LAYER(vtl)->vt, &(vtl->routes_iter), vtl->wp_sort_order );
6761       }
6762     }
6763
6764     // Start trying to find same names again...
6765     track_names = NULL;
6766     g_hash_table_foreach ( track_table, (GHFunc) trw_layer_sorted_track_id_by_name_list, &track_names );
6767     udata.has_same_track_name = FALSE;
6768     GList *dummy_list2 = g_list_sort_with_data ( track_names, check_tracks_for_same_name, &udata );
6769
6770     // No tracks any more - give up searching
6771     if ( ! dummy_list2 )
6772       udata.has_same_track_name = FALSE;
6773   }
6774
6775   // Update
6776   vik_layers_panel_emit_update ( vlp );
6777 }
6778
6779 static void trw_layer_sort_order_a2z ( menu_array_sublayer values )
6780 {
6781   VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
6782   GtkTreeIter *iter;
6783
6784   switch (GPOINTER_TO_INT (values[MA_SUBTYPE])) {
6785   case VIK_TRW_LAYER_SUBLAYER_TRACKS:
6786     iter = &(vtl->tracks_iter);
6787     vtl->track_sort_order = VL_SO_ALPHABETICAL_ASCENDING;
6788     break;
6789   case VIK_TRW_LAYER_SUBLAYER_ROUTES:
6790     iter = &(vtl->routes_iter);
6791     vtl->track_sort_order = VL_SO_ALPHABETICAL_ASCENDING;
6792     break;
6793   default: // VIK_TRW_LAYER_SUBLAYER_WAYPOINTS:
6794     iter = &(vtl->waypoints_iter);
6795     vtl->wp_sort_order = VL_SO_ALPHABETICAL_ASCENDING;
6796     break;
6797   }
6798
6799   vik_treeview_sort_children ( VIK_LAYER(vtl)->vt, iter, VL_SO_ALPHABETICAL_ASCENDING );
6800 }
6801
6802 static void trw_layer_sort_order_z2a ( menu_array_sublayer values )
6803 {
6804   VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
6805   GtkTreeIter *iter;
6806
6807   switch (GPOINTER_TO_INT (values[MA_SUBTYPE])) {
6808   case VIK_TRW_LAYER_SUBLAYER_TRACKS:
6809     iter = &(vtl->tracks_iter);
6810     vtl->track_sort_order = VL_SO_ALPHABETICAL_DESCENDING;
6811     break;
6812   case VIK_TRW_LAYER_SUBLAYER_ROUTES:
6813     iter = &(vtl->routes_iter);
6814     vtl->track_sort_order = VL_SO_ALPHABETICAL_DESCENDING;
6815     break;
6816   default: // VIK_TRW_LAYER_SUBLAYER_WAYPOINTS:
6817     iter = &(vtl->waypoints_iter);
6818     vtl->wp_sort_order = VL_SO_ALPHABETICAL_DESCENDING;
6819     break;
6820   }
6821
6822   vik_treeview_sort_children ( VIK_LAYER(vtl)->vt, iter, VL_SO_ALPHABETICAL_DESCENDING );
6823 }
6824
6825 /**
6826  *
6827  */
6828 static void trw_layer_delete_tracks_from_selection ( menu_array_layer values )
6829 {
6830   VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
6831   GList *all = NULL;
6832
6833   // Ensure list of track names offered is unique
6834   if ( trw_layer_has_same_track_names ( vtl->tracks ) ) {
6835     if ( a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
6836                               _("Multiple entries with the same name exist. This method only works with unique names. Force unique names now?"), NULL ) ) {
6837       vik_trw_layer_uniquify_tracks ( vtl, VIK_LAYERS_PANEL(values[MA_VLP]), vtl->tracks, TRUE );
6838     }
6839     else
6840       return;
6841   }
6842
6843   // Sort list alphabetically for better presentation
6844   g_hash_table_foreach(vtl->tracks, (GHFunc) trw_layer_sorted_track_id_by_name_list, &all);
6845
6846   if ( ! all ) {
6847     a_dialog_error_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), _("No tracks found"));
6848     return;
6849   }
6850
6851   // Get list of items to delete from the user
6852   GList *delete_list = a_dialog_select_from_list(VIK_GTK_WINDOW_FROM_LAYER(vtl),
6853                                                  all,
6854                                                  TRUE,
6855                                                  _("Delete Selection"),
6856                                                  _("Select tracks to delete"));
6857   g_list_free(all);
6858
6859   // Delete requested tracks
6860   // since specificly requested, IMHO no need for extra confirmation
6861   if ( delete_list ) {
6862     GList *l;
6863     for (l = delete_list; l != NULL; l = g_list_next(l)) {
6864       // This deletes first trk it finds of that name (but uniqueness is enforced above)
6865       trw_layer_delete_track_by_name (vtl, l->data, vtl->tracks);
6866     }
6867     g_list_free(delete_list);
6868     vik_layer_emit_update( VIK_LAYER(vtl) );
6869   }
6870 }
6871
6872 /**
6873  *
6874  */
6875 static void trw_layer_delete_routes_from_selection ( menu_array_layer values )
6876 {
6877   VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
6878   GList *all = NULL;
6879
6880   // Ensure list of track names offered is unique
6881   if ( trw_layer_has_same_track_names ( vtl->routes ) ) {
6882     if ( a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
6883                               _("Multiple entries with the same name exist. This method only works with unique names. Force unique names now?"), NULL ) ) {
6884       vik_trw_layer_uniquify_tracks ( vtl, VIK_LAYERS_PANEL(values[MA_VLP]), vtl->routes, FALSE );
6885     }
6886     else
6887       return;
6888   }
6889
6890   // Sort list alphabetically for better presentation
6891   g_hash_table_foreach(vtl->routes, (GHFunc) trw_layer_sorted_track_id_by_name_list, &all);
6892
6893   if ( ! all ) {
6894     a_dialog_error_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), _("No routes found"));
6895     return;
6896   }
6897
6898   // Get list of items to delete from the user
6899   GList *delete_list = a_dialog_select_from_list ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
6900                                                    all,
6901                                                    TRUE,
6902                                                    _("Delete Selection"),
6903                                                    _("Select routes to delete") );
6904   g_list_free(all);
6905
6906   // Delete requested routes
6907   // since specificly requested, IMHO no need for extra confirmation
6908   if ( delete_list ) {
6909     GList *l;
6910     for (l = delete_list; l != NULL; l = g_list_next(l)) {
6911       // This deletes first route it finds of that name (but uniqueness is enforced above)
6912       trw_layer_delete_track_by_name (vtl, l->data, vtl->routes);
6913     }
6914     g_list_free(delete_list);
6915     vik_layer_emit_update( VIK_LAYER(vtl) );
6916   }
6917 }
6918
6919 typedef struct {
6920   gboolean    has_same_waypoint_name;
6921   const gchar *same_waypoint_name;
6922 } same_waypoint_name_udata;
6923
6924 static gint check_waypoints_for_same_name ( gconstpointer aa, gconstpointer bb, gpointer udata )
6925 {
6926   const gchar* namea = (const gchar*) aa;
6927   const gchar* nameb = (const gchar*) bb;
6928
6929   // the test
6930   gint result = strcmp ( namea, nameb );
6931
6932   if ( result == 0 ) {
6933     // Found two names the same
6934     same_waypoint_name_udata *user_data = udata;
6935     user_data->has_same_waypoint_name = TRUE;
6936     user_data->same_waypoint_name = namea;
6937   }
6938
6939   // Leave ordering the same
6940   return 0;
6941 }
6942
6943 /**
6944  * Find out if any waypoints have the same name in this layer
6945  */
6946 gboolean trw_layer_has_same_waypoint_names ( VikTrwLayer *vtl )
6947 {
6948   // Sort items by name, then compare if any next to each other are the same
6949
6950   GList *waypoint_names = NULL;
6951   g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_sorted_wp_id_by_name_list, &waypoint_names );
6952
6953   // No waypoints
6954   if ( ! waypoint_names )
6955     return FALSE;
6956
6957   same_waypoint_name_udata udata;
6958   udata.has_same_waypoint_name = FALSE;
6959
6960   // Use sort routine to traverse list comparing items
6961   // Don't care how this list ends up ordered ( doesn't actually change ) - care about the returned status
6962   GList *dummy_list = g_list_sort_with_data ( waypoint_names, check_waypoints_for_same_name, &udata );
6963   // Still no waypoints...
6964   if ( ! dummy_list )
6965     return FALSE;
6966
6967   return udata.has_same_waypoint_name;
6968 }
6969
6970 /**
6971  * Force unqiue waypoint names for this layer
6972  * Note the panel is a required parameter to enable the update of the names displayed
6973  */
6974 static void vik_trw_layer_uniquify_waypoints ( VikTrwLayer *vtl, VikLayersPanel *vlp )
6975 {
6976   // . Search list for an instance of repeated name
6977   // . get waypoint of this name
6978   // . create new name
6979   // . rename waypoint & update equiv. treeview iter
6980   // . repeat until all different
6981
6982   same_waypoint_name_udata udata;
6983
6984   GList *waypoint_names = NULL;
6985   udata.has_same_waypoint_name = FALSE;
6986   udata.same_waypoint_name = NULL;
6987
6988   g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_sorted_wp_id_by_name_list, &waypoint_names );
6989
6990   // No waypoints
6991   if ( ! waypoint_names )
6992     return;
6993
6994   GList *dummy_list1 = g_list_sort_with_data ( waypoint_names, check_waypoints_for_same_name, &udata );
6995
6996   // Still no waypoints...
6997   if ( ! dummy_list1 )
6998     return;
6999
7000   while ( udata.has_same_waypoint_name ) {
7001
7002     // Find a waypoint with the same name
7003     VikWaypoint *waypoint = vik_trw_layer_get_waypoint ( vtl, (gpointer) udata.same_waypoint_name );
7004
7005     if ( ! waypoint ) {
7006       // Broken :(
7007       g_critical("Houston, we've had a problem.");
7008       vik_statusbar_set_message ( vik_window_get_statusbar (VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl))), VIK_STATUSBAR_INFO, 
7009                                   _("Internal Error in vik_trw_layer_uniquify_waypoints") );
7010       return;
7011     }
7012
7013     // Rename it
7014     gchar *newname = trw_layer_new_unique_sublayer_name ( vtl, VIK_TRW_LAYER_SUBLAYER_WAYPOINT, udata.same_waypoint_name );
7015
7016     trw_layer_waypoint_rename ( vtl, waypoint, newname );
7017
7018     // Start trying to find same names again...
7019     waypoint_names = NULL;
7020     g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_sorted_wp_id_by_name_list, &waypoint_names );
7021     udata.has_same_waypoint_name = FALSE;
7022     GList *dummy_list2 = g_list_sort_with_data ( waypoint_names, check_waypoints_for_same_name, &udata );
7023
7024     // No waypoints any more - give up searching
7025     if ( ! dummy_list2 )
7026       udata.has_same_waypoint_name = FALSE;
7027   }
7028
7029   // Update
7030   vik_layers_panel_emit_update ( vlp );
7031 }
7032
7033 /**
7034  *
7035  */
7036 static void trw_layer_delete_waypoints_from_selection ( menu_array_layer values )
7037 {
7038   VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
7039   GList *all = NULL;
7040
7041   // Ensure list of waypoint names offered is unique
7042   if ( trw_layer_has_same_waypoint_names ( vtl ) ) {
7043     if ( a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
7044                               _("Multiple entries with the same name exist. This method only works with unique names. Force unique names now?"), NULL ) ) {
7045       vik_trw_layer_uniquify_waypoints ( vtl, VIK_LAYERS_PANEL(values[MA_VLP]) );
7046     }
7047     else
7048       return;
7049   }
7050
7051   // Sort list alphabetically for better presentation
7052   g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_sorted_wp_id_by_name_list, &all);
7053   if ( ! all ) {
7054     a_dialog_error_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), _("No waypoints found"));
7055     return;
7056   }
7057
7058   all = g_list_sort_with_data(all, sort_alphabetically, NULL);
7059
7060   // Get list of items to delete from the user
7061   GList *delete_list = a_dialog_select_from_list(VIK_GTK_WINDOW_FROM_LAYER(vtl),
7062                                                  all,
7063                                                  TRUE,
7064                                                  _("Delete Selection"),
7065                                                  _("Select waypoints to delete"));
7066   g_list_free(all);
7067
7068   // Delete requested waypoints
7069   // since specificly requested, IMHO no need for extra confirmation
7070   if ( delete_list ) {
7071     GList *l;
7072     for (l = delete_list; l != NULL; l = g_list_next(l)) {
7073       // This deletes first waypoint it finds of that name (but uniqueness is enforced above)
7074       trw_layer_delete_waypoint_by_name (vtl, l->data);
7075     }
7076     g_list_free(delete_list);
7077
7078     trw_layer_calculate_bounds_waypoints ( vtl );
7079     vik_layer_emit_update( VIK_LAYER(vtl) );
7080   }
7081
7082 }
7083
7084 /**
7085  *
7086  */
7087 static void trw_layer_iter_visibility_toggle ( gpointer id, GtkTreeIter *it, VikTreeview *vt )
7088 {
7089   vik_treeview_item_toggle_visible ( vt, it );
7090 }
7091
7092 /**
7093  *
7094  */
7095 static void trw_layer_iter_visibility ( gpointer id, GtkTreeIter *it, gpointer vis_data[2] )
7096 {
7097   vik_treeview_item_set_visible ( (VikTreeview*)vis_data[0], it, GPOINTER_TO_INT (vis_data[1]) );
7098 }
7099
7100 /**
7101  *
7102  */
7103 static void trw_layer_waypoints_visibility ( gpointer id, VikWaypoint *wp, gpointer on_off )
7104 {
7105   wp->visible = GPOINTER_TO_INT (on_off);
7106 }
7107
7108 /**
7109  *
7110  */
7111 static void trw_layer_waypoints_toggle_visibility ( gpointer id, VikWaypoint *wp )
7112 {
7113   wp->visible = !wp->visible;
7114 }
7115
7116 /**
7117  *
7118  */
7119 static void trw_layer_waypoints_visibility_off ( menu_array_layer values )
7120 {
7121   VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
7122   gpointer vis_data[2] = { VIK_LAYER(vtl)->vt, GINT_TO_POINTER(FALSE) };
7123   g_hash_table_foreach ( vtl->waypoints_iters, (GHFunc) trw_layer_iter_visibility, vis_data );
7124   g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_waypoints_visibility, vis_data[1] );
7125   // Redraw
7126   vik_layer_emit_update ( VIK_LAYER(vtl) );
7127 }
7128
7129 /**
7130  *
7131  */
7132 static void trw_layer_waypoints_visibility_on ( menu_array_layer values )
7133 {
7134   VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
7135   gpointer vis_data[2] = { VIK_LAYER(vtl)->vt, GINT_TO_POINTER(TRUE) };
7136   g_hash_table_foreach ( vtl->waypoints_iters, (GHFunc) trw_layer_iter_visibility, vis_data );
7137   g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_waypoints_visibility, vis_data[1] );
7138   // Redraw
7139   vik_layer_emit_update ( VIK_LAYER(vtl) );
7140 }
7141
7142 /**
7143  *
7144  */
7145 static void trw_layer_waypoints_visibility_toggle ( menu_array_layer values )
7146 {
7147   VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
7148   g_hash_table_foreach ( vtl->waypoints_iters, (GHFunc) trw_layer_iter_visibility_toggle, VIK_LAYER(vtl)->vt );
7149   g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_waypoints_toggle_visibility, NULL );
7150   // Redraw
7151   vik_layer_emit_update ( VIK_LAYER(vtl) );
7152 }
7153
7154 /**
7155  *
7156  */
7157 static void trw_layer_tracks_visibility ( gpointer id, VikTrack *trk, gpointer on_off )
7158 {
7159   trk->visible = GPOINTER_TO_INT (on_off);
7160 }
7161
7162 /**
7163  *
7164  */
7165 static void trw_layer_tracks_toggle_visibility ( gpointer id, VikTrack *trk )
7166 {
7167   trk->visible = !trk->visible;
7168 }
7169
7170 /**
7171  *
7172  */
7173 static void trw_layer_tracks_visibility_off ( menu_array_layer values )
7174 {
7175   VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
7176   gpointer vis_data[2] = { VIK_LAYER(vtl)->vt, GINT_TO_POINTER(FALSE) };
7177   g_hash_table_foreach ( vtl->tracks_iters, (GHFunc) trw_layer_iter_visibility, vis_data );
7178   g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_tracks_visibility, vis_data[1] );
7179   // Redraw
7180   vik_layer_emit_update ( VIK_LAYER(vtl) );
7181 }
7182
7183 /**
7184  *
7185  */
7186 static void trw_layer_tracks_visibility_on ( menu_array_layer values )
7187 {
7188   VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
7189   gpointer vis_data[2] = { VIK_LAYER(vtl)->vt, GINT_TO_POINTER(TRUE) };
7190   g_hash_table_foreach ( vtl->tracks_iters, (GHFunc) trw_layer_iter_visibility, vis_data );
7191   g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_tracks_visibility, vis_data[1] );
7192   // Redraw
7193   vik_layer_emit_update ( VIK_LAYER(vtl) );
7194 }
7195
7196 /**
7197  *
7198  */
7199 static void trw_layer_tracks_visibility_toggle ( menu_array_layer values )
7200 {
7201   VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
7202   g_hash_table_foreach ( vtl->tracks_iters, (GHFunc) trw_layer_iter_visibility_toggle, VIK_LAYER(vtl)->vt );
7203   g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_tracks_toggle_visibility, NULL );
7204   // Redraw
7205   vik_layer_emit_update ( VIK_LAYER(vtl) );
7206 }
7207
7208 /**
7209  *
7210  */
7211 static void trw_layer_routes_visibility_off ( menu_array_layer values )
7212 {
7213   VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
7214   gpointer vis_data[2] = { VIK_LAYER(vtl)->vt, GINT_TO_POINTER(FALSE) };
7215   g_hash_table_foreach ( vtl->routes_iters, (GHFunc) trw_layer_iter_visibility, vis_data );
7216   g_hash_table_foreach ( vtl->routes, (GHFunc) trw_layer_tracks_visibility, vis_data[1] );
7217   // Redraw
7218   vik_layer_emit_update ( VIK_LAYER(vtl) );
7219 }
7220
7221 /**
7222  *
7223  */
7224 static void trw_layer_routes_visibility_on ( menu_array_layer values )
7225 {
7226   VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
7227   gpointer vis_data[2] = { VIK_LAYER(vtl)->vt, GINT_TO_POINTER(TRUE) };
7228   g_hash_table_foreach ( vtl->routes_iters, (GHFunc) trw_layer_iter_visibility, vis_data );
7229   g_hash_table_foreach ( vtl->routes, (GHFunc) trw_layer_tracks_visibility, vis_data[1] );
7230   // Redraw
7231   vik_layer_emit_update ( VIK_LAYER(vtl) );
7232 }
7233
7234 /**
7235  *
7236  */
7237 static void trw_layer_routes_visibility_toggle ( menu_array_layer values )
7238 {
7239   VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
7240   g_hash_table_foreach ( vtl->routes_iters, (GHFunc) trw_layer_iter_visibility_toggle, VIK_LAYER(vtl)->vt );
7241   g_hash_table_foreach ( vtl->routes, (GHFunc) trw_layer_tracks_toggle_visibility, NULL );
7242   // Redraw
7243   vik_layer_emit_update ( VIK_LAYER(vtl) );
7244 }
7245
7246 /**
7247  * vik_trw_layer_build_waypoint_list_t:
7248  *
7249  * Helper function to construct a list of #vik_trw_waypoint_list_t
7250  */
7251 GList *vik_trw_layer_build_waypoint_list_t ( VikTrwLayer *vtl, GList *waypoints )
7252 {
7253   GList *waypoints_and_layers = NULL;
7254   // build waypoints_and_layers list
7255   while ( waypoints ) {
7256     vik_trw_waypoint_list_t *vtdl = g_malloc (sizeof(vik_trw_waypoint_list_t));
7257     vtdl->wpt = VIK_WAYPOINT(waypoints->data);
7258     vtdl->vtl = vtl;
7259     waypoints_and_layers = g_list_prepend ( waypoints_and_layers, vtdl );
7260     waypoints = g_list_next ( waypoints );
7261   }
7262   return waypoints_and_layers;
7263 }
7264
7265 /**
7266  * trw_layer_create_waypoint_list:
7267  *
7268  * Create the latest list of waypoints with the associated layer(s)
7269  *  Although this will always be from a single layer here
7270  */
7271 static GList* trw_layer_create_waypoint_list ( VikLayer *vl, gpointer user_data )
7272 {
7273   VikTrwLayer *vtl = VIK_TRW_LAYER(vl);
7274   GList *waypoints = g_hash_table_get_values ( vik_trw_layer_get_waypoints(vtl) );
7275
7276   return vik_trw_layer_build_waypoint_list_t ( vtl, waypoints );
7277 }
7278
7279 /**
7280  * trw_layer_analyse_close:
7281  *
7282  * Stuff to do on dialog closure
7283  */
7284 static void trw_layer_analyse_close ( GtkWidget *dialog, gint resp, VikLayer* vl )
7285 {
7286   VikTrwLayer *vtl = VIK_TRW_LAYER(vl);
7287   gtk_widget_destroy ( dialog );
7288   vtl->tracks_analysis_dialog = NULL;
7289 }
7290
7291 /**
7292  * vik_trw_layer_build_track_list_t:
7293  *
7294  * Helper function to construct a list of #vik_trw_track_list_t
7295  */
7296 GList *vik_trw_layer_build_track_list_t ( VikTrwLayer *vtl, GList *tracks )
7297 {
7298   GList *tracks_and_layers = NULL;
7299   // build tracks_and_layers list
7300   while ( tracks ) {
7301     vik_trw_track_list_t *vtdl = g_malloc (sizeof(vik_trw_track_list_t));
7302     vtdl->trk = VIK_TRACK(tracks->data);
7303     vtdl->vtl = vtl;
7304     tracks_and_layers = g_list_prepend ( tracks_and_layers, vtdl );
7305     tracks = g_list_next ( tracks );
7306   }
7307   return tracks_and_layers;
7308 }
7309
7310 /**
7311  * trw_layer_create_track_list:
7312  *
7313  * Create the latest list of tracks with the associated layer(s)
7314  *  Although this will always be from a single layer here
7315  */
7316 static GList* trw_layer_create_track_list ( VikLayer *vl, gpointer user_data )
7317 {
7318   VikTrwLayer *vtl = VIK_TRW_LAYER(vl);
7319   GList *tracks = NULL;
7320   if ( GPOINTER_TO_INT(user_data) == VIK_TRW_LAYER_SUBLAYER_TRACKS )
7321     tracks = g_hash_table_get_values ( vik_trw_layer_get_tracks(vtl) );
7322   else
7323     tracks = g_hash_table_get_values ( vik_trw_layer_get_routes(vtl) );
7324
7325   return vik_trw_layer_build_track_list_t ( vtl, tracks );
7326 }
7327
7328 static void trw_layer_tracks_stats ( menu_array_layer values )
7329 {
7330   VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
7331   // There can only be one!
7332   if ( vtl->tracks_analysis_dialog )
7333     return;
7334
7335   vtl->tracks_analysis_dialog = vik_trw_layer_analyse_this ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
7336                                                              VIK_LAYER(vtl)->name,
7337                                                              VIK_LAYER(vtl),
7338                                                              GINT_TO_POINTER(VIK_TRW_LAYER_SUBLAYER_TRACKS),
7339                                                              trw_layer_create_track_list,
7340                                                              trw_layer_analyse_close );
7341 }
7342
7343 /**
7344  *
7345  */
7346 static void trw_layer_routes_stats ( menu_array_layer values )
7347 {
7348   VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
7349   // There can only be one!
7350   if ( vtl->tracks_analysis_dialog )
7351     return;
7352
7353   vtl->tracks_analysis_dialog = vik_trw_layer_analyse_this ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
7354                                                              VIK_LAYER(vtl)->name,
7355                                                              VIK_LAYER(vtl),
7356                                                              GINT_TO_POINTER(VIK_TRW_LAYER_SUBLAYER_ROUTES),
7357                                                              trw_layer_create_track_list,
7358                                                              trw_layer_analyse_close );
7359 }
7360
7361 static void trw_layer_goto_waypoint ( menu_array_sublayer values )
7362 {
7363   VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
7364   VikWaypoint *wp = g_hash_table_lookup ( vtl->waypoints, values[MA_SUBLAYER_ID] );
7365   if ( wp )
7366     goto_coord ( values[MA_VLP], vtl, values[MA_VVP], &(wp->coord) );
7367 }
7368
7369 static void trw_layer_waypoint_gc_webpage ( menu_array_sublayer values )
7370 {
7371   VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
7372   VikWaypoint *wp = g_hash_table_lookup ( vtl->waypoints, values[MA_SUBLAYER_ID] );
7373   if ( !wp )
7374     return;
7375   gchar *webpage = g_strdup_printf("http://www.geocaching.com/seek/cache_details.aspx?wp=%s", wp->name );
7376   open_url(VIK_GTK_WINDOW_FROM_LAYER(VIK_LAYER(vtl)), webpage);
7377   g_free ( webpage );
7378 }
7379
7380 static void trw_layer_waypoint_webpage ( menu_array_sublayer values )
7381 {
7382   VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
7383   VikWaypoint *wp = g_hash_table_lookup ( vtl->waypoints, values[MA_SUBLAYER_ID] );
7384   if ( !wp )
7385     return;
7386   if ( !strncmp(wp->comment, "http", 4) ) {
7387     open_url(VIK_GTK_WINDOW_FROM_LAYER(VIK_LAYER(vtl)), wp->comment);
7388   } else if ( !strncmp(wp->description, "http", 4) ) {
7389     open_url(VIK_GTK_WINDOW_FROM_LAYER(VIK_LAYER(vtl)), wp->description);
7390   }
7391 }
7392
7393 static const gchar* trw_layer_sublayer_rename_request ( VikTrwLayer *l, const gchar *newname, gpointer vlp, gint subtype, gpointer sublayer, GtkTreeIter *iter )
7394 {
7395   if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT )
7396   {
7397     VikWaypoint *wp = g_hash_table_lookup ( l->waypoints, sublayer );
7398
7399     // No actual change to the name supplied
7400     if ( wp->name )
7401       if (strcmp(newname, wp->name) == 0 )
7402        return NULL;
7403
7404     VikWaypoint *wpf = vik_trw_layer_get_waypoint ( l, newname );
7405
7406     if ( wpf ) {
7407       // An existing waypoint has been found with the requested name
7408       if ( ! a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(l),
7409            _("A waypoint with the name \"%s\" already exists. Really rename to the same name?"),
7410            newname ) )
7411         return NULL;
7412     }
7413
7414     // Update WP name and refresh the treeview
7415     vik_waypoint_set_name (wp, newname);
7416
7417     vik_treeview_item_set_name ( VIK_LAYER(l)->vt, iter, newname );
7418     vik_treeview_sort_children ( VIK_LAYER(l)->vt, &(l->waypoints_iter), l->wp_sort_order );
7419
7420     vik_layers_panel_emit_update ( VIK_LAYERS_PANEL(vlp) );
7421
7422     return newname;
7423   }
7424
7425   if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
7426   {
7427     VikTrack *trk = g_hash_table_lookup ( l->tracks, sublayer );
7428
7429     // No actual change to the name supplied
7430     if ( trk->name )
7431       if (strcmp(newname, trk->name) == 0)
7432         return NULL;
7433
7434     VikTrack *trkf = vik_trw_layer_get_track ( l, (gpointer) newname );
7435
7436     if ( trkf ) {
7437       // An existing track has been found with the requested name
7438       if ( ! a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(l),
7439           _("A track with the name \"%s\" already exists. Really rename to the same name?"),
7440           newname ) )
7441         return NULL;
7442     }
7443     // Update track name and refresh GUI parts
7444     vik_track_set_name (trk, newname);
7445
7446     // Update any subwindows that could be displaying this track which has changed name
7447     // Only one Track Edit Window
7448     if ( l->current_tp_track == trk && l->tpwin ) {
7449       vik_trw_layer_tpwin_set_track_name ( l->tpwin, newname );
7450     }
7451     // Property Dialog of the track
7452     vik_trw_layer_propwin_update ( trk );
7453
7454     vik_treeview_item_set_name ( VIK_LAYER(l)->vt, iter, newname );
7455     vik_treeview_sort_children ( VIK_LAYER(l)->vt, &(l->tracks_iter), l->track_sort_order );
7456
7457     vik_layers_panel_emit_update ( VIK_LAYERS_PANEL(vlp) );
7458
7459     return newname;
7460   }
7461
7462   if ( subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE )
7463   {
7464     VikTrack *trk = g_hash_table_lookup ( l->routes, sublayer );
7465
7466     // No actual change to the name supplied
7467     if ( trk->name )
7468       if (strcmp(newname, trk->name) == 0)
7469         return NULL;
7470
7471     VikTrack *trkf = vik_trw_layer_get_route ( l, (gpointer) newname );
7472
7473     if ( trkf ) {
7474       // An existing track has been found with the requested name
7475       if ( ! a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(l),
7476           _("A route with the name \"%s\" already exists. Really rename to the same name?"),
7477           newname ) )
7478         return NULL;
7479     }
7480     // Update track name and refresh GUI parts
7481     vik_track_set_name (trk, newname);
7482
7483     // Update any subwindows that could be displaying this track which has changed name
7484     // Only one Track Edit Window
7485     if ( l->current_tp_track == trk && l->tpwin ) {
7486       vik_trw_layer_tpwin_set_track_name ( l->tpwin, newname );
7487     }
7488     // Property Dialog of the track
7489     vik_trw_layer_propwin_update ( trk );
7490
7491     vik_treeview_item_set_name ( VIK_LAYER(l)->vt, iter, newname );
7492     vik_treeview_sort_children ( VIK_LAYER(l)->vt, &(l->tracks_iter), l->track_sort_order );
7493
7494     vik_layers_panel_emit_update ( VIK_LAYERS_PANEL(vlp) );
7495
7496     return newname;
7497   }
7498   return NULL;
7499 }
7500
7501 static gboolean is_valid_geocache_name ( gchar *str )
7502 {
7503   gint len = strlen ( str );
7504   return len >= 3 && len <= 7 && str[0] == 'G' && str[1] == 'C' && isalnum(str[2]) && (len < 4 || isalnum(str[3])) && (len < 5 || isalnum(str[4])) && (len < 6 || isalnum(str[5])) && (len < 7 || isalnum(str[6]));
7505 }
7506
7507 static void trw_layer_track_use_with_filter ( menu_array_sublayer values )
7508 {
7509   VikTrack *trk = g_hash_table_lookup ( VIK_TRW_LAYER(values[MA_VTL])->tracks, values[MA_SUBLAYER_ID] );
7510   a_acquire_set_filter_track ( trk );
7511 }
7512
7513 #ifdef VIK_CONFIG_GOOGLE
7514 static gboolean is_valid_google_route ( VikTrwLayer *vtl, const gpointer track_id )
7515 {
7516   VikTrack *tr = g_hash_table_lookup ( vtl->routes, track_id );
7517   return ( tr && tr->comment && strlen(tr->comment) > 7 && !strncmp(tr->comment, "from:", 5) );
7518 }
7519
7520 static void trw_layer_google_route_webpage ( menu_array_sublayer values )
7521 {
7522   VikTrack *tr = g_hash_table_lookup ( VIK_TRW_LAYER(values[MA_VTL])->routes, values[MA_SUBLAYER_ID] );
7523   if ( tr ) {
7524     gchar *escaped = uri_escape ( tr->comment );
7525     gchar *webpage = g_strdup_printf("http://maps.google.com/maps?f=q&hl=en&q=%s", escaped );
7526     open_url(VIK_GTK_WINDOW_FROM_LAYER(VIK_LAYER(values[MA_VTL])), webpage);
7527     g_free ( escaped );
7528     g_free ( webpage );
7529   }
7530 }
7531 #endif
7532
7533 /* vlp can be NULL if necessary - i.e. right-click from a tool */
7534 /* viewpoint is now available instead */
7535 static gboolean trw_layer_sublayer_add_menu_items ( VikTrwLayer *l, GtkMenu *menu, gpointer vlp, gint subtype, gpointer sublayer, GtkTreeIter *iter, VikViewport *vvp )
7536 {
7537   static menu_array_sublayer pass_along;
7538   GtkWidget *item;
7539   gboolean rv = FALSE;
7540
7541   pass_along[MA_VTL]         = l;
7542   pass_along[MA_VLP]         = vlp;
7543   pass_along[MA_SUBTYPE]     = GINT_TO_POINTER (subtype);
7544   pass_along[MA_SUBLAYER_ID] = sublayer;
7545   pass_along[MA_CONFIRM]     = GINT_TO_POINTER (1); // Confirm delete request
7546   pass_along[MA_VVP]         = vvp;
7547   pass_along[MA_TV_ITER]     = iter;
7548   pass_along[MA_MISC]        = NULL; // For misc purposes - maybe track or waypoint
7549
7550   if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT || subtype == VIK_TRW_LAYER_SUBLAYER_TRACK || subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE )
7551   {
7552     rv = TRUE;
7553
7554     item = gtk_image_menu_item_new_from_stock ( GTK_STOCK_PROPERTIES, NULL );
7555     g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_properties_item), pass_along );
7556     gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7557     gtk_widget_show ( item );
7558
7559     if (subtype == VIK_TRW_LAYER_SUBLAYER_TRACK) {
7560       VikTrack *tr = g_hash_table_lookup ( l->tracks, sublayer );
7561       if (tr && tr->property_dialog)
7562         gtk_widget_set_sensitive(GTK_WIDGET(item), FALSE );
7563     }
7564     if (subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE) {
7565       VikTrack *tr = g_hash_table_lookup ( l->routes, sublayer );
7566       if (tr && tr->property_dialog)
7567         gtk_widget_set_sensitive(GTK_WIDGET(item), FALSE );
7568     }
7569
7570     item = gtk_image_menu_item_new_from_stock ( GTK_STOCK_CUT, NULL );
7571     g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_cut_item_cb), pass_along );
7572     gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7573     gtk_widget_show ( item );
7574
7575     item = gtk_image_menu_item_new_from_stock ( GTK_STOCK_COPY, NULL );
7576     g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_copy_item_cb), pass_along );
7577     gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7578     gtk_widget_show ( item );
7579
7580     item = gtk_image_menu_item_new_from_stock ( GTK_STOCK_DELETE, NULL );
7581     g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_item), pass_along );
7582     gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7583     gtk_widget_show ( item );
7584
7585     if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT )
7586     {
7587       // Always create separator as now there is always at least the transform menu option
7588       item = gtk_menu_item_new ();
7589       gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7590       gtk_widget_show ( item );
7591
7592       /* could be a right-click using the tool */
7593       if ( vlp != NULL ) {
7594         item = gtk_image_menu_item_new_with_mnemonic ( _("_Goto") );
7595         gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_JUMP_TO, GTK_ICON_SIZE_MENU) );
7596         g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_waypoint), pass_along );
7597         gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7598         gtk_widget_show ( item );
7599       }
7600
7601       VikWaypoint *wp = g_hash_table_lookup ( VIK_TRW_LAYER(l)->waypoints, sublayer );
7602
7603       if ( wp && wp->name ) {
7604         if ( is_valid_geocache_name ( wp->name ) ) {
7605           item = gtk_menu_item_new_with_mnemonic ( _("_Visit Geocache Webpage") );
7606           g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_waypoint_gc_webpage), pass_along );
7607           gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7608           gtk_widget_show ( item );
7609         }
7610 #ifdef VIK_CONFIG_GEOTAG
7611         item = gtk_menu_item_new_with_mnemonic ( _("Geotag _Images...") );
7612         g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_geotagging_waypoint), pass_along );
7613         gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7614         gtk_widget_set_tooltip_text (item, _("Geotag multiple images against this waypoint"));
7615         gtk_widget_show ( item );
7616 #endif
7617       }
7618
7619       if ( wp && wp->image )
7620       {
7621         // Set up image paramater
7622         pass_along[MA_MISC] = wp->image;
7623
7624         item = gtk_image_menu_item_new_with_mnemonic ( _("_Show Picture...") );
7625         gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock ("vik-icon-Show Picture", GTK_ICON_SIZE_MENU) ); // Own icon - see stock_icons in vikwindow.c
7626         g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_show_picture), pass_along );
7627         gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7628         gtk_widget_show ( item );
7629
7630 #ifdef VIK_CONFIG_GEOTAG
7631         GtkWidget *geotag_submenu = gtk_menu_new ();
7632         item = gtk_image_menu_item_new_with_mnemonic ( _("Update Geotag on _Image") );
7633         gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REFRESH, GTK_ICON_SIZE_MENU) );
7634         gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7635         gtk_widget_show ( item );
7636         gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), geotag_submenu );
7637   
7638         item = gtk_menu_item_new_with_mnemonic ( _("_Update") );
7639         g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_geotagging_waypoint_mtime_update), pass_along );
7640         gtk_menu_shell_append (GTK_MENU_SHELL (geotag_submenu), item);
7641         gtk_widget_show ( item );
7642
7643         item = gtk_menu_item_new_with_mnemonic ( _("Update and _Keep File Timestamp") );
7644         g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_geotagging_waypoint_mtime_keep), pass_along );
7645         gtk_menu_shell_append (GTK_MENU_SHELL (geotag_submenu), item);
7646         gtk_widget_show ( item );
7647 #endif
7648       }
7649
7650       if ( wp )
7651       {
7652         if ( ( wp->comment && !strncmp(wp->comment, "http", 4) ) ||
7653              ( wp->description && !strncmp(wp->description, "http", 4) )) {
7654           item = gtk_image_menu_item_new_with_mnemonic ( _("Visit _Webpage") );
7655           gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NETWORK, GTK_ICON_SIZE_MENU) );
7656           g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_waypoint_webpage), pass_along );
7657           gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7658           gtk_widget_show ( item );
7659         }
7660       }
7661     }
7662   }
7663
7664   if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINTS || subtype == VIK_TRW_LAYER_SUBLAYER_TRACKS || subtype == VIK_TRW_LAYER_SUBLAYER_ROUTES ) {
7665     item = gtk_image_menu_item_new_from_stock ( GTK_STOCK_PASTE, NULL );
7666     g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_paste_item_cb), pass_along );
7667     gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7668     gtk_widget_show ( item );
7669     // TODO: only enable if suitable item is in clipboard - want to determine *which* sublayer type
7670     if ( a_clipboard_type ( ) == VIK_CLIPBOARD_DATA_SUBLAYER )
7671       gtk_widget_set_sensitive ( item, TRUE );
7672     else
7673       gtk_widget_set_sensitive ( item, FALSE );
7674
7675     // Add separator
7676     item = gtk_menu_item_new ();
7677     gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7678     gtk_widget_show ( item );
7679   }
7680
7681   if ( vlp && (subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINTS || subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT) )
7682   {
7683     rv = TRUE;
7684     item = gtk_image_menu_item_new_with_mnemonic ( _("_New Waypoint...") );
7685     gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NEW, GTK_ICON_SIZE_MENU) );
7686     g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_wp), pass_along );
7687     gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7688     gtk_widget_show ( item );
7689   }
7690
7691   if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINTS )
7692   {
7693     item = gtk_image_menu_item_new_with_mnemonic ( _("_View All Waypoints") );
7694     gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ZOOM_FIT, GTK_ICON_SIZE_MENU) );
7695     g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_waypoints_view), pass_along );
7696     gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7697     gtk_widget_show ( item );
7698
7699     item = gtk_image_menu_item_new_with_mnemonic ( _("Goto _Waypoint...") );
7700     gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_JUMP_TO, GTK_ICON_SIZE_MENU) );
7701     g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_wp), pass_along );
7702     gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7703     gtk_widget_show ( item );
7704
7705     item = gtk_image_menu_item_new_with_mnemonic ( _("Delete _All Waypoints") );
7706     gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU) );
7707     g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_all_waypoints), pass_along );
7708     gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7709     gtk_widget_show ( item );
7710
7711     item = gtk_image_menu_item_new_with_mnemonic ( _("_Delete Waypoints From Selection...") );
7712     gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
7713     g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_waypoints_from_selection), pass_along );
7714     gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7715     gtk_widget_show ( item );
7716
7717     GtkWidget *vis_submenu = gtk_menu_new ();
7718     item = gtk_menu_item_new_with_mnemonic ( _("_Visibility") );
7719     gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7720     gtk_widget_show ( item );
7721     gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), vis_submenu );
7722
7723     item = gtk_image_menu_item_new_with_mnemonic ( _("_Show All Waypoints") );
7724     gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_APPLY, GTK_ICON_SIZE_MENU) );
7725     g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_waypoints_visibility_on), pass_along );
7726     gtk_menu_shell_append ( GTK_MENU_SHELL(vis_submenu), item );
7727     gtk_widget_show ( item );
7728
7729     item = gtk_image_menu_item_new_with_mnemonic ( _("_Hide All Waypoints") );
7730     gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_CLEAR, GTK_ICON_SIZE_MENU) );
7731     g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_waypoints_visibility_off), pass_along );
7732     gtk_menu_shell_append ( GTK_MENU_SHELL(vis_submenu), item );
7733     gtk_widget_show ( item );
7734
7735     item = gtk_image_menu_item_new_with_mnemonic ( _("_Toggle") );
7736     gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REFRESH, GTK_ICON_SIZE_MENU) );
7737     g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_waypoints_visibility_toggle), pass_along );
7738     gtk_menu_shell_append ( GTK_MENU_SHELL(vis_submenu), item );
7739     gtk_widget_show ( item );
7740
7741     item = gtk_image_menu_item_new_with_mnemonic ( _("_List Waypoints...") );
7742     gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
7743     g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_waypoint_list_dialog), pass_along );
7744     gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7745   }
7746
7747   if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACKS )
7748   {
7749     rv = TRUE;
7750
7751     if ( l->current_track && !l->current_track->is_route ) {
7752       item = gtk_menu_item_new_with_mnemonic ( _("_Finish Track") );
7753       g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_finish_track), pass_along );
7754       gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7755       gtk_widget_show ( item );
7756       // Add separator
7757       item = gtk_menu_item_new ();
7758       gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7759       gtk_widget_show ( item );
7760     }
7761
7762     item = gtk_image_menu_item_new_with_mnemonic ( _("_View All Tracks") );
7763     gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ZOOM_FIT, GTK_ICON_SIZE_MENU) );
7764     g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_tracks_view), pass_along );
7765     gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7766     gtk_widget_show ( item );
7767
7768     item = gtk_image_menu_item_new_with_mnemonic ( _("_New Track") );
7769     gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NEW, GTK_ICON_SIZE_MENU) );
7770     g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_track), pass_along );
7771     gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7772     gtk_widget_show ( item );
7773     // Make it available only when a new track *not* already in progress
7774     gtk_widget_set_sensitive ( item, ! (gboolean)GPOINTER_TO_INT(l->current_track) );
7775
7776     item = gtk_image_menu_item_new_with_mnemonic ( _("Delete _All Tracks") );
7777     gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU) );
7778     g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_all_tracks), pass_along );
7779     gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7780     gtk_widget_show ( item );
7781
7782     item = gtk_image_menu_item_new_with_mnemonic ( _("_Delete Tracks From Selection...") );
7783     gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
7784     g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_tracks_from_selection), pass_along );
7785     gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7786     gtk_widget_show ( item );
7787
7788     GtkWidget *vis_submenu = gtk_menu_new ();
7789     item = gtk_menu_item_new_with_mnemonic ( _("_Visibility") );
7790     gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7791     gtk_widget_show ( item );
7792     gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), vis_submenu );
7793
7794     item = gtk_image_menu_item_new_with_mnemonic ( _("_Show All Tracks") );
7795     gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_APPLY, GTK_ICON_SIZE_MENU) );
7796     g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_tracks_visibility_on), pass_along );
7797     gtk_menu_shell_append ( GTK_MENU_SHELL(vis_submenu), item );
7798     gtk_widget_show ( item );
7799
7800     item = gtk_image_menu_item_new_with_mnemonic ( _("_Hide All Tracks") );
7801     gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_CLEAR, GTK_ICON_SIZE_MENU) );
7802     g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_tracks_visibility_off), pass_along );
7803     gtk_menu_shell_append ( GTK_MENU_SHELL(vis_submenu), item );
7804     gtk_widget_show ( item );
7805
7806     item = gtk_image_menu_item_new_with_mnemonic ( _("_Toggle") );
7807     gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REFRESH, GTK_ICON_SIZE_MENU) );
7808     g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_tracks_visibility_toggle), pass_along );
7809     gtk_menu_shell_append ( GTK_MENU_SHELL(vis_submenu), item );
7810
7811     item = gtk_image_menu_item_new_with_mnemonic ( _("_List Tracks...") );
7812     gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
7813     g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_track_list_dialog_single), pass_along );
7814     gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7815     gtk_widget_show ( item );
7816
7817     item = gtk_menu_item_new_with_mnemonic ( _("_Statistics") );
7818     g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_tracks_stats), pass_along );
7819     gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7820     gtk_widget_show ( item );
7821   }
7822
7823   if ( subtype == VIK_TRW_LAYER_SUBLAYER_ROUTES )
7824   {
7825     rv = TRUE;
7826
7827     if ( l->current_track && l->current_track->is_route ) {
7828       item = gtk_menu_item_new_with_mnemonic ( _("_Finish Route") );
7829       // Reuse finish track method
7830       g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_finish_track), pass_along );
7831       gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7832       gtk_widget_show ( item );
7833       // Add separator
7834       item = gtk_menu_item_new ();
7835       gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7836       gtk_widget_show ( item );
7837     }
7838
7839     item = gtk_image_menu_item_new_with_mnemonic ( _("_View All Routes") );
7840     gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ZOOM_FIT, GTK_ICON_SIZE_MENU) );
7841     g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_routes_view), pass_along );
7842     gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7843     gtk_widget_show ( item );
7844
7845     item = gtk_image_menu_item_new_with_mnemonic ( _("_New Route") );
7846     gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NEW, GTK_ICON_SIZE_MENU) );
7847     g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_route), pass_along );
7848     gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7849     gtk_widget_show ( item );
7850     // Make it available only when a new track *not* already in progress
7851     gtk_widget_set_sensitive ( item, ! (gboolean)GPOINTER_TO_INT(l->current_track) );
7852
7853     item = gtk_image_menu_item_new_with_mnemonic ( _("Delete _All Routes") );
7854     gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU) );
7855     g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_all_routes), pass_along );
7856     gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7857     gtk_widget_show ( item );
7858
7859     item = gtk_image_menu_item_new_with_mnemonic ( _("_Delete Routes From Selection...") );
7860     gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
7861     g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_routes_from_selection), pass_along );
7862     gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7863     gtk_widget_show ( item );
7864
7865     GtkWidget *vis_submenu = gtk_menu_new ();
7866     item = gtk_menu_item_new_with_mnemonic ( _("_Visibility") );
7867     gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7868     gtk_widget_show ( item );
7869     gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), vis_submenu );
7870
7871     item = gtk_image_menu_item_new_with_mnemonic ( _("_Show All Routes") );
7872     gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_APPLY, GTK_ICON_SIZE_MENU) );
7873     g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_routes_visibility_on), pass_along );
7874     gtk_menu_shell_append ( GTK_MENU_SHELL(vis_submenu), item );
7875     gtk_widget_show ( item );
7876
7877     item = gtk_image_menu_item_new_with_mnemonic ( _("_Hide All Routes") );
7878     gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_CLEAR, GTK_ICON_SIZE_MENU) );
7879     g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_routes_visibility_off), pass_along );
7880     gtk_menu_shell_append ( GTK_MENU_SHELL(vis_submenu), item );
7881     gtk_widget_show ( item );
7882
7883     item = gtk_image_menu_item_new_with_mnemonic ( _("_Toggle") );
7884     gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REFRESH, GTK_ICON_SIZE_MENU) );
7885     g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_routes_visibility_toggle), pass_along );
7886     gtk_menu_shell_append ( GTK_MENU_SHELL(vis_submenu), item );
7887
7888     item = gtk_image_menu_item_new_with_mnemonic ( _("_List Routes...") );
7889     gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
7890     g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_track_list_dialog_single), pass_along );
7891     gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7892
7893     gtk_widget_show ( item );
7894
7895     item = gtk_menu_item_new_with_mnemonic ( _("_Statistics") );
7896     g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_routes_stats), pass_along );
7897     gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7898     gtk_widget_show ( item );
7899   }
7900
7901
7902   if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINTS || subtype == VIK_TRW_LAYER_SUBLAYER_TRACKS || subtype == VIK_TRW_LAYER_SUBLAYER_ROUTES ) {
7903     GtkWidget *submenu_sort = gtk_menu_new ();
7904     item = gtk_image_menu_item_new_with_mnemonic ( _("_Sort") );
7905     gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REFRESH, GTK_ICON_SIZE_MENU) );
7906     gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7907     gtk_widget_show ( item );
7908     gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), submenu_sort );
7909
7910     item = gtk_image_menu_item_new_with_mnemonic ( _("Name _Ascending") );
7911     gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_SORT_ASCENDING, GTK_ICON_SIZE_MENU) );
7912     g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_sort_order_a2z), pass_along );
7913     gtk_menu_shell_append ( GTK_MENU_SHELL(submenu_sort), item );
7914     gtk_widget_show ( item );
7915
7916     item = gtk_image_menu_item_new_with_mnemonic ( _("Name _Descending") );
7917     gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_SORT_DESCENDING, GTK_ICON_SIZE_MENU) );
7918     g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_sort_order_z2a), pass_along );
7919     gtk_menu_shell_append ( GTK_MENU_SHELL(submenu_sort), item );
7920     gtk_widget_show ( item );
7921   }
7922
7923   GtkWidget *upload_submenu = gtk_menu_new ();
7924
7925   if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK || subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE )
7926   {
7927     item = gtk_menu_item_new ();
7928     gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7929     gtk_widget_show ( item );
7930
7931     if ( l->current_track && subtype == VIK_TRW_LAYER_SUBLAYER_TRACK && !l->current_track->is_route )
7932       item = gtk_menu_item_new_with_mnemonic ( _("_Finish Track") );
7933     if ( l->current_track && subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE && l->current_track->is_route )
7934       item = gtk_menu_item_new_with_mnemonic ( _("_Finish Route") );
7935     if ( l->current_track ) {
7936       g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_finish_track), pass_along );
7937       gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7938       gtk_widget_show ( item );
7939
7940       // Add separator
7941       item = gtk_menu_item_new ();
7942       gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7943       gtk_widget_show ( item );
7944     }
7945
7946     if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
7947       item = gtk_image_menu_item_new_with_mnemonic ( _("_View Track") );
7948     else
7949       item = gtk_image_menu_item_new_with_mnemonic ( _("_View Route") );
7950     gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ZOOM_FIT, GTK_ICON_SIZE_MENU) );
7951     g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_track_view), pass_along );
7952     gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7953     gtk_widget_show ( item );
7954
7955     item = gtk_menu_item_new_with_mnemonic ( _("_Statistics") );
7956     g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_track_statistics), pass_along );
7957     gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7958     gtk_widget_show ( item );
7959
7960     GtkWidget *goto_submenu;
7961     goto_submenu = gtk_menu_new ();
7962     item = gtk_image_menu_item_new_with_mnemonic ( _("_Goto") );
7963     gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_JUMP_TO, GTK_ICON_SIZE_MENU) );
7964     gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7965     gtk_widget_show ( item );
7966     gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), goto_submenu );
7967
7968     item = gtk_image_menu_item_new_with_mnemonic ( _("_Startpoint") );
7969     gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GOTO_FIRST, GTK_ICON_SIZE_MENU) );
7970     g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_startpoint), pass_along );
7971     gtk_menu_shell_append ( GTK_MENU_SHELL(goto_submenu), item );
7972     gtk_widget_show ( item );
7973
7974     item = gtk_image_menu_item_new_with_mnemonic ( _("\"_Center\"") );
7975     gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_JUMP_TO, GTK_ICON_SIZE_MENU) );
7976     g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_center), pass_along );
7977     gtk_menu_shell_append ( GTK_MENU_SHELL(goto_submenu), item );
7978     gtk_widget_show ( item );
7979
7980     item = gtk_image_menu_item_new_with_mnemonic ( _("_Endpoint") );
7981     gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GOTO_LAST, GTK_ICON_SIZE_MENU) );
7982     g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_endpoint), pass_along );
7983     gtk_menu_shell_append ( GTK_MENU_SHELL(goto_submenu), item );
7984     gtk_widget_show ( item );
7985
7986     item = gtk_image_menu_item_new_with_mnemonic ( _("_Highest Altitude") );
7987     gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GOTO_TOP, GTK_ICON_SIZE_MENU) );
7988     g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_max_alt), pass_along );
7989     gtk_menu_shell_append ( GTK_MENU_SHELL(goto_submenu), item );
7990     gtk_widget_show ( item );
7991
7992     item = gtk_image_menu_item_new_with_mnemonic ( _("_Lowest Altitude") );
7993     gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GOTO_BOTTOM, GTK_ICON_SIZE_MENU) );
7994     g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_min_alt), pass_along );
7995     gtk_menu_shell_append ( GTK_MENU_SHELL(goto_submenu), item );
7996     gtk_widget_show ( item );
7997
7998     // Routes don't have speeds
7999     if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK ) {
8000       item = gtk_image_menu_item_new_with_mnemonic ( _("_Maximum Speed") );
8001       gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_MEDIA_FORWARD, GTK_ICON_SIZE_MENU) );
8002       g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_max_speed), pass_along );
8003       gtk_menu_shell_append ( GTK_MENU_SHELL(goto_submenu), item );
8004       gtk_widget_show ( item );
8005     }
8006
8007     GtkWidget *combine_submenu;
8008     combine_submenu = gtk_menu_new ();
8009     item = gtk_image_menu_item_new_with_mnemonic ( _("Co_mbine") );
8010     gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_CONNECT, GTK_ICON_SIZE_MENU) );
8011     gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
8012     gtk_widget_show ( item );
8013     gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), combine_submenu );
8014
8015     // Routes don't have times or segments...
8016     if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK ) {
8017       item = gtk_menu_item_new_with_mnemonic ( _("_Merge By Time...") );
8018       g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_merge_by_timestamp), pass_along );
8019       gtk_menu_shell_append ( GTK_MENU_SHELL(combine_submenu), item );
8020       gtk_widget_show ( item );
8021
8022       item = gtk_menu_item_new_with_mnemonic ( _("Merge _Segments") );
8023       g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_merge_by_segment), pass_along );
8024       gtk_menu_shell_append ( GTK_MENU_SHELL(combine_submenu), item );
8025       gtk_widget_show ( item );
8026     }
8027
8028     item = gtk_menu_item_new_with_mnemonic ( _("Merge _With Other Tracks...") );
8029     g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_merge_with_other), pass_along );
8030     gtk_menu_shell_append ( GTK_MENU_SHELL(combine_submenu), item );
8031     gtk_widget_show ( item );
8032
8033     if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
8034       item = gtk_menu_item_new_with_mnemonic ( _("_Append Track...") );
8035     else
8036       item = gtk_menu_item_new_with_mnemonic ( _("_Append Route...") );
8037     g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_append_track), pass_along );
8038     gtk_menu_shell_append ( GTK_MENU_SHELL(combine_submenu), item );
8039     gtk_widget_show ( item );
8040
8041     if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
8042       item = gtk_menu_item_new_with_mnemonic ( _("Append _Route...") );
8043     else
8044       item = gtk_menu_item_new_with_mnemonic ( _("Append _Track...") );
8045     g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_append_other), pass_along );
8046     gtk_menu_shell_append ( GTK_MENU_SHELL(combine_submenu), item );
8047     gtk_widget_show ( item );
8048
8049     GtkWidget *split_submenu;
8050     split_submenu = gtk_menu_new ();
8051     item = gtk_image_menu_item_new_with_mnemonic ( _("_Split") );
8052     gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_DISCONNECT, GTK_ICON_SIZE_MENU) );
8053     gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
8054     gtk_widget_show ( item );
8055     gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), split_submenu );
8056
8057     // Routes don't have times or segments...
8058     if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK ) {
8059       item = gtk_menu_item_new_with_mnemonic ( _("_Split By Time...") );
8060       g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_split_by_timestamp), pass_along );
8061       gtk_menu_shell_append ( GTK_MENU_SHELL(split_submenu), item );
8062       gtk_widget_show ( item );
8063
8064       // ATM always enable this entry - don't want to have to analyse the track before displaying the menu - to keep the menu speedy
8065       item = gtk_menu_item_new_with_mnemonic ( _("Split Se_gments") );
8066       g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_split_segments), pass_along );
8067       gtk_menu_shell_append ( GTK_MENU_SHELL(split_submenu), item );
8068       gtk_widget_show ( item );
8069     }
8070
8071     item = gtk_menu_item_new_with_mnemonic ( _("Split By _Number of Points...") );
8072     g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_split_by_n_points), pass_along );
8073     gtk_menu_shell_append ( GTK_MENU_SHELL(split_submenu), item );
8074     gtk_widget_show ( item );
8075
8076     item = gtk_menu_item_new_with_mnemonic ( _("Split at _Trackpoint") );
8077     g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_split_at_trackpoint), pass_along );
8078     gtk_menu_shell_append ( GTK_MENU_SHELL(split_submenu), item );
8079     gtk_widget_show ( item );
8080     // Make it available only when a trackpoint is selected.
8081     gtk_widget_set_sensitive ( item, (gboolean)GPOINTER_TO_INT(l->current_tpl) );
8082
8083     GtkWidget *insert_submenu = gtk_menu_new ();
8084     item = gtk_image_menu_item_new_with_mnemonic ( _("_Insert Points") );
8085     gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ADD, GTK_ICON_SIZE_MENU) );
8086     gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
8087     gtk_widget_show ( item );
8088     gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), insert_submenu );
8089
8090     item = gtk_menu_item_new_with_mnemonic ( _("Insert Point _Before Selected Point") );
8091     g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_insert_point_before), pass_along );
8092     gtk_menu_shell_append ( GTK_MENU_SHELL(insert_submenu), item );
8093     gtk_widget_show ( item );
8094     // Make it available only when a point is selected
8095     gtk_widget_set_sensitive ( item, (gboolean)GPOINTER_TO_INT(l->current_tpl) );
8096
8097     item = gtk_menu_item_new_with_mnemonic ( _("Insert Point _After Selected Point") );
8098     g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_insert_point_after), pass_along );
8099     gtk_menu_shell_append ( GTK_MENU_SHELL(insert_submenu), item );
8100     gtk_widget_show ( item );
8101     // Make it available only when a point is selected
8102     gtk_widget_set_sensitive ( item, (gboolean)GPOINTER_TO_INT(l->current_tpl) );
8103
8104     GtkWidget *delete_submenu;
8105     delete_submenu = gtk_menu_new ();
8106     item = gtk_image_menu_item_new_with_mnemonic ( _("Delete Poi_nts") );
8107     gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_DELETE, GTK_ICON_SIZE_MENU) );
8108     gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
8109     gtk_widget_show ( item );
8110     gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), delete_submenu );
8111
8112     item = gtk_image_menu_item_new_with_mnemonic ( _("Delete _Selected Point") );
8113     gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_DELETE, GTK_ICON_SIZE_MENU) );
8114     g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_point_selected), pass_along );
8115     gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
8116     gtk_widget_show ( item );
8117     // Make it available only when a point is selected
8118     gtk_widget_set_sensitive ( item, (gboolean)GPOINTER_TO_INT(l->current_tpl) );
8119
8120     item = gtk_menu_item_new_with_mnemonic ( _("Delete Points With The Same _Position") );
8121     g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_points_same_position), pass_along );
8122     gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
8123     gtk_widget_show ( item );
8124
8125     item = gtk_menu_item_new_with_mnemonic ( _("Delete Points With The Same _Time") );
8126     g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_points_same_time), pass_along );
8127     gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
8128     gtk_widget_show ( item );
8129
8130     GtkWidget *transform_submenu;
8131     transform_submenu = gtk_menu_new ();
8132     item = gtk_image_menu_item_new_with_mnemonic ( _("_Transform") );
8133     gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_CONVERT, GTK_ICON_SIZE_MENU) );
8134     gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
8135     gtk_widget_show ( item );
8136     gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), transform_submenu );
8137
8138     GtkWidget *dem_submenu;
8139     dem_submenu = gtk_menu_new ();
8140     item = gtk_image_menu_item_new_with_mnemonic ( _("_Apply DEM Data") );
8141     gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock ("vik-icon-DEM Download", GTK_ICON_SIZE_MENU) ); // Own icon - see stock_icons in vikwindow.c
8142     gtk_menu_shell_append ( GTK_MENU_SHELL(transform_submenu), item );
8143     gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), dem_submenu );
8144
8145     item = gtk_image_menu_item_new_with_mnemonic ( _("_Overwrite") );
8146     g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_apply_dem_data_all), pass_along );
8147     gtk_menu_shell_append ( GTK_MENU_SHELL(dem_submenu), item );
8148     gtk_widget_set_tooltip_text (item, _("Overwrite any existing elevation values with DEM values"));
8149     gtk_widget_show ( item );
8150
8151     item = gtk_image_menu_item_new_with_mnemonic ( _("_Keep Existing") );
8152     g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_apply_dem_data_only_missing), pass_along );
8153     gtk_menu_shell_append ( GTK_MENU_SHELL(dem_submenu), item );
8154     gtk_widget_set_tooltip_text (item, _("Keep existing elevation values, only attempt for missing values"));
8155     gtk_widget_show ( item );
8156
8157     GtkWidget *smooth_submenu;
8158     smooth_submenu = gtk_menu_new ();
8159     item = gtk_menu_item_new_with_mnemonic ( _("_Smooth Missing Elevation Data") );
8160     gtk_menu_shell_append ( GTK_MENU_SHELL(transform_submenu), item );
8161     gtk_widget_show ( item );
8162     gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), smooth_submenu );
8163
8164     item = gtk_image_menu_item_new_with_mnemonic ( _("_Interpolated") );
8165     g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_missing_elevation_data_interp), pass_along );
8166     gtk_menu_shell_append ( GTK_MENU_SHELL(smooth_submenu), item );
8167     gtk_widget_set_tooltip_text (item, _("Interpolate between known elevation values to derive values for the missing elevations"));
8168     gtk_widget_show ( item );
8169
8170     item = gtk_image_menu_item_new_with_mnemonic ( _("_Flat") );
8171     g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_missing_elevation_data_flat), pass_along );
8172     gtk_menu_shell_append ( GTK_MENU_SHELL(smooth_submenu), item );
8173     gtk_widget_set_tooltip_text (item, _("Set unknown elevation values to the last known value"));
8174     gtk_widget_show ( item );
8175
8176     if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
8177       item = gtk_image_menu_item_new_with_mnemonic ( _("C_onvert to a Route") );
8178     else
8179       item = gtk_image_menu_item_new_with_mnemonic ( _("C_onvert to a Track") );
8180     gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_CONVERT, GTK_ICON_SIZE_MENU) );
8181     g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_convert_track_route), pass_along );
8182     gtk_menu_shell_append ( GTK_MENU_SHELL(transform_submenu), item );
8183     gtk_widget_show ( item );
8184
8185     // Routes don't have timestamps - so this is only available for tracks
8186     if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK ) {
8187       item = gtk_image_menu_item_new_with_mnemonic ( _("_Anonymize Times") );
8188       g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_anonymize_times), pass_along );
8189       gtk_menu_shell_append ( GTK_MENU_SHELL(transform_submenu), item );
8190       gtk_widget_set_tooltip_text (item, _("Shift timestamps to a relative offset from 1901-01-01"));
8191       gtk_widget_show ( item );
8192     }
8193
8194     if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
8195       item = gtk_image_menu_item_new_with_mnemonic ( _("_Reverse Track") );
8196     else
8197       item = gtk_image_menu_item_new_with_mnemonic ( _("_Reverse Route") );
8198     gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_BACK, GTK_ICON_SIZE_MENU) );
8199     g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_reverse), pass_along );
8200     gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
8201     gtk_widget_show ( item );
8202
8203     if ( subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE ) {
8204       item = gtk_image_menu_item_new_with_mnemonic ( _("Refine Route...") );
8205       gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_FIND, GTK_ICON_SIZE_MENU) );
8206       g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_route_refine), pass_along );
8207       gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
8208       gtk_widget_show ( item );
8209     }
8210
8211     /* ATM This function is only available via the layers panel, due to the method in finding out the maps in use */
8212     if ( vlp ) {
8213       if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
8214         item = gtk_image_menu_item_new_with_mnemonic ( _("Down_load Maps Along Track...") );
8215       else
8216         item = gtk_image_menu_item_new_with_mnemonic ( _("Down_load Maps Along Route...") );
8217       gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock ("vik-icon-Maps Download", GTK_ICON_SIZE_MENU) ); // Own icon - see stock_icons in vikwindow.c
8218       g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_download_map_along_track_cb), pass_along );
8219       gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
8220       gtk_widget_show ( item );
8221     }
8222
8223     if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
8224       item = gtk_image_menu_item_new_with_mnemonic ( _("_Export Track as GPX...") );
8225     else
8226       item = gtk_image_menu_item_new_with_mnemonic ( _("_Export Route as GPX...") );
8227     gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_HARDDISK, GTK_ICON_SIZE_MENU) );
8228     g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_gpx_track), pass_along );
8229     gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
8230     gtk_widget_show ( item );
8231
8232     if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
8233       item = gtk_image_menu_item_new_with_mnemonic ( _("E_xtend Track End") );
8234     else
8235       item = gtk_image_menu_item_new_with_mnemonic ( _("E_xtend Route End") );
8236     gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ADD, GTK_ICON_SIZE_MENU) );
8237     g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_extend_track_end), pass_along );
8238     gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
8239     gtk_widget_show ( item );
8240
8241     if ( subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE ) {
8242       item = gtk_image_menu_item_new_with_mnemonic ( _("Extend _Using Route Finder") );
8243       gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock ("vik-icon-Route Finder", GTK_ICON_SIZE_MENU) ); // Own icon - see stock_icons in vikwindow.c
8244       g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_extend_track_end_route_finder), pass_along );
8245       gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
8246       gtk_widget_show ( item );
8247     }
8248
8249     // ATM can't upload a single waypoint but can do waypoints to a GPS
8250     if ( subtype != VIK_TRW_LAYER_SUBLAYER_WAYPOINT ) {
8251       item = gtk_image_menu_item_new_with_mnemonic ( _("_Upload") );
8252       gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_UP, GTK_ICON_SIZE_MENU) );
8253       gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
8254       gtk_widget_show ( item );
8255       gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), upload_submenu );
8256
8257       item = gtk_image_menu_item_new_with_mnemonic ( _("_Upload to GPS...") );
8258       gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_FORWARD, GTK_ICON_SIZE_MENU) );
8259       g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_gps_upload_any), pass_along );
8260       gtk_menu_shell_append ( GTK_MENU_SHELL(upload_submenu), item );
8261       gtk_widget_show ( item );
8262     }
8263   }
8264
8265   // Only made available if a suitable program is installed
8266   if ( have_diary_program ) {
8267     if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK || subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT ) {
8268       item = gtk_image_menu_item_new_with_mnemonic ( _("Diar_y") );
8269       gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_SPELL_CHECK, GTK_ICON_SIZE_MENU) );
8270       g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_diary), pass_along );
8271       gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
8272       gtk_widget_show ( item );
8273     }
8274   }
8275
8276 #ifdef VIK_CONFIG_GOOGLE
8277   if ( subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE && is_valid_google_route ( l, sublayer ) )
8278   {
8279     item = gtk_image_menu_item_new_with_mnemonic ( _("_View Google Directions") );
8280     gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NETWORK, GTK_ICON_SIZE_MENU) );
8281     g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_google_route_webpage), pass_along );
8282     gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
8283     gtk_widget_show ( item );
8284   }
8285 #endif
8286
8287   // Some things aren't usable with routes
8288   if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK ) {
8289 #ifdef VIK_CONFIG_OPENSTREETMAP
8290     item = gtk_image_menu_item_new_with_mnemonic ( _("Upload to _OSM...") );
8291     // Convert internal pointer into track
8292     pass_along[MA_MISC] = g_hash_table_lookup ( l->tracks, sublayer);
8293     gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_UP, GTK_ICON_SIZE_MENU) );
8294     g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_osm_traces_upload_track_cb), pass_along );
8295     gtk_menu_shell_append ( GTK_MENU_SHELL(upload_submenu), item );
8296     gtk_widget_show ( item );
8297 #endif
8298
8299     item = gtk_image_menu_item_new_with_mnemonic ( _("Use with _Filter") );
8300     gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
8301     g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_track_use_with_filter), pass_along );
8302     gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
8303     gtk_widget_show ( item );
8304
8305     /* ATM This function is only available via the layers panel, due to needing a vlp */
8306     if ( vlp ) {
8307       item = a_acquire_track_menu ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(l)), vlp,
8308                                     vik_layers_panel_get_viewport(VIK_LAYERS_PANEL(vlp)),
8309                                     g_hash_table_lookup ( l->tracks, (gchar *) sublayer ) );
8310       if ( item ) {
8311         gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
8312         gtk_widget_show ( item );
8313       }
8314     }
8315
8316 #ifdef VIK_CONFIG_GEOTAG
8317     item = gtk_menu_item_new_with_mnemonic ( _("Geotag _Images...") );
8318     g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_geotagging_track), pass_along );
8319     gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
8320     gtk_widget_show ( item );
8321 #endif
8322   }
8323
8324   if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK || subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE ) {
8325     // Only show on viewport popmenu when a trackpoint is selected
8326     if ( ! vlp && l->current_tpl ) {
8327       // Add separator
8328       item = gtk_menu_item_new ();
8329       gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
8330       gtk_widget_show ( item );
8331
8332       item = gtk_image_menu_item_new_with_mnemonic ( _("_Edit Trackpoint") );
8333       gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_PROPERTIES, GTK_ICON_SIZE_MENU) );
8334       g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_edit_trackpoint), pass_along );
8335       gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
8336       gtk_widget_show ( item );
8337     }
8338   }
8339
8340   if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINTS || subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT ) {
8341     GtkWidget *transform_submenu;
8342     transform_submenu = gtk_menu_new ();
8343     item = gtk_image_menu_item_new_with_mnemonic ( _("_Transform") );
8344     gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_CONVERT, GTK_ICON_SIZE_MENU) );
8345     gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
8346     gtk_widget_show ( item );
8347     gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), transform_submenu );
8348
8349     GtkWidget *dem_submenu;
8350     dem_submenu = gtk_menu_new ();
8351     item = gtk_image_menu_item_new_with_mnemonic ( _("_Apply DEM Data") );
8352     gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock ("vik-icon-DEM Download", GTK_ICON_SIZE_MENU) ); // Own icon - see stock_icons in vikwindow.c
8353     gtk_menu_shell_append ( GTK_MENU_SHELL(transform_submenu), item );
8354     gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), dem_submenu );
8355
8356     item = gtk_image_menu_item_new_with_mnemonic ( _("_Overwrite") );
8357     g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_apply_dem_data_wpt_all), pass_along );
8358     gtk_menu_shell_append ( GTK_MENU_SHELL(dem_submenu), item );
8359     gtk_widget_set_tooltip_text (item, _("Overwrite any existing elevation values with DEM values"));
8360     gtk_widget_show ( item );
8361
8362     item = gtk_image_menu_item_new_with_mnemonic ( _("_Keep Existing") );
8363     g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_apply_dem_data_wpt_only_missing), pass_along );
8364     gtk_menu_shell_append ( GTK_MENU_SHELL(dem_submenu), item );
8365     gtk_widget_set_tooltip_text (item, _("Keep existing elevation values, only attempt for missing values"));
8366     gtk_widget_show ( item );
8367   }
8368
8369   gtk_widget_show_all ( GTK_WIDGET(menu) );
8370
8371   return rv;
8372 }
8373
8374 // TODO: Probably better to rework this track manipulation in viktrack.c
8375 static void trw_layer_insert_tp_beside_current_tp ( VikTrwLayer *vtl, gboolean before )
8376 {
8377   // sanity check
8378   if (!vtl->current_tpl)
8379     return;
8380
8381   VikTrackpoint *tp_current = VIK_TRACKPOINT(vtl->current_tpl->data);
8382   VikTrackpoint *tp_other = NULL;
8383
8384   if ( before ) {
8385     if (!vtl->current_tpl->prev)
8386       return;
8387     tp_other = VIK_TRACKPOINT(vtl->current_tpl->prev->data);
8388   } else {
8389     if (!vtl->current_tpl->next)
8390       return;
8391     tp_other = VIK_TRACKPOINT(vtl->current_tpl->next->data);
8392   }
8393
8394   // Use current and other trackpoints to form a new track point which is inserted into the tracklist
8395   if ( tp_other ) {
8396
8397     VikTrackpoint *tp_new = vik_trackpoint_new();
8398     struct LatLon ll_current, ll_other;
8399     vik_coord_to_latlon ( &tp_current->coord, &ll_current );
8400     vik_coord_to_latlon ( &tp_other->coord, &ll_other );
8401
8402     /* main positional interpolation */
8403     struct LatLon ll_new = { (ll_current.lat+ll_other.lat)/2, (ll_current.lon+ll_other.lon)/2 };
8404     vik_coord_load_from_latlon ( &(tp_new->coord), vtl->coord_mode, &ll_new );
8405
8406     /* Now other properties that can be interpolated */
8407     tp_new->altitude = (tp_current->altitude + tp_other->altitude) / 2;
8408
8409     if (tp_current->has_timestamp && tp_other->has_timestamp) {
8410       /* Note here the division is applied to each part, then added
8411          This is to avoid potential overflow issues with a 32 time_t for dates after midpoint of this Unix time on 2004/01/04 */
8412       tp_new->timestamp = (tp_current->timestamp/2) + (tp_other->timestamp/2);
8413       tp_new->has_timestamp = TRUE;
8414     }
8415
8416     if (tp_current->speed != NAN && tp_other->speed != NAN)
8417       tp_new->speed = (tp_current->speed + tp_other->speed)/2;
8418
8419     /* TODO - improve interpolation of course, as it may not be correct.
8420        if courses in degrees are 350 + 020, the mid course more likely to be 005 (not 185)
8421        [similar applies if value is in radians] */
8422     if (tp_current->course != NAN && tp_other->course != NAN)
8423       tp_new->course = (tp_current->course + tp_other->course)/2;
8424
8425     /* DOP / sat values remain at defaults as not they do not seem applicable to a dreamt up point */
8426
8427     // Insert new point into the appropriate trackpoint list, either before or after the current trackpoint as directed
8428     VikTrack *trk = g_hash_table_lookup ( vtl->tracks, vtl->current_tp_id );
8429     if ( !trk )
8430       // Otherwise try routes
8431       trk = g_hash_table_lookup ( vtl->routes, vtl->current_tp_id );
8432     if ( !trk )
8433       return;
8434
8435     gint index =  g_list_index ( trk->trackpoints, tp_current );
8436     if ( index > -1 ) {
8437       if ( !before )
8438         index = index + 1;
8439       // NB no recalculation of bounds since it is inserted between points
8440       trk->trackpoints = g_list_insert ( trk->trackpoints, tp_new, index );
8441     }
8442   }
8443 }
8444
8445 static void trw_layer_cancel_current_tp ( VikTrwLayer *vtl, gboolean destroy )
8446 {
8447   if ( vtl->tpwin )
8448   {
8449     if ( destroy)
8450     {
8451       gtk_widget_destroy ( GTK_WIDGET(vtl->tpwin) );
8452       vtl->tpwin = NULL;
8453     }
8454     else
8455       vik_trw_layer_tpwin_set_empty ( vtl->tpwin );
8456   }
8457   if ( vtl->current_tpl )
8458   {
8459     vtl->current_tpl = NULL;
8460     vtl->current_tp_track = NULL;
8461     vtl->current_tp_id = NULL;
8462     vik_layer_emit_update(VIK_LAYER(vtl));
8463   }
8464 }
8465
8466 static void trw_layer_tpwin_response ( VikTrwLayer *vtl, gint response )
8467 {
8468   g_assert ( vtl->tpwin != NULL );
8469   if ( response == VIK_TRW_LAYER_TPWIN_CLOSE )
8470     trw_layer_cancel_current_tp ( vtl, TRUE );
8471
8472   if ( vtl->current_tpl == NULL )
8473     return;
8474
8475   if ( response == VIK_TRW_LAYER_TPWIN_SPLIT && vtl->current_tpl->next && vtl->current_tpl->prev )
8476   {
8477     trw_layer_split_at_selected_trackpoint ( vtl, vtl->current_tp_track->is_route ? VIK_TRW_LAYER_SUBLAYER_ROUTE : VIK_TRW_LAYER_SUBLAYER_TRACK );
8478     vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, vtl->current_tp_track->name );
8479   }
8480   else if ( response == VIK_TRW_LAYER_TPWIN_DELETE )
8481   {
8482     VikTrack *tr = g_hash_table_lookup ( vtl->tracks, vtl->current_tp_id );
8483     if ( tr == NULL )
8484       tr = g_hash_table_lookup ( vtl->routes, vtl->current_tp_id );
8485     if ( tr == NULL )
8486       return;
8487
8488     trw_layer_trackpoint_selected_delete ( vtl, tr );
8489
8490     if ( vtl->current_tpl )
8491       // Reset dialog with the available adjacent trackpoint
8492       vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, vtl->current_tp_track->name );
8493
8494     vik_layer_emit_update(VIK_LAYER(vtl));
8495   }
8496   else if ( response == VIK_TRW_LAYER_TPWIN_FORWARD && vtl->current_tpl->next )
8497   {
8498     if ( vtl->current_tp_track )
8499       vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl = vtl->current_tpl->next, vtl->current_tp_track->name );
8500     vik_layer_emit_update(VIK_LAYER(vtl)); /* TODO longone: either move or only update if tp is inside drawing window */
8501   }
8502   else if ( response == VIK_TRW_LAYER_TPWIN_BACK && vtl->current_tpl->prev )
8503   {
8504     if ( vtl->current_tp_track )
8505       vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl = vtl->current_tpl->prev, vtl->current_tp_track->name );
8506     vik_layer_emit_update(VIK_LAYER(vtl));
8507   }
8508   else if ( response == VIK_TRW_LAYER_TPWIN_INSERT && vtl->current_tpl->next )
8509   {
8510     trw_layer_insert_tp_beside_current_tp ( vtl, FALSE );
8511     vik_layer_emit_update(VIK_LAYER(vtl));
8512   }
8513   else if ( response == VIK_TRW_LAYER_TPWIN_DATA_CHANGED )
8514     vik_layer_emit_update(VIK_LAYER(vtl));
8515 }
8516
8517 /**
8518  * trw_layer_dialog_shift:
8519  * @vertical: The reposition strategy. If Vertical moves dialog vertically, otherwise moves it horizontally
8520  *
8521  * Try to reposition a dialog if it's over the specified coord
8522  *  so to not obscure the item of interest
8523  */
8524 void trw_layer_dialog_shift ( VikTrwLayer *vtl, GtkWindow *dialog, VikCoord *coord, gboolean vertical )
8525 {
8526   GtkWindow *parent = VIK_GTK_WINDOW_FROM_LAYER(vtl); //i.e. the main window
8527
8528   // Attempt force dialog to be shown so we can find out where it is more reliably...
8529   while ( gtk_events_pending() )
8530     gtk_main_iteration ();
8531
8532   // get parent window position & size
8533   gint win_pos_x, win_pos_y;
8534   gtk_window_get_position ( parent, &win_pos_x, &win_pos_y );
8535
8536   gint win_size_x, win_size_y;
8537   gtk_window_get_size ( parent, &win_size_x, &win_size_y );
8538
8539   // get own dialog size
8540   gint dia_size_x, dia_size_y;
8541   gtk_window_get_size ( dialog, &dia_size_x, &dia_size_y );
8542
8543   // get own dialog position
8544   gint dia_pos_x, dia_pos_y;
8545   gtk_window_get_position ( dialog, &dia_pos_x, &dia_pos_y );
8546
8547   // Dialog not 'realized'/positioned - so can't really do any repositioning logic
8548   if ( dia_pos_x > 2 && dia_pos_y > 2 ) {
8549
8550     VikViewport *vvp = vik_window_viewport ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)) );
8551
8552     gint vp_xx, vp_yy; // In viewport pixels
8553     vik_viewport_coord_to_screen ( vvp, coord, &vp_xx, &vp_yy );
8554
8555     // Work out the 'bounding box' in pixel terms of the dialog and only move it when over the position
8556
8557     gint dest_x = 0;
8558     gint dest_y = 0;
8559     if ( gtk_widget_translate_coordinates ( GTK_WIDGET(vvp), GTK_WIDGET(parent), 0, 0, &dest_x, &dest_y ) ) {
8560
8561       // Transform Viewport pixels into absolute pixels
8562       gint tmp_xx = vp_xx + dest_x + win_pos_x - 10;
8563       gint tmp_yy = vp_yy + dest_y + win_pos_y - 10;
8564
8565       // Is dialog over the point (to within an  ^^ edge value)
8566       if ( (tmp_xx > dia_pos_x) && (tmp_xx < (dia_pos_x + dia_size_x)) &&
8567            (tmp_yy > dia_pos_y) && (tmp_yy < (dia_pos_y + dia_size_y)) ) {
8568
8569         if ( vertical ) {
8570           // Shift up<->down
8571           gint hh = vik_viewport_get_height ( vvp );
8572
8573           // Consider the difference in viewport to the full window
8574           gint offset_y = dest_y;
8575           // Add difference between dialog and window sizes
8576           offset_y += win_pos_y + (hh/2 - dia_size_y)/2;
8577
8578           if ( vp_yy > hh/2 ) {
8579             // Point in bottom half, move window to top half
8580             gtk_window_move ( dialog, dia_pos_x, offset_y );
8581           }
8582           else {
8583             // Point in top half, move dialog down
8584             gtk_window_move ( dialog, dia_pos_x, hh/2 + offset_y );
8585           }
8586         }
8587         else {
8588           // Shift left<->right
8589           gint ww = vik_viewport_get_width ( vvp );
8590
8591           // Consider the difference in viewport to the full window
8592           gint offset_x = dest_x;
8593           // Add difference between dialog and window sizes
8594           offset_x += win_pos_x + (ww/2 - dia_size_x)/2;
8595
8596           if ( vp_xx > ww/2 ) {
8597             // Point on right, move window to left
8598             gtk_window_move ( dialog, offset_x, dia_pos_y );
8599           }
8600           else {
8601             // Point on left, move right
8602             gtk_window_move ( dialog, ww/2 + offset_x, dia_pos_y );
8603           }
8604         }
8605       }
8606     }
8607   }
8608 }
8609
8610 static void trw_layer_tpwin_init ( VikTrwLayer *vtl )
8611 {
8612   if ( ! vtl->tpwin )
8613   {
8614     vtl->tpwin = vik_trw_layer_tpwin_new ( VIK_GTK_WINDOW_FROM_LAYER(vtl) );
8615     g_signal_connect_swapped ( GTK_DIALOG(vtl->tpwin), "response", G_CALLBACK(trw_layer_tpwin_response), vtl );
8616     /* connect signals -- DELETE SIGNAL VERY IMPORTANT TO SET TO NULL */
8617     g_signal_connect_swapped ( vtl->tpwin, "delete-event", G_CALLBACK(trw_layer_cancel_current_tp), vtl );
8618
8619     gtk_widget_show_all ( GTK_WIDGET(vtl->tpwin) );
8620
8621     if ( vtl->current_tpl ) {
8622       // get tp pixel position
8623       VikTrackpoint *tp = VIK_TRACKPOINT(vtl->current_tpl->data);
8624
8625       // Shift up<->down to try not to obscure the trackpoint.
8626       trw_layer_dialog_shift ( vtl, GTK_WINDOW(vtl->tpwin), &(tp->coord), TRUE );
8627     }
8628   }
8629
8630   if ( vtl->current_tpl )
8631     if ( vtl->current_tp_track )
8632       vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, vtl->current_tp_track->name );
8633   /* set layer name and TP data */
8634 }
8635
8636 /***************************************************************************
8637  ** Tool code
8638  ***************************************************************************/
8639
8640 /*** Utility data structures and functions ****/
8641
8642 typedef struct {
8643   gint x, y;
8644   gint closest_x, closest_y;
8645   gboolean draw_images;
8646   gpointer *closest_wp_id;
8647   VikWaypoint *closest_wp;
8648   VikViewport *vvp;
8649 } WPSearchParams;
8650
8651 typedef struct {
8652   gint x, y;
8653   gint closest_x, closest_y;
8654   gpointer closest_track_id;
8655   VikTrackpoint *closest_tp;
8656   VikViewport *vvp;
8657   GList *closest_tpl;
8658   LatLonBBox bbox;
8659 } TPSearchParams;
8660
8661 static void waypoint_search_closest_tp ( gpointer id, VikWaypoint *wp, WPSearchParams *params )
8662 {
8663   gint x, y;
8664   if ( !wp->visible )
8665     return;
8666
8667   vik_viewport_coord_to_screen ( params->vvp, &(wp->coord), &x, &y );
8668
8669   // If waypoint has an image then use the image size to select
8670   if ( params->draw_images && wp->image ) {
8671     gint slackx, slacky;
8672     slackx = wp->image_width / 2;
8673     slacky = wp->image_height / 2;
8674
8675     if (    x <= params->x + slackx && x >= params->x - slackx
8676          && y <= params->y + slacky && y >= params->y - slacky ) {
8677       params->closest_wp_id = id;
8678       params->closest_wp = wp;
8679       params->closest_x = x;
8680       params->closest_y = y;
8681     }
8682   }
8683   else if ( abs (x - params->x) <= WAYPOINT_SIZE_APPROX && abs (y - params->y) <= WAYPOINT_SIZE_APPROX &&
8684             ((!params->closest_wp) ||        /* was the old waypoint we already found closer than this one? */
8685              abs(x - params->x)+abs(y - params->y) < abs(x - params->closest_x)+abs(y - params->closest_y)))
8686     {
8687       params->closest_wp_id = id;
8688       params->closest_wp = wp;
8689       params->closest_x = x;
8690       params->closest_y = y;
8691     }
8692 }
8693
8694 static void track_search_closest_tp ( gpointer id, VikTrack *t, TPSearchParams *params )
8695 {
8696   GList *tpl = t->trackpoints;
8697   VikTrackpoint *tp;
8698
8699   if ( !t->visible )
8700     return;
8701
8702   if ( ! BBOX_INTERSECT ( t->bbox, params->bbox ) )
8703     return;
8704
8705   while (tpl)
8706   {
8707     gint x, y;
8708     tp = VIK_TRACKPOINT(tpl->data);
8709
8710     vik_viewport_coord_to_screen ( params->vvp, &(tp->coord), &x, &y );
8711  
8712     if ( abs (x - params->x) <= TRACKPOINT_SIZE_APPROX && abs (y - params->y) <= TRACKPOINT_SIZE_APPROX &&
8713         ((!params->closest_tp) ||        /* was the old trackpoint we already found closer than this one? */
8714           abs(x - params->x)+abs(y - params->y) < abs(x - params->closest_x)+abs(y - params->closest_y)))
8715     {
8716       params->closest_track_id = id;
8717       params->closest_tp = tp;
8718       params->closest_tpl = tpl;
8719       params->closest_x = x;
8720       params->closest_y = y;
8721     }
8722     tpl = tpl->next;
8723   }
8724 }
8725
8726 // ATM: Leave this as 'Track' only.
8727 //  Not overly bothered about having a snap to route trackpoint capability
8728 static VikTrackpoint *closest_tp_in_five_pixel_interval ( VikTrwLayer *vtl, VikViewport *vvp, gint x, gint y )
8729 {
8730   TPSearchParams params;
8731   params.x = x;
8732   params.y = y;
8733   params.vvp = vvp;
8734   params.closest_track_id = NULL;
8735   params.closest_tp = NULL;
8736   vik_viewport_get_min_max_lat_lon ( params.vvp, &(params.bbox.south), &(params.bbox.north), &(params.bbox.west), &(params.bbox.east) );
8737   g_hash_table_foreach ( vtl->tracks, (GHFunc) track_search_closest_tp, &params);
8738   return params.closest_tp;
8739 }
8740
8741 static VikWaypoint *closest_wp_in_five_pixel_interval ( VikTrwLayer *vtl, VikViewport *vvp, gint x, gint y )
8742 {
8743   WPSearchParams params;
8744   params.x = x;
8745   params.y = y;
8746   params.vvp = vvp;
8747   params.draw_images = vtl->drawimages;
8748   params.closest_wp = NULL;
8749   params.closest_wp_id = NULL;
8750   g_hash_table_foreach ( vtl->waypoints, (GHFunc) waypoint_search_closest_tp, &params);
8751   return params.closest_wp;
8752 }
8753
8754
8755 // Some forward declarations
8756 static void marker_begin_move ( tool_ed_t *t, gint x, gint y );
8757 static void marker_moveto ( tool_ed_t *t, gint x, gint y );
8758 static void marker_end_move ( tool_ed_t *t );
8759 //
8760
8761 static gboolean trw_layer_select_move ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp, tool_ed_t* t )
8762 {
8763   if ( t->holding ) {
8764     VikCoord new_coord;
8765     vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
8766
8767     // Here always allow snapping back to the original location
8768     //  this is useful when one decides not to move the thing afterall
8769     // If one wants to move the item only a little bit then don't hold down the 'snap' key!
8770  
8771     // snap to TP
8772     if ( event->state & GDK_CONTROL_MASK )
8773     {
8774       VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
8775       if ( tp )
8776         new_coord = tp->coord;
8777     }
8778
8779     // snap to WP
8780     if ( event->state & GDK_SHIFT_MASK )
8781     {
8782       VikWaypoint *wp = closest_wp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
8783       if ( wp )
8784         new_coord = wp->coord;
8785     }
8786     
8787     gint x, y;
8788     vik_viewport_coord_to_screen ( vvp, &new_coord, &x, &y );
8789
8790     marker_moveto ( t, x, y );
8791
8792     return TRUE;
8793   }
8794   return FALSE;
8795 }
8796
8797 static gboolean trw_layer_select_release ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp, tool_ed_t* t )
8798 {
8799   if ( t->holding && event->button == 1 )
8800   {
8801     VikCoord new_coord;
8802     vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
8803
8804     // snap to TP
8805     if ( event->state & GDK_CONTROL_MASK )
8806     {
8807       VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
8808       if ( tp )
8809         new_coord = tp->coord;
8810     }
8811
8812     // snap to WP
8813     if ( event->state & GDK_SHIFT_MASK )
8814     {
8815       VikWaypoint *wp = closest_wp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
8816       if ( wp )
8817         new_coord = wp->coord;
8818     }
8819
8820     marker_end_move ( t );
8821
8822     // Determine if working on a waypoint or a trackpoint
8823     if ( t->is_waypoint ) {
8824       // Update waypoint position
8825       vtl->current_wp->coord = new_coord;
8826       trw_layer_calculate_bounds_waypoints ( vtl );
8827       // Reset waypoint pointer
8828       vtl->current_wp    = NULL;
8829       vtl->current_wp_id = NULL;
8830     }
8831     else {
8832       if ( vtl->current_tpl ) {
8833         VIK_TRACKPOINT(vtl->current_tpl->data)->coord = new_coord;
8834
8835         if ( vtl->current_tp_track )
8836           vik_track_calculate_bounds ( vtl->current_tp_track );
8837
8838         if ( vtl->tpwin )
8839           if ( vtl->current_tp_track )
8840             vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, vtl->current_tp_track->name );
8841         // NB don't reset the selected trackpoint, thus ensuring it's still in the tpwin
8842       }
8843     }
8844
8845     vik_layer_emit_update ( VIK_LAYER(vtl) );
8846     return TRUE;
8847   }
8848   return FALSE;
8849 }
8850
8851 /*
8852   Returns true if a waypoint or track is found near the requested event position for this particular layer
8853   The item found is automatically selected
8854   This is a tool like feature but routed via the layer interface, since it's instigated by a 'global' layer tool in vikwindow.c
8855  */
8856 static gboolean trw_layer_select_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp, tool_ed_t* tet )
8857 {
8858   if ( event->button != 1 )
8859     return FALSE;
8860
8861   if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
8862     return FALSE;
8863
8864   if ( !vtl->tracks_visible && !vtl->waypoints_visible && !vtl->routes_visible )
8865     return FALSE;
8866
8867   LatLonBBox bbox;
8868   vik_viewport_get_min_max_lat_lon ( vvp, &(bbox.south), &(bbox.north), &(bbox.west), &(bbox.east) );
8869
8870   // Go for waypoints first as these often will be near a track, but it's likely the wp is wanted rather then the track
8871
8872   if ( vtl->waypoints_visible && BBOX_INTERSECT (vtl->waypoints_bbox, bbox ) ) {
8873     WPSearchParams wp_params;
8874     wp_params.vvp = vvp;
8875     wp_params.x = event->x;
8876     wp_params.y = event->y;
8877     wp_params.draw_images = vtl->drawimages;
8878     wp_params.closest_wp_id = NULL;
8879     wp_params.closest_wp = NULL;
8880
8881     g_hash_table_foreach ( vtl->waypoints, (GHFunc) waypoint_search_closest_tp, &wp_params);
8882
8883     if ( wp_params.closest_wp )  {
8884
8885       // Select
8886       vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, g_hash_table_lookup ( vtl->waypoints_iters, wp_params.closest_wp_id ), TRUE );
8887
8888       // Too easy to move it so must be holding shift to start immediately moving it
8889       //   or otherwise be previously selected but not have an image (otherwise clicking within image bounds (again) moves it)
8890       if ( event->state & GDK_SHIFT_MASK ||
8891            ( vtl->current_wp == wp_params.closest_wp && !vtl->current_wp->image ) ) {
8892         // Put into 'move buffer'
8893         // NB vvp & vw already set in tet
8894         tet->vtl = (gpointer)vtl;
8895         tet->is_waypoint = TRUE;
8896       
8897         marker_begin_move (tet, event->x, event->y);
8898       }
8899
8900       vtl->current_wp =    wp_params.closest_wp;
8901       vtl->current_wp_id = wp_params.closest_wp_id;
8902
8903       if ( event->type == GDK_2BUTTON_PRESS ) {
8904         if ( vtl->current_wp->image ) {
8905           menu_array_sublayer values;
8906           values[MA_VTL] = vtl;
8907           values[MA_MISC] = vtl->current_wp->image;
8908           trw_layer_show_picture ( values );
8909         }
8910       }
8911
8912       vik_layer_emit_update ( VIK_LAYER(vtl) );
8913
8914       return TRUE;
8915     }
8916   }
8917
8918   // Used for both track and route lists
8919   TPSearchParams tp_params;
8920   tp_params.vvp = vvp;
8921   tp_params.x = event->x;
8922   tp_params.y = event->y;
8923   tp_params.closest_track_id = NULL;
8924   tp_params.closest_tp = NULL;
8925   tp_params.closest_tpl = NULL;
8926   tp_params.bbox = bbox;
8927
8928   if (vtl->tracks_visible) {
8929     g_hash_table_foreach ( vtl->tracks, (GHFunc) track_search_closest_tp, &tp_params);
8930
8931     if ( tp_params.closest_tp )  {
8932
8933       // Always select + highlight the track
8934       vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, g_hash_table_lookup ( vtl->tracks_iters, tp_params.closest_track_id ), TRUE );
8935
8936       tet->is_waypoint = FALSE;
8937
8938       // Select the Trackpoint
8939       // Can move it immediately when control held or it's the previously selected tp
8940       if ( event->state & GDK_CONTROL_MASK ||
8941            vtl->current_tpl == tp_params.closest_tpl ) {
8942         // Put into 'move buffer'
8943         // NB vvp & vw already set in tet
8944         tet->vtl = (gpointer)vtl;
8945         marker_begin_move (tet, event->x, event->y);
8946       }
8947
8948       vtl->current_tpl = tp_params.closest_tpl;
8949       vtl->current_tp_id = tp_params.closest_track_id;
8950       vtl->current_tp_track = g_hash_table_lookup ( vtl->tracks, tp_params.closest_track_id );
8951
8952       set_statusbar_msg_info_trkpt ( vtl, tp_params.closest_tp );
8953
8954       if ( vtl->tpwin )
8955         vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, vtl->current_tp_track->name );
8956
8957       vik_layer_emit_update ( VIK_LAYER(vtl) );
8958       return TRUE;
8959     }
8960   }
8961
8962   // Try again for routes
8963   if (vtl->routes_visible) {
8964     g_hash_table_foreach ( vtl->routes, (GHFunc) track_search_closest_tp, &tp_params);
8965
8966     if ( tp_params.closest_tp )  {
8967
8968       // Always select + highlight the track
8969       vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, g_hash_table_lookup ( vtl->routes_iters, tp_params.closest_track_id ), TRUE );
8970
8971       tet->is_waypoint = FALSE;
8972
8973       // Select the Trackpoint
8974       // Can move it immediately when control held or it's the previously selected tp
8975       if ( event->state & GDK_CONTROL_MASK ||
8976            vtl->current_tpl == tp_params.closest_tpl ) {
8977         // Put into 'move buffer'
8978         // NB vvp & vw already set in tet
8979         tet->vtl = (gpointer)vtl;
8980         marker_begin_move (tet, event->x, event->y);
8981       }
8982
8983       vtl->current_tpl = tp_params.closest_tpl;
8984       vtl->current_tp_id = tp_params.closest_track_id;
8985       vtl->current_tp_track = g_hash_table_lookup ( vtl->routes, tp_params.closest_track_id );
8986
8987       set_statusbar_msg_info_trkpt ( vtl, tp_params.closest_tp );
8988
8989       if ( vtl->tpwin )
8990         vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, vtl->current_tp_track->name );
8991
8992       vik_layer_emit_update ( VIK_LAYER(vtl) );
8993       return TRUE;
8994     }
8995   }
8996
8997   /* these aren't the droids you're looking for */
8998   vtl->current_wp    = NULL;
8999   vtl->current_wp_id = NULL;
9000   trw_layer_cancel_current_tp ( vtl, FALSE );
9001
9002   // Blank info
9003   vik_statusbar_set_message ( vik_window_get_statusbar (VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl))), VIK_STATUSBAR_INFO, "" );
9004
9005   return FALSE;
9006 }
9007
9008 static gboolean trw_layer_show_selected_viewport_menu ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
9009 {
9010   if ( event->button != 3 )
9011     return FALSE;
9012
9013   if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
9014     return FALSE;
9015
9016   if ( !vtl->tracks_visible && !vtl->waypoints_visible && !vtl->routes_visible )
9017     return FALSE;
9018
9019   /* Post menu for the currently selected item */
9020
9021   /* See if a track is selected */
9022   VikTrack *track = (VikTrack*)vik_window_get_selected_track ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(vtl) );
9023   if ( track && track->visible ) {
9024
9025     if ( track->name ) {
9026
9027       if ( vtl->track_right_click_menu )
9028         g_object_ref_sink ( G_OBJECT(vtl->track_right_click_menu) );
9029
9030       vtl->track_right_click_menu = GTK_MENU ( gtk_menu_new () );
9031       
9032       trku_udata udataU;
9033       udataU.trk  = track;
9034       udataU.uuid = NULL;
9035
9036       gpointer *trkf;
9037       if ( track->is_route )
9038         trkf = g_hash_table_find ( vtl->routes, (GHRFunc) trw_layer_track_find_uuid, &udataU );
9039       else
9040         trkf = g_hash_table_find ( vtl->tracks, (GHRFunc) trw_layer_track_find_uuid, &udataU );
9041
9042       if ( trkf && udataU.uuid ) {
9043
9044         GtkTreeIter *iter;
9045         if ( track->is_route )
9046           iter = g_hash_table_lookup ( vtl->routes_iters, udataU.uuid );
9047         else
9048           iter = g_hash_table_lookup ( vtl->tracks_iters, udataU.uuid );
9049
9050         trw_layer_sublayer_add_menu_items ( vtl,
9051                                             vtl->track_right_click_menu,
9052                                             NULL,
9053                                             track->is_route ? VIK_TRW_LAYER_SUBLAYER_ROUTE : VIK_TRW_LAYER_SUBLAYER_TRACK,
9054                                             udataU.uuid,
9055                                             iter,
9056                                             vvp );
9057       }
9058
9059       gtk_menu_popup ( vtl->track_right_click_menu, NULL, NULL, NULL, NULL, event->button, gtk_get_current_event_time() );
9060         
9061       return TRUE;
9062     }
9063   }
9064
9065   /* See if a waypoint is selected */
9066   VikWaypoint *waypoint = (VikWaypoint*)vik_window_get_selected_waypoint ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(vtl) );
9067   if ( waypoint && waypoint->visible ) {
9068     if ( waypoint->name ) {
9069
9070       if ( vtl->wp_right_click_menu )
9071         g_object_ref_sink ( G_OBJECT(vtl->wp_right_click_menu) );
9072
9073       vtl->wp_right_click_menu = GTK_MENU ( gtk_menu_new () );
9074
9075       wpu_udata udata;
9076       udata.wp   = waypoint;
9077       udata.uuid = NULL;
9078
9079       gpointer *wpf = g_hash_table_find ( vtl->waypoints, (GHRFunc) trw_layer_waypoint_find_uuid, (gpointer) &udata );
9080
9081       if ( wpf && udata.uuid ) {
9082         GtkTreeIter *iter = g_hash_table_lookup ( vtl->waypoints_iters, udata.uuid );
9083
9084         trw_layer_sublayer_add_menu_items ( vtl,
9085                                             vtl->wp_right_click_menu,
9086                                             NULL,
9087                                             VIK_TRW_LAYER_SUBLAYER_WAYPOINT,
9088                                             udata.uuid,
9089                                             iter,
9090                                             vvp );
9091       }
9092       gtk_menu_popup ( vtl->wp_right_click_menu, NULL, NULL, NULL, NULL, event->button, gtk_get_current_event_time() );
9093
9094       return TRUE;
9095     }
9096   }
9097
9098   return FALSE;
9099 }
9100
9101 /* background drawing hook, to be passed the viewport */
9102 static gboolean tool_sync_done = TRUE;
9103
9104 static gboolean tool_sync(gpointer data)
9105 {
9106   VikViewport *vvp = data;
9107   gdk_threads_enter();
9108   vik_viewport_sync(vvp);
9109   tool_sync_done = TRUE;
9110   gdk_threads_leave();
9111   return FALSE;
9112 }
9113
9114 static void marker_begin_move ( tool_ed_t *t, gint x, gint y )
9115 {
9116   t->holding = TRUE;
9117   t->gc = vik_viewport_new_gc (t->vvp, "black", 2);
9118   gdk_gc_set_function ( t->gc, GDK_INVERT );
9119   vik_viewport_draw_rectangle ( t->vvp, t->gc, FALSE, x-3, y-3, 6, 6 );
9120   vik_viewport_sync(t->vvp);
9121   t->oldx = x;
9122   t->oldy = y;
9123 }
9124
9125 static void marker_moveto ( tool_ed_t *t, gint x, gint y )
9126 {
9127   VikViewport *vvp =  t->vvp;
9128   vik_viewport_draw_rectangle ( vvp, t->gc, FALSE, t->oldx-3, t->oldy-3, 6, 6 );
9129   vik_viewport_draw_rectangle ( vvp, t->gc, FALSE, x-3, y-3, 6, 6 );
9130   t->oldx = x;
9131   t->oldy = y;
9132
9133   if (tool_sync_done) {
9134     g_idle_add_full (G_PRIORITY_HIGH_IDLE + 10, tool_sync, vvp, NULL);
9135     tool_sync_done = FALSE;
9136   }
9137 }
9138
9139 static void marker_end_move ( tool_ed_t *t )
9140 {
9141   vik_viewport_draw_rectangle ( t->vvp, t->gc, FALSE, t->oldx-3, t->oldy-3, 6, 6 );
9142   g_object_unref ( t->gc );
9143   t->holding = FALSE;
9144 }
9145
9146 /*** Edit waypoint ****/
9147
9148 static gpointer tool_edit_waypoint_create ( VikWindow *vw, VikViewport *vvp)
9149 {
9150   tool_ed_t *t = g_new(tool_ed_t, 1);
9151   t->vvp = vvp;
9152   t->holding = FALSE;
9153   return t;
9154 }
9155
9156 static void tool_edit_waypoint_destroy ( tool_ed_t *t )
9157 {
9158   g_free ( t );
9159 }
9160
9161 static gboolean tool_edit_waypoint_click ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data )
9162 {
9163   WPSearchParams params;
9164   tool_ed_t *t = data;
9165   VikViewport *vvp = t->vvp;
9166
9167   if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
9168     return FALSE;
9169
9170   if ( t->holding ) {
9171     return TRUE;
9172   }
9173
9174   if ( !vtl->vl.visible || !vtl->waypoints_visible )
9175     return FALSE;
9176
9177   if ( vtl->current_wp && vtl->current_wp->visible )
9178   {
9179     /* first check if current WP is within area (other may be 'closer', but we want to move the current) */
9180     gint x, y;
9181     vik_viewport_coord_to_screen ( vvp, &(vtl->current_wp->coord), &x, &y );
9182
9183     if ( abs(x - event->x) <= WAYPOINT_SIZE_APPROX &&
9184          abs(y - event->y) <= WAYPOINT_SIZE_APPROX )
9185     {
9186       if ( event->button == 3 )
9187         vtl->waypoint_rightclick = TRUE; /* remember that we're clicking; other layers will ignore release signal */
9188       else {
9189         marker_begin_move(t, event->x, event->y);
9190       }
9191       return TRUE;
9192     }
9193   }
9194
9195   params.vvp = vvp;
9196   params.x = event->x;
9197   params.y = event->y;
9198   params.draw_images = vtl->drawimages;
9199   params.closest_wp_id = NULL;
9200   params.closest_wp = NULL;
9201   g_hash_table_foreach ( vtl->waypoints, (GHFunc) waypoint_search_closest_tp, &params);
9202   if ( vtl->current_wp && (vtl->current_wp == params.closest_wp) )
9203   {
9204     if ( event->button == 3 )
9205       vtl->waypoint_rightclick = TRUE; /* remember that we're clicking; other layers will ignore release signal */
9206     else
9207       marker_begin_move(t, event->x, event->y);
9208     return FALSE;
9209   }
9210   else if ( params.closest_wp )
9211   {
9212     if ( event->button == 3 )
9213       vtl->waypoint_rightclick = TRUE; /* remember that we're clicking; other layers will ignore release signal */
9214     else
9215       vtl->waypoint_rightclick = FALSE;
9216
9217     vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, g_hash_table_lookup ( vtl->waypoints_iters, params.closest_wp_id ), TRUE );
9218
9219     vtl->current_wp = params.closest_wp;
9220     vtl->current_wp_id = params.closest_wp_id;
9221
9222     /* could make it so don't update if old WP is off screen and new is null but oh well */
9223     vik_layer_emit_update ( VIK_LAYER(vtl) );
9224     return TRUE;
9225   }
9226
9227   vtl->current_wp = NULL;
9228   vtl->current_wp_id = NULL;
9229   vtl->waypoint_rightclick = FALSE;
9230   vik_layer_emit_update ( VIK_LAYER(vtl) );
9231   return FALSE;
9232 }
9233
9234 static gboolean tool_edit_waypoint_move ( VikTrwLayer *vtl, GdkEventMotion *event, gpointer data )
9235 {
9236   tool_ed_t *t = data;
9237   VikViewport *vvp = t->vvp;
9238
9239   if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
9240     return FALSE;
9241
9242   if ( t->holding ) {
9243     VikCoord new_coord;
9244     vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
9245
9246     /* snap to TP */
9247     if ( event->state & GDK_CONTROL_MASK )
9248     {
9249       VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
9250       if ( tp )
9251         new_coord = tp->coord;
9252     }
9253
9254     /* snap to WP */
9255     if ( event->state & GDK_SHIFT_MASK )
9256     {
9257       VikWaypoint *wp = closest_wp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
9258       if ( wp && wp != vtl->current_wp )
9259         new_coord = wp->coord;
9260     }
9261     
9262     { 
9263       gint x, y;
9264       vik_viewport_coord_to_screen ( vvp, &new_coord, &x, &y );
9265
9266       marker_moveto ( t, x, y );
9267     } 
9268     return TRUE;
9269   }
9270   return FALSE;
9271 }
9272
9273 static gboolean tool_edit_waypoint_release ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data )
9274 {
9275   tool_ed_t *t = data;
9276   VikViewport *vvp = t->vvp;
9277
9278   if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
9279     return FALSE;
9280   
9281   if ( t->holding && event->button == 1 )
9282   {
9283     VikCoord new_coord;
9284     vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
9285
9286     /* snap to TP */
9287     if ( event->state & GDK_CONTROL_MASK )
9288     {
9289       VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
9290       if ( tp )
9291         new_coord = tp->coord;
9292     }
9293
9294     /* snap to WP */
9295     if ( event->state & GDK_SHIFT_MASK )
9296     {
9297       VikWaypoint *wp = closest_wp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
9298       if ( wp && wp != vtl->current_wp )
9299         new_coord = wp->coord;
9300     }
9301
9302     marker_end_move ( t );
9303
9304     vtl->current_wp->coord = new_coord;
9305
9306     trw_layer_calculate_bounds_waypoints ( vtl );
9307     vik_layer_emit_update ( VIK_LAYER(vtl) );
9308     return TRUE;
9309   }
9310   /* PUT IN RIGHT PLACE!!! */
9311   if ( event->button == 3 && vtl->waypoint_rightclick )
9312   {
9313     if ( vtl->wp_right_click_menu )
9314       g_object_ref_sink ( G_OBJECT(vtl->wp_right_click_menu) );
9315     if ( vtl->current_wp ) {
9316       vtl->wp_right_click_menu = GTK_MENU ( gtk_menu_new () );
9317       trw_layer_sublayer_add_menu_items ( vtl, vtl->wp_right_click_menu, NULL, VIK_TRW_LAYER_SUBLAYER_WAYPOINT, vtl->current_wp_id, g_hash_table_lookup ( vtl->waypoints_iters, vtl->current_wp_id ), vvp );
9318       gtk_menu_popup ( vtl->wp_right_click_menu, NULL, NULL, NULL, NULL, event->button, gtk_get_current_event_time() );
9319     }
9320     vtl->waypoint_rightclick = FALSE;
9321   }
9322   return FALSE;
9323 }
9324
9325 /*** New track ****/
9326
9327 static gpointer tool_new_track_create ( VikWindow *vw, VikViewport *vvp)
9328 {
9329   return vvp;
9330 }
9331
9332 typedef struct {
9333   VikTrwLayer *vtl;
9334   GdkDrawable *drawable;
9335   GdkGC *gc;
9336   GdkPixmap *pixmap;
9337 } draw_sync_t;
9338
9339 /*
9340  * Draw specified pixmap
9341  */
9342 static gboolean draw_sync ( gpointer data )
9343 {
9344   draw_sync_t *ds = (draw_sync_t*) data;
9345   // Sometimes don't want to draw
9346   //  normally because another update has taken precedent such as panning the display
9347   //   which means this pixmap is no longer valid
9348   if ( ds->vtl->draw_sync_do ) {
9349     gdk_threads_enter();
9350     gdk_draw_drawable (ds->drawable,
9351                        ds->gc,
9352                        ds->pixmap,
9353                        0, 0, 0, 0, -1, -1);
9354     ds->vtl->draw_sync_done = TRUE;
9355     gdk_threads_leave();
9356   }
9357   g_free ( ds );
9358   return FALSE;
9359 }
9360
9361 static gchar* distance_string (gdouble distance)
9362 {
9363   gchar str[128];
9364
9365   /* draw label with distance */
9366   vik_units_distance_t dist_units = a_vik_get_units_distance ();
9367   switch (dist_units) {
9368   case VIK_UNITS_DISTANCE_MILES:
9369     if (distance >= VIK_MILES_TO_METERS(1) && distance < VIK_MILES_TO_METERS(100)) {
9370       g_sprintf(str, "%3.2f miles", VIK_METERS_TO_MILES(distance));
9371     } else if (distance < 1609.4) {
9372       g_sprintf(str, "%d yards", (int)(distance*1.0936133));
9373     } else {
9374       g_sprintf(str, "%d miles", (int)VIK_METERS_TO_MILES(distance));
9375     }
9376     break;
9377   default:
9378     // VIK_UNITS_DISTANCE_KILOMETRES
9379     if (distance >= 1000 && distance < 100000) {
9380       g_sprintf(str, "%3.2f km", distance/1000.0);
9381     } else if (distance < 1000) {
9382       g_sprintf(str, "%d m", (int)distance);
9383     } else {
9384       g_sprintf(str, "%d km", (int)distance/1000);
9385     }
9386     break;
9387   }
9388   return g_strdup (str);
9389 }
9390
9391 /*
9392  * Actually set the message in statusbar
9393  */
9394 static void statusbar_write (gdouble distance, gdouble elev_gain, gdouble elev_loss, gdouble last_step, gdouble angle, VikTrwLayer *vtl )
9395 {
9396   // Only show elevation data when track has some elevation properties
9397   gchar str_gain_loss[64];
9398   str_gain_loss[0] = '\0';
9399   gchar str_last_step[64];
9400   str_last_step[0] = '\0';
9401   gchar *str_total = distance_string (distance);
9402   
9403   if ( (elev_gain > 0.1) || (elev_loss > 0.1) ) {
9404     if ( a_vik_get_units_height () == VIK_UNITS_HEIGHT_METRES )
9405       g_sprintf(str_gain_loss, _(" - Gain %dm:Loss %dm"), (int)elev_gain, (int)elev_loss);
9406     else
9407       g_sprintf(str_gain_loss, _(" - Gain %dft:Loss %dft"), (int)VIK_METERS_TO_FEET(elev_gain), (int)VIK_METERS_TO_FEET(elev_loss));
9408   }
9409   
9410   if ( last_step > 0 ) {
9411       gchar *tmp = distance_string (last_step);
9412       g_sprintf(str_last_step, _(" - Bearing %3.1f° - Step %s"), RAD2DEG(angle), tmp);
9413       g_free ( tmp );
9414   }
9415   
9416   VikWindow *vw = VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl));
9417
9418   // Write with full gain/loss information
9419   gchar *msg = g_strdup_printf ( "Total %s%s%s", str_total, str_last_step, str_gain_loss);
9420   vik_statusbar_set_message ( vik_window_get_statusbar (vw), VIK_STATUSBAR_INFO, msg );
9421   g_free ( msg );
9422   g_free ( str_total );
9423 }
9424
9425 /*
9426  * Figure out what information should be set in the statusbar and then write it
9427  */
9428 static void update_statusbar ( VikTrwLayer *vtl )
9429 {
9430   // Get elevation data
9431   gdouble elev_gain, elev_loss;
9432   vik_track_get_total_elevation_gain ( vtl->current_track, &elev_gain, &elev_loss);
9433
9434   /* Find out actual distance of current track */
9435   gdouble distance = vik_track_get_length (vtl->current_track);
9436
9437   statusbar_write (distance, elev_gain, elev_loss, 0, 0, vtl);
9438 }
9439
9440
9441 static VikLayerToolFuncStatus tool_new_track_move ( VikTrwLayer *vtl, GdkEventMotion *event, VikViewport *vvp )
9442 {
9443   /* if we haven't sync'ed yet, we don't have time to do more. */
9444   if ( vtl->draw_sync_done && vtl->current_track && vtl->current_track->trackpoints ) {
9445     VikTrackpoint *last_tpt = vik_track_get_tp_last(vtl->current_track);
9446
9447     static GdkPixmap *pixmap = NULL;
9448     int w1, h1, w2, h2;
9449     // Need to check in case window has been resized
9450     w1 = vik_viewport_get_width(vvp);
9451     h1 = vik_viewport_get_height(vvp);
9452     if (!pixmap) {
9453       pixmap = gdk_pixmap_new ( gtk_widget_get_window(GTK_WIDGET(vvp)), w1, h1, -1 );
9454     }
9455     gdk_drawable_get_size (pixmap, &w2, &h2);
9456     if (w1 != w2 || h1 != h2) {
9457       g_object_unref ( G_OBJECT ( pixmap ) );
9458       pixmap = gdk_pixmap_new ( gtk_widget_get_window(GTK_WIDGET(vvp)), w1, h1, -1 );
9459     }
9460
9461     // Reset to background
9462     gdk_draw_drawable (pixmap,
9463                        vtl->current_track_newpoint_gc,
9464                        vik_viewport_get_pixmap(vvp),
9465                        0, 0, 0, 0, -1, -1);
9466
9467     draw_sync_t *passalong;
9468     gint x1, y1;
9469
9470     vik_viewport_coord_to_screen ( vvp, &(last_tpt->coord), &x1, &y1 );
9471
9472     // FOR SCREEN OVERLAYS WE MUST DRAW INTO THIS PIXMAP (when using the reset method)
9473     //  otherwise using vik_viewport_draw_* functions puts the data into the base pixmap,
9474     //  thus when we come to reset to the background it would include what we have already drawn!!
9475     gdk_draw_line ( pixmap,
9476                     vtl->current_track_newpoint_gc,
9477                     x1, y1, event->x, event->y );
9478     // Using this reset method is more reliable than trying to undraw previous efforts via the GDK_INVERT method
9479
9480     /* Find out actual distance of current track */
9481     gdouble distance = vik_track_get_length (vtl->current_track);
9482
9483     // Now add distance to where the pointer is //
9484     VikCoord coord;
9485     struct LatLon ll;
9486     vik_viewport_screen_to_coord ( vvp, (gint) event->x, (gint) event->y, &coord );
9487     vik_coord_to_latlon ( &coord, &ll );
9488     gdouble last_step = vik_coord_diff( &coord, &(last_tpt->coord));
9489     distance = distance + last_step;
9490
9491     // Get elevation data
9492     gdouble elev_gain, elev_loss;
9493     vik_track_get_total_elevation_gain ( vtl->current_track, &elev_gain, &elev_loss);
9494
9495     // Adjust elevation data (if available) for the current pointer position
9496     gdouble elev_new;
9497     elev_new = (gdouble) a_dems_get_elev_by_coord ( &coord, VIK_DEM_INTERPOL_BEST );
9498     if ( elev_new != VIK_DEM_INVALID_ELEVATION ) {
9499       if ( last_tpt->altitude != VIK_DEFAULT_ALTITUDE ) {
9500         // Adjust elevation of last track point
9501         if ( elev_new > last_tpt->altitude )
9502           // Going up
9503           elev_gain += elev_new - last_tpt->altitude;
9504         else
9505           // Going down
9506           elev_loss += last_tpt->altitude - elev_new;
9507       }
9508     }
9509
9510     //
9511     // Display of the distance 'tooltip' during track creation is controlled by a preference
9512     //
9513     if ( a_vik_get_create_track_tooltip() ) {
9514
9515       gchar *str = distance_string (distance);
9516
9517       PangoLayout *pl = gtk_widget_create_pango_layout (GTK_WIDGET(vvp), NULL);
9518       pango_layout_set_font_description (pl, gtk_widget_get_style(GTK_WIDGET(vvp))->font_desc);
9519       pango_layout_set_text (pl, str, -1);
9520       gint wd, hd;
9521       pango_layout_get_pixel_size ( pl, &wd, &hd );
9522
9523       gint xd,yd;
9524       // offset from cursor a bit depending on font size
9525       xd = event->x + 10;
9526       yd = event->y - hd;
9527
9528       // Create a background block to make the text easier to read over the background map
9529       GdkGC *background_block_gc = vik_viewport_new_gc ( vvp, "#cccccc", 1);
9530       gdk_draw_rectangle (pixmap, background_block_gc, TRUE, xd-2, yd-2, wd+4, hd+2);
9531       gdk_draw_layout (pixmap, vtl->current_track_newpoint_gc, xd, yd, pl);
9532
9533       g_object_unref ( G_OBJECT ( pl ) );
9534       g_object_unref ( G_OBJECT ( background_block_gc ) );
9535       g_free (str);
9536     }
9537
9538     passalong = g_new(draw_sync_t,1); // freed by draw_sync()
9539     passalong->vtl = vtl;
9540     passalong->pixmap = pixmap;
9541     passalong->drawable = gtk_widget_get_window(GTK_WIDGET(vvp));
9542     passalong->gc = vtl->current_track_newpoint_gc;
9543
9544     gdouble angle;
9545     gdouble baseangle;
9546     vik_viewport_compute_bearing ( vvp, x1, y1, event->x, event->y, &angle, &baseangle );
9547
9548     // Update statusbar with full gain/loss information
9549     statusbar_write (distance, elev_gain, elev_loss, last_step, angle, vtl);
9550
9551     // draw pixmap when we have time to
9552     g_idle_add_full (G_PRIORITY_HIGH_IDLE + 10, draw_sync, passalong, NULL);
9553     vtl->draw_sync_done = FALSE;
9554     return VIK_LAYER_TOOL_ACK_GRAB_FOCUS;
9555   }
9556   return VIK_LAYER_TOOL_ACK;
9557 }
9558
9559 // NB vtl->current_track must be valid
9560 static void undo_trackpoint_add ( VikTrwLayer *vtl )
9561 {
9562   // 'undo'
9563   if ( vtl->current_track->trackpoints ) {
9564     // TODO rework this...
9565     //vik_trackpoint_free ( vik_track_get_tp_last (vtl->current_track) );
9566     GList *last = g_list_last(vtl->current_track->trackpoints);
9567     g_free ( last->data );
9568     vtl->current_track->trackpoints = g_list_remove_link ( vtl->current_track->trackpoints, last );
9569
9570     vik_track_calculate_bounds ( vtl->current_track );
9571   }
9572 }
9573
9574 static gboolean tool_new_track_key_press ( VikTrwLayer *vtl, GdkEventKey *event, VikViewport *vvp )
9575 {
9576   if ( vtl->current_track && event->keyval == GDK_Escape ) {
9577     vtl->current_track = NULL;
9578     vik_layer_emit_update ( VIK_LAYER(vtl) );
9579     return TRUE;
9580   } else if ( vtl->current_track && event->keyval == GDK_BackSpace ) {
9581     undo_trackpoint_add ( vtl );
9582     update_statusbar ( vtl );
9583     vik_layer_emit_update ( VIK_LAYER(vtl) );
9584     return TRUE;
9585   }
9586   return FALSE;
9587 }
9588
9589 /*
9590  * Common function to handle trackpoint button requests on either a route or a track
9591  *  . enables adding a point via normal click
9592  *  . enables removal of last point via right click
9593  *  . finishing of the track or route via double clicking
9594  */
9595 static gboolean tool_new_track_or_route_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
9596 {
9597   VikTrackpoint *tp;
9598
9599   if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
9600     return FALSE;
9601
9602   if ( event->button == 2 ) {
9603     // As the display is panning, the new track pixmap is now invalid so don't draw it
9604     //  otherwise this drawing done results in flickering back to an old image
9605     vtl->draw_sync_do = FALSE;
9606     return FALSE;
9607   }
9608
9609   if ( event->button == 3 )
9610   {
9611     if ( !vtl->current_track )
9612       return FALSE;
9613     undo_trackpoint_add ( vtl );
9614     update_statusbar ( vtl );
9615     vik_layer_emit_update ( VIK_LAYER(vtl) );
9616     return TRUE;
9617   }
9618
9619   if ( event->type == GDK_2BUTTON_PRESS )
9620   {
9621     /* subtract last (duplicate from double click) tp then end */
9622     if ( vtl->current_track && vtl->current_track->trackpoints && vtl->ct_x1 == vtl->ct_x2 && vtl->ct_y1 == vtl->ct_y2 )
9623     {
9624       /* undo last, then end */
9625       undo_trackpoint_add ( vtl );
9626       vtl->current_track = NULL;
9627     }
9628     vik_layer_emit_update ( VIK_LAYER(vtl) );
9629     return TRUE;
9630   }
9631
9632   tp = vik_trackpoint_new();
9633   vik_viewport_screen_to_coord ( vvp, event->x, event->y, &(tp->coord) );
9634
9635   /* snap to other TP */
9636   if ( event->state & GDK_CONTROL_MASK )
9637   {
9638     VikTrackpoint *other_tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
9639     if ( other_tp )
9640       tp->coord = other_tp->coord;
9641   }
9642
9643   tp->newsegment = FALSE;
9644   tp->has_timestamp = FALSE;
9645   tp->timestamp = 0;
9646
9647   if ( vtl->current_track ) {
9648     vik_track_add_trackpoint ( vtl->current_track, tp, TRUE ); // Ensure bounds is updated
9649     /* Auto attempt to get elevation from DEM data (if it's available) */
9650     vik_track_apply_dem_data_last_trackpoint ( vtl->current_track );
9651   }
9652
9653   vtl->ct_x1 = vtl->ct_x2;
9654   vtl->ct_y1 = vtl->ct_y2;
9655   vtl->ct_x2 = event->x;
9656   vtl->ct_y2 = event->y;
9657
9658   vik_layer_emit_update ( VIK_LAYER(vtl) );
9659   return TRUE;
9660 }
9661
9662 static gboolean tool_new_track_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
9663 {
9664   // ----------------------------------------------------- if current is a route - switch to new track
9665   if ( event->button == 1 && ( ! vtl->current_track || (vtl->current_track && vtl->current_track->is_route ) ))
9666   {
9667     gchar *name = trw_layer_new_unique_sublayer_name(vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, _("Track"));
9668     if ( ( name = a_dialog_new_track ( VIK_GTK_WINDOW_FROM_LAYER(vtl), name, FALSE ) ) )
9669     {
9670       new_track_create_common ( vtl, name );
9671       g_free ( name );
9672     }
9673     else
9674       return TRUE;
9675   }
9676   return tool_new_track_or_route_click ( vtl, event, vvp );
9677 }
9678
9679 static void tool_new_track_release ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
9680 {
9681   if ( event->button == 2 ) {
9682     // Pan moving ended - enable potential point drawing again
9683     vtl->draw_sync_do = TRUE;
9684     vtl->draw_sync_done = TRUE;
9685   }
9686 }
9687
9688 /*** New route ****/
9689
9690 static gpointer tool_new_route_create ( VikWindow *vw, VikViewport *vvp)
9691 {
9692   return vvp;
9693 }
9694
9695 static gboolean tool_new_route_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
9696 {
9697   // -------------------------- if current is a track - switch to new route
9698   if ( event->button == 1 && ( ! vtl->current_track || (vtl->current_track && !vtl->current_track->is_route ) ) )
9699   {
9700     gchar *name = trw_layer_new_unique_sublayer_name(vtl, VIK_TRW_LAYER_SUBLAYER_ROUTE, _("Route"));
9701     if ( ( name = a_dialog_new_track ( VIK_GTK_WINDOW_FROM_LAYER(vtl), name, TRUE ) ) ) {
9702       new_route_create_common ( vtl, name );
9703       g_free ( name );
9704     }
9705     else
9706       return TRUE;
9707   }
9708   return tool_new_track_or_route_click ( vtl, event, vvp );
9709 }
9710
9711 /*** New waypoint ****/
9712
9713 static gpointer tool_new_waypoint_create ( VikWindow *vw, VikViewport *vvp)
9714 {
9715   return vvp;
9716 }
9717
9718 static gboolean tool_new_waypoint_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
9719 {
9720   VikCoord coord;
9721   if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
9722     return FALSE;
9723   vik_viewport_screen_to_coord ( vvp, event->x, event->y, &coord );
9724   if (vik_trw_layer_new_waypoint ( vtl, VIK_GTK_WINDOW_FROM_LAYER(vtl), &coord ) && VIK_LAYER(vtl)->visible) {
9725     trw_layer_calculate_bounds_waypoints ( vtl );
9726     vik_layer_emit_update ( VIK_LAYER(vtl) );
9727   }
9728   return TRUE;
9729 }
9730
9731
9732 /*** Edit trackpoint ****/
9733
9734 static gpointer tool_edit_trackpoint_create ( VikWindow *vw, VikViewport *vvp)
9735 {
9736   tool_ed_t *t = g_new(tool_ed_t, 1);
9737   t->vvp = vvp;
9738   t->holding = FALSE;
9739   return t;
9740 }
9741
9742 static void tool_edit_trackpoint_destroy ( tool_ed_t *t )
9743 {
9744   g_free ( t );
9745 }
9746
9747 static gboolean tool_edit_trackpoint_click ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data )
9748 {
9749   tool_ed_t *t = data;
9750   VikViewport *vvp = t->vvp;
9751   TPSearchParams params;
9752   /* OUTDATED DOCUMENTATION:
9753    find 5 pixel range on each side. then put these UTM, and a pointer
9754    to the winning track name (and maybe the winning track itself), and a
9755    pointer to the winning trackpoint, inside an array or struct. pass 
9756    this along, do a foreach on the tracks which will do a foreach on the 
9757    trackpoints. */
9758   params.vvp = vvp;
9759   params.x = event->x;
9760   params.y = event->y;
9761   params.closest_track_id = NULL;
9762   /* TODO: should get track listitem so we can break it up, make a new track, mess it up, all that. */
9763   params.closest_tp = NULL;
9764   params.closest_tpl = NULL;
9765   vik_viewport_get_min_max_lat_lon ( vvp, &(params.bbox.south), &(params.bbox.north), &(params.bbox.west), &(params.bbox.east) );
9766
9767   if ( event->button != 1 ) 
9768     return FALSE;
9769
9770   if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
9771     return FALSE;
9772
9773   if ( !vtl->vl.visible || !vtl->tracks_visible || !vtl->routes_visible )
9774     return FALSE;
9775
9776   if ( vtl->current_tpl )
9777   {
9778     /* first check if it is within range of prev. tp. and if current_tp track is shown. (if it is, we are moving that trackpoint.) */
9779     VikTrackpoint *tp = VIK_TRACKPOINT(vtl->current_tpl->data);
9780     VikTrack *current_tr = VIK_TRACK(g_hash_table_lookup(vtl->tracks, vtl->current_tp_id));
9781     if ( !current_tr )
9782       return FALSE;
9783
9784     gint x, y;
9785     vik_viewport_coord_to_screen ( vvp, &(tp->coord), &x, &y );
9786
9787     if ( current_tr->visible && 
9788          abs(x - event->x) < TRACKPOINT_SIZE_APPROX &&
9789          abs(y - event->y) < TRACKPOINT_SIZE_APPROX ) {
9790       marker_begin_move ( t, event->x, event->y );
9791       return TRUE;
9792     }
9793
9794   }
9795
9796   if ( vtl->tracks_visible )
9797     g_hash_table_foreach ( vtl->tracks, (GHFunc) track_search_closest_tp, &params);
9798
9799   if ( params.closest_tp )
9800   {
9801     vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, g_hash_table_lookup ( vtl->tracks_iters, params.closest_track_id ), TRUE );
9802     vtl->current_tpl = params.closest_tpl;
9803     vtl->current_tp_id = params.closest_track_id;
9804     vtl->current_tp_track = g_hash_table_lookup ( vtl->tracks, params.closest_track_id );
9805     trw_layer_tpwin_init ( vtl );
9806     set_statusbar_msg_info_trkpt ( vtl, params.closest_tp );
9807     vik_layer_emit_update ( VIK_LAYER(vtl) );
9808     return TRUE;
9809   }
9810
9811   if ( vtl->routes_visible )
9812     g_hash_table_foreach ( vtl->routes, (GHFunc) track_search_closest_tp, &params);
9813
9814   if ( params.closest_tp )
9815   {
9816     vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, g_hash_table_lookup ( vtl->routes_iters, params.closest_track_id ), TRUE );
9817     vtl->current_tpl = params.closest_tpl;
9818     vtl->current_tp_id = params.closest_track_id;
9819     vtl->current_tp_track = g_hash_table_lookup ( vtl->routes, params.closest_track_id );
9820     trw_layer_tpwin_init ( vtl );
9821     set_statusbar_msg_info_trkpt ( vtl, params.closest_tp );
9822     vik_layer_emit_update ( VIK_LAYER(vtl) );
9823     return TRUE;
9824   }
9825
9826   /* these aren't the droids you're looking for */
9827   return FALSE;
9828 }
9829
9830 static gboolean tool_edit_trackpoint_move ( VikTrwLayer *vtl, GdkEventMotion *event, gpointer data )
9831 {
9832   tool_ed_t *t = data;
9833   VikViewport *vvp = t->vvp;
9834
9835   if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
9836     return FALSE;
9837
9838   if ( t->holding )
9839   {
9840     VikCoord new_coord;
9841     vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
9842
9843     /* snap to TP */
9844     if ( event->state & GDK_CONTROL_MASK )
9845     {
9846       VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
9847       if ( tp && tp != vtl->current_tpl->data )
9848         new_coord = tp->coord;
9849     }
9850     //    VIK_TRACKPOINT(vtl->current_tpl->data)->coord = new_coord;
9851     { 
9852       gint x, y;
9853       vik_viewport_coord_to_screen ( vvp, &new_coord, &x, &y );
9854       marker_moveto ( t, x, y );
9855     } 
9856
9857     return TRUE;
9858   }
9859   return FALSE;
9860 }
9861
9862 static gboolean tool_edit_trackpoint_release ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data )
9863 {
9864   tool_ed_t *t = data;
9865   VikViewport *vvp = t->vvp;
9866
9867   if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
9868     return FALSE;
9869   if ( event->button != 1) 
9870     return FALSE;
9871
9872   if ( t->holding ) {
9873     VikCoord new_coord;
9874     vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
9875
9876     /* snap to TP */
9877     if ( event->state & GDK_CONTROL_MASK )
9878     {
9879       VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
9880       if ( tp && tp != vtl->current_tpl->data )
9881         new_coord = tp->coord;
9882     }
9883
9884     VIK_TRACKPOINT(vtl->current_tpl->data)->coord = new_coord;
9885     if ( vtl->current_tp_track )
9886       vik_track_calculate_bounds ( vtl->current_tp_track );
9887
9888     marker_end_move ( t );
9889
9890     /* diff dist is diff from orig */
9891     if ( vtl->tpwin )
9892       vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, vtl->current_tp_track->name );
9893
9894     vik_layer_emit_update ( VIK_LAYER(vtl) );
9895     return TRUE;
9896   }
9897   return FALSE;
9898 }
9899
9900
9901 /*** Route Finder ***/
9902 static gpointer tool_route_finder_create ( VikWindow *vw, VikViewport *vvp)
9903 {
9904   return vvp;
9905 }
9906
9907 static gboolean tool_route_finder_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
9908 {
9909   VikCoord tmp;
9910   if ( !vtl ) return FALSE;
9911   vik_viewport_screen_to_coord ( vvp, event->x, event->y, &tmp );
9912   if ( event->button == 3 && vtl->route_finder_current_track ) {
9913     VikCoord *new_end;
9914     new_end = vik_track_cut_back_to_double_point ( vtl->route_finder_current_track );
9915     if ( new_end ) {
9916       vtl->route_finder_coord = *new_end;
9917       g_free ( new_end );
9918       vik_layer_emit_update ( VIK_LAYER(vtl) );
9919       /* remove last ' to:...' */
9920       if ( vtl->route_finder_current_track->comment ) {
9921         gchar *last_to = strrchr ( vtl->route_finder_current_track->comment, 't' );
9922         if ( last_to && (last_to - vtl->route_finder_current_track->comment > 1) ) {
9923           gchar *new_comment = g_strndup ( vtl->route_finder_current_track->comment,
9924                                            last_to - vtl->route_finder_current_track->comment - 1);
9925           vik_track_set_comment_no_copy ( vtl->route_finder_current_track, new_comment );
9926         }
9927       }
9928     }
9929   }
9930   else if ( vtl->route_finder_started || (event->state & GDK_CONTROL_MASK && vtl->route_finder_current_track) ) {
9931     struct LatLon start, end;
9932
9933     vik_coord_to_latlon ( &(vtl->route_finder_coord), &start );
9934     vik_coord_to_latlon ( &(tmp), &end );
9935     vtl->route_finder_coord = tmp; /* for continuations */
9936
9937     /* these are checked when adding a track from a file (vik_trw_layer_filein_add_track) */
9938     if ( event->state & GDK_CONTROL_MASK && vtl->route_finder_current_track ) {
9939       vtl->route_finder_append = TRUE;  // merge tracks. keep started true.
9940     } else {
9941       vtl->route_finder_check_added_track = TRUE;
9942       vtl->route_finder_started = FALSE;
9943     }
9944
9945     vik_routing_default_find ( vtl, start, end);
9946
9947     /* see if anything was done -- a track was added or appended to */
9948     if ( vtl->route_finder_check_added_track && vtl->route_finder_added_track ) {
9949       vik_track_set_comment_no_copy ( vtl->route_finder_added_track, g_strdup_printf("from: %f,%f to: %f,%f", start.lat, start.lon, end.lat, end.lon ) );
9950     } else if ( vtl->route_finder_append == FALSE && vtl->route_finder_current_track ) {
9951       /* route_finder_append was originally TRUE but set to FALSE by filein_add_track */
9952       gchar *new_comment = g_strdup_printf("%s to: %f,%f", vtl->route_finder_current_track->comment, end.lat, end.lon );
9953       vik_track_set_comment_no_copy ( vtl->route_finder_current_track, new_comment );
9954     }
9955
9956     if ( vtl->route_finder_added_track )
9957       vik_track_calculate_bounds ( vtl->route_finder_added_track );
9958
9959     vtl->route_finder_added_track = NULL;
9960     vtl->route_finder_check_added_track = FALSE;
9961     vtl->route_finder_append = FALSE;
9962
9963     vik_layer_emit_update ( VIK_LAYER(vtl) );
9964   } else {
9965     vtl->route_finder_started = TRUE;
9966     vtl->route_finder_coord = tmp;
9967     vtl->route_finder_current_track = NULL;
9968   }
9969   return TRUE;
9970 }
9971
9972 /*** Show picture ****/
9973
9974 static gpointer tool_show_picture_create ( VikWindow *vw, VikViewport *vvp)
9975 {
9976   return vvp;
9977 }
9978
9979 /* Params are: vvp, event, last match found or NULL */
9980 static void tool_show_picture_wp ( const gpointer id, VikWaypoint *wp, gpointer params[3] )
9981 {
9982   if ( wp->image && wp->visible )
9983   {
9984     gint x, y, slackx, slacky;
9985     GdkEventButton *event = (GdkEventButton *) params[1];
9986
9987     vik_viewport_coord_to_screen ( VIK_VIEWPORT(params[0]), &(wp->coord), &x, &y );
9988     slackx = wp->image_width / 2;
9989     slacky = wp->image_height / 2;
9990     if (    x <= event->x + slackx && x >= event->x - slackx
9991          && y <= event->y + slacky && y >= event->y - slacky )
9992     {
9993       params[2] = wp->image; /* we've found a match. however continue searching
9994                               * since we want to find the last match -- that
9995                               * is, the match that was drawn last. */
9996     }
9997   }
9998 }
9999
10000 static void trw_layer_show_picture ( menu_array_sublayer values )
10001 {
10002   /* thanks to the Gaim people for showing me ShellExecute and g_spawn_command_line_async */
10003 #ifdef WINDOWS
10004   ShellExecute(NULL, "open", (char *) values[MA_MISC], NULL, NULL, SW_SHOWNORMAL);
10005 #else /* WINDOWS */
10006   GError *err = NULL;
10007   gchar *quoted_file = g_shell_quote ( (gchar *) values[MA_MISC] );
10008   gchar *cmd = g_strdup_printf ( "%s %s", a_vik_get_image_viewer(), quoted_file );
10009   g_free ( quoted_file );
10010   if ( ! g_spawn_command_line_async ( cmd, &err ) )
10011     {
10012       a_dialog_error_msg_extra ( VIK_GTK_WINDOW_FROM_LAYER(values[MA_VTL]), _("Could not launch %s to open file."), a_vik_get_image_viewer() );
10013       g_error_free ( err );
10014     }
10015   g_free ( cmd );
10016 #endif /* WINDOWS */
10017 }
10018
10019 static gboolean tool_show_picture_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
10020 {
10021   gpointer params[3] = { vvp, event, NULL };
10022   if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
10023     return FALSE;
10024   g_hash_table_foreach ( vtl->waypoints, (GHFunc) tool_show_picture_wp, params );
10025   if ( params[2] )
10026   {
10027     static menu_array_sublayer values;
10028     values[MA_VTL] = vtl;
10029     values[MA_MISC] = params[2];
10030     trw_layer_show_picture ( values );
10031     return TRUE; /* found a match */
10032   }
10033   else
10034     return FALSE; /* go through other layers, searching for a match */
10035 }
10036
10037 /***************************************************************************
10038  ** End tool code 
10039  ***************************************************************************/
10040
10041
10042 static void image_wp_make_list ( const gpointer id, VikWaypoint *wp, GSList **pics )
10043 {
10044   if ( wp->image && ( ! a_thumbnails_exists ( wp->image ) ) )
10045     *pics = g_slist_append ( *pics, (gpointer) g_strdup ( wp->image ) );
10046 }
10047
10048 /* Structure for thumbnail creating data used in the background thread */
10049 typedef struct {
10050   VikTrwLayer *vtl; // Layer needed for redrawing
10051   GSList *pics;     // Image list
10052 } thumbnail_create_thread_data;
10053
10054 static int create_thumbnails_thread ( thumbnail_create_thread_data *tctd, gpointer threaddata )
10055 {
10056   guint total = g_slist_length(tctd->pics), done = 0;
10057   while ( tctd->pics )
10058   {
10059     a_thumbnails_create ( (gchar *) tctd->pics->data );
10060     int result = a_background_thread_progress ( threaddata, ((gdouble) ++done) / total );
10061     if ( result != 0 )
10062       return -1; /* Abort thread */
10063
10064     tctd->pics = tctd->pics->next;
10065   }
10066
10067   // Redraw to show the thumbnails as they are now created
10068   if ( IS_VIK_LAYER(tctd->vtl) )
10069     vik_layer_emit_update ( VIK_LAYER(tctd->vtl) ); // NB update from background thread
10070
10071   return 0;
10072 }
10073
10074 static void thumbnail_create_thread_free ( thumbnail_create_thread_data *tctd )
10075 {
10076   while ( tctd->pics )
10077   {
10078     g_free ( tctd->pics->data );
10079     tctd->pics = g_slist_delete_link ( tctd->pics, tctd->pics );
10080   }
10081   g_free ( tctd );
10082 }
10083
10084 void trw_layer_verify_thumbnails ( VikTrwLayer *vtl, GtkWidget *vp )
10085 {
10086   if ( ! vtl->has_verified_thumbnails )
10087   {
10088     GSList *pics = NULL;
10089     g_hash_table_foreach ( vtl->waypoints, (GHFunc) image_wp_make_list, &pics );
10090     if ( pics )
10091     {
10092       gint len = g_slist_length ( pics );
10093       gchar *tmp = g_strdup_printf ( _("Creating %d Image Thumbnails..."), len );
10094       thumbnail_create_thread_data *tctd = g_malloc ( sizeof(thumbnail_create_thread_data) );
10095       tctd->vtl = vtl;
10096       tctd->pics = pics;
10097       a_background_thread ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
10098                             tmp,
10099                             (vik_thr_func) create_thumbnails_thread,
10100                             tctd,
10101                             (vik_thr_free_func) thumbnail_create_thread_free,
10102                             NULL,
10103                             len );
10104       g_free ( tmp );
10105     }
10106   }
10107 }
10108
10109 static const gchar* my_track_colors ( gint ii )
10110 {
10111   static const gchar* colors[VIK_TRW_LAYER_TRACK_GCS] = {
10112     "#2d870a",
10113     "#135D34",
10114     "#0a8783",
10115     "#0e4d87",
10116     "#05469f",
10117     "#695CBB",
10118     "#2d059f",
10119     "#4a059f",
10120     "#5A171A",
10121     "#96059f"
10122   };
10123   // Fast and reliable way of returning a colour
10124   return colors[(ii % VIK_TRW_LAYER_TRACK_GCS)];
10125 }
10126
10127 static void trw_layer_track_alloc_colors ( VikTrwLayer *vtl )
10128 {
10129   GHashTableIter iter;
10130   gpointer key, value;
10131
10132   gint ii = 0;
10133   // Tracks
10134   g_hash_table_iter_init ( &iter, vtl->tracks );
10135
10136   while ( g_hash_table_iter_next (&iter, &key, &value) ) {
10137
10138     // Tracks get a random spread of colours if not already assigned
10139     if ( ! VIK_TRACK(value)->has_color ) {
10140       if ( vtl->drawmode == DRAWMODE_ALL_SAME_COLOR )
10141         VIK_TRACK(value)->color = vtl->track_color;
10142       else {
10143         gdk_color_parse ( my_track_colors (ii), &(VIK_TRACK(value)->color) );
10144       }
10145       VIK_TRACK(value)->has_color = TRUE;
10146     }
10147
10148     trw_layer_update_treeview ( vtl, VIK_TRACK(value) );
10149
10150     ii++;
10151     if (ii > VIK_TRW_LAYER_TRACK_GCS)
10152       ii = 0;
10153   }
10154
10155   // Routes
10156   ii = 0;
10157   g_hash_table_iter_init ( &iter, vtl->routes );
10158
10159   while ( g_hash_table_iter_next (&iter, &key, &value) ) {
10160
10161     // Routes get an intermix of reds
10162     if ( ! VIK_TRACK(value)->has_color ) {
10163       if ( ii )
10164         gdk_color_parse ( "#FF0000" , &(VIK_TRACK(value)->color) ); // Red
10165       else
10166         gdk_color_parse ( "#B40916" , &(VIK_TRACK(value)->color) ); // Dark Red
10167       VIK_TRACK(value)->has_color = TRUE;
10168     }
10169
10170     trw_layer_update_treeview ( vtl, VIK_TRACK(value) );
10171
10172     ii = !ii;
10173   }
10174 }
10175
10176 /*
10177  * (Re)Calculate the bounds of the waypoints in this layer,
10178  * This should be called whenever waypoints are changed
10179  */
10180 void trw_layer_calculate_bounds_waypoints ( VikTrwLayer *vtl )
10181 {
10182   struct LatLon topleft = { 0.0, 0.0 };
10183   struct LatLon bottomright = { 0.0, 0.0 };
10184   struct LatLon ll;
10185
10186   GHashTableIter iter;
10187   gpointer key, value;
10188
10189   g_hash_table_iter_init ( &iter, vtl->waypoints );
10190
10191   // Set bounds to first point
10192   if ( g_hash_table_iter_next (&iter, &key, &value) ) {
10193     vik_coord_to_latlon ( &(VIK_WAYPOINT(value)->coord), &topleft );
10194     vik_coord_to_latlon ( &(VIK_WAYPOINT(value)->coord), &bottomright );
10195   }
10196
10197   // Ensure there is another point...
10198   if ( g_hash_table_size ( vtl->waypoints ) > 1 ) {
10199
10200     while ( g_hash_table_iter_next (&iter, &key, &value) ) {
10201
10202       // See if this point increases the bounds.
10203       vik_coord_to_latlon ( &(VIK_WAYPOINT(value)->coord), &ll );
10204
10205       if ( ll.lat > topleft.lat) topleft.lat = ll.lat;
10206       if ( ll.lon < topleft.lon) topleft.lon = ll.lon;
10207       if ( ll.lat < bottomright.lat) bottomright.lat = ll.lat;
10208       if ( ll.lon > bottomright.lon) bottomright.lon = ll.lon;
10209     }
10210   }
10211
10212   vtl->waypoints_bbox.north = topleft.lat;
10213   vtl->waypoints_bbox.east = bottomright.lon;
10214   vtl->waypoints_bbox.south = bottomright.lat;
10215   vtl->waypoints_bbox.west = topleft.lon;
10216 }
10217
10218 static void trw_layer_calculate_bounds_track ( gpointer id, VikTrack *trk )
10219 {
10220   vik_track_calculate_bounds ( trk );
10221 }
10222
10223 static void trw_layer_calculate_bounds_tracks ( VikTrwLayer *vtl )
10224 {
10225   g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_calculate_bounds_track, NULL );
10226   g_hash_table_foreach ( vtl->routes, (GHFunc) trw_layer_calculate_bounds_track, NULL );
10227 }
10228
10229 static void trw_layer_sort_all ( VikTrwLayer *vtl )
10230 {
10231   if ( ! VIK_LAYER(vtl)->vt )
10232     return;
10233
10234   // Obviously need 2 to tango - sorting with only 1 (or less) is a lonely activity!
10235   if ( g_hash_table_size (vtl->tracks) > 1 )
10236     vik_treeview_sort_children ( VIK_LAYER(vtl)->vt, &(vtl->tracks_iter), vtl->track_sort_order );
10237
10238   if ( g_hash_table_size (vtl->routes) > 1 )
10239     vik_treeview_sort_children ( VIK_LAYER(vtl)->vt, &(vtl->routes_iter), vtl->track_sort_order );
10240
10241   if ( g_hash_table_size (vtl->waypoints) > 1 )
10242     vik_treeview_sort_children ( VIK_LAYER(vtl)->vt, &(vtl->waypoints_iter), vtl->wp_sort_order );
10243 }
10244
10245 static void trw_layer_post_read ( VikTrwLayer *vtl, GtkWidget *vvp, gboolean from_file )
10246 {
10247   if ( VIK_LAYER(vtl)->realized )
10248     trw_layer_verify_thumbnails ( vtl, vvp );
10249   trw_layer_track_alloc_colors ( vtl );
10250
10251   trw_layer_calculate_bounds_waypoints ( vtl );
10252   trw_layer_calculate_bounds_tracks ( vtl );
10253
10254   // Apply treeview sort after loading all the tracks for this layer
10255   //  (rather than sorted insert on each individual track additional)
10256   //  and after subsequent changes to the properties as the specified order may have changed.
10257   //  since the sorting of a treeview section is now very quick
10258   // NB sorting is also performed after every name change as well to maintain the list order
10259   trw_layer_sort_all ( vtl );
10260
10261   // Setting metadata time if not otherwise set
10262   if ( vtl->metadata ) {
10263
10264     gboolean need_to_set_time = TRUE;
10265     if ( vtl->metadata->timestamp ) {
10266       need_to_set_time = FALSE;
10267       if ( !g_strcmp0(vtl->metadata->timestamp, "" ) )
10268         need_to_set_time = TRUE;
10269     }
10270
10271     if ( need_to_set_time ) {
10272       // Could rewrite this as a general get first time of a TRW Layer function
10273       GTimeVal timestamp;
10274       timestamp.tv_usec = 0;
10275       gboolean has_timestamp = FALSE;
10276
10277       GList *gl = NULL;
10278       gl = g_hash_table_get_values ( vtl->tracks );
10279       gl = g_list_sort ( gl, vik_track_compare_timestamp );
10280       gl = g_list_first ( gl );
10281
10282       // Check times of tracks
10283       if ( gl ) {
10284         // Only need to check the first track as they have been sorted by time
10285         VikTrack *trk = (VikTrack*)gl->data;
10286         // Assume trackpoints already sorted by time
10287         VikTrackpoint *tpt = vik_track_get_tp_first(trk);
10288         if ( tpt && tpt->has_timestamp ) {
10289           timestamp.tv_sec = tpt->timestamp;
10290           has_timestamp = TRUE;
10291         }
10292         g_list_free ( gl );
10293       }
10294
10295       if ( !has_timestamp ) {
10296         // 'Last' resort - current time
10297         // Get before waypoint tests - so that if a waypoint time value (in the past) is found it should be used
10298         g_get_current_time ( &timestamp );
10299
10300         // Check times of waypoints
10301         gl = g_hash_table_get_values ( vtl->waypoints );
10302         GList *iter;
10303         for (iter = g_list_first (gl); iter != NULL; iter = g_list_next (iter)) {
10304           VikWaypoint *wpt = (VikWaypoint*)iter->data;
10305           if ( wpt->has_timestamp ) {
10306             if ( timestamp.tv_sec > wpt->timestamp ) {
10307               timestamp.tv_sec = wpt->timestamp;
10308               has_timestamp = TRUE;
10309             }
10310           }
10311         }
10312         g_list_free ( gl );
10313       }
10314
10315       vtl->metadata->timestamp = g_time_val_to_iso8601 ( &timestamp );
10316     }
10317   }
10318 }
10319
10320 VikCoordMode vik_trw_layer_get_coord_mode ( VikTrwLayer *vtl )
10321 {
10322   return vtl->coord_mode;
10323 }
10324
10325 /**
10326  * Uniquify the whole layer
10327  * Also requires the layers panel as the names shown there need updating too
10328  * Returns whether the operation was successful or not
10329  */
10330 gboolean vik_trw_layer_uniquify ( VikTrwLayer *vtl, VikLayersPanel *vlp )
10331 {
10332   if ( vtl && vlp ) {
10333     vik_trw_layer_uniquify_tracks ( vtl, vlp, vtl->tracks, TRUE );
10334     vik_trw_layer_uniquify_tracks ( vtl, vlp, vtl->routes, FALSE );
10335     vik_trw_layer_uniquify_waypoints ( vtl, vlp );
10336     return TRUE;
10337   }
10338   return FALSE;
10339 }
10340
10341 static void waypoint_convert ( const gpointer id, VikWaypoint *wp, VikCoordMode *dest_mode )
10342 {
10343   vik_coord_convert ( &(wp->coord), *dest_mode );
10344 }
10345
10346 static void track_convert ( const gpointer id, VikTrack *tr, VikCoordMode *dest_mode )
10347 {
10348   vik_track_convert ( tr, *dest_mode );
10349 }
10350
10351 static void trw_layer_change_coord_mode ( VikTrwLayer *vtl, VikCoordMode dest_mode )
10352 {
10353   if ( vtl->coord_mode != dest_mode )
10354   {
10355     vtl->coord_mode = dest_mode;
10356     g_hash_table_foreach ( vtl->waypoints, (GHFunc) waypoint_convert, &dest_mode );
10357     g_hash_table_foreach ( vtl->tracks, (GHFunc) track_convert, &dest_mode );
10358     g_hash_table_foreach ( vtl->routes, (GHFunc) track_convert, &dest_mode );
10359   }
10360 }
10361
10362 static void trw_layer_set_menu_selection ( VikTrwLayer *vtl, guint16 selection )
10363 {
10364   vtl->menu_selection = selection;
10365 }
10366
10367 static guint16 trw_layer_get_menu_selection ( VikTrwLayer *vtl )
10368 {
10369   return (vtl->menu_selection);
10370 }
10371
10372 /* ----------- Downloading maps along tracks --------------- */
10373
10374 static int get_download_area_width(VikViewport *vvp, gdouble zoom_level, struct LatLon *wh)
10375 {
10376   /* TODO: calculating based on current size of viewport */
10377   const gdouble w_at_zoom_0_125 = 0.0013;
10378   const gdouble h_at_zoom_0_125 = 0.0011;
10379   gdouble zoom_factor = zoom_level/0.125;
10380
10381   wh->lat = h_at_zoom_0_125 * zoom_factor;
10382   wh->lon = w_at_zoom_0_125 * zoom_factor;
10383
10384   return 0;   /* all OK */
10385 }
10386
10387 static VikCoord *get_next_coord(VikCoord *from, VikCoord *to, struct LatLon *dist, gdouble gradient)
10388 {
10389   if ((dist->lon >= ABS(to->east_west - from->east_west)) &&
10390       (dist->lat >= ABS(to->north_south - from->north_south)))
10391     return NULL;
10392
10393   VikCoord *coord = g_malloc(sizeof(VikCoord));
10394   coord->mode = VIK_COORD_LATLON;
10395
10396   if (ABS(gradient) < 1) {
10397     if (from->east_west > to->east_west)
10398       coord->east_west = from->east_west - dist->lon;
10399     else
10400       coord->east_west = from->east_west + dist->lon;
10401     coord->north_south = gradient * (coord->east_west - from->east_west) + from->north_south;
10402   } else {
10403     if (from->north_south > to->north_south)
10404       coord->north_south = from->north_south - dist->lat;
10405     else
10406       coord->north_south = from->north_south + dist->lat;
10407     coord->east_west = (1/gradient) * (coord->north_south - from->north_south) + from->north_south;
10408   }
10409
10410   return coord;
10411 }
10412
10413 static GList *add_fillins(GList *list, VikCoord *from, VikCoord *to, struct LatLon *dist)
10414 {
10415   /* TODO: handle virtical track (to->east_west - from->east_west == 0) */
10416   gdouble gradient = (to->north_south - from->north_south)/(to->east_west - from->east_west);
10417
10418   VikCoord *next = from;
10419   while (TRUE) {
10420     if ((next = get_next_coord(next, to, dist, gradient)) == NULL)
10421         break;
10422     list = g_list_prepend(list, next);
10423   }
10424
10425   return list;
10426 }
10427
10428 void vik_track_download_map(VikTrack *tr, VikMapsLayer *vml, VikViewport *vvp, gdouble zoom_level)
10429 {
10430   typedef struct _Rect {
10431     VikCoord tl;
10432     VikCoord br;
10433     VikCoord center;
10434   } Rect;
10435 #define GLRECT(iter) ((Rect *)((iter)->data))
10436
10437   struct LatLon wh;
10438   GList *rects_to_download = NULL;
10439   GList *rect_iter;
10440
10441   if (get_download_area_width(vvp, zoom_level, &wh))
10442     return;
10443
10444   GList *iter = tr->trackpoints;
10445   if (!iter)
10446     return;
10447
10448   gboolean new_map = TRUE;
10449   VikCoord *cur_coord, tl, br;
10450   Rect *rect;
10451   while (iter) {
10452     cur_coord = &(VIK_TRACKPOINT(iter->data))->coord;
10453     if (new_map) {
10454       vik_coord_set_area(cur_coord, &wh, &tl, &br);
10455       rect = g_malloc(sizeof(Rect));
10456       rect->tl = tl;
10457       rect->br = br;
10458       rect->center = *cur_coord;
10459       rects_to_download = g_list_prepend(rects_to_download, rect);
10460       new_map = FALSE;
10461       iter = iter->next;
10462       continue;
10463     }
10464     gboolean found = FALSE;
10465     for (rect_iter = rects_to_download; rect_iter; rect_iter = rect_iter->next) {
10466       if (vik_coord_inside(cur_coord, &GLRECT(rect_iter)->tl, &GLRECT(rect_iter)->br)) {
10467         found = TRUE;
10468         break;
10469       }
10470     }
10471     if (found)
10472         iter = iter->next;
10473     else
10474       new_map = TRUE;
10475   }
10476
10477   GList *fillins = NULL;
10478   /* 'fillin' doesn't work in UTM mode - potentially ending up in massive loop continually allocating memory - hence don't do it */
10479   /* seems that ATM the function get_next_coord works only for LATLON */
10480   if ( cur_coord->mode == VIK_COORD_LATLON ) {
10481     /* fill-ins for far apart points */
10482     GList *cur_rect, *next_rect;
10483     for (cur_rect = rects_to_download;
10484          (next_rect = cur_rect->next) != NULL;
10485          cur_rect = cur_rect->next) {
10486       if ((wh.lon < ABS(GLRECT(cur_rect)->center.east_west - GLRECT(next_rect)->center.east_west)) ||
10487           (wh.lat < ABS(GLRECT(cur_rect)->center.north_south - GLRECT(next_rect)->center.north_south))) {
10488         fillins = add_fillins(fillins, &GLRECT(cur_rect)->center, &GLRECT(next_rect)->center, &wh);
10489       }
10490     }
10491   } else
10492     g_message("%s: this feature works only in Mercator mode", __FUNCTION__);
10493
10494   if (fillins) {
10495     GList *fiter = fillins;
10496     while (fiter) {
10497       cur_coord = (VikCoord *)(fiter->data);
10498       vik_coord_set_area(cur_coord, &wh, &tl, &br);
10499       rect = g_malloc(sizeof(Rect));
10500       rect->tl = tl;
10501       rect->br = br;
10502       rect->center = *cur_coord;
10503       rects_to_download = g_list_prepend(rects_to_download, rect);
10504       fiter = fiter->next;
10505     }
10506   }
10507
10508   for (rect_iter = rects_to_download; rect_iter; rect_iter = rect_iter->next) {
10509     vik_maps_layer_download_section (vml, vvp, &(((Rect *)(rect_iter->data))->tl), &(((Rect *)(rect_iter->data))->br), zoom_level);
10510   }
10511
10512   if (fillins) {
10513     for (iter = fillins; iter; iter = iter->next)
10514       g_free(iter->data);
10515     g_list_free(fillins);
10516   }
10517   if (rects_to_download) {
10518     for (rect_iter = rects_to_download; rect_iter; rect_iter = rect_iter->next)
10519       g_free(rect_iter->data);
10520     g_list_free(rects_to_download);
10521   }
10522 }
10523
10524 static void trw_layer_download_map_along_track_cb ( menu_array_sublayer values )
10525 {
10526   VikMapsLayer *vml;
10527   gint selected_map;
10528   gchar *zoomlist[] = {"0.125", "0.25", "0.5", "1", "2", "4", "8", "16", "32", "64", "128", "256", "512", "1024", NULL };
10529   gdouble zoom_vals[] = {0.125, 0.25, 0.5, 1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024};
10530   gint selected_zoom, default_zoom;
10531
10532   VikTrwLayer *vtl = values[MA_VTL];
10533   VikLayersPanel *vlp = values[MA_VLP];
10534   VikTrack *trk;
10535   if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
10536     trk = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
10537   else
10538     trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
10539   if ( !trk )
10540     return;
10541
10542   VikViewport *vvp = vik_window_viewport((VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl)));
10543
10544   GList *vmls = vik_layers_panel_get_all_layers_of_type(vlp, VIK_LAYER_MAPS, TRUE); // Includes hidden map layer types
10545   int num_maps = g_list_length(vmls);
10546
10547   if (!num_maps) {
10548     a_dialog_error_msg(VIK_GTK_WINDOW_FROM_LAYER(vtl), _("No map layer in use. Create one first") );
10549     return;
10550   }
10551
10552   // Convert from list of vmls to list of names. Allowing the user to select one of them
10553   gchar **map_names = g_malloc(1 + num_maps * sizeof(gpointer));
10554   VikMapsLayer **map_layers = g_malloc(1 + num_maps * sizeof(gpointer));
10555
10556   gchar **np = map_names;
10557   VikMapsLayer **lp = map_layers;
10558   int i;
10559   for (i = 0; i < num_maps; i++) {
10560     vml = (VikMapsLayer *)(vmls->data);
10561     *lp++ = vml;
10562     *np++ = vik_maps_layer_get_map_label(vml);
10563     vmls = vmls->next;
10564   }
10565   // Mark end of the array lists
10566   *lp = NULL;
10567   *np = NULL;
10568
10569   gdouble cur_zoom = vik_viewport_get_zoom(vvp);
10570   for (default_zoom = 0; default_zoom < G_N_ELEMENTS(zoom_vals); default_zoom++) {
10571     if (cur_zoom == zoom_vals[default_zoom])
10572       break;
10573   }
10574   default_zoom = (default_zoom == G_N_ELEMENTS(zoom_vals)) ? G_N_ELEMENTS(zoom_vals) - 1 : default_zoom;
10575
10576   if (!a_dialog_map_n_zoom(VIK_GTK_WINDOW_FROM_LAYER(vtl), map_names, 0, zoomlist, default_zoom, &selected_map, &selected_zoom))
10577     goto done;
10578
10579   vik_track_download_map(trk, map_layers[selected_map], vvp, zoom_vals[selected_zoom]);
10580
10581 done:
10582   for (i = 0; i < num_maps; i++)
10583     g_free(map_names[i]);
10584   g_free(map_names);
10585   g_free(map_layers);
10586
10587   g_list_free(vmls);
10588
10589 }
10590
10591 /**** lowest waypoint number calculation ***/
10592 static gint highest_wp_number_name_to_number(const gchar *name) {
10593   if ( strlen(name) == 3 ) {
10594     int n = atoi(name);
10595     if ( n < 100 && name[0] != '0' )
10596       return -1;
10597     if ( n < 10 && name[0] != '0' )
10598       return -1;
10599     return n;
10600   }
10601   return -1;
10602 }
10603
10604
10605 static void highest_wp_number_reset(VikTrwLayer *vtl)
10606 {
10607   vtl->highest_wp_number = -1;
10608 }
10609
10610 static void highest_wp_number_add_wp(VikTrwLayer *vtl, const gchar *new_wp_name)
10611 {
10612   /* if is bigger that top, add it */
10613   gint new_wp_num = highest_wp_number_name_to_number(new_wp_name);
10614   if ( new_wp_num > vtl->highest_wp_number )
10615     vtl->highest_wp_number = new_wp_num;
10616 }
10617
10618 static void highest_wp_number_remove_wp(VikTrwLayer *vtl, const gchar *old_wp_name)
10619 {
10620   /* if wasn't top, do nothing. if was top, count backwards until we find one used */
10621   gint old_wp_num = highest_wp_number_name_to_number(old_wp_name);
10622   if ( vtl->highest_wp_number == old_wp_num ) {
10623     gchar buf[4];
10624     vtl->highest_wp_number--;
10625
10626     g_snprintf(buf,4,"%03d", vtl->highest_wp_number );
10627     /* search down until we find something that *does* exist */
10628
10629     while ( vtl->highest_wp_number > 0 && ! vik_trw_layer_get_waypoint ( vtl, buf )) {
10630       vtl->highest_wp_number--;
10631       g_snprintf(buf,4,"%03d", vtl->highest_wp_number );
10632     }
10633   }
10634 }
10635
10636 /* get lowest unused number */
10637 static gchar *highest_wp_number_get(VikTrwLayer *vtl)
10638 {
10639   gchar buf[4];
10640   if ( vtl->highest_wp_number < 0 || vtl->highest_wp_number >= 999 )
10641     return NULL;
10642   g_snprintf(buf,4,"%03d", vtl->highest_wp_number+1 );
10643   return g_strdup(buf);
10644 }
10645
10646 /**
10647  * trw_layer_create_track_list_both:
10648  *
10649  * Create the latest list of tracks and routes
10650  */
10651 static GList* trw_layer_create_track_list_both ( VikLayer *vl, gpointer user_data )
10652 {
10653   VikTrwLayer *vtl = VIK_TRW_LAYER(vl);
10654   GList *tracks = NULL;
10655   tracks = g_list_concat ( tracks, g_hash_table_get_values ( vik_trw_layer_get_tracks ( vtl ) ) );
10656   tracks = g_list_concat ( tracks, g_hash_table_get_values ( vik_trw_layer_get_routes ( vtl ) ) );
10657
10658   return vik_trw_layer_build_track_list_t ( vtl, tracks );
10659 }
10660
10661 static void trw_layer_track_list_dialog_single ( menu_array_sublayer values )
10662 {
10663   VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
10664
10665   gchar *title = NULL;
10666   if ( GPOINTER_TO_INT(values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_TRACKS )
10667     title = g_strdup_printf ( _("%s: Track List"), VIK_LAYER(vtl)->name );
10668   else
10669     title = g_strdup_printf ( _("%s: Route List"), VIK_LAYER(vtl)->name );
10670
10671   vik_trw_layer_track_list_show_dialog ( title, VIK_LAYER(vtl), values[MA_SUBTYPE], trw_layer_create_track_list, FALSE );
10672   g_free ( title );
10673 }
10674
10675 static void trw_layer_track_list_dialog ( menu_array_layer values )
10676 {
10677   VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
10678
10679   gchar *title = g_strdup_printf ( _("%s: Track and Route List"), VIK_LAYER(vtl)->name );
10680   vik_trw_layer_track_list_show_dialog ( title, VIK_LAYER(vtl), NULL, trw_layer_create_track_list_both, FALSE );
10681   g_free ( title );
10682 }
10683
10684 static void trw_layer_waypoint_list_dialog ( menu_array_layer values )
10685 {
10686   VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
10687
10688   gchar *title = g_strdup_printf ( _("%s: Waypoint List"), VIK_LAYER(vtl)->name );
10689   vik_trw_layer_waypoint_list_show_dialog ( title, VIK_LAYER(vtl), NULL, trw_layer_create_waypoint_list, FALSE );
10690   g_free ( title );
10691 }