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