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