]> git.street.me.uk Git - andy/viking.git/blob - src/viktrwlayer.c
Fix: Improve internal redrawing method.
[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), FALSE );
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), FALSE );
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  * Function to show basic track point information on the statusbar
1825  */
1826 static void set_statusbar_msg_info_trkpt ( VikTrwLayer *vtl, VikTrackpoint *trkpt )
1827 {
1828   gchar tmp_buf1[64];
1829   switch (a_vik_get_units_height ()) {
1830   case VIK_UNITS_HEIGHT_FEET:
1831     g_snprintf(tmp_buf1, sizeof(tmp_buf1), _("Trkpt: Alt %dft"), (int)round(VIK_METERS_TO_FEET(trkpt->altitude)));
1832     break;
1833   default:
1834     //VIK_UNITS_HEIGHT_METRES:
1835     g_snprintf(tmp_buf1, sizeof(tmp_buf1), _("Trkpt: Alt %dm"), (int)round(trkpt->altitude));
1836   }
1837   
1838   gchar tmp_buf2[64];
1839   tmp_buf2[0] = '\0';
1840   if ( trkpt->has_timestamp ) {
1841     // Compact date time format
1842     strftime (tmp_buf2, sizeof(tmp_buf2), _(" | Time %x %X"), localtime(&(trkpt->timestamp)));
1843   }
1844
1845   // Position part
1846   // Position is put later on, as this bit may not be seen if the display is not big enough,
1847   //   one can easily use the current pointer position to see this if needed
1848   gchar *lat = NULL, *lon = NULL;
1849   static struct LatLon ll;
1850   vik_coord_to_latlon (&(trkpt->coord), &ll);
1851   a_coords_latlon_to_string ( &ll, &lat, &lon );
1852
1853   // Track name
1854   // Again is put later on, as this bit may not be seen if the display is not big enough
1855   //  trackname can be seen from the treeview (when enabled)
1856   // Also name could be very long to not leave room for anything else
1857   gchar tmp_buf3[64];
1858   tmp_buf3[0] = '\0';
1859   if ( vtl->current_tp_track_name ) {
1860     g_snprintf(tmp_buf3, sizeof(tmp_buf3),  _(" | Track: %s"), vtl->current_tp_track_name );
1861   }
1862
1863   // Combine parts to make overall message
1864   gchar *msg = g_strdup_printf (_("%s%s | %s %s %s"), tmp_buf1, tmp_buf2, lat, lon, tmp_buf3);
1865   vik_statusbar_set_message ( vik_window_get_statusbar (VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl))), VIK_STATUSBAR_INFO, msg );
1866   g_free ( lat );
1867   g_free ( lon );
1868   g_free ( msg );
1869 }
1870
1871 /*
1872  * Function to show basic waypoint information on the statusbar
1873  */
1874 static void set_statusbar_msg_info_wpt ( VikTrwLayer *vtl, VikWaypoint *wpt )
1875 {
1876   gchar tmp_buf1[64];
1877   switch (a_vik_get_units_height ()) {
1878   case VIK_UNITS_HEIGHT_FEET:
1879     g_snprintf(tmp_buf1, sizeof(tmp_buf1), _("Wpt: Alt %dft"), (int)round(VIK_METERS_TO_FEET(wpt->altitude)));
1880     break;
1881   default:
1882     //VIK_UNITS_HEIGHT_METRES:
1883     g_snprintf(tmp_buf1, sizeof(tmp_buf1), _("Wpt: Alt %dm"), (int)round(wpt->altitude));
1884   }
1885   
1886   // Position part
1887   // Position is put last, as this bit is most likely not to be seen if the display is not big enough,
1888   //   one can easily use the current pointer position to see this if needed
1889   gchar *lat = NULL, *lon = NULL;
1890   static struct LatLon ll;
1891   vik_coord_to_latlon (&(wpt->coord), &ll);
1892   a_coords_latlon_to_string ( &ll, &lat, &lon );
1893
1894   // Combine parts to make overall message
1895   gchar *msg;
1896   if ( wpt->comment )
1897     // Add comment if available
1898     msg = g_strdup_printf ( _("%s | %s %s | Comment: %s"), tmp_buf1, lat, lon, wpt->comment );
1899   else
1900     msg = g_strdup_printf ( _("%s | %s %s"), tmp_buf1, lat, lon );
1901   vik_statusbar_set_message ( vik_window_get_statusbar (VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl))), VIK_STATUSBAR_INFO, msg );
1902   g_free ( lat );
1903   g_free ( lon );
1904   g_free ( msg );
1905 }
1906
1907 /**
1908  * General layer selection function, find out which bit is selected and take appropriate action
1909  */
1910 static gboolean trw_layer_selected ( VikTrwLayer *l, gint subtype, gpointer sublayer, gint type, gpointer vlp )
1911 {
1912   // Reset
1913   l->current_wp      = NULL;
1914   l->current_wp_name = NULL;
1915   trw_layer_cancel_current_tp ( l, FALSE );
1916
1917   // Clear statusbar
1918   vik_statusbar_set_message ( vik_window_get_statusbar (VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(l))), VIK_STATUSBAR_INFO, "" );
1919
1920   switch ( type )
1921     {
1922     case VIK_TREEVIEW_TYPE_LAYER:
1923       {
1924         vik_window_set_selected_trw_layer ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l), l );
1925         /* Mark for redraw */
1926         return TRUE;
1927       }
1928       break;
1929
1930     case VIK_TREEVIEW_TYPE_SUBLAYER:
1931       {
1932         switch ( subtype )
1933           {
1934           case VIK_TRW_LAYER_SUBLAYER_TRACKS:
1935             {
1936               vik_window_set_selected_tracks ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l), l->tracks, l );
1937               /* Mark for redraw */
1938               return TRUE;
1939             }
1940             break;
1941           case VIK_TRW_LAYER_SUBLAYER_TRACK:
1942             {
1943               VikTrack *track = g_hash_table_lookup ( l->tracks, sublayer );
1944               vik_window_set_selected_track ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l), (gpointer)track, l, sublayer );
1945               /* Mark for redraw */
1946               return TRUE;
1947             }
1948             break;
1949           case VIK_TRW_LAYER_SUBLAYER_WAYPOINTS:
1950             {
1951               vik_window_set_selected_waypoints ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l), l->waypoints, l );
1952               /* Mark for redraw */
1953               return TRUE;
1954             }
1955             break;
1956           case VIK_TRW_LAYER_SUBLAYER_WAYPOINT:
1957             {
1958               VikWaypoint *wpt = g_hash_table_lookup ( l->waypoints, sublayer );
1959               vik_window_set_selected_waypoint ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l), (gpointer)wpt, l, sublayer );
1960               // Show some waypoint info
1961               set_statusbar_msg_info_wpt ( l, wpt );
1962               /* Mark for redraw */
1963               return TRUE;
1964             }
1965             break;
1966           default:
1967             {
1968               return vik_window_clear_highlight ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l) );
1969             }
1970             break;
1971           }
1972         return FALSE;
1973       }
1974       break;
1975
1976     default:
1977       return vik_window_clear_highlight ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l) );
1978       break;
1979     }
1980 }
1981
1982 GHashTable *vik_trw_layer_get_tracks ( VikTrwLayer *l )
1983 {
1984   return l->tracks;
1985 }
1986
1987 GHashTable *vik_trw_layer_get_waypoints ( VikTrwLayer *l )
1988 {
1989   return l->waypoints;
1990 }
1991
1992 static void trw_layer_find_maxmin_waypoints ( const gchar *name, const VikWaypoint *w, struct LatLon maxmin[2] )
1993 {
1994   static VikCoord fixme;
1995   vik_coord_copy_convert ( &(w->coord), VIK_COORD_LATLON, &fixme );
1996   if ( VIK_LATLON(&fixme)->lat > maxmin[0].lat || maxmin[0].lat == 0.0 )
1997     maxmin[0].lat = VIK_LATLON(&fixme)->lat;
1998   if ( VIK_LATLON(&fixme)->lat < maxmin[1].lat || maxmin[1].lat == 0.0 )
1999     maxmin[1].lat = VIK_LATLON(&fixme)->lat;
2000   if ( VIK_LATLON(&fixme)->lon > maxmin[0].lon || maxmin[0].lon == 0.0 )
2001     maxmin[0].lon = VIK_LATLON(&fixme)->lon;
2002   if ( VIK_LATLON(&fixme)->lon < maxmin[1].lon || maxmin[1].lon == 0.0 )
2003     maxmin[1].lon = VIK_LATLON(&fixme)->lon;
2004 }
2005
2006 static void trw_layer_find_maxmin_tracks ( const gchar *name, GList **t, struct LatLon maxmin[2] )
2007 {
2008   GList *tr = *t;
2009   static VikCoord fixme;
2010
2011   while ( tr )
2012   {
2013     vik_coord_copy_convert ( &(VIK_TRACKPOINT(tr->data)->coord), VIK_COORD_LATLON, &fixme );
2014     if ( VIK_LATLON(&fixme)->lat > maxmin[0].lat || maxmin[0].lat == 0.0 )
2015       maxmin[0].lat = VIK_LATLON(&fixme)->lat;
2016     if ( VIK_LATLON(&fixme)->lat < maxmin[1].lat || maxmin[1].lat == 0.0 )
2017       maxmin[1].lat = VIK_LATLON(&fixme)->lat;
2018     if ( VIK_LATLON(&fixme)->lon > maxmin[0].lon || maxmin[0].lon == 0.0 )
2019       maxmin[0].lon = VIK_LATLON(&fixme)->lon;
2020     if ( VIK_LATLON(&fixme)->lon < maxmin[1].lon || maxmin[1].lon == 0.0 )
2021       maxmin[1].lon = VIK_LATLON(&fixme)->lon;
2022     tr = tr->next;
2023   }
2024 }
2025
2026 static void trw_layer_find_maxmin (VikTrwLayer *vtl, struct LatLon maxmin[2])
2027 {
2028   struct LatLon wpt_maxmin[2] = { {0.0,0.0}, {0.0,0.0} };
2029   struct LatLon trk_maxmin[2] = { {0.0,0.0}, {0.0,0.0} };
2030   
2031   g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_find_maxmin_waypoints, wpt_maxmin );
2032   g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_find_maxmin_tracks, trk_maxmin );
2033   if ((wpt_maxmin[0].lat != 0.0 && wpt_maxmin[0].lat > trk_maxmin[0].lat) || trk_maxmin[0].lat == 0.0) {
2034     maxmin[0].lat = wpt_maxmin[0].lat;
2035   }
2036   else {
2037     maxmin[0].lat = trk_maxmin[0].lat;
2038   }
2039   if ((wpt_maxmin[0].lon != 0.0 && wpt_maxmin[0].lon > trk_maxmin[0].lon) || trk_maxmin[0].lon == 0.0) {
2040     maxmin[0].lon = wpt_maxmin[0].lon;
2041   }
2042   else {
2043     maxmin[0].lon = trk_maxmin[0].lon;
2044   }
2045   if ((wpt_maxmin[1].lat != 0.0 && wpt_maxmin[1].lat < trk_maxmin[1].lat) || trk_maxmin[1].lat == 0.0) {
2046     maxmin[1].lat = wpt_maxmin[1].lat;
2047   }
2048   else {
2049     maxmin[1].lat = trk_maxmin[1].lat;
2050   }
2051   if ((wpt_maxmin[1].lon != 0.0 && wpt_maxmin[1].lon < trk_maxmin[1].lon) || trk_maxmin[1].lon == 0.0) {
2052     maxmin[1].lon = wpt_maxmin[1].lon;
2053   }
2054   else {
2055     maxmin[1].lon = trk_maxmin[1].lon;
2056   }
2057 }
2058
2059 gboolean vik_trw_layer_find_center ( VikTrwLayer *vtl, VikCoord *dest )
2060 {
2061   /* 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... */
2062   struct LatLon maxmin[2] = { {0.0,0.0}, {0.0,0.0} };
2063   trw_layer_find_maxmin (vtl, maxmin);
2064   if (maxmin[0].lat == 0.0 && maxmin[0].lon == 0.0 && maxmin[1].lat == 0.0 && maxmin[1].lon == 0.0)
2065     return FALSE;
2066   else
2067   {
2068     struct LatLon average = { (maxmin[0].lat+maxmin[1].lat)/2, (maxmin[0].lon+maxmin[1].lon)/2 };
2069     vik_coord_load_from_latlon ( dest, vtl->coord_mode, &average );
2070     return TRUE;
2071   }
2072 }
2073
2074 static void trw_layer_centerize ( gpointer layer_and_vlp[2] )
2075 {
2076   VikCoord coord;
2077   if ( vik_trw_layer_find_center ( VIK_TRW_LAYER(layer_and_vlp[0]), &coord ) )
2078     goto_coord ( layer_and_vlp[1], NULL, NULL, &coord );
2079   else
2080     a_dialog_info_msg ( VIK_GTK_WINDOW_FROM_LAYER(layer_and_vlp[0]), _("This layer has no waypoints or trackpoints.") );
2081 }
2082
2083 static void trw_layer_zoom_to_show_latlons ( VikTrwLayer *vtl, VikViewport *vvp, struct LatLon maxmin[2] )
2084 {
2085   /* First set the center [in case previously viewing from elsewhere] */
2086   /* Then loop through zoom levels until provided positions are in view */
2087   /* This method is not particularly fast - but should work well enough */
2088   struct LatLon average = { (maxmin[0].lat+maxmin[1].lat)/2, (maxmin[0].lon+maxmin[1].lon)/2 };
2089   VikCoord coord;
2090   vik_coord_load_from_latlon ( &coord, vtl->coord_mode, &average );
2091   vik_viewport_set_center_coord ( vvp, &coord );
2092
2093   /* Convert into definite 'smallest' and 'largest' positions */
2094   struct LatLon minmin;
2095   if ( maxmin[0].lat < maxmin[1].lat )
2096     minmin.lat = maxmin[0].lat;
2097   else
2098     minmin.lat = maxmin[1].lat;
2099
2100   struct LatLon maxmax;
2101   if ( maxmin[0].lon > maxmin[1].lon )
2102     maxmax.lon = maxmin[0].lon;
2103   else
2104     maxmax.lon = maxmin[1].lon;
2105
2106   /* Never zoom in too far - generally not that useful, as too close ! */
2107   /* Always recalculate the 'best' zoom level */
2108   gdouble zoom = 1.0;
2109   vik_viewport_set_zoom ( vvp, zoom );
2110
2111   gdouble min_lat, max_lat, min_lon, max_lon;
2112   /* Should only be a maximum of about 18 iterations from min to max zoom levels */
2113   while ( zoom <= VIK_VIEWPORT_MAX_ZOOM ) {
2114     vik_viewport_get_min_max_lat_lon ( vvp, &min_lat, &max_lat, &min_lon, &max_lon );
2115     /* NB I think the logic used in this test to determine if the bounds is within view
2116        fails if track goes across 180 degrees longitude.
2117        Hopefully that situation is not too common...
2118        Mind you viking doesn't really do edge locations to well anyway */
2119     if ( min_lat < minmin.lat &&
2120          max_lat > minmin.lat &&
2121          min_lon < maxmax.lon &&
2122          max_lon > maxmax.lon )
2123       /* Found within zoom level */
2124       break;
2125
2126     /* Try next */
2127     zoom = zoom * 2;
2128     vik_viewport_set_zoom ( vvp, zoom );
2129   }
2130 }
2131
2132 gboolean vik_trw_layer_auto_set_view ( VikTrwLayer *vtl, VikViewport *vvp )
2133 {
2134   /* TODO: what if there's only one waypoint @ 0,0, it will think nothing found. */
2135   struct LatLon maxmin[2] = { {0.0,0.0}, {0.0,0.0} };
2136   trw_layer_find_maxmin (vtl, maxmin);
2137   if (maxmin[0].lat == 0.0 && maxmin[0].lon == 0.0 && maxmin[1].lat == 0.0 && maxmin[1].lon == 0.0)
2138     return FALSE;
2139   else {
2140     trw_layer_zoom_to_show_latlons ( vtl, vvp, maxmin );
2141     return TRUE;
2142   }
2143 }
2144
2145 static void trw_layer_auto_view ( gpointer layer_and_vlp[2] )
2146 {
2147   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])) ) ) {
2148     vik_layers_panel_emit_update ( VIK_LAYERS_PANEL(layer_and_vlp[1]) );
2149   }
2150   else
2151     a_dialog_info_msg ( VIK_GTK_WINDOW_FROM_LAYER(layer_and_vlp[0]), _("This layer has no waypoints or trackpoints.") );
2152 }
2153
2154 static void trw_layer_export ( gpointer layer_and_vlp[2], const gchar *title, const gchar* default_name, const gchar* trackname, guint file_type )
2155 {
2156   GtkWidget *file_selector;
2157   const gchar *fn;
2158   gboolean failed = FALSE;
2159   file_selector = gtk_file_chooser_dialog_new (title,
2160                                                NULL,
2161                                                GTK_FILE_CHOOSER_ACTION_SAVE,
2162                                                GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
2163                                                GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT,
2164                                                NULL);
2165   gtk_file_chooser_set_current_name (GTK_FILE_CHOOSER(file_selector), default_name);
2166
2167   while ( gtk_dialog_run ( GTK_DIALOG(file_selector) ) == GTK_RESPONSE_ACCEPT )
2168   {
2169     fn = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER(file_selector) );
2170     if ( g_file_test ( fn, G_FILE_TEST_EXISTS ) == FALSE )
2171     {
2172       gtk_widget_hide ( file_selector );
2173       failed = ! a_file_export ( VIK_TRW_LAYER(layer_and_vlp[0]), fn, file_type, trackname);
2174       break;
2175     }
2176     else
2177     {
2178       if ( a_dialog_yes_or_no ( GTK_WINDOW(file_selector), _("The file \"%s\" exists, do you wish to overwrite it?"), a_file_basename ( fn ) ) )
2179       {
2180         gtk_widget_hide ( file_selector );
2181         failed = ! a_file_export ( VIK_TRW_LAYER(layer_and_vlp[0]), fn, file_type, trackname);
2182         break;
2183       }
2184     }
2185   }
2186   gtk_widget_destroy ( file_selector );
2187   if ( failed )
2188     a_dialog_error_msg ( VIK_GTK_WINDOW_FROM_LAYER(layer_and_vlp[0]), _("The filename you requested could not be opened for writing.") );
2189 }
2190
2191 static void trw_layer_export_gpspoint ( gpointer layer_and_vlp[2] )
2192 {
2193   trw_layer_export ( layer_and_vlp, _("Export Layer"), vik_layer_get_name(VIK_LAYER(layer_and_vlp[0])), NULL, FILE_TYPE_GPSPOINT );
2194 }
2195
2196 static void trw_layer_export_gpsmapper ( gpointer layer_and_vlp[2] )
2197 {
2198   trw_layer_export ( layer_and_vlp, _("Export Layer"), vik_layer_get_name(VIK_LAYER(layer_and_vlp[0])), NULL, FILE_TYPE_GPSMAPPER );
2199 }
2200
2201 static void trw_layer_export_gpx ( gpointer layer_and_vlp[2] )
2202 {
2203   /* Auto append '.gpx' to track name (providing it's not already there) for the default filename */
2204   gchar *auto_save_name = g_strdup ( vik_layer_get_name(VIK_LAYER(layer_and_vlp[0])) );
2205   if ( ! check_file_ext ( auto_save_name, ".gpx" ) )
2206     auto_save_name = g_strconcat ( auto_save_name, ".gpx", NULL );
2207
2208   trw_layer_export ( layer_and_vlp, _("Export Layer"), auto_save_name, NULL, FILE_TYPE_GPX );
2209
2210   g_free ( auto_save_name );
2211 }
2212
2213 static void trw_layer_export_kml ( gpointer layer_and_vlp[2] )
2214 {
2215   /* Auto append '.kml' to the name (providing it's not already there) for the default filename */
2216   gchar *auto_save_name = g_strdup ( vik_layer_get_name(VIK_LAYER(layer_and_vlp[0])) );
2217   if ( ! check_file_ext ( auto_save_name, ".kml" ) )
2218     auto_save_name = g_strconcat ( auto_save_name, ".kml", NULL );
2219
2220   trw_layer_export ( layer_and_vlp, _("Export Layer"), auto_save_name, NULL, FILE_TYPE_KML );
2221
2222   g_free ( auto_save_name );
2223 }
2224
2225 static void trw_layer_export_gpx_track ( gpointer pass_along[6] )
2226 {
2227   gpointer layer_and_vlp[2];
2228   layer_and_vlp[0] = pass_along[0];
2229   layer_and_vlp[1] = pass_along[1];
2230
2231   /* Auto append '.gpx' to track name (providing it's not already there) for the default filename */
2232   gchar *auto_save_name = g_strdup ( pass_along[3] );
2233   if ( ! check_file_ext ( auto_save_name, ".gpx" ) )
2234     auto_save_name = g_strconcat ( auto_save_name, ".gpx", NULL );
2235
2236   trw_layer_export ( layer_and_vlp, _("Export Track as GPX"), auto_save_name, pass_along[3], FILE_TYPE_GPX );
2237
2238   g_free ( auto_save_name );
2239 }
2240
2241 static void trw_layer_goto_wp ( gpointer layer_and_vlp[2] )
2242 {
2243   GHashTable *wps = vik_trw_layer_get_waypoints ( VIK_TRW_LAYER(layer_and_vlp[0]) );
2244   GtkWidget *dia = gtk_dialog_new_with_buttons (_("Find"),
2245                                                  VIK_GTK_WINDOW_FROM_LAYER(layer_and_vlp[0]),
2246                                                  GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
2247                                                  GTK_STOCK_CANCEL,
2248                                                  GTK_RESPONSE_REJECT,
2249                                                  GTK_STOCK_OK,
2250                                                  GTK_RESPONSE_ACCEPT,
2251                                                  NULL);
2252
2253   GtkWidget *label, *entry;
2254   label = gtk_label_new(_("Waypoint Name:"));
2255   entry = gtk_entry_new();
2256
2257   gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dia)->vbox), label, FALSE, FALSE, 0);
2258   gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dia)->vbox), entry, FALSE, FALSE, 0);
2259   gtk_widget_show_all ( label );
2260   gtk_widget_show_all ( entry );
2261
2262   gtk_dialog_set_default_response ( GTK_DIALOG(dia), GTK_RESPONSE_ACCEPT );
2263
2264   while ( gtk_dialog_run ( GTK_DIALOG(dia) ) == GTK_RESPONSE_ACCEPT )
2265   {
2266     VikWaypoint *wp;
2267     gchar *upname = g_strdup(gtk_entry_get_text(GTK_ENTRY(entry)));
2268     int i;
2269
2270     for ( i = strlen(upname)-1; i >= 0; i-- )
2271       upname[i] = toupper(upname[i]);
2272
2273     wp = g_hash_table_lookup ( wps, upname );
2274
2275     if (!wp)
2276       a_dialog_error_msg ( VIK_GTK_WINDOW_FROM_LAYER(layer_and_vlp[0]), _("Waypoint not found in this layer.") );
2277     else
2278     {
2279       vik_viewport_set_center_coord ( vik_layers_panel_get_viewport(VIK_LAYERS_PANEL(layer_and_vlp[1])), &(wp->coord) );
2280       vik_layers_panel_emit_update ( VIK_LAYERS_PANEL(layer_and_vlp[1]) );
2281       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 );
2282       break;
2283     }
2284
2285     g_free ( upname );
2286
2287   }
2288   gtk_widget_destroy ( dia );
2289 }
2290
2291 gboolean vik_trw_layer_new_waypoint ( VikTrwLayer *vtl, GtkWindow *w, const VikCoord *def_coord )
2292 {
2293   gchar *default_name = highest_wp_number_get(vtl);
2294   VikWaypoint *wp = vik_waypoint_new();
2295   gchar *returned_name;
2296   gboolean updated;
2297   wp->coord = *def_coord;
2298   
2299   // Attempt to auto set height if DEM data is available
2300   gint16 elev = a_dems_get_elev_by_coord ( &(wp->coord), VIK_DEM_INTERPOL_BEST );
2301   if ( elev != VIK_DEM_INVALID_ELEVATION )
2302     wp->altitude = (gdouble)elev;
2303
2304   if ( ( returned_name = a_dialog_waypoint ( w, default_name, wp, vik_trw_layer_get_waypoints ( vtl ), vtl->coord_mode, TRUE, &updated ) ) )
2305   {
2306     wp->visible = TRUE;
2307     vik_trw_layer_add_waypoint ( vtl, returned_name, wp );
2308     g_free (default_name);
2309     return TRUE;
2310   }
2311   g_free (default_name);
2312   vik_waypoint_free(wp);
2313   return FALSE;
2314 }
2315
2316 static void trw_layer_new_wikipedia_wp_viewport ( gpointer lav[2] )
2317 {
2318   VikCoord one, two;
2319   struct LatLon one_ll, two_ll;
2320   struct LatLon maxmin[2] = { {0.0,0.0}, {0.0,0.0} };
2321
2322   VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
2323   VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
2324   VikWindow *vw = (VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl));
2325   VikViewport *vvp =  vik_window_viewport(vw);
2326   vik_viewport_screen_to_coord ( vvp, 0, 0, &one);
2327   vik_viewport_screen_to_coord ( vvp, vik_viewport_get_width(vvp), vik_viewport_get_height(vvp), &two);
2328   vik_coord_to_latlon(&one, &one_ll);
2329   vik_coord_to_latlon(&two, &two_ll);
2330   if (one_ll.lat > two_ll.lat) {
2331     maxmin[0].lat = one_ll.lat;
2332     maxmin[1].lat = two_ll.lat;
2333   }
2334   else {
2335     maxmin[0].lat = two_ll.lat;
2336     maxmin[1].lat = one_ll.lat;
2337   }
2338   if (one_ll.lon > two_ll.lon) {
2339     maxmin[0].lon = one_ll.lon;
2340     maxmin[1].lon = two_ll.lon;
2341   }
2342   else {
2343     maxmin[0].lon = two_ll.lon;
2344     maxmin[1].lon = one_ll.lon;
2345   }
2346   a_geonames_wikipedia_box((VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl)), vtl, vlp, maxmin);
2347 }
2348
2349 static void trw_layer_new_wikipedia_wp_layer ( gpointer lav[2] )
2350 {
2351   VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
2352   VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
2353   struct LatLon maxmin[2] = { {0.0,0.0}, {0.0,0.0} };
2354   
2355   trw_layer_find_maxmin (vtl, maxmin);
2356   a_geonames_wikipedia_box((VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl)), vtl, vlp, maxmin);
2357 }
2358
2359 // 'Acquires' - Same as in File Menu -> Acquire - applies into the selected TRW Layer //
2360
2361 /*
2362  * Acquire into this TRW Layer straight from GPS Device
2363  */
2364 static void trw_layer_acquire_gps_cb ( gpointer lav[2] )
2365 {
2366   VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
2367   VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
2368   VikWindow *vw = (VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl));
2369   VikViewport *vvp =  vik_window_viewport(vw);
2370
2371   vik_datasource_gps_interface.mode = VIK_DATASOURCE_ADDTOLAYER;
2372   a_acquire ( vw, vlp, vvp, &vik_datasource_gps_interface );
2373 }
2374
2375 /*
2376  * Acquire into this TRW Layer from Google Directions
2377  */
2378 static void trw_layer_acquire_google_cb ( gpointer lav[2] )
2379 {
2380   VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
2381   VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
2382   VikWindow *vw = (VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl));
2383   VikViewport *vvp =  vik_window_viewport(vw);
2384
2385   a_acquire ( vw, vlp, vvp, &vik_datasource_google_interface );
2386 }
2387
2388 #ifdef VIK_CONFIG_OPENSTREETMAP
2389 /*
2390  * Acquire into this TRW Layer from OSM
2391  */
2392 static void trw_layer_acquire_osm_cb ( gpointer lav[2] )
2393 {
2394   VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
2395   VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
2396   VikWindow *vw = (VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl));
2397   VikViewport *vvp =  vik_window_viewport(vw);
2398
2399   a_acquire ( vw, vlp, vvp, &vik_datasource_osm_interface );
2400 }
2401 #endif
2402
2403 #ifdef VIK_CONFIG_GEOCACHES
2404 /*
2405  * Acquire into this TRW Layer from Geocaching.com
2406  */
2407 static void trw_layer_acquire_geocache_cb ( gpointer lav[2] )
2408 {
2409   VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
2410   VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
2411   VikWindow *vw = (VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl));
2412   VikViewport *vvp =  vik_window_viewport(vw);
2413
2414   a_acquire ( vw, vlp, vvp, &vik_datasource_gc_interface );
2415 }
2416 #endif
2417
2418 static void trw_layer_new_wp ( gpointer lav[2] )
2419 {
2420   VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
2421   VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
2422   /* TODO longone: okay, if layer above (aggregate) is invisible but vtl->visible is true, this redraws for no reason.
2423      instead return true if you want to update. */
2424   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 )
2425     vik_layers_panel_emit_update ( vlp );
2426 }
2427
2428 static void trw_layer_auto_tracks_view ( gpointer lav[2] )
2429 {
2430   VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
2431   VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
2432
2433   if ( g_hash_table_size (vtl->tracks) > 0 ) {
2434     struct LatLon maxmin[2] = { {0,0}, {0,0} };
2435     g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_find_maxmin_tracks, maxmin );
2436     trw_layer_zoom_to_show_latlons ( vtl, vik_layers_panel_get_viewport (vlp), maxmin );
2437     vik_layers_panel_emit_update ( vlp );
2438   }
2439 }
2440
2441 static void trw_layer_single_waypoint_jump ( const gchar *name, const VikWaypoint *wp, gpointer vvp )
2442 {
2443   /* NB do not care if wp is visible or not */
2444   vik_viewport_set_center_coord ( VIK_VIEWPORT(vvp), &(wp->coord) );
2445 }
2446
2447 static void trw_layer_auto_waypoints_view ( gpointer lav[2] )
2448 {
2449   VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
2450   VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
2451
2452   /* Only 1 waypoint - jump straight to it */
2453   if ( g_hash_table_size (vtl->waypoints) == 1 ) {
2454     VikViewport *vvp = vik_layers_panel_get_viewport (vlp);
2455     g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_single_waypoint_jump, (gpointer) vvp );
2456   }
2457   /* If at least 2 waypoints - find center and then zoom to fit */
2458   else if ( g_hash_table_size (vtl->waypoints) > 1 )
2459   {
2460     struct LatLon maxmin[2] = { {0,0}, {0,0} };
2461     g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_find_maxmin_waypoints, maxmin );
2462     trw_layer_zoom_to_show_latlons ( vtl, vik_layers_panel_get_viewport (vlp), maxmin );
2463   }
2464
2465   vik_layers_panel_emit_update ( vlp );
2466 }
2467
2468 static void trw_layer_add_menu_items ( VikTrwLayer *vtl, GtkMenu *menu, gpointer vlp )
2469 {
2470   static gpointer pass_along[2];
2471   GtkWidget *item;
2472   GtkWidget *export_submenu;
2473   GtkWidget *wikipedia_submenu;
2474   pass_along[0] = vtl;
2475   pass_along[1] = vlp;
2476
2477   item = gtk_menu_item_new();
2478   gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
2479   gtk_widget_show ( item );
2480
2481   /* Now with icons */
2482   item = gtk_image_menu_item_new_with_mnemonic ( _("_View Layer") );
2483   gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ZOOM_FIT, GTK_ICON_SIZE_MENU) );
2484   g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_view), pass_along );
2485   gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
2486   gtk_widget_show ( item );
2487
2488   item = gtk_menu_item_new_with_mnemonic ( _("View All Trac_ks") );
2489   g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_tracks_view), pass_along );
2490   gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
2491   gtk_widget_show ( item );
2492
2493   item = gtk_menu_item_new_with_mnemonic ( _("V_iew All Waypoints") );
2494   g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_waypoints_view), pass_along );
2495   gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
2496   gtk_widget_show ( item );
2497
2498   item = gtk_image_menu_item_new_with_mnemonic ( _("_Goto Center of Layer") );
2499   gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_JUMP_TO, GTK_ICON_SIZE_MENU) );
2500   g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_centerize), pass_along );
2501   gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
2502   gtk_widget_show ( item );
2503
2504   item = gtk_menu_item_new_with_mnemonic ( _("Goto _Waypoint...") );
2505   g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_wp), pass_along );
2506   gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
2507   gtk_widget_show ( item );
2508
2509   export_submenu = gtk_menu_new ();
2510   item = gtk_image_menu_item_new_with_mnemonic ( _("_Export Layer") );
2511   gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_HARDDISK, GTK_ICON_SIZE_MENU) );
2512   gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
2513   gtk_widget_show ( item );
2514   gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), export_submenu );
2515   
2516   item = gtk_menu_item_new_with_mnemonic ( _("Export as GPS_Point...") );
2517   g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_gpspoint), pass_along );
2518   gtk_menu_shell_append (GTK_MENU_SHELL (export_submenu), item);
2519   gtk_widget_show ( item );
2520
2521   item = gtk_menu_item_new_with_mnemonic ( _("Export as GPS_Mapper...") );
2522   g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_gpsmapper), pass_along );
2523   gtk_menu_shell_append (GTK_MENU_SHELL (export_submenu), item);
2524   gtk_widget_show ( item );
2525
2526   item = gtk_menu_item_new_with_mnemonic ( _("Export as _GPX...") );
2527   g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_gpx), pass_along );
2528   gtk_menu_shell_append (GTK_MENU_SHELL (export_submenu), item);
2529   gtk_widget_show ( item );
2530
2531   item = gtk_menu_item_new_with_mnemonic ( _("Export as _KML...") );
2532   g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_kml), pass_along );
2533   gtk_menu_shell_append (GTK_MENU_SHELL (export_submenu), item);
2534   gtk_widget_show ( item );
2535
2536   item = gtk_image_menu_item_new_with_mnemonic ( _("_New Waypoint...") );
2537   gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NEW, GTK_ICON_SIZE_MENU) );
2538   g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_wp), pass_along );
2539   gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
2540   gtk_widget_show ( item );
2541
2542 #ifdef VIK_CONFIG_GEONAMES
2543   wikipedia_submenu = gtk_menu_new();
2544   item = gtk_image_menu_item_new_with_mnemonic ( _("_Add Wikipedia Waypoints") );
2545   gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ADD, GTK_ICON_SIZE_MENU) );
2546   gtk_menu_shell_append(GTK_MENU_SHELL (menu), item);
2547   gtk_widget_show(item);
2548   gtk_menu_item_set_submenu(GTK_MENU_ITEM(item), wikipedia_submenu);
2549
2550   item = gtk_image_menu_item_new_with_mnemonic ( _("Within _Layer Bounds") );
2551   gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ZOOM_FIT, GTK_ICON_SIZE_MENU) );
2552   g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_wikipedia_wp_layer), pass_along );
2553   gtk_menu_shell_append (GTK_MENU_SHELL (wikipedia_submenu), item);
2554   gtk_widget_show ( item );
2555
2556   item = gtk_image_menu_item_new_with_mnemonic ( _("Within _Current View") );
2557   gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ZOOM_100, GTK_ICON_SIZE_MENU) );
2558   g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_wikipedia_wp_viewport), pass_along );
2559   gtk_menu_shell_append (GTK_MENU_SHELL (wikipedia_submenu), item);
2560   gtk_widget_show ( item );
2561 #endif
2562
2563   GtkWidget *acquire_submenu = gtk_menu_new ();
2564   item = gtk_image_menu_item_new_with_mnemonic ( _("Ac_quire") );
2565   gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_DOWN, GTK_ICON_SIZE_MENU) );
2566   gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
2567   gtk_widget_show ( item );
2568   gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), acquire_submenu );
2569   
2570   item = gtk_menu_item_new_with_mnemonic ( _("From _GPS...") );
2571   g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_acquire_gps_cb), pass_along );
2572   gtk_menu_shell_append (GTK_MENU_SHELL (acquire_submenu), item);
2573   gtk_widget_show ( item );
2574
2575   item = gtk_menu_item_new_with_mnemonic ( _("From G_oogle Directions...") );
2576   g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_acquire_google_cb), pass_along );
2577   gtk_menu_shell_append (GTK_MENU_SHELL (acquire_submenu), item);
2578   gtk_widget_show ( item );
2579
2580 #ifdef VIK_CONFIG_OPENSTREETMAP
2581   item = gtk_menu_item_new_with_mnemonic ( _("From _OSM Traces...") );
2582   g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_acquire_osm_cb), pass_along );
2583   gtk_menu_shell_append (GTK_MENU_SHELL (acquire_submenu), item);
2584   gtk_widget_show ( item );
2585 #endif
2586
2587 #ifdef VIK_CONFIG_GEOCACHES
2588   item = gtk_menu_item_new_with_mnemonic ( _("From Geo_caching...") );
2589   g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_acquire_geocache_cb), pass_along );
2590   gtk_menu_shell_append (GTK_MENU_SHELL (acquire_submenu), item);
2591   gtk_widget_show ( item );
2592 #endif
2593
2594 #ifdef VIK_CONFIG_OPENSTREETMAP 
2595   item = gtk_image_menu_item_new_with_mnemonic ( _("Upload to _OSM...") );
2596   gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_UP, GTK_ICON_SIZE_MENU) );
2597   g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(osm_traces_upload_cb), pass_along );
2598   gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
2599   gtk_widget_show ( item );
2600 #endif
2601
2602   GtkWidget *delete_submenu = gtk_menu_new ();
2603   item = gtk_image_menu_item_new_with_mnemonic ( _("De_lete") );
2604   gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU) );
2605   gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
2606   gtk_widget_show ( item );
2607   gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), delete_submenu );
2608   
2609   item = gtk_image_menu_item_new_with_mnemonic ( _("Delete All _Tracks") );
2610   gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU) );
2611   g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_all_tracks), pass_along );
2612   gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
2613   gtk_widget_show ( item );
2614   
2615   item = gtk_image_menu_item_new_with_mnemonic ( _("Delete Tracks _From Selection...") );
2616   gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
2617   g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_tracks_from_selection), pass_along );
2618   gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
2619   gtk_widget_show ( item );
2620   
2621   item = gtk_image_menu_item_new_with_mnemonic ( _("Delete All _Waypoints") );
2622   gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU) );
2623   g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_all_waypoints), pass_along );
2624   gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
2625   gtk_widget_show ( item );
2626   
2627   item = gtk_image_menu_item_new_with_mnemonic ( _("Delete Waypoints From _Selection...") );
2628   gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
2629   g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_waypoints_from_selection), pass_along );
2630   gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
2631   gtk_widget_show ( item );
2632   
2633   item = a_acquire_trwlayer_menu ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)), vlp,
2634                                    vik_layers_panel_get_viewport(VIK_LAYERS_PANEL(vlp)), vtl );
2635   if ( item ) {
2636     gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
2637     gtk_widget_show ( item );
2638   }  
2639
2640   item = a_acquire_trwlayer_track_menu ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)), vlp,
2641                                          vik_layers_panel_get_viewport(VIK_LAYERS_PANEL(vlp)), vtl );
2642   if ( item ) {
2643     gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
2644     gtk_widget_show ( item );
2645   }  
2646 }
2647
2648 void vik_trw_layer_add_waypoint ( VikTrwLayer *vtl, gchar *name, VikWaypoint *wp )
2649 {
2650   if ( VIK_LAYER(vtl)->realized )
2651   {
2652     VikWaypoint *oldwp = VIK_WAYPOINT ( g_hash_table_lookup ( vtl->waypoints, name ) );
2653     if ( oldwp )
2654       wp->visible = oldwp->visible; /* same visibility so we don't have to update viktreeview */
2655     else
2656     {
2657       GtkTreeIter *iter = g_malloc(sizeof(GtkTreeIter));
2658       // Visibility column always needed for waypoints
2659 #ifdef VIK_CONFIG_ALPHABETIZED_TRW
2660       vik_treeview_add_sublayer_alphabetized ( VIK_LAYER(vtl)->vt, &(vtl->waypoints_iter), iter, name, vtl, name, VIK_TRW_LAYER_SUBLAYER_WAYPOINT, NULL, TRUE, TRUE );
2661 #else
2662       vik_treeview_add_sublayer ( VIK_LAYER(vtl)->vt, &(vtl->waypoints_iter), iter, name, vtl, name, VIK_TRW_LAYER_SUBLAYER_WAYPOINT, NULL, TRUE, TRUE );
2663 #endif
2664       // Actual setting of visibility dependent on the waypoint
2665       vik_treeview_item_set_visible ( VIK_LAYER(vtl)->vt, iter, wp->visible );
2666       g_hash_table_insert ( vtl->waypoints_iters, name, iter );
2667     }
2668   }
2669
2670   highest_wp_number_add_wp(vtl, name);
2671   g_hash_table_insert ( vtl->waypoints, name, wp );
2672  
2673 }
2674
2675 void vik_trw_layer_add_track ( VikTrwLayer *vtl, gchar *name, VikTrack *t )
2676 {
2677   if ( VIK_LAYER(vtl)->realized )
2678   {
2679     VikTrack *oldt = VIK_TRACK ( g_hash_table_lookup ( vtl->tracks, name ) );
2680     if ( oldt )
2681       t->visible = oldt->visible; /* same visibility so we don't have to update viktreeview */
2682     else
2683     {
2684       GtkTreeIter *iter = g_malloc(sizeof(GtkTreeIter));
2685       // Visibility column always needed for tracks
2686 #ifdef VIK_CONFIG_ALPHABETIZED_TRW
2687       vik_treeview_add_sublayer_alphabetized ( VIK_LAYER(vtl)->vt, &(vtl->tracks_iter), iter, name, vtl, name, VIK_TRW_LAYER_SUBLAYER_TRACK, NULL, TRUE, TRUE );
2688 #else
2689       vik_treeview_add_sublayer ( VIK_LAYER(vtl)->vt, &(vtl->tracks_iter), iter, name, vtl, name, VIK_TRW_LAYER_SUBLAYER_TRACK, NULL, TRUE, TRUE );
2690 #endif
2691       // Actual setting of visibility dependent on the track
2692       vik_treeview_item_set_visible ( VIK_LAYER(vtl)->vt, iter, t->visible );
2693       g_hash_table_insert ( vtl->tracks_iters, name, iter );
2694     }
2695   }
2696
2697   g_hash_table_insert ( vtl->tracks, name, t );
2698  
2699 }
2700
2701 /* to be called whenever a track has been deleted or may have been changed. */
2702 void trw_layer_cancel_tps_of_track ( VikTrwLayer *vtl, const gchar *trk_name )
2703 {
2704   if (vtl->current_tp_track_name && g_strcasecmp(trk_name, vtl->current_tp_track_name) == 0)
2705     trw_layer_cancel_current_tp ( vtl, FALSE );
2706   else if (vtl->last_tp_track_name && g_strcasecmp(trk_name, vtl->last_tp_track_name) == 0)
2707     trw_layer_cancel_last_tp ( vtl );
2708 }
2709         
2710 static gchar *get_new_unique_sublayer_name (VikTrwLayer *vtl, gint sublayer_type, const gchar *name)
2711 {
2712  gint i = 2;
2713  gchar *newname = g_strdup(name);
2714  while ((sublayer_type == VIK_TRW_LAYER_SUBLAYER_TRACK) ?
2715          (void *)vik_trw_layer_get_track(vtl, newname) : (void *)vik_trw_layer_get_waypoint(vtl, newname)) {
2716     gchar *new_newname = g_strdup_printf("%s#%d", name, i);
2717     g_free(newname);
2718     newname = new_newname;
2719     i++;
2720   }
2721   return newname;
2722 }
2723
2724 void vik_trw_layer_filein_add_waypoint ( VikTrwLayer *vtl, gchar *name, VikWaypoint *wp )
2725 {
2726   vik_trw_layer_add_waypoint ( vtl,
2727                         get_new_unique_sublayer_name(vtl, VIK_TRW_LAYER_SUBLAYER_WAYPOINT, name),
2728                         wp );
2729 }
2730 void vik_trw_layer_filein_add_track ( VikTrwLayer *vtl, gchar *name, VikTrack *tr )
2731 {
2732   if ( vtl->route_finder_append && vtl->route_finder_current_track ) {
2733     vik_track_remove_dup_points ( tr ); /* make "double point" track work to undo */
2734     vik_track_steal_and_append_trackpoints ( vtl->route_finder_current_track, tr );
2735     vik_track_free ( tr );
2736     vtl->route_finder_append = FALSE; /* this means we have added it */
2737   } else {
2738     gchar *new_name = get_new_unique_sublayer_name(vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, name);
2739     vik_trw_layer_add_track ( vtl, new_name, tr );
2740
2741     if ( vtl->route_finder_check_added_track ) {
2742       vik_track_remove_dup_points ( tr ); /* make "double point" track work to undo */
2743       if ( vtl->route_finder_added_track_name ) /* for google routes */
2744         g_free ( vtl->route_finder_added_track_name );
2745       vtl->route_finder_added_track_name = g_strdup(new_name);
2746     }
2747   }
2748 }
2749
2750 static void trw_layer_enum_item ( const gchar *name, GList **tr, GList **l )
2751 {
2752   *l = g_list_append(*l, (gpointer)name);
2753 }
2754
2755 static void trw_layer_move_item ( VikTrwLayer *vtl_src, VikTrwLayer *vtl_dest, gchar *name, gint type )
2756 {
2757   gchar *newname = get_new_unique_sublayer_name(vtl_dest, type, name);
2758   if (type == VIK_TRW_LAYER_SUBLAYER_TRACK) {
2759     VikTrack *t;
2760     t = vik_track_copy(vik_trw_layer_get_track(vtl_src, name));
2761     vik_trw_layer_delete_track(vtl_src, name);
2762     vik_trw_layer_add_track(vtl_dest, newname, t);
2763   }
2764   if (type==VIK_TRW_LAYER_SUBLAYER_WAYPOINT) {
2765     VikWaypoint *w;
2766     w = vik_waypoint_copy(vik_trw_layer_get_waypoint(vtl_src, name));
2767     vik_trw_layer_delete_waypoint(vtl_src, name);
2768     vik_trw_layer_add_waypoint(vtl_dest, newname, w);
2769   }
2770 }
2771
2772 static void trw_layer_drag_drop_request ( VikTrwLayer *vtl_src, VikTrwLayer *vtl_dest, GtkTreeIter *src_item_iter, GtkTreePath *dest_path )
2773 {
2774   VikTreeview *vt = VIK_LAYER(vtl_src)->vt;
2775   gint type = vik_treeview_item_get_data(vt, src_item_iter);
2776
2777   if (!vik_treeview_item_get_pointer(vt, src_item_iter)) {
2778     GList *items = NULL;
2779     GList *iter;
2780
2781     if (type==VIK_TRW_LAYER_SUBLAYER_TRACKS) {
2782       g_hash_table_foreach ( vtl_src->tracks, (GHFunc)trw_layer_enum_item, &items);
2783     } 
2784     if (type==VIK_TRW_LAYER_SUBLAYER_WAYPOINTS) {
2785       g_hash_table_foreach ( vtl_src->waypoints, (GHFunc)trw_layer_enum_item, &items);
2786     }    
2787       
2788     iter = items;
2789     while (iter) {
2790       if (type==VIK_TRW_LAYER_SUBLAYER_TRACKS) {
2791         trw_layer_move_item ( vtl_src, vtl_dest, iter->data, VIK_TRW_LAYER_SUBLAYER_TRACK);
2792       } else {
2793         trw_layer_move_item ( vtl_src, vtl_dest, iter->data, VIK_TRW_LAYER_SUBLAYER_WAYPOINT);
2794       }
2795       iter = iter->next;
2796     }
2797     if (items) 
2798       g_list_free(items);
2799   } else {
2800     gchar *name = vik_treeview_item_get_pointer(vt, src_item_iter);
2801     trw_layer_move_item(vtl_src, vtl_dest, name, type);
2802   }
2803 }
2804
2805 gboolean vik_trw_layer_delete_track ( VikTrwLayer *vtl, const gchar *trk_name )
2806 {
2807   VikTrack *t = g_hash_table_lookup ( vtl->tracks, trk_name );
2808   gboolean was_visible = FALSE;
2809   if ( t )
2810   {
2811     GtkTreeIter *it;
2812     was_visible = t->visible;
2813     if ( t == vtl->current_track ) {
2814       vtl->current_track = NULL;
2815     }
2816     if ( t == vtl->route_finder_current_track )
2817       vtl->route_finder_current_track = NULL;
2818
2819     /* could be current_tp, so we have to check */
2820     trw_layer_cancel_tps_of_track ( vtl, trk_name );
2821
2822     g_assert ( ( it = g_hash_table_lookup ( vtl->tracks_iters, trk_name ) ) );
2823     vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, it );
2824     g_hash_table_remove ( vtl->tracks_iters, trk_name );
2825
2826     /* do this last because trk_name may be pointing to actual orig key */
2827     g_hash_table_remove ( vtl->tracks, trk_name );
2828   }
2829   return was_visible;
2830 }
2831
2832 gboolean vik_trw_layer_delete_waypoint ( VikTrwLayer *vtl, const gchar *wp_name )
2833 {
2834   gboolean was_visible = FALSE;
2835   VikWaypoint *wp;
2836
2837   wp = g_hash_table_lookup ( vtl->waypoints, wp_name );
2838   if ( wp ) {
2839     GtkTreeIter *it;
2840
2841     if ( wp == vtl->current_wp ) {
2842       vtl->current_wp = NULL;
2843       vtl->current_wp_name = NULL;
2844       vtl->moving_wp = FALSE;
2845     }
2846
2847     was_visible = wp->visible;
2848     g_assert ( ( it = g_hash_table_lookup ( vtl->waypoints_iters, (gchar *) wp_name ) ) );
2849     vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, it );
2850     g_hash_table_remove ( vtl->waypoints_iters, (gchar *) wp_name );
2851
2852     highest_wp_number_remove_wp(vtl, wp_name);
2853     g_hash_table_remove ( vtl->waypoints, wp_name ); /* last because this frees name */
2854   }
2855
2856   return was_visible;
2857 }
2858
2859 static void remove_item_from_treeview(const gchar *name, GtkTreeIter *it, VikTreeview * vt)
2860 {
2861     vik_treeview_item_delete (vt, it );
2862 }
2863
2864 void vik_trw_layer_delete_all_tracks ( VikTrwLayer *vtl )
2865 {
2866
2867   vtl->current_track = NULL;
2868   vtl->route_finder_current_track = NULL;
2869   if (vtl->current_tp_track_name)
2870     trw_layer_cancel_current_tp(vtl, FALSE);
2871   if (vtl->last_tp_track_name)
2872     trw_layer_cancel_last_tp ( vtl );
2873
2874   g_hash_table_foreach(vtl->tracks_iters, (GHFunc) remove_item_from_treeview, VIK_LAYER(vtl)->vt);
2875   g_hash_table_remove_all(vtl->tracks_iters);
2876   g_hash_table_remove_all(vtl->tracks);
2877
2878   vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
2879 }
2880
2881 void vik_trw_layer_delete_all_waypoints ( VikTrwLayer *vtl )
2882 {
2883   vtl->current_wp = NULL;
2884   vtl->current_wp_name = NULL;
2885   vtl->moving_wp = FALSE;
2886
2887   highest_wp_number_reset(vtl);
2888
2889   g_hash_table_foreach(vtl->waypoints_iters, (GHFunc) remove_item_from_treeview, VIK_LAYER(vtl)->vt);
2890   g_hash_table_remove_all(vtl->waypoints_iters);
2891   g_hash_table_remove_all(vtl->waypoints);
2892
2893   vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
2894 }
2895
2896 static void trw_layer_delete_all_tracks ( gpointer lav[2] )
2897 {
2898   VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
2899   // Get confirmation from the user
2900   if ( a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
2901                             _("Are you sure you want to delete all tracks in %s?"),
2902                             vik_layer_get_name ( VIK_LAYER(vtl) ) ) )
2903     vik_trw_layer_delete_all_tracks (vtl);
2904 }
2905
2906 static void trw_layer_delete_all_waypoints ( gpointer lav[2] )
2907 {
2908   VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
2909   // Get confirmation from the user
2910   if ( a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
2911                             _("Are you sure you want to delete all waypoints in %s?"),
2912                             vik_layer_get_name ( VIK_LAYER(vtl) ) ) )
2913     vik_trw_layer_delete_all_waypoints (vtl);
2914 }
2915
2916 static void trw_layer_delete_item ( gpointer pass_along[6] )
2917 {
2918   VikTrwLayer *vtl = VIK_TRW_LAYER(pass_along[0]);
2919   gboolean was_visible = FALSE;
2920   if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_WAYPOINT )
2921   {
2922     if ( GPOINTER_TO_INT ( pass_along[4]) )
2923       // Get confirmation from the user
2924       // Maybe this Waypoint Delete should be optional as is it could get annoying...
2925       if ( ! a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
2926                                   _("Are you sure you want to delete the waypoint \"%s\""),
2927                                   pass_along[3] ) )
2928         return;
2929     was_visible = vik_trw_layer_delete_waypoint ( vtl, (gchar *) pass_along[3] );
2930   }
2931   else
2932   {
2933     if ( GPOINTER_TO_INT ( pass_along[4]) )
2934       // Get confirmation from the user
2935       if ( ! a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
2936                                   _("Are you sure you want to delete the track \"%s\""),
2937                                   pass_along[3] ) )
2938         return;
2939     was_visible = vik_trw_layer_delete_track ( vtl, (gchar *) pass_along[3] );
2940   }
2941   if ( was_visible )
2942     vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
2943 }
2944
2945
2946 static void trw_layer_properties_item ( gpointer pass_along[6] )
2947 {
2948   VikTrwLayer *vtl = VIK_TRW_LAYER(pass_along[0]);
2949   if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_WAYPOINT )
2950   {
2951     VikWaypoint *wp = g_hash_table_lookup ( vtl->waypoints, pass_along[3] );
2952     if ( wp )
2953     {
2954       gboolean updated = FALSE;
2955       a_dialog_waypoint ( VIK_GTK_WINDOW_FROM_LAYER(vtl), pass_along[3], wp, NULL, vtl->coord_mode, FALSE, &updated );
2956
2957       if ( updated && VIK_LAYER(vtl)->visible )
2958         vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
2959     }
2960   }
2961   else
2962   {
2963     VikTrack *tr = g_hash_table_lookup ( vtl->tracks, pass_along[3] );
2964     if ( tr )
2965     {
2966       vik_trw_layer_propwin_run ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
2967                                   vtl, tr,
2968                                   pass_along[1], /* vlp */
2969                                   pass_along[3],  /* track name */
2970                                   pass_along[5] );  /* vvp */
2971     }
2972   }
2973 }
2974
2975 /*
2976    Parameter 1 -> VikLayersPanel
2977    Parameter 2 -> VikLayer
2978    Parameter 3 -> VikViewport
2979 */
2980 static void goto_coord ( gpointer *vlp, gpointer vl, gpointer vvp, const VikCoord *coord )
2981 {
2982   if ( vlp ) {
2983     vik_viewport_set_center_coord ( vik_layers_panel_get_viewport (VIK_LAYERS_PANEL(vlp)), coord );
2984     vik_layers_panel_emit_update ( VIK_LAYERS_PANEL(vlp) );
2985   }
2986   else {
2987     /* since vlp not set, vl & vvp should be valid instead! */
2988     if ( vl && vvp ) {
2989       vik_viewport_set_center_coord ( VIK_VIEWPORT(vvp), coord );
2990       vik_layer_emit_update ( VIK_LAYER(vl), FALSE );
2991     }
2992   }
2993 }
2994
2995 static void trw_layer_goto_track_startpoint ( gpointer pass_along[6] )
2996 {
2997   GList *trps = ((VikTrack *) g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, pass_along[3] ))->trackpoints;
2998   if ( trps && trps->data )
2999     goto_coord ( pass_along[1], pass_along[0], pass_along[5], &(((VikTrackpoint *) trps->data)->coord));
3000 }
3001
3002 static void trw_layer_goto_track_center ( gpointer pass_along[6] )
3003 {
3004   /* FIXME: get this into viktrack.c, and should be ->trackpoints right? */
3005   GList **trps = g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, pass_along[3] );
3006   if ( trps && *trps )
3007   {
3008     struct LatLon average, maxmin[2] = { {0,0}, {0,0} };
3009     VikCoord coord;
3010     trw_layer_find_maxmin_tracks ( NULL, trps, maxmin );
3011     average.lat = (maxmin[0].lat+maxmin[1].lat)/2;
3012     average.lon = (maxmin[0].lon+maxmin[1].lon)/2;
3013     vik_coord_load_from_latlon ( &coord, VIK_TRW_LAYER(pass_along[0])->coord_mode, &average );
3014     goto_coord ( pass_along[1], pass_along[0], pass_along[5], &coord);
3015   }
3016 }
3017
3018 static void trw_layer_extend_track_end ( gpointer pass_along[6] )
3019 {
3020   VikTrwLayer *vtl = VIK_TRW_LAYER(pass_along[0]);
3021   VikTrack *track = g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, pass_along[3] );
3022
3023   vtl->current_track = track;
3024   vik_window_enable_layer_tool ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)), VIK_LAYER_TRW, TOOL_CREATE_TRACK);
3025
3026   if ( track->trackpoints )
3027     goto_coord ( pass_along[1], pass_along[0], pass_along[5], &(((VikTrackpoint *)g_list_last(track->trackpoints)->data)->coord) );
3028 }
3029
3030 /**
3031  * extend a track using route finder
3032  */
3033 static void trw_layer_extend_track_end_route_finder ( gpointer pass_along[6] )
3034 {
3035   VikTrwLayer *vtl = VIK_TRW_LAYER(pass_along[0]);
3036   VikTrack *track = g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, pass_along[3] );
3037   VikCoord last_coord = (((VikTrackpoint *)g_list_last(track->trackpoints)->data)->coord);
3038
3039   vik_window_enable_layer_tool ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)), VIK_LAYER_TRW, NUM_TOOLS );
3040   vtl->route_finder_coord =  last_coord;
3041   vtl->route_finder_current_track = track;
3042   vtl->route_finder_started = TRUE;
3043
3044   if ( track->trackpoints )
3045     goto_coord ( pass_along[1], pass_along[0], pass_along[5], &last_coord) ;
3046
3047 }
3048
3049 static void trw_layer_apply_dem_data ( gpointer pass_along[6] )
3050 {
3051   /* TODO: check & warn if no DEM data, or no applicable DEM data. */
3052   /* Also warn if overwrite old elevation data */
3053   VikTrack *track = (VikTrack *) g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, pass_along[3] );
3054
3055   vik_track_apply_dem_data ( track );
3056 }
3057
3058 static void trw_layer_goto_track_endpoint ( gpointer pass_along[6] )
3059 {
3060   GList *trps = ((VikTrack *) g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, pass_along[3] ))->trackpoints;
3061   if ( !trps )
3062     return;
3063   trps = g_list_last(trps);
3064   goto_coord ( pass_along[1], pass_along[0], pass_along[5], &(((VikTrackpoint *) trps->data)->coord));
3065 }
3066
3067 static void trw_layer_goto_track_max_speed ( gpointer pass_along[6] )
3068 {
3069   VikTrackpoint* vtp = vik_track_get_tp_by_max_speed ( g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, pass_along[3] ) );
3070   if ( !vtp )
3071     return;
3072   goto_coord ( pass_along[1], pass_along[0], pass_along[5], &(vtp->coord));
3073 }
3074
3075 static void trw_layer_goto_track_max_alt ( gpointer pass_along[6] )
3076 {
3077   VikTrackpoint* vtp = vik_track_get_tp_by_max_alt ( g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, pass_along[3] ) );
3078   if ( !vtp )
3079     return;
3080   goto_coord ( pass_along[1], pass_along[0], pass_along[5], &(vtp->coord));
3081 }
3082
3083 static void trw_layer_goto_track_min_alt ( gpointer pass_along[6] )
3084 {
3085   VikTrackpoint* vtp = vik_track_get_tp_by_min_alt ( g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, pass_along[3] ) );
3086   if ( !vtp )
3087     return;
3088   goto_coord ( pass_along[1], pass_along[0], pass_along[5], &(vtp->coord));
3089 }
3090
3091 /* 
3092  * Automatically change the viewport to center on the track and zoom to see the extent of the track
3093  */ 
3094 static void trw_layer_auto_track_view ( gpointer pass_along[5] )
3095 {
3096   GList **trps = g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, pass_along[3] );
3097   if ( trps && *trps )
3098   {
3099     struct LatLon maxmin[2] = { {0,0}, {0,0} };
3100     trw_layer_find_maxmin_tracks ( NULL, trps, maxmin );
3101     trw_layer_zoom_to_show_latlons ( VIK_TRW_LAYER(pass_along[0]), pass_along[5], maxmin );
3102     if ( pass_along[1] )
3103       vik_layers_panel_emit_update ( VIK_LAYERS_PANEL(pass_along[1]) );
3104     else
3105       vik_layer_emit_update ( VIK_LAYER(pass_along[0]), FALSE );
3106   }
3107 }
3108
3109 static void trw_layer_edit_trackpoint ( gpointer pass_along[6] )
3110 {
3111   VikTrwLayer *vtl = VIK_TRW_LAYER(pass_along[0]);
3112   trw_layer_tpwin_init ( vtl );
3113 }
3114
3115 /*************************************
3116  * merge/split by time routines 
3117  *************************************/
3118
3119 /* called for each key in track hash table.
3120  * If the current track has the same time stamp type, add it to the result,
3121  * except the one pointed by "exclude".
3122  * set exclude to NULL if there is no exclude to check.
3123  * Note that the result is in reverse (for performance reasons).
3124  */
3125 typedef struct {
3126   GList **result;
3127   GList  *exclude;
3128   gboolean with_timestamps;
3129 } twt_udata;
3130 static void find_tracks_with_timestamp_type(gpointer key, gpointer value, gpointer udata)
3131 {
3132   twt_udata *user_data = udata;
3133   VikTrackpoint *p1, *p2;
3134
3135   if (VIK_TRACK(value)->trackpoints == user_data->exclude) {
3136     return;
3137   }
3138
3139   if (VIK_TRACK(value)->trackpoints) {
3140     p1 = VIK_TRACKPOINT(VIK_TRACK(value)->trackpoints->data);
3141     p2 = VIK_TRACKPOINT(g_list_last(VIK_TRACK(value)->trackpoints)->data);
3142
3143     if ( user_data->with_timestamps ) {
3144       if (!p1->has_timestamp || !p2->has_timestamp) {
3145         return;
3146       }
3147     }
3148     else {
3149       // Don't add tracks with timestamps when getting non timestamp tracks
3150       if (p1->has_timestamp || p2->has_timestamp) {
3151         return;
3152       }
3153     }
3154   }
3155
3156   *(user_data->result) = g_list_prepend(*(user_data->result), key);
3157 }
3158
3159 /* called for each key in track hash table. if original track user_data[1] is close enough
3160  * to the passed one, add it to list in user_data[0] 
3161  */
3162 static void find_nearby_track(gpointer key, gpointer value, gpointer user_data)
3163 {
3164   time_t t1, t2;
3165   VikTrackpoint *p1, *p2;
3166
3167   GList **nearby_tracks = ((gpointer *)user_data)[0];
3168   GList *orig_track = ((gpointer *)user_data)[1];
3169   guint thr = GPOINTER_TO_UINT (((gpointer *)user_data)[2]);
3170
3171   /* outline: 
3172    * detect reasons for not merging, and return
3173    * if no reason is found not to merge, then do it.
3174    */
3175
3176   if (VIK_TRACK(value)->trackpoints == orig_track) {
3177     return;
3178   }
3179
3180   t1 = VIK_TRACKPOINT(orig_track->data)->timestamp;
3181   t2 = VIK_TRACKPOINT(g_list_last(orig_track)->data)->timestamp;
3182
3183   if (VIK_TRACK(value)->trackpoints) {
3184     p1 = VIK_TRACKPOINT(VIK_TRACK(value)->trackpoints->data);
3185     p2 = VIK_TRACKPOINT(g_list_last(VIK_TRACK(value)->trackpoints)->data);
3186
3187     if (!p1->has_timestamp || !p2->has_timestamp) {
3188       g_print("no timestamp\n");
3189       return;
3190     }
3191
3192     /*  g_print("Got track named %s, times %d, %d\n", (gchar *)key, p1->timestamp, p2->timestamp); */
3193     if (! (abs(t1 - p2->timestamp) < thr*60 ||
3194         /*  p1 p2      t1 t2 */
3195            abs(p1->timestamp - t2) < thr*60)
3196         /*  t1 t2      p1 p2 */
3197         ) {
3198       return;
3199     }
3200   }
3201
3202   *nearby_tracks = g_list_prepend(*nearby_tracks, key);
3203 }
3204
3205 /* comparison function used to sort tracks; a and b are hash table keys */
3206 /* Not actively used - can be restored if needed
3207 static gint track_compare(gconstpointer a, gconstpointer b, gpointer user_data)
3208 {
3209   GHashTable *tracks = user_data;
3210   time_t t1, t2;
3211
3212   t1 = VIK_TRACKPOINT(VIK_TRACK(g_hash_table_lookup(tracks, a))->trackpoints->data)->timestamp;
3213   t2 = VIK_TRACKPOINT(VIK_TRACK(g_hash_table_lookup(tracks, b))->trackpoints->data)->timestamp;
3214   
3215   if (t1 < t2) return -1;
3216   if (t1 > t2) return 1;
3217   return 0;
3218 }
3219 */
3220
3221 /* comparison function used to sort trackpoints */
3222 static gint trackpoint_compare(gconstpointer a, gconstpointer b)
3223 {
3224   time_t t1 = VIK_TRACKPOINT(a)->timestamp, t2 = VIK_TRACKPOINT(b)->timestamp;
3225   
3226   if (t1 < t2) return -1;
3227   if (t1 > t2) return 1;
3228   return 0;
3229 }
3230
3231 #ifdef VIK_CONFIG_ALPHABETIZED_TRW
3232 /**
3233  * comparison function which can be used to sort tracks or waypoints by name
3234  */
3235 static gint sort_alphabetically (gconstpointer a, gconstpointer b, gpointer user_data)
3236 {
3237   const gchar* namea = (const gchar*) a;
3238   const gchar* nameb = (const gchar*) b;
3239   if ( namea == NULL || nameb == NULL)
3240     return 0;
3241   else
3242     // Same sort method as used in the vik_treeview_*_alphabetize functions
3243     return strcmp ( namea, nameb );
3244 }
3245 #endif
3246
3247 /**
3248  * Attempt to merge selected track with other tracks specified by the user
3249  * Tracks to merge with must be of the same 'type' as the selected track -
3250  *  either all with timestamps, or all without timestamps
3251  */
3252 static void trw_layer_merge_with_other ( gpointer pass_along[6] )
3253 {
3254   VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
3255   gchar *orig_track_name = pass_along[3];
3256   GList *other_tracks = NULL;
3257   VikTrack *track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, orig_track_name );
3258
3259   if ( !track->trackpoints )
3260     return;
3261
3262   twt_udata udata;
3263   udata.result = &other_tracks;
3264   udata.exclude = track->trackpoints;
3265   // Allow merging with 'similar' time type time tracks
3266   // i.e. either those times, or those without
3267   udata.with_timestamps = (VIK_TRACKPOINT(track->trackpoints->data)->has_timestamp);
3268
3269   g_hash_table_foreach(vtl->tracks, find_tracks_with_timestamp_type, (gpointer)&udata);
3270   other_tracks = g_list_reverse(other_tracks);
3271
3272   if ( !other_tracks ) {
3273     if ( udata.with_timestamps )
3274       a_dialog_error_msg(VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Failed. No other tracks with timestamps in this layer found"));
3275     else
3276       a_dialog_error_msg(VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Failed. No other tracks without timestamps in this layer found"));
3277     return;
3278   }
3279
3280 #ifdef VIK_CONFIG_ALPHABETIZED_TRW
3281   // Sort alphabetically for user presentation
3282   other_tracks = g_list_sort_with_data (other_tracks, sort_alphabetically, NULL);
3283 #endif
3284
3285   GList *merge_list = a_dialog_select_from_list(VIK_GTK_WINDOW_FROM_LAYER(vtl),
3286       other_tracks, TRUE,
3287       _("Merge with..."), _("Select track to merge with"));
3288   g_list_free(other_tracks);
3289
3290   if (merge_list)
3291   {
3292     GList *l;
3293     for (l = merge_list; l != NULL; l = g_list_next(l)) {
3294       VikTrack *merge_track = (VikTrack *) g_hash_table_lookup (vtl->tracks, l->data );
3295       if (merge_track) {
3296         track->trackpoints = g_list_concat(track->trackpoints, merge_track->trackpoints);
3297         merge_track->trackpoints = NULL;
3298         vik_trw_layer_delete_track(vtl, l->data);
3299         track->trackpoints = g_list_sort(track->trackpoints, trackpoint_compare);
3300       }
3301     }
3302     /* TODO: free data before free merge_list */
3303     for (l = merge_list; l != NULL; l = g_list_next(l))
3304       g_free(l->data);
3305     g_list_free(merge_list);
3306     vik_layer_emit_update( VIK_LAYER(vtl), FALSE );
3307   }
3308 }
3309
3310 /* merge by time routine */
3311 static void trw_layer_merge_by_timestamp ( gpointer pass_along[6] )
3312 {
3313   VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
3314   gchar *orig_track_name = strdup(pass_along[3]);
3315
3316   //time_t t1, t2;
3317   GList *nearby_tracks;
3318   VikTrack *track;
3319   GList *trps;
3320   static  guint thr = 1;
3321   guint track_count = 0;
3322
3323   GList *tracks_with_timestamp = NULL;
3324   track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, orig_track_name );
3325   if (track->trackpoints &&
3326       !VIK_TRACKPOINT(track->trackpoints->data)->has_timestamp) {
3327     a_dialog_error_msg(VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Failed. This track does not have timestamp"));
3328     free(orig_track_name);
3329     return;
3330   }
3331
3332   twt_udata udata;
3333   udata.result = &tracks_with_timestamp;
3334   udata.exclude = track->trackpoints;
3335   udata.with_timestamps = TRUE;
3336   g_hash_table_foreach(vtl->tracks, find_tracks_with_timestamp_type, (gpointer)&udata);
3337   tracks_with_timestamp = g_list_reverse(tracks_with_timestamp);
3338
3339   if (!tracks_with_timestamp) {
3340     a_dialog_error_msg(VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Failed. No other track in this layer has timestamp"));
3341     free(orig_track_name);
3342     return;
3343   }
3344   g_list_free(tracks_with_timestamp);
3345
3346   if (!a_dialog_time_threshold(VIK_GTK_WINDOW_FROM_LAYER(vtl), 
3347                                _("Merge Threshold..."), 
3348                                _("Merge when time between tracks less than:"), 
3349                                &thr)) {
3350     free(orig_track_name);
3351     return;
3352   }
3353
3354   /* merge tracks until we can't */
3355   nearby_tracks = NULL;
3356   do {
3357     gpointer params[3];
3358
3359     track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, orig_track_name );
3360     trps = track->trackpoints;
3361     if ( !trps )
3362       return;
3363
3364
3365     if (nearby_tracks) {
3366       g_list_free(nearby_tracks);
3367       nearby_tracks = NULL;
3368     }
3369
3370     //t1 = ((VikTrackpoint *)trps->data)->timestamp;
3371     //t2 = ((VikTrackpoint *)g_list_last(trps)->data)->timestamp;
3372     
3373     /*    g_print("Original track times: %d and %d\n", t1, t2);  */
3374     params[0] = &nearby_tracks;
3375     params[1] = trps;
3376     params[2] = GUINT_TO_POINTER (thr);
3377
3378     /* get a list of adjacent-in-time tracks */
3379     g_hash_table_foreach(vtl->tracks, find_nearby_track, (gpointer)params);
3380
3381     /* add original track */
3382     nearby_tracks = g_list_prepend(nearby_tracks, orig_track_name);
3383
3384     /* merge them */
3385     { 
3386 #define get_track(x) VIK_TRACK(g_hash_table_lookup(vtl->tracks, (gchar *)((x)->data)))
3387 #define get_first_trackpoint(x) VIK_TRACKPOINT(get_track(x)->trackpoints->data)
3388 #define get_last_trackpoint(x) VIK_TRACKPOINT(g_list_last(get_track(x)->trackpoints)->data)
3389       GList *l = nearby_tracks;
3390       VikTrack *tr = vik_track_new();
3391       tr->visible = track->visible;
3392       track_count = 0;
3393       while (l) {
3394         /*
3395         time_t t1, t2;
3396         t1 = get_first_trackpoint(l)->timestamp;
3397         t2 = get_last_trackpoint(l)->timestamp;
3398         g_print("     %20s: track %d - %d\n", (char *)l->data, (int)t1, (int)t2);
3399         */
3400
3401
3402         /* remove trackpoints from merged track, delete track */
3403         tr->trackpoints = g_list_concat(tr->trackpoints, get_track(l)->trackpoints);
3404         get_track(l)->trackpoints = NULL;
3405         vik_trw_layer_delete_track(vtl, l->data);
3406
3407         track_count ++;
3408         l = g_list_next(l);
3409       }
3410       tr->trackpoints = g_list_sort(tr->trackpoints, trackpoint_compare);
3411       vik_trw_layer_add_track(vtl, strdup(orig_track_name), tr);
3412
3413 #undef get_first_trackpoint
3414 #undef get_last_trackpoint
3415 #undef get_track
3416     }
3417   } while (track_count > 1);
3418   g_list_free(nearby_tracks);
3419   free(orig_track_name);
3420   vik_layer_emit_update( VIK_LAYER(vtl), FALSE );
3421 }
3422
3423 /* split by time routine */
3424 static void trw_layer_split_by_timestamp ( gpointer pass_along[6] )
3425 {
3426   VikTrack *track = (VikTrack *) g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, pass_along[3] );
3427   GList *trps = track->trackpoints;
3428   GList *iter;
3429   GList *newlists = NULL;
3430   GList *newtps = NULL;
3431   guint i;
3432   static guint thr = 1;
3433
3434   time_t ts, prev_ts;
3435
3436   if ( !trps )
3437     return;
3438
3439   if (!a_dialog_time_threshold(VIK_GTK_WINDOW_FROM_LAYER(pass_along[0]), 
3440                                _("Split Threshold..."), 
3441                                _("Split when time between trackpoints exceeds:"), 
3442                                &thr)) {
3443     return;
3444   }
3445
3446   /* iterate through trackpoints, and copy them into new lists without touching original list */
3447   prev_ts = VIK_TRACKPOINT(trps->data)->timestamp;
3448   iter = trps;
3449
3450   while (iter) {
3451     ts = VIK_TRACKPOINT(iter->data)->timestamp;
3452     if (ts < prev_ts) {
3453       g_print("panic: ts < prev_ts: this should never happen!\n");
3454       return;
3455     }
3456     if (ts - prev_ts > thr*60) {
3457       /* flush accumulated trackpoints into new list */
3458       newlists = g_list_append(newlists, g_list_reverse(newtps));
3459       newtps = NULL;
3460     }
3461
3462     /* accumulate trackpoint copies in newtps, in reverse order */
3463     newtps = g_list_prepend(newtps, vik_trackpoint_copy(VIK_TRACKPOINT(iter->data)));
3464     prev_ts = ts;
3465     iter = g_list_next(iter);
3466   }
3467   if (newtps) {
3468       newlists = g_list_append(newlists, g_list_reverse(newtps));
3469   }
3470
3471   /* put lists of trackpoints into tracks */
3472   iter = newlists;
3473   i = 1;
3474   // Only bother updating if the split results in new tracks
3475   if (g_list_length (newlists) > 1) {
3476     while (iter) {
3477       gchar *new_tr_name;
3478       VikTrack *tr;
3479
3480       tr = vik_track_new();
3481       tr->visible = track->visible;
3482       tr->trackpoints = (GList *)(iter->data);
3483
3484       new_tr_name = g_strdup_printf("%s #%d", (gchar *) pass_along[3], i++);
3485       vik_trw_layer_add_track(VIK_TRW_LAYER(pass_along[0]), new_tr_name, tr);
3486       /*    g_print("adding track %s, times %d - %d\n", new_tr_name, VIK_TRACKPOINT(tr->trackpoints->data)->timestamp,
3487           VIK_TRACKPOINT(g_list_last(tr->trackpoints)->data)->timestamp);*/
3488
3489       iter = g_list_next(iter);
3490     }
3491     vik_trw_layer_delete_track(VIK_TRW_LAYER(pass_along[0]), (gchar *)pass_along[3]);
3492     vik_layer_emit_update(VIK_LAYER(pass_along[0]), FALSE);
3493   }
3494   g_list_free(newlists);
3495 }
3496
3497 /**
3498  * Split a track by the number of points as specified by the user
3499  */
3500 static void trw_layer_split_by_n_points ( gpointer pass_along[6] )
3501 {
3502   VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
3503   VikTrack *track = (VikTrack *)g_hash_table_lookup ( vtl->tracks, pass_along[3] );
3504
3505   // Check valid track
3506   GList *trps = track->trackpoints;
3507   if ( !trps )
3508     return;
3509
3510   gint points = a_dialog_get_positive_number(VIK_GTK_WINDOW_FROM_LAYER(pass_along[0]),
3511                                              _("Split Every Nth Point"),
3512                                              _("Split on every Nth point:"),
3513                                              250,   // Default value as per typical limited track capacity of various GPS devices
3514                                              2,     // Min
3515                                              65536, // Max
3516                                              5);    // Step
3517   // Was a valid number returned?
3518   if (!points)
3519     return;
3520
3521   // Now split...
3522   GList *iter;
3523   GList *newlists = NULL;
3524   GList *newtps = NULL;
3525   gint count = 0;
3526   iter = trps;
3527
3528   while (iter) {
3529     /* accumulate trackpoint copies in newtps, in reverse order */
3530     newtps = g_list_prepend(newtps, vik_trackpoint_copy(VIK_TRACKPOINT(iter->data)));
3531     count++;
3532     if (count >= points) {
3533       /* flush accumulated trackpoints into new list */
3534       newlists = g_list_append(newlists, g_list_reverse(newtps));
3535       newtps = NULL;
3536       count = 0;
3537     }
3538     iter = g_list_next(iter);
3539   }
3540
3541   // If there is a remaining chunk put that into the new split list
3542   // This may well be the whole track if no split points were encountered
3543   if (newtps) {
3544       newlists = g_list_append(newlists, g_list_reverse(newtps));
3545   }
3546
3547   /* put lists of trackpoints into tracks */
3548   iter = newlists;
3549   guint i = 1;
3550   // Only bother updating if the split results in new tracks
3551   if (g_list_length (newlists) > 1) {
3552     while (iter) {
3553       gchar *new_tr_name;
3554       VikTrack *tr;
3555
3556       tr = vik_track_new();
3557       tr->visible = track->visible;
3558       tr->trackpoints = (GList *)(iter->data);
3559
3560       new_tr_name = g_strdup_printf("%s #%d", (gchar *) pass_along[3], i++);
3561       vik_trw_layer_add_track(VIK_TRW_LAYER(pass_along[0]), new_tr_name, tr);
3562
3563       iter = g_list_next(iter);
3564     }
3565     // Remove original track and then update the display
3566     vik_trw_layer_delete_track(VIK_TRW_LAYER(pass_along[0]), (gchar *)pass_along[3]);
3567     vik_layer_emit_update(VIK_LAYER(pass_along[0]), FALSE);
3568   }
3569   g_list_free(newlists);
3570 }
3571
3572 /* end of split/merge routines */
3573
3574 /**
3575  * Similar to trw_layer_enum_item, but this uses a sorted method
3576  */
3577 static void trw_layer_sorted_name_list(gpointer key, gpointer value, gpointer udata)
3578 {
3579   GList **list = (GList**)udata;
3580   //*list = g_list_prepend(*all, key); //unsorted method
3581   // Sort named list alphabetically
3582   *list = g_list_insert_sorted_with_data (*list, key, sort_alphabetically, NULL);
3583 }
3584
3585 /**
3586  *
3587  */
3588 static void trw_layer_delete_tracks_from_selection ( gpointer lav[2] )
3589 {
3590   VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
3591   GList *all = NULL;
3592   // Sort list alphabetically for better presentation
3593   g_hash_table_foreach(vtl->tracks, trw_layer_sorted_name_list, &all);
3594
3595   if ( ! all ) {
3596     a_dialog_error_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), _("No tracks found"));
3597     return;
3598   }
3599
3600   // Get list of items to delete from the user
3601   GList *delete_list = a_dialog_select_from_list(VIK_GTK_WINDOW_FROM_LAYER(vtl),
3602                                                  all,
3603                                                  TRUE,
3604                                                  _("Delete Selection"),
3605                                                  _("Select tracks to delete"));
3606   g_list_free(all);
3607
3608   // Delete requested tracks
3609   // since specificly requested, IMHO no need for extra confirmation
3610   if ( delete_list ) {
3611     GList *l;
3612     for (l = delete_list; l != NULL; l = g_list_next(l)) {
3613       vik_trw_layer_delete_track(vtl, l->data);
3614     }
3615     g_list_free(delete_list);
3616     vik_layer_emit_update( VIK_LAYER(vtl), FALSE );
3617   }
3618 }
3619
3620 /**
3621  *
3622  */
3623 static void trw_layer_delete_waypoints_from_selection ( gpointer lav[2] )
3624 {
3625   VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
3626   GList *all = NULL;
3627
3628   // Sort list alphabetically for better presentation
3629   g_hash_table_foreach ( vtl->waypoints, trw_layer_sorted_name_list, &all);
3630   if ( ! all ) {
3631     a_dialog_error_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), _("No waypoints found"));
3632     return;
3633   }
3634
3635   all = g_list_sort_with_data(all, sort_alphabetically, NULL);
3636
3637   // Get list of items to delete from the user
3638   GList *delete_list = a_dialog_select_from_list(VIK_GTK_WINDOW_FROM_LAYER(vtl),
3639                                                  all,
3640                                                  TRUE,
3641                                                  _("Delete Selection"),
3642                                                  _("Select waypoints to delete"));
3643   g_list_free(all);
3644
3645   // Delete requested waypoints
3646   // since specificly requested, IMHO no need for extra confirmation
3647   if ( delete_list ) {
3648     GList *l;
3649     for (l = delete_list; l != NULL; l = g_list_next(l)) {
3650       vik_trw_layer_delete_waypoint(vtl, l->data);
3651     }
3652     g_list_free(delete_list);
3653     vik_layer_emit_update( VIK_LAYER(vtl), FALSE );
3654   }
3655
3656 }
3657
3658 static void trw_layer_goto_waypoint ( gpointer pass_along[6] )
3659 {
3660   VikWaypoint *wp = g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->waypoints, pass_along[3] );
3661   if ( wp )
3662     goto_coord ( pass_along[1], pass_along[0], pass_along[5], &(wp->coord) );
3663 }
3664
3665 static void trw_layer_waypoint_gc_webpage ( gpointer pass_along[6] )
3666 {
3667   gchar *webpage = g_strdup_printf("http://www.geocaching.com/seek/cache_details.aspx?wp=%s", (gchar *) pass_along[3] );
3668   open_url(VIK_GTK_WINDOW_FROM_LAYER(VIK_LAYER(pass_along[0])), webpage);
3669   g_free ( webpage );
3670 }
3671
3672 static const gchar* trw_layer_sublayer_rename_request ( VikTrwLayer *l, const gchar *newname, gpointer vlp, gint subtype, gpointer sublayer, GtkTreeIter *iter )
3673 {
3674   if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT )
3675   {
3676     gchar *rv;
3677     VikWaypoint *wp;
3678
3679     if (strcmp(newname, sublayer) == 0 )
3680       return NULL;
3681
3682     if (strcasecmp(newname, sublayer)) { /* Not just changing case */
3683       if (g_hash_table_lookup( l->waypoints, newname))
3684       {
3685         a_dialog_error_msg ( VIK_GTK_WINDOW_FROM_LAYER(l), _("Waypoint Already Exists") );
3686         return NULL;
3687       }
3688     }
3689
3690     iter = g_hash_table_lookup ( l->waypoints_iters, sublayer );
3691     g_hash_table_steal ( l->waypoints_iters, sublayer );
3692
3693     wp = vik_waypoint_copy ( VIK_WAYPOINT(g_hash_table_lookup ( l->waypoints, sublayer )) );
3694     highest_wp_number_remove_wp(l, sublayer);
3695     g_hash_table_remove ( l->waypoints, sublayer );
3696
3697     rv = g_strdup(newname);
3698
3699     vik_treeview_item_set_pointer ( VIK_LAYER(l)->vt, iter, rv );
3700
3701     highest_wp_number_add_wp(l, rv);
3702     g_hash_table_insert ( l->waypoints, rv, wp );
3703     g_hash_table_insert ( l->waypoints_iters, rv, iter );
3704
3705     /* it hasn't been updated yet so we pass new name */
3706 #ifdef VIK_CONFIG_ALPHABETIZED_TRW
3707     vik_treeview_sublayer_realphabetize ( VIK_LAYER(l)->vt, iter, rv );
3708 #endif
3709
3710     vik_layers_panel_emit_update ( VIK_LAYERS_PANEL(vlp) );
3711     return rv;
3712   }
3713   if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
3714   {
3715     gchar *rv;
3716     VikTrack *tr;
3717     GtkTreeIter *iter;
3718     gchar *orig_key;
3719
3720     if (strcmp(newname, sublayer) == 0)
3721       return NULL;
3722
3723     if (strcasecmp(newname, sublayer)) { /* Not just changing case */
3724       if (g_hash_table_lookup( l->tracks, newname))
3725       {
3726         a_dialog_error_msg ( VIK_GTK_WINDOW_FROM_LAYER(l), _("Track Already Exists") );
3727         return NULL;
3728       }
3729     }
3730
3731     g_hash_table_lookup_extended ( l->tracks, sublayer, (void *)&orig_key, (void *)&tr );
3732     g_hash_table_steal ( l->tracks, sublayer );
3733
3734     iter = g_hash_table_lookup ( l->tracks_iters, sublayer );
3735     g_hash_table_steal ( l->tracks_iters, sublayer );
3736
3737     rv = g_strdup(newname);
3738
3739     vik_treeview_item_set_pointer ( VIK_LAYER(l)->vt, iter, rv );
3740
3741     g_hash_table_insert ( l->tracks, rv, tr );
3742     g_hash_table_insert ( l->tracks_iters, rv, iter );
3743
3744     /* don't forget about current_tp_track_name, update that too */
3745     if ( l->current_tp_track_name && g_strcasecmp(orig_key,l->current_tp_track_name) == 0 )
3746     {
3747       l->current_tp_track_name = rv;
3748       if ( l->tpwin )
3749         vik_trw_layer_tpwin_set_track_name ( l->tpwin, rv );
3750     }
3751     else if ( l->last_tp_track_name && g_strcasecmp(orig_key,l->last_tp_track_name) == 0 )
3752       l->last_tp_track_name = rv;
3753
3754     g_free ( orig_key );
3755
3756 #ifdef VIK_CONFIG_ALPHABETIZED_TRW
3757     vik_treeview_sublayer_realphabetize ( VIK_LAYER(l)->vt, iter, rv );
3758 #endif
3759
3760     vik_layers_panel_emit_update ( VIK_LAYERS_PANEL(vlp) );
3761     return rv;
3762   }
3763   return NULL;
3764 }
3765
3766 static gboolean is_valid_geocache_name ( gchar *str )
3767 {
3768   gint len = strlen ( str );
3769   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]));
3770 }
3771
3772 static void trw_layer_track_use_with_filter ( gpointer pass_along[6] )
3773 {
3774   gchar *track_name = (gchar *) pass_along[3];
3775   VikTrack *tr = g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, track_name );
3776   a_acquire_set_filter_track ( tr, track_name );
3777 }
3778
3779 static gboolean is_valid_google_route ( VikTrwLayer *vtl, const gchar *track_name )
3780 {
3781   VikTrack *tr = g_hash_table_lookup ( vtl->tracks, track_name );
3782   return ( tr && tr->comment && strlen(tr->comment) > 7 && !strncmp(tr->comment, "from:", 5) );
3783 }
3784
3785 static void trw_layer_track_google_route_webpage ( gpointer pass_along[6] )
3786 {
3787   gchar *track_name = (gchar *) pass_along[3];
3788   VikTrack *tr = g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, track_name );
3789   if ( tr ) {
3790     gchar *escaped = uri_escape ( tr->comment );
3791     gchar *webpage = g_strdup_printf("http://maps.google.com/maps?f=q&hl=en&q=%s", escaped );
3792     open_url(VIK_GTK_WINDOW_FROM_LAYER(VIK_LAYER(pass_along[0])), webpage);
3793     g_free ( escaped );
3794     g_free ( webpage );
3795   }
3796 }
3797
3798 /* vlp can be NULL if necessary - i.e. right-click from a tool */
3799 /* viewpoint is now available instead */
3800 static gboolean trw_layer_sublayer_add_menu_items ( VikTrwLayer *l, GtkMenu *menu, gpointer vlp, gint subtype, gpointer sublayer, GtkTreeIter *iter, VikViewport *vvp )
3801 {
3802   static gpointer pass_along[6];
3803   GtkWidget *item;
3804   gboolean rv = FALSE;
3805
3806   pass_along[0] = l;
3807   pass_along[1] = vlp;
3808   pass_along[2] = GINT_TO_POINTER (subtype);
3809   pass_along[3] = sublayer;
3810   pass_along[4] = GINT_TO_POINTER (1); // Confirm delete request
3811   pass_along[5] = vvp;
3812
3813   if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT || subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
3814   {
3815     rv = TRUE;
3816
3817     item = gtk_image_menu_item_new_from_stock ( GTK_STOCK_PROPERTIES, NULL );
3818     g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_properties_item), pass_along );
3819     gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
3820     gtk_widget_show ( item );
3821
3822     if (subtype == VIK_TRW_LAYER_SUBLAYER_TRACK) {
3823       VikTrwLayer *vtl = l;
3824       VikTrack *tr = g_hash_table_lookup ( vtl->tracks, sublayer );
3825       if (tr && tr->property_dialog)
3826         gtk_widget_set_sensitive(GTK_WIDGET(item), FALSE );
3827     }
3828
3829     item = gtk_image_menu_item_new_from_stock ( GTK_STOCK_CUT, NULL );
3830     g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_cut_item_cb), pass_along );
3831     gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
3832     gtk_widget_show ( item );
3833
3834     item = gtk_image_menu_item_new_from_stock ( GTK_STOCK_COPY, NULL );
3835     g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_copy_item_cb), pass_along );
3836     gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
3837     gtk_widget_show ( item );
3838
3839     item = gtk_image_menu_item_new_from_stock ( GTK_STOCK_DELETE, NULL );
3840     g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_item), pass_along );
3841     gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
3842     gtk_widget_show ( item );
3843
3844     if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT )
3845     {
3846       gboolean separator_created = FALSE;
3847
3848       /* could be a right-click using the tool */
3849       if ( vlp != NULL ) {
3850         item = gtk_menu_item_new ();
3851         gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
3852         gtk_widget_show ( item );
3853
3854         separator_created = TRUE;
3855
3856         item = gtk_image_menu_item_new_with_mnemonic ( _("_Goto") );
3857         gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_JUMP_TO, GTK_ICON_SIZE_MENU) );
3858         g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_waypoint), pass_along );
3859         gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
3860         gtk_widget_show ( item );
3861       }
3862
3863       if ( is_valid_geocache_name ( (gchar *) sublayer ) )
3864       {
3865         if ( !separator_created ) {
3866           item = gtk_menu_item_new ();
3867           gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
3868           gtk_widget_show ( item );
3869           separator_created = TRUE;
3870         }
3871
3872         item = gtk_menu_item_new_with_mnemonic ( _("_Visit Geocache Webpage") );
3873         g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_waypoint_gc_webpage), pass_along );
3874         gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
3875         gtk_widget_show ( item );
3876       }
3877
3878       VikWaypoint *wp = g_hash_table_lookup ( VIK_TRW_LAYER(l)->waypoints, sublayer );
3879
3880       if ( wp && wp->image )
3881       {
3882         if ( !separator_created ) {
3883           item = gtk_menu_item_new ();
3884           gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
3885           gtk_widget_show ( item );
3886           separator_created = TRUE;
3887         }
3888
3889         // Set up image paramater
3890         pass_along[5] = wp->image;
3891
3892         item = gtk_image_menu_item_new_with_mnemonic ( _("_Show Picture...") );
3893         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
3894         g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_show_picture), pass_along );
3895         gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
3896         gtk_widget_show ( item );
3897       }
3898
3899     }
3900   }
3901
3902   if ( vlp && (subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINTS || subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT) )
3903   {
3904     rv = TRUE;
3905     item = gtk_image_menu_item_new_with_mnemonic ( _("_New Waypoint...") );
3906     gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NEW, GTK_ICON_SIZE_MENU) );
3907     g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_wp), pass_along );
3908     gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3909     gtk_widget_show ( item );
3910   }
3911
3912   if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINTS )
3913   {
3914     item = gtk_image_menu_item_new_with_mnemonic ( _("_View All Waypoints") );
3915     gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ZOOM_FIT, GTK_ICON_SIZE_MENU) );
3916     g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_waypoints_view), pass_along );
3917     gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3918     gtk_widget_show ( item );
3919
3920     item = gtk_image_menu_item_new_with_mnemonic ( _("Goto _Waypoint...") );
3921     gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_JUMP_TO, GTK_ICON_SIZE_MENU) );
3922     g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_wp), pass_along );
3923     gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3924     gtk_widget_show ( item );
3925
3926     item = gtk_image_menu_item_new_with_mnemonic ( _("Delete _All Waypoints") );
3927     gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU) );
3928     g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_all_waypoints), pass_along );
3929     gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
3930     gtk_widget_show ( item );
3931
3932     item = gtk_image_menu_item_new_with_mnemonic ( _("_Delete Waypoints From Selection...") );
3933     gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
3934     g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_waypoints_from_selection), pass_along );
3935     gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
3936     gtk_widget_show ( item );
3937   }
3938
3939   if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACKS )
3940   {
3941     rv = TRUE;
3942
3943     item = gtk_image_menu_item_new_with_mnemonic ( _("_View All Tracks") );
3944     gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ZOOM_FIT, GTK_ICON_SIZE_MENU) );
3945     g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_tracks_view), pass_along );
3946     gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3947     gtk_widget_show ( item );
3948
3949     item = gtk_image_menu_item_new_with_mnemonic ( _("Delete _All Tracks") );
3950     gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU) );
3951     g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_all_tracks), pass_along );
3952     gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
3953     gtk_widget_show ( item );
3954
3955     item = gtk_image_menu_item_new_with_mnemonic ( _("_Delete Tracks From Selection...") );
3956     gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
3957     g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_tracks_from_selection), pass_along );
3958     gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
3959     gtk_widget_show ( item );
3960   }
3961
3962   if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
3963   {
3964     GtkWidget *goto_submenu;
3965     item = gtk_menu_item_new ();
3966     gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
3967     gtk_widget_show ( item );
3968
3969     goto_submenu = gtk_menu_new ();
3970     item = gtk_image_menu_item_new_with_mnemonic ( _("_Goto") );
3971     gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_JUMP_TO, GTK_ICON_SIZE_MENU) );
3972     gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3973     gtk_widget_show ( item );
3974     gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), goto_submenu );
3975
3976     item = gtk_image_menu_item_new_with_mnemonic ( _("_Startpoint") );
3977     gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GOTO_FIRST, GTK_ICON_SIZE_MENU) );
3978     g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_startpoint), pass_along );
3979     gtk_menu_shell_append ( GTK_MENU_SHELL(goto_submenu), item );
3980     gtk_widget_show ( item );
3981
3982     item = gtk_image_menu_item_new_with_mnemonic ( _("\"_Center\"") );
3983     gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_JUMP_TO, GTK_ICON_SIZE_MENU) );
3984     g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_center), pass_along );
3985     gtk_menu_shell_append ( GTK_MENU_SHELL(goto_submenu), item );
3986     gtk_widget_show ( item );
3987
3988     item = gtk_image_menu_item_new_with_mnemonic ( _("_Endpoint") );
3989     gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GOTO_LAST, GTK_ICON_SIZE_MENU) );
3990     g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_endpoint), pass_along );
3991     gtk_menu_shell_append ( GTK_MENU_SHELL(goto_submenu), item );
3992     gtk_widget_show ( item );
3993
3994     item = gtk_image_menu_item_new_with_mnemonic ( _("_Highest Altitude") );
3995     gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GOTO_TOP, GTK_ICON_SIZE_MENU) );
3996     g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_max_alt), pass_along );
3997     gtk_menu_shell_append ( GTK_MENU_SHELL(goto_submenu), item );
3998     gtk_widget_show ( item );
3999
4000     item = gtk_image_menu_item_new_with_mnemonic ( _("_Lowest Altitude") );
4001     gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GOTO_BOTTOM, GTK_ICON_SIZE_MENU) );
4002     g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_min_alt), pass_along );
4003     gtk_menu_shell_append ( GTK_MENU_SHELL(goto_submenu), item );
4004     gtk_widget_show ( item );
4005
4006     item = gtk_image_menu_item_new_with_mnemonic ( _("_Maximum Speed") );
4007     gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_MEDIA_FORWARD, GTK_ICON_SIZE_MENU) );
4008     g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_max_speed), pass_along );
4009     gtk_menu_shell_append ( GTK_MENU_SHELL(goto_submenu), item );
4010     gtk_widget_show ( item );
4011
4012     item = gtk_image_menu_item_new_with_mnemonic ( _("_View Track") );
4013     gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ZOOM_FIT, GTK_ICON_SIZE_MENU) );
4014     g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_track_view), pass_along );
4015     gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
4016     gtk_widget_show ( item );
4017
4018     item = gtk_menu_item_new_with_mnemonic ( _("_Merge By Time...") );
4019     g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_merge_by_timestamp), pass_along );
4020     gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
4021     gtk_widget_show ( item );
4022
4023     item = gtk_menu_item_new_with_mnemonic ( _("Merge _With Other Tracks...") );
4024     g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_merge_with_other), pass_along );
4025     gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
4026     gtk_widget_show ( item );
4027
4028     item = gtk_menu_item_new_with_mnemonic ( _("_Split By Time...") );
4029     g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_split_by_timestamp), pass_along );
4030     gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
4031     gtk_widget_show ( item );
4032
4033     item = gtk_menu_item_new_with_mnemonic ( _("Split By _Number of Points...") );
4034     g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_split_by_n_points), pass_along );
4035     gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
4036     gtk_widget_show ( item );
4037
4038     /* ATM This function is only available via the layers panel, due to the method in finding out the maps in use */
4039     if ( vlp ) {
4040       item = gtk_image_menu_item_new_with_mnemonic ( _("Down_load Maps Along Track...") );
4041       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
4042       g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_download_map_along_track_cb), pass_along );
4043       gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
4044       gtk_widget_show ( item );
4045     }
4046
4047     item = gtk_image_menu_item_new_with_mnemonic ( _("_Apply DEM Data") );
4048     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
4049     g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_apply_dem_data), pass_along );
4050     gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
4051     gtk_widget_show ( item );
4052
4053     item = gtk_image_menu_item_new_with_mnemonic ( _("Export Trac_k as GPX...") );
4054     gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_HARDDISK, GTK_ICON_SIZE_MENU) );
4055     g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_gpx_track), pass_along );
4056     gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
4057     gtk_widget_show ( item );
4058
4059     item = gtk_image_menu_item_new_with_mnemonic ( _("E_xtend Track End") );
4060     gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ADD, GTK_ICON_SIZE_MENU) );
4061     g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_extend_track_end), pass_along );
4062     gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
4063     gtk_widget_show ( item );
4064
4065     item = gtk_image_menu_item_new_with_mnemonic ( _("Extend _Using Route Finder") );
4066     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
4067     g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_extend_track_end_route_finder), pass_along );
4068     gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
4069     gtk_widget_show ( item );
4070
4071 #ifdef VIK_CONFIG_OPENSTREETMAP
4072     item = gtk_image_menu_item_new_with_mnemonic ( _("Upload to _OSM...") );
4073     g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(osm_traces_upload_track_cb), pass_along );
4074     gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_UP, GTK_ICON_SIZE_MENU) );
4075     gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
4076     gtk_widget_show ( item );
4077 #endif
4078
4079     if ( is_valid_google_route ( l, (gchar *) sublayer ) )
4080     {
4081       item = gtk_image_menu_item_new_with_mnemonic ( _("_View Google Directions") );
4082       gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NETWORK, GTK_ICON_SIZE_MENU) );
4083       g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_track_google_route_webpage), pass_along );
4084       gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
4085       gtk_widget_show ( item );
4086     }
4087
4088     item = gtk_image_menu_item_new_with_mnemonic ( _("Use with _Filter") );
4089     gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
4090     g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_track_use_with_filter), pass_along );
4091     gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
4092     gtk_widget_show ( item );
4093
4094     /* ATM This function is only available via the layers panel, due to needing a vlp */
4095     if ( vlp ) {
4096       item = a_acquire_track_menu ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(l)), vlp,
4097                                     vik_layers_panel_get_viewport(VIK_LAYERS_PANEL(vlp)),
4098                                     g_hash_table_lookup ( l->tracks, (gchar *) sublayer ) );
4099       if ( item ) {
4100         gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
4101         gtk_widget_show ( item );
4102       }
4103     }
4104
4105     // Only show on viewport popmenu when a trackpoint is selected
4106     if ( ! vlp && l->current_tpl ) {
4107       // Add separator
4108       item = gtk_menu_item_new ();
4109       gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
4110       gtk_widget_show ( item );
4111
4112       item = gtk_image_menu_item_new_with_mnemonic ( _("_Edit Trackpoint") );
4113       gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_PROPERTIES, GTK_ICON_SIZE_MENU) );
4114       g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_edit_trackpoint), pass_along );
4115       gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
4116       gtk_widget_show ( item );
4117     }
4118
4119   }
4120
4121   return rv;
4122 }
4123
4124 static void trw_layer_insert_tp_after_current_tp ( VikTrwLayer *vtl )
4125 {
4126   /* sanity checks */
4127   if (!vtl->current_tpl)
4128     return;
4129   if (!vtl->current_tpl->next)
4130     return;
4131
4132   VikTrackpoint *tp_current = VIK_TRACKPOINT(vtl->current_tpl->data);
4133   VikTrackpoint *tp_next = VIK_TRACKPOINT(vtl->current_tpl->next->data);
4134
4135   /* Use current and next trackpoints to form a new track point which is inserted into the tracklist */
4136   if ( tp_next ) {
4137
4138     VikTrackpoint *tp_new = vik_trackpoint_new();
4139     struct LatLon ll_current, ll_next;
4140     vik_coord_to_latlon ( &tp_current->coord, &ll_current );
4141     vik_coord_to_latlon ( &tp_next->coord, &ll_next );
4142
4143     /* main positional interpolation */
4144     struct LatLon ll_new = { (ll_current.lat+ll_next.lat)/2, (ll_current.lon+ll_next.lon)/2 };
4145     vik_coord_load_from_latlon ( &(tp_new->coord), vtl->coord_mode, &ll_new );
4146
4147     /* Now other properties that can be interpolated */
4148     tp_new->altitude = (tp_current->altitude + tp_next->altitude) / 2;
4149
4150     if (tp_current->has_timestamp && tp_next->has_timestamp) {
4151       /* Note here the division is applied to each part, then added
4152          This is to avoid potential overflow issues with a 32 time_t for dates after midpoint of this Unix time on 2004/01/04 */
4153       tp_new->timestamp = (tp_current->timestamp/2) + (tp_next->timestamp/2);
4154       tp_new->has_timestamp = TRUE;
4155     }
4156
4157     if (tp_current->speed != NAN && tp_next->speed != NAN)
4158       tp_new->speed = (tp_current->speed + tp_next->speed)/2;
4159
4160     /* TODO - improve interpolation of course, as it may not be correct.
4161        if courses in degrees are 350 + 020, the mid course more likely to be 005 (not 185)
4162        [similar applies if value is in radians] */
4163     if (tp_current->course != NAN && tp_next->course != NAN)
4164       tp_new->speed = (tp_current->course + tp_next->course)/2;
4165
4166     /* DOP / sat values remain at defaults as not they do not seem applicable to a dreamt up point */
4167
4168     /* Insert new point into the trackpoints list after the current TP */
4169     VikTrack *tr = g_hash_table_lookup ( vtl->tracks, vtl->current_tp_track_name );
4170     gint index =  g_list_index ( tr->trackpoints, tp_current );
4171     if ( index > -1 ) {
4172       tr->trackpoints = g_list_insert (tr->trackpoints, tp_new, index+1 );
4173     }
4174   }
4175 }
4176
4177 /* to be called when last_tpl no long exists. */
4178 static void trw_layer_cancel_last_tp ( VikTrwLayer *vtl )
4179 {
4180   if ( vtl->tpwin ) /* can't join with a non-existant TP. */
4181     vik_trw_layer_tpwin_disable_join ( vtl->tpwin );
4182   vtl->last_tpl = NULL;
4183   vtl->last_tp_track_name = NULL;
4184 }
4185
4186 static void trw_layer_cancel_current_tp ( VikTrwLayer *vtl, gboolean destroy )
4187 {
4188   if ( vtl->tpwin )
4189   {
4190     if ( destroy)
4191     {
4192       gtk_widget_destroy ( GTK_WIDGET(vtl->tpwin) );
4193       vtl->tpwin = NULL;
4194     }
4195     else
4196       vik_trw_layer_tpwin_set_empty ( vtl->tpwin );
4197   }
4198   if ( vtl->current_tpl )
4199   {
4200     vtl->current_tpl = NULL;
4201     vtl->current_tp_track_name = NULL;
4202     vik_layer_emit_update(VIK_LAYER(vtl), FALSE);
4203   }
4204 }
4205
4206 static void trw_layer_tpwin_response ( VikTrwLayer *vtl, gint response )
4207 {
4208   g_assert ( vtl->tpwin != NULL );
4209   if ( response == VIK_TRW_LAYER_TPWIN_CLOSE )
4210     trw_layer_cancel_current_tp ( vtl, TRUE );
4211
4212   if ( vtl->current_tpl == NULL )
4213     return;
4214
4215   if ( response == VIK_TRW_LAYER_TPWIN_SPLIT && vtl->current_tpl->next && vtl->current_tpl->prev )
4216   {
4217     gchar *name = get_new_unique_sublayer_name(vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, vtl->current_tp_track_name);
4218     if ( ( name = a_dialog_new_track ( GTK_WINDOW(vtl->tpwin), vtl->tracks, name ) ) )
4219     {
4220       VikTrack *tr = vik_track_new ();
4221       GList *newglist = g_list_alloc ();
4222       newglist->prev = NULL;
4223       newglist->next = vtl->current_tpl->next;
4224       newglist->data = vik_trackpoint_copy(VIK_TRACKPOINT(vtl->current_tpl->data));
4225       tr->trackpoints = newglist;
4226
4227       vtl->current_tpl->next->prev = newglist; /* end old track here */
4228       vtl->current_tpl->next = NULL;
4229
4230       vtl->current_tpl = newglist; /* change tp to first of new track. */
4231       vtl->current_tp_track_name = name;
4232
4233       vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, vtl->current_tp_track_name );
4234
4235       tr->visible = TRUE;
4236
4237       vik_trw_layer_add_track ( vtl, name, tr );
4238       vik_layer_emit_update(VIK_LAYER(vtl), FALSE);
4239     }
4240   }
4241   else if ( response == VIK_TRW_LAYER_TPWIN_DELETE )
4242   {
4243     VikTrack *tr = g_hash_table_lookup ( vtl->tracks, vtl->current_tp_track_name );
4244     GList *new_tpl;
4245     g_assert(tr != NULL);
4246
4247     /* can't join with a non-existent trackpoint */
4248     vtl->last_tpl = NULL;
4249     vtl->last_tp_track_name = NULL;
4250
4251     if ( (new_tpl = vtl->current_tpl->next) || (new_tpl = vtl->current_tpl->prev) )
4252     {
4253       if ( VIK_TRACKPOINT(vtl->current_tpl->data)->newsegment && vtl->current_tpl->next )
4254         VIK_TRACKPOINT(vtl->current_tpl->next->data)->newsegment = TRUE; /* don't concat segments on del */
4255
4256       tr->trackpoints = g_list_remove_link ( tr->trackpoints, vtl->current_tpl ); /* this nulls current_tpl->prev and next */
4257
4258       /* at this point the old trackpoint exists, but the list links are correct (new), so it is safe to do this. */
4259       vik_trw_layer_tpwin_set_tp ( vtl->tpwin, new_tpl, vtl->current_tp_track_name );
4260
4261       trw_layer_cancel_last_tp ( vtl );
4262
4263       g_free ( vtl->current_tpl->data ); /* TODO: vik_trackpoint_free() */
4264       g_list_free_1 ( vtl->current_tpl );
4265       vtl->current_tpl = new_tpl;
4266       vik_layer_emit_update(VIK_LAYER(vtl), FALSE);
4267     }
4268     else
4269     {
4270       tr->trackpoints = g_list_remove_link ( tr->trackpoints, vtl->current_tpl );
4271       g_free ( vtl->current_tpl->data ); /* TODO longone: vik_trackpoint_new() and vik_trackpoint_free() */
4272       g_list_free_1 ( vtl->current_tpl );
4273       trw_layer_cancel_current_tp ( vtl, FALSE );
4274     }
4275   }
4276   else if ( response == VIK_TRW_LAYER_TPWIN_FORWARD && vtl->current_tpl->next )
4277   {
4278     vtl->last_tpl = vtl->current_tpl;
4279     vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl = vtl->current_tpl->next, vtl->current_tp_track_name );
4280     vik_layer_emit_update(VIK_LAYER(vtl), FALSE); /* TODO longone: either move or only update if tp is inside drawing window */
4281   }
4282   else if ( response == VIK_TRW_LAYER_TPWIN_BACK && vtl->current_tpl->prev )
4283   {
4284     vtl->last_tpl = vtl->current_tpl;
4285     vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl = vtl->current_tpl->prev, vtl->current_tp_track_name );
4286     vik_layer_emit_update(VIK_LAYER(vtl), FALSE);
4287   }
4288   else if ( response == VIK_TRW_LAYER_TPWIN_JOIN )
4289   {
4290     // Check tracks exist and are different before joining
4291     if ( ! vtl->last_tp_track_name || ! vtl->current_tp_track_name || vtl->last_tp_track_name == vtl->current_tp_track_name )
4292       return;
4293
4294     VikTrack *tr1 = g_hash_table_lookup ( vtl->tracks, vtl->last_tp_track_name );
4295     VikTrack *tr2 = g_hash_table_lookup ( vtl->tracks, vtl->current_tp_track_name );
4296
4297     VikTrack *tr_first = tr1, *tr_last = tr2;
4298
4299     gchar *tmp;
4300
4301     if ( (!vtl->last_tpl->next) && (!vtl->current_tpl->next) ) /* both endpoints */
4302       vik_track_reverse ( tr2 ); /* reverse the second, that way second track clicked will be later. */
4303     else if ( (!vtl->last_tpl->prev) && (!vtl->current_tpl->prev) )
4304       vik_track_reverse ( tr1 );
4305     else if ( (!vtl->last_tpl->prev) && (!vtl->current_tpl->next) ) /* clicked startpoint, then endpoint -- concat end to start */
4306     {
4307       tr_first = tr2;
4308       tr_last = tr1;
4309     }
4310     /* default -- clicked endpoint then startpoint -- connect endpoint to startpoint */
4311
4312     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. */
4313       VIK_TRACKPOINT(tr_last->trackpoints->data)->newsegment = FALSE;
4314     tr1->trackpoints = g_list_concat ( tr_first->trackpoints, tr_last->trackpoints );
4315     tr2->trackpoints = NULL;
4316
4317     tmp = vtl->current_tp_track_name;
4318
4319     vtl->current_tp_track_name = vtl->last_tp_track_name; /* current_tp stays the same (believe it or not!) */
4320     vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, vtl->current_tp_track_name );
4321
4322     /* if we did this before, trw_layer_delete_track would have canceled the current tp because
4323      * it was the current track. canceling the current tp would have set vtl->current_tpl to NULL */
4324     vik_trw_layer_delete_track ( vtl, tmp );
4325
4326     trw_layer_cancel_last_tp ( vtl ); /* same TP, can't join. */
4327     vik_layer_emit_update(VIK_LAYER(vtl), FALSE);
4328   }
4329   else if ( response == VIK_TRW_LAYER_TPWIN_INSERT && vtl->current_tpl->next )
4330   {
4331     trw_layer_insert_tp_after_current_tp ( vtl );
4332     vik_layer_emit_update(VIK_LAYER(vtl), FALSE);
4333   }
4334   else if ( response == VIK_TRW_LAYER_TPWIN_DATA_CHANGED )
4335     vik_layer_emit_update(VIK_LAYER(vtl), FALSE);
4336 }
4337
4338 static void trw_layer_tpwin_init ( VikTrwLayer *vtl )
4339 {
4340   if ( ! vtl->tpwin )
4341   {
4342     vtl->tpwin = vik_trw_layer_tpwin_new ( VIK_GTK_WINDOW_FROM_LAYER(vtl) );
4343     g_signal_connect_swapped ( GTK_DIALOG(vtl->tpwin), "response", G_CALLBACK(trw_layer_tpwin_response), vtl );
4344     /* connect signals -- DELETE SIGNAL VERY IMPORTANT TO SET TO NULL */
4345     g_signal_connect_swapped ( vtl->tpwin, "delete-event", G_CALLBACK(trw_layer_cancel_current_tp), vtl );
4346     gtk_widget_show_all ( GTK_WIDGET(vtl->tpwin) );
4347   }
4348   if ( vtl->current_tpl )
4349     vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, vtl->current_tp_track_name );
4350   /* set layer name and TP data */
4351 }
4352
4353 /***************************************************************************
4354  ** Tool code
4355  ***************************************************************************/
4356
4357 /*** Utility data structures and functions ****/
4358
4359 typedef struct {
4360   gint x, y;
4361   gint closest_x, closest_y;
4362   gchar *closest_wp_name;
4363   VikWaypoint *closest_wp;
4364   VikViewport *vvp;
4365 } WPSearchParams;
4366
4367 typedef struct {
4368   gint x, y;
4369   gint closest_x, closest_y;
4370   gchar *closest_track_name;
4371   VikTrackpoint *closest_tp;
4372   VikViewport *vvp;
4373   GList *closest_tpl;
4374 } TPSearchParams;
4375
4376 static void waypoint_search_closest_tp ( gchar *name, VikWaypoint *wp, WPSearchParams *params )
4377 {
4378   gint x, y;
4379   if ( !wp->visible )
4380     return;
4381
4382   vik_viewport_coord_to_screen ( params->vvp, &(wp->coord), &x, &y );
4383
4384   // If waypoint has an image then use the image size to select
4385   if ( wp->image ) {
4386     gint slackx, slacky;
4387     slackx = wp->image_width / 2;
4388     slacky = wp->image_height / 2;
4389
4390     if (    x <= params->x + slackx && x >= params->x - slackx
4391          && y <= params->y + slacky && y >= params->y - slacky ) {
4392       params->closest_wp_name = name;
4393       params->closest_wp = wp;
4394       params->closest_x = x;
4395       params->closest_y = y;
4396     }
4397   }
4398   else if ( abs (x - params->x) <= WAYPOINT_SIZE_APPROX && abs (y - params->y) <= WAYPOINT_SIZE_APPROX &&
4399             ((!params->closest_wp) ||        /* was the old waypoint we already found closer than this one? */
4400              abs(x - params->x)+abs(y - params->y) < abs(x - params->closest_x)+abs(y - params->closest_y)))
4401     {
4402       params->closest_wp_name = name;
4403       params->closest_wp = wp;
4404       params->closest_x = x;
4405       params->closest_y = y;
4406     }
4407 }
4408
4409 static void track_search_closest_tp ( gchar *name, VikTrack *t, TPSearchParams *params )
4410 {
4411   GList *tpl = t->trackpoints;
4412   VikTrackpoint *tp;
4413
4414   if ( !t->visible )
4415     return;
4416
4417   while (tpl)
4418   {
4419     gint x, y;
4420     tp = VIK_TRACKPOINT(tpl->data);
4421
4422     vik_viewport_coord_to_screen ( params->vvp, &(tp->coord), &x, &y );
4423  
4424     if ( abs (x - params->x) <= TRACKPOINT_SIZE_APPROX && abs (y - params->y) <= TRACKPOINT_SIZE_APPROX &&
4425         ((!params->closest_tp) ||        /* was the old trackpoint we already found closer than this one? */
4426           abs(x - params->x)+abs(y - params->y) < abs(x - params->closest_x)+abs(y - params->closest_y)))
4427     {
4428       params->closest_track_name = name;
4429       params->closest_tp = tp;
4430       params->closest_tpl = tpl;
4431       params->closest_x = x;
4432       params->closest_y = y;
4433     }
4434     tpl = tpl->next;
4435   }
4436 }
4437
4438 static VikTrackpoint *closest_tp_in_five_pixel_interval ( VikTrwLayer *vtl, VikViewport *vvp, gint x, gint y )
4439 {
4440   TPSearchParams params;
4441   params.x = x;
4442   params.y = y;
4443   params.vvp = vvp;
4444   params.closest_track_name = NULL;
4445   params.closest_tp = NULL;
4446   g_hash_table_foreach ( vtl->tracks, (GHFunc) track_search_closest_tp, &params);
4447   return params.closest_tp;
4448 }
4449
4450 static VikWaypoint *closest_wp_in_five_pixel_interval ( VikTrwLayer *vtl, VikViewport *vvp, gint x, gint y )
4451 {
4452   WPSearchParams params;
4453   params.x = x;
4454   params.y = y;
4455   params.vvp = vvp;
4456   params.closest_wp = NULL;
4457   params.closest_wp_name = NULL;
4458   g_hash_table_foreach ( vtl->waypoints, (GHFunc) waypoint_search_closest_tp, &params);
4459   return params.closest_wp;
4460 }
4461
4462
4463 // Some forward declarations
4464 static void marker_begin_move ( tool_ed_t *t, gint x, gint y );
4465 static void marker_moveto ( tool_ed_t *t, gint x, gint y );
4466 static void marker_end_move ( tool_ed_t *t );
4467 //
4468
4469 static gboolean trw_layer_select_move ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp, tool_ed_t* t )
4470 {
4471   if ( t->holding ) {
4472     VikCoord new_coord;
4473     vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
4474
4475     // Here always allow snapping back to the original location
4476     //  this is useful when one decides not to move the thing afterall
4477     // If one wants to move the item only a little bit then don't hold down the 'snap' key!
4478  
4479     // snap to TP
4480     if ( event->state & GDK_CONTROL_MASK )
4481     {
4482       VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
4483       if ( tp )
4484         new_coord = tp->coord;
4485     }
4486
4487     // snap to WP
4488     if ( event->state & GDK_SHIFT_MASK )
4489     {
4490       VikWaypoint *wp = closest_wp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
4491       if ( wp )
4492         new_coord = wp->coord;
4493     }
4494     
4495     gint x, y;
4496     vik_viewport_coord_to_screen ( vvp, &new_coord, &x, &y );
4497
4498     marker_moveto ( t, x, y );
4499
4500     return TRUE;
4501   }
4502   return FALSE;
4503 }
4504
4505 static gboolean trw_layer_select_release ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp, tool_ed_t* t )
4506 {
4507   if ( t->holding && event->button == 1 )
4508   {
4509     VikCoord new_coord;
4510     vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
4511
4512     // snap to TP
4513     if ( event->state & GDK_CONTROL_MASK )
4514     {
4515       VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
4516       if ( tp )
4517         new_coord = tp->coord;
4518     }
4519
4520     // snap to WP
4521     if ( event->state & GDK_SHIFT_MASK )
4522     {
4523       VikWaypoint *wp = closest_wp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
4524       if ( wp )
4525         new_coord = wp->coord;
4526     }
4527
4528     marker_end_move ( t );
4529
4530     // Determine if working on a waypoint or a trackpoint
4531     if ( t->is_waypoint )
4532       vtl->current_wp->coord = new_coord;
4533     else {
4534       if ( vtl->current_tpl ) {
4535         VIK_TRACKPOINT(vtl->current_tpl->data)->coord = new_coord;
4536       
4537         if ( vtl->tpwin )
4538           vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, vtl->current_tp_track_name );
4539
4540         // Don't really know what this is for but seems like it might be handy...
4541         /* can't join with itself! */
4542         trw_layer_cancel_last_tp ( vtl );
4543       }
4544     }
4545
4546     // Reset
4547     vtl->current_wp      = NULL;
4548     vtl->current_wp_name = NULL;
4549     trw_layer_cancel_current_tp ( vtl, FALSE );
4550
4551     vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
4552     return TRUE;
4553   }
4554   return FALSE;
4555 }
4556
4557 /*
4558   Returns true if a waypoint or track is found near the requested event position for this particular layer
4559   The item found is automatically selected
4560   This is a tool like feature but routed via the layer interface, since it's instigated by a 'global' layer tool in vikwindow.c
4561  */
4562 static gboolean trw_layer_select_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp, tool_ed_t* tet )
4563 {
4564   if ( event->button != 1 )
4565     return FALSE;
4566
4567   if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
4568     return FALSE;
4569
4570   if ( !vtl->tracks_visible && !vtl->waypoints_visible )
4571     return FALSE;
4572
4573   // Go for waypoints first as these often will be near a track, but it's likely the wp is wanted rather then the track
4574
4575   if (vtl->waypoints_visible) {
4576     WPSearchParams wp_params;
4577     wp_params.vvp = vvp;
4578     wp_params.x = event->x;
4579     wp_params.y = event->y;
4580     wp_params.closest_wp_name = NULL;
4581     wp_params.closest_wp = NULL;
4582
4583     g_hash_table_foreach ( vtl->waypoints, (GHFunc) waypoint_search_closest_tp, &wp_params);
4584
4585     if ( wp_params.closest_wp )  {
4586
4587       // Select
4588       vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, g_hash_table_lookup ( vtl->waypoints_iters, wp_params.closest_wp_name ), TRUE );
4589
4590       // Too easy to move it so must be holding shift to start immediately moving it
4591       //   or otherwise be previously selected but not have an image (otherwise clicking within image bounds (again) moves it)
4592       if ( event->state & GDK_SHIFT_MASK ||
4593            ( vtl->current_wp == wp_params.closest_wp && !vtl->current_wp->image ) ) {
4594         // Put into 'move buffer'
4595         // NB vvp & vw already set in tet
4596         tet->vtl = (gpointer)vtl;
4597         tet->is_waypoint = TRUE;
4598       
4599         marker_begin_move (tet, event->x, event->y);
4600       }
4601
4602       vtl->current_wp =      wp_params.closest_wp;
4603       vtl->current_wp_name = wp_params.closest_wp_name;
4604
4605       vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
4606
4607       return TRUE;
4608     }
4609   }
4610
4611   if (vtl->tracks_visible) {
4612     TPSearchParams tp_params;
4613     tp_params.vvp = vvp;
4614     tp_params.x = event->x;
4615     tp_params.y = event->y;
4616     tp_params.closest_track_name = NULL;
4617     tp_params.closest_tp = NULL;
4618
4619     g_hash_table_foreach ( vtl->tracks, (GHFunc) track_search_closest_tp, &tp_params);
4620
4621     if ( tp_params.closest_tp )  {
4622
4623       // Always select + highlight the track
4624       vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, g_hash_table_lookup ( vtl->tracks_iters, tp_params.closest_track_name ), TRUE );
4625
4626       tet->is_waypoint = FALSE;
4627
4628       // Select the Trackpoint
4629       // Can move it immediately when control held or it's the previously selected tp
4630       if ( event->state & GDK_CONTROL_MASK ||
4631            vtl->current_tpl == tp_params.closest_tpl ) {
4632         // Put into 'move buffer'
4633         // NB vvp & vw already set in tet
4634         tet->vtl = (gpointer)vtl;
4635         marker_begin_move (tet, event->x, event->y);
4636       }
4637
4638       vtl->current_tpl = tp_params.closest_tpl;
4639       vtl->current_tp_track_name = tp_params.closest_track_name;
4640
4641       set_statusbar_msg_info_trkpt ( vtl, tp_params.closest_tp );
4642
4643       if ( vtl->tpwin )
4644         vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, vtl->current_tp_track_name );
4645
4646       vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
4647       return TRUE;
4648     }
4649   }
4650
4651   /* these aren't the droids you're looking for */
4652   vtl->current_wp      = NULL;
4653   vtl->current_wp_name = NULL;
4654   trw_layer_cancel_current_tp ( vtl, FALSE );
4655
4656   // Blank info
4657   vik_statusbar_set_message ( vik_window_get_statusbar (VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl))), VIK_STATUSBAR_INFO, "" );
4658
4659   return FALSE;
4660 }
4661
4662 static gboolean trw_layer_show_selected_viewport_menu ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
4663 {
4664   if ( event->button != 3 )
4665     return FALSE;
4666
4667   if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
4668     return FALSE;
4669
4670   if ( !vtl->tracks_visible && !vtl->waypoints_visible )
4671     return FALSE;
4672
4673   /* Post menu for the currently selected item */
4674
4675   /* See if a track is selected */
4676   VikTrack *track = (VikTrack*)vik_window_get_selected_track ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(vtl) );
4677   if ( track && track->visible ) {
4678
4679     if ( vik_window_get_selected_name ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(vtl) ) ) {
4680
4681       if ( vtl->track_right_click_menu )
4682         gtk_object_sink ( GTK_OBJECT(vtl->track_right_click_menu) );
4683
4684       vtl->track_right_click_menu = GTK_MENU ( gtk_menu_new () );
4685       
4686       trw_layer_sublayer_add_menu_items ( vtl,
4687                                           vtl->track_right_click_menu,
4688                                           NULL,
4689                                           VIK_TRW_LAYER_SUBLAYER_TRACK,
4690                                           vik_window_get_selected_name ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(vtl) ),
4691                                           g_hash_table_lookup ( vtl->tracks_iters, vik_window_get_selected_name ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(vtl) ) ),
4692                                           vvp);
4693
4694       gtk_menu_popup ( vtl->track_right_click_menu, NULL, NULL, NULL, NULL, event->button, gtk_get_current_event_time() );
4695         
4696       return TRUE;
4697     }
4698   }
4699
4700   /* See if a waypoint is selected */
4701   VikWaypoint *waypoint = (VikWaypoint*)vik_window_get_selected_waypoint ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(vtl) );
4702   if ( waypoint && waypoint->visible ) {
4703     if ( vik_window_get_selected_name ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(vtl) ) ) {
4704
4705       if ( vtl->wp_right_click_menu )
4706         gtk_object_sink ( GTK_OBJECT(vtl->wp_right_click_menu) );
4707
4708       vtl->wp_right_click_menu = GTK_MENU ( gtk_menu_new () );
4709       trw_layer_sublayer_add_menu_items ( vtl,
4710                                           vtl->wp_right_click_menu,
4711                                           NULL,
4712                                           VIK_TRW_LAYER_SUBLAYER_WAYPOINT,
4713                                           vik_window_get_selected_name ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(vtl) ),
4714                                           g_hash_table_lookup ( vtl->waypoints_iters, vik_window_get_selected_name ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(vtl) ) ),
4715                                           vvp);
4716       gtk_menu_popup ( vtl->wp_right_click_menu, NULL, NULL, NULL, NULL, event->button, gtk_get_current_event_time() );
4717
4718       return TRUE;
4719     }
4720   }
4721
4722   return FALSE;
4723 }
4724
4725 /* background drawing hook, to be passed the viewport */
4726 static gboolean tool_sync_done = TRUE;
4727
4728 static gboolean tool_sync(gpointer data)
4729 {
4730   VikViewport *vvp = data;
4731   gdk_threads_enter();
4732   vik_viewport_sync(vvp);
4733   tool_sync_done = TRUE;
4734   gdk_threads_leave();
4735   return FALSE;
4736 }
4737
4738 static void marker_begin_move ( tool_ed_t *t, gint x, gint y )
4739 {
4740   t->holding = TRUE;
4741   t->gc = vik_viewport_new_gc (t->vvp, "black", 2);
4742   gdk_gc_set_function ( t->gc, GDK_INVERT );
4743   vik_viewport_draw_rectangle ( t->vvp, t->gc, FALSE, x-3, y-3, 6, 6 );
4744   vik_viewport_sync(t->vvp);
4745   t->oldx = x;
4746   t->oldy = y;
4747 }
4748
4749 static void marker_moveto ( tool_ed_t *t, gint x, gint y )
4750 {
4751   VikViewport *vvp =  t->vvp;
4752   vik_viewport_draw_rectangle ( vvp, t->gc, FALSE, t->oldx-3, t->oldy-3, 6, 6 );
4753   vik_viewport_draw_rectangle ( vvp, t->gc, FALSE, x-3, y-3, 6, 6 );
4754   t->oldx = x;
4755   t->oldy = y;
4756
4757   if (tool_sync_done) {
4758     g_idle_add_full (G_PRIORITY_HIGH_IDLE + 10, tool_sync, vvp, NULL);
4759     tool_sync_done = FALSE;
4760   }
4761 }
4762
4763 static void marker_end_move ( tool_ed_t *t )
4764 {
4765   vik_viewport_draw_rectangle ( t->vvp, t->gc, FALSE, t->oldx-3, t->oldy-3, 6, 6 );
4766   g_object_unref ( t->gc );
4767   t->holding = FALSE;
4768 }
4769
4770 /*** Edit waypoint ****/
4771
4772 static gpointer tool_edit_waypoint_create ( VikWindow *vw, VikViewport *vvp)
4773 {
4774   tool_ed_t *t = g_new(tool_ed_t, 1);
4775   t->vvp = vvp;
4776   t->holding = FALSE;
4777   return t;
4778 }
4779
4780 static gboolean tool_edit_waypoint_click ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data )
4781 {
4782   WPSearchParams params;
4783   tool_ed_t *t = data;
4784   VikViewport *vvp = t->vvp;
4785
4786   if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
4787     return FALSE;
4788
4789   if ( t->holding ) {
4790     return TRUE;
4791   }
4792
4793   if ( !vtl->vl.visible || !vtl->waypoints_visible )
4794     return FALSE;
4795
4796   if ( vtl->current_wp && vtl->current_wp->visible )
4797   {
4798     /* first check if current WP is within area (other may be 'closer', but we want to move the current) */
4799     gint x, y;
4800     vik_viewport_coord_to_screen ( vvp, &(vtl->current_wp->coord), &x, &y );
4801
4802     if ( abs(x - event->x) <= WAYPOINT_SIZE_APPROX &&
4803          abs(y - event->y) <= WAYPOINT_SIZE_APPROX )
4804     {
4805       if ( event->button == 3 )
4806         vtl->waypoint_rightclick = TRUE; /* remember that we're clicking; other layers will ignore release signal */
4807       else {
4808         marker_begin_move(t, event->x, event->y);
4809       }
4810       return TRUE;
4811     }
4812   }
4813
4814   params.vvp = vvp;
4815   params.x = event->x;
4816   params.y = event->y;
4817   params.closest_wp_name = NULL;
4818   /* TODO: should get track listitem so we can break it up, make a new track, mess it up, all that. */
4819   params.closest_wp = NULL;
4820   g_hash_table_foreach ( vtl->waypoints, (GHFunc) waypoint_search_closest_tp, &params);
4821   if ( vtl->current_wp == params.closest_wp && vtl->current_wp != NULL )
4822   {
4823     /* how do we get here? I'm putting in the abort until we can figure it out. -alex */
4824     marker_begin_move(t, event->x, event->y);
4825     g_critical("shouldn't be here");
4826     exit(1);
4827   }
4828   else if ( params.closest_wp )
4829   {
4830     if ( event->button == 3 )
4831       vtl->waypoint_rightclick = TRUE; /* remember that we're clicking; other layers will ignore release signal */
4832     else
4833       vtl->waypoint_rightclick = FALSE;
4834
4835     vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, g_hash_table_lookup ( vtl->waypoints_iters, params.closest_wp_name ), TRUE );
4836
4837     vtl->current_wp = params.closest_wp;
4838     vtl->current_wp_name = params.closest_wp_name;
4839
4840     /* could make it so don't update if old WP is off screen and new is null but oh well */
4841     vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
4842     return TRUE;
4843   }
4844
4845   vtl->current_wp = NULL;
4846   vtl->current_wp_name = NULL;
4847   vtl->waypoint_rightclick = FALSE;
4848   vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
4849   return FALSE;
4850 }
4851
4852 static gboolean tool_edit_waypoint_move ( VikTrwLayer *vtl, GdkEventMotion *event, gpointer data )
4853 {
4854   tool_ed_t *t = data;
4855   VikViewport *vvp = t->vvp;
4856
4857   if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
4858     return FALSE;
4859
4860   if ( t->holding ) {
4861     VikCoord new_coord;
4862     vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
4863
4864     /* snap to TP */
4865     if ( event->state & GDK_CONTROL_MASK )
4866     {
4867       VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
4868       if ( tp )
4869         new_coord = tp->coord;
4870     }
4871
4872     /* snap to WP */
4873     if ( event->state & GDK_SHIFT_MASK )
4874     {
4875       VikWaypoint *wp = closest_wp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
4876       if ( wp && wp != vtl->current_wp )
4877         new_coord = wp->coord;
4878     }
4879     
4880     { 
4881       gint x, y;
4882       vik_viewport_coord_to_screen ( vvp, &new_coord, &x, &y );
4883
4884       marker_moveto ( t, x, y );
4885     } 
4886     return TRUE;
4887   }
4888   return FALSE;
4889 }
4890
4891 static gboolean tool_edit_waypoint_release ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data )
4892 {
4893   tool_ed_t *t = data;
4894   VikViewport *vvp = t->vvp;
4895
4896   if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
4897     return FALSE;
4898   
4899   if ( t->holding && event->button == 1 )
4900   {
4901     VikCoord new_coord;
4902     vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
4903
4904     /* snap to TP */
4905     if ( event->state & GDK_CONTROL_MASK )
4906     {
4907       VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
4908       if ( tp )
4909         new_coord = tp->coord;
4910     }
4911
4912     /* snap to WP */
4913     if ( event->state & GDK_SHIFT_MASK )
4914     {
4915       VikWaypoint *wp = closest_wp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
4916       if ( wp && wp != vtl->current_wp )
4917         new_coord = wp->coord;
4918     }
4919
4920     marker_end_move ( t );
4921
4922     vtl->current_wp->coord = new_coord;
4923     vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
4924     return TRUE;
4925   }
4926   /* PUT IN RIGHT PLACE!!! */
4927   if ( event->button == 3 && vtl->waypoint_rightclick )
4928   {
4929     if ( vtl->wp_right_click_menu )
4930       g_object_ref_sink ( G_OBJECT(vtl->wp_right_click_menu) );
4931     if ( vtl->current_wp ) {
4932       vtl->wp_right_click_menu = GTK_MENU ( gtk_menu_new () );
4933       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 );
4934       gtk_menu_popup ( vtl->wp_right_click_menu, NULL, NULL, NULL, NULL, event->button, gtk_get_current_event_time() );
4935     }
4936     vtl->waypoint_rightclick = FALSE;
4937   }
4938   return FALSE;
4939 }
4940
4941 /**** Begin track ***/
4942 static gpointer tool_begin_track_create ( VikWindow *vw, VikViewport *vvp)
4943 {
4944   return vvp;
4945 }
4946
4947 static gboolean tool_begin_track_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
4948 {
4949   vtl->current_track = NULL;
4950   return tool_new_track_click ( vtl, event, vvp );
4951 }
4952
4953 /*** New track ****/
4954
4955 static gpointer tool_new_track_create ( VikWindow *vw, VikViewport *vvp)
4956 {
4957   return vvp;
4958 }
4959
4960 typedef struct {
4961   VikTrwLayer *vtl;
4962   VikViewport *vvp;
4963   gint x1,y1,x2,y2,x3,y3;
4964   const gchar* str;
4965 } new_track_move_passalong_t;
4966
4967 /* sync and undraw, but only when we have time */
4968 static gboolean ct_sync ( gpointer passalong )
4969 {
4970   new_track_move_passalong_t *p = (new_track_move_passalong_t *) passalong;
4971
4972   vik_viewport_sync ( p->vvp );
4973   gdk_gc_set_function ( p->vtl->current_track_gc, GDK_INVERT );
4974   vik_viewport_draw_line ( p->vvp, p->vtl->current_track_gc, p->x1, p->y1, p->x2, p->y2 );
4975   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);
4976   gdk_gc_set_function ( p->vtl->current_track_gc, GDK_COPY );
4977
4978   g_free ( (gpointer) p->str ) ;
4979   p->vtl->ct_sync_done = TRUE;
4980   g_free ( p );
4981   return FALSE;
4982 }
4983
4984 static const gchar* distance_string (gdouble distance)
4985 {
4986   gchar str[128];
4987
4988   /* draw label with distance */
4989   vik_units_distance_t dist_units = a_vik_get_units_distance ();
4990   switch (dist_units) {
4991   case VIK_UNITS_DISTANCE_MILES:
4992     if (distance >= VIK_MILES_TO_METERS(1) && distance < VIK_MILES_TO_METERS(100)) {
4993       g_sprintf(str, "%3.2f miles", VIK_METERS_TO_MILES(distance));
4994     } else if (distance < 1609.4) {
4995       g_sprintf(str, "%d yards", (int)(distance*1.0936133));
4996     } else {
4997       g_sprintf(str, "%d miles", (int)VIK_METERS_TO_MILES(distance));
4998     }
4999     break;
5000   default:
5001     // VIK_UNITS_DISTANCE_KILOMETRES
5002     if (distance >= 1000 && distance < 100000) {
5003       g_sprintf(str, "%3.2f km", distance/1000.0);
5004     } else if (distance < 1000) {
5005       g_sprintf(str, "%d m", (int)distance);
5006     } else {
5007       g_sprintf(str, "%d km", (int)distance/1000);
5008     }
5009     break;
5010   }
5011   return g_strdup (str);
5012 }
5013
5014 /*
5015  * Actually set the message in statusbar
5016  */
5017 static void statusbar_write (const gchar *distance_string, gdouble elev_gain, gdouble elev_loss, VikTrwLayer *vtl )
5018 {
5019   // Only show elevation data when track has some elevation properties
5020   gchar str_gain_loss[64];
5021   str_gain_loss[0] = '\0';
5022   
5023   if ( (elev_gain > 0.1) || (elev_loss > 0.1) ) {
5024     if ( a_vik_get_units_height () == VIK_UNITS_HEIGHT_METRES )
5025       g_sprintf(str_gain_loss, _(" - Gain %dm:Loss %dm"), (int)elev_gain, (int)elev_loss);
5026     else
5027       g_sprintf(str_gain_loss, _(" - Gain %dft:Loss %dft"), (int)VIK_METERS_TO_FEET(elev_gain), (int)VIK_METERS_TO_FEET(elev_loss));
5028   }
5029
5030   // Write with full gain/loss information
5031   gchar *msg = g_strdup_printf ( "%s%s", distance_string, str_gain_loss);
5032   vik_statusbar_set_message ( vik_window_get_statusbar (VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl))), VIK_STATUSBAR_INFO, msg );
5033   g_free ( msg );
5034 }
5035
5036 /*
5037  * Figure out what information should be set in the statusbar and then write it
5038  */
5039 static void update_statusbar ( VikTrwLayer *vtl )
5040 {
5041   // Get elevation data
5042   gdouble elev_gain, elev_loss;
5043   vik_track_get_total_elevation_gain ( vtl->current_track, &elev_gain, &elev_loss);
5044
5045   /* Find out actual distance of current track */
5046   gdouble distance = vik_track_get_length (vtl->current_track);
5047   const gchar *str = distance_string (distance);
5048
5049   statusbar_write (str, elev_gain, elev_loss, vtl);
5050
5051   g_free ((gpointer)str);
5052 }
5053
5054
5055 static VikLayerToolFuncStatus tool_new_track_move ( VikTrwLayer *vtl, GdkEventMotion *event, VikViewport *vvp )
5056 {
5057   /* if we haven't sync'ed yet, we don't have time to do more. */
5058   if ( vtl->ct_sync_done && vtl->current_track && vtl->current_track->trackpoints ) {
5059     GList *iter = vtl->current_track->trackpoints;
5060     new_track_move_passalong_t *passalong;
5061     gint x1, y1;
5062
5063     while ( iter->next )
5064       iter = iter->next;
5065     gdk_gc_set_function ( vtl->current_track_gc, GDK_INVERT );
5066     vik_viewport_coord_to_screen ( vvp, &(VIK_TRACKPOINT(iter->data)->coord), &x1, &y1 );
5067     vik_viewport_draw_line ( vvp, vtl->current_track_gc, x1, y1, event->x, event->y );
5068
5069     /* Find out actual distance of current track */
5070     gdouble distance = vik_track_get_length (vtl->current_track);
5071
5072     // Now add distance to where the pointer is //
5073     VikCoord coord;
5074     struct LatLon ll;
5075     vik_viewport_screen_to_coord ( vvp, (gint) event->x, (gint) event->y, &coord );
5076     vik_coord_to_latlon ( &coord, &ll );
5077     distance = distance + vik_coord_diff( &coord, &(VIK_TRACKPOINT(iter->data)->coord));
5078
5079     // Get elevation data
5080     gdouble elev_gain, elev_loss;
5081     vik_track_get_total_elevation_gain ( vtl->current_track, &elev_gain, &elev_loss);
5082
5083     // Adjust elevation data (if available) for the current pointer position
5084     gdouble elev_new;
5085     elev_new = (gdouble) a_dems_get_elev_by_coord ( &coord, VIK_DEM_INTERPOL_BEST );
5086     if ( elev_new != VIK_DEM_INVALID_ELEVATION ) {
5087       if ( VIK_TRACKPOINT(iter->data)->altitude != VIK_DEFAULT_ALTITUDE ) {
5088         // Adjust elevation of last track point
5089         if ( elev_new > VIK_TRACKPOINT(iter->data)->altitude )
5090           // Going up
5091           elev_gain += elev_new - VIK_TRACKPOINT(iter->data)->altitude;
5092         else
5093           // Going down
5094           elev_loss += VIK_TRACKPOINT(iter->data)->altitude - elev_new;
5095       }
5096     }
5097       
5098     const gchar *str = distance_string (distance);
5099     gint xd,yd;
5100     /* offset from cursor a bit */
5101     xd = event->x + 10;
5102     yd = event->y - 10;
5103     /* note attempted setting text using pango layouts - but the 'undraw' technique didn't work */
5104     vik_viewport_draw_string (vvp, gdk_font_from_description (pango_font_description_from_string ("Sans 8")), vtl->current_track_gc, xd, yd, str);
5105
5106     gdk_gc_set_function ( vtl->current_track_gc, GDK_COPY );
5107
5108     passalong = g_new(new_track_move_passalong_t,1); /* freed by sync */
5109     passalong->vtl = vtl;
5110     passalong->vvp = vvp;
5111     passalong->x1 = x1;
5112     passalong->y1 = y1;
5113     passalong->x2 = event->x;
5114     passalong->y2 = event->y;
5115     passalong->x3 = xd;
5116     passalong->y3 = yd;
5117     passalong->str = str;
5118
5119     // Update statusbar with full gain/loss information
5120     statusbar_write (str, elev_gain, elev_loss, vtl);
5121
5122     /* this will sync and undraw when we have time to */
5123     g_idle_add_full (G_PRIORITY_HIGH_IDLE + 10, ct_sync, passalong, NULL);
5124     vtl->ct_sync_done = FALSE;
5125     return VIK_LAYER_TOOL_ACK_GRAB_FOCUS;
5126   }
5127   return VIK_LAYER_TOOL_ACK;
5128 }
5129
5130 static gboolean tool_new_track_key_press ( VikTrwLayer *vtl, GdkEventKey *event, VikViewport *vvp )
5131 {
5132   if ( vtl->current_track && event->keyval == GDK_Escape ) {
5133     vtl->current_track = NULL;
5134     vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
5135     return TRUE;
5136   } else if ( vtl->current_track && event->keyval == GDK_BackSpace ) {
5137     /* undo */
5138     if ( vtl->current_track->trackpoints )
5139     {
5140       GList *last = g_list_last(vtl->current_track->trackpoints);
5141       g_free ( last->data );
5142       vtl->current_track->trackpoints = g_list_remove_link ( vtl->current_track->trackpoints, last );
5143     }
5144     
5145     update_statusbar ( vtl );
5146
5147     vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
5148     return TRUE;
5149   }
5150   return FALSE;
5151 }
5152
5153 static gboolean tool_new_track_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
5154 {
5155   VikTrackpoint *tp;
5156
5157   if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
5158     return FALSE;
5159
5160   if ( event->button == 3 && vtl->current_track )
5161   {
5162     /* undo */
5163     if ( vtl->current_track->trackpoints )
5164     {
5165       GList *last = g_list_last(vtl->current_track->trackpoints);
5166       g_free ( last->data );
5167       vtl->current_track->trackpoints = g_list_remove_link ( vtl->current_track->trackpoints, last );
5168     }
5169     update_statusbar ( vtl );
5170
5171     vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
5172     return TRUE;
5173   }
5174
5175   if ( event->type == GDK_2BUTTON_PRESS )
5176   {
5177     /* subtract last (duplicate from double click) tp then end */
5178     if ( vtl->current_track && vtl->current_track->trackpoints && vtl->ct_x1 == vtl->ct_x2 && vtl->ct_y1 == vtl->ct_y2 )
5179     {
5180       GList *last = g_list_last(vtl->current_track->trackpoints);
5181       g_free ( last->data );
5182       vtl->current_track->trackpoints = g_list_remove_link ( vtl->current_track->trackpoints, last );
5183       /* undo last, then end */
5184       vtl->current_track = NULL;
5185     }
5186     vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
5187     return TRUE;
5188   }
5189
5190   if ( ! vtl->current_track )
5191   {
5192     gchar *name = get_new_unique_sublayer_name(vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, _("Track"));
5193     if ( ( name = a_dialog_new_track ( VIK_GTK_WINDOW_FROM_LAYER(vtl), vtl->tracks, name ) ) )
5194     {
5195       vtl->current_track = vik_track_new();
5196       vtl->current_track->visible = TRUE;
5197       vik_trw_layer_add_track ( vtl, name, vtl->current_track );
5198
5199       /* incase it was created by begin track */
5200       vik_window_enable_layer_tool ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)), VIK_LAYER_TRW, TOOL_CREATE_TRACK );
5201     }
5202     else
5203       return TRUE;
5204   }
5205   tp = vik_trackpoint_new();
5206   vik_viewport_screen_to_coord ( vvp, event->x, event->y, &(tp->coord) );
5207
5208   /* snap to other TP */
5209   if ( event->state & GDK_CONTROL_MASK )
5210   {
5211     VikTrackpoint *other_tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
5212     if ( other_tp )
5213       tp->coord = other_tp->coord;
5214   }
5215
5216   tp->newsegment = FALSE;
5217   tp->has_timestamp = FALSE;
5218   tp->timestamp = 0;
5219   vtl->current_track->trackpoints = g_list_append ( vtl->current_track->trackpoints, tp );
5220   /* Auto attempt to get elevation from DEM data (if it's available) */
5221   vik_track_apply_dem_data_last_trackpoint ( vtl->current_track );
5222
5223   vtl->ct_x1 = vtl->ct_x2;
5224   vtl->ct_y1 = vtl->ct_y2;
5225   vtl->ct_x2 = event->x;
5226   vtl->ct_y2 = event->y;
5227
5228   vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
5229   return TRUE;
5230 }
5231
5232 /*** New waypoint ****/
5233
5234 static gpointer tool_new_waypoint_create ( VikWindow *vw, VikViewport *vvp)
5235 {
5236   return vvp;
5237 }
5238
5239 static gboolean tool_new_waypoint_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
5240 {
5241   VikCoord coord;
5242   if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
5243     return FALSE;
5244   vik_viewport_screen_to_coord ( vvp, event->x, event->y, &coord );
5245   if (vik_trw_layer_new_waypoint ( vtl, VIK_GTK_WINDOW_FROM_LAYER(vtl), &coord ) && VIK_LAYER(vtl)->visible)
5246     vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
5247   return TRUE;
5248 }
5249
5250
5251 /*** Edit trackpoint ****/
5252
5253 static gpointer tool_edit_trackpoint_create ( VikWindow *vw, VikViewport *vvp)
5254 {
5255   tool_ed_t *t = g_new(tool_ed_t, 1);
5256   t->vvp = vvp;
5257   t->holding = FALSE;
5258   return t;
5259 }
5260
5261 static gboolean tool_edit_trackpoint_click ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data )
5262 {
5263   tool_ed_t *t = data;
5264   VikViewport *vvp = t->vvp;
5265   TPSearchParams params;
5266   /* OUTDATED DOCUMENTATION:
5267    find 5 pixel range on each side. then put these UTM, and a pointer
5268    to the winning track name (and maybe the winning track itself), and a
5269    pointer to the winning trackpoint, inside an array or struct. pass 
5270    this along, do a foreach on the tracks which will do a foreach on the 
5271    trackpoints. */
5272   params.vvp = vvp;
5273   params.x = event->x;
5274   params.y = event->y;
5275   params.closest_track_name = NULL;
5276   /* TODO: should get track listitem so we can break it up, make a new track, mess it up, all that. */
5277   params.closest_tp = NULL;
5278
5279   if ( event->button != 1 ) 
5280     return FALSE;
5281
5282   if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
5283     return FALSE;
5284
5285   if ( !vtl->vl.visible || !vtl->tracks_visible )
5286     return FALSE;
5287
5288   if ( vtl->current_tpl )
5289   {
5290     /* 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.) */
5291     VikTrackpoint *tp = VIK_TRACKPOINT(vtl->current_tpl->data);
5292     VikTrack *current_tr = VIK_TRACK(g_hash_table_lookup(vtl->tracks, vtl->current_tp_track_name));
5293     gint x, y;
5294     g_assert ( current_tr );
5295
5296     vik_viewport_coord_to_screen ( vvp, &(tp->coord), &x, &y );
5297
5298     if ( current_tr->visible && 
5299          abs(x - event->x) < TRACKPOINT_SIZE_APPROX &&
5300          abs(y - event->y) < TRACKPOINT_SIZE_APPROX ) {
5301       marker_begin_move ( t, event->x, event->y );
5302       return TRUE;
5303     }
5304
5305     vtl->last_tpl = vtl->current_tpl;
5306     vtl->last_tp_track_name = vtl->current_tp_track_name;
5307   }
5308
5309   g_hash_table_foreach ( vtl->tracks, (GHFunc) track_search_closest_tp, &params);
5310
5311   if ( params.closest_tp )
5312   {
5313     vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, g_hash_table_lookup ( vtl->tracks_iters, params.closest_track_name ), TRUE );
5314     vtl->current_tpl = params.closest_tpl;
5315     vtl->current_tp_track_name = params.closest_track_name;
5316     trw_layer_tpwin_init ( vtl );
5317     set_statusbar_msg_info_trkpt ( vtl, params.closest_tp );
5318     vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
5319     return TRUE;
5320   }
5321
5322   /* these aren't the droids you're looking for */
5323   return FALSE;
5324 }
5325
5326 static gboolean tool_edit_trackpoint_move ( VikTrwLayer *vtl, GdkEventMotion *event, gpointer data )
5327 {
5328   tool_ed_t *t = data;
5329   VikViewport *vvp = t->vvp;
5330
5331   if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
5332     return FALSE;
5333
5334   if ( t->holding )
5335   {
5336     VikCoord new_coord;
5337     vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
5338
5339     /* snap to TP */
5340     if ( event->state & GDK_CONTROL_MASK )
5341     {
5342       VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
5343       if ( tp && tp != vtl->current_tpl->data )
5344         new_coord = tp->coord;
5345     }
5346     //    VIK_TRACKPOINT(vtl->current_tpl->data)->coord = new_coord;
5347     { 
5348       gint x, y;
5349       vik_viewport_coord_to_screen ( vvp, &new_coord, &x, &y );
5350       marker_moveto ( t, x, y );
5351     } 
5352
5353     return TRUE;
5354   }
5355   return FALSE;
5356 }
5357
5358 static gboolean tool_edit_trackpoint_release ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data )
5359 {
5360   tool_ed_t *t = data;
5361   VikViewport *vvp = t->vvp;
5362
5363   if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
5364     return FALSE;
5365   if ( event->button != 1) 
5366     return FALSE;
5367
5368   if ( t->holding ) {
5369     VikCoord new_coord;
5370     vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
5371
5372     /* snap to TP */
5373     if ( event->state & GDK_CONTROL_MASK )
5374     {
5375       VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
5376       if ( tp && tp != vtl->current_tpl->data )
5377         new_coord = tp->coord;
5378     }
5379
5380     VIK_TRACKPOINT(vtl->current_tpl->data)->coord = new_coord;
5381
5382     marker_end_move ( t );
5383
5384     /* diff dist is diff from orig */
5385     if ( vtl->tpwin )
5386       vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, vtl->current_tp_track_name );
5387     /* can't join with itself! */
5388     trw_layer_cancel_last_tp ( vtl );
5389
5390     vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
5391     return TRUE;
5392   }
5393   return FALSE;
5394 }
5395
5396
5397 /*** Route Finder ***/
5398 static gpointer tool_route_finder_create ( VikWindow *vw, VikViewport *vvp)
5399 {
5400   return vvp;
5401 }
5402
5403 static gboolean tool_route_finder_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
5404 {
5405   VikCoord tmp;
5406   if ( !vtl ) return FALSE;
5407   vik_viewport_screen_to_coord ( vvp, event->x, event->y, &tmp );
5408   if ( event->button == 3 && vtl->route_finder_current_track ) {
5409     VikCoord *new_end;
5410     new_end = vik_track_cut_back_to_double_point ( vtl->route_finder_current_track );
5411     if ( new_end ) {
5412       vtl->route_finder_coord = *new_end;
5413       g_free ( new_end );
5414       vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
5415       /* remove last ' to:...' */
5416       if ( vtl->route_finder_current_track->comment ) {
5417         gchar *last_to = strrchr ( vtl->route_finder_current_track->comment, 't' );
5418         if ( last_to && (last_to - vtl->route_finder_current_track->comment > 1) ) {
5419           gchar *new_comment = g_strndup ( vtl->route_finder_current_track->comment,
5420                                            last_to - vtl->route_finder_current_track->comment - 1);
5421           vik_track_set_comment_no_copy ( vtl->route_finder_current_track, new_comment );
5422         }
5423       }
5424     }
5425   }
5426   else if ( vtl->route_finder_started || (event->state & GDK_CONTROL_MASK && vtl->route_finder_current_track) ) {
5427     struct LatLon start, end;
5428     gchar startlat[G_ASCII_DTOSTR_BUF_SIZE], startlon[G_ASCII_DTOSTR_BUF_SIZE];
5429     gchar endlat[G_ASCII_DTOSTR_BUF_SIZE], endlon[G_ASCII_DTOSTR_BUF_SIZE];
5430     gchar *url;
5431
5432     vik_coord_to_latlon ( &(vtl->route_finder_coord), &start );
5433     vik_coord_to_latlon ( &(tmp), &end );
5434     vtl->route_finder_coord = tmp; /* for continuations */
5435
5436     /* these are checked when adding a track from a file (vik_trw_layer_filein_add_track) */
5437     if ( event->state & GDK_CONTROL_MASK && vtl->route_finder_current_track ) {
5438       vtl->route_finder_append = TRUE;  // merge tracks. keep started true.
5439     } else {
5440       vtl->route_finder_check_added_track = TRUE;
5441       vtl->route_finder_started = FALSE;
5442     }
5443
5444     url = g_strdup_printf(GOOGLE_DIRECTIONS_STRING,
5445                           g_ascii_dtostr (startlat, G_ASCII_DTOSTR_BUF_SIZE, (gdouble) start.lat),
5446                           g_ascii_dtostr (startlon, G_ASCII_DTOSTR_BUF_SIZE, (gdouble) start.lon),
5447                           g_ascii_dtostr (endlat, G_ASCII_DTOSTR_BUF_SIZE, (gdouble) end.lat),
5448                           g_ascii_dtostr (endlon, G_ASCII_DTOSTR_BUF_SIZE, (gdouble) end.lon));
5449     a_babel_convert_from_url ( vtl, url, "kml", NULL, NULL );
5450     g_free ( url );
5451
5452     /* see if anything was done -- a track was added or appended to */
5453     if ( vtl->route_finder_check_added_track && vtl->route_finder_added_track_name ) {
5454       VikTrack *tr;
5455
5456       tr = g_hash_table_lookup ( vtl->tracks, vtl->route_finder_added_track_name );
5457
5458       if ( tr )
5459         vik_track_set_comment_no_copy ( tr, g_strdup_printf("from: %f,%f to: %f,%f", start.lat, start.lon, end.lat, end.lon ) );
5460  
5461       vtl->route_finder_current_track = tr;
5462
5463       g_free ( vtl->route_finder_added_track_name );
5464       vtl->route_finder_added_track_name = NULL;
5465     } else if ( vtl->route_finder_append == FALSE && vtl->route_finder_current_track ) {
5466       /* route_finder_append was originally TRUE but set to FALSE by filein_add_track */
5467       gchar *new_comment = g_strdup_printf("%s to: %f,%f", vtl->route_finder_current_track->comment, end.lat, end.lon );
5468       vik_track_set_comment_no_copy ( vtl->route_finder_current_track, new_comment );
5469     }
5470     vtl->route_finder_check_added_track = FALSE;
5471     vtl->route_finder_append = FALSE;
5472
5473     vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
5474   } else {
5475     vtl->route_finder_started = TRUE;
5476     vtl->route_finder_coord = tmp;
5477     vtl->route_finder_current_track = NULL;
5478   }
5479   return TRUE;
5480 }
5481
5482 /*** Show picture ****/
5483
5484 static gpointer tool_show_picture_create ( VikWindow *vw, VikViewport *vvp)
5485 {
5486   return vvp;
5487 }
5488
5489 /* Params are: vvp, event, last match found or NULL */
5490 static void tool_show_picture_wp ( char *name, VikWaypoint *wp, gpointer params[2] )
5491 {
5492   if ( wp->image && wp->visible )
5493   {
5494     gint x, y, slackx, slacky;
5495     GdkEventButton *event = (GdkEventButton *) params[1];
5496
5497     vik_viewport_coord_to_screen ( VIK_VIEWPORT(params[0]), &(wp->coord), &x, &y );
5498     slackx = wp->image_width / 2;
5499     slacky = wp->image_height / 2;
5500     if (    x <= event->x + slackx && x >= event->x - slackx
5501          && y <= event->y + slacky && y >= event->y - slacky )
5502     {
5503       params[2] = wp->image; /* we've found a match. however continue searching
5504                               * since we want to find the last match -- that
5505                               * is, the match that was drawn last. */
5506     }
5507   }
5508 }
5509
5510 static void trw_layer_show_picture ( gpointer pass_along[6] )
5511 {
5512   /* thanks to the Gaim people for showing me ShellExecute and g_spawn_command_line_async */
5513 #ifdef WINDOWS
5514   ShellExecute(NULL, NULL, (char *) pass_along[2], NULL, ".\\", 0);
5515 #else /* WINDOWS */
5516   GError *err = NULL;
5517   gchar *quoted_file = g_shell_quote ( (gchar *) pass_along[5] );
5518   gchar *cmd = g_strdup_printf ( "eog %s", quoted_file );
5519   g_free ( quoted_file );
5520   if ( ! g_spawn_command_line_async ( cmd, &err ) )
5521     {
5522       a_dialog_error_msg ( VIK_GTK_WINDOW_FROM_LAYER( pass_along[0]), _("Could not launch eog to open file.") );
5523       g_error_free ( err );
5524     }
5525   g_free ( cmd );
5526 #endif /* WINDOWS */
5527 }
5528
5529 static gboolean tool_show_picture_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
5530 {
5531   gpointer params[3] = { vvp, event, NULL };
5532   if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
5533     return FALSE;
5534   g_hash_table_foreach ( vtl->waypoints, (GHFunc) tool_show_picture_wp, params );
5535   if ( params[2] )
5536   {
5537     static gpointer pass_along[6];
5538     pass_along[0] = vtl;
5539     pass_along[5] = params[2];
5540     trw_layer_show_picture ( pass_along );
5541     return TRUE; /* found a match */
5542   }
5543   else
5544     return FALSE; /* go through other layers, searching for a match */
5545 }
5546
5547 /***************************************************************************
5548  ** End tool code 
5549  ***************************************************************************/
5550
5551
5552
5553
5554
5555 static void image_wp_make_list ( char *name, VikWaypoint *wp, GSList **pics )
5556 {
5557   if ( wp->image && ( ! a_thumbnails_exists ( wp->image ) ) )
5558     *pics = g_slist_append ( *pics, (gpointer) g_strdup ( wp->image ) );
5559 }
5560
5561 /* Structure for thumbnail creating data used in the background thread */
5562 typedef struct {
5563   VikTrwLayer *vtl; // Layer needed for redrawing
5564   GSList *pics;     // Image list
5565 } thumbnail_create_thread_data;
5566
5567 static int create_thumbnails_thread ( thumbnail_create_thread_data *tctd, gpointer threaddata )
5568 {
5569   guint total = g_slist_length(tctd->pics), done = 0;
5570   while ( tctd->pics )
5571   {
5572     a_thumbnails_create ( (gchar *) tctd->pics->data );
5573     int result = a_background_thread_progress ( threaddata, ((gdouble) ++done) / total );
5574     if ( result != 0 )
5575       return -1; /* Abort thread */
5576
5577     tctd->pics = tctd->pics->next;
5578   }
5579
5580   // Redraw to show the thumbnails as they are now created
5581   if ( IS_VIK_LAYER(tctd->vtl) )
5582     vik_layer_emit_update ( VIK_LAYER(tctd->vtl), TRUE ); // Yes update from background thread
5583
5584   return 0;
5585 }
5586
5587 static void thumbnail_create_thread_free ( thumbnail_create_thread_data *tctd )
5588 {
5589   while ( tctd->pics )
5590   {
5591     g_free ( tctd->pics->data );
5592     tctd->pics = g_slist_delete_link ( tctd->pics, tctd->pics );
5593   }
5594   g_free ( tctd );
5595 }
5596
5597 static void trw_layer_verify_thumbnails ( VikTrwLayer *vtl, GtkWidget *vp )
5598 {
5599   if ( ! vtl->has_verified_thumbnails )
5600   {
5601     GSList *pics = NULL;
5602     g_hash_table_foreach ( vtl->waypoints, (GHFunc) image_wp_make_list, &pics );
5603     if ( pics )
5604     {
5605       gint len = g_slist_length ( pics );
5606       gchar *tmp = g_strdup_printf ( _("Creating %d Image Thumbnails..."), len );
5607       thumbnail_create_thread_data *tctd = g_malloc ( sizeof(thumbnail_create_thread_data) );
5608       tctd->vtl = vtl;
5609       tctd->pics = pics;
5610       a_background_thread ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
5611                             tmp,
5612                             (vik_thr_func) create_thumbnails_thread,
5613                             tctd,
5614                             (vik_thr_free_func) thumbnail_create_thread_free,
5615                             NULL,
5616                             len );
5617       g_free ( tmp );
5618     }
5619   }
5620 }
5621
5622 VikCoordMode vik_trw_layer_get_coord_mode ( VikTrwLayer *vtl )
5623 {
5624   return vtl->coord_mode;
5625 }
5626
5627
5628
5629 static void waypoint_convert ( const gchar *name, VikWaypoint *wp, VikCoordMode *dest_mode )
5630 {
5631   vik_coord_convert ( &(wp->coord), *dest_mode );
5632 }
5633
5634 static void track_convert ( const gchar *name, VikTrack *tr, VikCoordMode *dest_mode )
5635 {
5636   vik_track_convert ( tr, *dest_mode );
5637 }
5638
5639 static void trw_layer_change_coord_mode ( VikTrwLayer *vtl, VikCoordMode dest_mode )
5640 {
5641   if ( vtl->coord_mode != dest_mode )
5642   {
5643     vtl->coord_mode = dest_mode;
5644     g_hash_table_foreach ( vtl->waypoints, (GHFunc) waypoint_convert, &dest_mode );
5645     g_hash_table_foreach ( vtl->tracks, (GHFunc) track_convert, &dest_mode );
5646   }
5647 }
5648
5649 VikWaypoint *vik_trw_layer_get_waypoint ( VikTrwLayer *vtl, gchar *name )
5650 {
5651   return g_hash_table_lookup ( vtl->waypoints, name );
5652 }
5653
5654 VikTrack *vik_trw_layer_get_track ( VikTrwLayer *vtl, const gchar *name )
5655 {
5656   return g_hash_table_lookup ( vtl->tracks, name );
5657 }
5658
5659 static void trw_layer_set_menu_selection ( VikTrwLayer *vtl, guint16 selection )
5660 {
5661   vtl->menu_selection = selection;
5662 }
5663
5664 static guint16 trw_layer_get_menu_selection ( VikTrwLayer *vtl )
5665 {
5666   return (vtl->menu_selection);
5667 }
5668
5669 /* ----------- Downloading maps along tracks --------------- */
5670
5671 static int get_download_area_width(VikViewport *vvp, gdouble zoom_level, struct LatLon *wh)
5672 {
5673   /* TODO: calculating based on current size of viewport */
5674   const gdouble w_at_zoom_0_125 = 0.0013;
5675   const gdouble h_at_zoom_0_125 = 0.0011;
5676   gdouble zoom_factor = zoom_level/0.125;
5677
5678   wh->lat = h_at_zoom_0_125 * zoom_factor;
5679   wh->lon = w_at_zoom_0_125 * zoom_factor;
5680
5681   return 0;   /* all OK */
5682 }
5683
5684 static VikCoord *get_next_coord(VikCoord *from, VikCoord *to, struct LatLon *dist, gdouble gradient)
5685 {
5686   if ((dist->lon >= ABS(to->east_west - from->east_west)) &&
5687       (dist->lat >= ABS(to->north_south - from->north_south)))
5688     return NULL;
5689
5690   VikCoord *coord = g_malloc(sizeof(VikCoord));
5691   coord->mode = VIK_COORD_LATLON;
5692
5693   if (ABS(gradient) < 1) {
5694     if (from->east_west > to->east_west)
5695       coord->east_west = from->east_west - dist->lon;
5696     else
5697       coord->east_west = from->east_west + dist->lon;
5698     coord->north_south = gradient * (coord->east_west - from->east_west) + from->north_south;
5699   } else {
5700     if (from->north_south > to->north_south)
5701       coord->north_south = from->north_south - dist->lat;
5702     else
5703       coord->north_south = from->north_south + dist->lat;
5704     coord->east_west = (1/gradient) * (coord->north_south - from->north_south) + from->north_south;
5705   }
5706
5707   return coord;
5708 }
5709
5710 static GList *add_fillins(GList *list, VikCoord *from, VikCoord *to, struct LatLon *dist)
5711 {
5712   /* TODO: handle virtical track (to->east_west - from->east_west == 0) */
5713   gdouble gradient = (to->north_south - from->north_south)/(to->east_west - from->east_west);
5714
5715   VikCoord *next = from;
5716   while (TRUE) {
5717     if ((next = get_next_coord(next, to, dist, gradient)) == NULL)
5718         break;
5719     list = g_list_prepend(list, next);
5720   }
5721
5722   return list;
5723 }
5724
5725 void vik_track_download_map(VikTrack *tr, VikMapsLayer *vml, VikViewport *vvp, gdouble zoom_level)
5726 {
5727   typedef struct _Rect {
5728     VikCoord tl;
5729     VikCoord br;
5730     VikCoord center;
5731   } Rect;
5732 #define GLRECT(iter) ((Rect *)((iter)->data))
5733
5734   struct LatLon wh;
5735   GList *rects_to_download = NULL;
5736   GList *rect_iter;
5737
5738   if (get_download_area_width(vvp, zoom_level, &wh))
5739     return;
5740
5741   GList *iter = tr->trackpoints;
5742   if (!iter)
5743     return;
5744
5745   gboolean new_map = TRUE;
5746   VikCoord *cur_coord, tl, br;
5747   Rect *rect;
5748   while (iter) {
5749     cur_coord = &(VIK_TRACKPOINT(iter->data))->coord;
5750     if (new_map) {
5751       vik_coord_set_area(cur_coord, &wh, &tl, &br);
5752       rect = g_malloc(sizeof(Rect));
5753       rect->tl = tl;
5754       rect->br = br;
5755       rect->center = *cur_coord;
5756       rects_to_download = g_list_prepend(rects_to_download, rect);
5757       new_map = FALSE;
5758       iter = iter->next;
5759       continue;
5760     }
5761     gboolean found = FALSE;
5762     for (rect_iter = rects_to_download; rect_iter; rect_iter = rect_iter->next) {
5763       if (vik_coord_inside(cur_coord, &GLRECT(rect_iter)->tl, &GLRECT(rect_iter)->br)) {
5764         found = TRUE;
5765         break;
5766       }
5767     }
5768     if (found)
5769         iter = iter->next;
5770     else
5771       new_map = TRUE;
5772   }
5773
5774   GList *fillins = NULL;
5775   /* 'fillin' doesn't work in UTM mode - potentially ending up in massive loop continually allocating memory - hence don't do it */
5776   /* seems that ATM the function get_next_coord works only for LATLON */
5777   if ( cur_coord->mode == VIK_COORD_LATLON ) {
5778     /* fill-ins for far apart points */
5779     GList *cur_rect, *next_rect;
5780     for (cur_rect = rects_to_download;
5781          (next_rect = cur_rect->next) != NULL;
5782          cur_rect = cur_rect->next) {
5783       if ((wh.lon < ABS(GLRECT(cur_rect)->center.east_west - GLRECT(next_rect)->center.east_west)) ||
5784           (wh.lat < ABS(GLRECT(cur_rect)->center.north_south - GLRECT(next_rect)->center.north_south))) {
5785         fillins = add_fillins(fillins, &GLRECT(cur_rect)->center, &GLRECT(next_rect)->center, &wh);
5786       }
5787     }
5788   } else
5789     g_message("%s: this feature works only in Mercator mode", __FUNCTION__);
5790
5791   if (fillins) {
5792     GList *iter = fillins;
5793     while (iter) {
5794       cur_coord = (VikCoord *)(iter->data);
5795       vik_coord_set_area(cur_coord, &wh, &tl, &br);
5796       rect = g_malloc(sizeof(Rect));
5797       rect->tl = tl;
5798       rect->br = br;
5799       rect->center = *cur_coord;
5800       rects_to_download = g_list_prepend(rects_to_download, rect);
5801       iter = iter->next;
5802     }
5803   }
5804
5805   for (rect_iter = rects_to_download; rect_iter; rect_iter = rect_iter->next) {
5806     maps_layer_download_section_without_redraw(vml, vvp, &(((Rect *)(rect_iter->data))->tl), &(((Rect *)(rect_iter->data))->br), zoom_level);
5807   }
5808
5809   if (fillins) {
5810     for (iter = fillins; iter; iter = iter->next)
5811       g_free(iter->data);
5812     g_list_free(fillins);
5813   }
5814   if (rects_to_download) {
5815     for (rect_iter = rects_to_download; rect_iter; rect_iter = rect_iter->next)
5816       g_free(rect_iter->data);
5817     g_list_free(rects_to_download);
5818   }
5819 }
5820
5821 static void trw_layer_download_map_along_track_cb ( gpointer pass_along[6] )
5822 {
5823   VikMapsLayer *vml;
5824   gint selected_map, default_map;
5825   gchar *zoomlist[] = {"0.125", "0.25", "0.5", "1", "2", "4", "8", "16", "32", "64", "128", "256", "512", "1024", NULL };
5826   gdouble zoom_vals[] = {0.125, 0.25, 0.5, 1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024};
5827   gint selected_zoom, default_zoom;
5828   int i,j;
5829
5830
5831   VikTrwLayer *vtl = pass_along[0];
5832   VikLayersPanel *vlp = pass_along[1];
5833   VikTrack *tr = (VikTrack *) g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, pass_along[3] );
5834   VikViewport *vvp = vik_window_viewport((VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl)));
5835
5836   GList *vmls = vik_layers_panel_get_all_layers_of_type(vlp, VIK_LAYER_MAPS, TRUE); // Includes hidden map layer types
5837   int num_maps = g_list_length(vmls);
5838
5839   if (!num_maps) {
5840     a_dialog_msg(VIK_GTK_WINDOW_FROM_LAYER(vtl), GTK_MESSAGE_ERROR, _("No map layer in use. Create one first"), NULL);
5841     return;
5842   }
5843
5844   gchar **map_names = g_malloc(1 + num_maps * sizeof(gpointer));
5845   VikMapsLayer **map_layers = g_malloc(1 + num_maps * sizeof(gpointer));
5846
5847   gchar **np = map_names;
5848   VikMapsLayer **lp = map_layers;
5849   for (i = 0; i < num_maps; i++) {
5850     gboolean dup = FALSE;
5851     vml = (VikMapsLayer *)(vmls->data);
5852     for (j = 0; j < i; j++) { /* no duplicate allowed */
5853       if (vik_maps_layer_get_map_type(vml) == vik_maps_layer_get_map_type(map_layers[j])) {
5854         dup = TRUE;
5855         break;
5856       }
5857     }
5858     if (!dup) {
5859       *lp++ = vml;
5860       *np++ = vik_maps_layer_get_map_label(vml);
5861     }
5862     vmls = vmls->next;
5863   }
5864   *lp = NULL;
5865   *np = NULL;
5866   num_maps = lp - map_layers;
5867
5868   for (default_map = 0; default_map < num_maps; default_map++) {
5869     /* TODO: check for parent layer's visibility */
5870     if (VIK_LAYER(map_layers[default_map])->visible)
5871       break;
5872   }
5873   default_map = (default_map == num_maps) ? 0 : default_map;
5874
5875   gdouble cur_zoom = vik_viewport_get_zoom(vvp);
5876   for (default_zoom = 0; default_zoom < sizeof(zoom_vals)/sizeof(gdouble); default_zoom++) {
5877     if (cur_zoom == zoom_vals[default_zoom])
5878       break;
5879   }
5880   default_zoom = (default_zoom == sizeof(zoom_vals)/sizeof(gdouble)) ? sizeof(zoom_vals)/sizeof(gdouble) - 1 : default_zoom;
5881
5882   if (!a_dialog_map_n_zoom(VIK_GTK_WINDOW_FROM_LAYER(vtl), map_names, default_map, zoomlist, default_zoom, &selected_map, &selected_zoom))
5883     goto done;
5884
5885   vik_track_download_map(tr, map_layers[selected_map], vvp, zoom_vals[selected_zoom]);
5886
5887 done:
5888   for (i = 0; i < num_maps; i++)
5889     g_free(map_names[i]);
5890   g_free(map_names);
5891   g_free(map_layers);
5892
5893   g_list_free(vmls);
5894
5895 }
5896
5897 /**** lowest waypoint number calculation ***/
5898 static gint highest_wp_number_name_to_number(const gchar *name) {
5899   if ( strlen(name) == 3 ) {
5900     int n = atoi(name);
5901     if ( n < 100 && name[0] != '0' )
5902       return -1;
5903     if ( n < 10 && name[0] != '0' )
5904       return -1;
5905     return n;
5906   }
5907   return -1;
5908 }
5909
5910
5911 static void highest_wp_number_reset(VikTrwLayer *vtl)
5912 {
5913   vtl->highest_wp_number = -1;
5914 }
5915
5916 static void highest_wp_number_add_wp(VikTrwLayer *vtl, const gchar *new_wp_name)
5917 {
5918   /* if is bigger that top, add it */
5919   gint new_wp_num = highest_wp_number_name_to_number(new_wp_name);
5920   if ( new_wp_num > vtl->highest_wp_number )
5921     vtl->highest_wp_number = new_wp_num;
5922 }
5923
5924 static void highest_wp_number_remove_wp(VikTrwLayer *vtl, const gchar *old_wp_name)
5925 {
5926   /* if wasn't top, do nothing. if was top, count backwards until we find one used */
5927   gint old_wp_num = highest_wp_number_name_to_number(old_wp_name);
5928   if ( vtl->highest_wp_number == old_wp_num ) {
5929     gchar buf[4];
5930     vtl->highest_wp_number --;
5931
5932     g_snprintf(buf,4,"%03d", vtl->highest_wp_number );
5933     /* search down until we find something that *does* exist */
5934
5935     while ( vtl->highest_wp_number > 0 && ! g_hash_table_lookup ( vtl->waypoints, buf ) ) {
5936       vtl->highest_wp_number --;
5937       g_snprintf(buf,4,"%03d", vtl->highest_wp_number );
5938     }
5939   }
5940 }
5941
5942 /* get lowest unused number */
5943 static gchar *highest_wp_number_get(VikTrwLayer *vtl)
5944 {
5945   gchar buf[4];
5946   if ( vtl->highest_wp_number < 0 || vtl->highest_wp_number >= 999 )
5947     return NULL;
5948   g_snprintf(buf,4,"%03d", vtl->highest_wp_number+1 );
5949   return g_strdup(buf);
5950 }