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