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