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