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