]> git.street.me.uk Git - andy/viking.git/blob - src/viktrwlayer.c
Improve contrast between default track colours.
[andy/viking.git] / src / viktrwlayer.c
1 /*
2  * viking -- GPS Data and Topo Analyzer, Explorer, and Manager
3  *
4  * Copyright (C) 2003-2007, Evan Battaglia <gtoevan@gmx.net>
5  * Copyright (C) 2005-2008, Alex Foobarian <foobarian@gmail.com>
6  * Copyright (C) 2007, Quy Tonthat <qtonthat@gmail.com>
7  * Copyright (C) 2009, Hein Ragas <viking@ragas.nl>
8  * Copyright (c) 2012, Rob Norris <rw_norris@hotmail.com>
9  *
10  * This program is free software; you can redistribute it and/or modify
11  * it under the terms of the GNU General Public License as published by
12  * the Free Software Foundation; either version 2 of the License, or
13  * (at your option) any later version.
14  *
15  * This program is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU General Public License for more details.
19  *
20  * You should have received a copy of the GNU General Public License
21  * along with this program; if not, write to the Free Software
22  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
23  *
24  */
25 /* WARNING: If you go beyond this point, we are NOT responsible for any ill effects on your sanity */
26 /* viktrwlayer.c -- 8000+ lines can make a difference in the state of things */
27
28 #ifdef HAVE_CONFIG_H
29 #include "config.h"
30 #endif
31
32 #include "viking.h"
33 #include "vikmapslayer.h"
34 #include "vikgpslayer.h"
35 #include "viktrwlayer_tpwin.h"
36 #include "viktrwlayer_propwin.h"
37 #ifdef VIK_CONFIG_GEOTAG
38 #include "viktrwlayer_geotag.h"
39 #include "geotag_exif.h"
40 #endif
41 #include "garminsymbols.h"
42 #include "thumbnails.h"
43 #include "background.h"
44 #include "gpx.h"
45 #include "babel.h"
46 #include "dem.h"
47 #include "dems.h"
48 #include "geonamessearch.h"
49 #ifdef VIK_CONFIG_OPENSTREETMAP
50 #include "osm-traces.h"
51 #endif
52 #include "acquire.h"
53 #include "datasources.h"
54 #include "datasource_gps.h"
55 #include "util.h"
56
57 #include "icons/icons.h"
58
59 #ifdef HAVE_MATH_H
60 #include <math.h>
61 #endif
62 #ifdef HAVE_STRING_H
63 #include <string.h>
64 #endif
65 #ifdef HAVE_STDLIB_H
66 #include <stdlib.h>
67 #endif
68 #include <stdio.h>
69 #include <ctype.h>
70
71 #include <gdk/gdkkeysyms.h>
72 #include <glib.h>
73 #include <glib/gstdio.h>
74 #include <glib/gi18n.h>
75
76 #ifdef VIK_CONFIG_GOOGLE
77 #define GOOGLE_DIRECTIONS_STRING "maps.google.com/maps?q=from:%s,%s+to:%s,%s&output=js"
78 #endif
79
80 #define VIK_TRW_LAYER_TRACK_GC 5
81 #define VIK_TRW_LAYER_TRACK_GCS 10
82 #define VIK_TRW_LAYER_TRACK_GC_BLACK 0
83 #define VIK_TRW_LAYER_TRACK_GC_SLOW 1
84 #define VIK_TRW_LAYER_TRACK_GC_AVER 2
85 #define VIK_TRW_LAYER_TRACK_GC_FAST 3
86 #define VIK_TRW_LAYER_TRACK_GC_STOP 4
87
88 #define DRAWMODE_BY_TRACK 0
89 #define DRAWMODE_BY_SPEED 1
90 #define DRAWMODE_ALL_BLACK 2
91 // Note using DRAWMODE_BY_SPEED may be slow especially for vast numbers of trackpoints
92 //  as we are (re)calculating the colour for every point
93
94 #define POINTS 1
95 #define LINES 2
96
97 /* this is how it knows when you click if you are clicking close to a trackpoint. */
98 #define TRACKPOINT_SIZE_APPROX 5
99 #define WAYPOINT_SIZE_APPROX 5
100
101 #define MIN_STOP_LENGTH 15
102 #define MAX_STOP_LENGTH 86400
103 #define DRAW_ELEVATION_FACTOR 30 /* height of elevation plotting, sort of relative to zoom level ("mpp" that isn't mpp necessarily) */
104                                  /* this is multiplied by user-inputted value from 1-100. */
105
106 enum { WP_SYMBOL_FILLED_SQUARE, WP_SYMBOL_SQUARE, WP_SYMBOL_CIRCLE, WP_SYMBOL_X, WP_NUM_SYMBOLS };
107
108 // See http://developer.gnome.org/pango/stable/PangoMarkupFormat.html
109 typedef enum {
110   FS_XX_SMALL = 0, // 'xx-small'
111   FS_X_SMALL,
112   FS_SMALL,
113   FS_MEDIUM, // DEFAULT
114   FS_LARGE,
115   FS_X_LARGE,
116   FS_XX_LARGE,
117   FS_NUM_SIZES
118 } font_size_t;
119
120 struct _VikTrwLayer {
121   VikLayer vl;
122   GHashTable *tracks;
123   GHashTable *tracks_iters;
124   GHashTable *routes;
125   GHashTable *routes_iters;
126   GHashTable *waypoints_iters;
127   GHashTable *waypoints;
128   GtkTreeIter tracks_iter, routes_iter, waypoints_iter;
129   gboolean tracks_visible, routes_visible, waypoints_visible;
130   guint8 drawmode;
131   guint8 drawpoints;
132   guint8 drawpoints_size;
133   guint8 drawelevation;
134   guint8 elevation_factor;
135   guint8 drawstops;
136   guint32 stop_length;
137   guint8 drawlines;
138   guint8 drawdirections;
139   guint8 drawdirections_size;
140   guint8 line_thickness;
141   guint8 bg_line_thickness;
142
143   guint8 wp_symbol;
144   guint8 wp_size;
145   gboolean wp_draw_symbols;
146   font_size_t wp_font_size;
147
148   gdouble track_draw_speed_factor;
149   GArray *track_gc;
150   GdkGC *track_1color_gc;
151   GdkGC *current_track_gc;
152   // Separate GC for a track's potential new point as drawn via separate method
153   //  (compared to the actual track points drawn in the main trw_layer_draw_track function)
154   GdkGC *current_track_newpoint_gc;
155   GdkGC *track_bg_gc;
156   GdkGC *waypoint_gc;
157   GdkGC *waypoint_text_gc;
158   GdkGC *waypoint_bg_gc;
159   GdkFont *waypoint_font;
160   VikTrack *current_track; // ATM shared between new tracks and new routes
161   guint16 ct_x1, ct_y1, ct_x2, ct_y2;
162   gboolean draw_sync_done;
163   gboolean draw_sync_do;
164
165   VikCoordMode coord_mode;
166
167   /* wp editing tool */
168   VikWaypoint *current_wp;
169   gpointer current_wp_id;
170   gboolean moving_wp;
171   gboolean waypoint_rightclick;
172
173   /* track editing tool */
174   GList *current_tpl;
175   VikTrack *current_tp_track;
176   gpointer current_tp_id;
177   VikTrwLayerTpwin *tpwin;
178
179   /* track editing tool -- more specifically, moving tps */
180   gboolean moving_tp;
181
182   /* route finder tool */
183   gboolean route_finder_started;
184   VikCoord route_finder_coord;
185   gboolean route_finder_check_added_track;
186   VikTrack *route_finder_added_track;
187   VikTrack *route_finder_current_track;
188   gboolean route_finder_append;
189
190   gboolean drawlabels;
191   gboolean drawimages;
192   guint8 image_alpha;
193   GQueue *image_cache;
194   guint8 image_size;
195   guint16 image_cache_size;
196
197   /* for waypoint text */
198   PangoLayout *wplabellayout;
199
200   gboolean has_verified_thumbnails;
201
202   GtkMenu *wp_right_click_menu;
203   GtkMenu *track_right_click_menu;
204
205   /* menu */
206   VikStdLayerMenuItem menu_selection;
207
208   gint highest_wp_number;
209 };
210
211 /* A caached waypoint image. */
212 typedef struct {
213   GdkPixbuf *pixbuf;
214   gchar *image; /* filename */
215 } CachedPixbuf;
216
217 struct DrawingParams {
218   VikViewport *vp;
219   VikTrwLayer *vtl;
220   gdouble xmpp, ympp;
221   guint16 width, height;
222   gdouble cc; // Cosine factor in track directions
223   gdouble ss; // Sine factor in track directions
224   const VikCoord *center;
225   gboolean one_zone, lat_lon;
226   gdouble ce1, ce2, cn1, cn2;
227 };
228
229 static gboolean trw_layer_delete_waypoint ( VikTrwLayer *vtl, VikWaypoint *wp );
230
231 static void trw_layer_delete_item ( gpointer pass_along[6] );
232 static void trw_layer_copy_item_cb ( gpointer pass_along[6] );
233 static void trw_layer_cut_item_cb ( gpointer pass_along[6] );
234
235 static void trw_layer_find_maxmin_waypoints ( const gchar *name, const VikWaypoint *w, struct LatLon maxmin[2] );
236 static void trw_layer_find_maxmin_tracks ( const gchar *name, const VikTrack *trk, struct LatLon maxmin[2] );
237 static void trw_layer_find_maxmin (VikTrwLayer *vtl, struct LatLon maxmin[2]);
238
239 static void trw_layer_new_track_gcs ( VikTrwLayer *vtl, VikViewport *vp );
240 static void trw_layer_free_track_gcs ( VikTrwLayer *vtl );
241
242 static void trw_layer_draw_track_cb ( const gchar *name, VikTrack *track, struct DrawingParams *dp );
243 static void trw_layer_draw_waypoint ( const gchar *name, VikWaypoint *wp, struct DrawingParams *dp );
244
245 static void goto_coord ( gpointer *vlp, gpointer vvp, gpointer vl, const VikCoord *coord );
246 static void trw_layer_goto_track_startpoint ( gpointer pass_along[6] );
247 static void trw_layer_goto_track_endpoint ( gpointer pass_along[6] );
248 static void trw_layer_goto_track_max_speed ( gpointer pass_along[6] );
249 static void trw_layer_goto_track_max_alt ( gpointer pass_along[6] );
250 static void trw_layer_goto_track_min_alt ( gpointer pass_along[6] );
251 static void trw_layer_goto_track_center ( gpointer pass_along[6] );
252 static void trw_layer_merge_by_segment ( gpointer pass_along[6] );
253 static void trw_layer_merge_by_timestamp ( gpointer pass_along[6] );
254 static void trw_layer_merge_with_other ( gpointer pass_along[6] );
255 static void trw_layer_append_track ( gpointer pass_along[6] );
256 static void trw_layer_split_by_timestamp ( gpointer pass_along[6] );
257 static void trw_layer_split_by_n_points ( gpointer pass_along[6] );
258 static void trw_layer_split_at_trackpoint ( gpointer pass_along[6] );
259 static void trw_layer_split_segments ( gpointer pass_along[6] );
260 static void trw_layer_delete_points_same_position ( gpointer pass_along[6] );
261 static void trw_layer_delete_points_same_time ( gpointer pass_along[6] );
262 static void trw_layer_reverse ( gpointer pass_along[6] );
263 static void trw_layer_download_map_along_track_cb ( gpointer pass_along[6] );
264 static void trw_layer_edit_trackpoint ( gpointer pass_along[6] );
265 static void trw_layer_show_picture ( gpointer pass_along[6] );
266 static void trw_layer_gps_upload_any ( gpointer pass_along[6] );
267
268 static void trw_layer_centerize ( gpointer layer_and_vlp[2] );
269 static void trw_layer_auto_view ( gpointer layer_and_vlp[2] );
270 static void trw_layer_export ( gpointer layer_and_vlp[2], const gchar* title, const gchar* default_name, VikTrack* trk, guint file_type );
271 static void trw_layer_goto_wp ( gpointer layer_and_vlp[2] );
272 static void trw_layer_new_wp ( gpointer lav[2] );
273 static void trw_layer_new_track ( gpointer lav[2] );
274 static void trw_layer_new_route ( gpointer lav[2] );
275 static void trw_layer_finish_track ( gpointer lav[2] );
276 static void trw_layer_auto_waypoints_view ( gpointer lav[2] );
277 static void trw_layer_auto_tracks_view ( gpointer lav[2] );
278 static void trw_layer_delete_all_tracks ( gpointer lav[2] );
279 static void trw_layer_delete_tracks_from_selection ( gpointer lav[2] );
280 static void trw_layer_delete_all_waypoints ( gpointer lav[2] );
281 static void trw_layer_delete_waypoints_from_selection ( gpointer lav[2] );
282 static void trw_layer_new_wikipedia_wp_viewport ( gpointer lav[2] );
283 static void trw_layer_new_wikipedia_wp_layer ( gpointer lav[2] );
284 #ifdef VIK_CONFIG_GEOTAG
285 static void trw_layer_geotagging_waypoint_mtime_keep ( gpointer pass_along[6] );
286 static void trw_layer_geotagging_waypoint_mtime_update ( gpointer pass_along[6] );
287 static void trw_layer_geotagging_track ( gpointer pass_along[6] );
288 static void trw_layer_geotagging ( gpointer lav[2] );
289 #endif
290 static void trw_layer_acquire_gps_cb ( gpointer lav[2] );
291 #ifdef VIK_CONFIG_GOOGLE
292 static void trw_layer_acquire_google_cb ( gpointer lav[2] );
293 #endif
294 #ifdef VIK_CONFIG_OPENSTREETMAP
295 static void trw_layer_acquire_osm_cb ( gpointer lav[2] );
296 #endif
297 #ifdef VIK_CONFIG_GEOCACHES
298 static void trw_layer_acquire_geocache_cb ( gpointer lav[2] );
299 #endif
300 #ifdef VIK_CONFIG_GEOTAG
301 static void trw_layer_acquire_geotagged_cb ( gpointer lav[2] );
302 #endif
303 static void trw_layer_acquire_file_cb ( gpointer lav[2] );
304 static void trw_layer_gps_upload ( gpointer lav[2] );
305
306 // Specific route versions:
307 //  Most track handling functions can handle operating on the route list
308 //  However these ones are easier in separate functions
309 static void trw_layer_auto_routes_view ( gpointer lav[2] );
310 static void trw_layer_delete_routes_from_selection ( gpointer lav[2] );
311
312 /* pop-up items */
313 static void trw_layer_properties_item ( gpointer pass_along[7] );
314 static void trw_layer_goto_waypoint ( gpointer pass_along[6] );
315 static void trw_layer_waypoint_gc_webpage ( gpointer pass_along[6] );
316
317 static void trw_layer_realize_waypoint ( gpointer id, VikWaypoint *wp, gpointer pass_along[5] );
318 static void trw_layer_realize_track ( gpointer id, VikTrack *track, gpointer pass_along[5] );
319 static void init_drawing_params ( struct DrawingParams *dp, VikTrwLayer *vtl, VikViewport *vp );
320
321 static void trw_layer_insert_tp_after_current_tp ( VikTrwLayer *vtl );
322 static void trw_layer_cancel_current_tp ( VikTrwLayer *vtl, gboolean destroy );
323 static void trw_layer_tpwin_response ( VikTrwLayer *vtl, gint response );
324 static void trw_layer_tpwin_init ( VikTrwLayer *vtl );
325
326 static gpointer tool_edit_trackpoint_create ( VikWindow *vw, VikViewport *vvp);
327 static gboolean tool_edit_trackpoint_click ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data );
328 static gboolean tool_edit_trackpoint_move ( VikTrwLayer *vtl, GdkEventMotion *event, gpointer data );
329 static gboolean tool_edit_trackpoint_release ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data );
330 static gpointer tool_show_picture_create ( VikWindow *vw, VikViewport *vvp);
331 static gboolean tool_show_picture_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp ); 
332 static gpointer tool_edit_waypoint_create ( VikWindow *vw, VikViewport *vvp);
333 static gboolean tool_edit_waypoint_click ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data );
334 static gboolean tool_edit_waypoint_move ( VikTrwLayer *vtl, GdkEventMotion *event, gpointer data );
335 static gboolean tool_edit_waypoint_release ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data );
336 static gpointer tool_new_route_create ( VikWindow *vw, VikViewport *vvp);
337 static gboolean tool_new_route_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp );
338 static gpointer tool_new_track_create ( VikWindow *vw, VikViewport *vvp);
339 static gboolean tool_new_track_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp ); 
340 static VikLayerToolFuncStatus tool_new_track_move ( VikTrwLayer *vtl, GdkEventMotion *event, VikViewport *vvp ); 
341 static void tool_new_track_release ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp );
342 static gboolean tool_new_track_key_press ( VikTrwLayer *vtl, GdkEventKey *event, VikViewport *vvp ); 
343 static gpointer tool_new_waypoint_create ( VikWindow *vw, VikViewport *vvp);
344 static gboolean tool_new_waypoint_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp );
345 #ifdef VIK_CONFIG_GOOGLE
346 static gpointer tool_route_finder_create ( VikWindow *vw, VikViewport *vvp);
347 static gboolean tool_route_finder_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp );
348 #endif
349
350 static void cached_pixbuf_free ( CachedPixbuf *cp );
351 static gint cached_pixbuf_cmp ( CachedPixbuf *cp, const gchar *name );
352
353 static VikTrackpoint *closest_tp_in_five_pixel_interval ( VikTrwLayer *vtl, VikViewport *vvp, gint x, gint y );
354 static VikWaypoint *closest_wp_in_five_pixel_interval ( VikTrwLayer *vtl, VikViewport *vvp, gint x, gint y );
355
356 static void waypoint_convert ( const gpointer id, VikWaypoint *wp, VikCoordMode *dest_mode );
357 static void track_convert ( const gchar *name, VikTrack *tr, VikCoordMode *dest_mode );
358
359 static gchar *highest_wp_number_get(VikTrwLayer *vtl);
360 static void highest_wp_number_reset(VikTrwLayer *vtl);
361 static void highest_wp_number_add_wp(VikTrwLayer *vtl, const gchar *new_wp_name);
362 static void highest_wp_number_remove_wp(VikTrwLayer *vtl, const gchar *old_wp_name);
363
364 // Note for the following tool GtkRadioActionEntry texts:
365 //  the very first text value is an internal name not displayed anywhere
366 //  the first N_ text value is the name used for menu entries - hence has an underscore for the keyboard accelerator
367 //    * remember not to clash with the values used for VikWindow level tools (Pan, Zoom, Ruler + Select)
368 //  the second N_ text value is used for the button tooltip (i.e. generally don't want an underscore here)
369 //  the value is always set to 0 and the tool loader in VikWindow will set the actual appropriate value used
370 static VikToolInterface trw_layer_tools[] = {
371   { { "CreateWaypoint", "vik-icon-Create Waypoint", N_("Create _Waypoint"), "<control><shift>W", N_("Create Waypoint"), 0 },
372     (VikToolConstructorFunc) tool_new_waypoint_create,    NULL, NULL, NULL,
373     (VikToolMouseFunc) tool_new_waypoint_click,    NULL, NULL, (VikToolKeyFunc) NULL,
374     FALSE,
375     GDK_CURSOR_IS_PIXMAP, &cursor_addwp_pixbuf },
376
377   { { "CreateTrack", "vik-icon-Create Track", N_("Create _Track"), "<control><shift>T", N_("Create Track"), 0 },
378     (VikToolConstructorFunc) tool_new_track_create,       NULL, NULL, NULL,
379     (VikToolMouseFunc) tool_new_track_click,
380     (VikToolMouseMoveFunc) tool_new_track_move,
381     (VikToolMouseFunc) tool_new_track_release,
382     (VikToolKeyFunc) tool_new_track_key_press,
383     TRUE, // Still need to handle clicks when in PAN mode to disable the potential trackpoint drawing
384     GDK_CURSOR_IS_PIXMAP, &cursor_addtr_pixbuf },
385
386   { { "CreateRoute", "vik-icon-Create Route", N_("Create _Route"), "<control><shift>B", N_("Create Route"), 0 },
387     (VikToolConstructorFunc) tool_new_route_create,       NULL, NULL, NULL,
388     (VikToolMouseFunc) tool_new_route_click,
389     (VikToolMouseMoveFunc) tool_new_track_move, // -\#
390     (VikToolMouseFunc) tool_new_track_release,  //   -> Reuse these track methods on a route
391     (VikToolKeyFunc) tool_new_track_key_press,  // -/#
392     TRUE, // Still need to handle clicks when in PAN mode to disable the potential trackpoint drawing
393     GDK_CURSOR_IS_PIXMAP, &cursor_new_route_pixbuf },
394
395   { { "EditWaypoint", "vik-icon-Edit Waypoint", N_("_Edit Waypoint"), "<control><shift>E", N_("Edit Waypoint"), 0 },
396     (VikToolConstructorFunc) tool_edit_waypoint_create,   NULL, NULL, NULL,
397     (VikToolMouseFunc) tool_edit_waypoint_click,   
398     (VikToolMouseMoveFunc) tool_edit_waypoint_move,
399     (VikToolMouseFunc) tool_edit_waypoint_release, (VikToolKeyFunc) NULL,
400     FALSE,
401     GDK_CURSOR_IS_PIXMAP, &cursor_edwp_pixbuf },
402
403   { { "EditTrackpoint", "vik-icon-Edit Trackpoint", N_("Edit Trac_kpoint"), "<control><shift>K", N_("Edit Trackpoint"), 0 },
404     (VikToolConstructorFunc) tool_edit_trackpoint_create, NULL, NULL, NULL,
405     (VikToolMouseFunc) tool_edit_trackpoint_click,
406     (VikToolMouseMoveFunc) tool_edit_trackpoint_move,
407     (VikToolMouseFunc) tool_edit_trackpoint_release, (VikToolKeyFunc) NULL,
408     FALSE,
409     GDK_CURSOR_IS_PIXMAP, &cursor_edtr_pixbuf },
410
411   { { "ShowPicture", "vik-icon-Show Picture", N_("Show P_icture"), "<control><shift>I", N_("Show Picture"), 0 },
412     (VikToolConstructorFunc) tool_show_picture_create,    NULL, NULL, NULL,
413     (VikToolMouseFunc) tool_show_picture_click,    NULL, NULL, (VikToolKeyFunc) NULL,
414     FALSE,
415     GDK_CURSOR_IS_PIXMAP, &cursor_showpic_pixbuf },
416
417 #ifdef VIK_CONFIG_GOOGLE
418   { { "RouteFinder", "vik-icon-Route Finder", N_("Route _Finder"), "<control><shift>F", N_("Route Finder"), 0 },
419     (VikToolConstructorFunc) tool_route_finder_create,  NULL, NULL, NULL,
420     (VikToolMouseFunc) tool_route_finder_click, NULL, NULL, (VikToolKeyFunc) NULL,
421     FALSE,
422     GDK_CURSOR_IS_PIXMAP, &cursor_route_finder_pixbuf },
423 #endif
424 };
425 enum { TOOL_CREATE_WAYPOINT=0, TOOL_CREATE_TRACK, TOOL_CREATE_ROUTE, TOOL_EDIT_WAYPOINT, TOOL_EDIT_TRACKPOINT, TOOL_SHOW_PICTURE, NUM_TOOLS };
426
427 /****** PARAMETERS ******/
428
429 static gchar *params_groups[] = { N_("Waypoints"), N_("Tracks"), N_("Waypoint Images") };
430 enum { GROUP_WAYPOINTS, GROUP_TRACKS, GROUP_IMAGES };
431
432 static gchar *params_drawmodes[] = { N_("Draw by Track"), N_("Draw by Speed"), N_("All Tracks Black"), 0 };
433 static gchar *params_wpsymbols[] = { N_("Filled Square"), N_("Square"), N_("Circle"), N_("X"), 0 };
434
435 #define MIN_POINT_SIZE 2
436 #define MAX_POINT_SIZE 10
437
438 #define MIN_ARROW_SIZE 3
439 #define MAX_ARROW_SIZE 20
440
441 static VikLayerParamScale params_scales[] = {
442  /* min  max    step digits */
443  {  1,   10,    1,   0 }, /* line_thickness */
444  {  0,   100,   1,   0 }, /* track draw speed factor */
445  {  1.0, 100.0, 1.0, 2 }, /* UNUSED */
446                 /* 5 * step == how much to turn */
447  {  16,   128,  4,   0 }, // 3: image_size - NB step size ignored when an HSCALE used
448  {   0,   255,  5,   0 }, // 4: image alpha -    "     "      "            "
449  {   5,   500,  5,   0 }, // 5: image cache_size -     "      "
450  {   0,   8,    1,   0 }, // 6: Background line thickness
451  {   1,  64,    1,   0 }, /* wpsize */
452  {   MIN_STOP_LENGTH, MAX_STOP_LENGTH, 1,   0 }, /* stop_length */
453  {   1, 100, 1,   0 }, // 9: elevation factor
454  {   MIN_POINT_SIZE,  MAX_POINT_SIZE,  1,   0 }, // 10: track point size
455  {   MIN_ARROW_SIZE,  MAX_ARROW_SIZE,  1,   0 }, // 11: direction arrow size
456 };
457
458 static gchar* params_font_sizes[] = {
459   N_("Extra Extra Small"),
460   N_("Extra Small"),
461   N_("Small"),
462   N_("Medium"),
463   N_("Large"),
464   N_("Extra Large"),
465   N_("Extra Extra Large"),
466   NULL };
467
468 VikLayerParam trw_layer_params[] = {
469   { "tracks_visible", VIK_LAYER_PARAM_BOOLEAN, VIK_LAYER_NOT_IN_PROPERTIES },
470   { "waypoints_visible", VIK_LAYER_PARAM_BOOLEAN, VIK_LAYER_NOT_IN_PROPERTIES },
471   { "routes_visible", VIK_LAYER_PARAM_BOOLEAN, VIK_LAYER_NOT_IN_PROPERTIES },
472
473   { "drawmode", VIK_LAYER_PARAM_UINT, GROUP_TRACKS, N_("Track Drawing Mode:"), VIK_LAYER_WIDGET_RADIOGROUP, NULL },
474   { "drawlines", VIK_LAYER_PARAM_BOOLEAN, GROUP_TRACKS, N_("Draw Track Lines"), VIK_LAYER_WIDGET_CHECKBUTTON },
475   { "line_thickness", VIK_LAYER_PARAM_UINT, GROUP_TRACKS, N_("Track Thickness:"), VIK_LAYER_WIDGET_SPINBUTTON, params_scales + 0 },
476   { "drawdirections", VIK_LAYER_PARAM_BOOLEAN, GROUP_TRACKS, N_("Draw Track Direction"), VIK_LAYER_WIDGET_CHECKBUTTON },
477   { "trkdirectionsize", VIK_LAYER_PARAM_UINT, GROUP_TRACKS, N_("Direction Size:"), VIK_LAYER_WIDGET_SPINBUTTON, params_scales + 11 },
478   { "drawpoints", VIK_LAYER_PARAM_BOOLEAN, GROUP_TRACKS, N_("Draw Trackpoints"), VIK_LAYER_WIDGET_CHECKBUTTON },
479   { "trkpointsize", VIK_LAYER_PARAM_UINT, GROUP_TRACKS, N_("Trackpoint Size:"), VIK_LAYER_WIDGET_SPINBUTTON, params_scales + 10 },
480   { "drawelevation", VIK_LAYER_PARAM_BOOLEAN, GROUP_TRACKS, N_("Draw Elevation"), VIK_LAYER_WIDGET_CHECKBUTTON },
481   { "elevation_factor", VIK_LAYER_PARAM_UINT, GROUP_TRACKS, N_("Draw Elevation Height %:"), VIK_LAYER_WIDGET_HSCALE, params_scales + 9 },
482
483   { "drawstops", VIK_LAYER_PARAM_BOOLEAN, GROUP_TRACKS, N_("Draw Stops"), VIK_LAYER_WIDGET_CHECKBUTTON },
484   { "stop_length", VIK_LAYER_PARAM_UINT, GROUP_TRACKS, N_("Min Stop Length (seconds):"), VIK_LAYER_WIDGET_SPINBUTTON, params_scales + 8 },
485
486   { "bg_line_thickness", VIK_LAYER_PARAM_UINT, GROUP_TRACKS, N_("Track BG Thickness:"), VIK_LAYER_WIDGET_SPINBUTTON, params_scales + 6 },
487   { "trackbgcolor", VIK_LAYER_PARAM_COLOR, GROUP_TRACKS, N_("Track Background Color"), VIK_LAYER_WIDGET_COLOR, 0 },
488   { "speed_factor", VIK_LAYER_PARAM_DOUBLE, GROUP_TRACKS, N_("Draw by Speed Factor (%):"), VIK_LAYER_WIDGET_HSCALE, params_scales + 1 },
489
490   { "drawlabels", VIK_LAYER_PARAM_BOOLEAN, GROUP_WAYPOINTS, N_("Draw Labels"), VIK_LAYER_WIDGET_CHECKBUTTON },
491   { "wpfontsize", VIK_LAYER_PARAM_UINT, GROUP_WAYPOINTS, N_("Waypoint Font Size:"), VIK_LAYER_WIDGET_COMBOBOX, params_font_sizes, NULL },
492   { "wpcolor", VIK_LAYER_PARAM_COLOR, GROUP_WAYPOINTS, N_("Waypoint Color:"), VIK_LAYER_WIDGET_COLOR, 0 },
493   { "wptextcolor", VIK_LAYER_PARAM_COLOR, GROUP_WAYPOINTS, N_("Waypoint Text:"), VIK_LAYER_WIDGET_COLOR, 0 },
494   { "wpbgcolor", VIK_LAYER_PARAM_COLOR, GROUP_WAYPOINTS, N_("Background:"), VIK_LAYER_WIDGET_COLOR, 0 },
495   { "wpbgand", VIK_LAYER_PARAM_BOOLEAN, GROUP_WAYPOINTS, N_("Fake BG Color Translucency:"), VIK_LAYER_WIDGET_CHECKBUTTON, 0 },
496   { "wpsymbol", VIK_LAYER_PARAM_UINT, GROUP_WAYPOINTS, N_("Waypoint marker:"), VIK_LAYER_WIDGET_RADIOGROUP, NULL },
497   { "wpsize", VIK_LAYER_PARAM_UINT, GROUP_WAYPOINTS, N_("Waypoint size:"), VIK_LAYER_WIDGET_SPINBUTTON, params_scales + 7 },
498   { "wpsyms", VIK_LAYER_PARAM_BOOLEAN, GROUP_WAYPOINTS, N_("Draw Waypoint Symbols:"), VIK_LAYER_WIDGET_CHECKBUTTON },
499
500   { "drawimages", VIK_LAYER_PARAM_BOOLEAN, GROUP_IMAGES, N_("Draw Waypoint Images"), VIK_LAYER_WIDGET_CHECKBUTTON },
501   { "image_size", VIK_LAYER_PARAM_UINT, GROUP_IMAGES, N_("Image Size (pixels):"), VIK_LAYER_WIDGET_HSCALE, params_scales + 3 },
502   { "image_alpha", VIK_LAYER_PARAM_UINT, GROUP_IMAGES, N_("Image Alpha:"), VIK_LAYER_WIDGET_HSCALE, params_scales + 4 },
503   { "image_cache_size", VIK_LAYER_PARAM_UINT, GROUP_IMAGES, N_("Image Memory Cache Size:"), VIK_LAYER_WIDGET_HSCALE, params_scales + 5 },
504 };
505
506 // ENUMERATION MUST BE IN THE SAME ORDER AS THE NAMED PARAMS ABOVE
507 enum {
508   // Sublayer visibilities
509   PARAM_TV,
510   PARAM_WV,
511   PARAM_RV,
512   // Tracks
513   PARAM_DM,
514   PARAM_DL,
515   PARAM_LT,
516   PARAM_DD,
517   PARAM_DDS,
518   PARAM_DP,
519   PARAM_DPS,
520   PARAM_DE,
521   PARAM_EF,
522   PARAM_DS,
523   PARAM_SL,
524   PARAM_BLT,
525   PARAM_TBGC,
526   PARAM_TDSF,
527   // Waypoints
528   PARAM_DLA,
529   PARAM_WPFONTSIZE,
530   PARAM_WPC,
531   PARAM_WPTC,
532   PARAM_WPBC,
533   PARAM_WPBA,
534   PARAM_WPSYM,
535   PARAM_WPSIZE,
536   PARAM_WPSYMS,
537   // WP images
538   PARAM_DI,
539   PARAM_IS,
540   PARAM_IA,
541   PARAM_ICS,
542   NUM_PARAMS
543 };
544
545 /*** TO ADD A PARAM:
546  *** 1) Add to trw_layer_params and enumeration
547  *** 2) Handle in get_param & set_param (presumably adding on to VikTrwLayer struct)
548  ***/
549
550 /****** END PARAMETERS ******/
551
552 static VikTrwLayer* trw_layer_new ( gint drawmode );
553 /* Layer Interface function definitions */
554 static VikTrwLayer* trw_layer_create ( VikViewport *vp );
555 static void trw_layer_realize ( VikTrwLayer *vtl, VikTreeview *vt, GtkTreeIter *layer_iter );
556 static void trw_layer_post_read ( VikTrwLayer *vtl, GtkWidget *vvp );
557 static void trw_layer_free ( VikTrwLayer *trwlayer );
558 static void trw_layer_draw ( VikTrwLayer *l, gpointer data );
559 static void trw_layer_change_coord_mode ( VikTrwLayer *vtl, VikCoordMode dest_mode );
560 static void trw_layer_set_menu_selection ( VikTrwLayer *vtl, guint16 );
561 static guint16 trw_layer_get_menu_selection ( VikTrwLayer *vtl );
562 static void trw_layer_add_menu_items ( VikTrwLayer *vtl, GtkMenu *menu, gpointer vlp );
563 static gboolean trw_layer_sublayer_add_menu_items ( VikTrwLayer *l, GtkMenu *menu, gpointer vlp, gint subtype, gpointer sublayer, GtkTreeIter *iter, VikViewport *vvp );
564 static const gchar* trw_layer_sublayer_rename_request ( VikTrwLayer *l, const gchar *newname, gpointer vlp, gint subtype, gpointer sublayer, GtkTreeIter *iter );
565 static gboolean trw_layer_sublayer_toggle_visible ( VikTrwLayer *l, gint subtype, gpointer sublayer );
566 static const gchar* trw_layer_layer_tooltip ( VikTrwLayer *vtl );
567 static const gchar* trw_layer_sublayer_tooltip ( VikTrwLayer *l, gint subtype, gpointer sublayer );
568 static gboolean trw_layer_selected ( VikTrwLayer *l, gint subtype, gpointer sublayer, gint type, gpointer vlp );
569 static void trw_layer_marshall ( VikTrwLayer *vtl, guint8 **data, gint *len );
570 static VikTrwLayer *trw_layer_unmarshall ( guint8 *data, gint len, VikViewport *vvp );
571 static gboolean trw_layer_set_param ( VikTrwLayer *vtl, guint16 id, VikLayerParamData data, VikViewport *vp, gboolean is_file_operation );
572 static VikLayerParamData trw_layer_get_param ( VikTrwLayer *vtl, guint16 id, gboolean is_file_operation );
573 static void trw_layer_del_item ( VikTrwLayer *vtl, gint subtype, gpointer sublayer );
574 static void trw_layer_cut_item ( VikTrwLayer *vtl, gint subtype, gpointer sublayer );
575 static void trw_layer_copy_item ( VikTrwLayer *vtl, gint subtype, gpointer sublayer, guint8 **item, guint *len );
576 static gboolean trw_layer_paste_item ( VikTrwLayer *vtl, gint subtype, guint8 *item, guint len );
577 static void trw_layer_free_copied_item ( gint subtype, gpointer item );
578 static void trw_layer_drag_drop_request ( VikTrwLayer *vtl_src, VikTrwLayer *vtl_dest, GtkTreeIter *src_item_iter, GtkTreePath *dest_path );
579 static gboolean trw_layer_select_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp, tool_ed_t *t );
580 static gboolean trw_layer_select_move ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp, tool_ed_t *t );
581 static gboolean trw_layer_select_release ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp, tool_ed_t *t );
582 static gboolean trw_layer_show_selected_viewport_menu ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp );
583 /* End Layer Interface function definitions */
584
585 VikLayerInterface vik_trw_layer_interface = {
586   "TrackWaypoint",
587   N_("TrackWaypoint"),
588   "<control><shift>Y",
589   &viktrwlayer_pixbuf,
590
591   trw_layer_tools,
592   sizeof(trw_layer_tools) / sizeof(VikToolInterface),
593
594   trw_layer_params,
595   NUM_PARAMS,
596   params_groups, /* params_groups */
597   sizeof(params_groups)/sizeof(params_groups[0]),    /* number of groups */
598
599   VIK_MENU_ITEM_ALL,
600
601   (VikLayerFuncCreate)                  trw_layer_create,
602   (VikLayerFuncRealize)                 trw_layer_realize,
603   (VikLayerFuncPostRead)                trw_layer_post_read,
604   (VikLayerFuncFree)                    trw_layer_free,
605
606   (VikLayerFuncProperties)              NULL,
607   (VikLayerFuncDraw)                    trw_layer_draw,
608   (VikLayerFuncChangeCoordMode)         trw_layer_change_coord_mode,
609
610   (VikLayerFuncSetMenuItemsSelection)   trw_layer_set_menu_selection,
611   (VikLayerFuncGetMenuItemsSelection)   trw_layer_get_menu_selection,
612
613   (VikLayerFuncAddMenuItems)            trw_layer_add_menu_items,
614   (VikLayerFuncSublayerAddMenuItems)    trw_layer_sublayer_add_menu_items,
615
616   (VikLayerFuncSublayerRenameRequest)   trw_layer_sublayer_rename_request,
617   (VikLayerFuncSublayerToggleVisible)   trw_layer_sublayer_toggle_visible,
618   (VikLayerFuncSublayerTooltip)         trw_layer_sublayer_tooltip,
619   (VikLayerFuncLayerTooltip)            trw_layer_layer_tooltip,
620   (VikLayerFuncLayerSelected)           trw_layer_selected,
621
622   (VikLayerFuncMarshall)                trw_layer_marshall,
623   (VikLayerFuncUnmarshall)              trw_layer_unmarshall,
624
625   (VikLayerFuncSetParam)                trw_layer_set_param,
626   (VikLayerFuncGetParam)                trw_layer_get_param,
627
628   (VikLayerFuncReadFileData)            a_gpspoint_read_file,
629   (VikLayerFuncWriteFileData)           a_gpspoint_write_file,
630
631   (VikLayerFuncDeleteItem)              trw_layer_del_item,
632   (VikLayerFuncCutItem)                 trw_layer_cut_item,
633   (VikLayerFuncCopyItem)                trw_layer_copy_item,
634   (VikLayerFuncPasteItem)               trw_layer_paste_item,
635   (VikLayerFuncFreeCopiedItem)          trw_layer_free_copied_item,
636   
637   (VikLayerFuncDragDropRequest)         trw_layer_drag_drop_request,
638
639   (VikLayerFuncSelectClick)             trw_layer_select_click,
640   (VikLayerFuncSelectMove)              trw_layer_select_move,
641   (VikLayerFuncSelectRelease)           trw_layer_select_release,
642   (VikLayerFuncSelectedViewportMenu)    trw_layer_show_selected_viewport_menu,
643 };
644
645 GType vik_trw_layer_get_type ()
646 {
647   static GType vtl_type = 0;
648
649   if (!vtl_type)
650   {
651     static const GTypeInfo vtl_info =
652     {
653       sizeof (VikTrwLayerClass),
654       NULL, /* base_init */
655       NULL, /* base_finalize */
656       NULL, /* class init */
657       NULL, /* class_finalize */
658       NULL, /* class_data */
659       sizeof (VikTrwLayer),
660       0,
661       NULL /* instance init */
662     };
663     vtl_type = g_type_register_static ( VIK_LAYER_TYPE, "VikTrwLayer", &vtl_info, 0 );
664   }
665
666   return vtl_type;
667 }
668
669 static void trw_layer_del_item ( VikTrwLayer *vtl, gint subtype, gpointer sublayer )
670 {
671   static gpointer pass_along[6];
672   if (!sublayer) {
673     return;
674   }
675   
676   pass_along[0] = vtl;
677   pass_along[1] = NULL;
678   pass_along[2] = GINT_TO_POINTER (subtype);
679   pass_along[3] = sublayer;
680   pass_along[4] = GINT_TO_POINTER (1); // Confirm delete request
681   pass_along[5] = NULL;
682
683   trw_layer_delete_item ( pass_along );
684 }
685
686 static void trw_layer_cut_item ( VikTrwLayer *vtl, gint subtype, gpointer sublayer )
687 {
688   static gpointer pass_along[6];
689   if (!sublayer) {
690     return;
691   }
692
693   pass_along[0] = vtl;
694   pass_along[1] = NULL;
695   pass_along[2] = GINT_TO_POINTER (subtype);
696   pass_along[3] = sublayer;
697   pass_along[4] = GINT_TO_POINTER (0); // No delete confirmation needed for auto delete
698   pass_along[5] = NULL;
699
700   trw_layer_copy_item_cb(pass_along);
701   trw_layer_cut_item_cb(pass_along);
702 }
703
704 static void trw_layer_copy_item_cb ( gpointer pass_along[6])
705 {
706   VikTrwLayer *vtl = VIK_TRW_LAYER(pass_along[0]);
707   gint subtype = GPOINTER_TO_INT (pass_along[2]);
708   gpointer * sublayer = pass_along[3];
709   guint8 *data = NULL;
710   guint len;
711
712   trw_layer_copy_item( vtl, subtype, sublayer, &data, &len);
713
714   if (data) {
715     const gchar* name;
716     if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT ) {
717       VikWaypoint *wp = g_hash_table_lookup ( vtl->waypoints, sublayer);
718       if ( wp && wp->name )
719         name = wp->name;
720       else
721         name = NULL; // Broken :(
722     }
723     else if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK ) {
724       VikTrack *trk = g_hash_table_lookup ( vtl->tracks, sublayer);
725       if ( trk && trk->name )
726         name = trk->name;
727       else
728         name = NULL; // Broken :(
729     }
730     else {
731       VikTrack *trk = g_hash_table_lookup ( vtl->routes, sublayer);
732       if ( trk && trk->name )
733         name = trk->name;
734       else
735         name = NULL; // Broken :(
736     }
737
738     a_clipboard_copy( VIK_CLIPBOARD_DATA_SUBLAYER, VIK_LAYER_TRW,
739                       subtype, len, name, data);
740   }
741 }
742
743 static void trw_layer_cut_item_cb ( gpointer pass_along[6])
744 {
745   trw_layer_copy_item_cb(pass_along);
746   pass_along[4] = GINT_TO_POINTER (0); // Never need to confirm automatic delete
747   trw_layer_delete_item(pass_along);
748 }
749
750 static void trw_layer_paste_item_cb ( gpointer pass_along[6])
751 {
752   // Slightly cheating method, routing via the panels capability
753   a_clipboard_paste (VIK_LAYERS_PANEL(pass_along[1]));
754 }
755
756 static void trw_layer_copy_item ( VikTrwLayer *vtl, gint subtype, gpointer sublayer, guint8 **item, guint *len )
757 {
758   guint8 *id;
759   guint il;
760
761   if (!sublayer) {
762     *item = NULL;
763     return;
764   }
765
766   GByteArray *ba = g_byte_array_new ();
767
768   if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT ) {
769     vik_waypoint_marshall ( g_hash_table_lookup ( vtl->waypoints, sublayer ), &id, &il );
770   } else if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK ) {
771     vik_track_marshall ( g_hash_table_lookup ( vtl->tracks, sublayer ), &id, &il );
772   } else {
773     vik_track_marshall ( g_hash_table_lookup ( vtl->routes, sublayer ), &id, &il );
774   }
775
776   g_byte_array_append ( ba, id, il );
777
778   g_free(id);
779
780   *len = ba->len;
781   *item = ba->data;
782 }
783
784 static gboolean trw_layer_paste_item ( VikTrwLayer *vtl, gint subtype, guint8 *item, guint len )
785 {
786   if ( !item )
787     return FALSE;
788
789   gchar *name;
790
791   if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT )
792   {
793     VikWaypoint *w;
794
795     w = vik_waypoint_unmarshall ( item, len );
796     // When copying - we'll create a new name based on the original
797     name = trw_layer_new_unique_sublayer_name(vtl, VIK_TRW_LAYER_SUBLAYER_WAYPOINT, w->name);
798     vik_trw_layer_add_waypoint ( vtl, name, w );
799     waypoint_convert (NULL, w, &vtl->coord_mode);
800
801     // Consider if redraw necessary for the new item
802     if ( vtl->vl.visible && vtl->waypoints_visible && w->visible )
803       vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
804     return TRUE;
805   }
806   if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
807   {
808     VikTrack *t;
809
810     t = vik_track_unmarshall ( item, len );
811     // When copying - we'll create a new name based on the original
812     name = trw_layer_new_unique_sublayer_name(vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, t->name);
813     vik_trw_layer_add_track ( vtl, name, t );
814     vik_track_convert (t, vtl->coord_mode);
815
816     // Consider if redraw necessary for the new item
817     if ( vtl->vl.visible && vtl->tracks_visible && t->visible )
818       vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
819     return TRUE;
820   }
821   if ( subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE )
822   {
823     VikTrack *t;
824
825     t = vik_track_unmarshall ( item, len );
826     // When copying - we'll create a new name based on the original
827     name = trw_layer_new_unique_sublayer_name(vtl, VIK_TRW_LAYER_SUBLAYER_ROUTE, t->name);
828     vik_trw_layer_add_route ( vtl, name, t );
829     vik_track_convert (t, vtl->coord_mode);
830
831     // Consider if redraw necessary for the new item
832     if ( vtl->vl.visible && vtl->routes_visible && t->visible )
833       vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
834     return TRUE;
835   }
836   return FALSE;
837 }
838
839 static void trw_layer_free_copied_item ( gint subtype, gpointer item )
840 {
841   if (item) {
842     g_free(item);
843   }
844 }
845
846 static gboolean trw_layer_set_param ( VikTrwLayer *vtl, guint16 id, VikLayerParamData data, VikViewport *vp, gboolean is_file_operation )
847 {
848   switch ( id )
849   {
850     case PARAM_TV: vtl->tracks_visible = data.b; break;
851     case PARAM_WV: vtl->waypoints_visible = data.b; break;
852     case PARAM_RV: vtl->routes_visible = data.b; break;
853     case PARAM_DM: vtl->drawmode = data.u; break;
854     case PARAM_DP: vtl->drawpoints = data.b; break;
855     case PARAM_DPS:
856       if ( data.u >= MIN_POINT_SIZE && data.u <= MAX_POINT_SIZE )
857         vtl->drawpoints_size = data.u;
858       break;
859     case PARAM_DE: vtl->drawelevation = data.b; break;
860     case PARAM_DS: vtl->drawstops = data.b; break;
861     case PARAM_DL: vtl->drawlines = data.b; break;
862     case PARAM_DD: vtl->drawdirections = data.b; break;
863     case PARAM_DDS:
864       if ( data.u >= MIN_ARROW_SIZE && data.u <= MAX_ARROW_SIZE )
865         vtl->drawdirections_size = data.u;
866       break;
867     case PARAM_SL: if ( data.u >= MIN_STOP_LENGTH && data.u <= MAX_STOP_LENGTH )
868                      vtl->stop_length = data.u;
869                    break;
870     case PARAM_EF: if ( data.u >= 1 && data.u <= 100 )
871                      vtl->elevation_factor = data.u;
872                    break;
873     case PARAM_LT: if ( data.u > 0 && data.u < 15 && data.u != vtl->line_thickness )
874                    {
875                      vtl->line_thickness = data.u;
876                      trw_layer_new_track_gcs ( vtl, vp );
877                    }
878                    break;
879     case PARAM_BLT: if ( data.u >= 0 && data.u <= 8 && data.u != vtl->bg_line_thickness )
880                    {
881                      vtl->bg_line_thickness = data.u;
882                      trw_layer_new_track_gcs ( vtl, vp );
883                    }
884                    break;
885     case PARAM_TBGC: gdk_gc_set_rgb_fg_color(vtl->track_bg_gc, &(data.c)); break;
886     case PARAM_TDSF: vtl->track_draw_speed_factor = data.d; break;
887     case PARAM_DLA: vtl->drawlabels = data.b; break;
888     case PARAM_DI: vtl->drawimages = data.b; break;
889     case PARAM_IS: if ( data.u != vtl->image_size )
890       {
891         vtl->image_size = data.u;
892         g_list_foreach ( vtl->image_cache->head, (GFunc) cached_pixbuf_free, NULL );
893         g_queue_free ( vtl->image_cache );
894         vtl->image_cache = g_queue_new ();
895       }
896       break;
897     case PARAM_IA: vtl->image_alpha = data.u; break;
898     case PARAM_ICS: vtl->image_cache_size = data.u;
899       while ( vtl->image_cache->length > vtl->image_cache_size ) /* if shrinking cache_size, free pixbuf ASAP */
900           cached_pixbuf_free ( g_queue_pop_tail ( vtl->image_cache ) );
901       break;
902     case PARAM_WPC: gdk_gc_set_rgb_fg_color(vtl->waypoint_gc, &(data.c)); break;
903     case PARAM_WPTC: gdk_gc_set_rgb_fg_color(vtl->waypoint_text_gc, &(data.c)); break;
904     case PARAM_WPBC: gdk_gc_set_rgb_fg_color(vtl->waypoint_bg_gc, &(data.c)); break;
905     case PARAM_WPBA: gdk_gc_set_function(vtl->waypoint_bg_gc, data.b ? GDK_AND : GDK_COPY ); break;
906     case PARAM_WPSYM: if ( data.u < WP_NUM_SYMBOLS ) vtl->wp_symbol = data.u; break;
907     case PARAM_WPSIZE: if ( data.u > 0 && data.u <= 64 ) vtl->wp_size = data.u; break;
908     case PARAM_WPSYMS: vtl->wp_draw_symbols = data.b; break;
909     case PARAM_WPFONTSIZE: if ( data.u < FS_NUM_SIZES ) vtl->wp_font_size = data.u; break;
910   }
911   return TRUE;
912 }
913
914 static VikLayerParamData trw_layer_get_param ( VikTrwLayer *vtl, guint16 id, gboolean is_file_operation )
915 {
916   VikLayerParamData rv;
917   switch ( id )
918   {
919     case PARAM_TV: rv.b = vtl->tracks_visible; break;
920     case PARAM_WV: rv.b = vtl->waypoints_visible; break;
921     case PARAM_RV: rv.b = vtl->routes_visible; break;
922     case PARAM_DM: rv.u = vtl->drawmode; break;
923     case PARAM_DP: rv.b = vtl->drawpoints; break;
924     case PARAM_DPS: rv.u = vtl->drawpoints_size; break;
925     case PARAM_DE: rv.b = vtl->drawelevation; break;
926     case PARAM_EF: rv.u = vtl->elevation_factor; break;
927     case PARAM_DS: rv.b = vtl->drawstops; break;
928     case PARAM_SL: rv.u = vtl->stop_length; break;
929     case PARAM_DL: rv.b = vtl->drawlines; break;
930     case PARAM_DD: rv.b = vtl->drawdirections; break;
931     case PARAM_DDS: rv.u = vtl->drawdirections_size; break;
932     case PARAM_LT: rv.u = vtl->line_thickness; break;
933     case PARAM_BLT: rv.u = vtl->bg_line_thickness; break;
934     case PARAM_DLA: rv.b = vtl->drawlabels; break;
935     case PARAM_DI: rv.b = vtl->drawimages; break;
936     case PARAM_TBGC: vik_gc_get_fg_color(vtl->track_bg_gc, &(rv.c)); break;
937     case PARAM_TDSF: rv.d = vtl->track_draw_speed_factor; break;
938     case PARAM_IS: rv.u = vtl->image_size; break;
939     case PARAM_IA: rv.u = vtl->image_alpha; break;
940     case PARAM_ICS: rv.u = vtl->image_cache_size; break;
941     case PARAM_WPC: vik_gc_get_fg_color(vtl->waypoint_gc, &(rv.c)); break;
942     case PARAM_WPTC: vik_gc_get_fg_color(vtl->waypoint_text_gc, &(rv.c)); break;
943     case PARAM_WPBC: vik_gc_get_fg_color(vtl->waypoint_bg_gc, &(rv.c)); break;
944     case PARAM_WPBA: rv.b = (vik_gc_get_function(vtl->waypoint_bg_gc)==GDK_AND); break;
945     case PARAM_WPSYM: rv.u = vtl->wp_symbol; break;
946     case PARAM_WPSIZE: rv.u = vtl->wp_size; break;
947     case PARAM_WPSYMS: rv.b = vtl->wp_draw_symbols; break;
948     case PARAM_WPFONTSIZE: rv.u = vtl->wp_font_size; break;
949   }
950   return rv;
951 }
952
953 static void trw_layer_marshall( VikTrwLayer *vtl, guint8 **data, gint *len )
954 {
955   guint8 *pd;
956   gint pl;
957
958   *data = NULL;
959
960   // Use byte arrays to store sublayer data
961   // much like done elsewhere e.g. vik_layer_marshall_params()
962   GByteArray *ba = g_byte_array_new ( );
963
964   guint8 *sl_data;
965   guint sl_len;
966
967   guint object_length;
968   guint subtype;
969   // store:
970   // the length of the item
971   // the sublayer type of item
972   // the the actual item
973 #define tlm_append(object_pointer, size, type)  \
974   subtype = (type); \
975   object_length = (size); \
976   g_byte_array_append ( ba, (guint8 *)&object_length, sizeof(object_length) ); \
977   g_byte_array_append ( ba, (guint8 *)&subtype, sizeof(subtype) ); \
978   g_byte_array_append ( ba, (object_pointer), object_length );
979
980   // Layer parameters first
981   vik_layer_marshall_params(VIK_LAYER(vtl), &pd, &pl);
982   g_byte_array_append ( ba, (guint8 *)&pl, sizeof(pl) ); \
983   g_byte_array_append ( ba, pd, pl );
984   g_free ( pd );
985
986   // Now sublayer data
987   GHashTableIter iter;
988   gpointer key, value;
989
990   // Waypoints
991   g_hash_table_iter_init ( &iter, vtl->waypoints );
992   while ( g_hash_table_iter_next (&iter, &key, &value) ) {
993     vik_waypoint_marshall ( VIK_WAYPOINT(value), &sl_data, &sl_len );
994     tlm_append ( sl_data, sl_len, VIK_TRW_LAYER_SUBLAYER_WAYPOINT );
995     g_free ( sl_data );
996   }
997
998   // Tracks
999   g_hash_table_iter_init ( &iter, vtl->tracks );
1000   while ( g_hash_table_iter_next (&iter, &key, &value) ) {
1001     vik_track_marshall ( VIK_TRACK(value), &sl_data, &sl_len );
1002     tlm_append ( sl_data, sl_len, VIK_TRW_LAYER_SUBLAYER_TRACK );
1003     g_free ( sl_data );
1004   }
1005
1006   // Routes
1007   g_hash_table_iter_init ( &iter, vtl->routes );
1008   while ( g_hash_table_iter_next (&iter, &key, &value) ) {
1009     vik_track_marshall ( VIK_TRACK(value), &sl_data, &sl_len );
1010     tlm_append ( sl_data, sl_len, VIK_TRW_LAYER_SUBLAYER_ROUTE );
1011     g_free ( sl_data );
1012   }
1013
1014 #undef tlm_append
1015
1016   *data = ba->data;
1017   *len = ba->len;
1018 }
1019
1020 static VikTrwLayer *trw_layer_unmarshall( guint8 *data, gint len, VikViewport *vvp )
1021 {
1022   VikTrwLayer *vtl = VIK_TRW_LAYER(vik_layer_create ( VIK_LAYER_TRW, vvp, NULL, FALSE ));
1023   gint pl;
1024   gint consumed_length;
1025
1026   // First the overall layer parameters
1027   memcpy(&pl, data, sizeof(pl));
1028   data += sizeof(pl);
1029   vik_layer_unmarshall_params ( VIK_LAYER(vtl), data, pl, vvp );
1030   data += pl;
1031
1032   consumed_length = pl;
1033   const gint sizeof_len_and_subtype = sizeof(gint) + sizeof(gint);
1034
1035 #define tlm_size (*(gint *)data)
1036   // See marshalling above for order of how this is written
1037 #define tlm_next \
1038   data += sizeof_len_and_subtype + tlm_size;
1039
1040   // Now the individual sublayers:
1041
1042   while ( *data && consumed_length < len ) {
1043     // Normally four extra bytes at the end of the datastream
1044     //  (since it's a GByteArray and that's where it's length is stored)
1045     //  So only attempt read when there's an actual block of sublayer data
1046     if ( consumed_length + tlm_size < len ) {
1047
1048       // Reuse pl to read the subtype from the data stream
1049       memcpy(&pl, data+sizeof(gint), sizeof(pl));
1050
1051       if ( pl == VIK_TRW_LAYER_SUBLAYER_TRACK ) {
1052         VikTrack *trk = vik_track_unmarshall ( data + sizeof_len_and_subtype, 0 );
1053         gchar *name = g_strdup ( trk->name );
1054         vik_trw_layer_add_track ( vtl, name, trk );
1055         g_free ( name );
1056       }
1057       if ( pl == VIK_TRW_LAYER_SUBLAYER_WAYPOINT ) {
1058         VikWaypoint *wp = vik_waypoint_unmarshall ( data + sizeof_len_and_subtype, 0 );
1059         gchar *name = g_strdup ( wp->name );
1060         vik_trw_layer_add_waypoint ( vtl, name, wp );
1061         g_free ( name );
1062       }
1063       if ( pl == VIK_TRW_LAYER_SUBLAYER_ROUTE ) {
1064         VikTrack *trk = vik_track_unmarshall ( data + sizeof_len_and_subtype, 0 );
1065         gchar *name = g_strdup ( trk->name );
1066         vik_trw_layer_add_route ( vtl, name, trk );
1067         g_free ( name );
1068       }
1069     }
1070     consumed_length += tlm_size + sizeof_len_and_subtype;
1071     tlm_next;
1072   }
1073   //g_debug ("consumed_length %d vs len %d", consumed_length, len);
1074
1075   return vtl;
1076 }
1077
1078 // Keep interesting hash function at least visible
1079 /*
1080 static guint strcase_hash(gconstpointer v)
1081 {
1082   // 31 bit hash function
1083   int i;
1084   const gchar *t = v;
1085   gchar s[128];   // malloc is too slow for reading big files
1086   gchar *p = s;
1087
1088   for (i = 0; (i < (sizeof(s)- 1)) && t[i]; i++)
1089       p[i] = toupper(t[i]);
1090   p[i] = '\0';
1091
1092   p = s;
1093   guint32 h = *p;
1094   if (h) {
1095     for (p += 1; *p != '\0'; p++)
1096       h = (h << 5) - h + *p;
1097   }
1098
1099   return h;  
1100 }
1101 */
1102
1103 static VikTrwLayer* trw_layer_new ( gint drawmode )
1104 {
1105   if (trw_layer_params[PARAM_DM].widget_data == NULL)
1106     trw_layer_params[PARAM_DM].widget_data = str_array_to_glist(params_drawmodes);
1107   if (trw_layer_params[PARAM_WPSYM].widget_data == NULL)
1108     trw_layer_params[PARAM_WPSYM].widget_data = str_array_to_glist(params_wpsymbols);
1109
1110   VikTrwLayer *rv = VIK_TRW_LAYER ( g_object_new ( VIK_TRW_LAYER_TYPE, NULL ) );
1111   vik_layer_set_type ( VIK_LAYER(rv), VIK_LAYER_TRW );
1112
1113   // It's not entirely clear the benefits of hash tables usage here - possibly the simplicity of first implementation for unique names
1114   // Now with the name of the item stored as part of the item - these tables are effectively straightforward lists
1115
1116   // For this reworking I've choosen to keep the use of hash tables since for the expected data sizes
1117   // - even many hundreds of waypoints and tracks is quite small in the grand scheme of things,
1118   //  and with normal PC processing capabilities - it has negligibile performance impact
1119   // This also minimized the amount of rework - as the management of the hash tables already exists.
1120
1121   // The hash tables are indexed by simple integers acting as a UUID hash, which again shouldn't affect performance much
1122   //   we have to maintain a uniqueness (as before when multiple names where not allowed),
1123   //   this is to ensure it refers to the same item in the data structures used on the viewport and on the layers panel
1124
1125   rv->waypoints = g_hash_table_new_full ( g_direct_hash, g_direct_equal, NULL, (GDestroyNotify) vik_waypoint_free );
1126   rv->waypoints_iters = g_hash_table_new_full ( g_direct_hash, g_direct_equal, NULL, g_free );
1127   rv->tracks = g_hash_table_new_full ( g_direct_hash, g_direct_equal, NULL, (GDestroyNotify) vik_track_free );
1128   rv->tracks_iters = g_hash_table_new_full ( g_direct_hash, g_direct_equal, NULL, g_free );
1129   rv->routes = g_hash_table_new_full ( g_direct_hash, g_direct_equal, NULL, (GDestroyNotify) vik_track_free );
1130   rv->routes_iters = g_hash_table_new_full ( g_direct_hash, g_direct_equal, NULL, g_free );
1131
1132   // Default values
1133   rv->waypoints_visible = rv->tracks_visible = rv->routes_visible = TRUE;
1134   rv->drawmode = drawmode;
1135   rv->drawpoints = TRUE;
1136   rv->drawpoints_size = MIN_POINT_SIZE;
1137   rv->drawdirections_size = 5;
1138   rv->elevation_factor = 30;
1139   rv->stop_length = 60;
1140   rv->drawlines = TRUE;
1141   rv->track_draw_speed_factor = 30.0;
1142   rv->line_thickness = 1;
1143
1144   rv->draw_sync_done = TRUE;
1145   rv->draw_sync_do = TRUE;
1146
1147   rv->image_cache = g_queue_new();
1148   rv->image_size = 64;
1149   rv->image_alpha = 255;
1150   rv->image_cache_size = 300;
1151   rv->drawimages = TRUE;
1152   rv->drawlabels = TRUE;
1153   // Everything else is 0, FALSE or NULL
1154
1155   return rv;
1156 }
1157
1158
1159 static void trw_layer_free ( VikTrwLayer *trwlayer )
1160 {
1161   g_hash_table_destroy(trwlayer->waypoints);
1162   g_hash_table_destroy(trwlayer->tracks);
1163
1164   /* ODC: replace with GArray */
1165   trw_layer_free_track_gcs ( trwlayer );
1166
1167   if ( trwlayer->wp_right_click_menu )
1168     g_object_ref_sink ( G_OBJECT(trwlayer->wp_right_click_menu) );
1169
1170   if ( trwlayer->track_right_click_menu )
1171     gtk_object_sink ( GTK_OBJECT(trwlayer->track_right_click_menu) );
1172
1173   if ( trwlayer->wplabellayout != NULL)
1174     g_object_unref ( G_OBJECT ( trwlayer->wplabellayout ) );
1175
1176   if ( trwlayer->waypoint_gc != NULL )
1177     g_object_unref ( G_OBJECT ( trwlayer->waypoint_gc ) );
1178
1179   if ( trwlayer->waypoint_text_gc != NULL )
1180     g_object_unref ( G_OBJECT ( trwlayer->waypoint_text_gc ) );
1181
1182   if ( trwlayer->waypoint_bg_gc != NULL )
1183     g_object_unref ( G_OBJECT ( trwlayer->waypoint_bg_gc ) );
1184
1185   if ( trwlayer->tpwin != NULL )
1186     gtk_widget_destroy ( GTK_WIDGET(trwlayer->tpwin) );
1187
1188   g_list_foreach ( trwlayer->image_cache->head, (GFunc) cached_pixbuf_free, NULL );
1189   g_queue_free ( trwlayer->image_cache );
1190 }
1191
1192 static void init_drawing_params ( struct DrawingParams *dp, VikTrwLayer *vtl, VikViewport *vp )
1193 {
1194   dp->vtl = vtl;
1195   dp->vp = vp;
1196   dp->xmpp = vik_viewport_get_xmpp ( vp );
1197   dp->ympp = vik_viewport_get_ympp ( vp );
1198   dp->width = vik_viewport_get_width ( vp );
1199   dp->height = vik_viewport_get_height ( vp );
1200   dp->cc = vtl->drawdirections_size*cos(45 * DEG2RAD); // Calculate once per vtl update - even if not used
1201   dp->ss = vtl->drawdirections_size*sin(45 * DEG2RAD); // Calculate once per vtl update - even if not used
1202
1203   dp->center = vik_viewport_get_center ( vp );
1204   dp->one_zone = vik_viewport_is_one_zone ( vp ); /* false if some other projection besides UTM */
1205   dp->lat_lon = vik_viewport_get_coord_mode ( vp ) == VIK_COORD_LATLON;
1206
1207   if ( dp->one_zone )
1208   {
1209     gint w2, h2;
1210     w2 = dp->xmpp * (dp->width / 2) + 1600 / dp->xmpp; 
1211     h2 = dp->ympp * (dp->height / 2) + 1600 / dp->ympp;
1212     /* leniency -- for tracks. Obviously for waypoints this SHOULD be a lot smaller */
1213  
1214     dp->ce1 = dp->center->east_west-w2; 
1215     dp->ce2 = dp->center->east_west+w2;
1216     dp->cn1 = dp->center->north_south-h2;
1217     dp->cn2 = dp->center->north_south+h2;
1218   } else if ( dp->lat_lon ) {
1219     VikCoord upperleft, bottomright;
1220     /* quick & dirty calculation; really want to check all corners due to lat/lon smaller at top in northern hemisphere */
1221     /* this also DOESN'T WORK if you are crossing 180/-180 lon. I don't plan to in the near future...  */
1222     vik_viewport_screen_to_coord ( vp, -500, -500, &upperleft );
1223     vik_viewport_screen_to_coord ( vp, dp->width+500, dp->height+500, &bottomright );
1224     dp->ce1 = upperleft.east_west;
1225     dp->ce2 = bottomright.east_west;
1226     dp->cn1 = bottomright.north_south;
1227     dp->cn2 = upperleft.north_south;
1228   }
1229 }
1230
1231 /*
1232  * Determine the colour of the trackpoint (and/or trackline) relative to the average speed
1233  * Here a simple traffic like light colour system is used:
1234  *  . slow points are red
1235  *  . average is yellow
1236  *  . fast points are green
1237  */
1238 static gint track_section_colour_by_speed ( VikTrwLayer *vtl, VikTrackpoint *tp1, VikTrackpoint *tp2, gdouble average_speed, gdouble low_speed, gdouble high_speed )
1239 {
1240   gdouble rv = 0;
1241   if ( tp1->has_timestamp && tp2->has_timestamp ) {
1242     if ( average_speed > 0 ) {
1243       rv = ( vik_coord_diff ( &(tp1->coord), &(tp2->coord) ) / (tp1->timestamp - tp2->timestamp) );
1244       if ( rv < low_speed )
1245         return VIK_TRW_LAYER_TRACK_GC_SLOW;
1246       else if ( rv > high_speed )
1247         return VIK_TRW_LAYER_TRACK_GC_FAST;
1248       else
1249         return VIK_TRW_LAYER_TRACK_GC_AVER;
1250     }
1251   }
1252   return VIK_TRW_LAYER_TRACK_GC_BLACK;
1253 }
1254
1255 static void draw_utm_skip_insignia ( VikViewport *vvp, GdkGC *gc, gint x, gint y )
1256 {
1257   vik_viewport_draw_line ( vvp, gc, x+5, y, x-5, y );
1258   vik_viewport_draw_line ( vvp, gc, x, y+5, x, y-5 );
1259   vik_viewport_draw_line ( vvp, gc, x+5, y+5, x-5, y-5 );
1260   vik_viewport_draw_line ( vvp, gc, x+5, y-5, x-5, y+5 );
1261 }
1262
1263 static void trw_layer_draw_track ( const gchar *name, VikTrack *track, struct DrawingParams *dp, gboolean draw_track_outline )
1264 {
1265   /* TODO: this function is a mess, get rid of any redundancy */
1266   GList *list = track->trackpoints;
1267   GdkGC *main_gc;
1268   gboolean useoldvals = TRUE;
1269
1270   gboolean drawpoints;
1271   gboolean drawstops;
1272   gboolean drawelevation;
1273   gdouble min_alt, max_alt, alt_diff = 0;
1274
1275   const guint8 tp_size_reg = dp->vtl->drawpoints_size;
1276   const guint8 tp_size_cur = dp->vtl->drawpoints_size*2;
1277   guint8 tp_size;
1278
1279   if ( dp->vtl->drawelevation )
1280   {
1281     /* assume if it has elevation at the beginning, it has it throughout. not ness a true good assumption */
1282     if ( ( drawelevation = vik_track_get_minmax_alt ( track, &min_alt, &max_alt ) ) )
1283       alt_diff = max_alt - min_alt;
1284   }
1285
1286   if ( ! track->visible )
1287     return;
1288
1289   /* admittedly this is not an efficient way to do it because we go through the whole GC thing all over... */
1290   if ( dp->vtl->bg_line_thickness && !draw_track_outline )
1291     trw_layer_draw_track ( name, track, dp, TRUE );
1292
1293   if ( draw_track_outline )
1294     drawpoints = drawstops = FALSE;
1295   else {
1296     drawpoints = dp->vtl->drawpoints;
1297     drawstops = dp->vtl->drawstops;
1298   }
1299
1300   gboolean drawing_highlight = FALSE;
1301   /* Current track - used for creation */
1302   if ( track == dp->vtl->current_track )
1303     main_gc = dp->vtl->current_track_gc;
1304   else {
1305     if ( vik_viewport_get_draw_highlight ( dp->vp ) ) {
1306       /* Draw all tracks of the layer in special colour */
1307       /* if track is member of selected layer or is the current selected track
1308          then draw in the highlight colour.
1309          NB this supercedes the drawmode */
1310       if ( dp->vtl && ( ( dp->vtl == vik_window_get_selected_trw_layer ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(dp->vtl) ) ) ||
1311                         ( !track->is_route && ( dp->vtl->tracks == vik_window_get_selected_tracks ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(dp->vtl) ) ) ) ||
1312                         ( track->is_route && ( dp->vtl->routes == vik_window_get_selected_tracks ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(dp->vtl) ) ) ) ||
1313                         track == vik_window_get_selected_track ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(dp->vtl) ) ) ) {
1314         main_gc = vik_viewport_get_gc_highlight (dp->vp);
1315         drawing_highlight = TRUE;
1316       }
1317     }
1318     if ( !drawing_highlight ) {
1319       // Still need to figure out the gc according to the drawing mode:
1320       switch ( dp->vtl->drawmode ) {
1321       case DRAWMODE_BY_TRACK:
1322         if ( dp->vtl->track_1color_gc )
1323           g_object_unref ( dp->vtl->track_1color_gc );
1324         dp->vtl->track_1color_gc = vik_viewport_new_gc_from_color ( dp->vp, &track->color, dp->vtl->line_thickness );
1325         main_gc = dp->vtl->track_1color_gc;
1326         break;
1327       default:
1328         // Mostly for DRAWMODE_ALL_BLACK
1329         // but includes DRAWMODE_BY_SPEED, main_gc is set later on as necessary
1330         main_gc = g_array_index(dp->vtl->track_gc, GdkGC *, VIK_TRW_LAYER_TRACK_GC_BLACK);
1331         break;
1332       }
1333     }
1334   }
1335
1336   if (list) {
1337     int x, y, oldx, oldy;
1338     VikTrackpoint *tp = VIK_TRACKPOINT(list->data);
1339   
1340     tp_size = (list == dp->vtl->current_tpl) ? tp_size_cur : tp_size_reg;
1341
1342     vik_viewport_coord_to_screen ( dp->vp, &(tp->coord), &x, &y );
1343
1344     // Draw the first point as something a bit different from the normal points
1345     // ATM it's slightly bigger and a triangle
1346     if ( drawpoints ) {
1347       GdkPoint trian[3] = { { x, y-(3*tp_size) }, { x-(2*tp_size), y+(2*tp_size) }, {x+(2*tp_size), y+(2*tp_size)} };
1348       vik_viewport_draw_polygon ( dp->vp, main_gc, TRUE, trian, 3 );
1349     }
1350
1351     oldx = x;
1352     oldy = y;
1353
1354     gdouble average_speed = 0.0;
1355     gdouble low_speed = 0.0;
1356     gdouble high_speed = 0.0;
1357     // If necessary calculate these values - which is done only once per track redraw
1358     if ( dp->vtl->drawmode == DRAWMODE_BY_SPEED ) {
1359       // the percentage factor away from the average speed determines transistions between the levels
1360       average_speed = vik_track_get_average_speed_moving(track, dp->vtl->stop_length);
1361       low_speed = average_speed - (average_speed*(dp->vtl->track_draw_speed_factor/100.0));
1362       high_speed = average_speed + (average_speed*(dp->vtl->track_draw_speed_factor/100.0));
1363     }
1364
1365     while ((list = g_list_next(list)))
1366     {
1367       tp = VIK_TRACKPOINT(list->data);
1368       tp_size = (list == dp->vtl->current_tpl) ? tp_size_cur : tp_size_reg;
1369
1370       /* check some stuff -- but only if we're in UTM and there's only ONE ZONE; or lat lon */
1371       if ( (!dp->one_zone && !dp->lat_lon) ||     /* UTM & zones; do everything */
1372              ( ((!dp->one_zone) || tp->coord.utm_zone == dp->center->utm_zone) &&   /* only check zones if UTM & one_zone */
1373              tp->coord.east_west < dp->ce2 && tp->coord.east_west > dp->ce1 &&  /* both UTM and lat lon */
1374              tp->coord.north_south > dp->cn1 && tp->coord.north_south < dp->cn2 ) )
1375       {
1376         vik_viewport_coord_to_screen ( dp->vp, &(tp->coord), &x, &y );
1377
1378         /*
1379          * If points are the same in display coordinates, don't draw.
1380          */
1381         if ( useoldvals && x == oldx && y == oldy )
1382         {
1383           // Still need to process points to ensure 'stops' are drawn if required
1384           if ( drawstops && drawpoints && ! draw_track_outline && list->next &&
1385                (VIK_TRACKPOINT(list->next->data)->timestamp - VIK_TRACKPOINT(list->data)->timestamp > dp->vtl->stop_length) )
1386             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 );
1387
1388           goto skip;
1389         }
1390
1391         VikTrackpoint *tp2 = VIK_TRACKPOINT(list->prev->data);
1392         if ( drawpoints || dp->vtl->drawlines ) {
1393           // setup main_gc for both point and line drawing
1394           if ( !drawing_highlight && (dp->vtl->drawmode == DRAWMODE_BY_SPEED) ) {
1395             main_gc = g_array_index(dp->vtl->track_gc, GdkGC *, track_section_colour_by_speed ( dp->vtl, tp, tp2, average_speed, low_speed, high_speed ) );
1396           }
1397         }
1398
1399         if ( drawpoints && ! draw_track_outline )
1400         {
1401
1402           if ( list->next ) {
1403             /*
1404              * The concept of drawing stops is that a trackpoint
1405              * that is if the next trackpoint has a timestamp far into
1406              * the future, we draw a circle of 6x trackpoint size,
1407              * instead of a rectangle of 2x trackpoint size.
1408              * This is drawn first so the trackpoint will be drawn on top
1409              */
1410             /* stops */
1411             if ( drawstops && VIK_TRACKPOINT(list->next->data)->timestamp - VIK_TRACKPOINT(list->data)->timestamp > dp->vtl->stop_length )
1412               /* Stop point.  Draw 6x circle. Always in redish colour */
1413               vik_viewport_draw_arc ( dp->vp, g_array_index(dp->vtl->track_gc, GdkGC *, VIK_TRW_LAYER_TRACK_GC_STOP), TRUE, x-(3*tp_size), y-(3*tp_size), 6*tp_size, 6*tp_size, 0, 360*64 );
1414
1415             /* Regular point - draw 2x square. */
1416             vik_viewport_draw_rectangle ( dp->vp, main_gc, TRUE, x-tp_size, y-tp_size, 2*tp_size, 2*tp_size );
1417           }
1418           else
1419             /* Final point - draw 4x circle. */
1420             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 );
1421         }
1422
1423         if ((!tp->newsegment) && (dp->vtl->drawlines))
1424         {
1425
1426           /* UTM only: zone check */
1427           if ( drawpoints && dp->vtl->coord_mode == VIK_COORD_UTM && tp->coord.utm_zone != dp->center->utm_zone )
1428             draw_utm_skip_insignia (  dp->vp, main_gc, x, y);
1429
1430           if (!useoldvals)
1431             vik_viewport_coord_to_screen ( dp->vp, &(tp2->coord), &oldx, &oldy );
1432
1433           if ( draw_track_outline ) {
1434             vik_viewport_draw_line ( dp->vp, dp->vtl->track_bg_gc, oldx, oldy, x, y);
1435           }
1436           else {
1437
1438             vik_viewport_draw_line ( dp->vp, main_gc, oldx, oldy, x, y);
1439
1440             if ( dp->vtl->drawelevation && list->next && VIK_TRACKPOINT(list->next->data)->altitude != VIK_DEFAULT_ALTITUDE ) {
1441               GdkPoint tmp[4];
1442               #define FIXALTITUDE(what) ((VIK_TRACKPOINT((what))->altitude-min_alt)/alt_diff*DRAW_ELEVATION_FACTOR*dp->vtl->elevation_factor/dp->xmpp)
1443
1444               tmp[0].x = oldx;
1445               tmp[0].y = oldy;
1446               tmp[1].x = oldx;
1447               tmp[1].y = oldy-FIXALTITUDE(list->data);
1448               tmp[2].x = x;
1449               tmp[2].y = y-FIXALTITUDE(list->next->data);
1450               tmp[3].x = x;
1451               tmp[3].y = y;
1452
1453               GdkGC *tmp_gc;
1454               if ( ((oldx - x) > 0 && (oldy - y) > 0) || ((oldx - x) < 0 && (oldy - y) < 0))
1455                 tmp_gc = GTK_WIDGET(dp->vp)->style->light_gc[3];
1456               else
1457                 tmp_gc = GTK_WIDGET(dp->vp)->style->dark_gc[0];
1458               vik_viewport_draw_polygon ( dp->vp, tmp_gc, TRUE, tmp, 4);
1459
1460               vik_viewport_draw_line ( dp->vp, main_gc, oldx, oldy-FIXALTITUDE(list->data), x, y-FIXALTITUDE(list->next->data));
1461             }
1462           }
1463         }
1464
1465         if ( (!tp->newsegment) && dp->vtl->drawdirections ) {
1466           // Draw an arrow at the mid point to show the direction of the track
1467           // Code is a rework from vikwindow::draw_ruler()
1468           gint midx = (oldx + x) / 2;
1469           gint midy = (oldy + y) / 2;
1470
1471           gdouble len = sqrt ( ((midx-oldx) * (midx-oldx)) + ((midy-oldy) * (midy-oldy)) );
1472           // Avoid divide by zero and ensure at least 1 pixel big
1473           if ( len > 1 ) {
1474             gdouble dx = (oldx - midx) / len;
1475             gdouble dy = (oldy - midy) / len;
1476             vik_viewport_draw_line ( dp->vp, main_gc, midx, midy, midx + (dx * dp->cc + dy * dp->ss), midy + (dy * dp->cc - dx * dp->ss) );
1477             vik_viewport_draw_line ( dp->vp, main_gc, midx, midy, midx + (dx * dp->cc - dy * dp->ss), midy + (dy * dp->cc + dx * dp->ss) );
1478           }
1479         }
1480
1481       skip:
1482         oldx = x;
1483         oldy = y;
1484         useoldvals = TRUE;
1485       }
1486       else {
1487         if (useoldvals && dp->vtl->drawlines && (!tp->newsegment))
1488         {
1489           VikTrackpoint *tp2 = VIK_TRACKPOINT(list->prev->data);
1490           if ( dp->vtl->coord_mode != VIK_COORD_UTM || tp->coord.utm_zone == dp->center->utm_zone )
1491           {
1492             vik_viewport_coord_to_screen ( dp->vp, &(tp->coord), &x, &y );
1493
1494             if ( !drawing_highlight && (dp->vtl->drawmode == DRAWMODE_BY_SPEED) ) {
1495               main_gc = g_array_index(dp->vtl->track_gc, GdkGC *, track_section_colour_by_speed ( dp->vtl, tp, tp2, average_speed, low_speed, high_speed ));
1496             }
1497
1498             /*
1499              * If points are the same in display coordinates, don't draw.
1500              */
1501             if ( x != oldx || y != oldy )
1502               {
1503                 if ( draw_track_outline )
1504                   vik_viewport_draw_line ( dp->vp, dp->vtl->track_bg_gc, oldx, oldy, x, y);
1505                 else
1506                   vik_viewport_draw_line ( dp->vp, main_gc, oldx, oldy, x, y);
1507               }
1508           }
1509           else 
1510           {
1511             /*
1512              * If points are the same in display coordinates, don't draw.
1513              */
1514             if ( x != oldx && y != oldy )
1515               {
1516                 vik_viewport_coord_to_screen ( dp->vp, &(tp2->coord), &x, &y );
1517                 draw_utm_skip_insignia ( dp->vp, main_gc, x, y );
1518               }
1519           }
1520         }
1521         useoldvals = FALSE;
1522       }
1523     }
1524   }
1525 }
1526
1527 /* the only reason this exists is so that trw_layer_draw_track can first call itself to draw the white track background */
1528 static void trw_layer_draw_track_cb ( const gchar *name, VikTrack *track, struct DrawingParams *dp )
1529 {
1530   trw_layer_draw_track ( name, track, dp, FALSE );
1531 }
1532
1533 static void cached_pixbuf_free ( CachedPixbuf *cp )
1534 {
1535   g_object_unref ( G_OBJECT(cp->pixbuf) );
1536   g_free ( cp->image );
1537 }
1538
1539 static gint cached_pixbuf_cmp ( CachedPixbuf *cp, const gchar *name )
1540 {
1541   return strcmp ( cp->image, name );
1542 }
1543
1544 static void trw_layer_draw_waypoint ( const gchar *name, VikWaypoint *wp, struct DrawingParams *dp )
1545 {
1546   if ( wp->visible )
1547   if ( (!dp->one_zone && !dp->lat_lon) || ( ( dp->lat_lon || wp->coord.utm_zone == dp->center->utm_zone ) && 
1548              wp->coord.east_west < dp->ce2 && wp->coord.east_west > dp->ce1 && 
1549              wp->coord.north_south > dp->cn1 && wp->coord.north_south < dp->cn2 ) )
1550   {
1551     gint x, y;
1552     GdkPixbuf *sym = NULL;
1553     vik_viewport_coord_to_screen ( dp->vp, &(wp->coord), &x, &y );
1554
1555     /* if in shrunken_cache, get that. If not, get and add to shrunken_cache */
1556
1557     if ( wp->image && dp->vtl->drawimages )
1558     {
1559       GdkPixbuf *pixbuf = NULL;
1560       GList *l;
1561
1562       if ( dp->vtl->image_alpha == 0)
1563         return;
1564
1565       l = g_list_find_custom ( dp->vtl->image_cache->head, wp->image, (GCompareFunc) cached_pixbuf_cmp );
1566       if ( l )
1567         pixbuf = ((CachedPixbuf *) l->data)->pixbuf;
1568       else
1569       {
1570         gchar *image = wp->image;
1571         GdkPixbuf *regularthumb = a_thumbnails_get ( wp->image );
1572         if ( ! regularthumb )
1573         {
1574           regularthumb = a_thumbnails_get_default (); /* cache one 'not yet loaded' for all thumbs not loaded */
1575           image = "\x12\x00"; /* this shouldn't occur naturally. */
1576         }
1577         if ( regularthumb )
1578         {
1579           CachedPixbuf *cp = NULL;
1580           cp = g_malloc ( sizeof ( CachedPixbuf ) );
1581           if ( dp->vtl->image_size == 128 )
1582             cp->pixbuf = regularthumb;
1583           else
1584           {
1585             cp->pixbuf = a_thumbnails_scale_pixbuf(regularthumb, dp->vtl->image_size, dp->vtl->image_size);
1586             g_assert ( cp->pixbuf );
1587             g_object_unref ( G_OBJECT(regularthumb) );
1588           }
1589           cp->image = g_strdup ( image );
1590
1591           /* needed so 'click picture' tool knows how big the pic is; we don't
1592            * store it in cp because they may have been freed already. */
1593           wp->image_width = gdk_pixbuf_get_width ( cp->pixbuf );
1594           wp->image_height = gdk_pixbuf_get_height ( cp->pixbuf );
1595
1596           g_queue_push_head ( dp->vtl->image_cache, cp );
1597           if ( dp->vtl->image_cache->length > dp->vtl->image_cache_size )
1598             cached_pixbuf_free ( g_queue_pop_tail ( dp->vtl->image_cache ) );
1599
1600           pixbuf = cp->pixbuf;
1601         }
1602         else
1603         {
1604           pixbuf = a_thumbnails_get_default (); /* thumbnail not yet loaded */
1605         }
1606       }
1607       if ( pixbuf )
1608       {
1609         gint w, h;
1610         w = gdk_pixbuf_get_width ( pixbuf );
1611         h = gdk_pixbuf_get_height ( pixbuf );
1612
1613         if ( x+(w/2) > 0 && y+(h/2) > 0 && x-(w/2) < dp->width && y-(h/2) < dp->height ) /* always draw within boundaries */
1614         {
1615           if ( vik_viewport_get_draw_highlight ( dp->vp ) ) {
1616             if ( dp->vtl == vik_window_get_selected_trw_layer ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(dp->vtl) ) ||
1617                  dp->vtl->waypoints == vik_window_get_selected_waypoints ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(dp->vtl) ) ||
1618                  wp == vik_window_get_selected_waypoint ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(dp->vtl) ) ) {
1619               // Highlighted - so draw a little border around the chosen one
1620               // single line seems a little weak so draw 2 of them
1621               vik_viewport_draw_rectangle (dp->vp, vik_viewport_get_gc_highlight (dp->vp), FALSE,
1622                                            x - (w/2) - 1, y - (h/2) - 1, w + 2, h + 2 );
1623               vik_viewport_draw_rectangle (dp->vp, vik_viewport_get_gc_highlight (dp->vp), FALSE,
1624                                            x - (w/2) - 2, y - (h/2) - 2, w + 4, h + 4 );
1625             }
1626           }
1627           if ( dp->vtl->image_alpha == 255 )
1628             vik_viewport_draw_pixbuf ( dp->vp, pixbuf, 0, 0, x - (w/2), y - (h/2), w, h );
1629           else
1630             vik_viewport_draw_pixbuf_with_alpha ( dp->vp, pixbuf, dp->vtl->image_alpha, 0, 0, x - (w/2), y - (h/2), w, h );
1631         }
1632         return; /* if failed to draw picture, default to drawing regular waypoint (below) */
1633       }
1634     }
1635
1636     /* DRAW ACTUAL DOT */
1637     if ( dp->vtl->wp_draw_symbols && wp->symbol && (sym = a_get_wp_sym(wp->symbol)) ) {
1638       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 );
1639     } 
1640     else if ( wp == dp->vtl->current_wp ) {
1641       switch ( dp->vtl->wp_symbol ) {
1642         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;
1643         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;
1644         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;
1645         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 );
1646                           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 );
1647       }
1648     }
1649     else {
1650       switch ( dp->vtl->wp_symbol ) {
1651         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;
1652         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;
1653         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;
1654         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 );
1655                           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;
1656       }
1657     }
1658
1659     if ( dp->vtl->drawlabels )
1660     {
1661       /* thanks to the GPSDrive people (Fritz Ganter et al.) for hints on this part ... yah, I'm too lazy to study documentation */
1662       gint label_x, label_y;
1663       gint width, height;
1664       // Hopefully name won't break the markup (may need to sanitize - g_markup_escape_text())
1665
1666       // Could this stored in the waypoint rather than recreating each pass?
1667       gchar *fsize = NULL;
1668       switch (dp->vtl->wp_font_size) {
1669         case FS_XX_SMALL: fsize = g_strdup ( "xx-small" ); break;
1670         case FS_X_SMALL: fsize = g_strdup ( "x-small" ); break;
1671         case FS_SMALL: fsize = g_strdup ( "small" ); break;
1672         case FS_LARGE: fsize = g_strdup ( "large" ); break;
1673         case FS_X_LARGE: fsize = g_strdup ( "x-large" ); break;
1674         case FS_XX_LARGE: fsize = g_strdup ( "xx-large" ); break;
1675         default: fsize = g_strdup ( "medium" ); break;
1676       }
1677
1678       gchar *wp_label_markup = g_strdup_printf ( "<span size=\"%s\">%s</span>", fsize, wp->name );
1679
1680       if ( pango_parse_markup ( wp_label_markup, -1, 0, NULL, NULL, NULL, NULL ) )
1681         pango_layout_set_markup ( dp->vtl->wplabellayout, wp_label_markup, -1 );
1682       else
1683         // Fallback if parse failure
1684         pango_layout_set_text ( dp->vtl->wplabellayout, wp->name, -1 );
1685
1686       g_free ( wp_label_markup );
1687       g_free ( fsize );
1688
1689       pango_layout_get_pixel_size ( dp->vtl->wplabellayout, &width, &height );
1690       label_x = x - width/2;
1691       if (sym)
1692         label_y = y - height - 2 - gdk_pixbuf_get_height(sym)/2;
1693       else
1694         label_y = y - dp->vtl->wp_size - height - 2;
1695
1696       /* if highlight mode on, then draw background text in highlight colour */
1697       if ( vik_viewport_get_draw_highlight ( dp->vp ) ) {
1698         if ( dp->vtl == vik_window_get_selected_trw_layer ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(dp->vtl) ) ||
1699              dp->vtl->waypoints == vik_window_get_selected_waypoints ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(dp->vtl) ) ||
1700              wp == vik_window_get_selected_waypoint ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(dp->vtl) ) )
1701           vik_viewport_draw_rectangle ( dp->vp, vik_viewport_get_gc_highlight (dp->vp), TRUE, label_x - 1, label_y-1,width+2,height+2);
1702         else
1703           vik_viewport_draw_rectangle ( dp->vp, dp->vtl->waypoint_bg_gc, TRUE, label_x - 1, label_y-1,width+2,height+2);
1704       }
1705       else {
1706         vik_viewport_draw_rectangle ( dp->vp, dp->vtl->waypoint_bg_gc, TRUE, label_x - 1, label_y-1,width+2,height+2);
1707       }
1708       vik_viewport_draw_layout ( dp->vp, dp->vtl->waypoint_text_gc, label_x, label_y, dp->vtl->wplabellayout );
1709     }
1710   }
1711 }
1712
1713 static void trw_layer_draw ( VikTrwLayer *l, gpointer data )
1714 {
1715   static struct DrawingParams dp;
1716   g_assert ( l != NULL );
1717
1718   init_drawing_params ( &dp, l, VIK_VIEWPORT(data) );
1719
1720   if ( l->tracks_visible )
1721     g_hash_table_foreach ( l->tracks, (GHFunc) trw_layer_draw_track_cb, &dp );
1722
1723   if ( l->routes_visible )
1724     g_hash_table_foreach ( l->routes, (GHFunc) trw_layer_draw_track_cb, &dp );
1725
1726   if (l->waypoints_visible)
1727     g_hash_table_foreach ( l->waypoints, (GHFunc) trw_layer_draw_waypoint, &dp );
1728 }
1729
1730 static void trw_layer_free_track_gcs ( VikTrwLayer *vtl )
1731 {
1732   int i;
1733   if ( vtl->track_bg_gc ) 
1734   {
1735     g_object_unref ( vtl->track_bg_gc );
1736     vtl->track_bg_gc = NULL;
1737   }
1738   if ( vtl->track_1color_gc )
1739   {
1740     g_object_unref ( vtl->track_1color_gc );
1741     vtl->track_1color_gc = NULL;
1742   }
1743   if ( vtl->current_track_gc ) 
1744   {
1745     g_object_unref ( vtl->current_track_gc );
1746     vtl->current_track_gc = NULL;
1747   }
1748   if ( vtl->current_track_newpoint_gc )
1749   {
1750     g_object_unref ( vtl->current_track_newpoint_gc );
1751     vtl->current_track_newpoint_gc = NULL;
1752   }
1753
1754   if ( ! vtl->track_gc )
1755     return;
1756   for ( i = vtl->track_gc->len - 1; i >= 0; i-- )
1757     g_object_unref ( g_array_index ( vtl->track_gc, GObject *, i ) );
1758   g_array_free ( vtl->track_gc, TRUE );
1759   vtl->track_gc = NULL;
1760 }
1761
1762 static void trw_layer_new_track_gcs ( VikTrwLayer *vtl, VikViewport *vp )
1763 {
1764   GdkGC *gc[ VIK_TRW_LAYER_TRACK_GC ];
1765   gint width = vtl->line_thickness;
1766
1767   if ( vtl->track_gc )
1768     trw_layer_free_track_gcs ( vtl );
1769
1770   if ( vtl->track_bg_gc )
1771     g_object_unref ( vtl->track_bg_gc );
1772   vtl->track_bg_gc = vik_viewport_new_gc ( vp, "#FFFFFF", width + vtl->bg_line_thickness );
1773
1774   // Ensure new track drawing heeds line thickness setting
1775   //  however always have a minium of 2, as 1 pixel is really narrow
1776   gint new_track_width = (vtl->line_thickness < 2) ? 2 : vtl->line_thickness;
1777   
1778   if ( vtl->current_track_gc )
1779     g_object_unref ( vtl->current_track_gc );
1780   vtl->current_track_gc = vik_viewport_new_gc ( vp, "#FF0000", new_track_width );
1781   gdk_gc_set_line_attributes ( vtl->current_track_gc, new_track_width, GDK_LINE_ON_OFF_DASH, GDK_CAP_ROUND, GDK_JOIN_ROUND );
1782
1783   // 'newpoint' gc is exactly the same as the current track gc
1784   if ( vtl->current_track_newpoint_gc )
1785     g_object_unref ( vtl->current_track_newpoint_gc );
1786   vtl->current_track_newpoint_gc = vik_viewport_new_gc ( vp, "#FF0000", new_track_width );
1787   gdk_gc_set_line_attributes ( vtl->current_track_newpoint_gc, new_track_width, GDK_LINE_ON_OFF_DASH, GDK_CAP_ROUND, GDK_JOIN_ROUND );
1788
1789   vtl->track_gc = g_array_sized_new ( FALSE, FALSE, sizeof ( GdkGC * ), VIK_TRW_LAYER_TRACK_GC );
1790
1791   gc[VIK_TRW_LAYER_TRACK_GC_STOP] = vik_viewport_new_gc ( vp, "#874200", width );
1792   gc[VIK_TRW_LAYER_TRACK_GC_BLACK] = vik_viewport_new_gc ( vp, "#000000", width ); // black
1793
1794   gc[VIK_TRW_LAYER_TRACK_GC_SLOW] = vik_viewport_new_gc ( vp, "#E6202E", width ); // red-ish
1795   gc[VIK_TRW_LAYER_TRACK_GC_AVER] = vik_viewport_new_gc ( vp, "#D2CD26", width ); // yellow-ish
1796   gc[VIK_TRW_LAYER_TRACK_GC_FAST] = vik_viewport_new_gc ( vp, "#2B8700", width ); // green-ish
1797
1798   g_array_append_vals ( vtl->track_gc, gc, VIK_TRW_LAYER_TRACK_GC );
1799 }
1800
1801 static VikTrwLayer* trw_layer_create ( VikViewport *vp )
1802 {
1803   VikTrwLayer *rv = trw_layer_new ( DRAWMODE_BY_TRACK );
1804   vik_layer_rename ( VIK_LAYER(rv), vik_trw_layer_interface.name );
1805
1806   if ( vp == NULL || GTK_WIDGET(vp)->window == NULL ) {
1807     /* early exit, as the rest is GUI related */
1808     return rv;
1809   }
1810
1811   rv->wplabellayout = gtk_widget_create_pango_layout (GTK_WIDGET(vp), NULL);
1812   pango_layout_set_font_description (rv->wplabellayout, GTK_WIDGET(vp)->style->font_desc);
1813
1814   trw_layer_new_track_gcs ( rv, vp );
1815
1816   rv->waypoint_gc = vik_viewport_new_gc ( vp, "#000000", 2 );
1817   rv->waypoint_text_gc = vik_viewport_new_gc ( vp, "#FFFFFF", 1 );
1818   rv->waypoint_bg_gc = vik_viewport_new_gc ( vp, "#8383C4", 1 );
1819   gdk_gc_set_function ( rv->waypoint_bg_gc, GDK_AND );
1820
1821   rv->has_verified_thumbnails = FALSE;
1822   rv->wp_symbol = WP_SYMBOL_FILLED_SQUARE;
1823   rv->wp_size = 4;
1824   rv->wp_draw_symbols = TRUE;
1825   rv->wp_font_size = FS_MEDIUM;
1826
1827   rv->coord_mode = vik_viewport_get_coord_mode ( vp );
1828
1829   rv->menu_selection = vik_layer_get_interface(VIK_LAYER(rv)->type)->menu_items_selection;
1830
1831   return rv;
1832 }
1833
1834 #define SMALL_ICON_SIZE 18
1835 /*
1836  * Can accept a null symbol, and may return null value
1837  */
1838 static GdkPixbuf* get_wp_sym_small ( gchar *symbol )
1839 {
1840   GdkPixbuf* wp_icon = a_get_wp_sym (symbol);
1841   // ATM a_get_wp_sym returns a cached icon, with the size dependent on the preferences.
1842   //  So needing a small icon for the treeview may need some resizing:
1843   if ( wp_icon && gdk_pixbuf_get_width ( wp_icon ) != SMALL_ICON_SIZE )
1844     wp_icon = gdk_pixbuf_scale_simple ( wp_icon, SMALL_ICON_SIZE, SMALL_ICON_SIZE, GDK_INTERP_BILINEAR );
1845   return wp_icon;
1846 }
1847
1848 static void trw_layer_realize_track ( gpointer id, VikTrack *track, gpointer pass_along[5] )
1849 {
1850   GtkTreeIter *new_iter = g_malloc(sizeof(GtkTreeIter));
1851
1852   GdkPixbuf *pixbuf = NULL;
1853
1854   if ( track->has_color ) {
1855     pixbuf = gdk_pixbuf_new ( GDK_COLORSPACE_RGB, FALSE, 8, SMALL_ICON_SIZE, SMALL_ICON_SIZE );
1856     // Annoyingly the GdkColor.pixel does not give the correct color when passed to gdk_pixbuf_fill (even when alloc'ed)
1857     // Here is some magic found to do the conversion
1858     // http://www.cs.binghamton.edu/~sgreene/cs360-2011s/topics/gtk+-2.20.1/gtk/gtkcolorbutton.c
1859     guint32 pixel = ((track->color.red & 0xff00) << 16) |
1860       ((track->color.green & 0xff00) << 8) |
1861       (track->color.blue & 0xff00);
1862
1863     gdk_pixbuf_fill ( pixbuf, pixel );
1864   }
1865
1866 #ifdef VIK_CONFIG_ALPHABETIZED_TRW
1867   vik_treeview_add_sublayer_alphabetized ( (VikTreeview *) pass_along[3], (GtkTreeIter *) pass_along[0], (GtkTreeIter *) pass_along[1], track->name, pass_along[2], id, GPOINTER_TO_INT (pass_along[4]), pixbuf, TRUE, TRUE );
1868 #else
1869   vik_treeview_add_sublayer ( (VikTreeview *) pass_along[3], (GtkTreeIter *) pass_along[0], (GtkTreeIter *) pass_along[1], track->name, pass_along[2], id, GPOINTER_TO_INT (pass_along[4]), pixbuf, TRUE, TRUE );
1870 #endif
1871
1872   if ( pixbuf )
1873     g_object_unref (pixbuf);
1874
1875   *new_iter = *((GtkTreeIter *) pass_along[1]);
1876   if ( track->is_route )
1877     g_hash_table_insert ( VIK_TRW_LAYER(pass_along[2])->routes_iters, id, new_iter );
1878   else
1879     g_hash_table_insert ( VIK_TRW_LAYER(pass_along[2])->tracks_iters, id, new_iter );
1880
1881   if ( ! track->visible )
1882     vik_treeview_item_set_visible ( (VikTreeview *) pass_along[3], (GtkTreeIter *) pass_along[1], FALSE );
1883 }
1884
1885 static void trw_layer_realize_waypoint ( gpointer id, VikWaypoint *wp, gpointer pass_along[5] )
1886 {
1887   GtkTreeIter *new_iter = g_malloc(sizeof(GtkTreeIter));
1888
1889 #ifdef VIK_CONFIG_ALPHABETIZED_TRW
1890   vik_treeview_add_sublayer_alphabetized ( (VikTreeview *) pass_along[3], (GtkTreeIter *) pass_along[0], (GtkTreeIter *) pass_along[1], wp->name, pass_along[2], id, GPOINTER_TO_INT (pass_along[4]), get_wp_sym_small (wp->symbol), TRUE, TRUE );
1891 #else
1892   vik_treeview_add_sublayer ( (VikTreeview *) pass_along[3], (GtkTreeIter *) pass_along[0], (GtkTreeIter *) pass_along[1], wp->name, pass_along[2], id, GPOINTER_TO_UINT (pass_along[4]), get_wp_sym_small (wp->symbol), TRUE, TRUE );
1893 #endif
1894
1895   *new_iter = *((GtkTreeIter *) pass_along[1]);
1896   g_hash_table_insert ( VIK_TRW_LAYER(pass_along[2])->waypoints_iters, id, new_iter );
1897
1898   if ( ! wp->visible )
1899     vik_treeview_item_set_visible ( (VikTreeview *) pass_along[3], (GtkTreeIter *) pass_along[1], FALSE );
1900 }
1901
1902 static void trw_layer_add_sublayer_tracks ( VikTrwLayer *vtl, VikTreeview *vt, GtkTreeIter *layer_iter )
1903 {
1904 #ifdef VIK_CONFIG_ALPHABETIZED_TRW
1905   vik_treeview_add_sublayer_alphabetized ( (VikTreeview *) vt, layer_iter, &(vtl->tracks_iter), _("Tracks"), vtl, NULL, VIK_TRW_LAYER_SUBLAYER_TRACKS, NULL, TRUE, FALSE );
1906 #else
1907   vik_treeview_add_sublayer ( (VikTreeview *) vt, layer_iter, &(vtl->tracks_iter), _("Tracks"), vtl, NULL, VIK_TRW_LAYER_SUBLAYER_TRACKS, NULL, TRUE, FALSE );
1908 #endif
1909 }
1910
1911 static void trw_layer_add_sublayer_waypoints ( VikTrwLayer *vtl, VikTreeview *vt, GtkTreeIter *layer_iter )
1912 {
1913 #ifdef VIK_CONFIG_ALPHABETIZED_TRW
1914   vik_treeview_add_sublayer_alphabetized ( (VikTreeview *) vt, layer_iter, &(vtl->waypoints_iter), _("Waypoints"), vtl, NULL, VIK_TRW_LAYER_SUBLAYER_WAYPOINTS, NULL, TRUE, FALSE );
1915 #else
1916   vik_treeview_add_sublayer ( (VikTreeview *) vt, layer_iter, &(vtl->waypoints_iter), _("Waypoints"), vtl, NULL, VIK_TRW_LAYER_SUBLAYER_WAYPOINTS, NULL, TRUE, FALSE );
1917 #endif
1918 }
1919
1920 static void trw_layer_add_sublayer_routes ( VikTrwLayer *vtl, VikTreeview *vt, GtkTreeIter *layer_iter )
1921 {
1922 #ifdef VIK_CONFIG_ALPHABETIZED_TRW
1923   vik_treeview_add_sublayer_alphabetized ( (VikTreeview *) vt, layer_iter, &(vtl->routes_iter), _("Routes"), vtl, NULL, VIK_TRW_LAYER_SUBLAYER_ROUTES, NULL, TRUE, FALSE );
1924 #else
1925   vik_treeview_add_sublayer ( (VikTreeview *) vt, layer_iter, &(vtl->routes_iter), _("Routes"), vtl, NULL, VIK_TRW_LAYER_SUBLAYER_ROUTES, NULL, TRUE, FALSE );
1926 #endif
1927 }
1928
1929 static void trw_layer_realize ( VikTrwLayer *vtl, VikTreeview *vt, GtkTreeIter *layer_iter )
1930 {
1931   GtkTreeIter iter2;
1932   gpointer pass_along[5] = { &(vtl->tracks_iter), &iter2, vtl, vt, GINT_TO_POINTER(VIK_TRW_LAYER_SUBLAYER_TRACK) };
1933
1934   if ( g_hash_table_size (vtl->tracks) > 0 ) {
1935     trw_layer_add_sublayer_tracks ( vtl, vt , layer_iter );
1936     g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_realize_track, pass_along );
1937
1938     vik_treeview_item_set_visible ( (VikTreeview *) vt, &(vtl->tracks_iter), vtl->tracks_visible );
1939   }
1940
1941   if ( g_hash_table_size (vtl->routes) > 0 ) {
1942
1943     trw_layer_add_sublayer_routes ( vtl, vt, layer_iter );
1944
1945     pass_along[0] = &(vtl->routes_iter);
1946     pass_along[4] = GINT_TO_POINTER(VIK_TRW_LAYER_SUBLAYER_ROUTE);
1947
1948     g_hash_table_foreach ( vtl->routes, (GHFunc) trw_layer_realize_track, pass_along );
1949
1950     vik_treeview_item_set_visible ( (VikTreeview *) vt, &(vtl->routes_iter), vtl->routes_visible );
1951   }
1952
1953   if ( g_hash_table_size (vtl->waypoints) > 0 ) {
1954     trw_layer_add_sublayer_waypoints ( vtl, vt, layer_iter );
1955
1956     pass_along[0] = &(vtl->waypoints_iter);
1957     pass_along[4] = GINT_TO_POINTER(VIK_TRW_LAYER_SUBLAYER_WAYPOINT);
1958
1959     g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_realize_waypoint, pass_along );
1960
1961     vik_treeview_item_set_visible ( (VikTreeview *) vt, &(vtl->waypoints_iter), vtl->waypoints_visible );
1962   }
1963
1964 }
1965
1966 static gboolean trw_layer_sublayer_toggle_visible ( VikTrwLayer *l, gint subtype, gpointer sublayer )
1967 {
1968   switch ( subtype )
1969   {
1970     case VIK_TRW_LAYER_SUBLAYER_TRACKS: return (l->tracks_visible ^= 1);
1971     case VIK_TRW_LAYER_SUBLAYER_WAYPOINTS: return (l->waypoints_visible ^= 1);
1972     case VIK_TRW_LAYER_SUBLAYER_ROUTES: return (l->routes_visible ^= 1);
1973     case VIK_TRW_LAYER_SUBLAYER_TRACK:
1974     {
1975       VikTrack *t = g_hash_table_lookup ( l->tracks, sublayer );
1976       if (t)
1977         return (t->visible ^= 1);
1978       else
1979         return TRUE;
1980     }
1981     case VIK_TRW_LAYER_SUBLAYER_WAYPOINT:
1982     {
1983       VikWaypoint *t = g_hash_table_lookup ( l->waypoints, sublayer );
1984       if (t)
1985         return (t->visible ^= 1);
1986       else
1987         return TRUE;
1988     }
1989     case VIK_TRW_LAYER_SUBLAYER_ROUTE:
1990     {
1991       VikTrack *t = g_hash_table_lookup ( l->routes, sublayer );
1992       if (t)
1993         return (t->visible ^= 1);
1994       else
1995         return TRUE;
1996     }
1997   }
1998   return TRUE;
1999 }
2000
2001 /*
2002  * Return a property about tracks for this layer
2003  */
2004 gint vik_trw_layer_get_property_tracks_line_thickness ( VikTrwLayer *vtl )
2005 {
2006   return vtl->line_thickness;
2007 }
2008
2009 // Structure to hold multiple track information for a layer
2010 typedef struct {
2011   gdouble length;
2012   time_t  start_time;
2013   time_t  end_time;
2014   gint    duration;
2015 } tooltip_tracks;
2016
2017 /*
2018  * Build up layer multiple track information via updating the tooltip_tracks structure
2019  */
2020 static void trw_layer_tracks_tooltip ( const gchar *name, VikTrack *tr, tooltip_tracks *tt )
2021 {
2022   tt->length = tt->length + vik_track_get_length (tr);
2023
2024   // Ensure times are available
2025   if ( tr->trackpoints &&
2026        VIK_TRACKPOINT(tr->trackpoints->data)->has_timestamp &&
2027        VIK_TRACKPOINT(g_list_last(tr->trackpoints)->data)->has_timestamp ) {
2028
2029     time_t t1, t2;
2030     t1 = VIK_TRACKPOINT(tr->trackpoints->data)->timestamp;
2031     t2 = VIK_TRACKPOINT(g_list_last(tr->trackpoints)->data)->timestamp;
2032
2033     // Assume never actually have a track with a time of 0 (1st Jan 1970)
2034     // Hence initialize to the first 'proper' value
2035     if ( tt->start_time == 0 )
2036         tt->start_time = t1;
2037     if ( tt->end_time == 0 )
2038         tt->end_time = t2;
2039
2040     // Update find the earliest / last times
2041     if ( t1 < tt->start_time )
2042         tt->start_time = t1;
2043     if ( t2 > tt->end_time )
2044         tt->end_time = t2;
2045
2046     // Keep track of total time
2047     //  there maybe gaps within a track (eg segments)
2048     //  but this should be generally good enough for a simple indicator
2049     tt->duration = tt->duration + (int)(t2-t1);
2050   }
2051 }
2052
2053 /*
2054  * Generate tooltip text for the layer.
2055  * This is relatively complicated as it considers information for
2056  *   no tracks, a single track or multiple tracks
2057  *     (which may or may not have timing information)
2058  */
2059 static const gchar* trw_layer_layer_tooltip ( VikTrwLayer *vtl )
2060 {
2061   gchar tbuf1[32];
2062   gchar tbuf2[64];
2063   gchar tbuf3[64];
2064   gchar tbuf4[10];
2065   tbuf1[0] = '\0';
2066   tbuf2[0] = '\0';
2067   tbuf3[0] = '\0';
2068   tbuf4[0] = '\0';
2069
2070   static gchar tmp_buf[128];
2071   tmp_buf[0] = '\0';
2072
2073   // For compact date format I'm using '%x'     [The preferred date representation for the current locale without the time.]
2074
2075   // Safety check - I think these should always be valid
2076   if ( vtl->tracks && vtl->waypoints ) {
2077     tooltip_tracks tt = { 0.0, 0, 0 };
2078     g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_tracks_tooltip, &tt );
2079
2080     GDate* gdate_start = g_date_new ();
2081     g_date_set_time_t (gdate_start, tt.start_time);
2082
2083     GDate* gdate_end = g_date_new ();
2084     g_date_set_time_t (gdate_end, tt.end_time);
2085
2086     if ( g_date_compare (gdate_start, gdate_end) ) {
2087       // Dates differ so print range on separate line
2088       g_date_strftime (tbuf1, sizeof(tbuf1), "%x", gdate_start);
2089       g_date_strftime (tbuf2, sizeof(tbuf2), "%x", gdate_end);
2090       g_snprintf (tbuf3, sizeof(tbuf3), "%s to %s\n", tbuf1, tbuf2);
2091     }
2092     else {
2093       // Same date so just show it and keep rest of text on the same line - provided it's a valid time!
2094       if ( tt.start_time != 0 )
2095         g_date_strftime (tbuf3, sizeof(tbuf3), "%x: ", gdate_start);
2096     }
2097
2098     tbuf2[0] = '\0';
2099     if ( tt.length > 0.0 ) {
2100       gdouble len_in_units;
2101
2102       // Setup info dependent on distance units
2103       if ( a_vik_get_units_distance() == VIK_UNITS_DISTANCE_MILES ) {
2104         g_snprintf (tbuf4, sizeof(tbuf4), "miles");
2105         len_in_units = VIK_METERS_TO_MILES(tt.length);
2106       }
2107       else {
2108         g_snprintf (tbuf4, sizeof(tbuf4), "kms");
2109         len_in_units = tt.length/1000.0;
2110       }
2111
2112       // Timing information if available
2113       tbuf1[0] = '\0';
2114       if ( tt.duration > 0 ) {
2115         g_snprintf (tbuf1, sizeof(tbuf1),
2116                     _(" in %d:%02d hrs:mins"),
2117                     (int)round(tt.duration/3600), (int)round((tt.duration/60)%60));
2118       }
2119       g_snprintf (tbuf2, sizeof(tbuf2),
2120                   _("\n%sTotal Length %.1f %s%s"),
2121                   tbuf3, len_in_units, tbuf4, tbuf1);
2122     }
2123
2124     // Put together all the elements to form compact tooltip text
2125     g_snprintf (tmp_buf, sizeof(tmp_buf),
2126                 _("Tracks: %d - Waypoints: %d - Routes: %d%s"),
2127                 g_hash_table_size (vtl->tracks), g_hash_table_size (vtl->waypoints), g_hash_table_size (vtl->routes), tbuf2);
2128
2129     g_date_free (gdate_start);
2130     g_date_free (gdate_end);
2131
2132   }
2133
2134   return tmp_buf;
2135 }
2136
2137 static const gchar* trw_layer_sublayer_tooltip ( VikTrwLayer *l, gint subtype, gpointer sublayer )
2138 {
2139   switch ( subtype )
2140   {
2141     case VIK_TRW_LAYER_SUBLAYER_TRACKS:
2142     {
2143       // Very simple tooltip - may expand detail in the future...
2144       static gchar tmp_buf[32];
2145       g_snprintf (tmp_buf, sizeof(tmp_buf),
2146                   _("Tracks: %d"),
2147                   g_hash_table_size (l->tracks));
2148       return tmp_buf;
2149     }
2150     break;
2151     case VIK_TRW_LAYER_SUBLAYER_ROUTES:
2152     {
2153       // Very simple tooltip - may expand detail in the future...
2154       static gchar tmp_buf[32];
2155       g_snprintf (tmp_buf, sizeof(tmp_buf),
2156                   _("Routes: %d"),
2157                   g_hash_table_size (l->routes));
2158       return tmp_buf;
2159     }
2160     break;
2161
2162     case VIK_TRW_LAYER_SUBLAYER_ROUTE:
2163       // Same tooltip for a route
2164     case VIK_TRW_LAYER_SUBLAYER_TRACK:
2165     {
2166       VikTrack *tr;
2167       if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
2168         tr = g_hash_table_lookup ( l->tracks, sublayer );
2169       else
2170         tr = g_hash_table_lookup ( l->routes, sublayer );
2171
2172       if ( tr ) {
2173         // Could be a better way of handling strings - but this works...
2174         gchar time_buf1[20];
2175         gchar time_buf2[20];
2176         time_buf1[0] = '\0';
2177         time_buf2[0] = '\0';
2178         static gchar tmp_buf[100];
2179         // Compact info: Short date eg (11/20/99), duration and length
2180         // Hopefully these are the things that are most useful and so promoted into the tooltip
2181         if ( tr->trackpoints && VIK_TRACKPOINT(tr->trackpoints->data)->has_timestamp ) {
2182           // %x     The preferred date representation for the current locale without the time.
2183           strftime (time_buf1, sizeof(time_buf1), "%x: ", gmtime(&(VIK_TRACKPOINT(tr->trackpoints->data)->timestamp)));
2184           if ( VIK_TRACKPOINT(g_list_last(tr->trackpoints)->data)->has_timestamp ) {
2185             gint dur = ( (VIK_TRACKPOINT(g_list_last(tr->trackpoints)->data)->timestamp) - (VIK_TRACKPOINT(tr->trackpoints->data)->timestamp) );
2186             if ( dur > 0 )
2187               g_snprintf ( time_buf2, sizeof(time_buf2), _("- %d:%02d hrs:mins"), (int)round(dur/3600), (int)round((dur/60)%60) );
2188           }
2189         }
2190         // Get length and consider the appropriate distance units
2191         gdouble tr_len = vik_track_get_length(tr);
2192         vik_units_distance_t dist_units = a_vik_get_units_distance ();
2193         switch (dist_units) {
2194         case VIK_UNITS_DISTANCE_KILOMETRES:
2195           g_snprintf (tmp_buf, sizeof(tmp_buf), _("%s%.1f km %s"), time_buf1, tr_len/1000.0, time_buf2);
2196           break;
2197         case VIK_UNITS_DISTANCE_MILES:
2198           g_snprintf (tmp_buf, sizeof(tmp_buf), _("%s%.1f miles %s"), time_buf1, VIK_METERS_TO_MILES(tr_len), time_buf2);
2199           break;
2200         default:
2201           break;
2202         }
2203         return tmp_buf;
2204       }
2205     }
2206     break;
2207     case VIK_TRW_LAYER_SUBLAYER_WAYPOINTS:
2208     {
2209       // Very simple tooltip - may expand detail in the future...
2210       static gchar tmp_buf[32];
2211       g_snprintf (tmp_buf, sizeof(tmp_buf),
2212                   _("Waypoints: %d"),
2213                   g_hash_table_size (l->waypoints));
2214       return tmp_buf;
2215     }
2216     break;
2217     case VIK_TRW_LAYER_SUBLAYER_WAYPOINT:
2218     {
2219       VikWaypoint *w = g_hash_table_lookup ( l->waypoints, sublayer );
2220       // NB It's OK to return NULL
2221       if ( w )
2222         return w->comment;
2223     }
2224     break;
2225     default: break;
2226   }
2227   return NULL;
2228 }
2229
2230 /*
2231  * Function to show basic track point information on the statusbar
2232  */
2233 static void set_statusbar_msg_info_trkpt ( VikTrwLayer *vtl, VikTrackpoint *trkpt )
2234 {
2235   gchar tmp_buf1[64];
2236   switch (a_vik_get_units_height ()) {
2237   case VIK_UNITS_HEIGHT_FEET:
2238     g_snprintf(tmp_buf1, sizeof(tmp_buf1), _("Trkpt: Alt %dft"), (int)round(VIK_METERS_TO_FEET(trkpt->altitude)));
2239     break;
2240   default:
2241     //VIK_UNITS_HEIGHT_METRES:
2242     g_snprintf(tmp_buf1, sizeof(tmp_buf1), _("Trkpt: Alt %dm"), (int)round(trkpt->altitude));
2243   }
2244   
2245   gchar tmp_buf2[64];
2246   tmp_buf2[0] = '\0';
2247   if ( trkpt->has_timestamp ) {
2248     // Compact date time format
2249     strftime (tmp_buf2, sizeof(tmp_buf2), _(" | Time %x %X"), localtime(&(trkpt->timestamp)));
2250   }
2251
2252   // Position part
2253   // Position is put later on, as this bit may not be seen if the display is not big enough,
2254   //   one can easily use the current pointer position to see this if needed
2255   gchar *lat = NULL, *lon = NULL;
2256   static struct LatLon ll;
2257   vik_coord_to_latlon (&(trkpt->coord), &ll);
2258   a_coords_latlon_to_string ( &ll, &lat, &lon );
2259
2260   // Track name
2261   // Again is put later on, as this bit may not be seen if the display is not big enough
2262   //  trackname can be seen from the treeview (when enabled)
2263   // Also name could be very long to not leave room for anything else
2264   gchar tmp_buf3[64];
2265   tmp_buf3[0] = '\0';
2266   if ( vtl->current_tp_track ) {
2267     g_snprintf(tmp_buf3, sizeof(tmp_buf3),  _(" | Track: %s"), vtl->current_tp_track->name );
2268   }
2269
2270   // Combine parts to make overall message
2271   gchar *msg = g_strdup_printf (_("%s%s | %s %s %s"), tmp_buf1, tmp_buf2, lat, lon, tmp_buf3);
2272   vik_statusbar_set_message ( vik_window_get_statusbar (VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl))), VIK_STATUSBAR_INFO, msg );
2273   g_free ( lat );
2274   g_free ( lon );
2275   g_free ( msg );
2276 }
2277
2278 /*
2279  * Function to show basic waypoint information on the statusbar
2280  */
2281 static void set_statusbar_msg_info_wpt ( VikTrwLayer *vtl, VikWaypoint *wpt )
2282 {
2283   gchar tmp_buf1[64];
2284   switch (a_vik_get_units_height ()) {
2285   case VIK_UNITS_HEIGHT_FEET:
2286     g_snprintf(tmp_buf1, sizeof(tmp_buf1), _("Wpt: Alt %dft"), (int)round(VIK_METERS_TO_FEET(wpt->altitude)));
2287     break;
2288   default:
2289     //VIK_UNITS_HEIGHT_METRES:
2290     g_snprintf(tmp_buf1, sizeof(tmp_buf1), _("Wpt: Alt %dm"), (int)round(wpt->altitude));
2291   }
2292   
2293   // Position part
2294   // Position is put last, as this bit is most likely not to be seen if the display is not big enough,
2295   //   one can easily use the current pointer position to see this if needed
2296   gchar *lat = NULL, *lon = NULL;
2297   static struct LatLon ll;
2298   vik_coord_to_latlon (&(wpt->coord), &ll);
2299   a_coords_latlon_to_string ( &ll, &lat, &lon );
2300
2301   // Combine parts to make overall message
2302   gchar *msg;
2303   if ( wpt->comment )
2304     // Add comment if available
2305     msg = g_strdup_printf ( _("%s | %s %s | Comment: %s"), tmp_buf1, lat, lon, wpt->comment );
2306   else
2307     msg = g_strdup_printf ( _("%s | %s %s"), tmp_buf1, lat, lon );
2308   vik_statusbar_set_message ( vik_window_get_statusbar (VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl))), VIK_STATUSBAR_INFO, msg );
2309   g_free ( lat );
2310   g_free ( lon );
2311   g_free ( msg );
2312 }
2313
2314 /**
2315  * General layer selection function, find out which bit is selected and take appropriate action
2316  */
2317 static gboolean trw_layer_selected ( VikTrwLayer *l, gint subtype, gpointer sublayer, gint type, gpointer vlp )
2318 {
2319   // Reset
2320   l->current_wp    = NULL;
2321   l->current_wp_id = NULL;
2322   trw_layer_cancel_current_tp ( l, FALSE );
2323
2324   // Clear statusbar
2325   vik_statusbar_set_message ( vik_window_get_statusbar (VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(l))), VIK_STATUSBAR_INFO, "" );
2326
2327   switch ( type )
2328     {
2329     case VIK_TREEVIEW_TYPE_LAYER:
2330       {
2331         vik_window_set_selected_trw_layer ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l), l );
2332         /* Mark for redraw */
2333         return TRUE;
2334       }
2335       break;
2336
2337     case VIK_TREEVIEW_TYPE_SUBLAYER:
2338       {
2339         switch ( subtype )
2340           {
2341           case VIK_TRW_LAYER_SUBLAYER_TRACKS:
2342             {
2343               vik_window_set_selected_tracks ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l), l->tracks, l );
2344               /* Mark for redraw */
2345               return TRUE;
2346             }
2347             break;
2348           case VIK_TRW_LAYER_SUBLAYER_TRACK:
2349             {
2350               VikTrack *track = g_hash_table_lookup ( l->tracks, sublayer );
2351               vik_window_set_selected_track ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l), (gpointer)track, l );
2352               /* Mark for redraw */
2353               return TRUE;
2354             }
2355             break;
2356           case VIK_TRW_LAYER_SUBLAYER_ROUTES:
2357             {
2358               vik_window_set_selected_tracks ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l), l->routes, l );
2359               /* Mark for redraw */
2360               return TRUE;
2361             }
2362             break;
2363           case VIK_TRW_LAYER_SUBLAYER_ROUTE:
2364             {
2365               VikTrack *track = g_hash_table_lookup ( l->routes, sublayer );
2366               vik_window_set_selected_track ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l), (gpointer)track, l );
2367               /* Mark for redraw */
2368               return TRUE;
2369             }
2370             break;
2371           case VIK_TRW_LAYER_SUBLAYER_WAYPOINTS:
2372             {
2373               vik_window_set_selected_waypoints ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l), l->waypoints, l );
2374               /* Mark for redraw */
2375               return TRUE;
2376             }
2377             break;
2378           case VIK_TRW_LAYER_SUBLAYER_WAYPOINT:
2379             {
2380               VikWaypoint *wpt = g_hash_table_lookup ( l->waypoints, sublayer );
2381               if ( wpt ) {
2382                 vik_window_set_selected_waypoint ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l), (gpointer)wpt, l );
2383                 // Show some waypoint info
2384                 set_statusbar_msg_info_wpt ( l, wpt );
2385                 /* Mark for redraw */
2386                 return TRUE;
2387               }
2388             }
2389             break;
2390           default:
2391             {
2392               return vik_window_clear_highlight ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l) );
2393             }
2394             break;
2395           }
2396         return FALSE;
2397       }
2398       break;
2399
2400     default:
2401       return vik_window_clear_highlight ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l) );
2402       break;
2403     }
2404 }
2405
2406 GHashTable *vik_trw_layer_get_tracks ( VikTrwLayer *l )
2407 {
2408   return l->tracks;
2409 }
2410
2411 GHashTable *vik_trw_layer_get_routes ( VikTrwLayer *l )
2412 {
2413   return l->routes;
2414 }
2415
2416 GHashTable *vik_trw_layer_get_waypoints ( VikTrwLayer *l )
2417 {
2418   return l->waypoints;
2419 }
2420
2421 /*
2422  * ATM use a case sensitive find
2423  * Finds the first one
2424  */
2425 static gboolean trw_layer_waypoint_find ( const gpointer id, const VikWaypoint *wp, const gchar *name )
2426 {
2427   if ( wp && wp->name )
2428     if ( ! strcmp ( wp->name, name ) )
2429       return TRUE;
2430   return FALSE;
2431 }
2432
2433 /*
2434  * Get waypoint by name - not guaranteed to be unique
2435  * Finds the first one
2436  */
2437 VikWaypoint *vik_trw_layer_get_waypoint ( VikTrwLayer *vtl, const gchar *name )
2438 {
2439   return g_hash_table_find ( vtl->waypoints, (GHRFunc) trw_layer_waypoint_find, (gpointer) name );
2440 }
2441
2442 /*
2443  * ATM use a case sensitive find
2444  * Finds the first one
2445  */
2446 static gboolean trw_layer_track_find ( const gpointer id, const VikTrack *trk, const gchar *name )
2447 {
2448   if ( trk && trk->name )
2449     if ( ! strcmp ( trk->name, name ) )
2450       return TRUE;
2451   return FALSE;
2452 }
2453
2454 /*
2455  * Get track by name - not guaranteed to be unique
2456  * Finds the first one
2457  */
2458 VikTrack *vik_trw_layer_get_track ( VikTrwLayer *vtl, const gchar *name )
2459 {
2460   return g_hash_table_find ( vtl->tracks, (GHRFunc) trw_layer_track_find, (gpointer) name );
2461 }
2462
2463 /*
2464  * Get route by name - not guaranteed to be unique
2465  * Finds the first one
2466  */
2467 VikTrack *vik_trw_layer_get_route ( VikTrwLayer *vtl, const gchar *name )
2468 {
2469   return g_hash_table_find ( vtl->routes, (GHRFunc) trw_layer_track_find, (gpointer) name );
2470 }
2471
2472 static void trw_layer_find_maxmin_waypoints ( const gchar *name, const VikWaypoint *w, struct LatLon maxmin[2] )
2473 {
2474   static VikCoord fixme;
2475   vik_coord_copy_convert ( &(w->coord), VIK_COORD_LATLON, &fixme );
2476   if ( VIK_LATLON(&fixme)->lat > maxmin[0].lat || maxmin[0].lat == 0.0 )
2477     maxmin[0].lat = VIK_LATLON(&fixme)->lat;
2478   if ( VIK_LATLON(&fixme)->lat < maxmin[1].lat || maxmin[1].lat == 0.0 )
2479     maxmin[1].lat = VIK_LATLON(&fixme)->lat;
2480   if ( VIK_LATLON(&fixme)->lon > maxmin[0].lon || maxmin[0].lon == 0.0 )
2481     maxmin[0].lon = VIK_LATLON(&fixme)->lon;
2482   if ( VIK_LATLON(&fixme)->lon < maxmin[1].lon || maxmin[1].lon == 0.0 )
2483     maxmin[1].lon = VIK_LATLON(&fixme)->lon;
2484 }
2485
2486 static void trw_layer_find_maxmin_tracks ( const gchar *name, const VikTrack *trk, struct LatLon maxmin[2] )
2487 {
2488   GList *tr = trk->trackpoints;
2489   static VikCoord fixme;
2490
2491   while ( tr )
2492   {
2493     vik_coord_copy_convert ( &(VIK_TRACKPOINT(tr->data)->coord), VIK_COORD_LATLON, &fixme );
2494     if ( VIK_LATLON(&fixme)->lat > maxmin[0].lat || maxmin[0].lat == 0.0 )
2495       maxmin[0].lat = VIK_LATLON(&fixme)->lat;
2496     if ( VIK_LATLON(&fixme)->lat < maxmin[1].lat || maxmin[1].lat == 0.0 )
2497       maxmin[1].lat = VIK_LATLON(&fixme)->lat;
2498     if ( VIK_LATLON(&fixme)->lon > maxmin[0].lon || maxmin[0].lon == 0.0 )
2499       maxmin[0].lon = VIK_LATLON(&fixme)->lon;
2500     if ( VIK_LATLON(&fixme)->lon < maxmin[1].lon || maxmin[1].lon == 0.0 )
2501       maxmin[1].lon = VIK_LATLON(&fixme)->lon;
2502     tr = tr->next;
2503   }
2504 }
2505
2506 static void trw_layer_find_maxmin (VikTrwLayer *vtl, struct LatLon maxmin[2])
2507 {
2508   // Continually reuse maxmin to find the latest maximum and minimum values
2509   g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_find_maxmin_waypoints, maxmin );
2510   g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_find_maxmin_tracks, maxmin );
2511   g_hash_table_foreach ( vtl->routes, (GHFunc) trw_layer_find_maxmin_tracks, maxmin );
2512 }
2513
2514 gboolean vik_trw_layer_find_center ( VikTrwLayer *vtl, VikCoord *dest )
2515 {
2516   /* 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... */
2517   struct LatLon maxmin[2] = { {0.0,0.0}, {0.0,0.0} };
2518   trw_layer_find_maxmin (vtl, maxmin);
2519   if (maxmin[0].lat == 0.0 && maxmin[0].lon == 0.0 && maxmin[1].lat == 0.0 && maxmin[1].lon == 0.0)
2520     return FALSE;
2521   else
2522   {
2523     struct LatLon average = { (maxmin[0].lat+maxmin[1].lat)/2, (maxmin[0].lon+maxmin[1].lon)/2 };
2524     vik_coord_load_from_latlon ( dest, vtl->coord_mode, &average );
2525     return TRUE;
2526   }
2527 }
2528
2529 static void trw_layer_centerize ( gpointer layer_and_vlp[2] )
2530 {
2531   VikCoord coord;
2532   if ( vik_trw_layer_find_center ( VIK_TRW_LAYER(layer_and_vlp[0]), &coord ) )
2533     goto_coord ( layer_and_vlp[1], NULL, NULL, &coord );
2534   else
2535     a_dialog_info_msg ( VIK_GTK_WINDOW_FROM_LAYER(layer_and_vlp[0]), _("This layer has no waypoints or trackpoints.") );
2536 }
2537
2538 static void trw_layer_zoom_to_show_latlons ( VikTrwLayer *vtl, VikViewport *vvp, struct LatLon maxmin[2] )
2539 {
2540   /* First set the center [in case previously viewing from elsewhere] */
2541   /* Then loop through zoom levels until provided positions are in view */
2542   /* This method is not particularly fast - but should work well enough */
2543   struct LatLon average = { (maxmin[0].lat+maxmin[1].lat)/2, (maxmin[0].lon+maxmin[1].lon)/2 };
2544   VikCoord coord;
2545   vik_coord_load_from_latlon ( &coord, vtl->coord_mode, &average );
2546   vik_viewport_set_center_coord ( vvp, &coord );
2547
2548   /* Convert into definite 'smallest' and 'largest' positions */
2549   struct LatLon minmin;
2550   if ( maxmin[0].lat < maxmin[1].lat )
2551     minmin.lat = maxmin[0].lat;
2552   else
2553     minmin.lat = maxmin[1].lat;
2554
2555   struct LatLon maxmax;
2556   if ( maxmin[0].lon > maxmin[1].lon )
2557     maxmax.lon = maxmin[0].lon;
2558   else
2559     maxmax.lon = maxmin[1].lon;
2560
2561   /* Never zoom in too far - generally not that useful, as too close ! */
2562   /* Always recalculate the 'best' zoom level */
2563   gdouble zoom = 1.0;
2564   vik_viewport_set_zoom ( vvp, zoom );
2565
2566   gdouble min_lat, max_lat, min_lon, max_lon;
2567   /* Should only be a maximum of about 18 iterations from min to max zoom levels */
2568   while ( zoom <= VIK_VIEWPORT_MAX_ZOOM ) {
2569     vik_viewport_get_min_max_lat_lon ( vvp, &min_lat, &max_lat, &min_lon, &max_lon );
2570     /* NB I think the logic used in this test to determine if the bounds is within view
2571        fails if track goes across 180 degrees longitude.
2572        Hopefully that situation is not too common...
2573        Mind you viking doesn't really do edge locations to well anyway */
2574     if ( min_lat < minmin.lat &&
2575          max_lat > minmin.lat &&
2576          min_lon < maxmax.lon &&
2577          max_lon > maxmax.lon )
2578       /* Found within zoom level */
2579       break;
2580
2581     /* Try next */
2582     zoom = zoom * 2;
2583     vik_viewport_set_zoom ( vvp, zoom );
2584   }
2585 }
2586
2587 gboolean vik_trw_layer_auto_set_view ( VikTrwLayer *vtl, VikViewport *vvp )
2588 {
2589   /* TODO: what if there's only one waypoint @ 0,0, it will think nothing found. */
2590   struct LatLon maxmin[2] = { {0.0,0.0}, {0.0,0.0} };
2591   trw_layer_find_maxmin (vtl, maxmin);
2592   if (maxmin[0].lat == 0.0 && maxmin[0].lon == 0.0 && maxmin[1].lat == 0.0 && maxmin[1].lon == 0.0)
2593     return FALSE;
2594   else {
2595     trw_layer_zoom_to_show_latlons ( vtl, vvp, maxmin );
2596     return TRUE;
2597   }
2598 }
2599
2600 static void trw_layer_auto_view ( gpointer layer_and_vlp[2] )
2601 {
2602   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])) ) ) {
2603     vik_layers_panel_emit_update ( VIK_LAYERS_PANEL(layer_and_vlp[1]) );
2604   }
2605   else
2606     a_dialog_info_msg ( VIK_GTK_WINDOW_FROM_LAYER(layer_and_vlp[0]), _("This layer has no waypoints or trackpoints.") );
2607 }
2608
2609 static void trw_layer_export ( gpointer layer_and_vlp[2], const gchar *title, const gchar* default_name, VikTrack* trk, guint file_type )
2610 {
2611   GtkWidget *file_selector;
2612   const gchar *fn;
2613   gboolean failed = FALSE;
2614   file_selector = gtk_file_chooser_dialog_new (title,
2615                                                NULL,
2616                                                GTK_FILE_CHOOSER_ACTION_SAVE,
2617                                                GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
2618                                                GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT,
2619                                                NULL);
2620   gtk_file_chooser_set_current_name (GTK_FILE_CHOOSER(file_selector), default_name);
2621
2622   while ( gtk_dialog_run ( GTK_DIALOG(file_selector) ) == GTK_RESPONSE_ACCEPT )
2623   {
2624     fn = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER(file_selector) );
2625     if ( g_file_test ( fn, G_FILE_TEST_EXISTS ) == FALSE )
2626     {
2627       gtk_widget_hide ( file_selector );
2628       failed = ! a_file_export ( VIK_TRW_LAYER(layer_and_vlp[0]), fn, file_type, trk, TRUE );
2629       break;
2630     }
2631     else
2632     {
2633       if ( a_dialog_yes_or_no ( GTK_WINDOW(file_selector), _("The file \"%s\" exists, do you wish to overwrite it?"), a_file_basename ( fn ) ) )
2634       {
2635         gtk_widget_hide ( file_selector );
2636         failed = ! a_file_export ( VIK_TRW_LAYER(layer_and_vlp[0]), fn, file_type, trk, TRUE );
2637         break;
2638       }
2639     }
2640   }
2641   gtk_widget_destroy ( file_selector );
2642   if ( failed )
2643     a_dialog_error_msg ( VIK_GTK_WINDOW_FROM_LAYER(layer_and_vlp[0]), _("The filename you requested could not be opened for writing.") );
2644 }
2645
2646 static void trw_layer_export_gpspoint ( gpointer layer_and_vlp[2] )
2647 {
2648   trw_layer_export ( layer_and_vlp, _("Export Layer"), vik_layer_get_name(VIK_LAYER(layer_and_vlp[0])), NULL, FILE_TYPE_GPSPOINT );
2649 }
2650
2651 static void trw_layer_export_gpsmapper ( gpointer layer_and_vlp[2] )
2652 {
2653   trw_layer_export ( layer_and_vlp, _("Export Layer"), vik_layer_get_name(VIK_LAYER(layer_and_vlp[0])), NULL, FILE_TYPE_GPSMAPPER );
2654 }
2655
2656 static void trw_layer_export_gpx ( gpointer layer_and_vlp[2] )
2657 {
2658   /* Auto append '.gpx' to track name (providing it's not already there) for the default filename */
2659   gchar *auto_save_name = g_strdup ( vik_layer_get_name(VIK_LAYER(layer_and_vlp[0])) );
2660   if ( ! check_file_ext ( auto_save_name, ".gpx" ) )
2661     auto_save_name = g_strconcat ( auto_save_name, ".gpx", NULL );
2662
2663   trw_layer_export ( layer_and_vlp, _("Export Layer"), auto_save_name, NULL, FILE_TYPE_GPX );
2664
2665   g_free ( auto_save_name );
2666 }
2667
2668 static void trw_layer_export_kml ( gpointer layer_and_vlp[2] )
2669 {
2670   /* Auto append '.kml' to the name (providing it's not already there) for the default filename */
2671   gchar *auto_save_name = g_strdup ( vik_layer_get_name(VIK_LAYER(layer_and_vlp[0])) );
2672   if ( ! check_file_ext ( auto_save_name, ".kml" ) )
2673     auto_save_name = g_strconcat ( auto_save_name, ".kml", NULL );
2674
2675   trw_layer_export ( layer_and_vlp, _("Export Layer"), auto_save_name, NULL, FILE_TYPE_KML );
2676
2677   g_free ( auto_save_name );
2678 }
2679
2680 /**
2681  * Convert the given TRW layer into a temporary GPX file and open it with the specified program
2682  *
2683  */
2684 static void trw_layer_export_external_gpx ( gpointer layer_and_vlp[2], const gchar* external_program )
2685 {
2686   gchar *name_used = NULL;
2687   int fd;
2688
2689   if ((fd = g_file_open_tmp("tmp-viking.XXXXXX.gpx", &name_used, NULL)) >= 0) {
2690     gboolean failed = ! a_file_export ( VIK_TRW_LAYER(layer_and_vlp[0]), name_used, FILE_TYPE_GPX, NULL, TRUE);
2691     if (failed) {
2692       a_dialog_error_msg (VIK_GTK_WINDOW_FROM_LAYER(layer_and_vlp[0]), _("Could not create temporary file for export.") );
2693     }
2694     else {
2695       GError *err = NULL;
2696       gchar *quoted_file = g_shell_quote ( name_used );
2697       gchar *cmd = g_strdup_printf ( "%s %s", external_program, quoted_file );
2698       g_free ( quoted_file );
2699       if ( ! g_spawn_command_line_async ( cmd, &err ) )
2700         {
2701           a_dialog_error_msg_extra ( VIK_GTK_WINDOW_FROM_LAYER( layer_and_vlp[0]), _("Could not launch %s."), external_program );
2702           g_error_free ( err );
2703         }
2704       g_free ( cmd );
2705     }
2706     // Note ATM the 'temporary' file is not deleted, as loading via another program is not instantaneous
2707     //g_remove ( name_used );
2708     // Perhaps should be deleted when the program ends?
2709     // For now leave it to the user to delete it / use system temp cleanup methods.
2710     g_free ( name_used );
2711   }
2712 }
2713
2714 static void trw_layer_export_external_gpx_1 ( gpointer layer_and_vlp[2] )
2715 {
2716   trw_layer_export_external_gpx ( layer_and_vlp, a_vik_get_external_gpx_program_1() );
2717 }
2718
2719 static void trw_layer_export_external_gpx_2 ( gpointer layer_and_vlp[2] )
2720 {
2721   trw_layer_export_external_gpx ( layer_and_vlp, a_vik_get_external_gpx_program_2() );
2722 }
2723
2724 static void trw_layer_export_gpx_track ( gpointer pass_along[6] )
2725 {
2726   gpointer layer_and_vlp[2];
2727   layer_and_vlp[0] = pass_along[0];
2728   layer_and_vlp[1] = pass_along[1];
2729
2730   VikTrwLayer *vtl = VIK_TRW_LAYER(pass_along[0]);
2731   VikTrack *trk;
2732   if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
2733     trk = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
2734   else
2735     trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
2736
2737   if ( !trk || !trk->name )
2738     return;
2739
2740   /* Auto append '.gpx' to track name (providing it's not already there) for the default filename */
2741   gchar *auto_save_name = g_strdup ( trk->name );
2742   if ( ! check_file_ext ( auto_save_name, ".gpx" ) )
2743     auto_save_name = g_strconcat ( auto_save_name, ".gpx", NULL );
2744
2745   trw_layer_export ( layer_and_vlp, _("Export Track as GPX"), auto_save_name, trk, FILE_TYPE_GPX );
2746
2747   g_free ( auto_save_name );
2748 }
2749
2750 typedef struct {
2751   VikWaypoint *wp; // input
2752   gpointer uuid;   // output
2753 } wpu_udata;
2754
2755 static gboolean trw_layer_waypoint_find_uuid ( const gpointer id, const VikWaypoint *wp, gpointer udata )
2756 {
2757   wpu_udata *user_data = udata;
2758   if ( wp == user_data->wp ) {
2759     user_data->uuid = id;
2760     return TRUE;
2761   }
2762   return FALSE;
2763 }
2764
2765 static void trw_layer_goto_wp ( gpointer layer_and_vlp[2] )
2766 {
2767   GtkWidget *dia = gtk_dialog_new_with_buttons (_("Find"),
2768                                                  VIK_GTK_WINDOW_FROM_LAYER(layer_and_vlp[0]),
2769                                                  GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
2770                                                  GTK_STOCK_CANCEL,
2771                                                  GTK_RESPONSE_REJECT,
2772                                                  GTK_STOCK_OK,
2773                                                  GTK_RESPONSE_ACCEPT,
2774                                                  NULL);
2775
2776   GtkWidget *label, *entry;
2777   label = gtk_label_new(_("Waypoint Name:"));
2778   entry = gtk_entry_new();
2779
2780   gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dia)->vbox), label, FALSE, FALSE, 0);
2781   gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dia)->vbox), entry, FALSE, FALSE, 0);
2782   gtk_widget_show_all ( label );
2783   gtk_widget_show_all ( entry );
2784
2785   gtk_dialog_set_default_response ( GTK_DIALOG(dia), GTK_RESPONSE_ACCEPT );
2786
2787   while ( gtk_dialog_run ( GTK_DIALOG(dia) ) == GTK_RESPONSE_ACCEPT )
2788   {
2789     gchar *name = g_strdup(gtk_entry_get_text(GTK_ENTRY(entry)));
2790     // Find *first* wp with the given name
2791     VikWaypoint *wp = vik_trw_layer_get_waypoint ( VIK_TRW_LAYER(layer_and_vlp[0]), name );
2792
2793     if ( !wp )
2794       a_dialog_error_msg ( VIK_GTK_WINDOW_FROM_LAYER(layer_and_vlp[0]), _("Waypoint not found in this layer.") );
2795     else
2796     {
2797       vik_viewport_set_center_coord ( vik_layers_panel_get_viewport(VIK_LAYERS_PANEL(layer_and_vlp[1])), &(wp->coord) );
2798       vik_layers_panel_emit_update ( VIK_LAYERS_PANEL(layer_and_vlp[1]) );
2799
2800       // Find and select on the side panel
2801       wpu_udata udata;
2802       udata.wp   = wp;
2803       udata.uuid = NULL;
2804
2805       // Hmmm, want key of it
2806       gpointer *wpf = g_hash_table_find ( VIK_TRW_LAYER(layer_and_vlp[0])->waypoints, (GHRFunc) trw_layer_waypoint_find_uuid, (gpointer) &udata );
2807
2808       if ( wpf && udata.uuid ) {
2809         GtkTreeIter *it = g_hash_table_lookup ( VIK_TRW_LAYER(layer_and_vlp[0])->waypoints_iters, udata.uuid );
2810         vik_treeview_select_iter ( VIK_LAYER(layer_and_vlp[0])->vt, it, TRUE );
2811       }
2812
2813       break;
2814     }
2815
2816     g_free ( name );
2817
2818   }
2819   gtk_widget_destroy ( dia );
2820 }
2821
2822 gboolean vik_trw_layer_new_waypoint ( VikTrwLayer *vtl, GtkWindow *w, const VikCoord *def_coord )
2823 {
2824   gchar *default_name = highest_wp_number_get(vtl);
2825   VikWaypoint *wp = vik_waypoint_new();
2826   gchar *returned_name;
2827   gboolean updated;
2828   wp->coord = *def_coord;
2829   
2830   // Attempt to auto set height if DEM data is available
2831   gint16 elev = a_dems_get_elev_by_coord ( &(wp->coord), VIK_DEM_INTERPOL_BEST );
2832   if ( elev != VIK_DEM_INVALID_ELEVATION )
2833     wp->altitude = (gdouble)elev;
2834
2835   returned_name = a_dialog_waypoint ( w, default_name, wp, vtl->coord_mode, TRUE, &updated );
2836
2837   if ( returned_name )
2838   {
2839     wp->visible = TRUE;
2840     vik_trw_layer_add_waypoint ( vtl, returned_name, wp );
2841     g_free (default_name);
2842     g_free (returned_name);
2843     return TRUE;
2844   }
2845   g_free (default_name);
2846   vik_waypoint_free(wp);
2847   return FALSE;
2848 }
2849
2850 static void trw_layer_new_wikipedia_wp_viewport ( gpointer lav[2] )
2851 {
2852   VikCoord one, two;
2853   struct LatLon one_ll, two_ll;
2854   struct LatLon maxmin[2] = { {0.0,0.0}, {0.0,0.0} };
2855
2856   VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
2857   VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
2858   VikWindow *vw = (VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl));
2859   VikViewport *vvp =  vik_window_viewport(vw);
2860   vik_viewport_screen_to_coord ( vvp, 0, 0, &one);
2861   vik_viewport_screen_to_coord ( vvp, vik_viewport_get_width(vvp), vik_viewport_get_height(vvp), &two);
2862   vik_coord_to_latlon(&one, &one_ll);
2863   vik_coord_to_latlon(&two, &two_ll);
2864   if (one_ll.lat > two_ll.lat) {
2865     maxmin[0].lat = one_ll.lat;
2866     maxmin[1].lat = two_ll.lat;
2867   }
2868   else {
2869     maxmin[0].lat = two_ll.lat;
2870     maxmin[1].lat = one_ll.lat;
2871   }
2872   if (one_ll.lon > two_ll.lon) {
2873     maxmin[0].lon = one_ll.lon;
2874     maxmin[1].lon = two_ll.lon;
2875   }
2876   else {
2877     maxmin[0].lon = two_ll.lon;
2878     maxmin[1].lon = one_ll.lon;
2879   }
2880   a_geonames_wikipedia_box((VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl)), vtl, vlp, maxmin);
2881 }
2882
2883 static void trw_layer_new_wikipedia_wp_layer ( gpointer lav[2] )
2884 {
2885   VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
2886   VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
2887   struct LatLon maxmin[2] = { {0.0,0.0}, {0.0,0.0} };
2888   
2889   trw_layer_find_maxmin (vtl, maxmin);
2890   a_geonames_wikipedia_box((VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl)), vtl, vlp, maxmin);
2891 }
2892
2893 #ifdef VIK_CONFIG_GEOTAG
2894 static void trw_layer_geotagging_waypoint_mtime_keep ( gpointer pass_along[6] )
2895 {
2896   VikWaypoint *wp = g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->waypoints, pass_along[3] );
2897   if ( wp )
2898     // Update directly - not changing the mtime
2899     a_geotag_write_exif_gps ( wp->image, wp->coord, wp->altitude, TRUE );
2900 }
2901
2902 static void trw_layer_geotagging_waypoint_mtime_update ( gpointer pass_along[6] )
2903 {
2904   VikWaypoint *wp = g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->waypoints, pass_along[3] );
2905   if ( wp )
2906     // Update directly
2907     a_geotag_write_exif_gps ( wp->image, wp->coord, wp->altitude, FALSE );
2908 }
2909
2910 /*
2911  * Use code in separate file for this feature as reasonably complex
2912  */
2913 static void trw_layer_geotagging_track ( gpointer pass_along[6] )
2914 {
2915   VikTrwLayer *vtl = VIK_TRW_LAYER(pass_along[0]);
2916   VikTrack *track = g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, pass_along[3] );
2917   // Unset so can be reverified later if necessary
2918   vtl->has_verified_thumbnails = FALSE;
2919
2920   trw_layer_geotag_dialog ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
2921                             vtl,
2922                             track,
2923                             track->name );
2924 }
2925
2926 static void trw_layer_geotagging ( gpointer lav[2] )
2927 {
2928   VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
2929   // Unset so can be reverified later if necessary
2930   vtl->has_verified_thumbnails = FALSE;
2931
2932   trw_layer_geotag_dialog ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
2933                             vtl,
2934                             NULL,
2935                             NULL);
2936 }
2937 #endif
2938
2939 // 'Acquires' - Same as in File Menu -> Acquire - applies into the selected TRW Layer //
2940
2941 /*
2942  * Acquire into this TRW Layer straight from GPS Device
2943  */
2944 static void trw_layer_acquire_gps_cb ( gpointer lav[2] )
2945 {
2946   VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
2947   VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
2948   VikWindow *vw = (VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl));
2949   VikViewport *vvp =  vik_window_viewport(vw);
2950
2951   vik_datasource_gps_interface.mode = VIK_DATASOURCE_ADDTOLAYER;
2952   a_acquire ( vw, vlp, vvp, &vik_datasource_gps_interface );
2953 }
2954
2955 #ifdef VIK_CONFIG_GOOGLE
2956 /*
2957  * Acquire into this TRW Layer from Google Directions
2958  */
2959 static void trw_layer_acquire_google_cb ( gpointer lav[2] )
2960 {
2961   VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
2962   VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
2963   VikWindow *vw = (VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl));
2964   VikViewport *vvp =  vik_window_viewport(vw);
2965
2966   a_acquire ( vw, vlp, vvp, &vik_datasource_google_interface );
2967 }
2968 #endif
2969
2970 #ifdef VIK_CONFIG_OPENSTREETMAP
2971 /*
2972  * Acquire into this TRW Layer from OSM
2973  */
2974 static void trw_layer_acquire_osm_cb ( gpointer lav[2] )
2975 {
2976   VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
2977   VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
2978   VikWindow *vw = (VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl));
2979   VikViewport *vvp =  vik_window_viewport(vw);
2980
2981   a_acquire ( vw, vlp, vvp, &vik_datasource_osm_interface );
2982 }
2983 #endif
2984
2985 #ifdef VIK_CONFIG_GEOCACHES
2986 /*
2987  * Acquire into this TRW Layer from Geocaching.com
2988  */
2989 static void trw_layer_acquire_geocache_cb ( gpointer lav[2] )
2990 {
2991   VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
2992   VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
2993   VikWindow *vw = (VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl));
2994   VikViewport *vvp =  vik_window_viewport(vw);
2995
2996   a_acquire ( vw, vlp, vvp, &vik_datasource_gc_interface );
2997 }
2998 #endif
2999
3000 #ifdef VIK_CONFIG_GEOTAG
3001 /*
3002  * Acquire into this TRW Layer from images
3003  */
3004 static void trw_layer_acquire_geotagged_cb ( gpointer lav[2] )
3005 {
3006   VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
3007   VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
3008   VikWindow *vw = (VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl));
3009   VikViewport *vvp =  vik_window_viewport(vw);
3010
3011   vik_datasource_geotag_interface.mode = VIK_DATASOURCE_ADDTOLAYER;
3012   a_acquire ( vw, vlp, vvp, &vik_datasource_geotag_interface );
3013
3014   // Reverify thumbnails as they may have changed
3015   vtl->has_verified_thumbnails = FALSE;
3016   trw_layer_verify_thumbnails ( vtl, NULL );
3017 }
3018 #endif
3019
3020 static void trw_layer_gps_upload ( gpointer lav[2] )
3021 {
3022   gpointer pass_along[6];
3023   pass_along[0] = lav[0];
3024   pass_along[1] = lav[1];
3025   pass_along[2] = NULL; // No track - operate on the layer
3026   pass_along[3] = NULL;
3027   pass_along[4] = NULL;
3028   pass_along[5] = NULL;
3029
3030   trw_layer_gps_upload_any ( pass_along );
3031 }
3032
3033 /**
3034  * If pass_along[3] is defined that this will upload just that track
3035  */
3036 static void trw_layer_gps_upload_any ( gpointer pass_along[6] )
3037 {
3038   VikTrwLayer *vtl = VIK_TRW_LAYER(pass_along[0]);
3039   VikLayersPanel *vlp = VIK_LAYERS_PANEL(pass_along[1]);
3040
3041   // May not actually get a track here as pass_along[2&3] can be null
3042   VikTrack *track = NULL;
3043   vik_gps_xfer_type xfer_type = TRK; // VIK_TRW_LAYER_SUBLAYER_TRACKS = 0 so hard to test different from NULL!
3044   gboolean xfer_all = FALSE;
3045
3046   if ( pass_along[2] ) {
3047     xfer_all = FALSE;
3048     if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE ) {
3049       track = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
3050       xfer_type = RTE;
3051     }
3052     else if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_TRACK ) {
3053       track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
3054       xfer_type = TRK;
3055     }
3056     else if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_WAYPOINTS ) {
3057       xfer_type = WPT;
3058     }
3059     else if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTES ) {
3060       xfer_type = RTE;
3061     }
3062   }
3063   else if ( !pass_along[4] )
3064     xfer_all = TRUE; // i.e. whole layer
3065
3066   if (track && !track->visible) {
3067     a_dialog_error_msg ( VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Can not upload invisible track.") );
3068     return;
3069   }
3070
3071   GtkWidget *dialog = gtk_dialog_new_with_buttons ( _("GPS Upload"),
3072                                                     VIK_GTK_WINDOW_FROM_LAYER(pass_along[0]),
3073                                                     GTK_DIALOG_DESTROY_WITH_PARENT,
3074                                                     GTK_STOCK_OK,
3075                                                     GTK_RESPONSE_ACCEPT,
3076                                                     GTK_STOCK_CANCEL,
3077                                                     GTK_RESPONSE_REJECT,
3078                                                     NULL );
3079
3080   gtk_dialog_set_default_response ( GTK_DIALOG(dialog), GTK_RESPONSE_ACCEPT );
3081   GtkWidget *response_w = NULL;
3082 #if GTK_CHECK_VERSION (2, 20, 0)
3083   response_w = gtk_dialog_get_widget_for_response ( GTK_DIALOG(dialog), GTK_RESPONSE_ACCEPT );
3084 #endif
3085
3086   if ( response_w )
3087     gtk_widget_grab_focus ( response_w );
3088
3089   gpointer dgs = datasource_gps_setup ( dialog, xfer_type, xfer_all );
3090
3091   if ( gtk_dialog_run ( GTK_DIALOG(dialog) ) != GTK_RESPONSE_ACCEPT ) {
3092     datasource_gps_clean_up ( dgs );
3093     gtk_widget_destroy ( dialog );
3094     return;
3095   }
3096
3097   // Get info from reused datasource dialog widgets
3098   gchar* protocol = datasource_gps_get_protocol ( dgs );
3099   gchar* port = datasource_gps_get_descriptor ( dgs );
3100   // NB don't free the above strings as they're references to values held elsewhere
3101   gboolean do_tracks = datasource_gps_get_do_tracks ( dgs );
3102   gboolean do_routes = datasource_gps_get_do_routes ( dgs );
3103   gboolean do_waypoints = datasource_gps_get_do_waypoints ( dgs );
3104   gboolean turn_off = datasource_gps_get_off ( dgs );
3105
3106   gtk_widget_destroy ( dialog );
3107
3108   // When called from the viewport - work the corresponding layerspanel:
3109   if ( !vlp ) {
3110     vlp = vik_window_layers_panel ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)) );
3111   }
3112
3113   // Apply settings to transfer to the GPS device
3114   vik_gps_comm ( vtl,
3115                  track,
3116                  GPS_UP,
3117                  protocol,
3118                  port,
3119                  FALSE,
3120                  vik_layers_panel_get_viewport (vlp),
3121                  vlp,
3122                  do_tracks,
3123                  do_routes,
3124                  do_waypoints,
3125                  turn_off );
3126 }
3127
3128 /*
3129  * Acquire into this TRW Layer from any GPS Babel supported file
3130  */
3131 static void trw_layer_acquire_file_cb ( gpointer lav[2] )
3132 {
3133   VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
3134   VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
3135   VikWindow *vw = (VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl));
3136   VikViewport *vvp =  vik_window_viewport(vw);
3137
3138   a_acquire ( vw, vlp, vvp, &vik_datasource_file_interface );
3139 }
3140
3141 static void trw_layer_new_wp ( gpointer lav[2] )
3142 {
3143   VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
3144   VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
3145   /* TODO longone: okay, if layer above (aggregate) is invisible but vtl->visible is true, this redraws for no reason.
3146      instead return true if you want to update. */
3147   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 )
3148     vik_layers_panel_emit_update ( vlp );
3149 }
3150
3151 static void trw_layer_new_track ( gpointer lav[2] )
3152 {
3153   VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
3154
3155   if ( ! vtl->current_track ) {
3156     gchar *name = trw_layer_new_unique_sublayer_name ( vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, _("Track")) ;
3157     vtl->current_track = vik_track_new();
3158     vtl->current_track->visible = TRUE;
3159     vik_trw_layer_add_track ( vtl, name, vtl->current_track );
3160
3161     vik_window_enable_layer_tool ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)), VIK_LAYER_TRW, TOOL_CREATE_TRACK );
3162   }
3163 }
3164
3165 static void new_route_create_common ( VikTrwLayer *vtl, gchar *name )
3166 {
3167   vtl->current_track = vik_track_new();
3168   vtl->current_track->visible = TRUE;
3169   vtl->current_track->is_route = TRUE;
3170   // By default make all routes red
3171   vtl->current_track->has_color = TRUE;
3172   gdk_color_parse ( "red", &vtl->current_track->color );
3173   vik_trw_layer_add_route ( vtl, name, vtl->current_track );
3174 }
3175
3176 static void trw_layer_new_route ( gpointer lav[2] )
3177 {
3178   VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
3179
3180   if ( ! vtl->current_track ) {
3181     gchar *name = trw_layer_new_unique_sublayer_name ( vtl, VIK_TRW_LAYER_SUBLAYER_ROUTE, _("Route")) ;
3182     new_route_create_common ( vtl, name );
3183     vik_window_enable_layer_tool ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)), VIK_LAYER_TRW, TOOL_CREATE_ROUTE );
3184   }
3185 }
3186
3187 static void trw_layer_auto_routes_view ( gpointer lav[2] )
3188 {
3189   VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
3190   VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
3191
3192   if ( g_hash_table_size (vtl->routes) > 0 ) {
3193     struct LatLon maxmin[2] = { {0,0}, {0,0} };
3194     g_hash_table_foreach ( vtl->routes, (GHFunc) trw_layer_find_maxmin_tracks, maxmin );
3195     trw_layer_zoom_to_show_latlons ( vtl, vik_layers_panel_get_viewport (vlp), maxmin );
3196     vik_layers_panel_emit_update ( vlp );
3197   }
3198 }
3199
3200
3201 static void trw_layer_finish_track ( gpointer lav[2] )
3202 {
3203   VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
3204   vtl->current_track = NULL;
3205   vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
3206 }
3207
3208 static void trw_layer_auto_tracks_view ( gpointer lav[2] )
3209 {
3210   VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
3211   VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
3212
3213   if ( g_hash_table_size (vtl->tracks) > 0 ) {
3214     struct LatLon maxmin[2] = { {0,0}, {0,0} };
3215     g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_find_maxmin_tracks, maxmin );
3216     trw_layer_zoom_to_show_latlons ( vtl, vik_layers_panel_get_viewport (vlp), maxmin );
3217     vik_layers_panel_emit_update ( vlp );
3218   }
3219 }
3220
3221 static void trw_layer_single_waypoint_jump ( const gchar *name, const VikWaypoint *wp, gpointer vvp )
3222 {
3223   /* NB do not care if wp is visible or not */
3224   vik_viewport_set_center_coord ( VIK_VIEWPORT(vvp), &(wp->coord) );
3225 }
3226
3227 static void trw_layer_auto_waypoints_view ( gpointer lav[2] )
3228 {
3229   VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
3230   VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
3231
3232   /* Only 1 waypoint - jump straight to it */
3233   if ( g_hash_table_size (vtl->waypoints) == 1 ) {
3234     VikViewport *vvp = vik_layers_panel_get_viewport (vlp);
3235     g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_single_waypoint_jump, (gpointer) vvp );
3236   }
3237   /* If at least 2 waypoints - find center and then zoom to fit */
3238   else if ( g_hash_table_size (vtl->waypoints) > 1 )
3239   {
3240     struct LatLon maxmin[2] = { {0,0}, {0,0} };
3241     g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_find_maxmin_waypoints, maxmin );
3242     trw_layer_zoom_to_show_latlons ( vtl, vik_layers_panel_get_viewport (vlp), maxmin );
3243   }
3244
3245   vik_layers_panel_emit_update ( vlp );
3246 }
3247
3248 static void trw_layer_add_menu_items ( VikTrwLayer *vtl, GtkMenu *menu, gpointer vlp )
3249 {
3250   static gpointer pass_along[2];
3251   GtkWidget *item;
3252   GtkWidget *export_submenu;
3253   pass_along[0] = vtl;
3254   pass_along[1] = vlp;
3255
3256   item = gtk_menu_item_new();
3257   gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
3258   gtk_widget_show ( item );
3259
3260   if ( vtl->current_track ) {
3261     if ( vtl->current_track->is_route )
3262       item = gtk_menu_item_new_with_mnemonic ( _("_Finish Route") );
3263     else
3264       item = gtk_menu_item_new_with_mnemonic ( _("_Finish Track") );
3265     g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_finish_track), pass_along );
3266     gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3267     gtk_widget_show ( item );
3268
3269     // Add separator
3270     item = gtk_menu_item_new ();
3271     gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
3272     gtk_widget_show ( item );
3273   }
3274
3275   /* Now with icons */
3276   item = gtk_image_menu_item_new_with_mnemonic ( _("_View Layer") );
3277   gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ZOOM_FIT, GTK_ICON_SIZE_MENU) );
3278   g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_view), pass_along );
3279   gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3280   gtk_widget_show ( item );
3281
3282   GtkWidget *view_submenu = gtk_menu_new();
3283   item = gtk_image_menu_item_new_with_mnemonic ( _("V_iew") );
3284   gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_FIND, GTK_ICON_SIZE_MENU) );
3285   gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3286   gtk_widget_show ( item );
3287   gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), view_submenu );
3288
3289   item = gtk_menu_item_new_with_mnemonic ( _("View All _Tracks") );
3290   g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_tracks_view), pass_along );
3291   gtk_menu_shell_append (GTK_MENU_SHELL (view_submenu), item);
3292   gtk_widget_show ( item );
3293
3294   item = gtk_menu_item_new_with_mnemonic ( _("View All _Routes") );
3295   g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_routes_view), pass_along );
3296   gtk_menu_shell_append (GTK_MENU_SHELL (view_submenu), item);
3297   gtk_widget_show ( item );
3298
3299   item = gtk_menu_item_new_with_mnemonic ( _("View All _Waypoints") );
3300   g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_waypoints_view), pass_along );
3301   gtk_menu_shell_append (GTK_MENU_SHELL (view_submenu), item);
3302   gtk_widget_show ( item );
3303
3304   item = gtk_image_menu_item_new_with_mnemonic ( _("_Goto Center of Layer") );
3305   gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_JUMP_TO, GTK_ICON_SIZE_MENU) );
3306   g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_centerize), pass_along );
3307   gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3308   gtk_widget_show ( item );
3309
3310   item = gtk_menu_item_new_with_mnemonic ( _("Goto _Waypoint...") );
3311   g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_wp), pass_along );
3312   gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3313   gtk_widget_show ( item );
3314
3315   export_submenu = gtk_menu_new ();
3316   item = gtk_image_menu_item_new_with_mnemonic ( _("_Export Layer") );
3317   gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_HARDDISK, GTK_ICON_SIZE_MENU) );
3318   gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3319   gtk_widget_show ( item );
3320   gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), export_submenu );
3321   
3322   item = gtk_menu_item_new_with_mnemonic ( _("Export as GPS_Point...") );
3323   g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_gpspoint), pass_along );
3324   gtk_menu_shell_append (GTK_MENU_SHELL (export_submenu), item);
3325   gtk_widget_show ( item );
3326
3327   item = gtk_menu_item_new_with_mnemonic ( _("Export as GPS_Mapper...") );
3328   g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_gpsmapper), pass_along );
3329   gtk_menu_shell_append (GTK_MENU_SHELL (export_submenu), item);
3330   gtk_widget_show ( item );
3331
3332   item = gtk_menu_item_new_with_mnemonic ( _("Export as _GPX...") );
3333   g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_gpx), pass_along );
3334   gtk_menu_shell_append (GTK_MENU_SHELL (export_submenu), item);
3335   gtk_widget_show ( item );
3336
3337   item = gtk_menu_item_new_with_mnemonic ( _("Export as _KML...") );
3338   g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_kml), pass_along );
3339   gtk_menu_shell_append (GTK_MENU_SHELL (export_submenu), item);
3340   gtk_widget_show ( item );
3341
3342   gchar* external1 = g_strconcat ( _("Open with External Program_1: "), a_vik_get_external_gpx_program_1(), NULL );
3343   item = gtk_menu_item_new_with_mnemonic ( external1 );
3344   g_free ( external1 );
3345   g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_external_gpx_1), pass_along );
3346   gtk_menu_shell_append (GTK_MENU_SHELL (export_submenu), item);
3347   gtk_widget_show ( item );
3348
3349   gchar* external2 = g_strconcat ( _("Open with External Program_2: "), a_vik_get_external_gpx_program_2(), NULL );
3350   item = gtk_menu_item_new_with_mnemonic ( external2 );
3351   g_free ( external2 );
3352   g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_external_gpx_2), pass_along );
3353   gtk_menu_shell_append (GTK_MENU_SHELL (export_submenu), item);
3354   gtk_widget_show ( item );
3355
3356   GtkWidget *new_submenu = gtk_menu_new();
3357   item = gtk_image_menu_item_new_with_mnemonic ( _("_New") );
3358   gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NEW, GTK_ICON_SIZE_MENU) );
3359   gtk_menu_shell_append(GTK_MENU_SHELL (menu), item);
3360   gtk_widget_show(item);
3361   gtk_menu_item_set_submenu(GTK_MENU_ITEM(item), new_submenu);
3362
3363   item = gtk_image_menu_item_new_with_mnemonic ( _("New _Waypoint...") );
3364   gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NEW, GTK_ICON_SIZE_MENU) );
3365   g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_wp), pass_along );
3366   gtk_menu_shell_append (GTK_MENU_SHELL (new_submenu), item);
3367   gtk_widget_show ( item );
3368
3369   item = gtk_image_menu_item_new_with_mnemonic ( _("New _Track") );
3370   gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NEW, GTK_ICON_SIZE_MENU) );
3371   g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_track), pass_along );
3372   gtk_menu_shell_append (GTK_MENU_SHELL (new_submenu), item);
3373   gtk_widget_show ( item );
3374   // Make it available only when a new track *not* already in progress
3375   gtk_widget_set_sensitive ( item, ! (gboolean)GPOINTER_TO_INT(vtl->current_track) );
3376
3377   item = gtk_image_menu_item_new_with_mnemonic ( _("New _Route") );
3378   gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NEW, GTK_ICON_SIZE_MENU) );
3379   g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_route), pass_along );
3380   gtk_menu_shell_append (GTK_MENU_SHELL (new_submenu), item);
3381   gtk_widget_show ( item );
3382   // Make it available only when a new track *not* already in progress
3383   gtk_widget_set_sensitive ( item, ! (gboolean)GPOINTER_TO_INT(vtl->current_track) );
3384
3385 #ifdef VIK_CONFIG_GEONAMES
3386   GtkWidget *wikipedia_submenu = gtk_menu_new();
3387   item = gtk_image_menu_item_new_with_mnemonic ( _("_Add Wikipedia Waypoints") );
3388   gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ADD, GTK_ICON_SIZE_MENU) );
3389   gtk_menu_shell_append(GTK_MENU_SHELL (menu), item);
3390   gtk_widget_show(item);
3391   gtk_menu_item_set_submenu(GTK_MENU_ITEM(item), wikipedia_submenu);
3392
3393   item = gtk_image_menu_item_new_with_mnemonic ( _("Within _Layer Bounds") );
3394   gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ZOOM_FIT, GTK_ICON_SIZE_MENU) );
3395   g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_wikipedia_wp_layer), pass_along );
3396   gtk_menu_shell_append (GTK_MENU_SHELL (wikipedia_submenu), item);
3397   gtk_widget_show ( item );
3398
3399   item = gtk_image_menu_item_new_with_mnemonic ( _("Within _Current View") );
3400   gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ZOOM_100, GTK_ICON_SIZE_MENU) );
3401   g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_wikipedia_wp_viewport), pass_along );
3402   gtk_menu_shell_append (GTK_MENU_SHELL (wikipedia_submenu), item);
3403   gtk_widget_show ( item );
3404 #endif
3405
3406 #ifdef VIK_CONFIG_GEOTAG
3407   item = gtk_menu_item_new_with_mnemonic ( _("Geotag _Images...") );
3408   g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_geotagging), pass_along );
3409   gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3410   gtk_widget_show ( item );
3411 #endif
3412
3413   GtkWidget *acquire_submenu = gtk_menu_new ();
3414   item = gtk_image_menu_item_new_with_mnemonic ( _("Ac_quire") );
3415   gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_DOWN, GTK_ICON_SIZE_MENU) );
3416   gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3417   gtk_widget_show ( item );
3418   gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), acquire_submenu );
3419   
3420   item = gtk_menu_item_new_with_mnemonic ( _("From _GPS...") );
3421   g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_acquire_gps_cb), pass_along );
3422   gtk_menu_shell_append (GTK_MENU_SHELL (acquire_submenu), item);
3423   gtk_widget_show ( item );
3424
3425 #ifdef VIK_CONFIG_GOOGLE
3426   item = gtk_menu_item_new_with_mnemonic ( _("From G_oogle Directions...") );
3427   g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_acquire_google_cb), pass_along );
3428   gtk_menu_shell_append (GTK_MENU_SHELL (acquire_submenu), item);
3429   gtk_widget_show ( item );
3430 #endif
3431
3432 #ifdef VIK_CONFIG_OPENSTREETMAP
3433   item = gtk_menu_item_new_with_mnemonic ( _("From _OSM Traces...") );
3434   g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_acquire_osm_cb), pass_along );
3435   gtk_menu_shell_append (GTK_MENU_SHELL (acquire_submenu), item);
3436   gtk_widget_show ( item );
3437 #endif
3438
3439 #ifdef VIK_CONFIG_GEOCACHES
3440   item = gtk_menu_item_new_with_mnemonic ( _("From Geo_caching...") );
3441   g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_acquire_geocache_cb), pass_along );
3442   gtk_menu_shell_append (GTK_MENU_SHELL (acquire_submenu), item);
3443   gtk_widget_show ( item );
3444 #endif
3445
3446 #ifdef VIK_CONFIG_GEOTAG
3447   item = gtk_menu_item_new_with_mnemonic ( _("From Geotagged _Images...") );
3448   g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_acquire_geotagged_cb), pass_along );
3449   gtk_menu_shell_append (GTK_MENU_SHELL (acquire_submenu), item);
3450   gtk_widget_show ( item );
3451 #endif
3452
3453   item = gtk_menu_item_new_with_mnemonic ( _("From _File...") );
3454   g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_acquire_file_cb), pass_along );
3455   gtk_menu_shell_append (GTK_MENU_SHELL (acquire_submenu), item);
3456   gtk_widget_show ( item );
3457
3458   GtkWidget *upload_submenu = gtk_menu_new ();
3459   item = gtk_image_menu_item_new_with_mnemonic ( _("_Upload") );
3460   gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_UP, GTK_ICON_SIZE_MENU) );
3461   gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3462   gtk_widget_show ( item );
3463   gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), upload_submenu );
3464
3465   item = gtk_image_menu_item_new_with_mnemonic ( _("Upload to _GPS...") );
3466   gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_FORWARD, GTK_ICON_SIZE_MENU) );
3467   g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_gps_upload), pass_along );
3468   gtk_menu_shell_append (GTK_MENU_SHELL (upload_submenu), item);
3469   gtk_widget_show ( item );
3470
3471 #ifdef VIK_CONFIG_OPENSTREETMAP 
3472   item = gtk_image_menu_item_new_with_mnemonic ( _("Upload to _OSM...") );
3473   gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_UP, GTK_ICON_SIZE_MENU) );
3474   g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(osm_traces_upload_cb), pass_along );
3475   gtk_menu_shell_append (GTK_MENU_SHELL (upload_submenu), item);
3476   gtk_widget_show ( item );
3477 #endif
3478
3479   GtkWidget *delete_submenu = gtk_menu_new ();
3480   item = gtk_image_menu_item_new_with_mnemonic ( _("De_lete") );
3481   gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU) );
3482   gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3483   gtk_widget_show ( item );
3484   gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), delete_submenu );
3485   
3486   item = gtk_image_menu_item_new_with_mnemonic ( _("Delete All _Tracks") );
3487   gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU) );
3488   g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_all_tracks), pass_along );
3489   gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
3490   gtk_widget_show ( item );
3491   
3492   item = gtk_image_menu_item_new_with_mnemonic ( _("Delete Tracks _From Selection...") );
3493   gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
3494   g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_tracks_from_selection), pass_along );
3495   gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
3496   gtk_widget_show ( item );
3497   
3498   item = gtk_image_menu_item_new_with_mnemonic ( _("Delete All _Waypoints") );
3499   gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU) );
3500   g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_all_waypoints), pass_along );
3501   gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
3502   gtk_widget_show ( item );
3503   
3504   item = gtk_image_menu_item_new_with_mnemonic ( _("Delete Waypoints From _Selection...") );
3505   gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
3506   g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_waypoints_from_selection), pass_along );
3507   gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
3508   gtk_widget_show ( item );
3509   
3510   item = a_acquire_trwlayer_menu ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)), vlp,
3511                                    vik_layers_panel_get_viewport(VIK_LAYERS_PANEL(vlp)), vtl );
3512   if ( item ) {
3513     gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3514     gtk_widget_show ( item );
3515   }  
3516
3517   item = a_acquire_trwlayer_track_menu ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)), vlp,
3518                                          vik_layers_panel_get_viewport(VIK_LAYERS_PANEL(vlp)), vtl );
3519   if ( item ) {
3520     gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3521     gtk_widget_show ( item );
3522   }  
3523 }
3524
3525 // Fake Waypoint UUIDs vi simple increasing integer
3526 static guint wp_uuid = 0;
3527
3528 void vik_trw_layer_add_waypoint ( VikTrwLayer *vtl, gchar *name, VikWaypoint *wp )
3529 {
3530   wp_uuid++;
3531
3532   vik_waypoint_set_name (wp, name);
3533
3534   if ( VIK_LAYER(vtl)->realized )
3535   {
3536     // Do we need to create the sublayer:
3537     if ( g_hash_table_size (vtl->waypoints) == 0 ) {
3538       trw_layer_add_sublayer_waypoints ( vtl, VIK_LAYER(vtl)->vt, &(VIK_LAYER(vtl)->iter) );
3539     }
3540
3541     GtkTreeIter *iter = g_malloc(sizeof(GtkTreeIter));
3542
3543     // Visibility column always needed for waypoints
3544 #ifdef VIK_CONFIG_ALPHABETIZED_TRW
3545     vik_treeview_add_sublayer_alphabetized ( VIK_LAYER(vtl)->vt, &(vtl->waypoints_iter), iter, name, vtl, GUINT_TO_POINTER(wp_uuid), VIK_TRW_LAYER_SUBLAYER_WAYPOINT, get_wp_sym_small (wp->symbol), TRUE, TRUE );
3546 #else
3547     vik_treeview_add_sublayer ( VIK_LAYER(vtl)->vt, &(vtl->waypoints_iter), iter, name, vtl, GUINT_TO_POINTER(wp_uuid), VIK_TRW_LAYER_SUBLAYER_WAYPOINT, get_wp_sym_small (wp->symbol), TRUE, TRUE );
3548 #endif
3549     // Actual setting of visibility dependent on the waypoint
3550     vik_treeview_item_set_visible ( VIK_LAYER(vtl)->vt, iter, wp->visible );
3551
3552     g_hash_table_insert ( vtl->waypoints_iters, GUINT_TO_POINTER(wp_uuid), iter );
3553   }
3554
3555   highest_wp_number_add_wp(vtl, name);
3556   g_hash_table_insert ( vtl->waypoints, GUINT_TO_POINTER(wp_uuid), wp );
3557  
3558 }
3559
3560 // Fake Track UUIDs vi simple increasing integer
3561 static guint tr_uuid = 0;
3562
3563 void vik_trw_layer_add_track ( VikTrwLayer *vtl, gchar *name, VikTrack *t )
3564 {
3565   tr_uuid++;
3566
3567   vik_track_set_name (t, name);
3568
3569   if ( VIK_LAYER(vtl)->realized )
3570   {
3571     // Do we need to create the sublayer:
3572     if ( g_hash_table_size (vtl->tracks) == 0 ) {
3573       trw_layer_add_sublayer_tracks ( vtl, VIK_LAYER(vtl)->vt, &(VIK_LAYER(vtl)->iter) );
3574     }
3575
3576     GtkTreeIter *iter = g_malloc(sizeof(GtkTreeIter));
3577     // Visibility column always needed for tracks
3578 #ifdef VIK_CONFIG_ALPHABETIZED_TRW
3579     vik_treeview_add_sublayer_alphabetized ( VIK_LAYER(vtl)->vt, &(vtl->tracks_iter), iter, name, vtl, GUINT_TO_POINTER(tr_uuid), VIK_TRW_LAYER_SUBLAYER_TRACK, NULL, TRUE, TRUE );
3580 #else
3581     vik_treeview_add_sublayer ( VIK_LAYER(vtl)->vt, &(vtl->tracks_iter), iter, name, vtl, GUINT_TO_POINTER(tr_uuid), VIK_TRW_LAYER_SUBLAYER_TRACK, NULL, TRUE, TRUE );
3582 #endif
3583     // Actual setting of visibility dependent on the track
3584     vik_treeview_item_set_visible ( VIK_LAYER(vtl)->vt, iter, t->visible );
3585
3586     g_hash_table_insert ( vtl->tracks_iters, GUINT_TO_POINTER(tr_uuid), iter );
3587   }
3588
3589   g_hash_table_insert ( vtl->tracks, GUINT_TO_POINTER(tr_uuid), t );
3590
3591   trw_layer_update_treeview ( vtl, t, GUINT_TO_POINTER(tr_uuid) );
3592 }
3593
3594 // Fake Route UUIDs vi simple increasing integer
3595 static guint rt_uuid = 0;
3596
3597 void vik_trw_layer_add_route ( VikTrwLayer *vtl, gchar *name, VikTrack *t )
3598 {
3599   rt_uuid++;
3600
3601   vik_track_set_name (t, name);
3602
3603   if ( VIK_LAYER(vtl)->realized )
3604   {
3605     // Do we need to create the sublayer:
3606     if ( g_hash_table_size (vtl->routes) == 0 ) {
3607       trw_layer_add_sublayer_routes ( vtl, VIK_LAYER(vtl)->vt, &(VIK_LAYER(vtl)->iter) );
3608     }
3609
3610     GtkTreeIter *iter = g_malloc(sizeof(GtkTreeIter));
3611     // Visibility column always needed for tracks
3612 #ifdef VIK_CONFIG_ALPHABETIZED_TRW
3613     vik_treeview_add_sublayer_alphabetized ( VIK_LAYER(vtl)->vt, &(vtl->routes_iter), iter, name, vtl, GUINT_TO_POINTER(rt_uuid), VIK_TRW_LAYER_SUBLAYER_ROUTE, NULL, TRUE, TRUE );
3614 #else
3615     vik_treeview_add_sublayer ( VIK_LAYER(vtl)->vt, &(vtl->routes_iter), iter, name, vtl, GUINT_TO_POINTER(rt_uuid), VIK_TRW_LAYER_SUBLAYER_ROUTE, NULL, TRUE, TRUE );
3616 #endif
3617     // Actual setting of visibility dependent on the track
3618     vik_treeview_item_set_visible ( VIK_LAYER(vtl)->vt, iter, t->visible );
3619
3620     g_hash_table_insert ( vtl->routes_iters, GUINT_TO_POINTER(rt_uuid), iter );
3621   }
3622
3623   g_hash_table_insert ( vtl->routes, GUINT_TO_POINTER(rt_uuid), t );
3624
3625   trw_layer_update_treeview ( vtl, t, GUINT_TO_POINTER(rt_uuid) );
3626 }
3627
3628 /* to be called whenever a track has been deleted or may have been changed. */
3629 void trw_layer_cancel_tps_of_track ( VikTrwLayer *vtl, VikTrack *trk )
3630 {
3631   if (vtl->current_tp_track == trk )
3632     trw_layer_cancel_current_tp ( vtl, FALSE );
3633 }
3634
3635 gchar *trw_layer_new_unique_sublayer_name (VikTrwLayer *vtl, gint sublayer_type, const gchar *name)
3636 {
3637   gint i = 2;
3638   gchar *newname = g_strdup(name);
3639
3640   gpointer id = NULL;
3641   do {
3642     id = NULL;
3643     switch ( sublayer_type ) {
3644     case VIK_TRW_LAYER_SUBLAYER_TRACK:
3645       id = (gpointer) vik_trw_layer_get_track ( vtl, newname );
3646       break;
3647     case VIK_TRW_LAYER_SUBLAYER_WAYPOINT:
3648       id = (gpointer) vik_trw_layer_get_waypoint ( vtl, newname );
3649       break;
3650     default:
3651       id = (gpointer) vik_trw_layer_get_route ( vtl, newname );
3652       break;
3653     }
3654     // If found a name already in use try adding 1 to it and we try again
3655     if ( id ) {
3656       gchar *new_newname = g_strdup_printf("%s#%d", name, i);
3657       g_free(newname);
3658       newname = new_newname;
3659       i++;
3660     }
3661   } while ( id != NULL);
3662
3663   return newname;
3664 }
3665
3666 void vik_trw_layer_filein_add_waypoint ( VikTrwLayer *vtl, gchar *name, VikWaypoint *wp )
3667 {
3668   // No more uniqueness of name forced when loading from a file
3669   // This now makes this function a little redunant as we just flow the parameters through
3670   vik_trw_layer_add_waypoint ( vtl, name, wp );
3671 }
3672
3673 void vik_trw_layer_filein_add_track ( VikTrwLayer *vtl, gchar *name, VikTrack *tr )
3674 {
3675   if ( vtl->route_finder_append && vtl->route_finder_current_track ) {
3676     vik_track_remove_dup_points ( tr ); /* make "double point" track work to undo */
3677     vik_track_steal_and_append_trackpoints ( vtl->route_finder_current_track, tr );
3678     vik_track_free ( tr );
3679     vtl->route_finder_append = FALSE; /* this means we have added it */
3680   } else {
3681
3682     // No more uniqueness of name forced when loading from a file
3683     if ( tr->is_route )
3684       vik_trw_layer_add_route ( vtl, name, tr );
3685     else
3686       vik_trw_layer_add_track ( vtl, name, tr );
3687
3688     if ( vtl->route_finder_check_added_track ) {
3689       vik_track_remove_dup_points ( tr ); /* make "double point" track work to undo */
3690       vtl->route_finder_added_track = tr;
3691     }
3692   }
3693 }
3694
3695 static void trw_layer_enum_item ( gpointer id, GList **tr, GList **l )
3696 {
3697   *l = g_list_append(*l, id);
3698 }
3699
3700 /*
3701  * Move an item from one TRW layer to another TRW layer
3702  */
3703 static void trw_layer_move_item ( VikTrwLayer *vtl_src, VikTrwLayer *vtl_dest, gpointer id, gint type )
3704 {
3705   if (type == VIK_TRW_LAYER_SUBLAYER_TRACK) {
3706     VikTrack *trk = g_hash_table_lookup ( vtl_src->tracks, id );
3707
3708     gchar *newname = trw_layer_new_unique_sublayer_name(vtl_dest, type, trk->name);
3709
3710     VikTrack *trk2 = vik_track_copy ( trk );
3711     vik_trw_layer_add_track ( vtl_dest, newname, trk2 );
3712     vik_trw_layer_delete_track ( vtl_src, trk );
3713   }
3714
3715   if (type == VIK_TRW_LAYER_SUBLAYER_ROUTE) {
3716     VikTrack *trk = g_hash_table_lookup ( vtl_src->routes, id );
3717
3718     gchar *newname = trw_layer_new_unique_sublayer_name(vtl_dest, type, trk->name);
3719
3720     VikTrack *trk2 = vik_track_copy ( trk );
3721     vik_trw_layer_add_route ( vtl_dest, newname, trk2 );
3722     vik_trw_layer_delete_route ( vtl_src, trk );
3723   }
3724
3725   if (type == VIK_TRW_LAYER_SUBLAYER_WAYPOINT) {
3726     VikWaypoint *wp = g_hash_table_lookup ( vtl_src->waypoints, id );
3727
3728     gchar *newname = trw_layer_new_unique_sublayer_name(vtl_dest, type, wp->name);
3729
3730     VikWaypoint *wp2 = vik_waypoint_copy ( wp );
3731     vik_trw_layer_add_waypoint ( vtl_dest, newname, wp2 );
3732     trw_layer_delete_waypoint ( vtl_src, wp );
3733   }
3734 }
3735
3736 static void trw_layer_drag_drop_request ( VikTrwLayer *vtl_src, VikTrwLayer *vtl_dest, GtkTreeIter *src_item_iter, GtkTreePath *dest_path )
3737 {
3738   VikTreeview *vt = VIK_LAYER(vtl_src)->vt;
3739   gint type = vik_treeview_item_get_data(vt, src_item_iter);
3740
3741   if (!vik_treeview_item_get_pointer(vt, src_item_iter)) {
3742     GList *items = NULL;
3743     GList *iter;
3744
3745     if (type==VIK_TRW_LAYER_SUBLAYER_TRACKS) {
3746       g_hash_table_foreach ( vtl_src->tracks, (GHFunc)trw_layer_enum_item, &items);
3747     } 
3748     if (type==VIK_TRW_LAYER_SUBLAYER_WAYPOINTS) {
3749       g_hash_table_foreach ( vtl_src->waypoints, (GHFunc)trw_layer_enum_item, &items);
3750     }    
3751     if (type==VIK_TRW_LAYER_SUBLAYER_ROUTES) {
3752       g_hash_table_foreach ( vtl_src->routes, (GHFunc)trw_layer_enum_item, &items);
3753     }
3754
3755     iter = items;
3756     while (iter) {
3757       if (type==VIK_TRW_LAYER_SUBLAYER_TRACKS) {
3758         trw_layer_move_item ( vtl_src, vtl_dest, iter->data, VIK_TRW_LAYER_SUBLAYER_TRACK);
3759       }
3760       else if (type==VIK_TRW_LAYER_SUBLAYER_ROUTES) {
3761         trw_layer_move_item ( vtl_src, vtl_dest, iter->data, VIK_TRW_LAYER_SUBLAYER_ROUTE);
3762       } else {
3763         trw_layer_move_item ( vtl_src, vtl_dest, iter->data, VIK_TRW_LAYER_SUBLAYER_WAYPOINT);
3764       }
3765       iter = iter->next;
3766     }
3767     if (items) 
3768       g_list_free(items);
3769   } else {
3770     gchar *name = vik_treeview_item_get_pointer(vt, src_item_iter);
3771     trw_layer_move_item(vtl_src, vtl_dest, name, type);
3772   }
3773 }
3774
3775 typedef struct {
3776   VikTrack *trk; // input
3777   gpointer uuid;   // output
3778 } trku_udata;
3779
3780 static gboolean trw_layer_track_find_uuid ( const gpointer id, const VikTrack *trk, gpointer udata )
3781 {
3782   trku_udata *user_data = udata;
3783   if ( trk == user_data->trk ) {
3784     user_data->uuid = id;
3785     return TRUE;
3786   }
3787   return FALSE;
3788 }
3789
3790 gboolean vik_trw_layer_delete_track ( VikTrwLayer *vtl, VikTrack *trk )
3791 {
3792   gboolean was_visible = FALSE;
3793
3794   if ( trk && trk->name ) {
3795
3796     if ( trk == vtl->current_track ) {
3797       vtl->current_track = NULL;
3798       vtl->current_tp_track = NULL;
3799       vtl->current_tp_id = NULL;
3800       vtl->moving_tp = FALSE;
3801     }
3802
3803     was_visible = trk->visible;
3804
3805     if ( trk == vtl->route_finder_current_track )
3806       vtl->route_finder_current_track = NULL;
3807
3808     if ( trk == vtl->route_finder_added_track )
3809       vtl->route_finder_added_track = NULL;
3810
3811     trku_udata udata;
3812     udata.trk  = trk;
3813     udata.uuid = NULL;
3814
3815     // Hmmm, want key of it
3816     gpointer *trkf = g_hash_table_find ( vtl->tracks, (GHRFunc) trw_layer_track_find_uuid, &udata );
3817
3818     if ( trkf && udata.uuid ) {
3819       /* could be current_tp, so we have to check */
3820       trw_layer_cancel_tps_of_track ( vtl, trk );
3821
3822       GtkTreeIter *it = g_hash_table_lookup ( vtl->tracks_iters, udata.uuid );
3823
3824       if ( it ) {
3825         vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, it );
3826         g_hash_table_remove ( vtl->tracks_iters, udata.uuid );
3827         g_hash_table_remove ( vtl->tracks, udata.uuid );
3828
3829         // If last sublayer, then remove sublayer container
3830         if ( g_hash_table_size (vtl->tracks) == 0 ) {
3831           vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, &(vtl->tracks_iter) );
3832         }
3833       }
3834     }
3835   }
3836   return was_visible;
3837 }
3838
3839 gboolean vik_trw_layer_delete_route ( VikTrwLayer *vtl, VikTrack *trk )
3840 {
3841   gboolean was_visible = FALSE;
3842
3843   if ( trk && trk->name ) {
3844
3845     if ( trk == vtl->current_track ) {
3846       vtl->current_track = NULL;
3847       vtl->current_tp_track = NULL;
3848       vtl->current_tp_id = NULL;
3849       vtl->moving_tp = FALSE;
3850     }
3851
3852     was_visible = trk->visible;
3853
3854     if ( trk == vtl->route_finder_current_track )
3855       vtl->route_finder_current_track = NULL;
3856
3857     if ( trk == vtl->route_finder_added_track )
3858       vtl->route_finder_added_track = NULL;
3859
3860     trku_udata udata;
3861     udata.trk  = trk;
3862     udata.uuid = NULL;
3863
3864     // Hmmm, want key of it
3865     gpointer *trkf = g_hash_table_find ( vtl->routes, (GHRFunc) trw_layer_track_find_uuid, &udata );
3866
3867     if ( trkf && udata.uuid ) {
3868       /* could be current_tp, so we have to check */
3869       trw_layer_cancel_tps_of_track ( vtl, trk );
3870
3871       GtkTreeIter *it = g_hash_table_lookup ( vtl->routes_iters, udata.uuid );
3872
3873       if ( it ) {
3874         vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, it );
3875         g_hash_table_remove ( vtl->routes_iters, udata.uuid );
3876         g_hash_table_remove ( vtl->routes, udata.uuid );
3877
3878         // If last sublayer, then remove sublayer container
3879         if ( g_hash_table_size (vtl->routes) == 0 ) {
3880           vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, &(vtl->routes_iter) );
3881         }
3882       }
3883     }
3884   }
3885   return was_visible;
3886 }
3887
3888 static gboolean trw_layer_delete_waypoint ( VikTrwLayer *vtl, VikWaypoint *wp )
3889 {
3890   gboolean was_visible = FALSE;
3891
3892   if ( wp && wp->name ) {
3893
3894     if ( wp == vtl->current_wp ) {
3895       vtl->current_wp = NULL;
3896       vtl->current_wp_id = NULL;
3897       vtl->moving_wp = FALSE;
3898     }
3899
3900     was_visible = wp->visible;
3901     
3902     wpu_udata udata;
3903     udata.wp   = wp;
3904     udata.uuid = NULL;
3905
3906     // Hmmm, want key of it
3907     gpointer *wpf = g_hash_table_find ( vtl->waypoints, (GHRFunc) trw_layer_waypoint_find_uuid, (gpointer) &udata );
3908
3909     if ( wpf && udata.uuid ) {
3910       GtkTreeIter *it = g_hash_table_lookup ( vtl->waypoints_iters, udata.uuid );
3911     
3912       if ( it ) {
3913         vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, it );
3914         g_hash_table_remove ( vtl->waypoints_iters, udata.uuid );
3915
3916         highest_wp_number_remove_wp(vtl, wp->name);
3917         g_hash_table_remove ( vtl->waypoints, udata.uuid ); // last because this frees the name
3918
3919         // If last sublayer, then remove sublayer container
3920         if ( g_hash_table_size (vtl->waypoints) == 0 ) {
3921           vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, &(vtl->waypoints_iter) );
3922         }
3923       }
3924     }
3925
3926   }
3927
3928   return was_visible;
3929 }
3930
3931 // Only for temporary use by trw_layer_delete_waypoint_by_name
3932 static gboolean trw_layer_waypoint_find_uuid_by_name ( const gpointer id, const VikWaypoint *wp, gpointer udata )
3933 {
3934   wpu_udata *user_data = udata;
3935   if ( ! strcmp ( wp->name, user_data->wp->name ) ) {
3936     user_data->uuid = id;
3937     return TRUE;
3938   }
3939   return FALSE;
3940 }
3941
3942 /*
3943  * Delete a waypoint by the given name
3944  * NOTE: ATM this will delete the first encountered Waypoint with the specified name
3945  *   as there be multiple waypoints with the same name
3946  */
3947 static gboolean trw_layer_delete_waypoint_by_name ( VikTrwLayer *vtl, const gchar *name )
3948 {
3949   wpu_udata udata;
3950   // Fake a waypoint with the given name
3951   udata.wp   = vik_waypoint_new ();
3952   vik_waypoint_set_name (udata.wp, name);
3953   // Currently only the name is used in this waypoint find function
3954   udata.uuid = NULL;
3955
3956   // Hmmm, want key of it
3957   gpointer *wpf = g_hash_table_find ( vtl->waypoints, (GHRFunc) trw_layer_waypoint_find_uuid_by_name, (gpointer) &udata );
3958
3959   vik_waypoint_free (udata.wp);
3960
3961   if ( wpf && udata.uuid )
3962     return trw_layer_delete_waypoint (vtl, g_hash_table_lookup ( vtl->waypoints, udata.uuid ));
3963   else
3964     return FALSE;
3965 }
3966
3967 typedef struct {
3968   VikTrack *trk; // input
3969   gpointer uuid; // output
3970 } tpu_udata;
3971
3972 // Only for temporary use by trw_layer_delete_track_by_name
3973 static gboolean trw_layer_track_find_uuid_by_name ( const gpointer id, const VikTrack *trk, gpointer udata )
3974 {
3975   tpu_udata *user_data = udata;
3976   if ( ! strcmp ( trk->name, user_data->trk->name ) ) {
3977     user_data->uuid = id;
3978     return TRUE;
3979   }
3980   return FALSE;
3981 }
3982
3983 /*
3984  * Delete a track by the given name
3985  * NOTE: ATM this will delete the first encountered Track with the specified name
3986  *   as there may be multiple tracks with the same name within the specified hash table
3987  */
3988 static gboolean trw_layer_delete_track_by_name ( VikTrwLayer *vtl, const gchar *name, GHashTable *ht_tracks )
3989 {
3990   tpu_udata udata;
3991   // Fake a track with the given name
3992   udata.trk   = vik_track_new ();
3993   vik_track_set_name (udata.trk, name);
3994   // Currently only the name is used in this waypoint find function
3995   udata.uuid = NULL;
3996
3997   // Hmmm, want key of it
3998   gpointer *trkf = g_hash_table_find ( ht_tracks, (GHRFunc) trw_layer_track_find_uuid_by_name, &udata );
3999
4000   vik_track_free (udata.trk);
4001
4002   if ( trkf && udata.uuid ) {
4003     // This could be a little better written...
4004     if ( vtl->tracks == ht_tracks )
4005       return vik_trw_layer_delete_track (vtl, g_hash_table_lookup ( ht_tracks, udata.uuid ));
4006     if ( vtl->routes == ht_tracks )
4007       return vik_trw_layer_delete_route (vtl, g_hash_table_lookup ( ht_tracks, udata.uuid ));
4008     return FALSE;
4009   }
4010   else
4011     return FALSE;
4012 }
4013
4014 static void remove_item_from_treeview(const gchar *name, GtkTreeIter *it, VikTreeview * vt)
4015 {
4016     vik_treeview_item_delete (vt, it );
4017 }
4018
4019 void vik_trw_layer_delete_all_routes ( VikTrwLayer *vtl )
4020 {
4021
4022   vtl->current_track = NULL;
4023   vtl->route_finder_current_track = NULL;
4024   vtl->route_finder_added_track = NULL;
4025   if (vtl->current_tp_track)
4026     trw_layer_cancel_current_tp(vtl, FALSE);
4027
4028   g_hash_table_foreach(vtl->routes_iters, (GHFunc) remove_item_from_treeview, VIK_LAYER(vtl)->vt);
4029   g_hash_table_remove_all(vtl->routes_iters);
4030   g_hash_table_remove_all(vtl->routes);
4031
4032   vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, &(vtl->routes_iter) );
4033
4034   vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
4035 }
4036
4037 void vik_trw_layer_delete_all_tracks ( VikTrwLayer *vtl )
4038 {
4039
4040   vtl->current_track = NULL;
4041   vtl->route_finder_current_track = NULL;
4042   vtl->route_finder_added_track = NULL;
4043   if (vtl->current_tp_track)
4044     trw_layer_cancel_current_tp(vtl, FALSE);
4045
4046   g_hash_table_foreach(vtl->tracks_iters, (GHFunc) remove_item_from_treeview, VIK_LAYER(vtl)->vt);
4047   g_hash_table_remove_all(vtl->tracks_iters);
4048   g_hash_table_remove_all(vtl->tracks);
4049
4050   vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, &(vtl->tracks_iter) );
4051
4052   vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
4053 }
4054
4055 void vik_trw_layer_delete_all_waypoints ( VikTrwLayer *vtl )
4056 {
4057   vtl->current_wp = NULL;
4058   vtl->current_wp_id = NULL;
4059   vtl->moving_wp = FALSE;
4060
4061   highest_wp_number_reset(vtl);
4062
4063   g_hash_table_foreach(vtl->waypoints_iters, (GHFunc) remove_item_from_treeview, VIK_LAYER(vtl)->vt);
4064   g_hash_table_remove_all(vtl->waypoints_iters);
4065   g_hash_table_remove_all(vtl->waypoints);
4066
4067   vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, &(vtl->waypoints_iter) );
4068
4069   vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
4070 }
4071
4072 static void trw_layer_delete_all_tracks ( gpointer lav[2] )
4073 {
4074   VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
4075   // Get confirmation from the user
4076   if ( a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
4077                             _("Are you sure you want to delete all tracks in %s?"),
4078                             vik_layer_get_name ( VIK_LAYER(vtl) ) ) )
4079     vik_trw_layer_delete_all_tracks (vtl);
4080 }
4081
4082 static void trw_layer_delete_all_routes ( gpointer lav[2] )
4083 {
4084   VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
4085   // Get confirmation from the user
4086   if ( a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
4087                             _("Are you sure you want to delete all routes in %s?"),
4088                             vik_layer_get_name ( VIK_LAYER(vtl) ) ) )
4089     vik_trw_layer_delete_all_routes (vtl);
4090 }
4091
4092 static void trw_layer_delete_all_waypoints ( gpointer lav[2] )
4093 {
4094   VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
4095   // Get confirmation from the user
4096   if ( a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
4097                             _("Are you sure you want to delete all waypoints in %s?"),
4098                             vik_layer_get_name ( VIK_LAYER(vtl) ) ) )
4099     vik_trw_layer_delete_all_waypoints (vtl);
4100 }
4101
4102 static void trw_layer_delete_item ( gpointer pass_along[6] )
4103 {
4104   VikTrwLayer *vtl = VIK_TRW_LAYER(pass_along[0]);
4105   gboolean was_visible = FALSE;
4106   if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_WAYPOINT )
4107   {
4108     VikWaypoint *wp = g_hash_table_lookup ( vtl->waypoints, pass_along[3] );
4109     if ( wp && wp->name ) {
4110       if ( GPOINTER_TO_INT ( pass_along[4]) )
4111         // Get confirmation from the user
4112         // Maybe this Waypoint Delete should be optional as is it could get annoying...
4113         if ( ! a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
4114             _("Are you sure you want to delete the waypoint \"%s\""),
4115             wp->name ) )
4116           return;
4117       was_visible = trw_layer_delete_waypoint ( vtl, wp );
4118     }
4119   }
4120   else if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_TRACK )
4121   {
4122     VikTrack *trk = g_hash_table_lookup ( vtl->tracks, pass_along[3] );
4123     if ( trk && trk->name ) {
4124       if ( GPOINTER_TO_INT ( pass_along[4]) )
4125         // Get confirmation from the user
4126         if ( ! a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
4127                                   _("Are you sure you want to delete the track \"%s\""),
4128                                   trk->name ) )
4129           return;
4130       was_visible = vik_trw_layer_delete_track ( vtl, trk );
4131     }
4132   }
4133   else
4134   {
4135     VikTrack *trk = g_hash_table_lookup ( vtl->routes, pass_along[3] );
4136     if ( trk && trk->name ) {
4137       if ( GPOINTER_TO_INT ( pass_along[4]) )
4138         // Get confirmation from the user
4139         if ( ! a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
4140                                     _("Are you sure you want to delete the route \"%s\""),
4141                                     trk->name ) )
4142           return;
4143       was_visible = vik_trw_layer_delete_route ( vtl, trk );
4144     }
4145   }
4146   if ( was_visible )
4147     vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
4148 }
4149
4150
4151 static void trw_layer_properties_item ( gpointer pass_along[7] )
4152 {
4153   VikTrwLayer *vtl = VIK_TRW_LAYER(pass_along[0]);
4154   if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_WAYPOINT )
4155   {
4156     VikWaypoint *wp = g_hash_table_lookup ( vtl->waypoints, pass_along[3] ); // sublayer
4157
4158     if ( wp && wp->name )
4159     {
4160       gboolean updated = FALSE;
4161       a_dialog_waypoint ( VIK_GTK_WINDOW_FROM_LAYER(vtl), wp->name, wp, vtl->coord_mode, FALSE, &updated );
4162
4163       if ( updated && pass_along[6] )
4164         vik_treeview_item_set_icon ( VIK_LAYER(vtl)->vt, pass_along[6], get_wp_sym_small (wp->symbol) );
4165
4166       if ( updated && VIK_LAYER(vtl)->visible )
4167         vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
4168     }
4169   }
4170   else
4171   {
4172     VikTrack *tr;
4173     if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_TRACK )
4174       tr = g_hash_table_lookup ( vtl->tracks, pass_along[3] );
4175     else
4176       tr = g_hash_table_lookup ( vtl->routes, pass_along[3] );
4177
4178     if ( tr && tr->name )
4179     {
4180       vik_trw_layer_propwin_run ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
4181                                   vtl,
4182                                   tr,
4183                                   pass_along[1], /* vlp */
4184                                   pass_along[5], /* vvp */
4185                                   pass_along[6]); /* iter */
4186     }
4187   }
4188 }
4189
4190 /*
4191  * Update the treeview of the track id - primarily to update the icon
4192  */
4193 void trw_layer_update_treeview ( VikTrwLayer *vtl, VikTrack *trk, gpointer *trk_id )
4194 {
4195   trku_udata udata;
4196   udata.trk  = trk;
4197   udata.uuid = NULL;
4198
4199   gpointer *trkf = NULL;
4200   if ( trk->is_route )
4201     trkf = g_hash_table_find ( vtl->routes, (GHRFunc) trw_layer_track_find_uuid, &udata );
4202   else
4203     trkf = g_hash_table_find ( vtl->tracks, (GHRFunc) trw_layer_track_find_uuid, &udata );
4204
4205   if ( trkf && udata.uuid ) {
4206
4207     GtkTreeIter *iter = NULL;
4208     if ( trk->is_route )
4209       iter = g_hash_table_lookup ( vtl->routes_iters, udata.uuid );
4210     else
4211       iter = g_hash_table_lookup ( vtl->tracks_iters, udata.uuid );
4212
4213     if ( iter ) {
4214       // TODO: Make this a function
4215       GdkPixbuf *pixbuf = gdk_pixbuf_new ( GDK_COLORSPACE_RGB, FALSE, 8, 18, 18);
4216       guint32 pixel = ((trk->color.red & 0xff00) << 16) |
4217         ((trk->color.green & 0xff00) << 8) |
4218         (trk->color.blue & 0xff00);
4219       gdk_pixbuf_fill ( pixbuf, pixel );
4220       vik_treeview_item_set_icon ( VIK_LAYER(vtl)->vt, iter, pixbuf );
4221       g_object_unref (pixbuf);
4222     }
4223
4224   }
4225 }
4226
4227 /*
4228    Parameter 1 -> VikLayersPanel
4229    Parameter 2 -> VikLayer
4230    Parameter 3 -> VikViewport
4231 */
4232 static void goto_coord ( gpointer *vlp, gpointer vl, gpointer vvp, const VikCoord *coord )
4233 {
4234   if ( vlp ) {
4235     vik_viewport_set_center_coord ( vik_layers_panel_get_viewport (VIK_LAYERS_PANEL(vlp)), coord );
4236     vik_layers_panel_emit_update ( VIK_LAYERS_PANEL(vlp) );
4237   }
4238   else {
4239     /* since vlp not set, vl & vvp should be valid instead! */
4240     if ( vl && vvp ) {
4241       vik_viewport_set_center_coord ( VIK_VIEWPORT(vvp), coord );
4242       vik_layer_emit_update ( VIK_LAYER(vl), FALSE );
4243     }
4244   }
4245 }
4246
4247 static void trw_layer_goto_track_startpoint ( gpointer pass_along[6] )
4248 {
4249   VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
4250   VikTrack *track;
4251   if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
4252     track = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
4253   else
4254     track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
4255
4256   if ( track && track->trackpoints )
4257     goto_coord ( pass_along[1], pass_along[0], pass_along[5], &(((VikTrackpoint *) track->trackpoints->data)->coord) );
4258 }
4259
4260 static void trw_layer_goto_track_center ( gpointer pass_along[6] )
4261 {
4262   VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
4263   VikTrack *track;
4264   if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
4265     track = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
4266   else
4267     track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
4268
4269   if ( track && track->trackpoints )
4270   {
4271     struct LatLon average, maxmin[2] = { {0,0}, {0,0} };
4272     VikCoord coord;
4273     trw_layer_find_maxmin_tracks ( NULL, track, maxmin );
4274     average.lat = (maxmin[0].lat+maxmin[1].lat)/2;
4275     average.lon = (maxmin[0].lon+maxmin[1].lon)/2;
4276     vik_coord_load_from_latlon ( &coord, vtl->coord_mode, &average );
4277     goto_coord ( pass_along[1], pass_along[0], pass_along[5], &coord);
4278   }
4279 }
4280
4281 static void trw_layer_convert_track_route ( gpointer pass_along[6] )
4282 {
4283   VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
4284   VikTrack *trk;
4285   if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
4286     trk = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
4287   else
4288     trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
4289
4290   if ( !trk )
4291     return;
4292
4293   // Converting a track to a route can be a bit more complicated,
4294   //  so give a chance to change our minds:
4295   if ( !trk->is_route &&
4296        ( ( vik_track_get_segment_count ( trk ) > 1 ) ||
4297          ( vik_track_get_average_speed ( trk ) > 0.0 ) ) ) {
4298
4299     if ( ! a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
4300                                 _("Converting a track to a route removes extra track data such as segments, timestamps, etc...\nDo you want to continue?"), NULL ) )
4301       return;
4302 }
4303
4304   // Copy it
4305   VikTrack *trk_copy = vik_track_copy ( trk );
4306
4307   // Convert
4308   trk_copy->is_route = !trk_copy->is_route;
4309
4310   // ATM can't set name to self - so must create temporary copy
4311   gchar *name = g_strdup ( trk_copy->name );
4312
4313   // Delete old one and then add new one
4314   if ( trk->is_route ) {
4315     vik_trw_layer_delete_route ( vtl, trk );
4316     vik_trw_layer_add_track ( vtl, name, trk_copy );
4317   }
4318   else {
4319     // Extra route conversion bits...
4320     vik_track_merge_segments ( trk_copy );
4321     vik_track_to_routepoints ( trk_copy );
4322
4323     vik_trw_layer_delete_track ( vtl, trk );
4324     vik_trw_layer_add_route ( vtl, name, trk_copy );
4325   }
4326   g_free ( name );
4327
4328   // Update in case color of track / route changes when moving between sublayers
4329   vik_layer_emit_update ( VIK_LAYER(pass_along[0]), FALSE );
4330 }
4331
4332
4333 static void trw_layer_extend_track_end ( gpointer pass_along[6] )
4334 {
4335   VikTrwLayer *vtl = VIK_TRW_LAYER(pass_along[0]);
4336   VikTrack *track;
4337   if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
4338     track = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
4339   else
4340     track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
4341
4342   if ( !track )
4343     return;
4344
4345   vtl->current_track = track;
4346   vik_window_enable_layer_tool ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)), VIK_LAYER_TRW, track->is_route ? TOOL_CREATE_ROUTE : TOOL_CREATE_TRACK);
4347
4348   if ( track->trackpoints )
4349     goto_coord ( pass_along[1], pass_along[0], pass_along[5], &(((VikTrackpoint *)g_list_last(track->trackpoints)->data)->coord) );
4350 }
4351
4352 #ifdef VIK_CONFIG_GOOGLE
4353 /**
4354  * extend a track using route finder
4355  */
4356 static void trw_layer_extend_track_end_route_finder ( gpointer pass_along[6] )
4357 {
4358   VikTrwLayer *vtl = VIK_TRW_LAYER(pass_along[0]);
4359   VikTrack *track = g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, pass_along[3] );
4360   VikCoord last_coord = (((VikTrackpoint *)g_list_last(track->trackpoints)->data)->coord);
4361
4362   vik_window_enable_layer_tool ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)), VIK_LAYER_TRW, NUM_TOOLS );
4363   vtl->route_finder_coord =  last_coord;
4364   vtl->route_finder_current_track = track;
4365   vtl->route_finder_started = TRUE;
4366
4367   if ( track->trackpoints )
4368     goto_coord ( pass_along[1], pass_along[0], pass_along[5], &last_coord) ;
4369
4370 }
4371 #endif
4372
4373 static void trw_layer_apply_dem_data ( gpointer pass_along[6] )
4374 {
4375   /* TODO: check & warn if no DEM data, or no applicable DEM data. */
4376   /* Also warn if overwrite old elevation data */
4377   VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
4378   VikTrack *track;
4379   if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
4380     track = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
4381   else
4382     track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
4383
4384   if ( track )
4385     vik_track_apply_dem_data ( track );
4386 }
4387
4388 static void trw_layer_goto_track_endpoint ( gpointer pass_along[6] )
4389 {
4390   VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
4391   VikTrack *track;
4392   if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
4393     track = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
4394   else
4395     track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
4396
4397   if ( !track )
4398     return;
4399
4400   GList *trps = track->trackpoints;
4401   if ( !trps )
4402     return;
4403   trps = g_list_last(trps);
4404   goto_coord ( pass_along[1], pass_along[0], pass_along[5], &(((VikTrackpoint *) trps->data)->coord));
4405 }
4406
4407 static void trw_layer_goto_track_max_speed ( gpointer pass_along[6] )
4408 {
4409   VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
4410   VikTrack *track;
4411   if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
4412     track = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
4413   else
4414     track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
4415
4416   if ( !track )
4417     return;
4418
4419   VikTrackpoint* vtp = vik_track_get_tp_by_max_speed ( track );
4420   if ( !vtp )
4421     return;
4422   goto_coord ( pass_along[1], pass_along[0], pass_along[5], &(vtp->coord));
4423 }
4424
4425 static void trw_layer_goto_track_max_alt ( gpointer pass_along[6] )
4426 {
4427   VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
4428   VikTrack *track;
4429   if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
4430     track = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
4431   else
4432     track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
4433
4434   if ( !track )
4435     return;
4436
4437   VikTrackpoint* vtp = vik_track_get_tp_by_max_alt ( track );
4438   if ( !vtp )
4439     return;
4440   goto_coord ( pass_along[1], pass_along[0], pass_along[5], &(vtp->coord));
4441 }
4442
4443 static void trw_layer_goto_track_min_alt ( gpointer pass_along[6] )
4444 {
4445   VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
4446   VikTrack *track;
4447   if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
4448     track = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
4449   else
4450     track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
4451
4452   if ( !track )
4453     return;
4454
4455   VikTrackpoint* vtp = vik_track_get_tp_by_min_alt ( track );
4456   if ( !vtp )
4457     return;
4458   goto_coord ( pass_along[1], pass_along[0], pass_along[5], &(vtp->coord));
4459 }
4460
4461 /*
4462  * Automatically change the viewport to center on the track and zoom to see the extent of the track
4463  */
4464 static void trw_layer_auto_track_view ( gpointer pass_along[6] )
4465 {
4466   VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
4467   VikTrack *trk;
4468   if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
4469     trk = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
4470   else
4471     trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
4472
4473   if ( trk && trk->trackpoints )
4474   {
4475     struct LatLon maxmin[2] = { {0,0}, {0,0} };
4476     trw_layer_find_maxmin_tracks ( NULL, trk, maxmin );
4477     trw_layer_zoom_to_show_latlons ( VIK_TRW_LAYER(pass_along[0]), pass_along[5], maxmin );
4478     if ( pass_along[1] )
4479       vik_layers_panel_emit_update ( VIK_LAYERS_PANEL(pass_along[1]) );
4480     else
4481       vik_layer_emit_update ( VIK_LAYER(pass_along[0]), FALSE );
4482   }
4483 }
4484
4485 static void trw_layer_edit_trackpoint ( gpointer pass_along[6] )
4486 {
4487   VikTrwLayer *vtl = VIK_TRW_LAYER(pass_along[0]);
4488   trw_layer_tpwin_init ( vtl );
4489 }
4490
4491 /*************************************
4492  * merge/split by time routines 
4493  *************************************/
4494
4495 /* called for each key in track hash table.
4496  * If the current track has the same time stamp type, add it to the result,
4497  * except the one pointed by "exclude".
4498  * set exclude to NULL if there is no exclude to check.
4499  * Note that the result is in reverse (for performance reasons).
4500  */
4501 typedef struct {
4502   GList **result;
4503   GList  *exclude;
4504   gboolean with_timestamps;
4505 } twt_udata;
4506 static void find_tracks_with_timestamp_type(gpointer key, gpointer value, gpointer udata)
4507 {
4508   twt_udata *user_data = udata;
4509   VikTrackpoint *p1, *p2;
4510
4511   if (VIK_TRACK(value)->trackpoints == user_data->exclude) {
4512     return;
4513   }
4514
4515   if (VIK_TRACK(value)->trackpoints) {
4516     p1 = VIK_TRACKPOINT(VIK_TRACK(value)->trackpoints->data);
4517     p2 = VIK_TRACKPOINT(g_list_last(VIK_TRACK(value)->trackpoints)->data);
4518
4519     if ( user_data->with_timestamps ) {
4520       if (!p1->has_timestamp || !p2->has_timestamp) {
4521         return;
4522       }
4523     }
4524     else {
4525       // Don't add tracks with timestamps when getting non timestamp tracks
4526       if (p1->has_timestamp || p2->has_timestamp) {
4527         return;
4528       }
4529     }
4530   }
4531
4532   *(user_data->result) = g_list_prepend(*(user_data->result), key);
4533 }
4534
4535 /* called for each key in track hash table. if original track user_data[1] is close enough
4536  * to the passed one, add it to list in user_data[0] 
4537  */
4538 static void find_nearby_tracks_by_time (gpointer key, gpointer value, gpointer user_data)
4539 {
4540   time_t t1, t2;
4541   VikTrackpoint *p1, *p2;
4542   VikTrack *trk = VIK_TRACK(value);
4543
4544   GList **nearby_tracks = ((gpointer *)user_data)[0];
4545   GList *tpoints = ((gpointer *)user_data)[1];
4546
4547   /* outline: 
4548    * detect reasons for not merging, and return
4549    * if no reason is found not to merge, then do it.
4550    */
4551
4552   // Exclude the original track from the compiled list
4553   if (trk->trackpoints == tpoints) {
4554     return;
4555   }
4556
4557   t1 = VIK_TRACKPOINT(g_list_first(tpoints)->data)->timestamp;
4558   t2 = VIK_TRACKPOINT(g_list_last(tpoints)->data)->timestamp;
4559
4560   if (trk->trackpoints) {
4561     p1 = VIK_TRACKPOINT(g_list_first(trk->trackpoints)->data);
4562     p2 = VIK_TRACKPOINT(g_list_last(trk->trackpoints)->data);
4563
4564     if (!p1->has_timestamp || !p2->has_timestamp) {
4565       //g_print("no timestamp\n");
4566       return;
4567     }
4568
4569     guint threshold = GPOINTER_TO_UINT (((gpointer *)user_data)[2]);
4570     //g_print("Got track named %s, times %d, %d\n", trk->name, p1->timestamp, p2->timestamp);
4571     if (! (abs(t1 - p2->timestamp) < threshold ||
4572         /*  p1 p2      t1 t2 */
4573            abs(p1->timestamp - t2) < threshold)
4574         /*  t1 t2      p1 p2 */
4575         ) {
4576       return;
4577     }
4578   }
4579
4580   *nearby_tracks = g_list_prepend(*nearby_tracks, value);
4581 }
4582
4583 /* comparison function used to sort tracks; a and b are hash table keys */
4584 /* Not actively used - can be restored if needed
4585 static gint track_compare(gconstpointer a, gconstpointer b, gpointer user_data)
4586 {
4587   GHashTable *tracks = user_data;
4588   time_t t1, t2;
4589
4590   t1 = VIK_TRACKPOINT(VIK_TRACK(g_hash_table_lookup(tracks, a))->trackpoints->data)->timestamp;
4591   t2 = VIK_TRACKPOINT(VIK_TRACK(g_hash_table_lookup(tracks, b))->trackpoints->data)->timestamp;
4592   
4593   if (t1 < t2) return -1;
4594   if (t1 > t2) return 1;
4595   return 0;
4596 }
4597 */
4598
4599 /* comparison function used to sort trackpoints */
4600 static gint trackpoint_compare(gconstpointer a, gconstpointer b)
4601 {
4602   time_t t1 = VIK_TRACKPOINT(a)->timestamp, t2 = VIK_TRACKPOINT(b)->timestamp;
4603   
4604   if (t1 < t2) return -1;
4605   if (t1 > t2) return 1;
4606   return 0;
4607 }
4608
4609 /**
4610  * comparison function which can be used to sort tracks or waypoints by name
4611  */
4612 static gint sort_alphabetically (gconstpointer a, gconstpointer b, gpointer user_data)
4613 {
4614   const gchar* namea = (const gchar*) a;
4615   const gchar* nameb = (const gchar*) b;
4616   if ( namea == NULL || nameb == NULL)
4617     return 0;
4618   else
4619     // Same sort method as used in the vik_treeview_*_alphabetize functions
4620     return strcmp ( namea, nameb );
4621 }
4622
4623 /**
4624  * Attempt to merge selected track with other tracks specified by the user
4625  * Tracks to merge with must be of the same 'type' as the selected track -
4626  *  either all with timestamps, or all without timestamps
4627  */
4628 static void trw_layer_merge_with_other ( gpointer pass_along[6] )
4629 {
4630   VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
4631   GList *other_tracks = NULL;
4632   GHashTable *ght_tracks;
4633   if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
4634     ght_tracks = vtl->routes;
4635   else
4636     ght_tracks = vtl->tracks;
4637
4638   VikTrack *track = (VikTrack *) g_hash_table_lookup ( ght_tracks, pass_along[3] );
4639
4640   if ( !track )
4641     return;
4642
4643   if ( !track->trackpoints )
4644     return;
4645
4646   twt_udata udata;
4647   udata.result = &other_tracks;
4648   udata.exclude = track->trackpoints;
4649   // Allow merging with 'similar' time type time tracks
4650   // i.e. either those times, or those without
4651   udata.with_timestamps = (VIK_TRACKPOINT(track->trackpoints->data)->has_timestamp);
4652
4653   g_hash_table_foreach(ght_tracks, find_tracks_with_timestamp_type, (gpointer)&udata);
4654   other_tracks = g_list_reverse(other_tracks);
4655
4656   if ( !other_tracks ) {
4657     if ( udata.with_timestamps )
4658       a_dialog_error_msg(VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Failed. No other tracks with timestamps in this layer found"));
4659     else
4660       a_dialog_error_msg(VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Failed. No other tracks without timestamps in this layer found"));
4661     return;
4662   }
4663
4664   // Sort alphabetically for user presentation
4665   // Convert into list of names for usage with dialog function
4666   // TODO: Need to consider how to work best when we can have multiple tracks the same name...
4667   GList *other_tracks_names = NULL;
4668   GList *iter = g_list_first ( other_tracks );
4669   while ( iter ) {
4670     other_tracks_names = g_list_append ( other_tracks_names, VIK_TRACK(g_hash_table_lookup (ght_tracks, iter->data))->name );
4671     iter = g_list_next ( iter );
4672   }
4673
4674   other_tracks_names = g_list_sort_with_data (other_tracks_names, sort_alphabetically, NULL);
4675
4676   GList *merge_list = a_dialog_select_from_list(VIK_GTK_WINDOW_FROM_LAYER(vtl),
4677                                                 other_tracks_names,
4678                                                 TRUE,
4679                                                 _("Merge with..."),
4680                                                 track->is_route ? _("Select route to merge with") : _("Select track to merge with"));
4681   g_list_free(other_tracks);
4682   g_list_free(other_tracks_names);
4683
4684   if (merge_list)
4685   {
4686     GList *l;
4687     for (l = merge_list; l != NULL; l = g_list_next(l)) {
4688       VikTrack *merge_track;
4689       if ( track->is_route )
4690         merge_track = vik_trw_layer_get_route ( vtl, l->data );
4691       else
4692         merge_track = vik_trw_layer_get_track ( vtl, l->data );
4693
4694       if (merge_track) {
4695         track->trackpoints = g_list_concat(track->trackpoints, merge_track->trackpoints);
4696         merge_track->trackpoints = NULL;
4697         if ( track->is_route )
4698           vik_trw_layer_delete_route (vtl, merge_track);
4699         else
4700           vik_trw_layer_delete_track (vtl, merge_track);
4701         track->trackpoints = g_list_sort(track->trackpoints, trackpoint_compare);
4702       }
4703     }
4704     /* TODO: free data before free merge_list */
4705     for (l = merge_list; l != NULL; l = g_list_next(l))
4706       g_free(l->data);
4707     g_list_free(merge_list);
4708     vik_layer_emit_update( VIK_LAYER(vtl), FALSE );
4709   }
4710 }
4711
4712 // c.f. trw_layer_sorted_track_id_by_name_list
4713 //  but don't add the specified track to the list (normally current track)
4714 static void trw_layer_sorted_track_id_by_name_list_exclude_self (const gpointer id, const VikTrack *trk, gpointer udata)
4715 {
4716   twt_udata *user_data = udata;
4717
4718   // Skip self
4719   if (trk->trackpoints == user_data->exclude) {
4720     return;
4721   }
4722
4723   // Sort named list alphabetically
4724   *(user_data->result) = g_list_insert_sorted_with_data (*(user_data->result), trk->name, sort_alphabetically, NULL);
4725 }
4726
4727 /**
4728  * Join - this allows combining 'tracks' and 'track routes'
4729  *  i.e. doesn't care about whether tracks have consistent timestamps
4730  * ATM can only append one track at a time to the currently selected track
4731  */
4732 static void trw_layer_append_track ( gpointer pass_along[6] )
4733 {
4734
4735   VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
4736   VikTrack *trk;
4737   GHashTable *ght_tracks;
4738   if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
4739     ght_tracks = vtl->routes;
4740   else
4741     ght_tracks = vtl->tracks;
4742
4743   trk = (VikTrack *) g_hash_table_lookup ( ght_tracks, pass_along[3] );
4744
4745   if ( !trk )
4746     return;
4747
4748   GList *other_tracks_names = NULL;
4749
4750   // Sort alphabetically for user presentation
4751   // Convert into list of names for usage with dialog function
4752   // TODO: Need to consider how to work best when we can have multiple tracks the same name...
4753   twt_udata udata;
4754   udata.result = &other_tracks_names;
4755   udata.exclude = trk->trackpoints;
4756
4757   g_hash_table_foreach(ght_tracks, (GHFunc) trw_layer_sorted_track_id_by_name_list_exclude_self, (gpointer)&udata);
4758
4759   // Note the limit to selecting one track only
4760   //  this is to control the ordering of appending tracks, i.e. the selected track always goes after the current track
4761   //  (otherwise with multiple select the ordering would not be controllable by the user - automatically being alphabetically)
4762   GList *append_list = a_dialog_select_from_list(VIK_GTK_WINDOW_FROM_LAYER(vtl),
4763                                                  other_tracks_names,
4764                                                  FALSE,
4765                                                  trk->is_route ? _("Append Route"): _("Append Track"),
4766                                                  trk->is_route ? _("Select the route to append after the current route") :
4767                                                                  _("Select the track to append after the current track") );
4768
4769   g_list_free(other_tracks_names);
4770
4771   // It's a list, but shouldn't contain more than one other track!
4772   if ( append_list ) {
4773     GList *l;
4774     for (l = append_list; l != NULL; l = g_list_next(l)) {
4775       // TODO: at present this uses the first track found by name,
4776       //  which with potential multiple same named tracks may not be the one selected...
4777       VikTrack *append_track;
4778       if ( trk->is_route )
4779         append_track = vik_trw_layer_get_route ( vtl, l->data );
4780       else
4781         append_track = vik_trw_layer_get_track ( vtl, l->data );
4782
4783       if ( append_track ) {
4784         trk->trackpoints = g_list_concat(trk->trackpoints, append_track->trackpoints);
4785         append_track->trackpoints = NULL;
4786         if ( trk->is_route )
4787           vik_trw_layer_delete_route (vtl, append_track);
4788         else
4789           vik_trw_layer_delete_track (vtl, append_track);
4790       }
4791     }
4792     for (l = append_list; l != NULL; l = g_list_next(l))
4793       g_free(l->data);
4794     g_list_free(append_list);
4795     vik_layer_emit_update( VIK_LAYER(vtl), FALSE );
4796   }
4797 }
4798
4799 /**
4800  * Very similar to trw_layer_append_track for joining
4801  * but this allows selection from the 'other' list
4802  * If a track is selected, then is shows routes and joins the selected one
4803  * If a route is selected, then is shows tracks and joins the selected one
4804  */
4805 static void trw_layer_append_other ( gpointer pass_along[6] )
4806 {
4807
4808   VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
4809   VikTrack *trk;
4810   GHashTable *ght_mykind, *ght_others;
4811   if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE ) {
4812     ght_mykind = vtl->routes;
4813     ght_others = vtl->tracks;
4814   }
4815   else {
4816     ght_mykind = vtl->tracks;
4817     ght_others = vtl->routes;
4818   }
4819
4820   trk = (VikTrack *) g_hash_table_lookup ( ght_mykind, pass_along[3] );
4821
4822   if ( !trk )
4823     return;
4824
4825   GList *other_tracks_names = NULL;
4826
4827   // Sort alphabetically for user presentation
4828   // Convert into list of names for usage with dialog function
4829   // TODO: Need to consider how to work best when we can have multiple tracks the same name...
4830   twt_udata udata;
4831   udata.result = &other_tracks_names;
4832   udata.exclude = trk->trackpoints;
4833
4834   g_hash_table_foreach(ght_others, (GHFunc) trw_layer_sorted_track_id_by_name_list_exclude_self, (gpointer)&udata);
4835
4836   // Note the limit to selecting one track only
4837   //  this is to control the ordering of appending tracks, i.e. the selected track always goes after the current track
4838   //  (otherwise with multiple select the ordering would not be controllable by the user - automatically being alphabetically)
4839   GList *append_list = a_dialog_select_from_list(VIK_GTK_WINDOW_FROM_LAYER(vtl),
4840                                                  other_tracks_names,
4841                                                  FALSE,
4842                                                  trk->is_route ? _("Append Track"): _("Append Route"),
4843                                                  trk->is_route ? _("Select the track to append after the current route") :
4844                                                                  _("Select the route to append after the current track") );
4845
4846   g_list_free(other_tracks_names);
4847
4848   // It's a list, but shouldn't contain more than one other track!
4849   if ( append_list ) {
4850     GList *l;
4851     for (l = append_list; l != NULL; l = g_list_next(l)) {
4852       // TODO: at present this uses the first track found by name,
4853       //  which with potential multiple same named tracks may not be the one selected...
4854
4855       // Get FROM THE OTHER TYPE list
4856       VikTrack *append_track;
4857       if ( trk->is_route )
4858         append_track = vik_trw_layer_get_track ( vtl, l->data );
4859       else
4860         append_track = vik_trw_layer_get_route ( vtl, l->data );
4861
4862       if ( append_track ) {
4863
4864         if ( !append_track->is_route &&
4865              ( ( vik_track_get_segment_count ( append_track ) > 1 ) ||
4866                ( vik_track_get_average_speed ( append_track ) > 0.0 ) ) ) {
4867
4868           if ( a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
4869                                       _("Converting a track to a route removes extra track data such as segments, timestamps, etc...\nDo you want to continue?"), NULL ) ) {
4870             vik_track_merge_segments ( append_track );
4871             vik_track_to_routepoints ( append_track );
4872           }
4873           else {
4874             break;
4875           }
4876         }
4877
4878         trk->trackpoints = g_list_concat(trk->trackpoints, append_track->trackpoints);
4879         append_track->trackpoints = NULL;
4880
4881         // Delete copied which is FROM THE OTHER TYPE list
4882         if ( trk->is_route )
4883           vik_trw_layer_delete_track (vtl, append_track);
4884         else
4885           vik_trw_layer_delete_route (vtl, append_track);
4886       }
4887     }
4888     for (l = append_list; l != NULL; l = g_list_next(l))
4889       g_free(l->data);
4890     g_list_free(append_list);
4891     vik_layer_emit_update( VIK_LAYER(vtl), FALSE );
4892   }
4893 }
4894
4895 /* merge by segments */
4896 static void trw_layer_merge_by_segment ( gpointer pass_along[6] )
4897 {
4898   VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
4899   VikTrack *trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
4900   guint segments = vik_track_merge_segments ( trk );
4901   // NB currently no need to redraw as segments not actually shown on the display
4902   // However inform the user of what happened:
4903   gchar str[64];
4904   const gchar *tmp_str = ngettext("%d segment merged", "%d segments merged", segments);
4905   g_snprintf(str, 64, tmp_str, segments);
4906   a_dialog_info_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), str );
4907 }
4908
4909 /* merge by time routine */
4910 static void trw_layer_merge_by_timestamp ( gpointer pass_along[6] )
4911 {
4912   VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
4913
4914   //time_t t1, t2;
4915
4916   GList *tracks_with_timestamp = NULL;
4917   VikTrack *orig_trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
4918   if (orig_trk->trackpoints &&
4919       !VIK_TRACKPOINT(orig_trk->trackpoints->data)->has_timestamp) {
4920     a_dialog_error_msg(VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Failed. This track does not have timestamp"));
4921     return;
4922   }
4923
4924   twt_udata udata;
4925   udata.result = &tracks_with_timestamp;
4926   udata.exclude = orig_trk->trackpoints;
4927   udata.with_timestamps = TRUE;
4928   g_hash_table_foreach(vtl->tracks, find_tracks_with_timestamp_type, (gpointer)&udata);
4929   tracks_with_timestamp = g_list_reverse(tracks_with_timestamp);
4930
4931   if (!tracks_with_timestamp) {
4932     a_dialog_error_msg(VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Failed. No other track in this layer has timestamp"));
4933     return;
4934   }
4935   g_list_free(tracks_with_timestamp);
4936
4937   static guint threshold_in_minutes = 1;
4938   if (!a_dialog_time_threshold(VIK_GTK_WINDOW_FROM_LAYER(vtl),
4939                                _("Merge Threshold..."),
4940                                _("Merge when time between tracks less than:"),
4941                                &threshold_in_minutes)) {
4942     return;
4943   }
4944
4945   // keep attempting to merge all tracks until no merges within the time specified is possible
4946   gboolean attempt_merge = TRUE;
4947   GList *nearby_tracks = NULL;
4948   GList *trps;
4949   static gpointer params[3];
4950
4951   while ( attempt_merge ) {
4952
4953     // Don't try again unless tracks have changed
4954     attempt_merge = FALSE;
4955
4956     trps = orig_trk->trackpoints;
4957     if ( !trps )
4958       return;
4959
4960     if (nearby_tracks) {
4961       g_list_free(nearby_tracks);
4962       nearby_tracks = NULL;
4963     }
4964
4965     //t1 = ((VikTrackpoint *)trps->data)->timestamp;
4966     //t2 = ((VikTrackpoint *)g_list_last(trps)->data)->timestamp;
4967     
4968     /*    g_print("Original track times: %d and %d\n", t1, t2);  */
4969     params[0] = &nearby_tracks;
4970     params[1] = (gpointer)trps;
4971     params[2] = GUINT_TO_POINTER (threshold_in_minutes*60); // In seconds
4972
4973     /* get a list of adjacent-in-time tracks */
4974     g_hash_table_foreach(vtl->tracks, find_nearby_tracks_by_time, params);
4975
4976     /* merge them */
4977     GList *l = nearby_tracks;
4978     while ( l ) {
4979        /*
4980 #define get_first_trackpoint(x) VIK_TRACKPOINT(VIK_TRACK(x)->trackpoints->data)
4981 #define get_last_trackpoint(x) VIK_TRACKPOINT(g_list_last(VIK_TRACK(x)->trackpoints)->data)
4982         time_t t1, t2;
4983         t1 = get_first_trackpoint(l)->timestamp;
4984         t2 = get_last_trackpoint(l)->timestamp;
4985 #undef get_first_trackpoint
4986 #undef get_last_trackpoint
4987         g_print("     %20s: track %d - %d\n", VIK_TRACK(l->data)->name, (int)t1, (int)t2);
4988        */
4989
4990       /* remove trackpoints from merged track, delete track */
4991       orig_trk->trackpoints = g_list_concat(orig_trk->trackpoints, VIK_TRACK(l->data)->trackpoints);
4992       VIK_TRACK(l->data)->trackpoints = NULL;
4993       vik_trw_layer_delete_track (vtl, VIK_TRACK(l->data));
4994
4995       // Tracks have changed, therefore retry again against all the remaining tracks
4996       attempt_merge = TRUE;
4997
4998       l = g_list_next(l);
4999     }
5000
5001     orig_trk->trackpoints = g_list_sort(orig_trk->trackpoints, trackpoint_compare);
5002   }
5003
5004   g_list_free(nearby_tracks);
5005   vik_layer_emit_update( VIK_LAYER(vtl), FALSE );
5006 }
5007
5008 /**
5009  * Split a track at the currently selected trackpoint
5010  */
5011 static void trw_layer_split_at_selected_trackpoint ( VikTrwLayer *vtl, gint subtype )
5012 {
5013   if ( !vtl->current_tpl )
5014     return;
5015
5016   if ( vtl->current_tpl->next && vtl->current_tpl->prev ) {
5017     gchar *name = trw_layer_new_unique_sublayer_name(vtl, subtype, vtl->current_tp_track->name);
5018     if ( name ) {
5019       VikTrack *tr = vik_track_new ();
5020       GList *newglist = g_list_alloc ();
5021       newglist->prev = NULL;
5022       newglist->next = vtl->current_tpl->next;
5023       newglist->data = vik_trackpoint_copy(VIK_TRACKPOINT(vtl->current_tpl->data));
5024       tr->trackpoints = newglist;
5025       tr->is_route = vtl->current_tp_track->is_route;
5026       tr->color = vtl->current_tp_track->color;
5027       tr->visible = TRUE;
5028
5029       vtl->current_tpl->next->prev = newglist; /* end old track here */
5030       vtl->current_tpl->next = NULL;
5031
5032       vtl->current_tpl = newglist; /* change tp to first of new track. */
5033       vtl->current_tp_track = tr;
5034
5035       if ( tr->is_route )
5036         vik_trw_layer_add_route ( vtl, name, tr );
5037       else
5038         vik_trw_layer_add_track ( vtl, name, tr );
5039
5040       trku_udata udata;
5041       udata.trk  = tr;
5042       udata.uuid = NULL;
5043
5044       // Also need id of newly created track
5045       gpointer *trkf;
5046       if ( tr->is_route )
5047          trkf = g_hash_table_find ( vtl->routes, (GHRFunc) trw_layer_track_find_uuid, &udata );
5048       else
5049          trkf = g_hash_table_find ( vtl->tracks, (GHRFunc) trw_layer_track_find_uuid, &udata );
5050
5051       if ( trkf && udata.uuid )
5052         vtl->current_tp_id = udata.uuid;
5053       else
5054         vtl->current_tp_id = NULL;
5055
5056       vik_layer_emit_update(VIK_LAYER(vtl), FALSE);
5057     }
5058   }
5059 }
5060
5061 /* split by time routine */
5062 static void trw_layer_split_by_timestamp ( gpointer pass_along[6] )
5063 {
5064   VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
5065   VikTrack *track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
5066   GList *trps = track->trackpoints;
5067   GList *iter;
5068   GList *newlists = NULL;
5069   GList *newtps = NULL;
5070   static guint thr = 1;
5071
5072   time_t ts, prev_ts;
5073
5074   if ( !trps )
5075     return;
5076
5077   if (!a_dialog_time_threshold(VIK_GTK_WINDOW_FROM_LAYER(pass_along[0]), 
5078                                _("Split Threshold..."), 
5079                                _("Split when time between trackpoints exceeds:"), 
5080                                &thr)) {
5081     return;
5082   }
5083
5084   /* iterate through trackpoints, and copy them into new lists without touching original list */
5085   prev_ts = VIK_TRACKPOINT(trps->data)->timestamp;
5086   iter = trps;
5087
5088   while (iter) {
5089     ts = VIK_TRACKPOINT(iter->data)->timestamp;
5090     if (ts < prev_ts) {
5091       g_print("panic: ts < prev_ts: this should never happen!\n");
5092       return;
5093     }
5094     if (ts - prev_ts > thr*60) {
5095       /* flush accumulated trackpoints into new list */
5096       newlists = g_list_append(newlists, g_list_reverse(newtps));
5097       newtps = NULL;
5098     }
5099
5100     /* accumulate trackpoint copies in newtps, in reverse order */
5101     newtps = g_list_prepend(newtps, vik_trackpoint_copy(VIK_TRACKPOINT(iter->data)));
5102     prev_ts = ts;
5103     iter = g_list_next(iter);
5104   }
5105   if (newtps) {
5106       newlists = g_list_append(newlists, g_list_reverse(newtps));
5107   }
5108
5109   /* put lists of trackpoints into tracks */
5110   iter = newlists;
5111   // Only bother updating if the split results in new tracks
5112   if (g_list_length (newlists) > 1) {
5113     while (iter) {
5114       gchar *new_tr_name;
5115       VikTrack *tr;
5116
5117       tr = vik_track_new();
5118       tr->visible = track->visible;
5119       tr->trackpoints = (GList *)(iter->data);
5120
5121       new_tr_name = trw_layer_new_unique_sublayer_name ( vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, track->name);
5122       vik_trw_layer_add_track(vtl, new_tr_name, tr);
5123       /*    g_print("adding track %s, times %d - %d\n", new_tr_name, VIK_TRACKPOINT(tr->trackpoints->data)->timestamp,
5124           VIK_TRACKPOINT(g_list_last(tr->trackpoints)->data)->timestamp);*/
5125
5126       iter = g_list_next(iter);
5127     }
5128     // Remove original track and then update the display
5129     vik_trw_layer_delete_track (vtl, track);
5130     vik_layer_emit_update(VIK_LAYER(pass_along[0]), FALSE);
5131   }
5132   g_list_free(newlists);
5133 }
5134
5135 /**
5136  * Split a track by the number of points as specified by the user
5137  */
5138 static void trw_layer_split_by_n_points ( gpointer pass_along[6] )
5139 {
5140   VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
5141   VikTrack *track;
5142   if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5143     track = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
5144   else
5145     track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
5146
5147   if ( !track )
5148     return;
5149
5150   // Check valid track
5151   GList *trps = track->trackpoints;
5152   if ( !trps )
5153     return;
5154
5155   gint points = a_dialog_get_positive_number(VIK_GTK_WINDOW_FROM_LAYER(pass_along[0]),
5156                                              _("Split Every Nth Point"),
5157                                              _("Split on every Nth point:"),
5158                                              250,   // Default value as per typical limited track capacity of various GPS devices
5159                                              2,     // Min
5160                                              65536, // Max
5161                                              5);    // Step
5162   // Was a valid number returned?
5163   if (!points)
5164     return;
5165
5166   // Now split...
5167   GList *iter;
5168   GList *newlists = NULL;
5169   GList *newtps = NULL;
5170   gint count = 0;
5171   iter = trps;
5172
5173   while (iter) {
5174     /* accumulate trackpoint copies in newtps, in reverse order */
5175     newtps = g_list_prepend(newtps, vik_trackpoint_copy(VIK_TRACKPOINT(iter->data)));
5176     count++;
5177     if (count >= points) {
5178       /* flush accumulated trackpoints into new list */
5179       newlists = g_list_append(newlists, g_list_reverse(newtps));
5180       newtps = NULL;
5181       count = 0;
5182     }
5183     iter = g_list_next(iter);
5184   }
5185
5186   // If there is a remaining chunk put that into the new split list
5187   // This may well be the whole track if no split points were encountered
5188   if (newtps) {
5189       newlists = g_list_append(newlists, g_list_reverse(newtps));
5190   }
5191
5192   /* put lists of trackpoints into tracks */
5193   iter = newlists;
5194   // Only bother updating if the split results in new tracks
5195   if (g_list_length (newlists) > 1) {
5196     while (iter) {
5197       gchar *new_tr_name;
5198       VikTrack *tr;
5199
5200       tr = vik_track_new();
5201       tr->visible = track->visible;
5202       tr->is_route = track->is_route;
5203       tr->color = track->color;
5204       tr->trackpoints = (GList *)(iter->data);
5205
5206       if ( track->is_route ) {
5207         new_tr_name = trw_layer_new_unique_sublayer_name ( vtl, VIK_TRW_LAYER_SUBLAYER_ROUTE, track->name);
5208         vik_trw_layer_add_route(vtl, new_tr_name, tr);
5209       }
5210       else {
5211         new_tr_name = trw_layer_new_unique_sublayer_name ( vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, track->name);
5212         vik_trw_layer_add_track(vtl, new_tr_name, tr);
5213       }
5214       iter = g_list_next(iter);
5215     }
5216     // Remove original track and then update the display
5217     if ( track->is_route )
5218       vik_trw_layer_delete_route (vtl, track);
5219     else
5220       vik_trw_layer_delete_track (vtl, track);
5221     vik_layer_emit_update(VIK_LAYER(pass_along[0]), FALSE);
5222   }
5223   g_list_free(newlists);
5224 }
5225
5226 /**
5227  * Split a track at the currently selected trackpoint
5228  */
5229 static void trw_layer_split_at_trackpoint ( gpointer pass_along[6] )
5230 {
5231   VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
5232   gint subtype = GPOINTER_TO_INT (pass_along[2]);
5233   trw_layer_split_at_selected_trackpoint ( vtl, subtype );
5234 }
5235
5236 /**
5237  * Split a track by its segments
5238  * Routes do not have segments so don't call this for routes
5239  */
5240 static void trw_layer_split_segments ( gpointer pass_along[6] )
5241 {
5242   VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
5243   VikTrack *trk = g_hash_table_lookup ( vtl->tracks, pass_along[3] );
5244
5245   if ( !trk )
5246     return;
5247
5248   guint ntracks;
5249
5250   VikTrack **tracks = vik_track_split_into_segments (trk, &ntracks);
5251   gchar *new_tr_name;
5252   guint i;
5253   for ( i = 0; i < ntracks; i++ ) {
5254     if ( tracks[i] ) {
5255       new_tr_name = trw_layer_new_unique_sublayer_name ( vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, trk->name);
5256       vik_trw_layer_add_track ( vtl, new_tr_name, tracks[i] );
5257     }
5258   }
5259   if ( tracks ) {
5260     g_free ( tracks );
5261     // Remove original track
5262     vik_trw_layer_delete_track ( vtl, trk );
5263     vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
5264   }
5265   else {
5266     a_dialog_error_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Can not split track as it has no segments"));
5267   }
5268 }
5269 /* end of split/merge routines */
5270
5271 /**
5272  * Delete adjacent track points at the same position
5273  * AKA Delete Dulplicates on the Properties Window
5274  */
5275 static void trw_layer_delete_points_same_position ( gpointer pass_along[6] )
5276 {
5277   VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
5278   VikTrack *trk;
5279   if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5280     trk = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
5281   else
5282     trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
5283
5284   if ( !trk )
5285     return;
5286
5287   gulong removed = vik_track_remove_dup_points ( trk );
5288
5289   // Track has been updated so update tps:
5290   trw_layer_cancel_tps_of_track ( vtl, trk );
5291
5292   // Inform user how much was deleted as it's not obvious from the normal view
5293   gchar str[64];
5294   const gchar *tmp_str = ngettext("Deleted %ld point", "Deleted %ld points", removed);
5295   g_snprintf(str, 64, tmp_str, removed);
5296   a_dialog_info_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), str);
5297
5298   vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
5299 }
5300
5301 /**
5302  * Delete adjacent track points with the same timestamp
5303  * Normally new tracks that are 'routes' won't have any timestamps so should be OK to clean up the track
5304  */
5305 static void trw_layer_delete_points_same_time ( gpointer pass_along[6] )
5306 {
5307   VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
5308   VikTrack *trk;
5309   if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5310     trk = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
5311   else
5312     trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
5313
5314   if ( !trk )
5315     return;
5316
5317   gulong removed = vik_track_remove_same_time_points ( trk );
5318
5319   // Track has been updated so update tps:
5320   trw_layer_cancel_tps_of_track ( vtl, trk );
5321
5322   // Inform user how much was deleted as it's not obvious from the normal view
5323   gchar str[64];
5324   const gchar *tmp_str = ngettext("Deleted %ld point", "Deleted %ld points", removed);
5325   g_snprintf(str, 64, tmp_str, removed);
5326   a_dialog_info_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), str);
5327
5328   vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
5329 }
5330
5331 /**
5332  * Reverse a track
5333  */
5334 static void trw_layer_reverse ( gpointer pass_along[6] )
5335 {
5336   VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
5337   VikTrack *track;
5338   if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5339     track = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
5340   else
5341     track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
5342
5343   if ( ! track )
5344     return;
5345
5346   // Check valid track
5347   GList *trps = track->trackpoints;
5348   if ( !trps )
5349     return;
5350
5351   vik_track_reverse ( track );
5352  
5353   vik_layer_emit_update ( VIK_LAYER(pass_along[0]), FALSE );
5354 }
5355
5356 /**
5357  * Similar to trw_layer_enum_item, but this uses a sorted method
5358  */
5359 /* Currently unused
5360 static void trw_layer_sorted_name_list(gpointer key, gpointer value, gpointer udata)
5361 {
5362   GList **list = (GList**)udata;
5363   // *list = g_list_prepend(*all, key); //unsorted method
5364   // Sort named list alphabetically
5365   *list = g_list_insert_sorted_with_data (*list, key, sort_alphabetically, NULL);
5366 }
5367 */
5368
5369 /**
5370  * Now Waypoint specific sort
5371  */
5372 static void trw_layer_sorted_wp_id_by_name_list (const gpointer id, const VikWaypoint *wp, gpointer udata)
5373 {
5374   GList **list = (GList**)udata;
5375   // Sort named list alphabetically
5376   *list = g_list_insert_sorted_with_data (*list, wp->name, sort_alphabetically, NULL);
5377 }
5378
5379 /**
5380  * Track specific sort
5381  */
5382 static void trw_layer_sorted_track_id_by_name_list (const gpointer id, const VikTrack *trk, gpointer udata)
5383 {
5384   GList **list = (GList**)udata;
5385   // Sort named list alphabetically
5386   *list = g_list_insert_sorted_with_data (*list, trk->name, sort_alphabetically, NULL);
5387 }
5388
5389
5390 typedef struct {
5391   gboolean    has_same_track_name;
5392   const gchar *same_track_name;
5393 } same_track_name_udata;
5394
5395 static gint check_tracks_for_same_name ( gconstpointer aa, gconstpointer bb, gpointer udata )
5396 {
5397   const gchar* namea = (const gchar*) aa;
5398   const gchar* nameb = (const gchar*) bb;
5399
5400   // the test
5401   gint result = strcmp ( namea, nameb );
5402
5403   if ( result == 0 ) {
5404     // Found two names the same
5405     same_track_name_udata *user_data = udata;
5406     user_data->has_same_track_name = TRUE;
5407     user_data->same_track_name = namea;
5408   }
5409
5410   // Leave ordering the same
5411   return 0;
5412 }
5413
5414 /**
5415  * Find out if any tracks have the same name in this hash table
5416  */
5417 static gboolean trw_layer_has_same_track_names ( GHashTable *ht_tracks )
5418 {
5419   // Sort items by name, then compare if any next to each other are the same
5420
5421   GList *track_names = NULL;
5422   g_hash_table_foreach ( ht_tracks, (GHFunc) trw_layer_sorted_track_id_by_name_list, &track_names );
5423
5424   // No tracks
5425   if ( ! track_names )
5426     return FALSE;
5427
5428   same_track_name_udata udata;
5429   udata.has_same_track_name = FALSE;
5430
5431   // Use sort routine to traverse list comparing items
5432   // Don't care how this list ends up ordered ( doesn't actually change ) - care about the returned status
5433   GList *dummy_list = g_list_sort_with_data ( track_names, check_tracks_for_same_name, &udata );
5434   // Still no tracks...
5435   if ( ! dummy_list )
5436     return FALSE;
5437
5438   return udata.has_same_track_name;
5439 }
5440
5441 /**
5442  * Force unqiue track names for the track table specified
5443  * Note the panel is a required parameter to enable the update of the names displayed
5444  * Specify if on tracks or else on routes
5445  */
5446 static void vik_trw_layer_uniquify_tracks ( VikTrwLayer *vtl, VikLayersPanel *vlp, GHashTable *track_table, gboolean ontrack )
5447 {
5448   // . Search list for an instance of repeated name
5449   // . get track of this name
5450   // . create new name
5451   // . rename track & update equiv. treeview iter
5452   // . repeat until all different
5453
5454   same_track_name_udata udata;
5455
5456   GList *track_names = NULL;
5457   udata.has_same_track_name = FALSE;
5458   udata.same_track_name = NULL;
5459
5460   g_hash_table_foreach ( track_table, (GHFunc) trw_layer_sorted_track_id_by_name_list, &track_names );
5461
5462   // No tracks
5463   if ( ! track_names )
5464     return;
5465
5466   GList *dummy_list1 = g_list_sort_with_data ( track_names, check_tracks_for_same_name, &udata );
5467
5468   // Still no tracks...
5469   if ( ! dummy_list1 )
5470     return;
5471
5472   while ( udata.has_same_track_name ) {
5473
5474     // Find a track with the same name
5475     VikTrack *trk;
5476     if ( ontrack )
5477       trk = vik_trw_layer_get_track ( vtl, (gpointer) udata.same_track_name );
5478     else
5479       trk = vik_trw_layer_get_route ( vtl, (gpointer) udata.same_track_name );
5480
5481     if ( ! trk ) {
5482       // Broken :(
5483       g_critical("Houston, we've had a problem.");
5484       vik_statusbar_set_message ( vik_window_get_statusbar (VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl))), VIK_STATUSBAR_INFO, 
5485                                   _("Internal Error in vik_trw_layer_uniquify_tracks") );
5486       return;
5487     }
5488
5489     // Rename it
5490     gchar *newname = trw_layer_new_unique_sublayer_name ( vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, udata.same_track_name );
5491     vik_track_set_name ( trk, newname );
5492
5493     trku_udata udataU;
5494     udataU.trk  = trk;
5495     udataU.uuid = NULL;
5496
5497     // Need want key of it for treeview update
5498     gpointer *trkf = g_hash_table_find ( track_table, (GHRFunc) trw_layer_track_find_uuid, &udataU );
5499
5500     if ( trkf && udataU.uuid ) {
5501
5502       GtkTreeIter *it;
5503       if ( ontrack )
5504         it = g_hash_table_lookup ( vtl->tracks_iters, udataU.uuid );
5505       else
5506         it = g_hash_table_lookup ( vtl->routes_iters, udataU.uuid );
5507
5508       if ( it ) {
5509         vik_treeview_item_set_name ( VIK_LAYER(vtl)->vt, it, newname );
5510 #ifdef VIK_CONFIG_ALPHABETIZED_TRW
5511         vik_treeview_sublayer_realphabetize ( VIK_LAYER(vtl)->vt, it, newname );
5512 #endif
5513       }
5514     }
5515
5516     // Start trying to find same names again...
5517     track_names = NULL;
5518     g_hash_table_foreach ( track_table, (GHFunc) trw_layer_sorted_track_id_by_name_list, &track_names );
5519     udata.has_same_track_name = FALSE;
5520     GList *dummy_list2 = g_list_sort_with_data ( track_names, check_tracks_for_same_name, &udata );
5521
5522     // No tracks any more - give up searching
5523     if ( ! dummy_list2 )
5524       udata.has_same_track_name = FALSE;
5525   }
5526
5527   // Update
5528   vik_layers_panel_emit_update ( vlp );
5529 }
5530
5531 /**
5532  *
5533  */
5534 static void trw_layer_delete_tracks_from_selection ( gpointer lav[2] )
5535 {
5536   VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
5537   GList *all = NULL;
5538
5539   // Ensure list of track names offered is unique
5540   if ( trw_layer_has_same_track_names ( vtl->tracks ) ) {
5541     if ( a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
5542                               _("Multiple entries with the same name exist. This method only works with unique names. Force unique names now?"), NULL ) ) {
5543       vik_trw_layer_uniquify_tracks ( vtl, VIK_LAYERS_PANEL(lav[1]), vtl->tracks, TRUE );
5544     }
5545     else
5546       return;
5547   }
5548
5549   // Sort list alphabetically for better presentation
5550   g_hash_table_foreach(vtl->tracks, (GHFunc) trw_layer_sorted_track_id_by_name_list, &all);
5551
5552   if ( ! all ) {
5553     a_dialog_error_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), _("No tracks found"));
5554     return;
5555   }
5556
5557   // Get list of items to delete from the user
5558   GList *delete_list = a_dialog_select_from_list(VIK_GTK_WINDOW_FROM_LAYER(vtl),
5559                                                  all,
5560                                                  TRUE,
5561                                                  _("Delete Selection"),
5562                                                  _("Select tracks to delete"));
5563   g_list_free(all);
5564
5565   // Delete requested tracks
5566   // since specificly requested, IMHO no need for extra confirmation
5567   if ( delete_list ) {
5568     GList *l;
5569     for (l = delete_list; l != NULL; l = g_list_next(l)) {
5570       // This deletes first trk it finds of that name (but uniqueness is enforced above)
5571       trw_layer_delete_track_by_name (vtl, l->data, vtl->tracks);
5572     }
5573     g_list_free(delete_list);
5574     vik_layer_emit_update( VIK_LAYER(vtl), FALSE );
5575   }
5576 }
5577
5578 /**
5579  *
5580  */
5581 static void trw_layer_delete_routes_from_selection ( gpointer lav[2] )
5582 {
5583   VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
5584   GList *all = NULL;
5585
5586   // Ensure list of track names offered is unique
5587   if ( trw_layer_has_same_track_names ( vtl->routes ) ) {
5588     if ( a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
5589                               _("Multiple entries with the same name exist. This method only works with unique names. Force unique names now?"), NULL ) ) {
5590       vik_trw_layer_uniquify_tracks ( vtl, VIK_LAYERS_PANEL(lav[1]), vtl->routes, FALSE );
5591     }
5592     else
5593       return;
5594   }
5595
5596   // Sort list alphabetically for better presentation
5597   g_hash_table_foreach(vtl->routes, (GHFunc) trw_layer_sorted_track_id_by_name_list, &all);
5598
5599   if ( ! all ) {
5600     a_dialog_error_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), _("No routes found"));
5601     return;
5602   }
5603
5604   // Get list of items to delete from the user
5605   GList *delete_list = a_dialog_select_from_list ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
5606                                                    all,
5607                                                    TRUE,
5608                                                    _("Delete Selection"),
5609                                                    _("Select routes to delete") );
5610   g_list_free(all);
5611
5612   // Delete requested routes
5613   // since specificly requested, IMHO no need for extra confirmation
5614   if ( delete_list ) {
5615     GList *l;
5616     for (l = delete_list; l != NULL; l = g_list_next(l)) {
5617       // This deletes first route it finds of that name (but uniqueness is enforced above)
5618       trw_layer_delete_track_by_name (vtl, l->data, vtl->routes);
5619     }
5620     g_list_free(delete_list);
5621     vik_layer_emit_update( VIK_LAYER(vtl), FALSE );
5622   }
5623 }
5624
5625 typedef struct {
5626   gboolean    has_same_waypoint_name;
5627   const gchar *same_waypoint_name;
5628 } same_waypoint_name_udata;
5629
5630 static gint check_waypoints_for_same_name ( gconstpointer aa, gconstpointer bb, gpointer udata )
5631 {
5632   const gchar* namea = (const gchar*) aa;
5633   const gchar* nameb = (const gchar*) bb;
5634
5635   // the test
5636   gint result = strcmp ( namea, nameb );
5637
5638   if ( result == 0 ) {
5639     // Found two names the same
5640     same_waypoint_name_udata *user_data = udata;
5641     user_data->has_same_waypoint_name = TRUE;
5642     user_data->same_waypoint_name = namea;
5643   }
5644
5645   // Leave ordering the same
5646   return 0;
5647 }
5648
5649 /**
5650  * Find out if any waypoints have the same name in this layer
5651  */
5652 gboolean trw_layer_has_same_waypoint_names ( VikTrwLayer *vtl )
5653 {
5654   // Sort items by name, then compare if any next to each other are the same
5655
5656   GList *waypoint_names = NULL;
5657   g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_sorted_wp_id_by_name_list, &waypoint_names );
5658
5659   // No waypoints
5660   if ( ! waypoint_names )
5661     return FALSE;
5662
5663   same_waypoint_name_udata udata;
5664   udata.has_same_waypoint_name = FALSE;
5665
5666   // Use sort routine to traverse list comparing items
5667   // Don't care how this list ends up ordered ( doesn't actually change ) - care about the returned status
5668   GList *dummy_list = g_list_sort_with_data ( waypoint_names, check_waypoints_for_same_name, &udata );
5669   // Still no waypoints...
5670   if ( ! dummy_list )
5671     return FALSE;
5672
5673   return udata.has_same_waypoint_name;
5674 }
5675
5676 /**
5677  * Force unqiue waypoint names for this layer
5678  * Note the panel is a required parameter to enable the update of the names displayed
5679  */
5680 static void vik_trw_layer_uniquify_waypoints ( VikTrwLayer *vtl, VikLayersPanel *vlp )
5681 {
5682   // . Search list for an instance of repeated name
5683   // . get waypoint of this name
5684   // . create new name
5685   // . rename waypoint & update equiv. treeview iter
5686   // . repeat until all different
5687
5688   same_waypoint_name_udata udata;
5689
5690   GList *waypoint_names = NULL;
5691   udata.has_same_waypoint_name = FALSE;
5692   udata.same_waypoint_name = NULL;
5693
5694   g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_sorted_wp_id_by_name_list, &waypoint_names );
5695
5696   // No waypoints
5697   if ( ! waypoint_names )
5698     return;
5699
5700   GList *dummy_list1 = g_list_sort_with_data ( waypoint_names, check_waypoints_for_same_name, &udata );
5701
5702   // Still no waypoints...
5703   if ( ! dummy_list1 )
5704     return;
5705
5706   while ( udata.has_same_waypoint_name ) {
5707
5708     // Find a waypoint with the same name
5709     VikWaypoint *waypoint = vik_trw_layer_get_waypoint ( vtl, (gpointer) udata.same_waypoint_name );
5710
5711     if ( ! waypoint ) {
5712       // Broken :(
5713       g_critical("Houston, we've had a problem.");
5714       vik_statusbar_set_message ( vik_window_get_statusbar (VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl))), VIK_STATUSBAR_INFO, 
5715                                   _("Internal Error in vik_trw_layer_uniquify_waypoints") );
5716       return;
5717     }
5718
5719     // Rename it
5720     gchar *newname = trw_layer_new_unique_sublayer_name ( vtl, VIK_TRW_LAYER_SUBLAYER_WAYPOINT, udata.same_waypoint_name );
5721     vik_waypoint_set_name ( waypoint, newname );
5722
5723     wpu_udata udataU;
5724     udataU.wp   = waypoint;
5725     udataU.uuid = NULL;
5726
5727     // Need want key of it for treeview update
5728     gpointer *wpf = g_hash_table_find ( vtl->waypoints, (GHRFunc) trw_layer_waypoint_find_uuid, &udataU );
5729
5730     if ( wpf && udataU.uuid ) {
5731
5732       GtkTreeIter *it = g_hash_table_lookup ( vtl->waypoints_iters, udataU.uuid );
5733
5734       if ( it ) {
5735         vik_treeview_item_set_name ( VIK_LAYER(vtl)->vt, it, newname );
5736 #ifdef VIK_CONFIG_ALPHABETIZED_TRW
5737         vik_treeview_sublayer_realphabetize ( VIK_LAYER(vtl)->vt, it, newname );
5738 #endif
5739       }
5740     }
5741
5742     // Start trying to find same names again...
5743     waypoint_names = NULL;
5744     g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_sorted_wp_id_by_name_list, &waypoint_names );
5745     udata.has_same_waypoint_name = FALSE;
5746     GList *dummy_list2 = g_list_sort_with_data ( waypoint_names, check_waypoints_for_same_name, &udata );
5747
5748     // No waypoints any more - give up searching
5749     if ( ! dummy_list2 )
5750       udata.has_same_waypoint_name = FALSE;
5751   }
5752
5753   // Update
5754   vik_layers_panel_emit_update ( vlp );
5755 }
5756
5757 /**
5758  *
5759  */
5760 static void trw_layer_delete_waypoints_from_selection ( gpointer lav[2] )
5761 {
5762   VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
5763   GList *all = NULL;
5764
5765   // Ensure list of waypoint names offered is unique
5766   if ( trw_layer_has_same_waypoint_names ( vtl ) ) {
5767     if ( a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
5768                               _("Multiple entries with the same name exist. This method only works with unique names. Force unique names now?"), NULL ) ) {
5769       vik_trw_layer_uniquify_waypoints ( vtl, VIK_LAYERS_PANEL(lav[1]) );
5770     }
5771     else
5772       return;
5773   }
5774
5775   // Sort list alphabetically for better presentation
5776   g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_sorted_wp_id_by_name_list, &all);
5777   if ( ! all ) {
5778     a_dialog_error_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), _("No waypoints found"));
5779     return;
5780   }
5781
5782   all = g_list_sort_with_data(all, sort_alphabetically, NULL);
5783
5784   // Get list of items to delete from the user
5785   GList *delete_list = a_dialog_select_from_list(VIK_GTK_WINDOW_FROM_LAYER(vtl),
5786                                                  all,
5787                                                  TRUE,
5788                                                  _("Delete Selection"),
5789                                                  _("Select waypoints to delete"));
5790   g_list_free(all);
5791
5792   // Delete requested waypoints
5793   // since specificly requested, IMHO no need for extra confirmation
5794   if ( delete_list ) {
5795     GList *l;
5796     for (l = delete_list; l != NULL; l = g_list_next(l)) {
5797       // This deletes first waypoint it finds of that name (but uniqueness is enforced above)
5798       trw_layer_delete_waypoint_by_name (vtl, l->data);
5799     }
5800     g_list_free(delete_list);
5801     vik_layer_emit_update( VIK_LAYER(vtl), FALSE );
5802   }
5803
5804 }
5805
5806 static void trw_layer_goto_waypoint ( gpointer pass_along[6] )
5807 {
5808   VikWaypoint *wp = g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->waypoints, pass_along[3] );
5809   if ( wp )
5810     goto_coord ( pass_along[1], pass_along[0], pass_along[5], &(wp->coord) );
5811 }
5812
5813 static void trw_layer_waypoint_gc_webpage ( gpointer pass_along[6] )
5814 {
5815   gchar *webpage = g_strdup_printf("http://www.geocaching.com/seek/cache_details.aspx?wp=%s", (gchar *) pass_along[3] );
5816   open_url(VIK_GTK_WINDOW_FROM_LAYER(VIK_LAYER(pass_along[0])), webpage);
5817   g_free ( webpage );
5818 }
5819
5820 static const gchar* trw_layer_sublayer_rename_request ( VikTrwLayer *l, const gchar *newname, gpointer vlp, gint subtype, gpointer sublayer, GtkTreeIter *iter )
5821 {
5822   if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT )
5823   {
5824     VikWaypoint *wp = g_hash_table_lookup ( l->waypoints, sublayer );
5825
5826     // No actual change to the name supplied
5827     if (strcmp(newname, wp->name) == 0 )
5828       return NULL;
5829
5830     VikWaypoint *wpf = vik_trw_layer_get_waypoint ( l, newname );
5831
5832     if ( wpf ) {
5833       // An existing waypoint has been found with the requested name
5834       if ( ! a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(l),
5835            _("A waypoint with the name \"%s\" already exists. Really rename to the same name?"),
5836            newname ) )
5837         return NULL;
5838     }
5839
5840     // Update WP name and refresh the treeview
5841     vik_waypoint_set_name (wp, newname);
5842
5843 #ifdef VIK_CONFIG_ALPHABETIZED_TRW
5844     vik_treeview_sublayer_realphabetize ( VIK_LAYER(l)->vt, iter, newname );
5845 #endif
5846
5847     vik_layers_panel_emit_update ( VIK_LAYERS_PANEL(vlp) );
5848
5849     return newname;
5850   }
5851
5852   if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
5853   {
5854     VikTrack *trk = g_hash_table_lookup ( l->tracks, sublayer );
5855
5856     // No actual change to the name supplied
5857     if (strcmp(newname, trk->name) == 0)
5858       return NULL;
5859
5860     VikTrack *trkf = vik_trw_layer_get_track ( l, (gpointer) newname );
5861
5862     if ( trkf ) {
5863       // An existing track has been found with the requested name
5864       if ( ! a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(l),
5865           _("A track with the name \"%s\" already exists. Really rename to the same name?"),
5866           newname ) )
5867         return NULL;
5868     }
5869     // Update track name and refresh GUI parts
5870     vik_track_set_name (trk, newname);
5871
5872     // Update any subwindows that could be displaying this track which has changed name
5873     // Only one Track Edit Window
5874     if ( l->current_tp_track == trk && l->tpwin ) {
5875       vik_trw_layer_tpwin_set_track_name ( l->tpwin, newname );
5876     }
5877     // Property Dialog of the track
5878     vik_trw_layer_propwin_update ( trk );
5879
5880 #ifdef VIK_CONFIG_ALPHABETIZED_TRW
5881     vik_treeview_sublayer_realphabetize ( VIK_LAYER(l)->vt, iter, newname );
5882 #endif
5883
5884     vik_layers_panel_emit_update ( VIK_LAYERS_PANEL(vlp) );
5885
5886     return newname;
5887   }
5888
5889   if ( subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5890   {
5891     VikTrack *trk = g_hash_table_lookup ( l->routes, sublayer );
5892
5893     // No actual change to the name supplied
5894     if (strcmp(newname, trk->name) == 0)
5895       return NULL;
5896
5897     VikTrack *trkf = vik_trw_layer_get_route ( l, (gpointer) newname );
5898
5899     if ( trkf ) {
5900       // An existing track has been found with the requested name
5901       if ( ! a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(l),
5902           _("A route with the name \"%s\" already exists. Really rename to the same name?"),
5903           newname ) )
5904         return NULL;
5905     }
5906     // Update track name and refresh GUI parts
5907     vik_track_set_name (trk, newname);
5908
5909     // Update any subwindows that could be displaying this track which has changed name
5910     // Only one Track Edit Window
5911     if ( l->current_tp_track == trk && l->tpwin ) {
5912       vik_trw_layer_tpwin_set_track_name ( l->tpwin, newname );
5913     }
5914     // Property Dialog of the track
5915     vik_trw_layer_propwin_update ( trk );
5916
5917 #ifdef VIK_CONFIG_ALPHABETIZED_TRW
5918     vik_treeview_sublayer_realphabetize ( VIK_LAYER(l)->vt, iter, newname );
5919 #endif
5920
5921     vik_layers_panel_emit_update ( VIK_LAYERS_PANEL(vlp) );
5922
5923     return newname;
5924   }
5925   return NULL;
5926 }
5927
5928 static gboolean is_valid_geocache_name ( gchar *str )
5929 {
5930   gint len = strlen ( str );
5931   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]));
5932 }
5933
5934 static void trw_layer_track_use_with_filter ( gpointer pass_along[6] )
5935 {
5936   VikTrack *trk = g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, pass_along[3] );
5937   a_acquire_set_filter_track ( trk );
5938 }
5939
5940 #ifdef VIK_CONFIG_GOOGLE
5941 static gboolean is_valid_google_route ( VikTrwLayer *vtl, const gpointer track_id )
5942 {
5943   VikTrack *tr = g_hash_table_lookup ( vtl->tracks, track_id );
5944   return ( tr && tr->comment && strlen(tr->comment) > 7 && !strncmp(tr->comment, "from:", 5) );
5945 }
5946
5947 static void trw_layer_track_google_route_webpage ( gpointer pass_along[6] )
5948 {
5949   VikTrack *tr = g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, pass_along[3] );
5950   if ( tr ) {
5951     gchar *escaped = uri_escape ( tr->comment );
5952     gchar *webpage = g_strdup_printf("http://maps.google.com/maps?f=q&hl=en&q=%s", escaped );
5953     open_url(VIK_GTK_WINDOW_FROM_LAYER(VIK_LAYER(pass_along[0])), webpage);
5954     g_free ( escaped );
5955     g_free ( webpage );
5956   }
5957 }
5958 #endif
5959
5960 /* vlp can be NULL if necessary - i.e. right-click from a tool */
5961 /* viewpoint is now available instead */
5962 static gboolean trw_layer_sublayer_add_menu_items ( VikTrwLayer *l, GtkMenu *menu, gpointer vlp, gint subtype, gpointer sublayer, GtkTreeIter *iter, VikViewport *vvp )
5963 {
5964   static gpointer pass_along[8];
5965   GtkWidget *item;
5966   gboolean rv = FALSE;
5967
5968   pass_along[0] = l;
5969   pass_along[1] = vlp;
5970   pass_along[2] = GINT_TO_POINTER (subtype);
5971   pass_along[3] = sublayer;
5972   pass_along[4] = GINT_TO_POINTER (1); // Confirm delete request
5973   pass_along[5] = vvp;
5974   pass_along[6] = iter;
5975   pass_along[7] = NULL; // For misc purposes - maybe track or waypoint
5976
5977   if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT || subtype == VIK_TRW_LAYER_SUBLAYER_TRACK || subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5978   {
5979     rv = TRUE;
5980
5981     item = gtk_image_menu_item_new_from_stock ( GTK_STOCK_PROPERTIES, NULL );
5982     g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_properties_item), pass_along );
5983     gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
5984     gtk_widget_show ( item );
5985
5986     if (subtype == VIK_TRW_LAYER_SUBLAYER_TRACK) {
5987       VikTrack *tr = g_hash_table_lookup ( l->tracks, sublayer );
5988       if (tr && tr->property_dialog)
5989         gtk_widget_set_sensitive(GTK_WIDGET(item), FALSE );
5990     }
5991     if (subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE) {
5992       VikTrack *tr = g_hash_table_lookup ( l->routes, sublayer );
5993       if (tr && tr->property_dialog)
5994         gtk_widget_set_sensitive(GTK_WIDGET(item), FALSE );
5995     }
5996
5997     item = gtk_image_menu_item_new_from_stock ( GTK_STOCK_CUT, NULL );
5998     g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_cut_item_cb), pass_along );
5999     gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6000     gtk_widget_show ( item );
6001
6002     item = gtk_image_menu_item_new_from_stock ( GTK_STOCK_COPY, NULL );
6003     g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_copy_item_cb), pass_along );
6004     gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6005     gtk_widget_show ( item );
6006
6007     item = gtk_image_menu_item_new_from_stock ( GTK_STOCK_DELETE, NULL );
6008     g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_item), pass_along );
6009     gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6010     gtk_widget_show ( item );
6011
6012     if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT )
6013     {
6014       gboolean separator_created = FALSE;
6015
6016       /* could be a right-click using the tool */
6017       if ( vlp != NULL ) {
6018         item = gtk_menu_item_new ();
6019         gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6020         gtk_widget_show ( item );
6021
6022         separator_created = TRUE;
6023
6024         item = gtk_image_menu_item_new_with_mnemonic ( _("_Goto") );
6025         gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_JUMP_TO, GTK_ICON_SIZE_MENU) );
6026         g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_waypoint), pass_along );
6027         gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6028         gtk_widget_show ( item );
6029       }
6030
6031       VikWaypoint *wp = g_hash_table_lookup ( VIK_TRW_LAYER(l)->waypoints, sublayer );
6032
6033       if ( wp && wp->name ) {
6034         if ( is_valid_geocache_name ( wp->name ) ) {
6035
6036           if ( !separator_created ) {
6037             item = gtk_menu_item_new ();
6038             gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6039             gtk_widget_show ( item );
6040             separator_created = TRUE;
6041           }
6042
6043           item = gtk_menu_item_new_with_mnemonic ( _("_Visit Geocache Webpage") );
6044           g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_waypoint_gc_webpage), pass_along );
6045           gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6046           gtk_widget_show ( item );
6047         }
6048       }
6049
6050       if ( wp && wp->image )
6051       {
6052         if ( !separator_created ) {
6053           item = gtk_menu_item_new ();
6054           gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6055           gtk_widget_show ( item );
6056           separator_created = TRUE;
6057         }
6058
6059         // Set up image paramater
6060         pass_along[5] = wp->image;
6061
6062         item = gtk_image_menu_item_new_with_mnemonic ( _("_Show Picture...") );
6063         gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock ("Show Picture", GTK_ICON_SIZE_MENU) ); // Own icon - see stock_icons in vikwindow.c
6064         g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_show_picture), pass_along );
6065         gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6066         gtk_widget_show ( item );
6067
6068 #ifdef VIK_CONFIG_GEOTAG
6069         GtkWidget *geotag_submenu = gtk_menu_new ();
6070         item = gtk_image_menu_item_new_with_mnemonic ( _("Update Geotag on _Image") );
6071         gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REFRESH, GTK_ICON_SIZE_MENU) );
6072         gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
6073         gtk_widget_show ( item );
6074         gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), geotag_submenu );
6075   
6076         item = gtk_menu_item_new_with_mnemonic ( _("_Update") );
6077         g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_geotagging_waypoint_mtime_update), pass_along );
6078         gtk_menu_shell_append (GTK_MENU_SHELL (geotag_submenu), item);
6079         gtk_widget_show ( item );
6080
6081         item = gtk_menu_item_new_with_mnemonic ( _("Update and _Keep File Timestamp") );
6082         g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_geotagging_waypoint_mtime_keep), pass_along );
6083         gtk_menu_shell_append (GTK_MENU_SHELL (geotag_submenu), item);
6084         gtk_widget_show ( item );
6085 #endif
6086       }
6087
6088     }
6089   }
6090
6091   if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINTS || subtype == VIK_TRW_LAYER_SUBLAYER_TRACKS || subtype == VIK_TRW_LAYER_SUBLAYER_ROUTES ) {
6092     item = gtk_image_menu_item_new_from_stock ( GTK_STOCK_PASTE, NULL );
6093     g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_paste_item_cb), pass_along );
6094     gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6095     gtk_widget_show ( item );
6096     // TODO: only enable if suitable item is in clipboard - want to determine *which* sublayer type
6097     if ( a_clipboard_type ( ) == VIK_CLIPBOARD_DATA_SUBLAYER )
6098       gtk_widget_set_sensitive ( item, TRUE );
6099     else
6100       gtk_widget_set_sensitive ( item, FALSE );
6101
6102     // Add separator
6103     item = gtk_menu_item_new ();
6104     gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6105     gtk_widget_show ( item );
6106   }
6107
6108   if ( vlp && (subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINTS || subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT) )
6109   {
6110     rv = TRUE;
6111     item = gtk_image_menu_item_new_with_mnemonic ( _("_New Waypoint...") );
6112     gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NEW, GTK_ICON_SIZE_MENU) );
6113     g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_wp), pass_along );
6114     gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
6115     gtk_widget_show ( item );
6116   }
6117
6118   if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINTS )
6119   {
6120     item = gtk_image_menu_item_new_with_mnemonic ( _("_View All Waypoints") );
6121     gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ZOOM_FIT, GTK_ICON_SIZE_MENU) );
6122     g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_waypoints_view), pass_along );
6123     gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
6124     gtk_widget_show ( item );
6125
6126     item = gtk_image_menu_item_new_with_mnemonic ( _("Goto _Waypoint...") );
6127     gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_JUMP_TO, GTK_ICON_SIZE_MENU) );
6128     g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_wp), pass_along );
6129     gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
6130     gtk_widget_show ( item );
6131
6132     item = gtk_image_menu_item_new_with_mnemonic ( _("Delete _All Waypoints") );
6133     gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU) );
6134     g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_all_waypoints), pass_along );
6135     gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6136     gtk_widget_show ( item );
6137
6138     item = gtk_image_menu_item_new_with_mnemonic ( _("_Delete Waypoints From Selection...") );
6139     gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
6140     g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_waypoints_from_selection), pass_along );
6141     gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6142     gtk_widget_show ( item );
6143   }
6144
6145   if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACKS )
6146   {
6147     rv = TRUE;
6148
6149     if ( l->current_track && !l->current_track->is_route ) {
6150       item = gtk_menu_item_new_with_mnemonic ( _("_Finish Track") );
6151       g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_finish_track), pass_along );
6152       gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
6153       gtk_widget_show ( item );
6154       // Add separator
6155       item = gtk_menu_item_new ();
6156       gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6157       gtk_widget_show ( item );
6158     }
6159
6160     item = gtk_image_menu_item_new_with_mnemonic ( _("_View All Tracks") );
6161     gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ZOOM_FIT, GTK_ICON_SIZE_MENU) );
6162     g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_tracks_view), pass_along );
6163     gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
6164     gtk_widget_show ( item );
6165
6166     item = gtk_image_menu_item_new_with_mnemonic ( _("_New Track") );
6167     gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NEW, GTK_ICON_SIZE_MENU) );
6168     g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_track), pass_along );
6169     gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
6170     gtk_widget_show ( item );
6171     // Make it available only when a new track *not* already in progress
6172     gtk_widget_set_sensitive ( item, ! (gboolean)GPOINTER_TO_INT(l->current_track) );
6173
6174     item = gtk_image_menu_item_new_with_mnemonic ( _("Delete _All Tracks") );
6175     gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU) );
6176     g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_all_tracks), pass_along );
6177     gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6178     gtk_widget_show ( item );
6179
6180     item = gtk_image_menu_item_new_with_mnemonic ( _("_Delete Tracks From Selection...") );
6181     gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
6182     g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_tracks_from_selection), pass_along );
6183     gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6184     gtk_widget_show ( item );
6185   }
6186
6187   if ( subtype == VIK_TRW_LAYER_SUBLAYER_ROUTES )
6188   {
6189     rv = TRUE;
6190
6191     if ( l->current_track && l->current_track->is_route ) {
6192       item = gtk_menu_item_new_with_mnemonic ( _("_Finish Route") );
6193       // Reuse finish track method
6194       g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_finish_track), pass_along );
6195       gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
6196       gtk_widget_show ( item );
6197       // Add separator
6198       item = gtk_menu_item_new ();
6199       gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6200       gtk_widget_show ( item );
6201     }
6202
6203     item = gtk_image_menu_item_new_with_mnemonic ( _("_View All Routes") );
6204     gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ZOOM_FIT, GTK_ICON_SIZE_MENU) );
6205     g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_routes_view), pass_along );
6206     gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
6207     gtk_widget_show ( item );
6208
6209     item = gtk_image_menu_item_new_with_mnemonic ( _("_New Route") );
6210     gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NEW, GTK_ICON_SIZE_MENU) );
6211     g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_route), pass_along );
6212     gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
6213     gtk_widget_show ( item );
6214     // Make it available only when a new track *not* already in progress
6215     gtk_widget_set_sensitive ( item, ! (gboolean)GPOINTER_TO_INT(l->current_track) );
6216
6217     item = gtk_image_menu_item_new_with_mnemonic ( _("Delete _All Routes") );
6218     gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU) );
6219     g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_all_routes), pass_along );
6220     gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6221     gtk_widget_show ( item );
6222
6223     item = gtk_image_menu_item_new_with_mnemonic ( _("_Delete Routes From Selection...") );
6224     gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
6225     g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_routes_from_selection), pass_along );
6226     gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6227     gtk_widget_show ( item );
6228   }
6229
6230   GtkWidget *upload_submenu = gtk_menu_new ();
6231
6232   if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK || subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE )
6233   {
6234     item = gtk_menu_item_new ();
6235     gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6236     gtk_widget_show ( item );
6237
6238     if ( l->current_track && subtype == VIK_TRW_LAYER_SUBLAYER_TRACK && !l->current_track->is_route )
6239       item = gtk_menu_item_new_with_mnemonic ( _("_Finish Track") );
6240     if ( l->current_track && subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE && l->current_track->is_route )
6241       item = gtk_menu_item_new_with_mnemonic ( _("_Finish Route") );
6242     if ( l->current_track ) {
6243       g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_finish_track), pass_along );
6244       gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
6245       gtk_widget_show ( item );
6246
6247       // Add separator
6248       item = gtk_menu_item_new ();
6249       gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6250       gtk_widget_show ( item );
6251     }
6252
6253     if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
6254       item = gtk_image_menu_item_new_with_mnemonic ( _("_View Track") );
6255     else
6256       item = gtk_image_menu_item_new_with_mnemonic ( _("_View Route") );
6257     gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ZOOM_FIT, GTK_ICON_SIZE_MENU) );
6258     g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_track_view), pass_along );
6259     gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6260     gtk_widget_show ( item );
6261
6262     GtkWidget *goto_submenu;
6263     goto_submenu = gtk_menu_new ();
6264     item = gtk_image_menu_item_new_with_mnemonic ( _("_Goto") );
6265     gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_JUMP_TO, GTK_ICON_SIZE_MENU) );
6266     gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
6267     gtk_widget_show ( item );
6268     gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), goto_submenu );
6269
6270     item = gtk_image_menu_item_new_with_mnemonic ( _("_Startpoint") );
6271     gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GOTO_FIRST, GTK_ICON_SIZE_MENU) );
6272     g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_startpoint), pass_along );
6273     gtk_menu_shell_append ( GTK_MENU_SHELL(goto_submenu), item );
6274     gtk_widget_show ( item );
6275
6276     item = gtk_image_menu_item_new_with_mnemonic ( _("\"_Center\"") );
6277     gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_JUMP_TO, GTK_ICON_SIZE_MENU) );
6278     g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_center), pass_along );
6279     gtk_menu_shell_append ( GTK_MENU_SHELL(goto_submenu), item );
6280     gtk_widget_show ( item );
6281
6282     item = gtk_image_menu_item_new_with_mnemonic ( _("_Endpoint") );
6283     gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GOTO_LAST, GTK_ICON_SIZE_MENU) );
6284     g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_endpoint), pass_along );
6285     gtk_menu_shell_append ( GTK_MENU_SHELL(goto_submenu), item );
6286     gtk_widget_show ( item );
6287
6288     item = gtk_image_menu_item_new_with_mnemonic ( _("_Highest Altitude") );
6289     gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GOTO_TOP, GTK_ICON_SIZE_MENU) );
6290     g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_max_alt), pass_along );
6291     gtk_menu_shell_append ( GTK_MENU_SHELL(goto_submenu), item );
6292     gtk_widget_show ( item );
6293
6294     item = gtk_image_menu_item_new_with_mnemonic ( _("_Lowest Altitude") );
6295     gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GOTO_BOTTOM, GTK_ICON_SIZE_MENU) );
6296     g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_min_alt), pass_along );
6297     gtk_menu_shell_append ( GTK_MENU_SHELL(goto_submenu), item );
6298     gtk_widget_show ( item );
6299
6300     // Routes don't have speeds
6301     if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK ) {
6302       item = gtk_image_menu_item_new_with_mnemonic ( _("_Maximum Speed") );
6303       gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_MEDIA_FORWARD, GTK_ICON_SIZE_MENU) );
6304       g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_max_speed), pass_along );
6305       gtk_menu_shell_append ( GTK_MENU_SHELL(goto_submenu), item );
6306       gtk_widget_show ( item );
6307     }
6308
6309     GtkWidget *combine_submenu;
6310     combine_submenu = gtk_menu_new ();
6311     item = gtk_image_menu_item_new_with_mnemonic ( _("Co_mbine") );
6312     gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_CONNECT, GTK_ICON_SIZE_MENU) );
6313     gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
6314     gtk_widget_show ( item );
6315     gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), combine_submenu );
6316
6317     // Routes don't have times or segments...
6318     if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK ) {
6319       item = gtk_menu_item_new_with_mnemonic ( _("_Merge By Time...") );
6320       g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_merge_by_timestamp), pass_along );
6321       gtk_menu_shell_append ( GTK_MENU_SHELL(combine_submenu), item );
6322       gtk_widget_show ( item );
6323
6324       item = gtk_menu_item_new_with_mnemonic ( _("Merge _Segments") );
6325       g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_merge_by_segment), pass_along );
6326       gtk_menu_shell_append ( GTK_MENU_SHELL(combine_submenu), item );
6327       gtk_widget_show ( item );
6328     }
6329
6330     item = gtk_menu_item_new_with_mnemonic ( _("Merge _With Other Tracks...") );
6331     g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_merge_with_other), pass_along );
6332     gtk_menu_shell_append ( GTK_MENU_SHELL(combine_submenu), item );
6333     gtk_widget_show ( item );
6334
6335     if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
6336       item = gtk_menu_item_new_with_mnemonic ( _("_Append Track...") );
6337     else
6338       item = gtk_menu_item_new_with_mnemonic ( _("_Append Route...") );
6339     g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_append_track), pass_along );
6340     gtk_menu_shell_append ( GTK_MENU_SHELL(combine_submenu), item );
6341     gtk_widget_show ( item );
6342
6343     if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
6344       item = gtk_menu_item_new_with_mnemonic ( _("Append _Route...") );
6345     else
6346       item = gtk_menu_item_new_with_mnemonic ( _("Append _Track...") );
6347     g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_append_other), pass_along );
6348     gtk_menu_shell_append ( GTK_MENU_SHELL(combine_submenu), item );
6349     gtk_widget_show ( item );
6350
6351     GtkWidget *split_submenu;
6352     split_submenu = gtk_menu_new ();
6353     item = gtk_image_menu_item_new_with_mnemonic ( _("_Split") );
6354     gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_DISCONNECT, GTK_ICON_SIZE_MENU) );
6355     gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
6356     gtk_widget_show ( item );
6357     gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), split_submenu );
6358
6359     // Routes don't have times or segments...
6360     if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK ) {
6361       item = gtk_menu_item_new_with_mnemonic ( _("_Split By Time...") );
6362       g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_split_by_timestamp), pass_along );
6363       gtk_menu_shell_append ( GTK_MENU_SHELL(split_submenu), item );
6364       gtk_widget_show ( item );
6365
6366       // ATM always enable this entry - don't want to have to analyse the track before displaying the menu - to keep the menu speedy
6367       item = gtk_menu_item_new_with_mnemonic ( _("Split Se_gments") );
6368       g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_split_segments), pass_along );
6369       gtk_menu_shell_append ( GTK_MENU_SHELL(split_submenu), item );
6370       gtk_widget_show ( item );
6371     }
6372
6373     item = gtk_menu_item_new_with_mnemonic ( _("Split By _Number of Points...") );
6374     g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_split_by_n_points), pass_along );
6375     gtk_menu_shell_append ( GTK_MENU_SHELL(split_submenu), item );
6376     gtk_widget_show ( item );
6377
6378     item = gtk_menu_item_new_with_mnemonic ( _("Split at _Trackpoint") );
6379     g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_split_at_trackpoint), pass_along );
6380     gtk_menu_shell_append ( GTK_MENU_SHELL(split_submenu), item );
6381     gtk_widget_show ( item );
6382     // Make it available only when a trackpoint is selected.
6383     gtk_widget_set_sensitive ( item, (gboolean)GPOINTER_TO_INT(l->current_tpl) );
6384
6385     GtkWidget *delete_submenu;
6386     delete_submenu = gtk_menu_new ();
6387     item = gtk_image_menu_item_new_with_mnemonic ( _("Delete Poi_nts") );
6388     gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_DELETE, GTK_ICON_SIZE_MENU) );
6389     gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
6390     gtk_widget_show ( item );
6391     gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), delete_submenu );
6392
6393     item = gtk_menu_item_new_with_mnemonic ( _("Delete Points With The Same _Position") );
6394     g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_points_same_position), pass_along );
6395     gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
6396     gtk_widget_show ( item );
6397
6398     item = gtk_menu_item_new_with_mnemonic ( _("Delete Points With The Same _Time") );
6399     g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_points_same_time), pass_along );
6400     gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
6401     gtk_widget_show ( item );
6402
6403     if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
6404       item = gtk_image_menu_item_new_with_mnemonic ( _("_Reverse Track") );
6405     else
6406       item = gtk_image_menu_item_new_with_mnemonic ( _("_Reverse Route") );
6407     gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_BACK, GTK_ICON_SIZE_MENU) );
6408     g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_reverse), pass_along );
6409     gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6410     gtk_widget_show ( item );
6411
6412     /* ATM This function is only available via the layers panel, due to the method in finding out the maps in use */
6413     if ( vlp ) {
6414       if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
6415         item = gtk_image_menu_item_new_with_mnemonic ( _("Down_load Maps Along Track...") );
6416       else
6417         item = gtk_image_menu_item_new_with_mnemonic ( _("Down_load Maps Along Route...") );
6418       gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock ("Maps Download", GTK_ICON_SIZE_MENU) ); // Own icon - see stock_icons in vikwindow.c
6419       g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_download_map_along_track_cb), pass_along );
6420       gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6421       gtk_widget_show ( item );
6422     }
6423
6424     item = gtk_image_menu_item_new_with_mnemonic ( _("_Apply DEM Data") );
6425     gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock ("DEM Download/Import", GTK_ICON_SIZE_MENU) ); // Own icon - see stock_icons in vikwindow.c
6426     g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_apply_dem_data), pass_along );
6427     gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6428     gtk_widget_show ( item );
6429
6430     if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
6431       item = gtk_image_menu_item_new_with_mnemonic ( _("_Export Track as GPX...") );
6432     else
6433       item = gtk_image_menu_item_new_with_mnemonic ( _("_Export Route as GPX...") );
6434     gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_HARDDISK, GTK_ICON_SIZE_MENU) );
6435     g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_gpx_track), pass_along );
6436     gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6437     gtk_widget_show ( item );
6438
6439     if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
6440       item = gtk_image_menu_item_new_with_mnemonic ( _("E_xtend Track End") );
6441     else
6442       item = gtk_image_menu_item_new_with_mnemonic ( _("E_xtend Route End") );
6443     gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ADD, GTK_ICON_SIZE_MENU) );
6444     g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_extend_track_end), pass_along );
6445     gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6446     gtk_widget_show ( item );
6447
6448     if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
6449       item = gtk_image_menu_item_new_with_mnemonic ( _("C_onvert to a Route") );
6450     else
6451       item = gtk_image_menu_item_new_with_mnemonic ( _("C_onvert to a Track") );
6452     gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_CONVERT, GTK_ICON_SIZE_MENU) );
6453     g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_convert_track_route), pass_along );
6454     gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6455     gtk_widget_show ( item );
6456
6457 #ifdef VIK_CONFIG_GOOGLE
6458     item = gtk_image_menu_item_new_with_mnemonic ( _("Extend _Using Route Finder") );
6459     gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock ("Route Finder", GTK_ICON_SIZE_MENU) ); // Own icon - see stock_icons in vikwindow.c
6460     g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_extend_track_end_route_finder), pass_along );
6461     gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6462     gtk_widget_show ( item );
6463 #endif
6464
6465     // ATM can't upload a single waypoint but can do waypoints to a GPS
6466     if ( subtype != VIK_TRW_LAYER_SUBLAYER_WAYPOINT ) {
6467       item = gtk_image_menu_item_new_with_mnemonic ( _("_Upload") );
6468       gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_UP, GTK_ICON_SIZE_MENU) );
6469       gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
6470       gtk_widget_show ( item );
6471       gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), upload_submenu );
6472
6473       item = gtk_image_menu_item_new_with_mnemonic ( _("_Upload to GPS...") );
6474       gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_FORWARD, GTK_ICON_SIZE_MENU) );
6475       g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_gps_upload_any), pass_along );
6476       gtk_menu_shell_append ( GTK_MENU_SHELL(upload_submenu), item );
6477       gtk_widget_show ( item );
6478     }
6479   }
6480
6481   // Some things aren't usable with routes
6482   if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK ) {
6483 #ifdef VIK_CONFIG_OPENSTREETMAP
6484     item = gtk_image_menu_item_new_with_mnemonic ( _("Upload to _OSM...") );
6485     // Convert internal pointer into actual track for usage outside this file
6486     pass_along[7] = g_hash_table_lookup ( l->tracks, sublayer);
6487     gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_UP, GTK_ICON_SIZE_MENU) );
6488     g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(osm_traces_upload_track_cb), pass_along );
6489     gtk_menu_shell_append ( GTK_MENU_SHELL(upload_submenu), item );
6490     gtk_widget_show ( item );
6491 #endif
6492
6493     item = gtk_image_menu_item_new_with_mnemonic ( _("_Upload to GPS...") );
6494     gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_FORWARD, GTK_ICON_SIZE_MENU) );
6495     g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_gps_upload_any), pass_along );
6496     gtk_menu_shell_append ( GTK_MENU_SHELL(upload_submenu), item );
6497     gtk_widget_show ( item );
6498
6499 #ifdef VIK_CONFIG_GOOGLE
6500     if ( is_valid_google_route ( l, sublayer ) )
6501     {
6502       item = gtk_image_menu_item_new_with_mnemonic ( _("_View Google Directions") );
6503       gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NETWORK, GTK_ICON_SIZE_MENU) );
6504       g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_track_google_route_webpage), pass_along );
6505       gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6506       gtk_widget_show ( item );
6507     }
6508 #endif
6509
6510     item = gtk_image_menu_item_new_with_mnemonic ( _("Use with _Filter") );
6511     gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
6512     g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_track_use_with_filter), pass_along );
6513     gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6514     gtk_widget_show ( item );
6515
6516     /* ATM This function is only available via the layers panel, due to needing a vlp */
6517     if ( vlp ) {
6518       item = a_acquire_track_menu ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(l)), vlp,
6519                                     vik_layers_panel_get_viewport(VIK_LAYERS_PANEL(vlp)),
6520                                     g_hash_table_lookup ( l->tracks, (gchar *) sublayer ) );
6521       if ( item ) {
6522         gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
6523         gtk_widget_show ( item );
6524       }
6525     }
6526
6527 #ifdef VIK_CONFIG_GEOTAG
6528     item = gtk_menu_item_new_with_mnemonic ( _("Geotag _Images...") );
6529     g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_geotagging_track), pass_along );
6530     gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
6531     gtk_widget_show ( item );
6532 #endif
6533   }
6534
6535   if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK || subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE ) {
6536     // Only show on viewport popmenu when a trackpoint is selected
6537     if ( ! vlp && l->current_tpl ) {
6538       // Add separator
6539       item = gtk_menu_item_new ();
6540       gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6541       gtk_widget_show ( item );
6542
6543       item = gtk_image_menu_item_new_with_mnemonic ( _("_Edit Trackpoint") );
6544       gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_PROPERTIES, GTK_ICON_SIZE_MENU) );
6545       g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_edit_trackpoint), pass_along );
6546       gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6547       gtk_widget_show ( item );
6548     }
6549   }
6550
6551   return rv;
6552 }
6553
6554 static void trw_layer_insert_tp_after_current_tp ( VikTrwLayer *vtl )
6555 {
6556   /* sanity checks */
6557   if (!vtl->current_tpl)
6558     return;
6559   if (!vtl->current_tpl->next)
6560     return;
6561
6562   VikTrackpoint *tp_current = VIK_TRACKPOINT(vtl->current_tpl->data);
6563   VikTrackpoint *tp_next = VIK_TRACKPOINT(vtl->current_tpl->next->data);
6564
6565   /* Use current and next trackpoints to form a new track point which is inserted into the tracklist */
6566   if ( tp_next ) {
6567
6568     VikTrackpoint *tp_new = vik_trackpoint_new();
6569     struct LatLon ll_current, ll_next;
6570     vik_coord_to_latlon ( &tp_current->coord, &ll_current );
6571     vik_coord_to_latlon ( &tp_next->coord, &ll_next );
6572
6573     /* main positional interpolation */
6574     struct LatLon ll_new = { (ll_current.lat+ll_next.lat)/2, (ll_current.lon+ll_next.lon)/2 };
6575     vik_coord_load_from_latlon ( &(tp_new->coord), vtl->coord_mode, &ll_new );
6576
6577     /* Now other properties that can be interpolated */
6578     tp_new->altitude = (tp_current->altitude + tp_next->altitude) / 2;
6579
6580     if (tp_current->has_timestamp && tp_next->has_timestamp) {
6581       /* Note here the division is applied to each part, then added
6582          This is to avoid potential overflow issues with a 32 time_t for dates after midpoint of this Unix time on 2004/01/04 */
6583       tp_new->timestamp = (tp_current->timestamp/2) + (tp_next->timestamp/2);
6584       tp_new->has_timestamp = TRUE;
6585     }
6586
6587     if (tp_current->speed != NAN && tp_next->speed != NAN)
6588       tp_new->speed = (tp_current->speed + tp_next->speed)/2;
6589
6590     /* TODO - improve interpolation of course, as it may not be correct.
6591        if courses in degrees are 350 + 020, the mid course more likely to be 005 (not 185)
6592        [similar applies if value is in radians] */
6593     if (tp_current->course != NAN && tp_next->course != NAN)
6594       tp_new->speed = (tp_current->course + tp_next->course)/2;
6595
6596     /* DOP / sat values remain at defaults as not they do not seem applicable to a dreamt up point */
6597
6598     /* Insert new point into the trackpoints list after the current TP */
6599     VikTrack *trk = g_hash_table_lookup ( vtl->tracks, vtl->current_tp_id );
6600     if ( !trk )
6601       // Otherwise try routes
6602       trk = g_hash_table_lookup ( vtl->routes, vtl->current_tp_id );
6603     if ( !trk )
6604       return;
6605
6606     gint index =  g_list_index ( trk->trackpoints, tp_current );
6607     if ( index > -1 ) {
6608       trk->trackpoints = g_list_insert ( trk->trackpoints, tp_new, index+1 );
6609     }
6610   }
6611 }
6612
6613 static void trw_layer_cancel_current_tp ( VikTrwLayer *vtl, gboolean destroy )
6614 {
6615   if ( vtl->tpwin )
6616   {
6617     if ( destroy)
6618     {
6619       gtk_widget_destroy ( GTK_WIDGET(vtl->tpwin) );
6620       vtl->tpwin = NULL;
6621     }
6622     else
6623       vik_trw_layer_tpwin_set_empty ( vtl->tpwin );
6624   }
6625   if ( vtl->current_tpl )
6626   {
6627     vtl->current_tpl = NULL;
6628     vtl->current_tp_track = NULL;
6629     vtl->current_tp_id = NULL;
6630     vik_layer_emit_update(VIK_LAYER(vtl), FALSE);
6631   }
6632 }
6633
6634 static void trw_layer_tpwin_response ( VikTrwLayer *vtl, gint response )
6635 {
6636   g_assert ( vtl->tpwin != NULL );
6637   if ( response == VIK_TRW_LAYER_TPWIN_CLOSE )
6638     trw_layer_cancel_current_tp ( vtl, TRUE );
6639
6640   if ( vtl->current_tpl == NULL )
6641     return;
6642
6643   if ( response == VIK_TRW_LAYER_TPWIN_SPLIT && vtl->current_tpl->next && vtl->current_tpl->prev )
6644   {
6645     trw_layer_split_at_selected_trackpoint ( vtl, vtl->current_tp_track->is_route ? VIK_TRW_LAYER_SUBLAYER_ROUTE : VIK_TRW_LAYER_SUBLAYER_TRACK );
6646     vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, vtl->current_tp_track->name );
6647   }
6648   else if ( response == VIK_TRW_LAYER_TPWIN_DELETE )
6649   {
6650     VikTrack *tr = g_hash_table_lookup ( vtl->tracks, vtl->current_tp_id );
6651     if ( tr == NULL )
6652       tr = g_hash_table_lookup ( vtl->routes, vtl->current_tp_id );
6653     if ( tr == NULL )
6654       return;
6655
6656     GList *new_tpl;
6657
6658     // Find available adjacent trackpoint
6659     if ( (new_tpl = vtl->current_tpl->next) || (new_tpl = vtl->current_tpl->prev) )
6660     {
6661       if ( VIK_TRACKPOINT(vtl->current_tpl->data)->newsegment && vtl->current_tpl->next )
6662         VIK_TRACKPOINT(vtl->current_tpl->next->data)->newsegment = TRUE; /* don't concat segments on del */
6663
6664       // Delete current trackpoint
6665       vik_trackpoint_free ( vtl->current_tpl->data );
6666       tr->trackpoints = g_list_delete_link ( tr->trackpoints, vtl->current_tpl );
6667
6668       // Set to current to the available adjacent trackpoint
6669       vtl->current_tpl = new_tpl;
6670
6671       // Reset dialog with the available adjacent trackpoint
6672       if ( vtl->current_tp_track )
6673         vik_trw_layer_tpwin_set_tp ( vtl->tpwin, new_tpl, vtl->current_tp_track->name );
6674
6675       vik_layer_emit_update(VIK_LAYER(vtl), FALSE);
6676     }
6677     else
6678     {
6679       // Delete current trackpoint
6680       vik_trackpoint_free ( vtl->current_tpl->data );
6681       tr->trackpoints = g_list_delete_link ( tr->trackpoints, vtl->current_tpl );
6682       trw_layer_cancel_current_tp ( vtl, FALSE );
6683     }
6684   }
6685   else if ( response == VIK_TRW_LAYER_TPWIN_FORWARD && vtl->current_tpl->next )
6686   {
6687     if ( vtl->current_tp_track )
6688       vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl = vtl->current_tpl->next, vtl->current_tp_track->name );
6689     vik_layer_emit_update(VIK_LAYER(vtl), FALSE); /* TODO longone: either move or only update if tp is inside drawing window */
6690   }
6691   else if ( response == VIK_TRW_LAYER_TPWIN_BACK && vtl->current_tpl->prev )
6692   {
6693     if ( vtl->current_tp_track )
6694       vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl = vtl->current_tpl->prev, vtl->current_tp_track->name );
6695     vik_layer_emit_update(VIK_LAYER(vtl), FALSE);
6696   }
6697   else if ( response == VIK_TRW_LAYER_TPWIN_INSERT && vtl->current_tpl->next )
6698   {
6699     trw_layer_insert_tp_after_current_tp ( vtl );
6700     vik_layer_emit_update(VIK_LAYER(vtl), FALSE);
6701   }
6702   else if ( response == VIK_TRW_LAYER_TPWIN_DATA_CHANGED )
6703     vik_layer_emit_update(VIK_LAYER(vtl), FALSE);
6704 }
6705
6706 static void trw_layer_tpwin_init ( VikTrwLayer *vtl )
6707 {
6708   if ( ! vtl->tpwin )
6709   {
6710     vtl->tpwin = vik_trw_layer_tpwin_new ( VIK_GTK_WINDOW_FROM_LAYER(vtl) );
6711     g_signal_connect_swapped ( GTK_DIALOG(vtl->tpwin), "response", G_CALLBACK(trw_layer_tpwin_response), vtl );
6712     /* connect signals -- DELETE SIGNAL VERY IMPORTANT TO SET TO NULL */
6713     g_signal_connect_swapped ( vtl->tpwin, "delete-event", G_CALLBACK(trw_layer_cancel_current_tp), vtl );
6714     gtk_widget_show_all ( GTK_WIDGET(vtl->tpwin) );
6715   }
6716   if ( vtl->current_tpl )
6717     if ( vtl->current_tp_track )
6718       vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, vtl->current_tp_track->name );
6719   /* set layer name and TP data */
6720 }
6721
6722 /***************************************************************************
6723  ** Tool code
6724  ***************************************************************************/
6725
6726 /*** Utility data structures and functions ****/
6727
6728 typedef struct {
6729   gint x, y;
6730   gint closest_x, closest_y;
6731   gpointer *closest_wp_id;
6732   VikWaypoint *closest_wp;
6733   VikViewport *vvp;
6734 } WPSearchParams;
6735
6736 typedef struct {
6737   gint x, y;
6738   gint closest_x, closest_y;
6739   gpointer closest_track_id;
6740   VikTrackpoint *closest_tp;
6741   VikViewport *vvp;
6742   GList *closest_tpl;
6743 } TPSearchParams;
6744
6745 static void waypoint_search_closest_tp ( gpointer id, VikWaypoint *wp, WPSearchParams *params )
6746 {
6747   gint x, y;
6748   if ( !wp->visible )
6749     return;
6750
6751   vik_viewport_coord_to_screen ( params->vvp, &(wp->coord), &x, &y );
6752
6753   // If waypoint has an image then use the image size to select
6754   if ( wp->image ) {
6755     gint slackx, slacky;
6756     slackx = wp->image_width / 2;
6757     slacky = wp->image_height / 2;
6758
6759     if (    x <= params->x + slackx && x >= params->x - slackx
6760          && y <= params->y + slacky && y >= params->y - slacky ) {
6761       params->closest_wp_id = id;
6762       params->closest_wp = wp;
6763       params->closest_x = x;
6764       params->closest_y = y;
6765     }
6766   }
6767   else if ( abs (x - params->x) <= WAYPOINT_SIZE_APPROX && abs (y - params->y) <= WAYPOINT_SIZE_APPROX &&
6768             ((!params->closest_wp) ||        /* was the old waypoint we already found closer than this one? */
6769              abs(x - params->x)+abs(y - params->y) < abs(x - params->closest_x)+abs(y - params->closest_y)))
6770     {
6771       params->closest_wp_id = id;
6772       params->closest_wp = wp;
6773       params->closest_x = x;
6774       params->closest_y = y;
6775     }
6776 }
6777
6778 static void track_search_closest_tp ( gpointer id, VikTrack *t, TPSearchParams *params )
6779 {
6780   GList *tpl = t->trackpoints;
6781   VikTrackpoint *tp;
6782
6783   if ( !t->visible )
6784     return;
6785
6786   while (tpl)
6787   {
6788     gint x, y;
6789     tp = VIK_TRACKPOINT(tpl->data);
6790
6791     vik_viewport_coord_to_screen ( params->vvp, &(tp->coord), &x, &y );
6792  
6793     if ( abs (x - params->x) <= TRACKPOINT_SIZE_APPROX && abs (y - params->y) <= TRACKPOINT_SIZE_APPROX &&
6794         ((!params->closest_tp) ||        /* was the old trackpoint we already found closer than this one? */
6795           abs(x - params->x)+abs(y - params->y) < abs(x - params->closest_x)+abs(y - params->closest_y)))
6796     {
6797       params->closest_track_id = id;
6798       params->closest_tp = tp;
6799       params->closest_tpl = tpl;
6800       params->closest_x = x;
6801       params->closest_y = y;
6802     }
6803     tpl = tpl->next;
6804   }
6805 }
6806
6807 // ATM: Leave this as 'Track' only.
6808 //  Not overly bothered about having a snap to route trackpoint capability
6809 static VikTrackpoint *closest_tp_in_five_pixel_interval ( VikTrwLayer *vtl, VikViewport *vvp, gint x, gint y )
6810 {
6811   TPSearchParams params;
6812   params.x = x;
6813   params.y = y;
6814   params.vvp = vvp;
6815   params.closest_track_id = NULL;
6816   params.closest_tp = NULL;
6817   g_hash_table_foreach ( vtl->tracks, (GHFunc) track_search_closest_tp, &params);
6818   return params.closest_tp;
6819 }
6820
6821 static VikWaypoint *closest_wp_in_five_pixel_interval ( VikTrwLayer *vtl, VikViewport *vvp, gint x, gint y )
6822 {
6823   WPSearchParams params;
6824   params.x = x;
6825   params.y = y;
6826   params.vvp = vvp;
6827   params.closest_wp = NULL;
6828   params.closest_wp_id = NULL;
6829   g_hash_table_foreach ( vtl->waypoints, (GHFunc) waypoint_search_closest_tp, &params);
6830   return params.closest_wp;
6831 }
6832
6833
6834 // Some forward declarations
6835 static void marker_begin_move ( tool_ed_t *t, gint x, gint y );
6836 static void marker_moveto ( tool_ed_t *t, gint x, gint y );
6837 static void marker_end_move ( tool_ed_t *t );
6838 //
6839
6840 static gboolean trw_layer_select_move ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp, tool_ed_t* t )
6841 {
6842   if ( t->holding ) {
6843     VikCoord new_coord;
6844     vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
6845
6846     // Here always allow snapping back to the original location
6847     //  this is useful when one decides not to move the thing afterall
6848     // If one wants to move the item only a little bit then don't hold down the 'snap' key!
6849  
6850     // snap to TP
6851     if ( event->state & GDK_CONTROL_MASK )
6852     {
6853       VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
6854       if ( tp )
6855         new_coord = tp->coord;
6856     }
6857
6858     // snap to WP
6859     if ( event->state & GDK_SHIFT_MASK )
6860     {
6861       VikWaypoint *wp = closest_wp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
6862       if ( wp )
6863         new_coord = wp->coord;
6864     }
6865     
6866     gint x, y;
6867     vik_viewport_coord_to_screen ( vvp, &new_coord, &x, &y );
6868
6869     marker_moveto ( t, x, y );
6870
6871     return TRUE;
6872   }
6873   return FALSE;
6874 }
6875
6876 static gboolean trw_layer_select_release ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp, tool_ed_t* t )
6877 {
6878   if ( t->holding && event->button == 1 )
6879   {
6880     VikCoord new_coord;
6881     vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
6882
6883     // snap to TP
6884     if ( event->state & GDK_CONTROL_MASK )
6885     {
6886       VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
6887       if ( tp )
6888         new_coord = tp->coord;
6889     }
6890
6891     // snap to WP
6892     if ( event->state & GDK_SHIFT_MASK )
6893     {
6894       VikWaypoint *wp = closest_wp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
6895       if ( wp )
6896         new_coord = wp->coord;
6897     }
6898
6899     marker_end_move ( t );
6900
6901     // Determine if working on a waypoint or a trackpoint
6902     if ( t->is_waypoint )
6903       vtl->current_wp->coord = new_coord;
6904     else {
6905       if ( vtl->current_tpl ) {
6906         VIK_TRACKPOINT(vtl->current_tpl->data)->coord = new_coord;
6907       
6908         if ( vtl->tpwin )
6909           if ( vtl->current_tp_track )
6910             vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, vtl->current_tp_track->name );
6911       }
6912     }
6913
6914     // Reset
6915     vtl->current_wp    = NULL;
6916     vtl->current_wp_id = NULL;
6917     trw_layer_cancel_current_tp ( vtl, FALSE );
6918
6919     vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
6920     return TRUE;
6921   }
6922   return FALSE;
6923 }
6924
6925 /*
6926   Returns true if a waypoint or track is found near the requested event position for this particular layer
6927   The item found is automatically selected
6928   This is a tool like feature but routed via the layer interface, since it's instigated by a 'global' layer tool in vikwindow.c
6929  */
6930 static gboolean trw_layer_select_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp, tool_ed_t* tet )
6931 {
6932   if ( event->button != 1 )
6933     return FALSE;
6934
6935   if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
6936     return FALSE;
6937
6938   if ( !vtl->tracks_visible && !vtl->waypoints_visible && !vtl->routes_visible )
6939     return FALSE;
6940
6941   // Go for waypoints first as these often will be near a track, but it's likely the wp is wanted rather then the track
6942
6943   if (vtl->waypoints_visible) {
6944     WPSearchParams wp_params;
6945     wp_params.vvp = vvp;
6946     wp_params.x = event->x;
6947     wp_params.y = event->y;
6948     wp_params.closest_wp_id = NULL;
6949     wp_params.closest_wp = NULL;
6950
6951     g_hash_table_foreach ( vtl->waypoints, (GHFunc) waypoint_search_closest_tp, &wp_params);
6952
6953     if ( wp_params.closest_wp )  {
6954
6955       // Select
6956       vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, g_hash_table_lookup ( vtl->waypoints_iters, wp_params.closest_wp_id ), TRUE );
6957
6958       // Too easy to move it so must be holding shift to start immediately moving it
6959       //   or otherwise be previously selected but not have an image (otherwise clicking within image bounds (again) moves it)
6960       if ( event->state & GDK_SHIFT_MASK ||
6961            ( vtl->current_wp == wp_params.closest_wp && !vtl->current_wp->image ) ) {
6962         // Put into 'move buffer'
6963         // NB vvp & vw already set in tet
6964         tet->vtl = (gpointer)vtl;
6965         tet->is_waypoint = TRUE;
6966       
6967         marker_begin_move (tet, event->x, event->y);
6968       }
6969
6970       vtl->current_wp =    wp_params.closest_wp;
6971       vtl->current_wp_id = wp_params.closest_wp_id;
6972
6973       vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
6974
6975       return TRUE;
6976     }
6977   }
6978
6979   // Used for both track and route lists
6980   TPSearchParams tp_params;
6981   tp_params.vvp = vvp;
6982   tp_params.x = event->x;
6983   tp_params.y = event->y;
6984   tp_params.closest_track_id = NULL;
6985   tp_params.closest_tp = NULL;
6986
6987   if (vtl->tracks_visible) {
6988     g_hash_table_foreach ( vtl->tracks, (GHFunc) track_search_closest_tp, &tp_params);
6989
6990     if ( tp_params.closest_tp )  {
6991
6992       // Always select + highlight the track
6993       vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, g_hash_table_lookup ( vtl->tracks_iters, tp_params.closest_track_id ), TRUE );
6994
6995       tet->is_waypoint = FALSE;
6996
6997       // Select the Trackpoint
6998       // Can move it immediately when control held or it's the previously selected tp
6999       if ( event->state & GDK_CONTROL_MASK ||
7000            vtl->current_tpl == tp_params.closest_tpl ) {
7001         // Put into 'move buffer'
7002         // NB vvp & vw already set in tet
7003         tet->vtl = (gpointer)vtl;
7004         marker_begin_move (tet, event->x, event->y);
7005       }
7006
7007       vtl->current_tpl = tp_params.closest_tpl;
7008       vtl->current_tp_id = tp_params.closest_track_id;
7009       vtl->current_tp_track = g_hash_table_lookup ( vtl->tracks, tp_params.closest_track_id );
7010
7011       set_statusbar_msg_info_trkpt ( vtl, tp_params.closest_tp );
7012
7013       if ( vtl->tpwin )
7014         vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, vtl->current_tp_track->name );
7015
7016       vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
7017       return TRUE;
7018     }
7019   }
7020
7021   // Try again for routes
7022   if (vtl->routes_visible) {
7023     g_hash_table_foreach ( vtl->routes, (GHFunc) track_search_closest_tp, &tp_params);
7024
7025     if ( tp_params.closest_tp )  {
7026
7027       // Always select + highlight the track
7028       vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, g_hash_table_lookup ( vtl->routes_iters, tp_params.closest_track_id ), TRUE );
7029
7030       tet->is_waypoint = FALSE;
7031
7032       // Select the Trackpoint
7033       // Can move it immediately when control held or it's the previously selected tp
7034       if ( event->state & GDK_CONTROL_MASK ||
7035            vtl->current_tpl == tp_params.closest_tpl ) {
7036         // Put into 'move buffer'
7037         // NB vvp & vw already set in tet
7038         tet->vtl = (gpointer)vtl;
7039         marker_begin_move (tet, event->x, event->y);
7040       }
7041
7042       vtl->current_tpl = tp_params.closest_tpl;
7043       vtl->current_tp_id = tp_params.closest_track_id;
7044       vtl->current_tp_track = g_hash_table_lookup ( vtl->routes, tp_params.closest_track_id );
7045
7046       set_statusbar_msg_info_trkpt ( vtl, tp_params.closest_tp );
7047
7048       if ( vtl->tpwin )
7049         vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, vtl->current_tp_track->name );
7050
7051       vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
7052       return TRUE;
7053     }
7054   }
7055
7056   /* these aren't the droids you're looking for */
7057   vtl->current_wp    = NULL;
7058   vtl->current_wp_id = NULL;
7059   trw_layer_cancel_current_tp ( vtl, FALSE );
7060
7061   // Blank info
7062   vik_statusbar_set_message ( vik_window_get_statusbar (VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl))), VIK_STATUSBAR_INFO, "" );
7063
7064   return FALSE;
7065 }
7066
7067 static gboolean trw_layer_show_selected_viewport_menu ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
7068 {
7069   if ( event->button != 3 )
7070     return FALSE;
7071
7072   if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
7073     return FALSE;
7074
7075   if ( !vtl->tracks_visible && !vtl->waypoints_visible && !vtl->routes_visible )
7076     return FALSE;
7077
7078   /* Post menu for the currently selected item */
7079
7080   /* See if a track is selected */
7081   VikTrack *track = (VikTrack*)vik_window_get_selected_track ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(vtl) );
7082   if ( track && track->visible ) {
7083
7084     if ( track->name ) {
7085
7086       if ( vtl->track_right_click_menu )
7087         gtk_object_sink ( GTK_OBJECT(vtl->track_right_click_menu) );
7088
7089       vtl->track_right_click_menu = GTK_MENU ( gtk_menu_new () );
7090       
7091       trku_udata udataU;
7092       udataU.trk  = track;
7093       udataU.uuid = NULL;
7094
7095       gpointer *trkf;
7096       if ( track->is_route )
7097         trkf = g_hash_table_find ( vtl->routes, (GHRFunc) trw_layer_track_find_uuid, &udataU );
7098       else
7099         trkf = g_hash_table_find ( vtl->tracks, (GHRFunc) trw_layer_track_find_uuid, &udataU );
7100
7101       if ( trkf && udataU.uuid ) {
7102
7103         GtkTreeIter *iter;
7104         if ( track->is_route )
7105           iter = g_hash_table_lookup ( vtl->routes_iters, udataU.uuid );
7106         else
7107           iter = g_hash_table_lookup ( vtl->tracks_iters, udataU.uuid );
7108
7109         trw_layer_sublayer_add_menu_items ( vtl,
7110                                             vtl->track_right_click_menu,
7111                                             NULL,
7112                                             track->is_route ? VIK_TRW_LAYER_SUBLAYER_ROUTE : VIK_TRW_LAYER_SUBLAYER_TRACK,
7113                                             udataU.uuid,
7114                                             iter,
7115                                             vvp );
7116       }
7117
7118       gtk_menu_popup ( vtl->track_right_click_menu, NULL, NULL, NULL, NULL, event->button, gtk_get_current_event_time() );
7119         
7120       return TRUE;
7121     }
7122   }
7123
7124   /* See if a waypoint is selected */
7125   VikWaypoint *waypoint = (VikWaypoint*)vik_window_get_selected_waypoint ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(vtl) );
7126   if ( waypoint && waypoint->visible ) {
7127     if ( waypoint->name ) {
7128
7129       if ( vtl->wp_right_click_menu )
7130         gtk_object_sink ( GTK_OBJECT(vtl->wp_right_click_menu) );
7131
7132       vtl->wp_right_click_menu = GTK_MENU ( gtk_menu_new () );
7133
7134       wpu_udata udata;
7135       udata.wp   = waypoint;
7136       udata.uuid = NULL;
7137
7138       gpointer *wpf = g_hash_table_find ( vtl->waypoints, (GHRFunc) trw_layer_waypoint_find_uuid, (gpointer) &udata );
7139
7140       if ( wpf && udata.uuid ) {
7141         GtkTreeIter *iter = g_hash_table_lookup ( vtl->waypoints_iters, udata.uuid );
7142
7143         trw_layer_sublayer_add_menu_items ( vtl,
7144                                             vtl->wp_right_click_menu,
7145                                             NULL,
7146                                             VIK_TRW_LAYER_SUBLAYER_WAYPOINT,
7147                                             udata.uuid,
7148                                             iter,
7149                                             vvp );
7150       }
7151       gtk_menu_popup ( vtl->wp_right_click_menu, NULL, NULL, NULL, NULL, event->button, gtk_get_current_event_time() );
7152
7153       return TRUE;
7154     }
7155   }
7156
7157   return FALSE;
7158 }
7159
7160 /* background drawing hook, to be passed the viewport */
7161 static gboolean tool_sync_done = TRUE;
7162
7163 static gboolean tool_sync(gpointer data)
7164 {
7165   VikViewport *vvp = data;
7166   gdk_threads_enter();
7167   vik_viewport_sync(vvp);
7168   tool_sync_done = TRUE;
7169   gdk_threads_leave();
7170   return FALSE;
7171 }
7172
7173 static void marker_begin_move ( tool_ed_t *t, gint x, gint y )
7174 {
7175   t->holding = TRUE;
7176   t->gc = vik_viewport_new_gc (t->vvp, "black", 2);
7177   gdk_gc_set_function ( t->gc, GDK_INVERT );
7178   vik_viewport_draw_rectangle ( t->vvp, t->gc, FALSE, x-3, y-3, 6, 6 );
7179   vik_viewport_sync(t->vvp);
7180   t->oldx = x;
7181   t->oldy = y;
7182 }
7183
7184 static void marker_moveto ( tool_ed_t *t, gint x, gint y )
7185 {
7186   VikViewport *vvp =  t->vvp;
7187   vik_viewport_draw_rectangle ( vvp, t->gc, FALSE, t->oldx-3, t->oldy-3, 6, 6 );
7188   vik_viewport_draw_rectangle ( vvp, t->gc, FALSE, x-3, y-3, 6, 6 );
7189   t->oldx = x;
7190   t->oldy = y;
7191
7192   if (tool_sync_done) {
7193     g_idle_add_full (G_PRIORITY_HIGH_IDLE + 10, tool_sync, vvp, NULL);
7194     tool_sync_done = FALSE;
7195   }
7196 }
7197
7198 static void marker_end_move ( tool_ed_t *t )
7199 {
7200   vik_viewport_draw_rectangle ( t->vvp, t->gc, FALSE, t->oldx-3, t->oldy-3, 6, 6 );
7201   g_object_unref ( t->gc );
7202   t->holding = FALSE;
7203 }
7204
7205 /*** Edit waypoint ****/
7206
7207 static gpointer tool_edit_waypoint_create ( VikWindow *vw, VikViewport *vvp)
7208 {
7209   tool_ed_t *t = g_new(tool_ed_t, 1);
7210   t->vvp = vvp;
7211   t->holding = FALSE;
7212   return t;
7213 }
7214
7215 static gboolean tool_edit_waypoint_click ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data )
7216 {
7217   WPSearchParams params;
7218   tool_ed_t *t = data;
7219   VikViewport *vvp = t->vvp;
7220
7221   if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
7222     return FALSE;
7223
7224   if ( t->holding ) {
7225     return TRUE;
7226   }
7227
7228   if ( !vtl->vl.visible || !vtl->waypoints_visible )
7229     return FALSE;
7230
7231   if ( vtl->current_wp && vtl->current_wp->visible )
7232   {
7233     /* first check if current WP is within area (other may be 'closer', but we want to move the current) */
7234     gint x, y;
7235     vik_viewport_coord_to_screen ( vvp, &(vtl->current_wp->coord), &x, &y );
7236
7237     if ( abs(x - event->x) <= WAYPOINT_SIZE_APPROX &&
7238          abs(y - event->y) <= WAYPOINT_SIZE_APPROX )
7239     {
7240       if ( event->button == 3 )
7241         vtl->waypoint_rightclick = TRUE; /* remember that we're clicking; other layers will ignore release signal */
7242       else {
7243         marker_begin_move(t, event->x, event->y);
7244       }
7245       return TRUE;
7246     }
7247   }
7248
7249   params.vvp = vvp;
7250   params.x = event->x;
7251   params.y = event->y;
7252   params.closest_wp_id = NULL;
7253   /* TODO: should get track listitem so we can break it up, make a new track, mess it up, all that. */
7254   params.closest_wp = NULL;
7255   g_hash_table_foreach ( vtl->waypoints, (GHFunc) waypoint_search_closest_tp, &params);
7256   if ( vtl->current_wp == params.closest_wp && vtl->current_wp != NULL )
7257   {
7258     // how do we get here?
7259     marker_begin_move(t, event->x, event->y);
7260     g_critical("shouldn't be here");
7261     return FALSE;
7262   }
7263   else if ( params.closest_wp )
7264   {
7265     if ( event->button == 3 )
7266       vtl->waypoint_rightclick = TRUE; /* remember that we're clicking; other layers will ignore release signal */
7267     else
7268       vtl->waypoint_rightclick = FALSE;
7269
7270     vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, g_hash_table_lookup ( vtl->waypoints_iters, params.closest_wp_id ), TRUE );
7271
7272     vtl->current_wp = params.closest_wp;
7273     vtl->current_wp_id = params.closest_wp_id;
7274
7275     /* could make it so don't update if old WP is off screen and new is null but oh well */
7276     vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
7277     return TRUE;
7278   }
7279
7280   vtl->current_wp = NULL;
7281   vtl->current_wp_id = NULL;
7282   vtl->waypoint_rightclick = FALSE;
7283   vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
7284   return FALSE;
7285 }
7286
7287 static gboolean tool_edit_waypoint_move ( VikTrwLayer *vtl, GdkEventMotion *event, gpointer data )
7288 {
7289   tool_ed_t *t = data;
7290   VikViewport *vvp = t->vvp;
7291
7292   if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
7293     return FALSE;
7294
7295   if ( t->holding ) {
7296     VikCoord new_coord;
7297     vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
7298
7299     /* snap to TP */
7300     if ( event->state & GDK_CONTROL_MASK )
7301     {
7302       VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
7303       if ( tp )
7304         new_coord = tp->coord;
7305     }
7306
7307     /* snap to WP */
7308     if ( event->state & GDK_SHIFT_MASK )
7309     {
7310       VikWaypoint *wp = closest_wp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
7311       if ( wp && wp != vtl->current_wp )
7312         new_coord = wp->coord;
7313     }
7314     
7315     { 
7316       gint x, y;
7317       vik_viewport_coord_to_screen ( vvp, &new_coord, &x, &y );
7318
7319       marker_moveto ( t, x, y );
7320     } 
7321     return TRUE;
7322   }
7323   return FALSE;
7324 }
7325
7326 static gboolean tool_edit_waypoint_release ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data )
7327 {
7328   tool_ed_t *t = data;
7329   VikViewport *vvp = t->vvp;
7330
7331   if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
7332     return FALSE;
7333   
7334   if ( t->holding && event->button == 1 )
7335   {
7336     VikCoord new_coord;
7337     vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
7338
7339     /* snap to TP */
7340     if ( event->state & GDK_CONTROL_MASK )
7341     {
7342       VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
7343       if ( tp )
7344         new_coord = tp->coord;
7345     }
7346
7347     /* snap to WP */
7348     if ( event->state & GDK_SHIFT_MASK )
7349     {
7350       VikWaypoint *wp = closest_wp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
7351       if ( wp && wp != vtl->current_wp )
7352         new_coord = wp->coord;
7353     }
7354
7355     marker_end_move ( t );
7356
7357     vtl->current_wp->coord = new_coord;
7358     vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
7359     return TRUE;
7360   }
7361   /* PUT IN RIGHT PLACE!!! */
7362   if ( event->button == 3 && vtl->waypoint_rightclick )
7363   {
7364     if ( vtl->wp_right_click_menu )
7365       g_object_ref_sink ( G_OBJECT(vtl->wp_right_click_menu) );
7366     if ( vtl->current_wp ) {
7367       vtl->wp_right_click_menu = GTK_MENU ( gtk_menu_new () );
7368       trw_layer_sublayer_add_menu_items ( vtl, vtl->wp_right_click_menu, NULL, VIK_TRW_LAYER_SUBLAYER_WAYPOINT, vtl->current_wp_id, g_hash_table_lookup ( vtl->waypoints_iters, vtl->current_wp_id ), vvp );
7369       gtk_menu_popup ( vtl->wp_right_click_menu, NULL, NULL, NULL, NULL, event->button, gtk_get_current_event_time() );
7370     }
7371     vtl->waypoint_rightclick = FALSE;
7372   }
7373   return FALSE;
7374 }
7375
7376 /*** New track ****/
7377
7378 static gpointer tool_new_track_create ( VikWindow *vw, VikViewport *vvp)
7379 {
7380   return vvp;
7381 }
7382
7383 typedef struct {
7384   VikTrwLayer *vtl;
7385   GdkDrawable *drawable;
7386   GdkGC *gc;
7387   GdkPixmap *pixmap;
7388 } draw_sync_t;
7389
7390 /*
7391  * Draw specified pixmap
7392  */
7393 static gboolean draw_sync ( gpointer data )
7394 {
7395   draw_sync_t *ds = (draw_sync_t*) data;
7396   // Sometimes don't want to draw
7397   //  normally because another update has taken precedent such as panning the display
7398   //   which means this pixmap is no longer valid
7399   if ( ds->vtl->draw_sync_do ) {
7400     gdk_threads_enter();
7401     gdk_draw_drawable (ds->drawable,
7402                        ds->gc,
7403                        ds->pixmap,
7404                        0, 0, 0, 0, -1, -1);
7405     ds->vtl->draw_sync_done = TRUE;
7406     gdk_threads_leave();
7407   }
7408   return FALSE;
7409 }
7410
7411 static gchar* distance_string (gdouble distance)
7412 {
7413   gchar str[128];
7414
7415   /* draw label with distance */
7416   vik_units_distance_t dist_units = a_vik_get_units_distance ();
7417   switch (dist_units) {
7418   case VIK_UNITS_DISTANCE_MILES:
7419     if (distance >= VIK_MILES_TO_METERS(1) && distance < VIK_MILES_TO_METERS(100)) {
7420       g_sprintf(str, "%3.2f miles", VIK_METERS_TO_MILES(distance));
7421     } else if (distance < 1609.4) {
7422       g_sprintf(str, "%d yards", (int)(distance*1.0936133));
7423     } else {
7424       g_sprintf(str, "%d miles", (int)VIK_METERS_TO_MILES(distance));
7425     }
7426     break;
7427   default:
7428     // VIK_UNITS_DISTANCE_KILOMETRES
7429     if (distance >= 1000 && distance < 100000) {
7430       g_sprintf(str, "%3.2f km", distance/1000.0);
7431     } else if (distance < 1000) {
7432       g_sprintf(str, "%d m", (int)distance);
7433     } else {
7434       g_sprintf(str, "%d km", (int)distance/1000);
7435     }
7436     break;
7437   }
7438   return g_strdup (str);
7439 }
7440
7441 /*
7442  * Actually set the message in statusbar
7443  */
7444 static void statusbar_write (const gchar *distance_string, gdouble elev_gain, gdouble elev_loss, VikTrwLayer *vtl )
7445 {
7446   // Only show elevation data when track has some elevation properties
7447   gchar str_gain_loss[64];
7448   str_gain_loss[0] = '\0';
7449   
7450   if ( (elev_gain > 0.1) || (elev_loss > 0.1) ) {
7451     if ( a_vik_get_units_height () == VIK_UNITS_HEIGHT_METRES )
7452       g_sprintf(str_gain_loss, _(" - Gain %dm:Loss %dm"), (int)elev_gain, (int)elev_loss);
7453     else
7454       g_sprintf(str_gain_loss, _(" - Gain %dft:Loss %dft"), (int)VIK_METERS_TO_FEET(elev_gain), (int)VIK_METERS_TO_FEET(elev_loss));
7455   }
7456
7457   // Write with full gain/loss information
7458   gchar *msg = g_strdup_printf ( "%s%s", distance_string, str_gain_loss);
7459   vik_statusbar_set_message ( vik_window_get_statusbar (VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl))), VIK_STATUSBAR_INFO, msg );
7460   g_free ( msg );
7461 }
7462
7463 /*
7464  * Figure out what information should be set in the statusbar and then write it
7465  */
7466 static void update_statusbar ( VikTrwLayer *vtl )
7467 {
7468   // Get elevation data
7469   gdouble elev_gain, elev_loss;
7470   vik_track_get_total_elevation_gain ( vtl->current_track, &elev_gain, &elev_loss);
7471
7472   /* Find out actual distance of current track */
7473   gdouble distance = vik_track_get_length (vtl->current_track);
7474   gchar *str = distance_string (distance);
7475
7476   statusbar_write (str, elev_gain, elev_loss, vtl);
7477
7478   g_free (str);
7479 }
7480
7481
7482 static VikLayerToolFuncStatus tool_new_track_move ( VikTrwLayer *vtl, GdkEventMotion *event, VikViewport *vvp )
7483 {
7484   /* if we haven't sync'ed yet, we don't have time to do more. */
7485   if ( vtl->draw_sync_done && vtl->current_track && vtl->current_track->trackpoints ) {
7486     GList *iter = g_list_last ( vtl->current_track->trackpoints );
7487     VikTrackpoint *last_tpt = VIK_TRACKPOINT(iter->data);
7488
7489     static GdkPixmap *pixmap = NULL;
7490     int w1, h1, w2, h2;
7491     // Need to check in case window has been resized
7492     w1 = vik_viewport_get_width(vvp);
7493     h1 = vik_viewport_get_height(vvp);
7494     if (!pixmap) {
7495       pixmap = gdk_pixmap_new ( GTK_WIDGET(vvp)->window, w1, h1, -1 );
7496     }
7497     gdk_drawable_get_size (pixmap, &w2, &h2);
7498     if (w1 != w2 || h1 != h2) {
7499       g_object_unref ( G_OBJECT ( pixmap ) );
7500       pixmap = gdk_pixmap_new ( GTK_WIDGET(vvp)->window, w1, h1, -1 );
7501     }
7502
7503     // Reset to background
7504     gdk_draw_drawable (pixmap,
7505                        vtl->current_track_newpoint_gc,
7506                        vik_viewport_get_pixmap(vvp),
7507                        0, 0, 0, 0, -1, -1);
7508
7509     draw_sync_t *passalong;
7510     gint x1, y1;
7511
7512     vik_viewport_coord_to_screen ( vvp, &(last_tpt->coord), &x1, &y1 );
7513
7514     // FOR SCREEN OVERLAYS WE MUST DRAW INTO THIS PIXMAP (when using the reset method)
7515     //  otherwise using vik_viewport_draw_* functions puts the data into the base pixmap,
7516     //  thus when we come to reset to the background it would include what we have already drawn!!
7517     gdk_draw_line ( pixmap,
7518                     vtl->current_track_newpoint_gc,
7519                     x1, y1, event->x, event->y );
7520     // Using this reset method is more reliable than trying to undraw previous efforts via the GDK_INVERT method
7521
7522     /* Find out actual distance of current track */
7523     gdouble distance = vik_track_get_length (vtl->current_track);
7524
7525     // Now add distance to where the pointer is //
7526     VikCoord coord;
7527     struct LatLon ll;
7528     vik_viewport_screen_to_coord ( vvp, (gint) event->x, (gint) event->y, &coord );
7529     vik_coord_to_latlon ( &coord, &ll );
7530     distance = distance + vik_coord_diff( &coord, &(last_tpt->coord));
7531
7532     // Get elevation data
7533     gdouble elev_gain, elev_loss;
7534     vik_track_get_total_elevation_gain ( vtl->current_track, &elev_gain, &elev_loss);
7535
7536     // Adjust elevation data (if available) for the current pointer position
7537     gdouble elev_new;
7538     elev_new = (gdouble) a_dems_get_elev_by_coord ( &coord, VIK_DEM_INTERPOL_BEST );
7539     if ( elev_new != VIK_DEM_INVALID_ELEVATION ) {
7540       if ( last_tpt->altitude != VIK_DEFAULT_ALTITUDE ) {
7541         // Adjust elevation of last track point
7542         if ( elev_new > last_tpt->altitude )
7543           // Going up
7544           elev_gain += elev_new - last_tpt->altitude;
7545         else
7546           // Going down
7547           elev_loss += last_tpt->altitude - elev_new;
7548       }
7549     }
7550       
7551     gchar *str = distance_string (distance);
7552
7553     PangoLayout *pl = gtk_widget_create_pango_layout (GTK_WIDGET(vvp), NULL);
7554     pango_layout_set_font_description (pl, GTK_WIDGET(vvp)->style->font_desc);
7555
7556     pango_layout_set_text (pl, str, -1);
7557     gint wd, hd;
7558     pango_layout_get_pixel_size ( pl, &wd, &hd );
7559
7560     gint xd,yd;
7561     // offset from cursor a bit depending on font size
7562     xd = event->x + 10;
7563     yd = event->y - hd;
7564
7565     // Create a background block to make the text easier to read over the background map
7566     GdkGC *background_block_gc = vik_viewport_new_gc ( vvp, "#cccccc", 1);
7567     gdk_draw_rectangle (pixmap, background_block_gc, TRUE, xd-2, yd-2, wd+4, hd+2);
7568     gdk_draw_layout (pixmap, vtl->current_track_newpoint_gc, xd, yd, pl);
7569
7570     g_object_unref ( G_OBJECT ( pl ) );
7571     g_object_unref ( G_OBJECT ( background_block_gc ) );
7572
7573     passalong = g_new(draw_sync_t,1); // freed by draw_sync()
7574     passalong->vtl = vtl;
7575     passalong->pixmap = pixmap;
7576     passalong->drawable = GTK_WIDGET(vvp)->window;
7577     passalong->gc = vtl->current_track_newpoint_gc;
7578
7579     // Update statusbar with full gain/loss information
7580     statusbar_write (str, elev_gain, elev_loss, vtl);
7581
7582     g_free (str);
7583
7584     // draw pixmap when we have time to
7585     g_idle_add_full (G_PRIORITY_HIGH_IDLE + 10, draw_sync, passalong, NULL);
7586     vtl->draw_sync_done = FALSE;
7587     return VIK_LAYER_TOOL_ACK_GRAB_FOCUS;
7588   }
7589   return VIK_LAYER_TOOL_ACK;
7590 }
7591
7592 static gboolean tool_new_track_key_press ( VikTrwLayer *vtl, GdkEventKey *event, VikViewport *vvp )
7593 {
7594   if ( vtl->current_track && event->keyval == GDK_Escape ) {
7595     vtl->current_track = NULL;
7596     vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
7597     return TRUE;
7598   } else if ( vtl->current_track && event->keyval == GDK_BackSpace ) {
7599     /* undo */
7600     if ( vtl->current_track->trackpoints )
7601     {
7602       GList *last = g_list_last(vtl->current_track->trackpoints);
7603       g_free ( last->data );
7604       vtl->current_track->trackpoints = g_list_remove_link ( vtl->current_track->trackpoints, last );
7605     }
7606
7607     update_statusbar ( vtl );
7608
7609     vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
7610     return TRUE;
7611   }
7612   return FALSE;
7613 }
7614
7615 /*
7616  * Common function to handle trackpoint button requests on either a route or a track
7617  *  . enables adding a point via normal click
7618  *  . enables removal of last point via right click
7619  *  . finishing of the track or route via double clicking
7620  */
7621 static gboolean tool_new_track_or_route_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
7622 {
7623   VikTrackpoint *tp;
7624
7625   if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
7626     return FALSE;
7627
7628   if ( event->button == 2 ) {
7629     // As the display is panning, the new track pixmap is now invalid so don't draw it
7630     //  otherwise this drawing done results in flickering back to an old image
7631     vtl->draw_sync_do = FALSE;
7632     return FALSE;
7633   }
7634
7635   if ( event->button == 3 )
7636   {
7637     if ( !vtl->current_track )
7638       return FALSE;
7639     /* undo */
7640     if ( vtl->current_track->trackpoints )
7641     {
7642       GList *last = g_list_last(vtl->current_track->trackpoints);
7643       g_free ( last->data );
7644       vtl->current_track->trackpoints = g_list_remove_link ( vtl->current_track->trackpoints, last );
7645     }
7646     update_statusbar ( vtl );
7647
7648     vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
7649     return TRUE;
7650   }
7651
7652   if ( event->type == GDK_2BUTTON_PRESS )
7653   {
7654     /* subtract last (duplicate from double click) tp then end */
7655     if ( vtl->current_track && vtl->current_track->trackpoints && vtl->ct_x1 == vtl->ct_x2 && vtl->ct_y1 == vtl->ct_y2 )
7656     {
7657       GList *last = g_list_last(vtl->current_track->trackpoints);
7658       g_free ( last->data );
7659       vtl->current_track->trackpoints = g_list_remove_link ( vtl->current_track->trackpoints, last );
7660       /* undo last, then end */
7661       vtl->current_track = NULL;
7662     }
7663     vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
7664     return TRUE;
7665   }
7666
7667   tp = vik_trackpoint_new();
7668   vik_viewport_screen_to_coord ( vvp, event->x, event->y, &(tp->coord) );
7669
7670   /* snap to other TP */
7671   if ( event->state & GDK_CONTROL_MASK )
7672   {
7673     VikTrackpoint *other_tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
7674     if ( other_tp )
7675       tp->coord = other_tp->coord;
7676   }
7677
7678   tp->newsegment = FALSE;
7679   tp->has_timestamp = FALSE;
7680   tp->timestamp = 0;
7681
7682   if ( vtl->current_track ) {
7683     vtl->current_track->trackpoints = g_list_append ( vtl->current_track->trackpoints, tp );
7684     /* Auto attempt to get elevation from DEM data (if it's available) */
7685     vik_track_apply_dem_data_last_trackpoint ( vtl->current_track );
7686   }
7687
7688   vtl->ct_x1 = vtl->ct_x2;
7689   vtl->ct_y1 = vtl->ct_y2;
7690   vtl->ct_x2 = event->x;
7691   vtl->ct_y2 = event->y;
7692
7693   vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
7694   return TRUE;
7695 }
7696
7697 static gboolean tool_new_track_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
7698 {
7699   // ----------------------------------------------------- if current is a route - switch to new track
7700   if ( event->button == 1 && ( ! vtl->current_track || (vtl->current_track && vtl->current_track->is_route ) ))
7701   {
7702     gchar *name = trw_layer_new_unique_sublayer_name(vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, _("Track"));
7703     if ( ( name = a_dialog_new_track ( VIK_GTK_WINDOW_FROM_LAYER(vtl), vtl->tracks, name, FALSE ) ) )
7704     {
7705       vtl->current_track = vik_track_new();
7706       vtl->current_track->visible = TRUE;
7707       vik_trw_layer_add_track ( vtl, name, vtl->current_track );
7708     }
7709     else
7710       return TRUE;
7711   }
7712   return tool_new_track_or_route_click ( vtl, event, vvp );
7713 }
7714
7715 static void tool_new_track_release ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
7716 {
7717   if ( event->button == 2 ) {
7718     // Pan moving ended - enable potential point drawing again
7719     vtl->draw_sync_do = TRUE;
7720     vtl->draw_sync_done = TRUE;
7721   }
7722 }
7723
7724 /*** New route ****/
7725
7726 static gpointer tool_new_route_create ( VikWindow *vw, VikViewport *vvp)
7727 {
7728   return vvp;
7729 }
7730
7731 static gboolean tool_new_route_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
7732 {
7733   // -------------------------- if current is a track - switch to new route
7734   if ( event->button == 1 && ( ! vtl->current_track || (vtl->current_track && !vtl->current_track->is_route ) ) )
7735   {
7736     gchar *name = trw_layer_new_unique_sublayer_name(vtl, VIK_TRW_LAYER_SUBLAYER_ROUTE, _("Route"));
7737     if ( ( name = a_dialog_new_track ( VIK_GTK_WINDOW_FROM_LAYER(vtl), vtl->routes, name, TRUE ) ) )
7738       new_route_create_common ( vtl, name );
7739     else
7740       return TRUE;
7741   }
7742   return tool_new_track_or_route_click ( vtl, event, vvp );
7743 }
7744
7745 /*** New waypoint ****/
7746
7747 static gpointer tool_new_waypoint_create ( VikWindow *vw, VikViewport *vvp)
7748 {
7749   return vvp;
7750 }
7751
7752 static gboolean tool_new_waypoint_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
7753 {
7754   VikCoord coord;
7755   if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
7756     return FALSE;
7757   vik_viewport_screen_to_coord ( vvp, event->x, event->y, &coord );
7758   if (vik_trw_layer_new_waypoint ( vtl, VIK_GTK_WINDOW_FROM_LAYER(vtl), &coord ) && VIK_LAYER(vtl)->visible)
7759     vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
7760   return TRUE;
7761 }
7762
7763
7764 /*** Edit trackpoint ****/
7765
7766 static gpointer tool_edit_trackpoint_create ( VikWindow *vw, VikViewport *vvp)
7767 {
7768   tool_ed_t *t = g_new(tool_ed_t, 1);
7769   t->vvp = vvp;
7770   t->holding = FALSE;
7771   return t;
7772 }
7773
7774 static gboolean tool_edit_trackpoint_click ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data )
7775 {
7776   tool_ed_t *t = data;
7777   VikViewport *vvp = t->vvp;
7778   TPSearchParams params;
7779   /* OUTDATED DOCUMENTATION:
7780    find 5 pixel range on each side. then put these UTM, and a pointer
7781    to the winning track name (and maybe the winning track itself), and a
7782    pointer to the winning trackpoint, inside an array or struct. pass 
7783    this along, do a foreach on the tracks which will do a foreach on the 
7784    trackpoints. */
7785   params.vvp = vvp;
7786   params.x = event->x;
7787   params.y = event->y;
7788   params.closest_track_id = NULL;
7789   /* TODO: should get track listitem so we can break it up, make a new track, mess it up, all that. */
7790   params.closest_tp = NULL;
7791
7792   if ( event->button != 1 ) 
7793     return FALSE;
7794
7795   if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
7796     return FALSE;
7797
7798   if ( !vtl->vl.visible || !vtl->tracks_visible || !vtl->routes_visible )
7799     return FALSE;
7800
7801   if ( vtl->current_tpl )
7802   {
7803     /* 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.) */
7804     VikTrackpoint *tp = VIK_TRACKPOINT(vtl->current_tpl->data);
7805     VikTrack *current_tr = VIK_TRACK(g_hash_table_lookup(vtl->tracks, vtl->current_tp_id));
7806     if ( !current_tr )
7807       return FALSE;
7808
7809     gint x, y;
7810     vik_viewport_coord_to_screen ( vvp, &(tp->coord), &x, &y );
7811
7812     if ( current_tr->visible && 
7813          abs(x - event->x) < TRACKPOINT_SIZE_APPROX &&
7814          abs(y - event->y) < TRACKPOINT_SIZE_APPROX ) {
7815       marker_begin_move ( t, event->x, event->y );
7816       return TRUE;
7817     }
7818
7819   }
7820
7821   if ( vtl->tracks_visible )
7822     g_hash_table_foreach ( vtl->tracks, (GHFunc) track_search_closest_tp, &params);
7823
7824   if ( params.closest_tp )
7825   {
7826     vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, g_hash_table_lookup ( vtl->tracks_iters, params.closest_track_id ), TRUE );
7827     vtl->current_tpl = params.closest_tpl;
7828     vtl->current_tp_id = params.closest_track_id;
7829     vtl->current_tp_track = g_hash_table_lookup ( vtl->tracks, params.closest_track_id );
7830     trw_layer_tpwin_init ( vtl );
7831     set_statusbar_msg_info_trkpt ( vtl, params.closest_tp );
7832     vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
7833     return TRUE;
7834   }
7835
7836   if ( vtl->routes_visible )
7837     g_hash_table_foreach ( vtl->routes, (GHFunc) track_search_closest_tp, &params);
7838
7839   if ( params.closest_tp )
7840   {
7841     vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, g_hash_table_lookup ( vtl->routes_iters, params.closest_track_id ), TRUE );
7842     vtl->current_tpl = params.closest_tpl;
7843     vtl->current_tp_id = params.closest_track_id;
7844     vtl->current_tp_track = g_hash_table_lookup ( vtl->routes, params.closest_track_id );
7845     trw_layer_tpwin_init ( vtl );
7846     set_statusbar_msg_info_trkpt ( vtl, params.closest_tp );
7847     vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
7848     return TRUE;
7849   }
7850
7851   /* these aren't the droids you're looking for */
7852   return FALSE;
7853 }
7854
7855 static gboolean tool_edit_trackpoint_move ( VikTrwLayer *vtl, GdkEventMotion *event, gpointer data )
7856 {
7857   tool_ed_t *t = data;
7858   VikViewport *vvp = t->vvp;
7859
7860   if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
7861     return FALSE;
7862
7863   if ( t->holding )
7864   {
7865     VikCoord new_coord;
7866     vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
7867
7868     /* snap to TP */
7869     if ( event->state & GDK_CONTROL_MASK )
7870     {
7871       VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
7872       if ( tp && tp != vtl->current_tpl->data )
7873         new_coord = tp->coord;
7874     }
7875     //    VIK_TRACKPOINT(vtl->current_tpl->data)->coord = new_coord;
7876     { 
7877       gint x, y;
7878       vik_viewport_coord_to_screen ( vvp, &new_coord, &x, &y );
7879       marker_moveto ( t, x, y );
7880     } 
7881
7882     return TRUE;
7883   }
7884   return FALSE;
7885 }
7886
7887 static gboolean tool_edit_trackpoint_release ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data )
7888 {
7889   tool_ed_t *t = data;
7890   VikViewport *vvp = t->vvp;
7891
7892   if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
7893     return FALSE;
7894   if ( event->button != 1) 
7895     return FALSE;
7896
7897   if ( t->holding ) {
7898     VikCoord new_coord;
7899     vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
7900
7901     /* snap to TP */
7902     if ( event->state & GDK_CONTROL_MASK )
7903     {
7904       VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
7905       if ( tp && tp != vtl->current_tpl->data )
7906         new_coord = tp->coord;
7907     }
7908
7909     VIK_TRACKPOINT(vtl->current_tpl->data)->coord = new_coord;
7910
7911     marker_end_move ( t );
7912
7913     /* diff dist is diff from orig */
7914     if ( vtl->tpwin )
7915       vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, vtl->current_tp_track->name );
7916
7917     vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
7918     return TRUE;
7919   }
7920   return FALSE;
7921 }
7922
7923
7924 #ifdef VIK_CONFIG_GOOGLE
7925 /*** Route Finder ***/
7926 static gpointer tool_route_finder_create ( VikWindow *vw, VikViewport *vvp)
7927 {
7928   return vvp;
7929 }
7930
7931 static gboolean tool_route_finder_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
7932 {
7933   VikCoord tmp;
7934   if ( !vtl ) return FALSE;
7935   vik_viewport_screen_to_coord ( vvp, event->x, event->y, &tmp );
7936   if ( event->button == 3 && vtl->route_finder_current_track ) {
7937     VikCoord *new_end;
7938     new_end = vik_track_cut_back_to_double_point ( vtl->route_finder_current_track );
7939     if ( new_end ) {
7940       vtl->route_finder_coord = *new_end;
7941       g_free ( new_end );
7942       vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
7943       /* remove last ' to:...' */
7944       if ( vtl->route_finder_current_track->comment ) {
7945         gchar *last_to = strrchr ( vtl->route_finder_current_track->comment, 't' );
7946         if ( last_to && (last_to - vtl->route_finder_current_track->comment > 1) ) {
7947           gchar *new_comment = g_strndup ( vtl->route_finder_current_track->comment,
7948                                            last_to - vtl->route_finder_current_track->comment - 1);
7949           vik_track_set_comment_no_copy ( vtl->route_finder_current_track, new_comment );
7950         }
7951       }
7952     }
7953   }
7954   else if ( vtl->route_finder_started || (event->state & GDK_CONTROL_MASK && vtl->route_finder_current_track) ) {
7955     struct LatLon start, end;
7956     gchar startlat[G_ASCII_DTOSTR_BUF_SIZE], startlon[G_ASCII_DTOSTR_BUF_SIZE];
7957     gchar endlat[G_ASCII_DTOSTR_BUF_SIZE], endlon[G_ASCII_DTOSTR_BUF_SIZE];
7958     gchar *url;
7959
7960     vik_coord_to_latlon ( &(vtl->route_finder_coord), &start );
7961     vik_coord_to_latlon ( &(tmp), &end );
7962     vtl->route_finder_coord = tmp; /* for continuations */
7963
7964     /* these are checked when adding a track from a file (vik_trw_layer_filein_add_track) */
7965     if ( event->state & GDK_CONTROL_MASK && vtl->route_finder_current_track ) {
7966       vtl->route_finder_append = TRUE;  // merge tracks. keep started true.
7967     } else {
7968       vtl->route_finder_check_added_track = TRUE;
7969       vtl->route_finder_started = FALSE;
7970     }
7971
7972     url = g_strdup_printf(GOOGLE_DIRECTIONS_STRING,
7973                           g_ascii_dtostr (startlat, G_ASCII_DTOSTR_BUF_SIZE, (gdouble) start.lat),
7974                           g_ascii_dtostr (startlon, G_ASCII_DTOSTR_BUF_SIZE, (gdouble) start.lon),
7975                           g_ascii_dtostr (endlat, G_ASCII_DTOSTR_BUF_SIZE, (gdouble) end.lat),
7976                           g_ascii_dtostr (endlon, G_ASCII_DTOSTR_BUF_SIZE, (gdouble) end.lon));
7977     a_babel_convert_from_url ( vtl, url, "google", NULL, NULL );
7978     g_free ( url );
7979
7980     /* see if anything was done -- a track was added or appended to */
7981     if ( vtl->route_finder_check_added_track && vtl->route_finder_added_track ) {
7982       vik_track_set_comment_no_copy ( vtl->route_finder_added_track, g_strdup_printf("from: %f,%f to: %f,%f", start.lat, start.lon, end.lat, end.lon ) );
7983     } else if ( vtl->route_finder_append == FALSE && vtl->route_finder_current_track ) {
7984       /* route_finder_append was originally TRUE but set to FALSE by filein_add_track */
7985       gchar *new_comment = g_strdup_printf("%s to: %f,%f", vtl->route_finder_current_track->comment, end.lat, end.lon );
7986       vik_track_set_comment_no_copy ( vtl->route_finder_current_track, new_comment );
7987     }
7988     vtl->route_finder_added_track = NULL;
7989     vtl->route_finder_check_added_track = FALSE;
7990     vtl->route_finder_append = FALSE;
7991
7992     vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
7993   } else {
7994     vtl->route_finder_started = TRUE;
7995     vtl->route_finder_coord = tmp;
7996     vtl->route_finder_current_track = NULL;
7997   }
7998   return TRUE;
7999 }
8000 #endif
8001
8002 /*** Show picture ****/
8003
8004 static gpointer tool_show_picture_create ( VikWindow *vw, VikViewport *vvp)
8005 {
8006   return vvp;
8007 }
8008
8009 /* Params are: vvp, event, last match found or NULL */
8010 static void tool_show_picture_wp ( char *name, VikWaypoint *wp, gpointer params[3] )
8011 {
8012   if ( wp->image && wp->visible )
8013   {
8014     gint x, y, slackx, slacky;
8015     GdkEventButton *event = (GdkEventButton *) params[1];
8016
8017     vik_viewport_coord_to_screen ( VIK_VIEWPORT(params[0]), &(wp->coord), &x, &y );
8018     slackx = wp->image_width / 2;
8019     slacky = wp->image_height / 2;
8020     if (    x <= event->x + slackx && x >= event->x - slackx
8021          && y <= event->y + slacky && y >= event->y - slacky )
8022     {
8023       params[2] = wp->image; /* we've found a match. however continue searching
8024                               * since we want to find the last match -- that
8025                               * is, the match that was drawn last. */
8026     }
8027   }
8028 }
8029
8030 static void trw_layer_show_picture ( gpointer pass_along[6] )
8031 {
8032   /* thanks to the Gaim people for showing me ShellExecute and g_spawn_command_line_async */
8033 #ifdef WINDOWS
8034   ShellExecute(NULL, "open", (char *) pass_along[5], NULL, NULL, SW_SHOWNORMAL);
8035 #else /* WINDOWS */
8036   GError *err = NULL;
8037   gchar *quoted_file = g_shell_quote ( (gchar *) pass_along[5] );
8038   gchar *cmd = g_strdup_printf ( "%s %s", a_vik_get_image_viewer(), quoted_file );
8039   g_free ( quoted_file );
8040   if ( ! g_spawn_command_line_async ( cmd, &err ) )
8041     {
8042       a_dialog_error_msg_extra ( VIK_GTK_WINDOW_FROM_LAYER( pass_along[0]), _("Could not launch %s to open file."), a_vik_get_image_viewer() );
8043       g_error_free ( err );
8044     }
8045   g_free ( cmd );
8046 #endif /* WINDOWS */
8047 }
8048
8049 static gboolean tool_show_picture_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
8050 {
8051   gpointer params[3] = { vvp, event, NULL };
8052   if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
8053     return FALSE;
8054   g_hash_table_foreach ( vtl->waypoints, (GHFunc) tool_show_picture_wp, params );
8055   if ( params[2] )
8056   {
8057     static gpointer pass_along[6];
8058     pass_along[0] = vtl;
8059     pass_along[5] = params[2];
8060     trw_layer_show_picture ( pass_along );
8061     return TRUE; /* found a match */
8062   }
8063   else
8064     return FALSE; /* go through other layers, searching for a match */
8065 }
8066
8067 /***************************************************************************
8068  ** End tool code 
8069  ***************************************************************************/
8070
8071
8072
8073
8074
8075 static void image_wp_make_list ( char *name, VikWaypoint *wp, GSList **pics )
8076 {
8077   if ( wp->image && ( ! a_thumbnails_exists ( wp->image ) ) )
8078     *pics = g_slist_append ( *pics, (gpointer) g_strdup ( wp->image ) );
8079 }
8080
8081 /* Structure for thumbnail creating data used in the background thread */
8082 typedef struct {
8083   VikTrwLayer *vtl; // Layer needed for redrawing
8084   GSList *pics;     // Image list
8085 } thumbnail_create_thread_data;
8086
8087 static int create_thumbnails_thread ( thumbnail_create_thread_data *tctd, gpointer threaddata )
8088 {
8089   guint total = g_slist_length(tctd->pics), done = 0;
8090   while ( tctd->pics )
8091   {
8092     a_thumbnails_create ( (gchar *) tctd->pics->data );
8093     int result = a_background_thread_progress ( threaddata, ((gdouble) ++done) / total );
8094     if ( result != 0 )
8095       return -1; /* Abort thread */
8096
8097     tctd->pics = tctd->pics->next;
8098   }
8099
8100   // Redraw to show the thumbnails as they are now created
8101   if ( IS_VIK_LAYER(tctd->vtl) )
8102     vik_layer_emit_update ( VIK_LAYER(tctd->vtl), TRUE ); // Yes update from background thread
8103
8104   return 0;
8105 }
8106
8107 static void thumbnail_create_thread_free ( thumbnail_create_thread_data *tctd )
8108 {
8109   while ( tctd->pics )
8110   {
8111     g_free ( tctd->pics->data );
8112     tctd->pics = g_slist_delete_link ( tctd->pics, tctd->pics );
8113   }
8114   g_free ( tctd );
8115 }
8116
8117 void trw_layer_verify_thumbnails ( VikTrwLayer *vtl, GtkWidget *vp )
8118 {
8119   if ( ! vtl->has_verified_thumbnails )
8120   {
8121     GSList *pics = NULL;
8122     g_hash_table_foreach ( vtl->waypoints, (GHFunc) image_wp_make_list, &pics );
8123     if ( pics )
8124     {
8125       gint len = g_slist_length ( pics );
8126       gchar *tmp = g_strdup_printf ( _("Creating %d Image Thumbnails..."), len );
8127       thumbnail_create_thread_data *tctd = g_malloc ( sizeof(thumbnail_create_thread_data) );
8128       tctd->vtl = vtl;
8129       tctd->pics = pics;
8130       a_background_thread ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
8131                             tmp,
8132                             (vik_thr_func) create_thumbnails_thread,
8133                             tctd,
8134                             (vik_thr_free_func) thumbnail_create_thread_free,
8135                             NULL,
8136                             len );
8137       g_free ( tmp );
8138     }
8139   }
8140 }
8141
8142 static const gchar* my_track_colors ( gint ii )
8143 {
8144   static const gchar* colors[VIK_TRW_LAYER_TRACK_GCS] = {
8145     "#2d870a",
8146     "#135D34",
8147     "#0a8783",
8148     "#0e4d87",
8149     "#05469f",
8150     "#695CBB",
8151     "#2d059f",
8152     "#4a059f",
8153     "#5A171A",
8154     "#96059f"
8155   };
8156   // Fast and reliable way of returning a colour
8157   return colors[(ii % VIK_TRW_LAYER_TRACK_GCS)];
8158 }
8159
8160 static void trw_layer_track_alloc_colors ( VikTrwLayer *vtl )
8161 {
8162   GHashTableIter iter;
8163   gpointer key, value;
8164
8165   gint ii = 0;
8166   // Tracks
8167   g_hash_table_iter_init ( &iter, vtl->tracks );
8168
8169   while ( g_hash_table_iter_next (&iter, &key, &value) ) {
8170
8171     // Tracks get a random spread of colours if not already assigned
8172     if ( ! VIK_TRACK(value)->has_color ) {
8173       gdk_color_parse ( my_track_colors (ii) , &(VIK_TRACK(value)->color) );
8174       VIK_TRACK(value)->has_color = TRUE;
8175     }
8176
8177     trw_layer_update_treeview ( vtl, VIK_TRACK(value), key );
8178
8179     ii++;
8180     if (ii > VIK_TRW_LAYER_TRACK_GCS)
8181       ii = 0;
8182   }
8183
8184   // Routes
8185   ii = 0;
8186   g_hash_table_iter_init ( &iter, vtl->routes );
8187
8188   while ( g_hash_table_iter_next (&iter, &key, &value) ) {
8189
8190     // Routes get an intermix of reds
8191     if ( ! VIK_TRACK(value)->has_color ) {
8192       if ( ii )
8193         gdk_color_parse ( "#FF0000" , &(VIK_TRACK(value)->color) ); // Red
8194       else
8195         gdk_color_parse ( "#B40916" , &(VIK_TRACK(value)->color) ); // Dark Red
8196       VIK_TRACK(value)->has_color = TRUE;
8197     }
8198
8199     trw_layer_update_treeview ( vtl, VIK_TRACK(value), key );
8200
8201     ii = !ii;
8202   }
8203 }
8204
8205 static void trw_layer_post_read ( VikTrwLayer *vtl, GtkWidget *vp )
8206 {
8207   trw_layer_verify_thumbnails ( vtl, vp );
8208   trw_layer_track_alloc_colors ( vtl );
8209 }
8210
8211 VikCoordMode vik_trw_layer_get_coord_mode ( VikTrwLayer *vtl )
8212 {
8213   return vtl->coord_mode;
8214 }
8215
8216 /**
8217  * Uniquify the whole layer
8218  * Also requires the layers panel as the names shown there need updating too
8219  * Returns whether the operation was successful or not
8220  */
8221 gboolean vik_trw_layer_uniquify ( VikTrwLayer *vtl, VikLayersPanel *vlp )
8222 {
8223   if ( vtl && vlp ) {
8224     vik_trw_layer_uniquify_tracks ( vtl, vlp, vtl->tracks, TRUE );
8225     vik_trw_layer_uniquify_tracks ( vtl, vlp, vtl->routes, FALSE );
8226     vik_trw_layer_uniquify_waypoints ( vtl, vlp );
8227     return TRUE;
8228   }
8229   return FALSE;
8230 }
8231
8232 static void waypoint_convert ( const gpointer id, VikWaypoint *wp, VikCoordMode *dest_mode )
8233 {
8234   vik_coord_convert ( &(wp->coord), *dest_mode );
8235 }
8236
8237 static void track_convert ( const gchar *name, VikTrack *tr, VikCoordMode *dest_mode )
8238 {
8239   vik_track_convert ( tr, *dest_mode );
8240 }
8241
8242 static void trw_layer_change_coord_mode ( VikTrwLayer *vtl, VikCoordMode dest_mode )
8243 {
8244   if ( vtl->coord_mode != dest_mode )
8245   {
8246     vtl->coord_mode = dest_mode;
8247     g_hash_table_foreach ( vtl->waypoints, (GHFunc) waypoint_convert, &dest_mode );
8248     g_hash_table_foreach ( vtl->tracks, (GHFunc) track_convert, &dest_mode );
8249   }
8250 }
8251
8252 static void trw_layer_set_menu_selection ( VikTrwLayer *vtl, guint16 selection )
8253 {
8254   vtl->menu_selection = selection;
8255 }
8256
8257 static guint16 trw_layer_get_menu_selection ( VikTrwLayer *vtl )
8258 {
8259   return (vtl->menu_selection);
8260 }
8261
8262 /* ----------- Downloading maps along tracks --------------- */
8263
8264 static int get_download_area_width(VikViewport *vvp, gdouble zoom_level, struct LatLon *wh)
8265 {
8266   /* TODO: calculating based on current size of viewport */
8267   const gdouble w_at_zoom_0_125 = 0.0013;
8268   const gdouble h_at_zoom_0_125 = 0.0011;
8269   gdouble zoom_factor = zoom_level/0.125;
8270
8271   wh->lat = h_at_zoom_0_125 * zoom_factor;
8272   wh->lon = w_at_zoom_0_125 * zoom_factor;
8273
8274   return 0;   /* all OK */
8275 }
8276
8277 static VikCoord *get_next_coord(VikCoord *from, VikCoord *to, struct LatLon *dist, gdouble gradient)
8278 {
8279   if ((dist->lon >= ABS(to->east_west - from->east_west)) &&
8280       (dist->lat >= ABS(to->north_south - from->north_south)))
8281     return NULL;
8282
8283   VikCoord *coord = g_malloc(sizeof(VikCoord));
8284   coord->mode = VIK_COORD_LATLON;
8285
8286   if (ABS(gradient) < 1) {
8287     if (from->east_west > to->east_west)
8288       coord->east_west = from->east_west - dist->lon;
8289     else
8290       coord->east_west = from->east_west + dist->lon;
8291     coord->north_south = gradient * (coord->east_west - from->east_west) + from->north_south;
8292   } else {
8293     if (from->north_south > to->north_south)
8294       coord->north_south = from->north_south - dist->lat;
8295     else
8296       coord->north_south = from->north_south + dist->lat;
8297     coord->east_west = (1/gradient) * (coord->north_south - from->north_south) + from->north_south;
8298   }
8299
8300   return coord;
8301 }
8302
8303 static GList *add_fillins(GList *list, VikCoord *from, VikCoord *to, struct LatLon *dist)
8304 {
8305   /* TODO: handle virtical track (to->east_west - from->east_west == 0) */
8306   gdouble gradient = (to->north_south - from->north_south)/(to->east_west - from->east_west);
8307
8308   VikCoord *next = from;
8309   while (TRUE) {
8310     if ((next = get_next_coord(next, to, dist, gradient)) == NULL)
8311         break;
8312     list = g_list_prepend(list, next);
8313   }
8314
8315   return list;
8316 }
8317
8318 void vik_track_download_map(VikTrack *tr, VikMapsLayer *vml, VikViewport *vvp, gdouble zoom_level)
8319 {
8320   typedef struct _Rect {
8321     VikCoord tl;
8322     VikCoord br;
8323     VikCoord center;
8324   } Rect;
8325 #define GLRECT(iter) ((Rect *)((iter)->data))
8326
8327   struct LatLon wh;
8328   GList *rects_to_download = NULL;
8329   GList *rect_iter;
8330
8331   if (get_download_area_width(vvp, zoom_level, &wh))
8332     return;
8333
8334   GList *iter = tr->trackpoints;
8335   if (!iter)
8336     return;
8337
8338   gboolean new_map = TRUE;
8339   VikCoord *cur_coord, tl, br;
8340   Rect *rect;
8341   while (iter) {
8342     cur_coord = &(VIK_TRACKPOINT(iter->data))->coord;
8343     if (new_map) {
8344       vik_coord_set_area(cur_coord, &wh, &tl, &br);
8345       rect = g_malloc(sizeof(Rect));
8346       rect->tl = tl;
8347       rect->br = br;
8348       rect->center = *cur_coord;
8349       rects_to_download = g_list_prepend(rects_to_download, rect);
8350       new_map = FALSE;
8351       iter = iter->next;
8352       continue;
8353     }
8354     gboolean found = FALSE;
8355     for (rect_iter = rects_to_download; rect_iter; rect_iter = rect_iter->next) {
8356       if (vik_coord_inside(cur_coord, &GLRECT(rect_iter)->tl, &GLRECT(rect_iter)->br)) {
8357         found = TRUE;
8358         break;
8359       }
8360     }
8361     if (found)
8362         iter = iter->next;
8363     else
8364       new_map = TRUE;
8365   }
8366
8367   GList *fillins = NULL;
8368   /* 'fillin' doesn't work in UTM mode - potentially ending up in massive loop continually allocating memory - hence don't do it */
8369   /* seems that ATM the function get_next_coord works only for LATLON */
8370   if ( cur_coord->mode == VIK_COORD_LATLON ) {
8371     /* fill-ins for far apart points */
8372     GList *cur_rect, *next_rect;
8373     for (cur_rect = rects_to_download;
8374          (next_rect = cur_rect->next) != NULL;
8375          cur_rect = cur_rect->next) {
8376       if ((wh.lon < ABS(GLRECT(cur_rect)->center.east_west - GLRECT(next_rect)->center.east_west)) ||
8377           (wh.lat < ABS(GLRECT(cur_rect)->center.north_south - GLRECT(next_rect)->center.north_south))) {
8378         fillins = add_fillins(fillins, &GLRECT(cur_rect)->center, &GLRECT(next_rect)->center, &wh);
8379       }
8380     }
8381   } else
8382     g_message("%s: this feature works only in Mercator mode", __FUNCTION__);
8383
8384   if (fillins) {
8385     GList *iter = fillins;
8386     while (iter) {
8387       cur_coord = (VikCoord *)(iter->data);
8388       vik_coord_set_area(cur_coord, &wh, &tl, &br);
8389       rect = g_malloc(sizeof(Rect));
8390       rect->tl = tl;
8391       rect->br = br;
8392       rect->center = *cur_coord;
8393       rects_to_download = g_list_prepend(rects_to_download, rect);
8394       iter = iter->next;
8395     }
8396   }
8397
8398   for (rect_iter = rects_to_download; rect_iter; rect_iter = rect_iter->next) {
8399     maps_layer_download_section (vml, vvp, &(((Rect *)(rect_iter->data))->tl), &(((Rect *)(rect_iter->data))->br), zoom_level);
8400   }
8401
8402   if (fillins) {
8403     for (iter = fillins; iter; iter = iter->next)
8404       g_free(iter->data);
8405     g_list_free(fillins);
8406   }
8407   if (rects_to_download) {
8408     for (rect_iter = rects_to_download; rect_iter; rect_iter = rect_iter->next)
8409       g_free(rect_iter->data);
8410     g_list_free(rects_to_download);
8411   }
8412 }
8413
8414 static void trw_layer_download_map_along_track_cb ( gpointer pass_along[6] )
8415 {
8416   VikMapsLayer *vml;
8417   gint selected_map, default_map;
8418   gchar *zoomlist[] = {"0.125", "0.25", "0.5", "1", "2", "4", "8", "16", "32", "64", "128", "256", "512", "1024", NULL };
8419   gdouble zoom_vals[] = {0.125, 0.25, 0.5, 1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024};
8420   gint selected_zoom, default_zoom;
8421   int i,j;
8422
8423
8424   VikTrwLayer *vtl = pass_along[0];
8425   VikLayersPanel *vlp = pass_along[1];
8426   VikTrack *trk;
8427   if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
8428     trk = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
8429   else
8430     trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
8431   if ( !trk )
8432     return;
8433
8434   VikViewport *vvp = vik_window_viewport((VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl)));
8435
8436   GList *vmls = vik_layers_panel_get_all_layers_of_type(vlp, VIK_LAYER_MAPS, TRUE); // Includes hidden map layer types
8437   int num_maps = g_list_length(vmls);
8438
8439   if (!num_maps) {
8440     a_dialog_msg(VIK_GTK_WINDOW_FROM_LAYER(vtl), GTK_MESSAGE_ERROR, _("No map layer in use. Create one first"), NULL);
8441     return;
8442   }
8443
8444   gchar **map_names = g_malloc(1 + num_maps * sizeof(gpointer));
8445   VikMapsLayer **map_layers = g_malloc(1 + num_maps * sizeof(gpointer));
8446
8447   gchar **np = map_names;
8448   VikMapsLayer **lp = map_layers;
8449   for (i = 0; i < num_maps; i++) {
8450     gboolean dup = FALSE;
8451     vml = (VikMapsLayer *)(vmls->data);
8452     for (j = 0; j < i; j++) { /* no duplicate allowed */
8453       if (vik_maps_layer_get_map_type(vml) == vik_maps_layer_get_map_type(map_layers[j])) {
8454         dup = TRUE;
8455         break;
8456       }
8457     }
8458     if (!dup) {
8459       *lp++ = vml;
8460       *np++ = vik_maps_layer_get_map_label(vml);
8461     }
8462     vmls = vmls->next;
8463   }
8464   *lp = NULL;
8465   *np = NULL;
8466   num_maps = lp - map_layers;
8467
8468   for (default_map = 0; default_map < num_maps; default_map++) {
8469     /* TODO: check for parent layer's visibility */
8470     if (VIK_LAYER(map_layers[default_map])->visible)
8471       break;
8472   }
8473   default_map = (default_map == num_maps) ? 0 : default_map;
8474
8475   gdouble cur_zoom = vik_viewport_get_zoom(vvp);
8476   for (default_zoom = 0; default_zoom < sizeof(zoom_vals)/sizeof(gdouble); default_zoom++) {
8477     if (cur_zoom == zoom_vals[default_zoom])
8478       break;
8479   }
8480   default_zoom = (default_zoom == sizeof(zoom_vals)/sizeof(gdouble)) ? sizeof(zoom_vals)/sizeof(gdouble) - 1 : default_zoom;
8481
8482   if (!a_dialog_map_n_zoom(VIK_GTK_WINDOW_FROM_LAYER(vtl), map_names, default_map, zoomlist, default_zoom, &selected_map, &selected_zoom))
8483     goto done;
8484
8485   vik_track_download_map(trk, map_layers[selected_map], vvp, zoom_vals[selected_zoom]);
8486
8487 done:
8488   for (i = 0; i < num_maps; i++)
8489     g_free(map_names[i]);
8490   g_free(map_names);
8491   g_free(map_layers);
8492
8493   g_list_free(vmls);
8494
8495 }
8496
8497 /**** lowest waypoint number calculation ***/
8498 static gint highest_wp_number_name_to_number(const gchar *name) {
8499   if ( strlen(name) == 3 ) {
8500     int n = atoi(name);
8501     if ( n < 100 && name[0] != '0' )
8502       return -1;
8503     if ( n < 10 && name[0] != '0' )
8504       return -1;
8505     return n;
8506   }
8507   return -1;
8508 }
8509
8510
8511 static void highest_wp_number_reset(VikTrwLayer *vtl)
8512 {
8513   vtl->highest_wp_number = -1;
8514 }
8515
8516 static void highest_wp_number_add_wp(VikTrwLayer *vtl, const gchar *new_wp_name)
8517 {
8518   /* if is bigger that top, add it */
8519   gint new_wp_num = highest_wp_number_name_to_number(new_wp_name);
8520   if ( new_wp_num > vtl->highest_wp_number )
8521     vtl->highest_wp_number = new_wp_num;
8522 }
8523
8524 static void highest_wp_number_remove_wp(VikTrwLayer *vtl, const gchar *old_wp_name)
8525 {
8526   /* if wasn't top, do nothing. if was top, count backwards until we find one used */
8527   gint old_wp_num = highest_wp_number_name_to_number(old_wp_name);
8528   if ( vtl->highest_wp_number == old_wp_num ) {
8529     gchar buf[4];
8530     vtl->highest_wp_number--;
8531
8532     g_snprintf(buf,4,"%03d", vtl->highest_wp_number );
8533     /* search down until we find something that *does* exist */
8534
8535     while ( vtl->highest_wp_number > 0 && ! vik_trw_layer_get_waypoint ( vtl, buf )) {
8536       vtl->highest_wp_number--;
8537       g_snprintf(buf,4,"%03d", vtl->highest_wp_number );
8538     }
8539   }
8540 }
8541
8542 /* get lowest unused number */
8543 static gchar *highest_wp_number_get(VikTrwLayer *vtl)
8544 {
8545   gchar buf[4];
8546   if ( vtl->highest_wp_number < 0 || vtl->highest_wp_number >= 999 )
8547     return NULL;
8548   g_snprintf(buf,4,"%03d", vtl->highest_wp_number+1 );
8549   return g_strdup(buf);
8550 }