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