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