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