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