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