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