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