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