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