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