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