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