]> git.street.me.uk Git - andy/viking.git/blob - src/viktrwlayer.c
Fix comment in AC DEFINE
[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-2015, Rob Norris <rw_norris@hotmail.com>
9  * Copyright (c) 2012-2013, Guilhem Bonnefille <guilhem.bonnefille@gmail.com>
10  *
11  * This program is free software; you can redistribute it and/or modify
12  * it under the terms of the GNU General Public License as published by
13  * the Free Software Foundation; either version 2 of the License, or
14  * (at your option) any later version.
15  *
16  * This program is distributed in the hope that it will be useful,
17  * but WITHOUT ANY WARRANTY; without even the implied warranty of
18  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19  * GNU General Public License for more details.
20  *
21  * You should have received a copy of the GNU General Public License
22  * along with this program; if not, write to the Free Software
23  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
24  *
25  */
26 /* WARNING: If you go beyond this point, we are NOT responsible for any ill effects on your sanity */
27 /* viktrwlayer.c -- 8000+ lines can make a difference in the state of things */
28
29 #ifdef HAVE_CONFIG_H
30 #include "config.h"
31 #endif
32
33 #include "viking.h"
34 #include "vikmapslayer.h"
35 #include "vikgpslayer.h"
36 #include "viktrwlayer_export.h"
37 #include "viktrwlayer_tpwin.h"
38 #include "viktrwlayer_wpwin.h"
39 #include "viktrwlayer_propwin.h"
40 #include "viktrwlayer_analysis.h"
41 #include "viktrwlayer_tracklist.h"
42 #include "viktrwlayer_waypointlist.h"
43 #ifdef VIK_CONFIG_GEOTAG
44 #include "viktrwlayer_geotag.h"
45 #include "geotag_exif.h"
46 #endif
47 #include "garminsymbols.h"
48 #include "thumbnails.h"
49 #include "background.h"
50 #include "gpx.h"
51 #include "geojson.h"
52 #include "babel.h"
53 #include "dem.h"
54 #include "dems.h"
55 #include "geonamessearch.h"
56 #ifdef VIK_CONFIG_OPENSTREETMAP
57 #include "osm-traces.h"
58 #endif
59 #include "acquire.h"
60 #include "datasources.h"
61 #include "datasource_gps.h"
62 #include "vikexttools.h"
63 #include "vikexttool_datasources.h"
64 #include "ui_util.h"
65 #include "vikrouting.h"
66
67 #include "icons/icons.h"
68
69 #ifdef HAVE_MATH_H
70 #include <math.h>
71 #endif
72 #ifdef HAVE_STRING_H
73 #include <string.h>
74 #endif
75 #ifdef HAVE_STDLIB_H
76 #include <stdlib.h>
77 #endif
78 #include <stdio.h>
79 #include <ctype.h>
80
81 #include <gdk/gdkkeysyms.h>
82 #include <glib.h>
83 #include <glib/gstdio.h>
84 #include <glib/gi18n.h>
85
86 #define VIK_TRW_LAYER_TRACK_GC 6
87 #define VIK_TRW_LAYER_TRACK_GCS 10
88 #define VIK_TRW_LAYER_TRACK_GC_BLACK 0
89 #define VIK_TRW_LAYER_TRACK_GC_SLOW 1
90 #define VIK_TRW_LAYER_TRACK_GC_AVER 2
91 #define VIK_TRW_LAYER_TRACK_GC_FAST 3
92 #define VIK_TRW_LAYER_TRACK_GC_STOP 4
93 #define VIK_TRW_LAYER_TRACK_GC_SINGLE 5
94
95 #define DRAWMODE_BY_TRACK 0
96 #define DRAWMODE_BY_SPEED 1
97 #define DRAWMODE_ALL_SAME_COLOR 2
98 // Note using DRAWMODE_BY_SPEED may be slow especially for vast numbers of trackpoints
99 //  as we are (re)calculating the colour for every point
100
101 #define POINTS 1
102 #define LINES 2
103
104 /* this is how it knows when you click if you are clicking close to a trackpoint. */
105 #define TRACKPOINT_SIZE_APPROX 5
106 #define WAYPOINT_SIZE_APPROX 5
107
108 #define MIN_STOP_LENGTH 15
109 #define MAX_STOP_LENGTH 86400
110 #define DRAW_ELEVATION_FACTOR 30 /* height of elevation plotting, sort of relative to zoom level ("mpp" that isn't mpp necessarily) */
111                                  /* this is multiplied by user-inputted value from 1-100. */
112
113 enum { WP_SYMBOL_FILLED_SQUARE, WP_SYMBOL_SQUARE, WP_SYMBOL_CIRCLE, WP_SYMBOL_X, WP_NUM_SYMBOLS };
114
115 // See http://developer.gnome.org/pango/stable/PangoMarkupFormat.html
116 typedef enum {
117   FS_XX_SMALL = 0, // 'xx-small'
118   FS_X_SMALL,
119   FS_SMALL,
120   FS_MEDIUM, // DEFAULT
121   FS_LARGE,
122   FS_X_LARGE,
123   FS_XX_LARGE,
124   FS_NUM_SIZES
125 } font_size_t;
126
127 struct _VikTrwLayer {
128   VikLayer vl;
129   GHashTable *tracks;
130   GHashTable *tracks_iters;
131   GHashTable *routes;
132   GHashTable *routes_iters;
133   GHashTable *waypoints_iters;
134   GHashTable *waypoints;
135   GtkTreeIter tracks_iter, routes_iter, waypoints_iter;
136   gboolean tracks_visible, routes_visible, waypoints_visible;
137   LatLonBBox waypoints_bbox;
138
139   gboolean track_draw_labels;
140   guint8 drawmode;
141   guint8 drawpoints;
142   guint8 drawpoints_size;
143   guint8 drawelevation;
144   guint8 elevation_factor;
145   guint8 drawstops;
146   guint32 stop_length;
147   guint8 drawlines;
148   guint8 drawdirections;
149   guint8 drawdirections_size;
150   guint8 line_thickness;
151   guint8 bg_line_thickness;
152   vik_layer_sort_order_t track_sort_order;
153
154   // Metadata
155   VikTRWMetadata *metadata;
156
157   PangoLayout *tracklabellayout;
158   font_size_t track_font_size;
159   gchar *track_fsize_str;
160
161   guint8 wp_symbol;
162   guint8 wp_size;
163   gboolean wp_draw_symbols;
164   font_size_t wp_font_size;
165   gchar *wp_fsize_str;
166   vik_layer_sort_order_t wp_sort_order;
167
168   gdouble track_draw_speed_factor;
169   GArray *track_gc;
170   GdkGC *track_1color_gc;
171   GdkColor track_color;
172   GdkGC *current_track_gc;
173   // Separate GC for a track's potential new point as drawn via separate method
174   //  (compared to the actual track points drawn in the main trw_layer_draw_track function)
175   GdkGC *current_track_newpoint_gc;
176   GdkGC *track_bg_gc; GdkColor track_bg_color;
177   GdkGC *waypoint_gc; GdkColor waypoint_color;
178   GdkGC *waypoint_text_gc; GdkColor waypoint_text_color;
179   GdkGC *waypoint_bg_gc; GdkColor waypoint_bg_color;
180   gboolean wpbgand;
181   VikTrack *current_track; // ATM shared between new tracks and new routes
182   guint16 ct_x1, ct_y1, ct_x2, ct_y2;
183   gboolean draw_sync_done;
184   gboolean draw_sync_do;
185
186   VikCoordMode coord_mode;
187
188   /* wp editing tool */
189   VikWaypoint *current_wp;
190   gpointer current_wp_id;
191   gboolean moving_wp;
192   gboolean waypoint_rightclick;
193
194   /* track editing tool */
195   GList *current_tpl;
196   VikTrack *current_tp_track;
197   gpointer current_tp_id;
198   VikTrwLayerTpwin *tpwin;
199
200   /* track editing tool -- more specifically, moving tps */
201   gboolean moving_tp;
202
203   /* route finder tool */
204   gboolean route_finder_started;
205   gboolean route_finder_check_added_track;
206   VikTrack *route_finder_added_track;
207   gboolean route_finder_append;
208
209   gboolean drawlabels;
210   gboolean drawimages;
211   guint8 image_alpha;
212   GQueue *image_cache;
213   guint8 image_size;
214   guint16 image_cache_size;
215
216   /* for waypoint text */
217   PangoLayout *wplabellayout;
218
219   gboolean has_verified_thumbnails;
220
221   GtkMenu *wp_right_click_menu;
222   GtkMenu *track_right_click_menu;
223
224   /* menu */
225   VikStdLayerMenuItem menu_selection;
226
227   gint highest_wp_number;
228
229   // One per layer
230   GtkWidget *tracks_analysis_dialog;
231 };
232
233 /* A caached waypoint image. */
234 typedef struct {
235   GdkPixbuf *pixbuf;
236   gchar *image; /* filename */
237 } CachedPixbuf;
238
239 struct DrawingParams {
240   VikViewport *vp;
241   VikTrwLayer *vtl;
242   VikWindow *vw;
243   gdouble xmpp, ympp;
244   guint16 width, height;
245   gdouble cc; // Cosine factor in track directions
246   gdouble ss; // Sine factor in track directions
247   const VikCoord *center;
248   gboolean one_zone, lat_lon;
249   gdouble ce1, ce2, cn1, cn2;
250   LatLonBBox bbox;
251   gboolean highlight;
252 };
253
254 static gboolean trw_layer_delete_waypoint ( VikTrwLayer *vtl, VikWaypoint *wp );
255
256 typedef enum {
257   MA_VTL = 0,
258   MA_VLP,
259   MA_SUBTYPE, // OR END for Layer only
260   MA_SUBLAYER_ID,
261   MA_CONFIRM,
262   MA_VVP,
263   MA_TV_ITER,
264   MA_MISC,
265   MA_LAST,
266 } menu_array_index;
267
268 typedef gpointer menu_array_layer[2];
269 typedef gpointer menu_array_sublayer[MA_LAST];
270
271 static void trw_layer_delete_item ( menu_array_sublayer values );
272 static void trw_layer_copy_item_cb ( menu_array_sublayer values );
273 static void trw_layer_cut_item_cb ( menu_array_sublayer values );
274
275 static void trw_layer_find_maxmin_tracks ( const gpointer id, const VikTrack *trk, struct LatLon maxmin[2] );
276 static void trw_layer_find_maxmin (VikTrwLayer *vtl, struct LatLon maxmin[2]);
277
278 static void trw_layer_new_track_gcs ( VikTrwLayer *vtl, VikViewport *vp );
279 static void trw_layer_free_track_gcs ( VikTrwLayer *vtl );
280
281 static void trw_layer_draw_track_cb ( const gpointer id, VikTrack *track, struct DrawingParams *dp );
282 static void trw_layer_draw_waypoint ( const gpointer id, VikWaypoint *wp, struct DrawingParams *dp );
283
284 static void goto_coord ( gpointer *vlp, gpointer vvp, gpointer vl, const VikCoord *coord );
285 static void trw_layer_goto_track_startpoint ( menu_array_sublayer values );
286 static void trw_layer_goto_track_endpoint ( menu_array_sublayer values );
287 static void trw_layer_goto_track_max_speed ( menu_array_sublayer values );
288 static void trw_layer_goto_track_max_alt ( menu_array_sublayer values );
289 static void trw_layer_goto_track_min_alt ( menu_array_sublayer values );
290 static void trw_layer_goto_track_center ( menu_array_sublayer values );
291 static void trw_layer_merge_by_segment ( menu_array_sublayer values );
292 static void trw_layer_merge_by_timestamp ( menu_array_sublayer values );
293 static void trw_layer_merge_with_other ( menu_array_sublayer values );
294 static void trw_layer_append_track ( menu_array_sublayer values );
295 static void trw_layer_split_by_timestamp ( menu_array_sublayer values );
296 static void trw_layer_split_by_n_points ( menu_array_sublayer values );
297 static void trw_layer_split_at_trackpoint ( menu_array_sublayer values );
298 static void trw_layer_split_segments ( menu_array_sublayer values );
299 static void trw_layer_delete_point_selected ( menu_array_sublayer values );
300 static void trw_layer_delete_points_same_position ( menu_array_sublayer values );
301 static void trw_layer_delete_points_same_time ( menu_array_sublayer values );
302 static void trw_layer_reverse ( menu_array_sublayer values );
303 static void trw_layer_download_map_along_track_cb ( menu_array_sublayer values );
304 static void trw_layer_edit_trackpoint ( menu_array_sublayer values );
305 static void trw_layer_show_picture ( menu_array_sublayer values );
306 static void trw_layer_gps_upload_any ( menu_array_sublayer values );
307
308 static void trw_layer_centerize ( menu_array_layer values );
309 static void trw_layer_auto_view ( menu_array_layer values );
310 static void trw_layer_goto_wp ( menu_array_layer values );
311 static void trw_layer_new_wp ( menu_array_layer values );
312 static void trw_layer_new_track ( menu_array_layer values );
313 static void trw_layer_new_route ( menu_array_layer values );
314 static void trw_layer_finish_track ( menu_array_layer values );
315 static void trw_layer_auto_waypoints_view ( menu_array_layer values );
316 static void trw_layer_auto_tracks_view ( menu_array_layer values );
317 static void trw_layer_delete_all_tracks ( menu_array_layer values );
318 static void trw_layer_delete_tracks_from_selection ( menu_array_layer values );
319 static void trw_layer_delete_all_waypoints ( menu_array_layer values );
320 static void trw_layer_delete_waypoints_from_selection ( menu_array_layer values );
321 static void trw_layer_new_wikipedia_wp_viewport ( menu_array_layer values );
322 static void trw_layer_new_wikipedia_wp_layer ( menu_array_layer values );
323 #ifdef VIK_CONFIG_GEOTAG
324 static void trw_layer_geotagging_waypoint_mtime_keep ( menu_array_sublayer values );
325 static void trw_layer_geotagging_waypoint_mtime_update ( menu_array_sublayer values );
326 static void trw_layer_geotagging_track ( menu_array_sublayer values );
327 static void trw_layer_geotagging ( menu_array_layer values );
328 #endif
329 static void trw_layer_acquire_gps_cb ( menu_array_layer values );
330 static void trw_layer_acquire_routing_cb ( menu_array_layer values );
331 static void trw_layer_acquire_url_cb ( menu_array_layer values );
332 #ifdef VIK_CONFIG_OPENSTREETMAP
333 static void trw_layer_acquire_osm_cb ( menu_array_layer values );
334 static void trw_layer_acquire_osm_my_traces_cb ( menu_array_layer values );
335 #endif
336 #ifdef VIK_CONFIG_GEOCACHES
337 static void trw_layer_acquire_geocache_cb ( menu_array_layer values );
338 #endif
339 #ifdef VIK_CONFIG_GEOTAG
340 static void trw_layer_acquire_geotagged_cb ( menu_array_layer values );
341 #endif
342 static void trw_layer_acquire_file_cb ( menu_array_layer values );
343 static void trw_layer_gps_upload ( menu_array_layer values );
344
345 static void trw_layer_track_list_dialog_single ( menu_array_sublayer values );
346 static void trw_layer_track_list_dialog ( menu_array_layer values );
347 static void trw_layer_waypoint_list_dialog ( menu_array_layer values );
348
349 // Specific route versions:
350 //  Most track handling functions can handle operating on the route list
351 //  However these ones are easier in separate functions
352 static void trw_layer_auto_routes_view ( menu_array_layer values );
353 static void trw_layer_delete_all_routes ( menu_array_layer values );
354 static void trw_layer_delete_routes_from_selection ( menu_array_layer values );
355
356 /* pop-up items */
357 static void trw_layer_properties_item ( gpointer pass_along[7] ); //TODO??
358 static void trw_layer_goto_waypoint ( menu_array_sublayer values );
359 static void trw_layer_waypoint_gc_webpage ( menu_array_sublayer values );
360 static void trw_layer_waypoint_webpage ( menu_array_sublayer values );
361
362 static void trw_layer_realize_waypoint ( gpointer id, VikWaypoint *wp, gpointer pass_along[5] );
363 static void trw_layer_realize_track ( gpointer id, VikTrack *track, gpointer pass_along[5] );
364
365 static void trw_layer_insert_tp_beside_current_tp ( VikTrwLayer *vtl, gboolean );
366 static void trw_layer_cancel_current_tp ( VikTrwLayer *vtl, gboolean destroy );
367 static void trw_layer_tpwin_response ( VikTrwLayer *vtl, gint response );
368 static void trw_layer_tpwin_init ( VikTrwLayer *vtl );
369
370 static void trw_layer_sort_all ( VikTrwLayer *vtl );
371
372 static gpointer tool_edit_trackpoint_create ( VikWindow *vw, VikViewport *vvp);
373 static void tool_edit_trackpoint_destroy ( tool_ed_t *t );
374 static gboolean tool_edit_trackpoint_click ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data );
375 static gboolean tool_edit_trackpoint_move ( VikTrwLayer *vtl, GdkEventMotion *event, gpointer data );
376 static gboolean tool_edit_trackpoint_release ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data );
377 static gpointer tool_show_picture_create ( VikWindow *vw, VikViewport *vvp);
378 static gboolean tool_show_picture_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp ); 
379 static gpointer tool_edit_waypoint_create ( VikWindow *vw, VikViewport *vvp);
380 static void tool_edit_waypoint_destroy ( tool_ed_t *t );
381 static gboolean tool_edit_waypoint_click ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data );
382 static gboolean tool_edit_waypoint_move ( VikTrwLayer *vtl, GdkEventMotion *event, gpointer data );
383 static gboolean tool_edit_waypoint_release ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data );
384 static gpointer tool_new_route_create ( VikWindow *vw, VikViewport *vvp);
385 static gboolean tool_new_route_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp );
386 static gpointer tool_new_track_create ( VikWindow *vw, VikViewport *vvp);
387 static gboolean tool_new_track_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp ); 
388 static VikLayerToolFuncStatus tool_new_track_move ( VikTrwLayer *vtl, GdkEventMotion *event, VikViewport *vvp ); 
389 static void tool_new_track_release ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp );
390 static gboolean tool_new_track_key_press ( VikTrwLayer *vtl, GdkEventKey *event, VikViewport *vvp ); 
391 static gpointer tool_new_waypoint_create ( VikWindow *vw, VikViewport *vvp);
392 static gboolean tool_new_waypoint_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp );
393 static gpointer tool_extended_route_finder_create ( VikWindow *vw, VikViewport *vvp);
394 static gboolean tool_extended_route_finder_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp );
395 static gboolean tool_extended_route_finder_key_press ( VikTrwLayer *vtl, GdkEventKey *event, VikViewport *vvp );
396
397 static void cached_pixbuf_free ( CachedPixbuf *cp );
398 static gint cached_pixbuf_cmp ( CachedPixbuf *cp, const gchar *name );
399
400 static VikTrackpoint *closest_tp_in_five_pixel_interval ( VikTrwLayer *vtl, VikViewport *vvp, gint x, gint y );
401 static VikWaypoint *closest_wp_in_five_pixel_interval ( VikTrwLayer *vtl, VikViewport *vvp, gint x, gint y );
402
403 static void waypoint_convert ( const gpointer id, VikWaypoint *wp, VikCoordMode *dest_mode );
404 static void track_convert ( const gpointer id, VikTrack *tr, VikCoordMode *dest_mode );
405
406 static gchar *highest_wp_number_get(VikTrwLayer *vtl);
407 static void highest_wp_number_reset(VikTrwLayer *vtl);
408 static void highest_wp_number_add_wp(VikTrwLayer *vtl, const gchar *new_wp_name);
409 static void highest_wp_number_remove_wp(VikTrwLayer *vtl, const gchar *old_wp_name);
410
411 // Note for the following tool GtkRadioActionEntry texts:
412 //  the very first text value is an internal name not displayed anywhere
413 //  the first N_ text value is the name used for menu entries - hence has an underscore for the keyboard accelerator
414 //    * remember not to clash with the values used for VikWindow level tools (Pan, Zoom, Ruler + Select)
415 //  the second N_ text value is used for the button tooltip (i.e. generally don't want an underscore here)
416 //  the value is always set to 0 and the tool loader in VikWindow will set the actual appropriate value used
417 static VikToolInterface trw_layer_tools[] = {
418   { { "CreateWaypoint", "vik-icon-Create Waypoint", N_("Create _Waypoint"), "<control><shift>W", N_("Create Waypoint"), 0 },
419     (VikToolConstructorFunc) tool_new_waypoint_create,    NULL, NULL, NULL,
420     (VikToolMouseFunc) tool_new_waypoint_click,    NULL, NULL, (VikToolKeyFunc) NULL,
421     FALSE,
422     GDK_CURSOR_IS_PIXMAP, &cursor_addwp_pixbuf, NULL },
423
424   { { "CreateTrack", "vik-icon-Create Track", N_("Create _Track"), "<control><shift>T", N_("Create Track"), 0 },
425     (VikToolConstructorFunc) tool_new_track_create,       NULL, NULL, NULL,
426     (VikToolMouseFunc) tool_new_track_click,
427     (VikToolMouseMoveFunc) tool_new_track_move,
428     (VikToolMouseFunc) tool_new_track_release,
429     (VikToolKeyFunc) tool_new_track_key_press,
430     TRUE, // Still need to handle clicks when in PAN mode to disable the potential trackpoint drawing
431     GDK_CURSOR_IS_PIXMAP, &cursor_addtr_pixbuf, NULL },
432
433   { { "CreateRoute", "vik-icon-Create Route", N_("Create _Route"), "<control><shift>B", N_("Create Route"), 0 },
434     (VikToolConstructorFunc) tool_new_route_create,       NULL, NULL, NULL,
435     (VikToolMouseFunc) tool_new_route_click,
436     (VikToolMouseMoveFunc) tool_new_track_move, // -\#
437     (VikToolMouseFunc) tool_new_track_release,  //   -> Reuse these track methods on a route
438     (VikToolKeyFunc) tool_new_track_key_press,  // -/#
439     TRUE, // Still need to handle clicks when in PAN mode to disable the potential trackpoint drawing
440     GDK_CURSOR_IS_PIXMAP, &cursor_new_route_pixbuf, NULL },
441
442   { { "ExtendedRouteFinder", "vik-icon-Route Finder", N_("Route _Finder"), "<control><shift>F", N_("Route Finder"), 0 },
443     (VikToolConstructorFunc) tool_extended_route_finder_create,  NULL, NULL, NULL,
444     (VikToolMouseFunc) tool_extended_route_finder_click,
445     (VikToolMouseMoveFunc) tool_new_track_move, // -\#
446     (VikToolMouseFunc) tool_new_track_release,  //   -> Reuse these track methods on a route
447     (VikToolKeyFunc) tool_extended_route_finder_key_press,
448     TRUE, // Still need to handle clicks when in PAN mode to disable the potential trackpoint drawing
449     GDK_CURSOR_IS_PIXMAP, &cursor_route_finder_pixbuf, NULL },
450
451   { { "EditWaypoint", "vik-icon-Edit Waypoint", N_("_Edit Waypoint"), "<control><shift>E", N_("Edit Waypoint"), 0 },
452     (VikToolConstructorFunc) tool_edit_waypoint_create,
453     (VikToolDestructorFunc) tool_edit_waypoint_destroy,
454     NULL, NULL,
455     (VikToolMouseFunc) tool_edit_waypoint_click,   
456     (VikToolMouseMoveFunc) tool_edit_waypoint_move,
457     (VikToolMouseFunc) tool_edit_waypoint_release, (VikToolKeyFunc) NULL,
458     FALSE,
459     GDK_CURSOR_IS_PIXMAP, &cursor_edwp_pixbuf, NULL },
460
461   { { "EditTrackpoint", "vik-icon-Edit Trackpoint", N_("Edit Trac_kpoint"), "<control><shift>K", N_("Edit Trackpoint"), 0 },
462     (VikToolConstructorFunc) tool_edit_trackpoint_create,
463     (VikToolDestructorFunc) tool_edit_trackpoint_destroy,
464     NULL, NULL,
465     (VikToolMouseFunc) tool_edit_trackpoint_click,
466     (VikToolMouseMoveFunc) tool_edit_trackpoint_move,
467     (VikToolMouseFunc) tool_edit_trackpoint_release, (VikToolKeyFunc) NULL,
468     FALSE,
469     GDK_CURSOR_IS_PIXMAP, &cursor_edtr_pixbuf, NULL },
470
471   { { "ShowPicture", "vik-icon-Show Picture", N_("Show P_icture"), "<control><shift>I", N_("Show Picture"), 0 },
472     (VikToolConstructorFunc) tool_show_picture_create,    NULL, NULL, NULL,
473     (VikToolMouseFunc) tool_show_picture_click,    NULL, NULL, (VikToolKeyFunc) NULL,
474     FALSE,
475     GDK_CURSOR_IS_PIXMAP, &cursor_showpic_pixbuf, NULL },
476
477 };
478
479 enum {
480   TOOL_CREATE_WAYPOINT=0,
481   TOOL_CREATE_TRACK,
482   TOOL_CREATE_ROUTE,
483   TOOL_ROUTE_FINDER,
484   TOOL_EDIT_WAYPOINT,
485   TOOL_EDIT_TRACKPOINT,
486   TOOL_SHOW_PICTURE,
487   NUM_TOOLS
488 };
489
490 /****** PARAMETERS ******/
491
492 static gchar *params_groups[] = { N_("Waypoints"), N_("Tracks"), N_("Waypoint Images"), N_("Tracks Advanced"), N_("Metadata") };
493 enum { GROUP_WAYPOINTS, GROUP_TRACKS, GROUP_IMAGES, GROUP_TRACKS_ADV, GROUP_METADATA };
494
495 static gchar *params_drawmodes[] = { N_("Draw by Track"), N_("Draw by Speed"), N_("All Tracks Same Color"), NULL };
496 static gchar *params_wpsymbols[] = { N_("Filled Square"), N_("Square"), N_("Circle"), N_("X"), 0 };
497
498 #define MIN_POINT_SIZE 2
499 #define MAX_POINT_SIZE 10
500
501 #define MIN_ARROW_SIZE 3
502 #define MAX_ARROW_SIZE 20
503
504 static VikLayerParamScale params_scales[] = {
505  /* min  max    step digits */
506  {  1,   10,    1,   0 }, /* line_thickness */
507  {  0,   100,   1,   0 }, /* track draw speed factor */
508  {  1.0, 100.0, 1.0, 2 }, /* UNUSED */
509                 /* 5 * step == how much to turn */
510  {  16,   128,  4,   0 }, // 3: image_size - NB step size ignored when an HSCALE used
511  {   0,   255,  5,   0 }, // 4: image alpha -    "     "      "            "
512  {   5,   500,  5,   0 }, // 5: image cache_size -     "      "
513  {   0,   8,    1,   0 }, // 6: Background line thickness
514  {   1,  64,    1,   0 }, /* wpsize */
515  {   MIN_STOP_LENGTH, MAX_STOP_LENGTH, 1,   0 }, /* stop_length */
516  {   1, 100, 1,   0 }, // 9: elevation factor
517  {   MIN_POINT_SIZE,  MAX_POINT_SIZE,  1,   0 }, // 10: track point size
518  {   MIN_ARROW_SIZE,  MAX_ARROW_SIZE,  1,   0 }, // 11: direction arrow size
519 };
520
521 static gchar* params_font_sizes[] = {
522   N_("Extra Extra Small"),
523   N_("Extra Small"),
524   N_("Small"),
525   N_("Medium"),
526   N_("Large"),
527   N_("Extra Large"),
528   N_("Extra Extra Large"),
529   NULL };
530
531 // Needs to align with vik_layer_sort_order_t
532 static gchar* params_sort_order[] = {
533   N_("None"),
534   N_("Name Ascending"),
535   N_("Name Descending"),
536   N_("Date Ascending"),
537   N_("Date Descending"),
538   NULL
539 };
540
541 static VikLayerParamData black_color_default ( void ) {
542   VikLayerParamData data; gdk_color_parse ( "#000000", &data.c ); return data; // Black
543 }
544 static VikLayerParamData drawmode_default ( void ) { return VIK_LPD_UINT ( DRAWMODE_BY_TRACK ); }
545 static VikLayerParamData line_thickness_default ( void ) { return VIK_LPD_UINT ( 1 ); }
546 static VikLayerParamData trkpointsize_default ( void ) { return VIK_LPD_UINT ( MIN_POINT_SIZE ); }
547 static VikLayerParamData trkdirectionsize_default ( void ) { return VIK_LPD_UINT ( 5 ); }
548 static VikLayerParamData bg_line_thickness_default ( void ) { return VIK_LPD_UINT ( 0 ); }
549 static VikLayerParamData trackbgcolor_default ( void ) {
550   VikLayerParamData data; gdk_color_parse ( "#FFFFFF", &data.c ); return data; // White
551 }
552 static VikLayerParamData elevation_factor_default ( void ) { return VIK_LPD_UINT ( 30 ); }
553 static VikLayerParamData stop_length_default ( void ) { return VIK_LPD_UINT ( 60 ); }
554 static VikLayerParamData speed_factor_default ( void ) { return VIK_LPD_DOUBLE ( 30.0 ); }
555
556 static VikLayerParamData tnfontsize_default ( void ) { return VIK_LPD_UINT ( FS_MEDIUM ); }
557 static VikLayerParamData wpfontsize_default ( void ) { return VIK_LPD_UINT ( FS_MEDIUM ); }
558 static VikLayerParamData wptextcolor_default ( void ) {
559   VikLayerParamData data; gdk_color_parse ( "#FFFFFF", &data.c ); return data; // White
560 }
561 static VikLayerParamData wpbgcolor_default ( void ) {
562   VikLayerParamData data; gdk_color_parse ( "#8383C4", &data.c ); return data; // Kind of Blue
563 }
564 static VikLayerParamData wpsize_default ( void ) { return VIK_LPD_UINT ( 4 ); }
565 static VikLayerParamData wpsymbol_default ( void ) { return VIK_LPD_UINT ( WP_SYMBOL_FILLED_SQUARE ); }
566
567 static VikLayerParamData image_size_default ( void ) { return VIK_LPD_UINT ( 64 ); }
568 static VikLayerParamData image_alpha_default ( void ) { return VIK_LPD_UINT ( 255 ); }
569 static VikLayerParamData image_cache_size_default ( void ) { return VIK_LPD_UINT ( 300 ); }
570
571 static VikLayerParamData sort_order_default ( void ) { return VIK_LPD_UINT ( 0 ); }
572
573 static VikLayerParamData string_default ( void )
574 {
575   VikLayerParamData data;
576   data.s = "";
577   return data;
578 }
579
580 VikLayerParam trw_layer_params[] = {
581   { VIK_LAYER_TRW, "tracks_visible", VIK_LAYER_PARAM_BOOLEAN, VIK_LAYER_NOT_IN_PROPERTIES, NULL, 0, NULL, NULL, NULL, vik_lpd_true_default, NULL, NULL },
582   { VIK_LAYER_TRW, "waypoints_visible", VIK_LAYER_PARAM_BOOLEAN, VIK_LAYER_NOT_IN_PROPERTIES, NULL, 0, NULL, NULL, NULL, vik_lpd_true_default, NULL, NULL },
583   { VIK_LAYER_TRW, "routes_visible", VIK_LAYER_PARAM_BOOLEAN, VIK_LAYER_NOT_IN_PROPERTIES, NULL, 0, NULL, NULL, NULL, vik_lpd_true_default, NULL, NULL },
584
585   { VIK_LAYER_TRW, "trackdrawlabels", VIK_LAYER_PARAM_BOOLEAN, GROUP_TRACKS, N_("Draw Labels"), VIK_LAYER_WIDGET_CHECKBUTTON, NULL, NULL,
586     N_("Note: the individual track controls what labels may be displayed"), vik_lpd_true_default, NULL, NULL },
587   { VIK_LAYER_TRW, "trackfontsize", VIK_LAYER_PARAM_UINT, GROUP_TRACKS_ADV, N_("Track Labels Font Size:"), VIK_LAYER_WIDGET_COMBOBOX, params_font_sizes, NULL, NULL, tnfontsize_default, NULL, NULL },
588   { VIK_LAYER_TRW, "drawmode", VIK_LAYER_PARAM_UINT, GROUP_TRACKS, N_("Track Drawing Mode:"), VIK_LAYER_WIDGET_COMBOBOX, params_drawmodes, NULL, NULL, drawmode_default, NULL, NULL },
589   { VIK_LAYER_TRW, "trackcolor", VIK_LAYER_PARAM_COLOR, GROUP_TRACKS, N_("All Tracks Color:"), VIK_LAYER_WIDGET_COLOR, NULL, NULL,
590     N_("The color used when 'All Tracks Same Color' drawing mode is selected"), black_color_default, NULL, NULL },
591   { VIK_LAYER_TRW, "drawlines", VIK_LAYER_PARAM_BOOLEAN, GROUP_TRACKS, N_("Draw Track Lines"), VIK_LAYER_WIDGET_CHECKBUTTON, NULL, NULL, NULL, vik_lpd_true_default, NULL, NULL },
592   { VIK_LAYER_TRW, "line_thickness", VIK_LAYER_PARAM_UINT, GROUP_TRACKS_ADV, N_("Track Thickness:"), VIK_LAYER_WIDGET_SPINBUTTON, &params_scales[0], NULL, NULL, line_thickness_default, NULL, NULL },
593   { VIK_LAYER_TRW, "drawdirections", VIK_LAYER_PARAM_BOOLEAN, GROUP_TRACKS, N_("Draw Track Direction"), VIK_LAYER_WIDGET_CHECKBUTTON, NULL, NULL, NULL, vik_lpd_false_default, NULL, NULL },
594   { VIK_LAYER_TRW, "trkdirectionsize", VIK_LAYER_PARAM_UINT, GROUP_TRACKS_ADV, N_("Direction Size:"), VIK_LAYER_WIDGET_SPINBUTTON, &params_scales[11], NULL, NULL, trkdirectionsize_default, NULL, NULL },
595   { VIK_LAYER_TRW, "drawpoints", VIK_LAYER_PARAM_BOOLEAN, GROUP_TRACKS, N_("Draw Trackpoints"), VIK_LAYER_WIDGET_CHECKBUTTON, NULL, NULL, NULL, vik_lpd_true_default, NULL, NULL },
596   { VIK_LAYER_TRW, "trkpointsize", VIK_LAYER_PARAM_UINT, GROUP_TRACKS_ADV, N_("Trackpoint Size:"), VIK_LAYER_WIDGET_SPINBUTTON, &params_scales[10], NULL, NULL, trkpointsize_default, NULL, NULL },
597   { VIK_LAYER_TRW, "drawelevation", VIK_LAYER_PARAM_BOOLEAN, GROUP_TRACKS, N_("Draw Elevation"), VIK_LAYER_WIDGET_CHECKBUTTON, NULL, NULL, NULL, vik_lpd_false_default, NULL, NULL },
598   { VIK_LAYER_TRW, "elevation_factor", VIK_LAYER_PARAM_UINT, GROUP_TRACKS_ADV, N_("Draw Elevation Height %:"), VIK_LAYER_WIDGET_HSCALE, &params_scales[9], NULL, NULL, elevation_factor_default, NULL, NULL },
599   { VIK_LAYER_TRW, "drawstops", VIK_LAYER_PARAM_BOOLEAN, GROUP_TRACKS, N_("Draw Stops"), VIK_LAYER_WIDGET_CHECKBUTTON, NULL, NULL,
600     N_("Whether to draw a marker when trackpoints are at the same position but over the minimum stop length apart in time"), vik_lpd_false_default, NULL, NULL },
601   { VIK_LAYER_TRW, "stop_length", VIK_LAYER_PARAM_UINT, GROUP_TRACKS_ADV, N_("Min Stop Length (seconds):"), VIK_LAYER_WIDGET_SPINBUTTON, &params_scales[8], NULL, NULL, stop_length_default, NULL, NULL },
602
603   { VIK_LAYER_TRW, "bg_line_thickness", VIK_LAYER_PARAM_UINT, GROUP_TRACKS_ADV, N_("Track BG Thickness:"), VIK_LAYER_WIDGET_SPINBUTTON, &params_scales[6], NULL, NULL, bg_line_thickness_default, NULL, NULL },
604   { VIK_LAYER_TRW, "trackbgcolor", VIK_LAYER_PARAM_COLOR, GROUP_TRACKS_ADV, N_("Track Background Color"), VIK_LAYER_WIDGET_COLOR, NULL, NULL, NULL, trackbgcolor_default, NULL, NULL },
605   { VIK_LAYER_TRW, "speed_factor", VIK_LAYER_PARAM_DOUBLE, GROUP_TRACKS_ADV, N_("Draw by Speed Factor (%):"), VIK_LAYER_WIDGET_HSCALE, &params_scales[1], NULL,
606     N_("The percentage factor away from the average speed determining the color used"), speed_factor_default, NULL, NULL },
607   { VIK_LAYER_TRW, "tracksortorder", VIK_LAYER_PARAM_UINT, GROUP_TRACKS_ADV, N_("Track Sort Order:"), VIK_LAYER_WIDGET_COMBOBOX, params_sort_order, NULL, NULL, sort_order_default, NULL, NULL },
608
609   { VIK_LAYER_TRW, "drawlabels", VIK_LAYER_PARAM_BOOLEAN, GROUP_WAYPOINTS, N_("Draw Labels"), VIK_LAYER_WIDGET_CHECKBUTTON, NULL, NULL, NULL, vik_lpd_true_default, NULL, NULL },
610   { VIK_LAYER_TRW, "wpfontsize", VIK_LAYER_PARAM_UINT, GROUP_WAYPOINTS, N_("Waypoint Font Size:"), VIK_LAYER_WIDGET_COMBOBOX, params_font_sizes, NULL, NULL, wpfontsize_default, NULL, NULL },
611   { VIK_LAYER_TRW, "wpcolor", VIK_LAYER_PARAM_COLOR, GROUP_WAYPOINTS, N_("Waypoint Color:"), VIK_LAYER_WIDGET_COLOR, NULL, NULL, NULL, black_color_default, NULL, NULL },
612   { VIK_LAYER_TRW, "wptextcolor", VIK_LAYER_PARAM_COLOR, GROUP_WAYPOINTS, N_("Waypoint Text:"), VIK_LAYER_WIDGET_COLOR, NULL, NULL, NULL, wptextcolor_default, NULL, NULL },
613   { VIK_LAYER_TRW, "wpbgcolor", VIK_LAYER_PARAM_COLOR, GROUP_WAYPOINTS, N_("Background:"), VIK_LAYER_WIDGET_COLOR, NULL, NULL, NULL, wpbgcolor_default, NULL, NULL },
614   { VIK_LAYER_TRW, "wpbgand", VIK_LAYER_PARAM_BOOLEAN, GROUP_WAYPOINTS, N_("Fake BG Color Translucency:"), VIK_LAYER_WIDGET_CHECKBUTTON, NULL, NULL, NULL, vik_lpd_false_default, NULL, NULL },
615   { VIK_LAYER_TRW, "wpsymbol", VIK_LAYER_PARAM_UINT, GROUP_WAYPOINTS, N_("Waypoint marker:"), VIK_LAYER_WIDGET_COMBOBOX, params_wpsymbols, NULL, NULL, wpsymbol_default, NULL, NULL },
616   { VIK_LAYER_TRW, "wpsize", VIK_LAYER_PARAM_UINT, GROUP_WAYPOINTS, N_("Waypoint size:"), VIK_LAYER_WIDGET_SPINBUTTON, &params_scales[7], NULL, NULL, wpsize_default, NULL, NULL },
617   { VIK_LAYER_TRW, "wpsyms", VIK_LAYER_PARAM_BOOLEAN, GROUP_WAYPOINTS, N_("Draw Waypoint Symbols:"), VIK_LAYER_WIDGET_CHECKBUTTON, NULL, NULL, NULL, vik_lpd_true_default, NULL, NULL },
618   { VIK_LAYER_TRW, "wpsortorder", VIK_LAYER_PARAM_UINT, GROUP_WAYPOINTS, N_("Waypoint Sort Order:"), VIK_LAYER_WIDGET_COMBOBOX, params_sort_order, NULL, NULL, sort_order_default, NULL, NULL },
619
620   { VIK_LAYER_TRW, "drawimages", VIK_LAYER_PARAM_BOOLEAN, GROUP_IMAGES, N_("Draw Waypoint Images"), VIK_LAYER_WIDGET_CHECKBUTTON, NULL, NULL, NULL, vik_lpd_true_default, NULL, NULL },
621   { VIK_LAYER_TRW, "image_size", VIK_LAYER_PARAM_UINT, GROUP_IMAGES, N_("Image Size (pixels):"), VIK_LAYER_WIDGET_HSCALE, &params_scales[3], NULL, NULL, image_size_default, NULL, NULL },
622   { VIK_LAYER_TRW, "image_alpha", VIK_LAYER_PARAM_UINT, GROUP_IMAGES, N_("Image Alpha:"), VIK_LAYER_WIDGET_HSCALE, &params_scales[4], NULL, NULL, image_alpha_default, NULL, NULL },
623   { VIK_LAYER_TRW, "image_cache_size", VIK_LAYER_PARAM_UINT, GROUP_IMAGES, N_("Image Memory Cache Size:"), VIK_LAYER_WIDGET_HSCALE, &params_scales[5], NULL, NULL, image_cache_size_default, NULL, NULL },
624
625   { VIK_LAYER_TRW, "metadatadesc", VIK_LAYER_PARAM_STRING, GROUP_METADATA, N_("Description"), VIK_LAYER_WIDGET_ENTRY, NULL, NULL, NULL, string_default, NULL, NULL },
626   { VIK_LAYER_TRW, "metadataauthor", VIK_LAYER_PARAM_STRING, GROUP_METADATA, N_("Author"), VIK_LAYER_WIDGET_ENTRY, NULL, NULL, NULL, string_default, NULL, NULL },
627   { VIK_LAYER_TRW, "metadatatime", VIK_LAYER_PARAM_STRING, GROUP_METADATA, N_("Creation Time"), VIK_LAYER_WIDGET_ENTRY, NULL, NULL, NULL, string_default, NULL, NULL },
628   { VIK_LAYER_TRW, "metadatakeywords", VIK_LAYER_PARAM_STRING, GROUP_METADATA, N_("Keywords"), VIK_LAYER_WIDGET_ENTRY, NULL, NULL, NULL, string_default, NULL, NULL },
629 };
630
631 // ENUMERATION MUST BE IN THE SAME ORDER AS THE NAMED PARAMS ABOVE
632 enum {
633   // Sublayer visibilities
634   PARAM_TV,
635   PARAM_WV,
636   PARAM_RV,
637   // Tracks
638   PARAM_TDL,
639   PARAM_TLFONTSIZE,
640   PARAM_DM,
641   PARAM_TC,
642   PARAM_DL,
643   PARAM_LT,
644   PARAM_DD,
645   PARAM_DDS,
646   PARAM_DP,
647   PARAM_DPS,
648   PARAM_DE,
649   PARAM_EF,
650   PARAM_DS,
651   PARAM_SL,
652   PARAM_BLT,
653   PARAM_TBGC,
654   PARAM_TDSF,
655   PARAM_TSO,
656   // Waypoints
657   PARAM_DLA,
658   PARAM_WPFONTSIZE,
659   PARAM_WPC,
660   PARAM_WPTC,
661   PARAM_WPBC,
662   PARAM_WPBA,
663   PARAM_WPSYM,
664   PARAM_WPSIZE,
665   PARAM_WPSYMS,
666   PARAM_WPSO,
667   // WP images
668   PARAM_DI,
669   PARAM_IS,
670   PARAM_IA,
671   PARAM_ICS,
672   // Metadata
673   PARAM_MDDESC,
674   PARAM_MDAUTH,
675   PARAM_MDTIME,
676   PARAM_MDKEYS,
677   NUM_PARAMS
678 };
679
680 /*** TO ADD A PARAM:
681  *** 1) Add to trw_layer_params and enumeration
682  *** 2) Handle in get_param & set_param (presumably adding on to VikTrwLayer struct)
683  ***/
684
685 /****** END PARAMETERS ******/
686
687 /* Layer Interface function definitions */
688 static VikTrwLayer* trw_layer_create ( VikViewport *vp );
689 static void trw_layer_realize ( VikTrwLayer *vtl, VikTreeview *vt, GtkTreeIter *layer_iter );
690 static void trw_layer_post_read ( VikTrwLayer *vtl, VikViewport *vvp, gboolean from_file );
691 static void trw_layer_free ( VikTrwLayer *trwlayer );
692 static void trw_layer_draw ( VikTrwLayer *l, gpointer data );
693 static void trw_layer_change_coord_mode ( VikTrwLayer *vtl, VikCoordMode dest_mode );
694 static time_t trw_layer_get_timestamp ( VikTrwLayer *vtl );
695 static void trw_layer_set_menu_selection ( VikTrwLayer *vtl, guint16 );
696 static guint16 trw_layer_get_menu_selection ( VikTrwLayer *vtl );
697 static void trw_layer_add_menu_items ( VikTrwLayer *vtl, GtkMenu *menu, gpointer vlp );
698 static gboolean trw_layer_sublayer_add_menu_items ( VikTrwLayer *l, GtkMenu *menu, gpointer vlp, gint subtype, gpointer sublayer, GtkTreeIter *iter, VikViewport *vvp );
699 static const gchar* trw_layer_sublayer_rename_request ( VikTrwLayer *l, const gchar *newname, gpointer vlp, gint subtype, gpointer sublayer, GtkTreeIter *iter );
700 static gboolean trw_layer_sublayer_toggle_visible ( VikTrwLayer *l, gint subtype, gpointer sublayer );
701 static const gchar* trw_layer_layer_tooltip ( VikTrwLayer *vtl );
702 static const gchar* trw_layer_sublayer_tooltip ( VikTrwLayer *l, gint subtype, gpointer sublayer );
703 static gboolean trw_layer_selected ( VikTrwLayer *l, gint subtype, gpointer sublayer, gint type, gpointer vlp );
704 static void trw_layer_marshall ( VikTrwLayer *vtl, guint8 **data, gint *len );
705 static VikTrwLayer *trw_layer_unmarshall ( guint8 *data, gint len, VikViewport *vvp );
706 static gboolean trw_layer_set_param ( VikTrwLayer *vtl, guint16 id, VikLayerParamData data, VikViewport *vp, gboolean is_file_operation );
707 static VikLayerParamData trw_layer_get_param ( VikTrwLayer *vtl, guint16 id, gboolean is_file_operation );
708 static void trw_layer_change_param ( GtkWidget *widget, ui_change_values values );
709 static void trw_layer_del_item ( VikTrwLayer *vtl, gint subtype, gpointer sublayer );
710 static void trw_layer_cut_item ( VikTrwLayer *vtl, gint subtype, gpointer sublayer );
711 static void trw_layer_copy_item ( VikTrwLayer *vtl, gint subtype, gpointer sublayer, guint8 **item, guint *len );
712 static gboolean trw_layer_paste_item ( VikTrwLayer *vtl, gint subtype, guint8 *item, guint len );
713 static void trw_layer_free_copied_item ( gint subtype, gpointer item );
714 static void trw_layer_drag_drop_request ( VikTrwLayer *vtl_src, VikTrwLayer *vtl_dest, GtkTreeIter *src_item_iter, GtkTreePath *dest_path );
715 static gboolean trw_layer_select_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp, tool_ed_t *t );
716 static gboolean trw_layer_select_move ( VikTrwLayer *vtl, GdkEventMotion *event, VikViewport *vvp, tool_ed_t *t );
717 static gboolean trw_layer_select_release ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp, tool_ed_t *t );
718 static gboolean trw_layer_show_selected_viewport_menu ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp );
719 /* End Layer Interface function definitions */
720
721 VikLayerInterface vik_trw_layer_interface = {
722   "TrackWaypoint",
723   N_("TrackWaypoint"),
724   "<control><shift>Y",
725   &viktrwlayer_pixbuf,
726
727   trw_layer_tools,
728   sizeof(trw_layer_tools) / sizeof(VikToolInterface),
729
730   trw_layer_params,
731   NUM_PARAMS,
732   params_groups, /* params_groups */
733   sizeof(params_groups)/sizeof(params_groups[0]),    /* number of groups */
734
735   VIK_MENU_ITEM_ALL,
736
737   (VikLayerFuncCreate)                  trw_layer_create,
738   (VikLayerFuncRealize)                 trw_layer_realize,
739   (VikLayerFuncPostRead)                trw_layer_post_read,
740   (VikLayerFuncFree)                    trw_layer_free,
741
742   (VikLayerFuncProperties)              NULL,
743   (VikLayerFuncDraw)                    trw_layer_draw,
744   (VikLayerFuncChangeCoordMode)         trw_layer_change_coord_mode,
745   (VikLayerFuncGetTimestamp)            trw_layer_get_timestamp,
746
747   (VikLayerFuncSetMenuItemsSelection)   trw_layer_set_menu_selection,
748   (VikLayerFuncGetMenuItemsSelection)   trw_layer_get_menu_selection,
749
750   (VikLayerFuncAddMenuItems)            trw_layer_add_menu_items,
751   (VikLayerFuncSublayerAddMenuItems)    trw_layer_sublayer_add_menu_items,
752
753   (VikLayerFuncSublayerRenameRequest)   trw_layer_sublayer_rename_request,
754   (VikLayerFuncSublayerToggleVisible)   trw_layer_sublayer_toggle_visible,
755   (VikLayerFuncSublayerTooltip)         trw_layer_sublayer_tooltip,
756   (VikLayerFuncLayerTooltip)            trw_layer_layer_tooltip,
757   (VikLayerFuncLayerSelected)           trw_layer_selected,
758
759   (VikLayerFuncMarshall)                trw_layer_marshall,
760   (VikLayerFuncUnmarshall)              trw_layer_unmarshall,
761
762   (VikLayerFuncSetParam)                trw_layer_set_param,
763   (VikLayerFuncGetParam)                trw_layer_get_param,
764   (VikLayerFuncChangeParam)             trw_layer_change_param,
765
766   (VikLayerFuncReadFileData)            a_gpspoint_read_file,
767   (VikLayerFuncWriteFileData)           a_gpspoint_write_file,
768
769   (VikLayerFuncDeleteItem)              trw_layer_del_item,
770   (VikLayerFuncCutItem)                 trw_layer_cut_item,
771   (VikLayerFuncCopyItem)                trw_layer_copy_item,
772   (VikLayerFuncPasteItem)               trw_layer_paste_item,
773   (VikLayerFuncFreeCopiedItem)          trw_layer_free_copied_item,
774   
775   (VikLayerFuncDragDropRequest)         trw_layer_drag_drop_request,
776
777   (VikLayerFuncSelectClick)             trw_layer_select_click,
778   (VikLayerFuncSelectMove)              trw_layer_select_move,
779   (VikLayerFuncSelectRelease)           trw_layer_select_release,
780   (VikLayerFuncSelectedViewportMenu)    trw_layer_show_selected_viewport_menu,
781 };
782
783 static gboolean have_diary_program = FALSE;
784 static gchar *diary_program = NULL;
785 #define VIK_SETTINGS_EXTERNAL_DIARY_PROGRAM "external_diary_program"
786
787 static gboolean have_geojson_export = FALSE;
788
789 static gboolean have_astro_program = FALSE;
790 static gchar *astro_program = NULL;
791 #define VIK_SETTINGS_EXTERNAL_ASTRO_PROGRAM "external_astro_program"
792
793 // NB Only performed once per program run
794 static void vik_trwlayer_class_init ( VikTrwLayerClass *klass )
795 {
796   if ( ! a_settings_get_string ( VIK_SETTINGS_EXTERNAL_DIARY_PROGRAM, &diary_program ) ) {
797 #ifdef WINDOWS
798     //diary_program = g_strdup ( "C:\\Program Files\\Rednotebook\\rednotebook.exe" );
799     diary_program = g_strdup ( "C:/Progra~1/Rednotebook/rednotebook.exe" );
800 #else
801     diary_program = g_strdup ( "rednotebook" );
802 #endif
803   }
804   else {
805     // User specified so assume it works
806     have_diary_program = TRUE;
807   }
808
809   if ( g_find_program_in_path( diary_program ) ) {
810     gchar *mystdout = NULL;
811     gchar *mystderr = NULL;
812     // Needs RedNotebook 1.7.3+ for support of opening on a specified date
813     gchar *cmd = g_strconcat ( diary_program, " --version", NULL ); // "rednotebook --version"
814     if ( g_spawn_command_line_sync ( cmd, &mystdout, &mystderr, NULL, NULL ) ) {
815       // Annoyingly 1.7.1|2|3 versions of RedNotebook prints the version to stderr!!
816       if ( mystdout )
817         g_debug ("Diary: %s", mystdout ); // Should be something like 'RedNotebook 1.4'
818       if ( mystderr )
819         g_warning ("Diary: stderr: %s", mystderr );
820
821       gchar **tokens = NULL;
822       if ( mystdout && g_strcmp0(mystdout, "") )
823         tokens = g_strsplit(mystdout, " ", 0);
824       else if ( mystderr )
825         tokens = g_strsplit(mystderr, " ", 0);
826
827       if ( tokens ) {
828         gint num = 0;
829         gchar *token = tokens[num];
830         while ( token && num < 2 ) {
831           if (num == 1) {
832             if ( viking_version_to_number(token) >= viking_version_to_number("1.7.3") )
833               have_diary_program = TRUE;
834           }
835           num++;
836           token = tokens[num];
837         }
838       }
839       g_strfreev ( tokens );
840     }
841     g_free ( mystdout );
842     g_free ( mystderr );
843     g_free ( cmd );
844   }
845
846   if ( g_find_program_in_path ( a_geojson_program_export() ) ) {
847     have_geojson_export = TRUE;
848   }
849
850   // Astronomy Domain
851   if ( ! a_settings_get_string ( VIK_SETTINGS_EXTERNAL_ASTRO_PROGRAM, &astro_program ) ) {
852 #ifdef WINDOWS
853     //astro_program = g_strdup ( "C:\\Program Files\\Stellarium\\stellarium.exe" );
854     astro_program = g_strdup ( "C:/Progra~1/Stellarium/stellarium.exe" );
855 #else
856     astro_program = g_strdup ( "stellarium" );
857 #endif
858   }
859   else {
860     // User specified so assume it works
861     have_astro_program = TRUE;
862   }
863   if ( g_find_program_in_path( astro_program ) ) {
864     have_astro_program = TRUE;
865   }
866 }
867
868 GType vik_trw_layer_get_type ()
869 {
870   static GType vtl_type = 0;
871
872   if (!vtl_type)
873   {
874     static const GTypeInfo vtl_info =
875     {
876       sizeof (VikTrwLayerClass),
877       NULL, /* base_init */
878       NULL, /* base_finalize */
879       (GClassInitFunc) vik_trwlayer_class_init, /* class init */
880       NULL, /* class_finalize */
881       NULL, /* class_data */
882       sizeof (VikTrwLayer),
883       0,
884       NULL /* instance init */
885     };
886     vtl_type = g_type_register_static ( VIK_LAYER_TYPE, "VikTrwLayer", &vtl_info, 0 );
887   }
888   return vtl_type;
889 }
890
891 VikTRWMetadata *vik_trw_metadata_new()
892 {
893   return (VikTRWMetadata*)g_malloc0(sizeof(VikTRWMetadata));
894 }
895
896 void vik_trw_metadata_free ( VikTRWMetadata *metadata)
897 {
898   g_free (metadata);
899 }
900
901 VikTRWMetadata *vik_trw_layer_get_metadata ( VikTrwLayer *vtl )
902 {
903   return vtl->metadata;
904 }
905
906 void vik_trw_layer_set_metadata ( VikTrwLayer *vtl, VikTRWMetadata *metadata)
907 {
908   if ( vtl->metadata )
909     vik_trw_metadata_free ( vtl->metadata );
910   vtl->metadata = metadata;
911 }
912
913 typedef struct {
914   gboolean found;
915   const gchar *date_str;
916   const VikTrack *trk;
917   const VikWaypoint *wpt;
918   gpointer trk_id;
919   gpointer wpt_id;
920 } date_finder_type;
921
922 static gboolean trw_layer_find_date_track ( const gpointer id, const VikTrack *trk, date_finder_type *df )
923 {
924   gchar date_buf[20];
925   date_buf[0] = '\0';
926   // Might be an easier way to compare dates rather than converting the strings all the time...
927   if ( trk->trackpoints && VIK_TRACKPOINT(trk->trackpoints->data)->has_timestamp ) {
928     strftime (date_buf, sizeof(date_buf), "%Y-%m-%d", gmtime(&(VIK_TRACKPOINT(trk->trackpoints->data)->timestamp)));
929
930     if ( ! g_strcmp0 ( df->date_str, date_buf ) ) {
931       df->found = TRUE;
932       df->trk = trk;
933       df->trk_id = id;
934     }
935   }
936   return df->found;
937 }
938
939 static gboolean trw_layer_find_date_waypoint ( const gpointer id, const VikWaypoint *wpt, date_finder_type *df )
940 {
941   gchar date_buf[20];
942   date_buf[0] = '\0';
943   // Might be an easier way to compare dates rather than converting the strings all the time...
944   if ( wpt->has_timestamp ) {
945     strftime (date_buf, sizeof(date_buf), "%Y-%m-%d", gmtime(&(wpt->timestamp)));
946
947     if ( ! g_strcmp0 ( df->date_str, date_buf ) ) {
948       df->found = TRUE;
949       df->wpt = wpt;
950       df->wpt_id = id;
951     }
952   }
953   return df->found;
954 }
955
956 /**
957  * Find an item by date
958  */
959 gboolean vik_trw_layer_find_date ( VikTrwLayer *vtl, const gchar *date_str, VikCoord *position, VikViewport *vvp, gboolean do_tracks, gboolean select )
960 {
961   date_finder_type df;
962   df.found = FALSE;
963   df.date_str = date_str;
964   df.trk = NULL;
965   df.wpt = NULL;
966   // Only tracks ATM
967   if ( do_tracks )
968     g_hash_table_find ( vtl->tracks, (GHRFunc) trw_layer_find_date_track, &df );
969   else
970     g_hash_table_find ( vtl->waypoints, (GHRFunc) trw_layer_find_date_waypoint, &df );
971
972   if ( select && df.found ) {
973     if ( do_tracks && df.trk ) {
974       struct LatLon maxmin[2] = { {0,0}, {0,0} };
975       trw_layer_find_maxmin_tracks ( NULL, df.trk, maxmin );
976       trw_layer_zoom_to_show_latlons ( vtl, vvp, maxmin );
977       vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, g_hash_table_lookup (vtl->tracks_iters, df.trk_id), TRUE );
978     }
979     else if ( df.wpt ) {
980       vik_viewport_set_center_coord ( vvp, &(df.wpt->coord), TRUE );
981       vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, g_hash_table_lookup (vtl->waypoints_iters, df.wpt_id), TRUE );
982     }
983     vik_layer_emit_update ( VIK_LAYER(vtl) );
984   }
985   return df.found;
986 }
987
988 static void trw_layer_del_item ( VikTrwLayer *vtl, gint subtype, gpointer sublayer )
989 {
990   static menu_array_sublayer values;
991   if (!sublayer) {
992     return;
993   }
994
995   gint ii;
996   for ( ii = MA_VTL; ii < MA_LAST; ii++ )
997     values[ii] = NULL;
998
999   values[MA_VTL]         = vtl;
1000   values[MA_SUBTYPE]     = GINT_TO_POINTER (subtype);
1001   values[MA_SUBLAYER_ID] = sublayer;
1002   values[MA_CONFIRM]     = GINT_TO_POINTER (1); // Confirm delete request
1003
1004   trw_layer_delete_item ( values );
1005 }
1006
1007 static void trw_layer_cut_item ( VikTrwLayer *vtl, gint subtype, gpointer sublayer )
1008 {
1009   static menu_array_sublayer values;
1010   if (!sublayer) {
1011     return;
1012   }
1013
1014   gint ii;
1015   for ( ii = MA_VTL; ii < MA_LAST; ii++ )
1016     values[ii] = NULL;
1017
1018   values[MA_VTL]         = vtl;
1019   values[MA_SUBTYPE]     = GINT_TO_POINTER (subtype);
1020   values[MA_SUBLAYER_ID] = sublayer;
1021   values[MA_CONFIRM]     = GINT_TO_POINTER (1); // Confirm delete request
1022
1023   trw_layer_copy_item_cb(values);
1024   trw_layer_cut_item_cb(values);
1025 }
1026
1027 static void trw_layer_copy_item_cb ( menu_array_sublayer values)
1028 {
1029   VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
1030   gint subtype = GPOINTER_TO_INT (values[MA_SUBTYPE]);
1031   gpointer sublayer = values[MA_SUBLAYER_ID];
1032   guint8 *data = NULL;
1033   guint len;
1034
1035   trw_layer_copy_item( vtl, subtype, sublayer, &data, &len);
1036
1037   if (data) {
1038     const gchar* name;
1039     if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT ) {
1040       VikWaypoint *wp = g_hash_table_lookup ( vtl->waypoints, sublayer);
1041       if ( wp && wp->name )
1042         name = wp->name;
1043       else
1044         name = NULL; // Broken :(
1045     }
1046     else if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK ) {
1047       VikTrack *trk = g_hash_table_lookup ( vtl->tracks, sublayer);
1048       if ( trk && trk->name )
1049         name = trk->name;
1050       else
1051         name = NULL; // Broken :(
1052     }
1053     else {
1054       VikTrack *trk = g_hash_table_lookup ( vtl->routes, sublayer);
1055       if ( trk && trk->name )
1056         name = trk->name;
1057       else
1058         name = NULL; // Broken :(
1059     }
1060
1061     a_clipboard_copy( VIK_CLIPBOARD_DATA_SUBLAYER, VIK_LAYER_TRW,
1062                       subtype, len, name, data);
1063   }
1064 }
1065
1066 static void trw_layer_cut_item_cb ( menu_array_sublayer values)
1067 {
1068   trw_layer_copy_item_cb(values);
1069   values[MA_CONFIRM] = GINT_TO_POINTER (0); // Never need to confirm automatic delete
1070   trw_layer_delete_item(values);
1071 }
1072
1073 static void trw_layer_paste_item_cb ( menu_array_sublayer values)
1074 {
1075   // Slightly cheating method, routing via the panels capability
1076   a_clipboard_paste (VIK_LAYERS_PANEL(values[MA_VLP]));
1077 }
1078
1079 static void trw_layer_copy_item ( VikTrwLayer *vtl, gint subtype, gpointer sublayer, guint8 **item, guint *len )
1080 {
1081   guint8 *id;
1082   guint il;
1083
1084   if (!sublayer) {
1085     *item = NULL;
1086     return;
1087   }
1088
1089   GByteArray *ba = g_byte_array_new ();
1090
1091   if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT ) {
1092     vik_waypoint_marshall ( g_hash_table_lookup ( vtl->waypoints, sublayer ), &id, &il );
1093   } else if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK ) {
1094     vik_track_marshall ( g_hash_table_lookup ( vtl->tracks, sublayer ), &id, &il );
1095   } else {
1096     vik_track_marshall ( g_hash_table_lookup ( vtl->routes, sublayer ), &id, &il );
1097   }
1098
1099   g_byte_array_append ( ba, id, il );
1100
1101   g_free(id);
1102
1103   *len = ba->len;
1104   *item = ba->data;
1105 }
1106
1107 static gboolean trw_layer_paste_item ( VikTrwLayer *vtl, gint subtype, guint8 *item, guint len )
1108 {
1109   if ( !item )
1110     return FALSE;
1111
1112   gchar *name;
1113
1114   if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT )
1115   {
1116     VikWaypoint *w;
1117
1118     w = vik_waypoint_unmarshall ( item, len );
1119     // When copying - we'll create a new name based on the original
1120     name = trw_layer_new_unique_sublayer_name(vtl, VIK_TRW_LAYER_SUBLAYER_WAYPOINT, w->name);
1121     vik_trw_layer_add_waypoint ( vtl, name, w );
1122     waypoint_convert (NULL, w, &vtl->coord_mode);
1123     g_free ( name );
1124
1125     trw_layer_calculate_bounds_waypoints ( vtl );
1126
1127     // Consider if redraw necessary for the new item
1128     if ( vtl->vl.visible && vtl->waypoints_visible && w->visible )
1129       vik_layer_emit_update ( VIK_LAYER(vtl) );
1130     return TRUE;
1131   }
1132   if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
1133   {
1134     VikTrack *t;
1135
1136     t = vik_track_unmarshall ( item, len );
1137     // When copying - we'll create a new name based on the original
1138     name = trw_layer_new_unique_sublayer_name(vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, t->name);
1139     vik_trw_layer_add_track ( vtl, name, t );
1140     vik_track_convert (t, vtl->coord_mode);
1141     g_free ( name );
1142
1143     // Consider if redraw necessary for the new item
1144     if ( vtl->vl.visible && vtl->tracks_visible && t->visible )
1145       vik_layer_emit_update ( VIK_LAYER(vtl) );
1146     return TRUE;
1147   }
1148   if ( subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE )
1149   {
1150     VikTrack *t;
1151
1152     t = vik_track_unmarshall ( item, len );
1153     // When copying - we'll create a new name based on the original
1154     name = trw_layer_new_unique_sublayer_name(vtl, VIK_TRW_LAYER_SUBLAYER_ROUTE, t->name);
1155     vik_trw_layer_add_route ( vtl, name, t );
1156     vik_track_convert (t, vtl->coord_mode);
1157     g_free ( name );
1158
1159     // Consider if redraw necessary for the new item
1160     if ( vtl->vl.visible && vtl->routes_visible && t->visible )
1161       vik_layer_emit_update ( VIK_LAYER(vtl) );
1162     return TRUE;
1163   }
1164   return FALSE;
1165 }
1166
1167 static void trw_layer_free_copied_item ( gint subtype, gpointer item )
1168 {
1169   if (item) {
1170     g_free(item);
1171   }
1172 }
1173
1174 static void image_cache_free ( VikTrwLayer *vtl )
1175 {
1176   g_list_foreach ( vtl->image_cache->head, (GFunc)cached_pixbuf_free, NULL );
1177   g_queue_free ( vtl->image_cache );
1178 }
1179
1180 static gboolean trw_layer_set_param ( VikTrwLayer *vtl, guint16 id, VikLayerParamData data, VikViewport *vp, gboolean is_file_operation )
1181 {
1182   switch ( id )
1183   {
1184     case PARAM_TV: vtl->tracks_visible = data.b; break;
1185     case PARAM_WV: vtl->waypoints_visible = data.b; break;
1186     case PARAM_RV: vtl->routes_visible = data.b; break;
1187     case PARAM_TDL: vtl->track_draw_labels = data.b; break;
1188     case PARAM_TLFONTSIZE:
1189       if ( data.u < FS_NUM_SIZES ) {
1190         vtl->track_font_size = data.u;
1191         g_free ( vtl->track_fsize_str );
1192         switch ( vtl->track_font_size ) {
1193           case FS_XX_SMALL: vtl->track_fsize_str = g_strdup ( "xx-small" ); break;
1194           case FS_X_SMALL: vtl->track_fsize_str = g_strdup ( "x-small" ); break;
1195           case FS_SMALL: vtl->track_fsize_str = g_strdup ( "small" ); break;
1196           case FS_LARGE: vtl->track_fsize_str = g_strdup ( "large" ); break;
1197           case FS_X_LARGE: vtl->track_fsize_str = g_strdup ( "x-large" ); break;
1198           case FS_XX_LARGE: vtl->track_fsize_str = g_strdup ( "xx-large" ); break;
1199           default: vtl->track_fsize_str = g_strdup ( "medium" ); break;
1200         }
1201       }
1202       break;
1203     case PARAM_DM: vtl->drawmode = data.u; break;
1204     case PARAM_TC:
1205       vtl->track_color = data.c;
1206       if ( vp ) trw_layer_new_track_gcs ( vtl, vp );
1207       break;
1208     case PARAM_DP: vtl->drawpoints = data.b; break;
1209     case PARAM_DPS:
1210       if ( data.u >= MIN_POINT_SIZE && data.u <= MAX_POINT_SIZE )
1211         vtl->drawpoints_size = data.u;
1212       break;
1213     case PARAM_DE: vtl->drawelevation = data.b; break;
1214     case PARAM_DS: vtl->drawstops = data.b; break;
1215     case PARAM_DL: vtl->drawlines = data.b; break;
1216     case PARAM_DD: vtl->drawdirections = data.b; break;
1217     case PARAM_DDS:
1218       if ( data.u >= MIN_ARROW_SIZE && data.u <= MAX_ARROW_SIZE )
1219         vtl->drawdirections_size = data.u;
1220       break;
1221     case PARAM_SL: if ( data.u >= MIN_STOP_LENGTH && data.u <= MAX_STOP_LENGTH )
1222                      vtl->stop_length = data.u;
1223                    break;
1224     case PARAM_EF: if ( data.u >= 1 && data.u <= 100 )
1225                      vtl->elevation_factor = data.u;
1226                    break;
1227     case PARAM_LT: if ( data.u > 0 && data.u < 15 && data.u != vtl->line_thickness )
1228                    {
1229                      vtl->line_thickness = data.u;
1230                      if ( vp ) trw_layer_new_track_gcs ( vtl, vp );
1231                    }
1232                    break;
1233     case PARAM_BLT: if ( data.u <= 8 && data.u != vtl->bg_line_thickness )
1234                    {
1235                      vtl->bg_line_thickness = data.u;
1236                      if ( vp ) trw_layer_new_track_gcs ( vtl, vp );
1237                    }
1238                    break;
1239     case PARAM_TBGC:
1240       vtl->track_bg_color = data.c;
1241       if ( vtl->track_bg_gc )
1242         gdk_gc_set_rgb_fg_color(vtl->track_bg_gc, &(vtl->track_bg_color));
1243       break;
1244     case PARAM_TDSF: vtl->track_draw_speed_factor = data.d; break;
1245     case PARAM_TSO: if ( data.u < VL_SO_LAST ) vtl->track_sort_order = data.u; break;
1246     case PARAM_DLA: vtl->drawlabels = data.b; break;
1247     case PARAM_DI: vtl->drawimages = data.b; break;
1248     case PARAM_IS: if ( data.u != vtl->image_size )
1249       {
1250         vtl->image_size = data.u;
1251         image_cache_free ( vtl );
1252         vtl->image_cache = g_queue_new ();
1253       }
1254       break;
1255     case PARAM_IA: if ( data.u != vtl->image_alpha )
1256       {
1257         vtl->image_alpha = data.u;
1258         image_cache_free ( vtl );
1259         vtl->image_cache = g_queue_new ();
1260       }
1261       break;
1262     case PARAM_ICS: vtl->image_cache_size = data.u;
1263       while ( vtl->image_cache->length > vtl->image_cache_size ) /* if shrinking cache_size, free pixbuf ASAP */
1264           cached_pixbuf_free ( g_queue_pop_tail ( vtl->image_cache ) );
1265       break;
1266     case PARAM_WPC:
1267       vtl->waypoint_color = data.c;
1268       if ( vtl->waypoint_gc )
1269         gdk_gc_set_rgb_fg_color(vtl->waypoint_gc, &(vtl->waypoint_color));
1270       break;
1271     case PARAM_WPTC:
1272       vtl->waypoint_text_color = data.c;
1273       if ( vtl->waypoint_text_gc )
1274         gdk_gc_set_rgb_fg_color(vtl->waypoint_text_gc, &(vtl->waypoint_text_color));
1275       break;
1276     case PARAM_WPBC:
1277       vtl->waypoint_bg_color = data.c;
1278       if ( vtl->waypoint_bg_gc )
1279         gdk_gc_set_rgb_fg_color(vtl->waypoint_bg_gc, &(vtl->waypoint_bg_color));
1280       break;
1281     case PARAM_WPBA:
1282       vtl->wpbgand = data.b;
1283       if ( vtl->waypoint_bg_gc )
1284         gdk_gc_set_function(vtl->waypoint_bg_gc, data.b ? GDK_AND : GDK_COPY );
1285       break;
1286     case PARAM_WPSYM: if ( data.u < WP_NUM_SYMBOLS ) vtl->wp_symbol = data.u; break;
1287     case PARAM_WPSIZE: if ( data.u > 0 && data.u <= 64 ) vtl->wp_size = data.u; break;
1288     case PARAM_WPSYMS: vtl->wp_draw_symbols = data.b; break;
1289     case PARAM_WPFONTSIZE:
1290       if ( data.u < FS_NUM_SIZES ) {
1291         vtl->wp_font_size = data.u;
1292         g_free ( vtl->wp_fsize_str );
1293         switch ( vtl->wp_font_size ) {
1294           case FS_XX_SMALL: vtl->wp_fsize_str = g_strdup ( "xx-small" ); break;
1295           case FS_X_SMALL: vtl->wp_fsize_str = g_strdup ( "x-small" ); break;
1296           case FS_SMALL: vtl->wp_fsize_str = g_strdup ( "small" ); break;
1297           case FS_LARGE: vtl->wp_fsize_str = g_strdup ( "large" ); break;
1298           case FS_X_LARGE: vtl->wp_fsize_str = g_strdup ( "x-large" ); break;
1299           case FS_XX_LARGE: vtl->wp_fsize_str = g_strdup ( "xx-large" ); break;
1300           default: vtl->wp_fsize_str = g_strdup ( "medium" ); break;
1301         }
1302       }
1303       break;
1304     case PARAM_WPSO: if ( data.u < VL_SO_LAST ) vtl->wp_sort_order = data.u; break;
1305     // Metadata
1306     case PARAM_MDDESC: if ( data.s && vtl->metadata ) vtl->metadata->description = g_strdup (data.s); break;
1307     case PARAM_MDAUTH: if ( data.s && vtl->metadata ) vtl->metadata->author = g_strdup (data.s); break;
1308     case PARAM_MDTIME: if ( data.s && vtl->metadata ) vtl->metadata->timestamp = g_strdup (data.s); break;
1309     case PARAM_MDKEYS: if ( data.s && vtl->metadata ) vtl->metadata->keywords = g_strdup (data.s); break;
1310     default: break;
1311   }
1312   return TRUE;
1313 }
1314
1315 static VikLayerParamData trw_layer_get_param ( VikTrwLayer *vtl, guint16 id, gboolean is_file_operation )
1316 {
1317   VikLayerParamData rv;
1318   switch ( id )
1319   {
1320     case PARAM_TV: rv.b = vtl->tracks_visible; break;
1321     case PARAM_WV: rv.b = vtl->waypoints_visible; break;
1322     case PARAM_RV: rv.b = vtl->routes_visible; break;
1323     case PARAM_TDL: rv.b = vtl->track_draw_labels; break;
1324     case PARAM_TLFONTSIZE: rv.u = vtl->track_font_size; break;
1325     case PARAM_DM: rv.u = vtl->drawmode; break;
1326     case PARAM_TC: rv.c = vtl->track_color; break;
1327     case PARAM_DP: rv.b = vtl->drawpoints; break;
1328     case PARAM_DPS: rv.u = vtl->drawpoints_size; break;
1329     case PARAM_DE: rv.b = vtl->drawelevation; break;
1330     case PARAM_EF: rv.u = vtl->elevation_factor; break;
1331     case PARAM_DS: rv.b = vtl->drawstops; break;
1332     case PARAM_SL: rv.u = vtl->stop_length; break;
1333     case PARAM_DL: rv.b = vtl->drawlines; break;
1334     case PARAM_DD: rv.b = vtl->drawdirections; break;
1335     case PARAM_DDS: rv.u = vtl->drawdirections_size; break;
1336     case PARAM_LT: rv.u = vtl->line_thickness; break;
1337     case PARAM_BLT: rv.u = vtl->bg_line_thickness; break;
1338     case PARAM_DLA: rv.b = vtl->drawlabels; break;
1339     case PARAM_DI: rv.b = vtl->drawimages; break;
1340     case PARAM_TBGC: rv.c = vtl->track_bg_color; break;
1341     case PARAM_TDSF: rv.d = vtl->track_draw_speed_factor; break;
1342     case PARAM_TSO: rv.u = vtl->track_sort_order; break;
1343     case PARAM_IS: rv.u = vtl->image_size; break;
1344     case PARAM_IA: rv.u = vtl->image_alpha; break;
1345     case PARAM_ICS: rv.u = vtl->image_cache_size; break;
1346     case PARAM_WPC: rv.c = vtl->waypoint_color; break;
1347     case PARAM_WPTC: rv.c = vtl->waypoint_text_color; break;
1348     case PARAM_WPBC: rv.c = vtl->waypoint_bg_color; break;
1349     case PARAM_WPBA: rv.b = vtl->wpbgand; break;
1350     case PARAM_WPSYM: rv.u = vtl->wp_symbol; break;
1351     case PARAM_WPSIZE: rv.u = vtl->wp_size; break;
1352     case PARAM_WPSYMS: rv.b = vtl->wp_draw_symbols; break;
1353     case PARAM_WPFONTSIZE: rv.u = vtl->wp_font_size; break;
1354     case PARAM_WPSO: rv.u = vtl->wp_sort_order; break;
1355     // Metadata
1356     case PARAM_MDDESC: if (vtl->metadata) { rv.s = vtl->metadata->description; } break;
1357     case PARAM_MDAUTH: if (vtl->metadata) { rv.s = vtl->metadata->author; } break;
1358     case PARAM_MDTIME: if (vtl->metadata) { rv.s = vtl->metadata->timestamp; } break;
1359     case PARAM_MDKEYS: if (vtl->metadata) { rv.s = vtl->metadata->keywords; } break;
1360     default: break;
1361   }
1362   return rv;
1363 }
1364
1365 static void trw_layer_change_param ( GtkWidget *widget, ui_change_values values )
1366 {
1367   // This '-3' is to account for the first few parameters not in the properties
1368   const gint OFFSET = -3;
1369
1370   switch ( GPOINTER_TO_INT(values[UI_CHG_PARAM_ID]) ) {
1371     // Alter sensitivity of waypoint draw image related widgets according to the draw image setting.
1372     case PARAM_DI: {
1373       // Get new value
1374       VikLayerParamData vlpd = a_uibuilder_widget_get_value ( widget, values[UI_CHG_PARAM] );
1375       GtkWidget **ww1 = values[UI_CHG_WIDGETS];
1376       GtkWidget **ww2 = values[UI_CHG_LABELS];
1377       GtkWidget *w1 = ww1[OFFSET + PARAM_IS];
1378       GtkWidget *w2 = ww2[OFFSET + PARAM_IS];
1379       GtkWidget *w3 = ww1[OFFSET + PARAM_IA];
1380       GtkWidget *w4 = ww2[OFFSET + PARAM_IA];
1381       GtkWidget *w5 = ww1[OFFSET + PARAM_ICS];
1382       GtkWidget *w6 = ww2[OFFSET + PARAM_ICS];
1383       if ( w1 ) gtk_widget_set_sensitive ( w1, vlpd.b );
1384       if ( w2 ) gtk_widget_set_sensitive ( w2, vlpd.b );
1385       if ( w3 ) gtk_widget_set_sensitive ( w3, vlpd.b );
1386       if ( w4 ) gtk_widget_set_sensitive ( w4, vlpd.b );
1387       if ( w5 ) gtk_widget_set_sensitive ( w5, vlpd.b );
1388       if ( w6 ) gtk_widget_set_sensitive ( w6, vlpd.b );
1389       break;
1390     }
1391     // Alter sensitivity of waypoint label related widgets according to the draw label setting.
1392     case PARAM_DLA: {
1393       // Get new value
1394       VikLayerParamData vlpd = a_uibuilder_widget_get_value ( widget, values[UI_CHG_PARAM] );
1395       GtkWidget **ww1 = values[UI_CHG_WIDGETS];
1396       GtkWidget **ww2 = values[UI_CHG_LABELS];
1397       GtkWidget *w1 = ww1[OFFSET + PARAM_WPTC];
1398       GtkWidget *w2 = ww2[OFFSET + PARAM_WPTC];
1399       GtkWidget *w3 = ww1[OFFSET + PARAM_WPBC];
1400       GtkWidget *w4 = ww2[OFFSET + PARAM_WPBC];
1401       GtkWidget *w5 = ww1[OFFSET + PARAM_WPBA];
1402       GtkWidget *w6 = ww2[OFFSET + PARAM_WPBA];
1403       GtkWidget *w7 = ww1[OFFSET + PARAM_WPFONTSIZE];
1404       GtkWidget *w8 = ww2[OFFSET + PARAM_WPFONTSIZE];
1405       if ( w1 ) gtk_widget_set_sensitive ( w1, vlpd.b );
1406       if ( w2 ) gtk_widget_set_sensitive ( w2, vlpd.b );
1407       if ( w3 ) gtk_widget_set_sensitive ( w3, vlpd.b );
1408       if ( w4 ) gtk_widget_set_sensitive ( w4, vlpd.b );
1409       if ( w5 ) gtk_widget_set_sensitive ( w5, vlpd.b );
1410       if ( w6 ) gtk_widget_set_sensitive ( w6, vlpd.b );
1411       if ( w7 ) gtk_widget_set_sensitive ( w7, vlpd.b );
1412       if ( w8 ) gtk_widget_set_sensitive ( w8, vlpd.b );
1413       break;
1414     }
1415     // Alter sensitivity of all track colours according to the draw track mode.
1416     case PARAM_DM: {
1417       // Get new value
1418       VikLayerParamData vlpd = a_uibuilder_widget_get_value ( widget, values[UI_CHG_PARAM] );
1419       gboolean sensitive = ( vlpd.u == DRAWMODE_ALL_SAME_COLOR );
1420       GtkWidget **ww1 = values[UI_CHG_WIDGETS];
1421       GtkWidget **ww2 = values[UI_CHG_LABELS];
1422       GtkWidget *w1 = ww1[OFFSET + PARAM_TC];
1423       GtkWidget *w2 = ww2[OFFSET + PARAM_TC];
1424       if ( w1 ) gtk_widget_set_sensitive ( w1, sensitive );
1425       if ( w2 ) gtk_widget_set_sensitive ( w2, sensitive );
1426       break;
1427     }
1428     case PARAM_MDTIME: {
1429       // Force metadata->timestamp to be always read-only for now.
1430       GtkWidget **ww = values[UI_CHG_WIDGETS];
1431       GtkWidget *w1 = ww[OFFSET + PARAM_MDTIME];
1432       if ( w1 ) gtk_widget_set_sensitive ( w1, FALSE );
1433     }
1434     // NB Since other track settings have been split across tabs,
1435     // I don't think it's useful to set sensitivities on widgets you can't immediately see
1436     default: break;
1437   }
1438 }
1439
1440 static void trw_layer_marshall( VikTrwLayer *vtl, guint8 **data, gint *len )
1441 {
1442   guint8 *pd;
1443   gint pl;
1444
1445   *data = NULL;
1446
1447   // Use byte arrays to store sublayer data
1448   // much like done elsewhere e.g. vik_layer_marshall_params()
1449   GByteArray *ba = g_byte_array_new ( );
1450
1451   guint8 *sl_data;
1452   guint sl_len;
1453
1454   guint object_length;
1455   guint subtype;
1456   // store:
1457   // the length of the item
1458   // the sublayer type of item
1459   // the the actual item
1460 #define tlm_append(object_pointer, size, type)  \
1461   subtype = (type); \
1462   object_length = (size); \
1463   g_byte_array_append ( ba, (guint8 *)&object_length, sizeof(object_length) ); \
1464   g_byte_array_append ( ba, (guint8 *)&subtype, sizeof(subtype) ); \
1465   g_byte_array_append ( ba, (object_pointer), object_length );
1466
1467   // Layer parameters first
1468   vik_layer_marshall_params(VIK_LAYER(vtl), &pd, &pl);
1469   g_byte_array_append ( ba, (guint8 *)&pl, sizeof(pl) ); \
1470   g_byte_array_append ( ba, pd, pl );
1471   g_free ( pd );
1472
1473   // Now sublayer data
1474   GHashTableIter iter;
1475   gpointer key, value;
1476
1477   // Waypoints
1478   g_hash_table_iter_init ( &iter, vtl->waypoints );
1479   while ( g_hash_table_iter_next (&iter, &key, &value) ) {
1480     vik_waypoint_marshall ( VIK_WAYPOINT(value), &sl_data, &sl_len );
1481     tlm_append ( sl_data, sl_len, VIK_TRW_LAYER_SUBLAYER_WAYPOINT );
1482     g_free ( sl_data );
1483   }
1484
1485   // Tracks
1486   g_hash_table_iter_init ( &iter, vtl->tracks );
1487   while ( g_hash_table_iter_next (&iter, &key, &value) ) {
1488     vik_track_marshall ( VIK_TRACK(value), &sl_data, &sl_len );
1489     tlm_append ( sl_data, sl_len, VIK_TRW_LAYER_SUBLAYER_TRACK );
1490     g_free ( sl_data );
1491   }
1492
1493   // Routes
1494   g_hash_table_iter_init ( &iter, vtl->routes );
1495   while ( g_hash_table_iter_next (&iter, &key, &value) ) {
1496     vik_track_marshall ( VIK_TRACK(value), &sl_data, &sl_len );
1497     tlm_append ( sl_data, sl_len, VIK_TRW_LAYER_SUBLAYER_ROUTE );
1498     g_free ( sl_data );
1499   }
1500
1501 #undef tlm_append
1502
1503   *data = ba->data;
1504   *len = ba->len;
1505 }
1506
1507 static VikTrwLayer *trw_layer_unmarshall( guint8 *data, gint len, VikViewport *vvp )
1508 {
1509   VikTrwLayer *vtl = VIK_TRW_LAYER(vik_layer_create ( VIK_LAYER_TRW, vvp, FALSE ));
1510   gint pl;
1511   gint consumed_length;
1512
1513   // First the overall layer parameters
1514   memcpy(&pl, data, sizeof(pl));
1515   data += sizeof(pl);
1516   vik_layer_unmarshall_params ( VIK_LAYER(vtl), data, pl, vvp );
1517   data += pl;
1518
1519   consumed_length = pl;
1520   const gint sizeof_len_and_subtype = sizeof(gint) + sizeof(gint);
1521
1522 #define tlm_size (*(gint *)data)
1523   // See marshalling above for order of how this is written
1524 #define tlm_next \
1525   data += sizeof_len_and_subtype + tlm_size;
1526
1527   // Now the individual sublayers:
1528
1529   while ( *data && consumed_length < len ) {
1530     // Normally four extra bytes at the end of the datastream
1531     //  (since it's a GByteArray and that's where it's length is stored)
1532     //  So only attempt read when there's an actual block of sublayer data
1533     if ( consumed_length + tlm_size < len ) {
1534
1535       // Reuse pl to read the subtype from the data stream
1536       memcpy(&pl, data+sizeof(gint), sizeof(pl));
1537
1538       // Also remember to (attempt to) convert each coordinate in case this is pasted into a different drawmode
1539       if ( pl == VIK_TRW_LAYER_SUBLAYER_TRACK ) {
1540         VikTrack *trk = vik_track_unmarshall ( data + sizeof_len_and_subtype, 0 );
1541         gchar *name = g_strdup ( trk->name );
1542         vik_trw_layer_add_track ( vtl, name, trk );
1543         g_free ( name );
1544         vik_track_convert (trk, vtl->coord_mode);
1545       }
1546       if ( pl == VIK_TRW_LAYER_SUBLAYER_WAYPOINT ) {
1547         VikWaypoint *wp = vik_waypoint_unmarshall ( data + sizeof_len_and_subtype, 0 );
1548         gchar *name = g_strdup ( wp->name );
1549         vik_trw_layer_add_waypoint ( vtl, name, wp );
1550         g_free ( name );
1551         waypoint_convert (NULL, wp, &vtl->coord_mode);
1552       }
1553       if ( pl == VIK_TRW_LAYER_SUBLAYER_ROUTE ) {
1554         VikTrack *trk = vik_track_unmarshall ( data + sizeof_len_and_subtype, 0 );
1555         gchar *name = g_strdup ( trk->name );
1556         vik_trw_layer_add_route ( vtl, name, trk );
1557         g_free ( name );
1558         vik_track_convert (trk, vtl->coord_mode);
1559       }
1560     }
1561     consumed_length += tlm_size + sizeof_len_and_subtype;
1562     tlm_next;
1563   }
1564   //g_debug ("consumed_length %d vs len %d", consumed_length, len);
1565
1566   // Not stored anywhere else so need to regenerate
1567   trw_layer_calculate_bounds_waypoints ( vtl );
1568
1569   return vtl;
1570 }
1571
1572 // Keep interesting hash function at least visible
1573 /*
1574 static guint strcase_hash(gconstpointer v)
1575 {
1576   // 31 bit hash function
1577   int i;
1578   const gchar *t = v;
1579   gchar s[128];   // malloc is too slow for reading big files
1580   gchar *p = s;
1581
1582   for (i = 0; (i < (sizeof(s)- 1)) && t[i]; i++)
1583       p[i] = toupper(t[i]);
1584   p[i] = '\0';
1585
1586   p = s;
1587   guint32 h = *p;
1588   if (h) {
1589     for (p += 1; *p != '\0'; p++)
1590       h = (h << 5) - h + *p;
1591   }
1592
1593   return h;  
1594 }
1595 */
1596
1597 // Stick a 1 at the end of the function name to make it more unique
1598 //  thus more easily searchable in a simple text editor
1599 static VikTrwLayer* trw_layer_new1 ( VikViewport *vvp )
1600 {
1601   VikTrwLayer *rv = VIK_TRW_LAYER ( g_object_new ( VIK_TRW_LAYER_TYPE, NULL ) );
1602   vik_layer_set_type ( VIK_LAYER(rv), VIK_LAYER_TRW );
1603
1604   // It's not entirely clear the benefits of hash tables usage here - possibly the simplicity of first implementation for unique names
1605   // Now with the name of the item stored as part of the item - these tables are effectively straightforward lists
1606
1607   // For this reworking I've choosen to keep the use of hash tables since for the expected data sizes
1608   // - even many hundreds of waypoints and tracks is quite small in the grand scheme of things,
1609   //  and with normal PC processing capabilities - it has negligibile performance impact
1610   // This also minimized the amount of rework - as the management of the hash tables already exists.
1611
1612   // The hash tables are indexed by simple integers acting as a UUID hash, which again shouldn't affect performance much
1613   //   we have to maintain a uniqueness (as before when multiple names where not allowed),
1614   //   this is to ensure it refers to the same item in the data structures used on the viewport and on the layers panel
1615
1616   rv->waypoints = g_hash_table_new_full ( g_direct_hash, g_direct_equal, NULL, (GDestroyNotify) vik_waypoint_free );
1617   rv->waypoints_iters = g_hash_table_new_full ( g_direct_hash, g_direct_equal, NULL, g_free );
1618   rv->tracks = g_hash_table_new_full ( g_direct_hash, g_direct_equal, NULL, (GDestroyNotify) vik_track_free );
1619   rv->tracks_iters = g_hash_table_new_full ( g_direct_hash, g_direct_equal, NULL, g_free );
1620   rv->routes = g_hash_table_new_full ( g_direct_hash, g_direct_equal, NULL, (GDestroyNotify) vik_track_free );
1621   rv->routes_iters = g_hash_table_new_full ( g_direct_hash, g_direct_equal, NULL, g_free );
1622
1623   rv->image_cache = g_queue_new(); // Must be performed before set_params via set_defaults
1624
1625   vik_layer_set_defaults ( VIK_LAYER(rv), vvp );
1626
1627   // Param settings that are not available via the GUI
1628   // Force to on after processing params (which defaults them to off with a zero value)
1629   rv->waypoints_visible = rv->tracks_visible = rv->routes_visible = TRUE;
1630
1631   rv->metadata = vik_trw_metadata_new ();
1632   rv->draw_sync_done = TRUE;
1633   rv->draw_sync_do = TRUE;
1634   rv->coord_mode = VIK_COORD_LATLON;
1635   // Everything else is 0, FALSE or NULL
1636
1637   return rv;
1638 }
1639
1640
1641 static void trw_layer_free ( VikTrwLayer *trwlayer )
1642 {
1643   g_hash_table_destroy(trwlayer->waypoints);
1644   g_hash_table_destroy(trwlayer->waypoints_iters);
1645   g_hash_table_destroy(trwlayer->tracks);
1646   g_hash_table_destroy(trwlayer->tracks_iters);
1647   g_hash_table_destroy(trwlayer->routes);
1648   g_hash_table_destroy(trwlayer->routes_iters);
1649
1650   /* ODC: replace with GArray */
1651   trw_layer_free_track_gcs ( trwlayer );
1652
1653   if ( trwlayer->wp_right_click_menu )
1654     g_object_ref_sink ( G_OBJECT(trwlayer->wp_right_click_menu) );
1655
1656   if ( trwlayer->track_right_click_menu )
1657     g_object_ref_sink ( G_OBJECT(trwlayer->track_right_click_menu) );
1658
1659   if ( trwlayer->tracklabellayout != NULL)
1660     g_object_unref ( G_OBJECT ( trwlayer->tracklabellayout ) );
1661
1662   if ( trwlayer->wplabellayout != NULL)
1663     g_object_unref ( G_OBJECT ( trwlayer->wplabellayout ) );
1664
1665   if ( trwlayer->waypoint_gc != NULL )
1666     g_object_unref ( G_OBJECT ( trwlayer->waypoint_gc ) );
1667
1668   if ( trwlayer->waypoint_text_gc != NULL )
1669     g_object_unref ( G_OBJECT ( trwlayer->waypoint_text_gc ) );
1670
1671   if ( trwlayer->waypoint_bg_gc != NULL )
1672     g_object_unref ( G_OBJECT ( trwlayer->waypoint_bg_gc ) );
1673
1674   g_free ( trwlayer->wp_fsize_str );
1675   g_free ( trwlayer->track_fsize_str );
1676
1677   if ( trwlayer->tpwin != NULL )
1678     gtk_widget_destroy ( GTK_WIDGET(trwlayer->tpwin) );
1679
1680   if ( trwlayer->tracks_analysis_dialog != NULL )
1681     gtk_widget_destroy ( GTK_WIDGET(trwlayer->tracks_analysis_dialog) );
1682
1683   image_cache_free ( trwlayer );
1684 }
1685
1686 static void init_drawing_params ( struct DrawingParams *dp, VikTrwLayer *vtl, VikViewport *vp, gboolean highlight )
1687 {
1688   dp->vtl = vtl;
1689   dp->vp = vp;
1690   dp->highlight = highlight;
1691   dp->vw = (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(dp->vtl);
1692   dp->xmpp = vik_viewport_get_xmpp ( vp );
1693   dp->ympp = vik_viewport_get_ympp ( vp );
1694   dp->width = vik_viewport_get_width ( vp );
1695   dp->height = vik_viewport_get_height ( vp );
1696   dp->cc = vtl->drawdirections_size*cos(DEG2RAD(45)); // Calculate once per vtl update - even if not used
1697   dp->ss = vtl->drawdirections_size*sin(DEG2RAD(45)); // Calculate once per vtl update - even if not used
1698
1699   dp->center = vik_viewport_get_center ( vp );
1700   dp->one_zone = vik_viewport_is_one_zone ( vp ); /* false if some other projection besides UTM */
1701   dp->lat_lon = vik_viewport_get_coord_mode ( vp ) == VIK_COORD_LATLON;
1702
1703   if ( dp->one_zone )
1704   {
1705     gint w2, h2;
1706     w2 = dp->xmpp * (dp->width / 2) + 1600 / dp->xmpp; 
1707     h2 = dp->ympp * (dp->height / 2) + 1600 / dp->ympp;
1708     /* leniency -- for tracks. Obviously for waypoints this SHOULD be a lot smaller */
1709  
1710     dp->ce1 = dp->center->east_west-w2; 
1711     dp->ce2 = dp->center->east_west+w2;
1712     dp->cn1 = dp->center->north_south-h2;
1713     dp->cn2 = dp->center->north_south+h2;
1714   } else if ( dp->lat_lon ) {
1715     VikCoord upperleft, bottomright;
1716     /* quick & dirty calculation; really want to check all corners due to lat/lon smaller at top in northern hemisphere */
1717     /* this also DOESN'T WORK if you are crossing 180/-180 lon. I don't plan to in the near future...  */
1718     vik_viewport_screen_to_coord ( vp, -500, -500, &upperleft );
1719     vik_viewport_screen_to_coord ( vp, dp->width+500, dp->height+500, &bottomright );
1720     dp->ce1 = upperleft.east_west;
1721     dp->ce2 = bottomright.east_west;
1722     dp->cn1 = bottomright.north_south;
1723     dp->cn2 = upperleft.north_south;
1724   }
1725
1726   vik_viewport_get_min_max_lat_lon ( vp, &(dp->bbox.south), &(dp->bbox.north), &(dp->bbox.west), &(dp->bbox.east) );
1727 }
1728
1729 /*
1730  * Determine the colour of the trackpoint (and/or trackline) relative to the average speed
1731  * Here a simple traffic like light colour system is used:
1732  *  . slow points are red
1733  *  . average is yellow
1734  *  . fast points are green
1735  */
1736 static gint track_section_colour_by_speed ( VikTrwLayer *vtl, VikTrackpoint *tp1, VikTrackpoint *tp2, gdouble average_speed, gdouble low_speed, gdouble high_speed )
1737 {
1738   gdouble rv = 0;
1739   if ( tp1->has_timestamp && tp2->has_timestamp ) {
1740     if ( average_speed > 0 ) {
1741       rv = ( vik_coord_diff ( &(tp1->coord), &(tp2->coord) ) / (tp1->timestamp - tp2->timestamp) );
1742       if ( rv < low_speed )
1743         return VIK_TRW_LAYER_TRACK_GC_SLOW;
1744       else if ( rv > high_speed )
1745         return VIK_TRW_LAYER_TRACK_GC_FAST;
1746       else
1747         return VIK_TRW_LAYER_TRACK_GC_AVER;
1748     }
1749   }
1750   return VIK_TRW_LAYER_TRACK_GC_BLACK;
1751 }
1752
1753 static void draw_utm_skip_insignia ( VikViewport *vvp, GdkGC *gc, gint x, gint y )
1754 {
1755   vik_viewport_draw_line ( vvp, gc, x+5, y, x-5, y );
1756   vik_viewport_draw_line ( vvp, gc, x, y+5, x, y-5 );
1757   vik_viewport_draw_line ( vvp, gc, x+5, y+5, x-5, y-5 );
1758   vik_viewport_draw_line ( vvp, gc, x+5, y-5, x-5, y+5 );
1759 }
1760
1761
1762 static void trw_layer_draw_track_label ( gchar *name, gchar *fgcolour, gchar *bgcolour, struct DrawingParams *dp, VikCoord *coord )
1763 {
1764   gchar *label_markup = g_strdup_printf ( "<span foreground=\"%s\" background=\"%s\" size=\"%s\">%s</span>", fgcolour, bgcolour, dp->vtl->track_fsize_str, name );
1765
1766   if ( pango_parse_markup ( label_markup, -1, 0, NULL, NULL, NULL, NULL ) )
1767     pango_layout_set_markup ( dp->vtl->tracklabellayout, label_markup, -1 );
1768   else
1769     // Fallback if parse failure
1770     pango_layout_set_text ( dp->vtl->tracklabellayout, name, -1 );
1771
1772   g_free ( label_markup );
1773
1774   gint label_x, label_y;
1775   gint width, height;
1776   pango_layout_get_pixel_size ( dp->vtl->tracklabellayout, &width, &height );
1777
1778   vik_viewport_coord_to_screen ( dp->vp, coord, &label_x, &label_y );
1779   vik_viewport_draw_layout ( dp->vp, dp->vtl->track_bg_gc, label_x-width/2, label_y-height/2, dp->vtl->tracklabellayout );
1780 }
1781
1782 /**
1783  * distance_in_preferred_units:
1784  * @dist: The source distance in standard SI Units (i.e. metres)
1785  *
1786  * TODO: This is a generic function that could be moved into globals.c or utils.c
1787  *
1788  * Probably best used if you have a only few conversions to perform.
1789  * However if doing many points (such as on all points along a track) then this may be a bit slow,
1790  *  since it will be doing the preference check on each call
1791  *
1792  * Returns: The distance in the units as specified by the preferences
1793  */
1794 static gdouble distance_in_preferred_units ( gdouble dist )
1795 {
1796   gdouble mydist;
1797   vik_units_distance_t dist_units = a_vik_get_units_distance ();
1798   switch (dist_units) {
1799   case VIK_UNITS_DISTANCE_MILES:
1800     mydist = VIK_METERS_TO_MILES(dist);
1801     break;
1802   case VIK_UNITS_DISTANCE_NAUTICAL_MILES:
1803     mydist = VIK_METERS_TO_NAUTICAL_MILES(dist);
1804     break;
1805   // VIK_UNITS_DISTANCE_KILOMETRES:
1806   default:
1807     mydist = dist/1000.0;
1808     break;
1809   }
1810   return mydist;
1811 }
1812
1813 /**
1814  * trw_layer_draw_dist_labels:
1815  *
1816  * Draw a few labels along a track at nicely seperated distances
1817  * This might slow things down if there's many tracks being displayed with this on.
1818  */
1819 static void trw_layer_draw_dist_labels ( struct DrawingParams *dp, VikTrack *trk, gboolean drawing_highlight )
1820 {
1821   static const gdouble chunksd[] = {0.25, 0.5, 1.0, 2.0, 5.0, 10.0, 15.0, 20.0,
1822                                     25.0, 40.0, 50.0, 75.0, 100.0,
1823                                     150.0, 200.0, 250.0, 500.0, 1000.0};
1824
1825   gdouble dist = vik_track_get_length_including_gaps ( trk ) / (trk->max_number_dist_labels+1);
1826
1827   // Convert to specified unit to find the friendly breakdown value
1828   dist = distance_in_preferred_units ( dist );
1829
1830   gint index = 0;
1831   gint i=0;
1832   for ( i = 0; i < G_N_ELEMENTS(chunksd); i++ ) {
1833     if ( chunksd[i] > dist ) {
1834       index = i;
1835       dist = chunksd[index];
1836       break;
1837     }
1838   }
1839
1840   vik_units_distance_t dist_units = a_vik_get_units_distance ();
1841
1842   for ( i = 1; i < trk->max_number_dist_labels+1; i++ ) {
1843     gdouble dist_i = dist * i;
1844
1845     // Convert distance back into metres for use in finding a trackpoint
1846     switch (dist_units) {
1847     case VIK_UNITS_DISTANCE_MILES:
1848       dist_i = VIK_MILES_TO_METERS(dist_i);
1849       break;
1850     case VIK_UNITS_DISTANCE_NAUTICAL_MILES:
1851       dist_i = VIK_NAUTICAL_MILES_TO_METERS(dist_i);
1852       break;
1853       // VIK_UNITS_DISTANCE_KILOMETRES:
1854     default:
1855       dist_i = dist_i*1000.0;
1856       break;
1857     }
1858
1859     gdouble dist_current = 0.0;
1860     VikTrackpoint *tp_current = vik_track_get_tp_by_dist ( trk, dist_i, FALSE, &dist_current );
1861     gdouble dist_next = 0.0;
1862     VikTrackpoint *tp_next = vik_track_get_tp_by_dist ( trk, dist_i, TRUE, &dist_next );
1863
1864     gdouble dist_between_tps = fabs (dist_next - dist_current);
1865     gdouble ratio = 0.0;
1866     // Prevent division by 0 errors
1867     if ( dist_between_tps > 0.0 )
1868       ratio = fabs(dist_i-dist_current)/dist_between_tps;
1869
1870     if ( tp_current && tp_next ) {
1871       // Construct the name based on the distance value
1872       gchar *name;
1873       gchar *units;
1874       switch (dist_units) {
1875       case VIK_UNITS_DISTANCE_MILES:
1876         units = g_strdup ( _("miles") );
1877         break;
1878       case VIK_UNITS_DISTANCE_NAUTICAL_MILES:
1879         units = g_strdup ( _("NM") );
1880         break;
1881         // VIK_UNITS_DISTANCE_KILOMETRES:
1882       default:
1883         units = g_strdup ( _("km") );
1884         break;
1885       }
1886
1887       // Convert for display
1888       dist_i = distance_in_preferred_units ( dist_i );
1889
1890       // Make the precision of the output related to the unit size.
1891       if ( index == 0 )
1892         name = g_strdup_printf ( "%.2f %s", dist_i, units);
1893       else if ( index == 1 )
1894         name = g_strdup_printf ( "%.1f %s", dist_i, units);
1895       else
1896         name = g_strdup_printf ( "%d %s", (gint)round(dist_i), units); // TODO single vs plurals
1897       g_free ( units );
1898
1899       struct LatLon ll_current, ll_next;
1900       vik_coord_to_latlon ( &tp_current->coord, &ll_current );
1901       vik_coord_to_latlon ( &tp_next->coord, &ll_next );
1902
1903       // positional interpolation
1904       // Using a simple ratio - may not be perfectly correct due to lat/long projections
1905       //  but should be good enough over the small scale that I anticipate usage on
1906       struct LatLon ll_new = { ll_current.lat + (ll_next.lat-ll_current.lat)*ratio,
1907                                ll_current.lon + (ll_next.lon-ll_current.lon)*ratio };
1908       VikCoord coord;
1909       vik_coord_load_from_latlon ( &coord, dp->vtl->coord_mode, &ll_new );
1910
1911       gchar *fgcolour;
1912       if ( dp->vtl->drawmode == DRAWMODE_BY_TRACK )
1913         fgcolour = gdk_color_to_string ( &(trk->color) );
1914       else
1915         fgcolour = gdk_color_to_string ( &(dp->vtl->track_color) );
1916
1917       // if highlight mode on, then colour the background in the highlight colour
1918       gchar *bgcolour;
1919       if ( drawing_highlight )
1920         bgcolour = g_strdup ( vik_viewport_get_highlight_color ( dp->vp ) );
1921       else
1922         bgcolour = gdk_color_to_string ( &(dp->vtl->track_bg_color) );
1923
1924       trw_layer_draw_track_label ( name, fgcolour, bgcolour, dp, &coord );
1925
1926       g_free ( fgcolour );
1927       g_free ( bgcolour );
1928       g_free ( name );
1929     }
1930   }
1931 }
1932
1933 /**
1934  * trw_layer_draw_track_name_labels:
1935  *
1936  * Draw a label (or labels) for the track name somewhere depending on the track's properties
1937  */
1938 static void trw_layer_draw_track_name_labels ( struct DrawingParams *dp, VikTrack *trk, gboolean drawing_highlight )
1939 {
1940   gchar *fgcolour;
1941   if ( dp->vtl->drawmode == DRAWMODE_BY_TRACK )
1942     fgcolour = gdk_color_to_string ( &(trk->color) );
1943   else
1944     fgcolour = gdk_color_to_string ( &(dp->vtl->track_color) );
1945
1946   // if highlight mode on, then colour the background in the highlight colour
1947   gchar *bgcolour;
1948   if ( drawing_highlight )
1949     bgcolour = g_strdup ( vik_viewport_get_highlight_color ( dp->vp ) );
1950   else
1951     bgcolour = gdk_color_to_string ( &(dp->vtl->track_bg_color) );
1952
1953   gchar *ename = g_markup_escape_text ( trk->name, -1 );
1954
1955   if ( trk->draw_name_mode == TRACK_DRAWNAME_START_END_CENTRE ||
1956        trk->draw_name_mode == TRACK_DRAWNAME_CENTRE ) {
1957     struct LatLon average, maxmin[2] = { {0,0}, {0,0} };
1958     trw_layer_find_maxmin_tracks ( NULL, trk, maxmin );
1959     average.lat = (maxmin[0].lat+maxmin[1].lat)/2;
1960     average.lon = (maxmin[0].lon+maxmin[1].lon)/2;
1961     VikCoord coord;
1962     vik_coord_load_from_latlon ( &coord, dp->vtl->coord_mode, &average );
1963
1964     trw_layer_draw_track_label ( ename, fgcolour, bgcolour, dp, &coord );
1965   }
1966
1967   if ( trk->draw_name_mode == TRACK_DRAWNAME_CENTRE )
1968     // No other labels to draw
1969     return;
1970
1971   VikTrackpoint *tp_end = vik_track_get_tp_last ( trk );
1972   if ( !tp_end )
1973     return;
1974   VikTrackpoint *tp_begin = vik_track_get_tp_first ( trk );
1975   if ( !tp_begin )
1976     return;
1977   VikCoord begin_coord = tp_begin->coord;
1978   VikCoord end_coord = tp_end->coord;
1979
1980   gboolean done_start_end = FALSE;
1981
1982   if ( trk->draw_name_mode == TRACK_DRAWNAME_START_END ||
1983        trk->draw_name_mode == TRACK_DRAWNAME_START_END_CENTRE ) {
1984
1985     // This number can be configured via the settings if you really want to change it
1986     gdouble distance_diff;
1987     if ( ! a_settings_get_double ( "trackwaypoint_start_end_distance_diff", &distance_diff ) )
1988       distance_diff = 100.0; // Metres
1989
1990     if ( vik_coord_diff ( &begin_coord, &end_coord ) < distance_diff ) {
1991       // Start and end 'close' together so only draw one label at an average location
1992       gint x1, x2, y1, y2;
1993       vik_viewport_coord_to_screen ( dp->vp, &begin_coord, &x1, &y1);
1994       vik_viewport_coord_to_screen ( dp->vp, &end_coord, &x2, &y2);
1995       VikCoord av_coord;
1996       vik_viewport_screen_to_coord ( dp->vp, (x1 + x2) / 2, (y1 + y2) / 2, &av_coord );
1997
1998       gchar *name = g_strdup_printf ( "%s: %s", ename, _("start/end") );
1999       trw_layer_draw_track_label ( name, fgcolour, bgcolour, dp, &av_coord );
2000       g_free ( name );
2001
2002       done_start_end = TRUE;
2003     }
2004   }
2005
2006   if ( ! done_start_end ) {
2007     if ( trk->draw_name_mode == TRACK_DRAWNAME_START ||
2008          trk->draw_name_mode == TRACK_DRAWNAME_START_END ||
2009          trk->draw_name_mode == TRACK_DRAWNAME_START_END_CENTRE ) {
2010       gchar *name_start = g_strdup_printf ( "%s: %s", ename, _("start") );
2011       trw_layer_draw_track_label ( name_start, fgcolour, bgcolour, dp, &begin_coord );
2012       g_free ( name_start );
2013     }
2014     // Don't draw end label if this is the one being created
2015     if ( trk != dp->vtl->current_track ) {
2016       if ( trk->draw_name_mode == TRACK_DRAWNAME_END ||
2017            trk->draw_name_mode == TRACK_DRAWNAME_START_END ||
2018            trk->draw_name_mode == TRACK_DRAWNAME_START_END_CENTRE ) {
2019         gchar *name_end = g_strdup_printf ( "%s: %s", ename, _("end") );
2020         trw_layer_draw_track_label ( name_end, fgcolour, bgcolour, dp, &end_coord );
2021         g_free ( name_end );
2022       }
2023     }
2024   }
2025
2026   g_free ( fgcolour );
2027   g_free ( bgcolour );
2028   g_free ( ename );
2029 }
2030
2031
2032 /**
2033  * trw_layer_draw_point_names:
2034  *
2035  * Draw a point labels along a track
2036  * This might slow things down if there's many tracks being displayed with this on.
2037  */
2038 static void trw_layer_draw_point_names ( struct DrawingParams *dp, VikTrack *trk, gboolean drawing_highlight )
2039 {
2040   GList *list = trk->trackpoints;
2041   if (!list) return;
2042   VikTrackpoint *tp = VIK_TRACKPOINT(list->data);
2043   gchar *fgcolour;
2044   if ( dp->vtl->drawmode == DRAWMODE_BY_TRACK )
2045     fgcolour = gdk_color_to_string ( &(trk->color) );
2046   else
2047     fgcolour = gdk_color_to_string ( &(dp->vtl->track_color) );
2048   gchar *bgcolour;
2049   if ( drawing_highlight )
2050     bgcolour = g_strdup ( vik_viewport_get_highlight_color ( dp->vp ) );
2051   else
2052     bgcolour = gdk_color_to_string ( &(dp->vtl->track_bg_color) );
2053   if ( tp->name )
2054     trw_layer_draw_track_label ( tp->name, fgcolour, bgcolour, dp, &tp->coord );
2055   while ((list = g_list_next(list)))
2056   {
2057     tp = VIK_TRACKPOINT(list->data);
2058     if ( tp->name )
2059       trw_layer_draw_track_label ( tp->name, fgcolour, bgcolour, dp, &tp->coord );
2060   };
2061   g_free ( fgcolour );
2062   g_free ( bgcolour );
2063 }
2064
2065 static void trw_layer_draw_track ( const gpointer id, VikTrack *track, struct DrawingParams *dp, gboolean draw_track_outline )
2066 {
2067   if ( ! track->visible )
2068     return;
2069
2070   /* TODO: this function is a mess, get rid of any redundancy */
2071   GList *list = track->trackpoints;
2072   GdkGC *main_gc;
2073   gboolean useoldvals = TRUE;
2074
2075   gboolean drawpoints;
2076   gboolean drawstops;
2077   gboolean drawelevation;
2078   gdouble min_alt, max_alt, alt_diff = 0;
2079
2080   const guint8 tp_size_reg = dp->vtl->drawpoints_size;
2081   const guint8 tp_size_cur = dp->vtl->drawpoints_size*2;
2082   guint8 tp_size;
2083
2084   if ( dp->vtl->drawelevation )
2085   {
2086     /* assume if it has elevation at the beginning, it has it throughout. not ness a true good assumption */
2087     if ( ( drawelevation = vik_track_get_minmax_alt ( track, &min_alt, &max_alt ) ) )
2088       alt_diff = max_alt - min_alt;
2089   }
2090
2091   /* admittedly this is not an efficient way to do it because we go through the whole GC thing all over... */
2092   if ( dp->vtl->bg_line_thickness && !draw_track_outline )
2093     trw_layer_draw_track ( id, track, dp, TRUE );
2094
2095   if ( draw_track_outline )
2096     drawpoints = drawstops = FALSE;
2097   else {
2098     drawpoints = dp->vtl->drawpoints;
2099     drawstops = dp->vtl->drawstops;
2100   }
2101
2102   gboolean drawing_highlight = FALSE;
2103   /* Current track - used for creation */
2104   if ( track == dp->vtl->current_track )
2105     main_gc = dp->vtl->current_track_gc;
2106   else {
2107     if ( dp->highlight ) {
2108       /* Draw all tracks of the layer in special colour
2109          NB this supercedes the drawmode */
2110       main_gc = vik_viewport_get_gc_highlight (dp->vp);
2111       drawing_highlight = TRUE;
2112     }
2113     if ( !drawing_highlight ) {
2114       // Still need to figure out the gc according to the drawing mode:
2115       switch ( dp->vtl->drawmode ) {
2116       case DRAWMODE_BY_TRACK:
2117         if ( dp->vtl->track_1color_gc )
2118           g_object_unref ( dp->vtl->track_1color_gc );
2119         dp->vtl->track_1color_gc = vik_viewport_new_gc_from_color ( dp->vp, &track->color, dp->vtl->line_thickness );
2120         main_gc = dp->vtl->track_1color_gc;
2121         break;
2122       default:
2123         // Mostly for DRAWMODE_ALL_SAME_COLOR
2124         // but includes DRAWMODE_BY_SPEED, main_gc is set later on as necessary
2125         main_gc = g_array_index(dp->vtl->track_gc, GdkGC *, VIK_TRW_LAYER_TRACK_GC_SINGLE);
2126         break;
2127       }
2128     }
2129   }
2130
2131   if (list) {
2132     int x, y, oldx, oldy;
2133     VikTrackpoint *tp = VIK_TRACKPOINT(list->data);
2134   
2135     tp_size = (list == dp->vtl->current_tpl) ? tp_size_cur : tp_size_reg;
2136
2137     vik_viewport_coord_to_screen ( dp->vp, &(tp->coord), &x, &y );
2138
2139     // Draw the first point as something a bit different from the normal points
2140     // ATM it's slightly bigger and a triangle
2141     if ( drawpoints ) {
2142       GdkPoint trian[3] = { { x, y-(3*tp_size) }, { x-(2*tp_size), y+(2*tp_size) }, {x+(2*tp_size), y+(2*tp_size)} };
2143       vik_viewport_draw_polygon ( dp->vp, main_gc, TRUE, trian, 3 );
2144     }
2145
2146     oldx = x;
2147     oldy = y;
2148
2149     gdouble average_speed = 0.0;
2150     gdouble low_speed = 0.0;
2151     gdouble high_speed = 0.0;
2152     // If necessary calculate these values - which is done only once per track redraw
2153     if ( dp->vtl->drawmode == DRAWMODE_BY_SPEED ) {
2154       // the percentage factor away from the average speed determines transistions between the levels
2155       average_speed = vik_track_get_average_speed_moving(track, dp->vtl->stop_length);
2156       low_speed = average_speed - (average_speed*(dp->vtl->track_draw_speed_factor/100.0));
2157       high_speed = average_speed + (average_speed*(dp->vtl->track_draw_speed_factor/100.0));
2158     }
2159
2160     while ((list = g_list_next(list)))
2161     {
2162       tp = VIK_TRACKPOINT(list->data);
2163       tp_size = (list == dp->vtl->current_tpl) ? tp_size_cur : tp_size_reg;
2164
2165       VikTrackpoint *tp2 = VIK_TRACKPOINT(list->prev->data);
2166       // See if in a different lat/lon 'quadrant' so don't draw massively long lines (presumably wrong way around the Earth)
2167       //  Mainly to prevent wrong lines drawn when a track crosses the 180 degrees East-West longitude boundary
2168       //  (since vik_viewport_draw_line() only copes with pixel value and has no concept of the globe)
2169       if ( dp->lat_lon &&
2170            (( tp2->coord.east_west < -90.0 && tp->coord.east_west > 90.0 ) ||
2171             ( tp2->coord.east_west > 90.0 && tp->coord.east_west < -90.0 )) ) {
2172         useoldvals = FALSE;
2173         continue;
2174       }
2175       /* check some stuff -- but only if we're in UTM and there's only ONE ZONE; or lat lon */
2176       if ( (!dp->one_zone && !dp->lat_lon) ||     /* UTM & zones; do everything */
2177              ( ((!dp->one_zone) || tp->coord.utm_zone == dp->center->utm_zone) &&   /* only check zones if UTM & one_zone */
2178              tp->coord.east_west < dp->ce2 && tp->coord.east_west > dp->ce1 &&  /* both UTM and lat lon */
2179              tp->coord.north_south > dp->cn1 && tp->coord.north_south < dp->cn2 ) )
2180       {
2181         vik_viewport_coord_to_screen ( dp->vp, &(tp->coord), &x, &y );
2182
2183         /*
2184          * If points are the same in display coordinates, don't draw.
2185          */
2186         if ( useoldvals && x == oldx && y == oldy )
2187         {
2188           // Still need to process points to ensure 'stops' are drawn if required
2189           if ( drawstops && drawpoints && ! draw_track_outline && list->next &&
2190                (VIK_TRACKPOINT(list->next->data)->timestamp - VIK_TRACKPOINT(list->data)->timestamp > dp->vtl->stop_length) )
2191             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 );
2192
2193           goto skip;
2194         }
2195
2196         if ( drawpoints || dp->vtl->drawlines ) {
2197           // setup main_gc for both point and line drawing
2198           if ( !drawing_highlight && (dp->vtl->drawmode == DRAWMODE_BY_SPEED) ) {
2199             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 ) );
2200           }
2201         }
2202
2203         if ( drawpoints && ! draw_track_outline )
2204         {
2205
2206           if ( list->next ) {
2207             /*
2208              * The concept of drawing stops is that a trackpoint
2209              * that is if the next trackpoint has a timestamp far into
2210              * the future, we draw a circle of 6x trackpoint size,
2211              * instead of a rectangle of 2x trackpoint size.
2212              * This is drawn first so the trackpoint will be drawn on top
2213              */
2214             /* stops */
2215             if ( drawstops && VIK_TRACKPOINT(list->next->data)->timestamp - VIK_TRACKPOINT(list->data)->timestamp > dp->vtl->stop_length )
2216               /* Stop point.  Draw 6x circle. Always in redish colour */
2217               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 );
2218
2219             /* Regular point - draw 2x square. */
2220             vik_viewport_draw_rectangle ( dp->vp, main_gc, TRUE, x-tp_size, y-tp_size, 2*tp_size, 2*tp_size );
2221           }
2222           else
2223             /* Final point - draw 4x circle. */
2224             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 );
2225         }
2226
2227         if ((!tp->newsegment) && (dp->vtl->drawlines))
2228         {
2229
2230           /* UTM only: zone check */
2231           if ( drawpoints && dp->vtl->coord_mode == VIK_COORD_UTM && tp->coord.utm_zone != dp->center->utm_zone )
2232             draw_utm_skip_insignia (  dp->vp, main_gc, x, y);
2233
2234           if (!useoldvals)
2235             vik_viewport_coord_to_screen ( dp->vp, &(tp2->coord), &oldx, &oldy );
2236
2237           if ( draw_track_outline ) {
2238             vik_viewport_draw_line ( dp->vp, dp->vtl->track_bg_gc, oldx, oldy, x, y);
2239           }
2240           else {
2241
2242             vik_viewport_draw_line ( dp->vp, main_gc, oldx, oldy, x, y);
2243
2244             if ( dp->vtl->drawelevation && list->next && VIK_TRACKPOINT(list->next->data)->altitude != VIK_DEFAULT_ALTITUDE ) {
2245               GdkPoint tmp[4];
2246               #define FIXALTITUDE(what) ((VIK_TRACKPOINT((what))->altitude-min_alt)/alt_diff*DRAW_ELEVATION_FACTOR*dp->vtl->elevation_factor/dp->xmpp)
2247
2248               tmp[0].x = oldx;
2249               tmp[0].y = oldy;
2250               tmp[1].x = oldx;
2251               tmp[1].y = oldy-FIXALTITUDE(list->data);
2252               tmp[2].x = x;
2253               tmp[2].y = y-FIXALTITUDE(list->next->data);
2254               tmp[3].x = x;
2255               tmp[3].y = y;
2256
2257               GdkGC *tmp_gc;
2258               if ( ((oldx - x) > 0 && (oldy - y) > 0) || ((oldx - x) < 0 && (oldy - y) < 0))
2259                 tmp_gc = gtk_widget_get_style(GTK_WIDGET(dp->vp))->light_gc[3];
2260               else
2261                 tmp_gc = gtk_widget_get_style(GTK_WIDGET(dp->vp))->dark_gc[0];
2262               vik_viewport_draw_polygon ( dp->vp, tmp_gc, TRUE, tmp, 4);
2263
2264               vik_viewport_draw_line ( dp->vp, main_gc, oldx, oldy-FIXALTITUDE(list->data), x, y-FIXALTITUDE(list->next->data));
2265             }
2266           }
2267         }
2268
2269         if ( (!tp->newsegment) && dp->vtl->drawdirections ) {
2270           // Draw an arrow at the mid point to show the direction of the track
2271           // Code is a rework from vikwindow::draw_ruler()
2272           gint midx = (oldx + x) / 2;
2273           gint midy = (oldy + y) / 2;
2274
2275           gdouble len = sqrt ( ((midx-oldx) * (midx-oldx)) + ((midy-oldy) * (midy-oldy)) );
2276           // Avoid divide by zero and ensure at least 1 pixel big
2277           if ( len > 1 ) {
2278             gdouble dx = (oldx - midx) / len;
2279             gdouble dy = (oldy - midy) / len;
2280             vik_viewport_draw_line ( dp->vp, main_gc, midx, midy, midx + (dx * dp->cc + dy * dp->ss), midy + (dy * dp->cc - dx * dp->ss) );
2281             vik_viewport_draw_line ( dp->vp, main_gc, midx, midy, midx + (dx * dp->cc - dy * dp->ss), midy + (dy * dp->cc + dx * dp->ss) );
2282           }
2283         }
2284
2285       skip:
2286         oldx = x;
2287         oldy = y;
2288         useoldvals = TRUE;
2289       }
2290       else {
2291         if (useoldvals && dp->vtl->drawlines && (!tp->newsegment))
2292         {
2293           if ( dp->vtl->coord_mode != VIK_COORD_UTM || tp->coord.utm_zone == dp->center->utm_zone )
2294           {
2295             vik_viewport_coord_to_screen ( dp->vp, &(tp->coord), &x, &y );
2296
2297             if ( !drawing_highlight && (dp->vtl->drawmode == DRAWMODE_BY_SPEED) ) {
2298               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 ));
2299             }
2300
2301             /*
2302              * If points are the same in display coordinates, don't draw.
2303              */
2304             if ( x != oldx || y != oldy )
2305               {
2306                 if ( draw_track_outline )
2307                   vik_viewport_draw_line ( dp->vp, dp->vtl->track_bg_gc, oldx, oldy, x, y);
2308                 else
2309                   vik_viewport_draw_line ( dp->vp, main_gc, oldx, oldy, x, y);
2310               }
2311           }
2312           else 
2313           {
2314             /*
2315              * If points are the same in display coordinates, don't draw.
2316              */
2317             if ( x != oldx && y != oldy )
2318               {
2319                 vik_viewport_coord_to_screen ( dp->vp, &(tp2->coord), &x, &y );
2320                 draw_utm_skip_insignia ( dp->vp, main_gc, x, y );
2321               }
2322           }
2323         }
2324         useoldvals = FALSE;
2325       }
2326     }
2327
2328     // Labels drawn after the trackpoints, so the labels are on top
2329     if ( dp->vtl->track_draw_labels ) {
2330       if ( track->max_number_dist_labels > 0 ) {
2331         trw_layer_draw_dist_labels ( dp, track, drawing_highlight );
2332       }
2333       trw_layer_draw_point_names (dp, track, drawing_highlight );
2334
2335       if ( track->draw_name_mode != TRACK_DRAWNAME_NO ) {
2336         trw_layer_draw_track_name_labels ( dp, track, drawing_highlight );
2337       }
2338     }
2339   }
2340 }
2341
2342 static void trw_layer_draw_track_cb ( const gpointer id, VikTrack *track, struct DrawingParams *dp )
2343 {
2344   if ( BBOX_INTERSECT ( track->bbox, dp->bbox ) ) {
2345     trw_layer_draw_track ( id, track, dp, FALSE );
2346   }
2347 }
2348
2349 static void cached_pixbuf_free ( CachedPixbuf *cp )
2350 {
2351   g_object_unref ( G_OBJECT(cp->pixbuf) );
2352   g_free ( cp->image );
2353 }
2354
2355 static gint cached_pixbuf_cmp ( CachedPixbuf *cp, const gchar *name )
2356 {
2357   return strcmp ( cp->image, name );
2358 }
2359
2360 static void trw_layer_draw_waypoint ( const gpointer id, VikWaypoint *wp, struct DrawingParams *dp )
2361 {
2362   if ( wp->visible )
2363   if ( (!dp->one_zone && !dp->lat_lon) || ( ( dp->lat_lon || wp->coord.utm_zone == dp->center->utm_zone ) && 
2364              wp->coord.east_west < dp->ce2 && wp->coord.east_west > dp->ce1 && 
2365              wp->coord.north_south > dp->cn1 && wp->coord.north_south < dp->cn2 ) )
2366   {
2367     gint x, y;
2368     vik_viewport_coord_to_screen ( dp->vp, &(wp->coord), &x, &y );
2369
2370     /* if in shrunken_cache, get that. If not, get and add to shrunken_cache */
2371
2372     if ( wp->image && dp->vtl->drawimages )
2373     {
2374       GdkPixbuf *pixbuf = NULL;
2375       GList *l;
2376
2377       if ( dp->vtl->image_alpha == 0)
2378         return;
2379
2380       l = g_list_find_custom ( dp->vtl->image_cache->head, wp->image, (GCompareFunc) cached_pixbuf_cmp );
2381       if ( l )
2382         pixbuf = ((CachedPixbuf *) l->data)->pixbuf;
2383       else
2384       {
2385         gchar *image = wp->image;
2386         GdkPixbuf *regularthumb = a_thumbnails_get ( wp->image );
2387         if ( ! regularthumb )
2388         {
2389           regularthumb = a_thumbnails_get_default (); /* cache one 'not yet loaded' for all thumbs not loaded */
2390           image = "\x12\x00"; /* this shouldn't occur naturally. */
2391         }
2392         if ( regularthumb )
2393         {
2394           CachedPixbuf *cp = NULL;
2395           cp = g_malloc ( sizeof ( CachedPixbuf ) );
2396           if ( dp->vtl->image_size == 128 )
2397             cp->pixbuf = regularthumb;
2398           else
2399           {
2400             cp->pixbuf = a_thumbnails_scale_pixbuf(regularthumb, dp->vtl->image_size, dp->vtl->image_size);
2401             g_assert ( cp->pixbuf );
2402             g_object_unref ( G_OBJECT(regularthumb) );
2403           }
2404           cp->image = g_strdup ( image );
2405
2406           // Apply alpha setting to the image before the pixbuf gets stored in the cache
2407           if ( dp->vtl->image_alpha != 255 )
2408             cp->pixbuf = ui_pixbuf_set_alpha ( cp->pixbuf, dp->vtl->image_alpha );
2409
2410           /* needed so 'click picture' tool knows how big the pic is; we don't
2411            * store it in cp because they may have been freed already. */
2412           wp->image_width = gdk_pixbuf_get_width ( cp->pixbuf );
2413           wp->image_height = gdk_pixbuf_get_height ( cp->pixbuf );
2414
2415           g_queue_push_head ( dp->vtl->image_cache, cp );
2416           if ( dp->vtl->image_cache->length > dp->vtl->image_cache_size )
2417             cached_pixbuf_free ( g_queue_pop_tail ( dp->vtl->image_cache ) );
2418
2419           pixbuf = cp->pixbuf;
2420         }
2421         else
2422         {
2423           pixbuf = a_thumbnails_get_default (); /* thumbnail not yet loaded */
2424         }
2425       }
2426       if ( pixbuf )
2427       {
2428         gint w, h;
2429         w = gdk_pixbuf_get_width ( pixbuf );
2430         h = gdk_pixbuf_get_height ( pixbuf );
2431
2432         if ( x+(w/2) > 0 && y+(h/2) > 0 && x-(w/2) < dp->width && y-(h/2) < dp->height ) /* always draw within boundaries */
2433         {
2434           if ( dp->highlight ) {
2435             // Highlighted - so draw a little border around the chosen one
2436             // single line seems a little weak so draw 2 of them
2437             vik_viewport_draw_rectangle (dp->vp, vik_viewport_get_gc_highlight (dp->vp), FALSE,
2438                                          x - (w/2) - 1, y - (h/2) - 1, w + 2, h + 2 );
2439             vik_viewport_draw_rectangle (dp->vp, vik_viewport_get_gc_highlight (dp->vp), FALSE,
2440                                          x - (w/2) - 2, y - (h/2) - 2, w + 4, h + 4 );
2441           }
2442
2443           vik_viewport_draw_pixbuf ( dp->vp, pixbuf, 0, 0, x - (w/2), y - (h/2), w, h );
2444         }
2445         return; /* if failed to draw picture, default to drawing regular waypoint (below) */
2446       }
2447     }
2448
2449     // Draw appropriate symbol - either symbol image or simple types
2450     if ( dp->vtl->wp_draw_symbols && wp->symbol && wp->symbol_pixbuf ) {
2451       vik_viewport_draw_pixbuf ( dp->vp, wp->symbol_pixbuf, 0, 0, x - gdk_pixbuf_get_width(wp->symbol_pixbuf)/2, y - gdk_pixbuf_get_height(wp->symbol_pixbuf)/2, -1, -1 );
2452     } 
2453     else if ( wp == dp->vtl->current_wp ) {
2454       switch ( dp->vtl->wp_symbol ) {
2455         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;
2456         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;
2457         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;
2458         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 );
2459                           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 );
2460         default: break;
2461       }
2462     }
2463     else {
2464       switch ( dp->vtl->wp_symbol ) {
2465         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;
2466         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;
2467         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;
2468         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 );
2469                           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;
2470         default: break;
2471       }
2472     }
2473
2474     if ( dp->vtl->drawlabels )
2475     {
2476       /* thanks to the GPSDrive people (Fritz Ganter et al.) for hints on this part ... yah, I'm too lazy to study documentation */
2477       gint label_x, label_y;
2478       gint width, height;
2479       // Hopefully name won't break the markup (may need to sanitize - g_markup_escape_text())
2480
2481       // Could this stored in the waypoint rather than recreating each pass?
2482       gchar *wp_label_markup = g_strdup_printf ( "<span size=\"%s\">%s</span>", dp->vtl->wp_fsize_str, wp->name );
2483
2484       if ( pango_parse_markup ( wp_label_markup, -1, 0, NULL, NULL, NULL, NULL ) )
2485         pango_layout_set_markup ( dp->vtl->wplabellayout, wp_label_markup, -1 );
2486       else
2487         // Fallback if parse failure
2488         pango_layout_set_text ( dp->vtl->wplabellayout, wp->name, -1 );
2489
2490       g_free ( wp_label_markup );
2491
2492       pango_layout_get_pixel_size ( dp->vtl->wplabellayout, &width, &height );
2493       label_x = x - width/2;
2494       if ( wp->symbol_pixbuf )
2495         label_y = y - height - 2 - gdk_pixbuf_get_height(wp->symbol_pixbuf)/2;
2496       else
2497         label_y = y - dp->vtl->wp_size - height - 2;
2498
2499       /* if highlight mode on, then draw background text in highlight colour */
2500       if ( dp->highlight )
2501         vik_viewport_draw_rectangle ( dp->vp, vik_viewport_get_gc_highlight (dp->vp), TRUE, label_x - 1, label_y-1,width+2,height+2);
2502       else
2503         vik_viewport_draw_rectangle ( dp->vp, dp->vtl->waypoint_bg_gc, TRUE, label_x - 1, label_y-1,width+2,height+2);
2504       vik_viewport_draw_layout ( dp->vp, dp->vtl->waypoint_text_gc, label_x, label_y, dp->vtl->wplabellayout );
2505     }
2506   }
2507 }
2508
2509 static void trw_layer_draw_waypoint_cb ( gpointer id, VikWaypoint *wp, struct DrawingParams *dp )
2510 {
2511   if ( BBOX_INTERSECT ( dp->vtl->waypoints_bbox, dp->bbox ) ) {
2512     trw_layer_draw_waypoint ( id, wp, dp );
2513   }
2514 }
2515
2516 static void trw_layer_draw_with_highlight ( VikTrwLayer *l, gpointer data, gboolean highlight )
2517 {
2518   static struct DrawingParams dp;
2519   g_assert ( l != NULL );
2520
2521   init_drawing_params ( &dp, l, VIK_VIEWPORT(data), highlight );
2522
2523   if ( l->tracks_visible )
2524     g_hash_table_foreach ( l->tracks, (GHFunc) trw_layer_draw_track_cb, &dp );
2525
2526   if ( l->routes_visible )
2527     g_hash_table_foreach ( l->routes, (GHFunc) trw_layer_draw_track_cb, &dp );
2528
2529   if (l->waypoints_visible)
2530     g_hash_table_foreach ( l->waypoints, (GHFunc) trw_layer_draw_waypoint_cb, &dp );
2531 }
2532
2533 static void trw_layer_draw ( VikTrwLayer *l, gpointer data )
2534 {
2535   // If this layer is to be highlighted - then don't draw now - as it will be drawn later on in the specific highlight draw stage
2536   // This may seem slightly inefficient to test each time for every layer
2537   //  but for a layer with *lots* of tracks & waypoints this can save some effort by not drawing the items twice
2538   if ( vik_viewport_get_draw_highlight ( (VikViewport*)data ) &&
2539        vik_window_get_selected_trw_layer ((VikWindow*)VIK_GTK_WINDOW_FROM_LAYER((VikLayer*)l)) == l )
2540     return;
2541   trw_layer_draw_with_highlight ( l, data, FALSE );
2542 }
2543
2544 void vik_trw_layer_draw_highlight ( VikTrwLayer *vtl, VikViewport *vvp )
2545 {
2546   // Check the layer for visibility (including all the parents visibilities)
2547   if ( !vik_treeview_item_get_visible_tree (VIK_LAYER(vtl)->vt, &(VIK_LAYER(vtl)->iter)) )
2548     return;
2549   trw_layer_draw_with_highlight ( vtl, vvp, TRUE );
2550 }
2551
2552 /**
2553  * vik_trw_layer_draw_highlight_item:
2554  *
2555  * Only handles a single track or waypoint ATM
2556  * It assumes the track or waypoint belongs to the TRW Layer (it doesn't check this is the case)
2557  */
2558 void vik_trw_layer_draw_highlight_item ( VikTrwLayer *vtl, VikTrack *trk, VikWaypoint *wpt, VikViewport *vvp )
2559 {
2560   // Check the layer for visibility (including all the parents visibilities)
2561   if ( !vik_treeview_item_get_visible_tree (VIK_LAYER(vtl)->vt, &(VIK_LAYER(vtl)->iter)) )
2562     return;
2563
2564   static struct DrawingParams dp;
2565   init_drawing_params ( &dp, vtl, vvp, TRUE );
2566
2567   if ( trk ) {
2568     gboolean draw = ( trk->is_route && vtl->routes_visible ) || ( !trk->is_route && vtl->tracks_visible );
2569     if ( draw )
2570       trw_layer_draw_track_cb ( NULL, trk, &dp );
2571   }
2572   if ( vtl->waypoints_visible && wpt ) {
2573     trw_layer_draw_waypoint_cb ( NULL, wpt, &dp );
2574   }
2575 }
2576
2577 /**
2578  * vik_trw_layer_draw_highlight_item:
2579  *
2580  * Generally for drawing all tracks or routes or waypoints
2581  * trks may be actually routes
2582  * It assumes they belong to the TRW Layer (it doesn't check this is the case)
2583  */
2584 void vik_trw_layer_draw_highlight_items ( VikTrwLayer *vtl, GHashTable *trks, GHashTable *wpts, VikViewport *vvp )
2585 {
2586   // Check the layer for visibility (including all the parents visibilities)
2587   if ( !vik_treeview_item_get_visible_tree (VIK_LAYER(vtl)->vt, &(VIK_LAYER(vtl)->iter)) )
2588     return;
2589
2590   static struct DrawingParams dp;
2591   init_drawing_params ( &dp, vtl, vvp, TRUE );
2592
2593   if ( trks ) {
2594     gboolean is_routes = (trks == vtl->routes);
2595     gboolean draw = ( is_routes && vtl->routes_visible ) || ( !is_routes && vtl->tracks_visible );
2596     if ( draw )
2597       g_hash_table_foreach ( trks, (GHFunc) trw_layer_draw_track_cb, &dp );
2598   }
2599
2600   if ( vtl->waypoints_visible && wpts )
2601     g_hash_table_foreach ( wpts, (GHFunc) trw_layer_draw_waypoint_cb, &dp );
2602 }
2603
2604 static void trw_layer_free_track_gcs ( VikTrwLayer *vtl )
2605 {
2606   int i;
2607   if ( vtl->track_bg_gc ) 
2608   {
2609     g_object_unref ( vtl->track_bg_gc );
2610     vtl->track_bg_gc = NULL;
2611   }
2612   if ( vtl->track_1color_gc )
2613   {
2614     g_object_unref ( vtl->track_1color_gc );
2615     vtl->track_1color_gc = NULL;
2616   }
2617   if ( vtl->current_track_gc ) 
2618   {
2619     g_object_unref ( vtl->current_track_gc );
2620     vtl->current_track_gc = NULL;
2621   }
2622   if ( vtl->current_track_newpoint_gc )
2623   {
2624     g_object_unref ( vtl->current_track_newpoint_gc );
2625     vtl->current_track_newpoint_gc = NULL;
2626   }
2627
2628   if ( ! vtl->track_gc )
2629     return;
2630   for ( i = vtl->track_gc->len - 1; i >= 0; i-- )
2631     g_object_unref ( g_array_index ( vtl->track_gc, GObject *, i ) );
2632   g_array_free ( vtl->track_gc, TRUE );
2633   vtl->track_gc = NULL;
2634 }
2635
2636 static void trw_layer_new_track_gcs ( VikTrwLayer *vtl, VikViewport *vp )
2637 {
2638   GdkGC *gc[ VIK_TRW_LAYER_TRACK_GC ];
2639   gint width = vtl->line_thickness;
2640
2641   if ( vtl->track_gc )
2642     trw_layer_free_track_gcs ( vtl );
2643
2644   if ( vtl->track_bg_gc )
2645     g_object_unref ( vtl->track_bg_gc );
2646   vtl->track_bg_gc = vik_viewport_new_gc_from_color ( vp, &(vtl->track_bg_color), width + vtl->bg_line_thickness );
2647
2648   // Ensure new track drawing heeds line thickness setting
2649   //  however always have a minium of 2, as 1 pixel is really narrow
2650   gint new_track_width = (vtl->line_thickness < 2) ? 2 : vtl->line_thickness;
2651   
2652   if ( vtl->current_track_gc )
2653     g_object_unref ( vtl->current_track_gc );
2654   vtl->current_track_gc = vik_viewport_new_gc ( vp, "#FF0000", new_track_width );
2655   gdk_gc_set_line_attributes ( vtl->current_track_gc, new_track_width, GDK_LINE_ON_OFF_DASH, GDK_CAP_ROUND, GDK_JOIN_ROUND );
2656
2657   // 'newpoint' gc is exactly the same as the current track gc
2658   if ( vtl->current_track_newpoint_gc )
2659     g_object_unref ( vtl->current_track_newpoint_gc );
2660   vtl->current_track_newpoint_gc = vik_viewport_new_gc ( vp, "#FF0000", new_track_width );
2661   gdk_gc_set_line_attributes ( vtl->current_track_newpoint_gc, new_track_width, GDK_LINE_ON_OFF_DASH, GDK_CAP_ROUND, GDK_JOIN_ROUND );
2662
2663   vtl->track_gc = g_array_sized_new ( FALSE, FALSE, sizeof ( GdkGC * ), VIK_TRW_LAYER_TRACK_GC );
2664
2665   gc[VIK_TRW_LAYER_TRACK_GC_STOP] = vik_viewport_new_gc ( vp, "#874200", width );
2666   gc[VIK_TRW_LAYER_TRACK_GC_BLACK] = vik_viewport_new_gc ( vp, "#000000", width ); // black
2667
2668   gc[VIK_TRW_LAYER_TRACK_GC_SLOW] = vik_viewport_new_gc ( vp, "#E6202E", width ); // red-ish
2669   gc[VIK_TRW_LAYER_TRACK_GC_AVER] = vik_viewport_new_gc ( vp, "#D2CD26", width ); // yellow-ish
2670   gc[VIK_TRW_LAYER_TRACK_GC_FAST] = vik_viewport_new_gc ( vp, "#2B8700", width ); // green-ish
2671
2672   gc[VIK_TRW_LAYER_TRACK_GC_SINGLE] = vik_viewport_new_gc_from_color ( vp, &(vtl->track_color), width );
2673
2674   g_array_append_vals ( vtl->track_gc, gc, VIK_TRW_LAYER_TRACK_GC );
2675 }
2676
2677 static VikTrwLayer* trw_layer_create ( VikViewport *vp )
2678 {
2679   VikTrwLayer *rv = trw_layer_new1 ( vp );
2680   vik_layer_rename ( VIK_LAYER(rv), vik_trw_layer_interface.name );
2681
2682   if ( vp == NULL || gtk_widget_get_window(GTK_WIDGET(vp)) == NULL ) {
2683     /* early exit, as the rest is GUI related */
2684     return rv;
2685   }
2686
2687   rv->wplabellayout = gtk_widget_create_pango_layout (GTK_WIDGET(vp), NULL);
2688   pango_layout_set_font_description (rv->wplabellayout, gtk_widget_get_style(GTK_WIDGET(vp))->font_desc);
2689
2690   rv->tracklabellayout = gtk_widget_create_pango_layout (GTK_WIDGET(vp), NULL);
2691   pango_layout_set_font_description (rv->tracklabellayout, gtk_widget_get_style(GTK_WIDGET(vp))->font_desc);
2692
2693   trw_layer_new_track_gcs ( rv, vp );
2694
2695   rv->waypoint_gc = vik_viewport_new_gc_from_color ( vp, &(rv->waypoint_color), 2 );
2696   rv->waypoint_text_gc = vik_viewport_new_gc_from_color ( vp, &(rv->waypoint_text_color), 1 );
2697   rv->waypoint_bg_gc = vik_viewport_new_gc_from_color ( vp, &(rv->waypoint_bg_color), 1 );
2698   gdk_gc_set_function ( rv->waypoint_bg_gc, rv->wpbgand );
2699
2700   rv->coord_mode = vik_viewport_get_coord_mode ( vp );
2701
2702   rv->menu_selection = vik_layer_get_interface(VIK_LAYER(rv)->type)->menu_items_selection;
2703
2704   return rv;
2705 }
2706
2707 #define SMALL_ICON_SIZE 18
2708 /*
2709  * Can accept a null symbol, and may return null value
2710  */
2711 GdkPixbuf* get_wp_sym_small ( gchar *symbol )
2712 {
2713   GdkPixbuf* wp_icon = a_get_wp_sym (symbol);
2714   // ATM a_get_wp_sym returns a cached icon, with the size dependent on the preferences.
2715   //  So needing a small icon for the treeview may need some resizing:
2716   if ( wp_icon && gdk_pixbuf_get_width ( wp_icon ) != SMALL_ICON_SIZE )
2717     wp_icon = gdk_pixbuf_scale_simple ( wp_icon, SMALL_ICON_SIZE, SMALL_ICON_SIZE, GDK_INTERP_BILINEAR );
2718   return wp_icon;
2719 }
2720
2721 static void trw_layer_realize_track ( gpointer id, VikTrack *track, gpointer pass_along[5] )
2722 {
2723   GtkTreeIter *new_iter = g_malloc(sizeof(GtkTreeIter));
2724
2725   GdkPixbuf *pixbuf = NULL;
2726
2727   if ( track->has_color ) {
2728     pixbuf = gdk_pixbuf_new ( GDK_COLORSPACE_RGB, FALSE, 8, SMALL_ICON_SIZE, SMALL_ICON_SIZE );
2729     // Annoyingly the GdkColor.pixel does not give the correct color when passed to gdk_pixbuf_fill (even when alloc'ed)
2730     // Here is some magic found to do the conversion
2731     // http://www.cs.binghamton.edu/~sgreene/cs360-2011s/topics/gtk+-2.20.1/gtk/gtkcolorbutton.c
2732     guint32 pixel = ((track->color.red & 0xff00) << 16) |
2733       ((track->color.green & 0xff00) << 8) |
2734       (track->color.blue & 0xff00);
2735
2736     gdk_pixbuf_fill ( pixbuf, pixel );
2737   }
2738
2739   time_t timestamp = 0;
2740   VikTrackpoint *tpt = vik_track_get_tp_first(track);
2741   if ( tpt && tpt->has_timestamp )
2742     timestamp = tpt->timestamp;
2743
2744   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, timestamp );
2745
2746   if ( pixbuf )
2747     g_object_unref (pixbuf);
2748
2749   *new_iter = *((GtkTreeIter *) pass_along[1]);
2750   if ( track->is_route )
2751     g_hash_table_insert ( VIK_TRW_LAYER(pass_along[2])->routes_iters, id, new_iter );
2752   else
2753     g_hash_table_insert ( VIK_TRW_LAYER(pass_along[2])->tracks_iters, id, new_iter );
2754
2755   if ( ! track->visible )
2756     vik_treeview_item_set_visible ( (VikTreeview *) pass_along[3], (GtkTreeIter *) pass_along[1], FALSE );
2757 }
2758
2759 static void trw_layer_realize_waypoint ( gpointer id, VikWaypoint *wp, gpointer pass_along[5] )
2760 {
2761   GtkTreeIter *new_iter = g_malloc(sizeof(GtkTreeIter));
2762
2763   time_t timestamp = 0;
2764   if ( wp->has_timestamp )
2765     timestamp = wp->timestamp;
2766
2767   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, timestamp );
2768
2769   *new_iter = *((GtkTreeIter *) pass_along[1]);
2770   g_hash_table_insert ( VIK_TRW_LAYER(pass_along[2])->waypoints_iters, id, new_iter );
2771
2772   if ( ! wp->visible )
2773     vik_treeview_item_set_visible ( (VikTreeview *) pass_along[3], (GtkTreeIter *) pass_along[1], FALSE );
2774 }
2775
2776 static void trw_layer_add_sublayer_tracks ( VikTrwLayer *vtl, VikTreeview *vt, GtkTreeIter *layer_iter )
2777 {
2778   vik_treeview_add_sublayer ( (VikTreeview *) vt, layer_iter, &(vtl->tracks_iter), _("Tracks"), vtl, NULL, VIK_TRW_LAYER_SUBLAYER_TRACKS, NULL, FALSE, 0 );
2779 }
2780
2781 static void trw_layer_add_sublayer_waypoints ( VikTrwLayer *vtl, VikTreeview *vt, GtkTreeIter *layer_iter )
2782 {
2783   vik_treeview_add_sublayer ( (VikTreeview *) vt, layer_iter, &(vtl->waypoints_iter), _("Waypoints"), vtl, NULL, VIK_TRW_LAYER_SUBLAYER_WAYPOINTS, NULL, FALSE, 0 );
2784 }
2785
2786 static void trw_layer_add_sublayer_routes ( VikTrwLayer *vtl, VikTreeview *vt, GtkTreeIter *layer_iter )
2787 {
2788   vik_treeview_add_sublayer ( (VikTreeview *) vt, layer_iter, &(vtl->routes_iter), _("Routes"), vtl, NULL, VIK_TRW_LAYER_SUBLAYER_ROUTES, NULL, FALSE, 0 );
2789 }
2790
2791 static void trw_layer_realize ( VikTrwLayer *vtl, VikTreeview *vt, GtkTreeIter *layer_iter )
2792 {
2793   GtkTreeIter iter2;
2794   gpointer pass_along[5] = { &(vtl->tracks_iter), &iter2, vtl, vt, GINT_TO_POINTER(VIK_TRW_LAYER_SUBLAYER_TRACK) };
2795
2796   if ( g_hash_table_size (vtl->tracks) > 0 ) {
2797     trw_layer_add_sublayer_tracks ( vtl, vt , layer_iter );
2798
2799     g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_realize_track, pass_along );
2800
2801     vik_treeview_item_set_visible ( vt, &(vtl->tracks_iter), vtl->tracks_visible );
2802   }
2803
2804   if ( g_hash_table_size (vtl->routes) > 0 ) {
2805     trw_layer_add_sublayer_routes ( vtl, vt, layer_iter );
2806
2807     pass_along[0] = &(vtl->routes_iter);
2808     pass_along[4] = GINT_TO_POINTER(VIK_TRW_LAYER_SUBLAYER_ROUTE);
2809
2810     g_hash_table_foreach ( vtl->routes, (GHFunc) trw_layer_realize_track, pass_along );
2811
2812     vik_treeview_item_set_visible ( (VikTreeview *) vt, &(vtl->routes_iter), vtl->routes_visible );
2813   }
2814
2815   if ( g_hash_table_size (vtl->waypoints) > 0 ) {
2816     trw_layer_add_sublayer_waypoints ( vtl, vt, layer_iter );
2817
2818     pass_along[0] = &(vtl->waypoints_iter);
2819     pass_along[4] = GINT_TO_POINTER(VIK_TRW_LAYER_SUBLAYER_WAYPOINT);
2820
2821     g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_realize_waypoint, pass_along );
2822
2823     vik_treeview_item_set_visible ( (VikTreeview *) vt, &(vtl->waypoints_iter), vtl->waypoints_visible );
2824   }
2825
2826   trw_layer_verify_thumbnails ( vtl );
2827
2828   trw_layer_sort_all ( vtl );
2829 }
2830
2831 static gboolean trw_layer_sublayer_toggle_visible ( VikTrwLayer *l, gint subtype, gpointer sublayer )
2832 {
2833   switch ( subtype )
2834   {
2835     case VIK_TRW_LAYER_SUBLAYER_TRACKS: return (l->tracks_visible ^= 1);
2836     case VIK_TRW_LAYER_SUBLAYER_WAYPOINTS: return (l->waypoints_visible ^= 1);
2837     case VIK_TRW_LAYER_SUBLAYER_ROUTES: return (l->routes_visible ^= 1);
2838     case VIK_TRW_LAYER_SUBLAYER_TRACK:
2839     {
2840       VikTrack *t = g_hash_table_lookup ( l->tracks, sublayer );
2841       if (t)
2842         return (t->visible ^= 1);
2843       else
2844         return TRUE;
2845     }
2846     case VIK_TRW_LAYER_SUBLAYER_WAYPOINT:
2847     {
2848       VikWaypoint *t = g_hash_table_lookup ( l->waypoints, sublayer );
2849       if (t)
2850         return (t->visible ^= 1);
2851       else
2852         return TRUE;
2853     }
2854     case VIK_TRW_LAYER_SUBLAYER_ROUTE:
2855     {
2856       VikTrack *t = g_hash_table_lookup ( l->routes, sublayer );
2857       if (t)
2858         return (t->visible ^= 1);
2859       else
2860         return TRUE;
2861     }
2862     default: break;
2863   }
2864   return TRUE;
2865 }
2866
2867 /*
2868  * Return a property about tracks for this layer
2869  */
2870 gint vik_trw_layer_get_property_tracks_line_thickness ( VikTrwLayer *vtl )
2871 {
2872   return vtl->line_thickness;
2873 }
2874
2875 /*
2876  * Build up multiple routes information
2877  */
2878 static void trw_layer_routes_tooltip ( const gpointer id, VikTrack *tr, gdouble *length )
2879 {
2880   *length = *length + vik_track_get_length (tr);
2881 }
2882
2883 // Structure to hold multiple track information for a layer
2884 typedef struct {
2885   gdouble length;
2886   time_t  start_time;
2887   time_t  end_time;
2888   gint    duration;
2889 } tooltip_tracks;
2890
2891 /*
2892  * Build up layer multiple track information via updating the tooltip_tracks structure
2893  */
2894 static void trw_layer_tracks_tooltip ( const gpointer id, VikTrack *tr, tooltip_tracks *tt )
2895 {
2896   tt->length = tt->length + vik_track_get_length (tr);
2897
2898   // Ensure times are available
2899   if ( tr->trackpoints && vik_track_get_tp_first(tr)->has_timestamp ) {
2900     // Get trkpt only once - as using vik_track_get_tp_last() iterates whole track each time
2901     VikTrackpoint *trkpt_last = vik_track_get_tp_last(tr);
2902     if ( trkpt_last->has_timestamp ) {
2903       time_t t1, t2;
2904       t1 = vik_track_get_tp_first(tr)->timestamp;
2905       t2 = trkpt_last->timestamp;
2906
2907       // Assume never actually have a track with a time of 0 (1st Jan 1970)
2908       // Hence initialize to the first 'proper' value
2909       if ( tt->start_time == 0 )
2910         tt->start_time = t1;
2911       if ( tt->end_time == 0 )
2912         tt->end_time = t2;
2913
2914       // Update find the earliest / last times
2915       if ( t1 < tt->start_time )
2916         tt->start_time = t1;
2917       if ( t2 > tt->end_time )
2918         tt->end_time = t2;
2919
2920       // Keep track of total time
2921       //  there maybe gaps within a track (eg segments)
2922       //  but this should be generally good enough for a simple indicator
2923       tt->duration = tt->duration + (int)(t2-t1);
2924     }
2925   }
2926 }
2927
2928 /*
2929  * Generate tooltip text for the layer.
2930  * This is relatively complicated as it considers information for
2931  *   no tracks, a single track or multiple tracks
2932  *     (which may or may not have timing information)
2933  */
2934 static const gchar* trw_layer_layer_tooltip ( VikTrwLayer *vtl )
2935 {
2936   gchar tbuf1[64];
2937   gchar tbuf2[64];
2938   gchar tbuf3[64];
2939   gchar tbuf4[10];
2940   tbuf1[0] = '\0';
2941   tbuf2[0] = '\0';
2942   tbuf3[0] = '\0';
2943   tbuf4[0] = '\0';
2944
2945   static gchar tmp_buf[128];
2946   tmp_buf[0] = '\0';
2947
2948   // For compact date format I'm using '%x'     [The preferred date representation for the current locale without the time.]
2949
2950   // Safety check - I think these should always be valid
2951   if ( vtl->tracks && vtl->waypoints ) {
2952     tooltip_tracks tt = { 0.0, 0, 0, 0 };
2953     g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_tracks_tooltip, &tt );
2954
2955     GDate* gdate_start = g_date_new ();
2956     g_date_set_time_t (gdate_start, tt.start_time);
2957
2958     GDate* gdate_end = g_date_new ();
2959     g_date_set_time_t (gdate_end, tt.end_time);
2960
2961     if ( g_date_compare (gdate_start, gdate_end) ) {
2962       // Dates differ so print range on separate line
2963       g_date_strftime (tbuf1, sizeof(tbuf1), "%x", gdate_start);
2964       g_date_strftime (tbuf2, sizeof(tbuf2), "%x", gdate_end);
2965       g_snprintf (tbuf3, sizeof(tbuf3), "%s to %s\n", tbuf1, tbuf2);
2966     }
2967     else {
2968       // Same date so just show it and keep rest of text on the same line - provided it's a valid time!
2969       if ( tt.start_time != 0 )
2970         g_date_strftime (tbuf3, sizeof(tbuf3), "%x: ", gdate_start);
2971     }
2972
2973     tbuf2[0] = '\0';
2974     if ( tt.length > 0.0 ) {
2975       gdouble len_in_units;
2976
2977       // Setup info dependent on distance units
2978       switch ( a_vik_get_units_distance() ) {
2979       case VIK_UNITS_DISTANCE_MILES:
2980         g_snprintf (tbuf4, sizeof(tbuf4), "miles");
2981         len_in_units = VIK_METERS_TO_MILES(tt.length);
2982         break;
2983       case VIK_UNITS_DISTANCE_NAUTICAL_MILES:
2984         g_snprintf (tbuf4, sizeof(tbuf4), "NM");
2985         len_in_units = VIK_METERS_TO_NAUTICAL_MILES(tt.length);
2986         break;
2987       default:
2988         g_snprintf (tbuf4, sizeof(tbuf4), "kms");
2989         len_in_units = tt.length/1000.0;
2990         break;
2991       }
2992
2993       // Timing information if available
2994       tbuf1[0] = '\0';
2995       if ( tt.duration > 0 ) {
2996         g_snprintf (tbuf1, sizeof(tbuf1),
2997                     _(" in %d:%02d hrs:mins"),
2998                     (int)(tt.duration/3600), (int)round(tt.duration/60.0)%60);
2999       }
3000       g_snprintf (tbuf2, sizeof(tbuf2),
3001                   _("\n%sTotal Length %.1f %s%s"),
3002                   tbuf3, len_in_units, tbuf4, tbuf1);
3003     }
3004
3005     tbuf1[0] = '\0';
3006     gdouble rlength = 0.0;
3007     g_hash_table_foreach ( vtl->routes, (GHFunc) trw_layer_routes_tooltip, &rlength );
3008     if ( rlength > 0.0 ) {
3009       gdouble len_in_units;
3010       // Setup info dependent on distance units
3011       switch ( a_vik_get_units_distance() ) {
3012       case VIK_UNITS_DISTANCE_MILES:
3013         g_snprintf (tbuf4, sizeof(tbuf4), "miles");
3014         len_in_units = VIK_METERS_TO_MILES(rlength);
3015         break;
3016       case VIK_UNITS_DISTANCE_NAUTICAL_MILES:
3017         g_snprintf (tbuf4, sizeof(tbuf4), "NM");
3018         len_in_units = VIK_METERS_TO_NAUTICAL_MILES(rlength);
3019         break;
3020       default:
3021         g_snprintf (tbuf4, sizeof(tbuf4), "kms");
3022         len_in_units = rlength/1000.0;
3023         break;
3024       }
3025       g_snprintf (tbuf1, sizeof(tbuf1), _("\nTotal route length %.1f %s"), len_in_units, tbuf4);
3026     }
3027
3028     // Put together all the elements to form compact tooltip text
3029     g_snprintf (tmp_buf, sizeof(tmp_buf),
3030                 _("Tracks: %d - Waypoints: %d - Routes: %d%s%s"),
3031                 g_hash_table_size (vtl->tracks), g_hash_table_size (vtl->waypoints), g_hash_table_size (vtl->routes), tbuf2, tbuf1);
3032
3033     g_date_free (gdate_start);
3034     g_date_free (gdate_end);
3035   }
3036
3037   return tmp_buf;
3038 }
3039
3040 static const gchar* trw_layer_sublayer_tooltip ( VikTrwLayer *l, gint subtype, gpointer sublayer )
3041 {
3042   switch ( subtype )
3043   {
3044     case VIK_TRW_LAYER_SUBLAYER_TRACKS:
3045     {
3046       // Very simple tooltip - may expand detail in the future...
3047       static gchar tmp_buf[32];
3048       g_snprintf (tmp_buf, sizeof(tmp_buf),
3049                   _("Tracks: %d"),
3050                   g_hash_table_size (l->tracks));
3051       return tmp_buf;
3052     }
3053     break;
3054     case VIK_TRW_LAYER_SUBLAYER_ROUTES:
3055     {
3056       // Very simple tooltip - may expand detail in the future...
3057       static gchar tmp_buf[32];
3058       g_snprintf (tmp_buf, sizeof(tmp_buf),
3059                   _("Routes: %d"),
3060                   g_hash_table_size (l->routes));
3061       return tmp_buf;
3062     }
3063     break;
3064
3065     case VIK_TRW_LAYER_SUBLAYER_ROUTE:
3066       // Same tooltip for a route
3067     case VIK_TRW_LAYER_SUBLAYER_TRACK:
3068     {
3069       VikTrack *tr;
3070       if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
3071         tr = g_hash_table_lookup ( l->tracks, sublayer );
3072       else
3073         tr = g_hash_table_lookup ( l->routes, sublayer );
3074
3075       if ( tr ) {
3076         // Could be a better way of handling strings - but this works...
3077         gchar time_buf1[20];
3078         gchar time_buf2[20];
3079         time_buf1[0] = '\0';
3080         time_buf2[0] = '\0';
3081         static gchar tmp_buf[100];
3082         // Compact info: Short date eg (11/20/99), duration and length
3083         // Hopefully these are the things that are most useful and so promoted into the tooltip
3084         if ( tr->trackpoints && vik_track_get_tp_first(tr)->has_timestamp ) {
3085           // %x     The preferred date representation for the current locale without the time.
3086           strftime (time_buf1, sizeof(time_buf1), "%x: ", gmtime(&(vik_track_get_tp_first(tr)->timestamp)));
3087           time_t dur = vik_track_get_duration ( tr, TRUE );
3088           if ( dur > 0 )
3089             g_snprintf ( time_buf2, sizeof(time_buf2), _("- %d:%02d hrs:mins"), (int)(dur/3600), (int)round(dur/60.0)%60 );
3090         }
3091         // Get length and consider the appropriate distance units
3092         gdouble tr_len = vik_track_get_length(tr);
3093         vik_units_distance_t dist_units = a_vik_get_units_distance ();
3094         switch (dist_units) {
3095         case VIK_UNITS_DISTANCE_KILOMETRES:
3096           g_snprintf (tmp_buf, sizeof(tmp_buf), _("%s%.1f km %s"), time_buf1, tr_len/1000.0, time_buf2);
3097           break;
3098         case VIK_UNITS_DISTANCE_MILES:
3099           g_snprintf (tmp_buf, sizeof(tmp_buf), _("%s%.1f miles %s"), time_buf1, VIK_METERS_TO_MILES(tr_len), time_buf2);
3100           break;
3101         case VIK_UNITS_DISTANCE_NAUTICAL_MILES:
3102           g_snprintf (tmp_buf, sizeof(tmp_buf), _("%s%.1f NM %s"), time_buf1, VIK_METERS_TO_NAUTICAL_MILES(tr_len), time_buf2);
3103           break;
3104         default:
3105           break;
3106         }
3107         return tmp_buf;
3108       }
3109     }
3110     break;
3111     case VIK_TRW_LAYER_SUBLAYER_WAYPOINTS:
3112     {
3113       // Very simple tooltip - may expand detail in the future...
3114       static gchar tmp_buf[32];
3115       g_snprintf (tmp_buf, sizeof(tmp_buf),
3116                   _("Waypoints: %d"),
3117                   g_hash_table_size (l->waypoints));
3118       return tmp_buf;
3119     }
3120     break;
3121     case VIK_TRW_LAYER_SUBLAYER_WAYPOINT:
3122     {
3123       VikWaypoint *w = g_hash_table_lookup ( l->waypoints, sublayer );
3124       // NB It's OK to return NULL
3125       if ( w ) {
3126         if ( w->comment )
3127           return w->comment;
3128         else
3129           return w->description;
3130       }
3131     }
3132     break;
3133     default: break;
3134   }
3135   return NULL;
3136 }
3137
3138 #define VIK_SETTINGS_TRKPT_SELECTED_STATUSBAR_FORMAT "trkpt_selected_statusbar_format"
3139
3140 /**
3141  * set_statusbar_msg_info_trkpt:
3142  *
3143  * Function to show track point information on the statusbar
3144  *  Items displayed is controlled by the settings format code
3145  */
3146 static void set_statusbar_msg_info_trkpt ( VikTrwLayer *vtl, VikTrackpoint *trkpt )
3147 {
3148   gchar *statusbar_format_code = NULL;
3149   gboolean need2free = FALSE;
3150   VikTrackpoint *trkpt_prev = NULL;
3151   if ( !a_settings_get_string ( VIK_SETTINGS_TRKPT_SELECTED_STATUSBAR_FORMAT, &statusbar_format_code ) ) {
3152     // Otherwise use default
3153     statusbar_format_code = g_strdup ( "KEATDN" );
3154     need2free = TRUE;
3155   }
3156   else {
3157     // Format code may want to show speed - so may need previous trkpt to work it out
3158     trkpt_prev = vik_track_get_tp_prev ( vtl->current_tp_track, trkpt );
3159   }
3160
3161   gchar *msg = vu_trackpoint_formatted_message ( statusbar_format_code, trkpt, trkpt_prev, vtl->current_tp_track, NAN );
3162   vik_statusbar_set_message ( vik_window_get_statusbar (VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl))), VIK_STATUSBAR_INFO, msg );
3163   g_free ( msg );
3164
3165   if ( need2free )
3166     g_free ( statusbar_format_code );
3167 }
3168
3169 /*
3170  * Function to show basic waypoint information on the statusbar
3171  */
3172 static void set_statusbar_msg_info_wpt ( VikTrwLayer *vtl, VikWaypoint *wpt )
3173 {
3174   gchar tmp_buf1[64];
3175   switch (a_vik_get_units_height ()) {
3176   case VIK_UNITS_HEIGHT_FEET:
3177     g_snprintf(tmp_buf1, sizeof(tmp_buf1), _("Wpt: Alt %dft"), (int)round(VIK_METERS_TO_FEET(wpt->altitude)));
3178     break;
3179   default:
3180     //VIK_UNITS_HEIGHT_METRES:
3181     g_snprintf(tmp_buf1, sizeof(tmp_buf1), _("Wpt: Alt %dm"), (int)round(wpt->altitude));
3182   }
3183   
3184   // Position part
3185   // Position is put last, as this bit is most likely not to be seen if the display is not big enough,
3186   //   one can easily use the current pointer position to see this if needed
3187   gchar *lat = NULL, *lon = NULL;
3188   static struct LatLon ll;
3189   vik_coord_to_latlon (&(wpt->coord), &ll);
3190   a_coords_latlon_to_string ( &ll, &lat, &lon );
3191
3192   // Combine parts to make overall message
3193   gchar *msg;
3194   if ( wpt->comment )
3195     // Add comment if available
3196     msg = g_strdup_printf ( _("%s | %s %s | Comment: %s"), tmp_buf1, lat, lon, wpt->comment );
3197   else
3198     msg = g_strdup_printf ( _("%s | %s %s"), tmp_buf1, lat, lon );
3199   vik_statusbar_set_message ( vik_window_get_statusbar (VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl))), VIK_STATUSBAR_INFO, msg );
3200   g_free ( lat );
3201   g_free ( lon );
3202   g_free ( msg );
3203 }
3204
3205 /**
3206  * General layer selection function, find out which bit is selected and take appropriate action
3207  */
3208 static gboolean trw_layer_selected ( VikTrwLayer *l, gint subtype, gpointer sublayer, gint type, gpointer vlp )
3209 {
3210   // Reset
3211   l->current_wp    = NULL;
3212   l->current_wp_id = NULL;
3213   trw_layer_cancel_current_tp ( l, FALSE );
3214
3215   // Clear statusbar
3216   vik_statusbar_set_message ( vik_window_get_statusbar (VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(l))), VIK_STATUSBAR_INFO, "" );
3217
3218   switch ( type )
3219     {
3220     case VIK_TREEVIEW_TYPE_LAYER:
3221       {
3222         vik_window_set_selected_trw_layer ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l), l );
3223         /* Mark for redraw */
3224         return TRUE;
3225       }
3226       break;
3227
3228     case VIK_TREEVIEW_TYPE_SUBLAYER:
3229       {
3230         switch ( subtype )
3231           {
3232           case VIK_TRW_LAYER_SUBLAYER_TRACKS:
3233             {
3234               vik_window_set_selected_tracks ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l), l->tracks, l );
3235               /* Mark for redraw */
3236               return TRUE;
3237             }
3238             break;
3239           case VIK_TRW_LAYER_SUBLAYER_TRACK:
3240             {
3241               VikTrack *track = g_hash_table_lookup ( l->tracks, sublayer );
3242               vik_window_set_selected_track ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l), (gpointer)track, l );
3243               /* Mark for redraw */
3244               return TRUE;
3245             }
3246             break;
3247           case VIK_TRW_LAYER_SUBLAYER_ROUTES:
3248             {
3249               vik_window_set_selected_tracks ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l), l->routes, l );
3250               /* Mark for redraw */
3251               return TRUE;
3252             }
3253             break;
3254           case VIK_TRW_LAYER_SUBLAYER_ROUTE:
3255             {
3256               VikTrack *track = g_hash_table_lookup ( l->routes, sublayer );
3257               vik_window_set_selected_track ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l), (gpointer)track, l );
3258               /* Mark for redraw */
3259               return TRUE;
3260             }
3261             break;
3262           case VIK_TRW_LAYER_SUBLAYER_WAYPOINTS:
3263             {
3264               vik_window_set_selected_waypoints ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l), l->waypoints, l );
3265               /* Mark for redraw */
3266               return TRUE;
3267             }
3268             break;
3269           case VIK_TRW_LAYER_SUBLAYER_WAYPOINT:
3270             {
3271               VikWaypoint *wpt = g_hash_table_lookup ( l->waypoints, sublayer );
3272               if ( wpt ) {
3273                 vik_window_set_selected_waypoint ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l), (gpointer)wpt, l );
3274                 // Show some waypoint info
3275                 set_statusbar_msg_info_wpt ( l, wpt );
3276                 /* Mark for redraw */
3277                 return TRUE;
3278               }
3279             }
3280             break;
3281           default:
3282             {
3283               return vik_window_clear_highlight ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l) );
3284             }
3285             break;
3286           }
3287         return FALSE;
3288       }
3289       break;
3290
3291     default:
3292       return vik_window_clear_highlight ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l) );
3293       break;
3294     }
3295 }
3296
3297 GHashTable *vik_trw_layer_get_tracks ( VikTrwLayer *l )
3298 {
3299   return l->tracks;
3300 }
3301
3302 GHashTable *vik_trw_layer_get_routes ( VikTrwLayer *l )
3303 {
3304   return l->routes;
3305 }
3306
3307 GHashTable *vik_trw_layer_get_waypoints ( VikTrwLayer *l )
3308 {
3309   return l->waypoints;
3310 }
3311
3312 GHashTable *vik_trw_layer_get_tracks_iters ( VikTrwLayer *vtl )
3313 {
3314   return vtl->tracks_iters;
3315 }
3316
3317 GHashTable *vik_trw_layer_get_routes_iters ( VikTrwLayer *vtl )
3318 {
3319   return vtl->routes_iters;
3320 }
3321
3322 GHashTable *vik_trw_layer_get_waypoints_iters ( VikTrwLayer *vtl )
3323 {
3324   return vtl->waypoints_iters;
3325 }
3326
3327 gboolean vik_trw_layer_is_empty ( VikTrwLayer *vtl )
3328 {
3329   return ! ( g_hash_table_size ( vtl->tracks ) ||
3330              g_hash_table_size ( vtl->routes ) ||
3331              g_hash_table_size ( vtl->waypoints ) );
3332 }
3333
3334 gboolean vik_trw_layer_get_tracks_visibility ( VikTrwLayer *vtl )
3335 {
3336   return vtl->tracks_visible;
3337 }
3338
3339 gboolean vik_trw_layer_get_routes_visibility ( VikTrwLayer *vtl )
3340 {
3341   return vtl->routes_visible;
3342 }
3343
3344 gboolean vik_trw_layer_get_waypoints_visibility ( VikTrwLayer *vtl )
3345 {
3346   return vtl->waypoints_visible;
3347 }
3348
3349 /*
3350  * ATM use a case sensitive find
3351  * Finds the first one
3352  */
3353 static gboolean trw_layer_waypoint_find ( const gpointer id, const VikWaypoint *wp, const gchar *name )
3354 {
3355   if ( wp && wp->name )
3356     if ( ! strcmp ( wp->name, name ) )
3357       return TRUE;
3358   return FALSE;
3359 }
3360
3361 /*
3362  * Get waypoint by name - not guaranteed to be unique
3363  * Finds the first one
3364  */
3365 VikWaypoint *vik_trw_layer_get_waypoint ( VikTrwLayer *vtl, const gchar *name )
3366 {
3367   return g_hash_table_find ( vtl->waypoints, (GHRFunc) trw_layer_waypoint_find, (gpointer) name );
3368 }
3369
3370 /*
3371  * ATM use a case sensitive find
3372  * Finds the first one
3373  */
3374 static gboolean trw_layer_track_find ( const gpointer id, const VikTrack *trk, const gchar *name )
3375 {
3376   if ( trk && trk->name )
3377     if ( ! strcmp ( trk->name, name ) )
3378       return TRUE;
3379   return FALSE;
3380 }
3381
3382 /*
3383  * Get track by name - not guaranteed to be unique
3384  * Finds the first one
3385  */
3386 VikTrack *vik_trw_layer_get_track ( VikTrwLayer *vtl, const gchar *name )
3387 {
3388   return g_hash_table_find ( vtl->tracks, (GHRFunc) trw_layer_track_find, (gpointer) name );
3389 }
3390
3391 /*
3392  * Get route by name - not guaranteed to be unique
3393  * Finds the first one
3394  */
3395 VikTrack *vik_trw_layer_get_route ( VikTrwLayer *vtl, const gchar *name )
3396 {
3397   return g_hash_table_find ( vtl->routes, (GHRFunc) trw_layer_track_find, (gpointer) name );
3398 }
3399
3400 static void trw_layer_find_maxmin_tracks ( const gpointer id, const VikTrack *trk, struct LatLon maxmin[2] )
3401 {
3402   if ( trk->bbox.north > maxmin[0].lat || maxmin[0].lat == 0.0 )
3403     maxmin[0].lat = trk->bbox.north;
3404   if ( trk->bbox.south < maxmin[1].lat || maxmin[1].lat == 0.0 )
3405     maxmin[1].lat = trk->bbox.south;
3406   if ( trk->bbox.east > maxmin[0].lon || maxmin[0].lon == 0.0 )
3407     maxmin[0].lon = trk->bbox.east;
3408   if ( trk->bbox.west < maxmin[1].lon || maxmin[1].lon == 0.0 )
3409     maxmin[1].lon = trk->bbox.west;
3410 }
3411
3412 static void trw_layer_find_maxmin (VikTrwLayer *vtl, struct LatLon maxmin[2])
3413 {
3414   // Continually reuse maxmin to find the latest maximum and minimum values
3415   // First set to waypoints bounds
3416   maxmin[0].lat = vtl->waypoints_bbox.north;
3417   maxmin[1].lat = vtl->waypoints_bbox.south;
3418   maxmin[0].lon = vtl->waypoints_bbox.east;
3419   maxmin[1].lon = vtl->waypoints_bbox.west;
3420   g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_find_maxmin_tracks, maxmin );
3421   g_hash_table_foreach ( vtl->routes, (GHFunc) trw_layer_find_maxmin_tracks, maxmin );
3422 }
3423
3424 gboolean vik_trw_layer_find_center ( VikTrwLayer *vtl, VikCoord *dest )
3425 {
3426   /* 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... */
3427   struct LatLon maxmin[2] = { {0.0,0.0}, {0.0,0.0} };
3428   trw_layer_find_maxmin (vtl, maxmin);
3429   if (maxmin[0].lat == 0.0 && maxmin[0].lon == 0.0 && maxmin[1].lat == 0.0 && maxmin[1].lon == 0.0)
3430     return FALSE;
3431   else
3432   {
3433     struct LatLon average = { (maxmin[0].lat+maxmin[1].lat)/2, (maxmin[0].lon+maxmin[1].lon)/2 };
3434     vik_coord_load_from_latlon ( dest, vtl->coord_mode, &average );
3435     return TRUE;
3436   }
3437 }
3438
3439 static void trw_layer_centerize ( menu_array_layer values )
3440 {
3441   VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
3442   VikCoord coord;
3443   if ( vik_trw_layer_find_center ( vtl, &coord ) )
3444     goto_coord ( values[MA_VLP], NULL, NULL, &coord );
3445   else
3446     a_dialog_info_msg ( VIK_GTK_WINDOW_FROM_LAYER(vtl), _("This layer has no waypoints or trackpoints.") );
3447 }
3448
3449 void trw_layer_zoom_to_show_latlons ( VikTrwLayer *vtl, VikViewport *vvp, struct LatLon maxmin[2] )
3450 {
3451   vu_zoom_to_show_latlons ( vtl->coord_mode, vvp, maxmin );
3452 }
3453
3454 gboolean vik_trw_layer_auto_set_view ( VikTrwLayer *vtl, VikViewport *vvp )
3455 {
3456   /* TODO: what if there's only one waypoint @ 0,0, it will think nothing found. */
3457   struct LatLon maxmin[2] = { {0.0,0.0}, {0.0,0.0} };
3458   trw_layer_find_maxmin (vtl, maxmin);
3459   if (maxmin[0].lat == 0.0 && maxmin[0].lon == 0.0 && maxmin[1].lat == 0.0 && maxmin[1].lon == 0.0)
3460     return FALSE;
3461   else {
3462     trw_layer_zoom_to_show_latlons ( vtl, vvp, maxmin );
3463     return TRUE;
3464   }
3465 }
3466
3467 static void trw_layer_auto_view ( menu_array_layer values )
3468 {
3469   VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
3470   VikLayersPanel *vlp = VIK_LAYERS_PANEL(values[MA_VLP]);
3471   if ( vik_trw_layer_auto_set_view ( vtl, vik_layers_panel_get_viewport (vlp) ) ) {
3472     vik_layers_panel_emit_update ( vlp );
3473   }
3474   else
3475     a_dialog_info_msg ( VIK_GTK_WINDOW_FROM_LAYER(vtl), _("This layer has no waypoints or trackpoints.") );
3476 }
3477
3478 static void trw_layer_export_gpspoint ( menu_array_layer values )
3479 {
3480   gchar *auto_save_name = append_file_ext ( vik_layer_get_name(VIK_LAYER(values[MA_VTL])), FILE_TYPE_GPSPOINT );
3481
3482   vik_trw_layer_export ( VIK_TRW_LAYER (values[MA_VTL]), _("Export Layer"), auto_save_name, NULL, FILE_TYPE_GPSPOINT );
3483
3484   g_free ( auto_save_name );
3485 }
3486
3487 static void trw_layer_export_gpsmapper ( menu_array_layer values )
3488 {
3489   gchar *auto_save_name = append_file_ext ( vik_layer_get_name(VIK_LAYER(values[MA_VTL])), FILE_TYPE_GPSMAPPER );
3490
3491   vik_trw_layer_export ( VIK_TRW_LAYER (values[MA_VTL]), _("Export Layer"), auto_save_name, NULL, FILE_TYPE_GPSMAPPER );
3492
3493   g_free ( auto_save_name );
3494 }
3495
3496 static void trw_layer_export_gpx ( menu_array_layer values )
3497 {
3498   gchar *auto_save_name = append_file_ext ( vik_layer_get_name(VIK_LAYER(values[MA_VTL])), FILE_TYPE_GPX );
3499
3500   vik_trw_layer_export ( VIK_TRW_LAYER (values[MA_VTL]), _("Export Layer"), auto_save_name, NULL, FILE_TYPE_GPX );
3501
3502   g_free ( auto_save_name );
3503 }
3504
3505 static void trw_layer_export_kml ( menu_array_layer values )
3506 {
3507   gchar *auto_save_name = append_file_ext ( vik_layer_get_name(VIK_LAYER(values[MA_VTL])), FILE_TYPE_KML );
3508
3509   vik_trw_layer_export ( VIK_TRW_LAYER (values[MA_VTL]), _("Export Layer"), auto_save_name, NULL, FILE_TYPE_KML );
3510
3511   g_free ( auto_save_name );
3512 }
3513
3514 static void trw_layer_export_geojson ( menu_array_layer values )
3515 {
3516   gchar *auto_save_name = append_file_ext ( vik_layer_get_name(VIK_LAYER(values[MA_VTL])), FILE_TYPE_GEOJSON );
3517
3518   vik_trw_layer_export ( VIK_TRW_LAYER (values[MA_VTL]), _("Export Layer"), auto_save_name, NULL, FILE_TYPE_GEOJSON );
3519
3520   g_free ( auto_save_name );
3521 }
3522
3523 static void trw_layer_export_babel ( gpointer layer_and_vlp[2] )
3524 {
3525   const gchar *auto_save_name = vik_layer_get_name(VIK_LAYER(layer_and_vlp[0]));
3526   vik_trw_layer_export_gpsbabel ( VIK_TRW_LAYER (layer_and_vlp[0]), _("Export Layer"), auto_save_name );
3527 }
3528
3529 static void trw_layer_export_external_gpx_1 ( menu_array_layer values )
3530 {
3531   vik_trw_layer_export_external_gpx ( VIK_TRW_LAYER (values[MA_VTL]), a_vik_get_external_gpx_program_1() );
3532 }
3533
3534 static void trw_layer_export_external_gpx_2 ( menu_array_layer values )
3535 {
3536   vik_trw_layer_export_external_gpx ( VIK_TRW_LAYER (values[MA_VTL]), a_vik_get_external_gpx_program_2() );
3537 }
3538
3539 static void trw_layer_export_gpx_track ( menu_array_sublayer values )
3540 {
3541   VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
3542   VikTrack *trk;
3543   if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
3544     trk = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
3545   else
3546     trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
3547
3548   if ( !trk || !trk->name )
3549     return;
3550
3551   gchar *auto_save_name = append_file_ext ( trk->name, FILE_TYPE_GPX );
3552
3553   gchar *label = NULL;
3554   if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
3555     label = _("Export Route as GPX");
3556   else
3557     label = _("Export Track as GPX");
3558   vik_trw_layer_export ( VIK_TRW_LAYER (values[MA_VTL]), label, auto_save_name, trk, FILE_TYPE_GPX );
3559
3560   g_free ( auto_save_name );
3561 }
3562
3563 gboolean trw_layer_waypoint_find_uuid ( const gpointer id, const VikWaypoint *wp, gpointer udata )
3564 {
3565   wpu_udata *user_data = udata;
3566   if ( wp == user_data->wp ) {
3567     user_data->uuid = id;
3568     return TRUE;
3569   }
3570   return FALSE;
3571 }
3572
3573 static void trw_layer_goto_wp ( menu_array_layer values )
3574 {
3575   VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
3576   VikLayersPanel *vlp = VIK_LAYERS_PANEL(values[MA_VLP]);
3577   GtkWidget *dia = gtk_dialog_new_with_buttons (_("Find"),
3578                                                  VIK_GTK_WINDOW_FROM_LAYER(vtl),
3579                                                  GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
3580                                                  GTK_STOCK_CANCEL,
3581                                                  GTK_RESPONSE_REJECT,
3582                                                  GTK_STOCK_OK,
3583                                                  GTK_RESPONSE_ACCEPT,
3584                                                  NULL);
3585
3586   GtkWidget *label, *entry;
3587   label = gtk_label_new(_("Waypoint Name:"));
3588   entry = ui_entry_new ( NULL, GTK_ENTRY_ICON_SECONDARY );
3589
3590   gtk_box_pack_start(GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dia))), label, FALSE, FALSE, 0);
3591   gtk_box_pack_start(GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dia))), entry, FALSE, FALSE, 0);
3592   gtk_widget_show_all ( dia );
3593   // 'ok' when press return in the entry
3594   g_signal_connect_swapped ( entry, "activate", G_CALLBACK(a_dialog_response_accept), dia );
3595   gtk_dialog_set_default_response ( GTK_DIALOG(dia), GTK_RESPONSE_ACCEPT );
3596
3597   while ( gtk_dialog_run ( GTK_DIALOG(dia) ) == GTK_RESPONSE_ACCEPT )
3598   {
3599     gchar *name = g_strdup(gtk_entry_get_text(GTK_ENTRY(entry)));
3600     // Find *first* wp with the given name
3601     VikWaypoint *wp = vik_trw_layer_get_waypoint ( vtl, name );
3602
3603     if ( !wp )
3604       a_dialog_error_msg ( VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Waypoint not found in this layer.") );
3605     else
3606     {
3607       vik_viewport_set_center_coord ( vik_layers_panel_get_viewport(vlp), &(wp->coord), TRUE );
3608       vik_layers_panel_emit_update ( vlp );
3609
3610       // Find and select on the side panel
3611       wpu_udata udata;
3612       udata.wp   = wp;
3613       udata.uuid = NULL;
3614
3615       // Hmmm, want key of it
3616       gpointer wpf = g_hash_table_find ( vtl->waypoints, (GHRFunc) trw_layer_waypoint_find_uuid, (gpointer) &udata );
3617
3618       if ( wpf && udata.uuid ) {
3619         GtkTreeIter *it = g_hash_table_lookup ( vtl->waypoints_iters, udata.uuid );
3620         vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, it, TRUE );
3621       }
3622
3623       break;
3624     }
3625
3626     g_free ( name );
3627
3628   }
3629   gtk_widget_destroy ( dia );
3630 }
3631
3632 gboolean vik_trw_layer_new_waypoint ( VikTrwLayer *vtl, GtkWindow *w, const VikCoord *def_coord )
3633 {
3634   gchar *default_name = highest_wp_number_get(vtl);
3635   VikWaypoint *wp = vik_waypoint_new();
3636   gchar *returned_name;
3637   gboolean updated;
3638   wp->coord = *def_coord;
3639   
3640   // Attempt to auto set height if DEM data is available
3641   vik_waypoint_apply_dem_data ( wp, TRUE );
3642
3643   returned_name = a_dialog_waypoint ( w, default_name, vtl, wp, vtl->coord_mode, TRUE, &updated );
3644
3645   if ( returned_name )
3646   {
3647     wp->visible = TRUE;
3648     vik_trw_layer_add_waypoint ( vtl, returned_name, wp );
3649     g_free (default_name);
3650     g_free (returned_name);
3651     return TRUE;
3652   }
3653   g_free (default_name);
3654   vik_waypoint_free(wp);
3655   return FALSE;
3656 }
3657
3658 static void trw_layer_new_wikipedia_wp_viewport ( menu_array_layer values )
3659 {
3660   struct LatLon maxmin[2] = { {0.0,0.0}, {0.0,0.0} };
3661   VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
3662   VikLayersPanel *vlp = VIK_LAYERS_PANEL(values[MA_VLP]);
3663   VikWindow *vw = (VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl));
3664   VikViewport *vvp =  vik_window_viewport(vw);
3665
3666   // Note the order is max part first then min part - thus reverse order of use in min_max function:
3667   vik_viewport_get_min_max_lat_lon ( vvp, &maxmin[1].lat, &maxmin[0].lat, &maxmin[1].lon, &maxmin[0].lon );
3668   a_geonames_wikipedia_box((VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl)), vtl, maxmin);
3669   trw_layer_calculate_bounds_waypoints ( vtl );
3670   vik_layers_panel_emit_update ( vlp );
3671 }
3672
3673 static void trw_layer_new_wikipedia_wp_layer ( menu_array_layer values )
3674 {
3675   VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
3676   VikLayersPanel *vlp = VIK_LAYERS_PANEL(values[MA_VLP]);
3677   struct LatLon maxmin[2] = { {0.0,0.0}, {0.0,0.0} };
3678   
3679   trw_layer_find_maxmin (vtl, maxmin);
3680   a_geonames_wikipedia_box((VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl)), vtl, maxmin);
3681   trw_layer_calculate_bounds_waypoints ( vtl );
3682   vik_layers_panel_emit_update ( vlp );
3683 }
3684
3685 #ifdef VIK_CONFIG_GEOTAG
3686 static void trw_layer_geotagging_waypoint_mtime_keep ( menu_array_sublayer values )
3687 {
3688   VikWaypoint *wp = g_hash_table_lookup ( VIK_TRW_LAYER(values[MA_VTL])->waypoints, values[MA_SUBLAYER_ID] );
3689   if ( wp )
3690     // Update directly - not changing the mtime
3691     a_geotag_write_exif_gps ( wp->image, wp->coord, wp->altitude, TRUE );
3692 }
3693
3694 static void trw_layer_geotagging_waypoint_mtime_update ( menu_array_sublayer values )
3695 {
3696   VikWaypoint *wp = g_hash_table_lookup ( VIK_TRW_LAYER(values[MA_VTL])->waypoints, values[MA_SUBLAYER_ID] );
3697   if ( wp )
3698     // Update directly
3699     a_geotag_write_exif_gps ( wp->image, wp->coord, wp->altitude, FALSE );
3700 }
3701
3702 /*
3703  * Use code in separate file for this feature as reasonably complex
3704  */
3705 static void trw_layer_geotagging_track ( menu_array_sublayer values )
3706 {
3707   VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
3708   VikTrack *track = g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
3709   // Unset so can be reverified later if necessary
3710   vtl->has_verified_thumbnails = FALSE;
3711
3712   trw_layer_geotag_dialog ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
3713                             vtl,
3714                             NULL,
3715                             track );
3716 }
3717
3718 static void trw_layer_geotagging_waypoint ( menu_array_sublayer values )
3719 {
3720   VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
3721   VikWaypoint *wpt = g_hash_table_lookup ( vtl->waypoints, values[MA_SUBLAYER_ID] );
3722
3723   trw_layer_geotag_dialog ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
3724                             vtl,
3725                             wpt,
3726                             NULL );
3727 }
3728
3729 static void trw_layer_geotagging ( menu_array_layer values )
3730 {
3731   VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
3732   // Unset so can be reverified later if necessary
3733   vtl->has_verified_thumbnails = FALSE;
3734
3735   trw_layer_geotag_dialog ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
3736                             vtl,
3737                             NULL,
3738                             NULL );
3739 }
3740 #endif
3741
3742 // 'Acquires' - Same as in File Menu -> Acquire - applies into the selected TRW Layer //
3743
3744 static void trw_layer_acquire ( menu_array_layer values, VikDataSourceInterface *datasource )
3745 {
3746   VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
3747   VikLayersPanel *vlp = VIK_LAYERS_PANEL(values[MA_VLP]);
3748   VikWindow *vw = (VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl));
3749   VikViewport *vvp =  vik_window_viewport(vw);
3750
3751   vik_datasource_mode_t mode = datasource->mode;
3752   if ( mode == VIK_DATASOURCE_AUTO_LAYER_MANAGEMENT )
3753     mode = VIK_DATASOURCE_ADDTOLAYER;
3754   a_acquire ( vw, vlp, vvp, mode, datasource, NULL, NULL );
3755 }
3756
3757 /*
3758  * Acquire into this TRW Layer straight from GPS Device
3759  */
3760 static void trw_layer_acquire_gps_cb ( menu_array_layer values )
3761 {
3762   trw_layer_acquire ( values, &vik_datasource_gps_interface );
3763 }
3764
3765 /*
3766  * Acquire into this TRW Layer from Directions
3767  */
3768 static void trw_layer_acquire_routing_cb ( menu_array_layer values )
3769 {
3770   trw_layer_acquire ( values, &vik_datasource_routing_interface );
3771 }
3772
3773 /*
3774  * Acquire into this TRW Layer from an entered URL
3775  */
3776 static void trw_layer_acquire_url_cb ( menu_array_layer values )
3777 {
3778   trw_layer_acquire ( values, &vik_datasource_url_interface );
3779 }
3780
3781 #ifdef VIK_CONFIG_OPENSTREETMAP
3782 /*
3783  * Acquire into this TRW Layer from OSM
3784  */
3785 static void trw_layer_acquire_osm_cb ( menu_array_layer values )
3786 {
3787   trw_layer_acquire ( values, &vik_datasource_osm_interface );
3788 }
3789
3790 /**
3791  * Acquire into this TRW Layer from OSM for 'My' Traces
3792  */
3793 static void trw_layer_acquire_osm_my_traces_cb ( menu_array_layer values )
3794 {
3795   trw_layer_acquire ( values, &vik_datasource_osm_my_traces_interface );
3796 }
3797 #endif
3798
3799 #ifdef VIK_CONFIG_GEOCACHES
3800 /*
3801  * Acquire into this TRW Layer from Geocaching.com
3802  */
3803 static void trw_layer_acquire_geocache_cb ( menu_array_layer values )
3804 {
3805   trw_layer_acquire ( values, &vik_datasource_gc_interface );
3806 }
3807 #endif
3808
3809 #ifdef VIK_CONFIG_GEOTAG
3810 /*
3811  * Acquire into this TRW Layer from images
3812  */
3813 static void trw_layer_acquire_geotagged_cb ( menu_array_layer values )
3814 {
3815   VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
3816
3817   trw_layer_acquire ( values, &vik_datasource_geotag_interface );
3818
3819   // Reverify thumbnails as they may have changed
3820   vtl->has_verified_thumbnails = FALSE;
3821   trw_layer_verify_thumbnails ( vtl );
3822 }
3823 #endif
3824
3825 /*
3826  * Acquire into this TRW Layer from any GPS Babel supported file
3827  */
3828 static void trw_layer_acquire_file_cb ( menu_array_layer values )
3829 {
3830   trw_layer_acquire ( values, &vik_datasource_file_interface );
3831 }
3832
3833 static void trw_layer_gps_upload ( menu_array_layer values )
3834 {
3835   menu_array_sublayer data;
3836   gint ii;
3837   for ( ii = MA_VTL; ii < MA_LAST; ii++ )
3838     data[ii] = NULL;
3839   data[MA_VTL] = values[MA_VTL];
3840   data[MA_VLP] = values[MA_VLP];
3841
3842   trw_layer_gps_upload_any ( data );
3843 }
3844
3845 /**
3846  * If pass_along[3] is defined that this will upload just that track
3847  */
3848 static void trw_layer_gps_upload_any ( menu_array_sublayer values )
3849 {
3850   VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
3851   VikLayersPanel *vlp = VIK_LAYERS_PANEL(values[MA_VLP]);
3852
3853   // May not actually get a track here as values[2&3] can be null
3854   VikTrack *track = NULL;
3855   vik_gps_xfer_type xfer_type = TRK; // VIK_TRW_LAYER_SUBLAYER_TRACKS = 0 so hard to test different from NULL!
3856   gboolean xfer_all = FALSE;
3857
3858   if ( values[MA_SUBTYPE] ) {
3859     xfer_all = FALSE;
3860     if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE ) {
3861       track = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
3862       xfer_type = RTE;
3863     }
3864     else if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_TRACK ) {
3865       track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
3866       xfer_type = TRK;
3867     }
3868     else if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_WAYPOINTS ) {
3869       xfer_type = WPT;
3870     }
3871     else if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTES ) {
3872       xfer_type = RTE;
3873     }
3874   }
3875   else if ( !values[MA_CONFIRM] )
3876     xfer_all = TRUE; // i.e. whole layer
3877
3878   if (track && !track->visible) {
3879     a_dialog_error_msg ( VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Can not upload invisible track.") );
3880     return;
3881   }
3882
3883   GtkWidget *dialog = gtk_dialog_new_with_buttons ( _("GPS Upload"),
3884                                                     VIK_GTK_WINDOW_FROM_LAYER(vtl),
3885                                                     GTK_DIALOG_DESTROY_WITH_PARENT,
3886                                                     GTK_STOCK_OK,
3887                                                     GTK_RESPONSE_ACCEPT,
3888                                                     GTK_STOCK_CANCEL,
3889                                                     GTK_RESPONSE_REJECT,
3890                                                     NULL );
3891
3892   gtk_dialog_set_default_response ( GTK_DIALOG(dialog), GTK_RESPONSE_ACCEPT );
3893   GtkWidget *response_w = NULL;
3894 #if GTK_CHECK_VERSION (2, 20, 0)
3895   response_w = gtk_dialog_get_widget_for_response ( GTK_DIALOG(dialog), GTK_RESPONSE_ACCEPT );
3896 #endif
3897
3898   if ( response_w )
3899     gtk_widget_grab_focus ( response_w );
3900
3901   gpointer dgs = datasource_gps_setup ( dialog, xfer_type, xfer_all );
3902
3903   if ( gtk_dialog_run ( GTK_DIALOG(dialog) ) != GTK_RESPONSE_ACCEPT ) {
3904     datasource_gps_clean_up ( dgs );
3905     gtk_widget_destroy ( dialog );
3906     return;
3907   }
3908
3909   // Get info from reused datasource dialog widgets
3910   gchar* protocol = datasource_gps_get_protocol ( dgs );
3911   gchar* port = datasource_gps_get_descriptor ( dgs );
3912   // NB don't free the above strings as they're references to values held elsewhere
3913   gboolean do_tracks = datasource_gps_get_do_tracks ( dgs );
3914   gboolean do_routes = datasource_gps_get_do_routes ( dgs );
3915   gboolean do_waypoints = datasource_gps_get_do_waypoints ( dgs );
3916   gboolean turn_off = datasource_gps_get_off ( dgs );
3917
3918   gtk_widget_destroy ( dialog );
3919
3920   // When called from the viewport - work the corresponding layerspanel:
3921   if ( !vlp ) {
3922     vlp = vik_window_layers_panel ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)) );
3923   }
3924
3925   // Apply settings to transfer to the GPS device
3926   vik_gps_comm ( vtl,
3927                  track,
3928                  GPS_UP,
3929                  protocol,
3930                  port,
3931                  FALSE,
3932                  vik_layers_panel_get_viewport (vlp),
3933                  vlp,
3934                  do_tracks,
3935                  do_routes,
3936                  do_waypoints,
3937                  turn_off );
3938 }
3939
3940 static void trw_layer_new_wp ( menu_array_layer values )
3941 {
3942   VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
3943   VikLayersPanel *vlp = VIK_LAYERS_PANEL(values[MA_VLP]);
3944   /* TODO longone: okay, if layer above (aggregate) is invisible but vtl->visible is true, this redraws for no reason.
3945      instead return true if you want to update. */
3946   if ( vik_trw_layer_new_waypoint ( vtl, VIK_GTK_WINDOW_FROM_LAYER(vtl), vik_viewport_get_center(vik_layers_panel_get_viewport(vlp))) ) {
3947     trw_layer_calculate_bounds_waypoints ( vtl );
3948     if ( VIK_LAYER(vtl)->visible )
3949       vik_layers_panel_emit_update ( vlp );
3950   }
3951 }
3952
3953 static void new_track_create_common ( VikTrwLayer *vtl, gchar *name )
3954 {
3955   vtl->current_track = vik_track_new();
3956   vik_track_set_defaults ( vtl->current_track );
3957   vtl->current_track->visible = TRUE;
3958   if ( vtl->drawmode == DRAWMODE_ALL_SAME_COLOR )
3959     // Create track with the preferred colour from the layer properties
3960     vtl->current_track->color = vtl->track_color;
3961   else
3962     gdk_color_parse ( "#000000", &(vtl->current_track->color) );
3963   vtl->current_track->has_color = TRUE;
3964   vik_trw_layer_add_track ( vtl, name, vtl->current_track );
3965 }
3966
3967 static void trw_layer_new_track ( menu_array_layer values )
3968 {
3969   VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
3970
3971   if ( ! vtl->current_track ) {
3972     gchar *name = trw_layer_new_unique_sublayer_name ( vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, _("Track")) ;
3973     new_track_create_common ( vtl, name );
3974     g_free ( name );
3975
3976     vik_window_enable_layer_tool ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)), VIK_LAYER_TRW, TOOL_CREATE_TRACK );
3977   }
3978 }
3979
3980 static void new_route_create_common ( VikTrwLayer *vtl, gchar *name )
3981 {
3982   vtl->current_track = vik_track_new();
3983   vik_track_set_defaults ( vtl->current_track );
3984   vtl->current_track->visible = TRUE;
3985   vtl->current_track->is_route = TRUE;
3986   // By default make all routes red
3987   vtl->current_track->has_color = TRUE;
3988   gdk_color_parse ( "red", &vtl->current_track->color );
3989   vik_trw_layer_add_route ( vtl, name, vtl->current_track );
3990 }
3991
3992 static void trw_layer_new_route ( menu_array_layer values )
3993 {
3994   VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
3995
3996   if ( ! vtl->current_track ) {
3997     gchar *name = trw_layer_new_unique_sublayer_name ( vtl, VIK_TRW_LAYER_SUBLAYER_ROUTE, _("Route")) ;
3998     new_route_create_common ( vtl, name );
3999     g_free ( name );
4000     vik_window_enable_layer_tool ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)), VIK_LAYER_TRW, TOOL_CREATE_ROUTE );
4001   }
4002 }
4003
4004 static void trw_layer_auto_routes_view ( menu_array_layer values )
4005 {
4006   VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
4007   VikLayersPanel *vlp = VIK_LAYERS_PANEL(values[MA_VLP]);
4008
4009   if ( g_hash_table_size (vtl->routes) > 0 ) {
4010     struct LatLon maxmin[2] = { {0,0}, {0,0} };
4011     g_hash_table_foreach ( vtl->routes, (GHFunc) trw_layer_find_maxmin_tracks, maxmin );
4012     trw_layer_zoom_to_show_latlons ( vtl, vik_layers_panel_get_viewport (vlp), maxmin );
4013     vik_layers_panel_emit_update ( vlp );
4014   }
4015 }
4016
4017
4018 static void trw_layer_finish_track ( menu_array_layer values )
4019 {
4020   VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
4021   vtl->current_track = NULL;
4022   vtl->route_finder_started = FALSE;
4023   vik_layer_emit_update ( VIK_LAYER(vtl) );
4024 }
4025
4026 static void trw_layer_auto_tracks_view ( menu_array_layer values )
4027 {
4028   VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
4029   VikLayersPanel *vlp = VIK_LAYERS_PANEL(values[MA_VLP]);
4030
4031   if ( g_hash_table_size (vtl->tracks) > 0 ) {
4032     struct LatLon maxmin[2] = { {0,0}, {0,0} };
4033     g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_find_maxmin_tracks, maxmin );
4034     trw_layer_zoom_to_show_latlons ( vtl, vik_layers_panel_get_viewport (vlp), maxmin );
4035     vik_layers_panel_emit_update ( vlp );
4036   }
4037 }
4038
4039 static void trw_layer_single_waypoint_jump ( const gpointer id, const VikWaypoint *wp, gpointer vvp )
4040 {
4041   /* NB do not care if wp is visible or not */
4042   vik_viewport_set_center_coord ( VIK_VIEWPORT(vvp), &(wp->coord), TRUE );
4043 }
4044
4045 static void trw_layer_auto_waypoints_view ( menu_array_layer values )
4046 {
4047   VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
4048   VikLayersPanel *vlp = VIK_LAYERS_PANEL(values[MA_VLP]);
4049
4050   /* Only 1 waypoint - jump straight to it */
4051   if ( g_hash_table_size (vtl->waypoints) == 1 ) {
4052     VikViewport *vvp = vik_layers_panel_get_viewport (vlp);
4053     g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_single_waypoint_jump, (gpointer) vvp );
4054   }
4055   /* If at least 2 waypoints - find center and then zoom to fit */
4056   else if ( g_hash_table_size (vtl->waypoints) > 1 )
4057   {
4058     struct LatLon maxmin[2] = { {0,0}, {0,0} };
4059     maxmin[0].lat = vtl->waypoints_bbox.north;
4060     maxmin[1].lat = vtl->waypoints_bbox.south;
4061     maxmin[0].lon = vtl->waypoints_bbox.east;
4062     maxmin[1].lon = vtl->waypoints_bbox.west;
4063     trw_layer_zoom_to_show_latlons ( vtl, vik_layers_panel_get_viewport (vlp), maxmin );
4064   }
4065
4066   vik_layers_panel_emit_update ( vlp );
4067 }
4068
4069 void trw_layer_osm_traces_upload_cb ( menu_array_layer values )
4070 {
4071   osm_traces_upload_viktrwlayer(VIK_TRW_LAYER(values[MA_VTL]), NULL);
4072 }
4073
4074 void trw_layer_osm_traces_upload_track_cb ( menu_array_sublayer values )
4075 {
4076   if ( values[MA_MISC] ) {
4077     VikTrack *trk = VIK_TRACK(values[MA_MISC]);
4078     osm_traces_upload_viktrwlayer(VIK_TRW_LAYER(values[MA_VTL]), trk);
4079   }
4080 }
4081
4082 static GtkWidget* create_external_submenu ( GtkMenu *menu )
4083 {
4084   GtkWidget *external_submenu = gtk_menu_new ();
4085   GtkWidget *item = gtk_image_menu_item_new_with_mnemonic ( _("Externa_l") );
4086   gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_EXECUTE, GTK_ICON_SIZE_MENU) );
4087   gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
4088   gtk_widget_show ( item );
4089   gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), external_submenu );
4090   return external_submenu;
4091 }
4092
4093 static void trw_layer_add_menu_items ( VikTrwLayer *vtl, GtkMenu *menu, gpointer vlp )
4094 {
4095   static menu_array_layer pass_along;
4096   GtkWidget *item;
4097   GtkWidget *export_submenu;
4098   pass_along[MA_VTL] = vtl;
4099   pass_along[MA_VLP] = vlp;
4100
4101   item = gtk_menu_item_new();
4102   gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
4103   gtk_widget_show ( item );
4104
4105   if ( vtl->current_track ) {
4106     if ( vtl->current_track->is_route )
4107       item = gtk_menu_item_new_with_mnemonic ( _("_Finish Route") );
4108     else
4109       item = gtk_menu_item_new_with_mnemonic ( _("_Finish Track") );
4110     g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_finish_track), pass_along );
4111     gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
4112     gtk_widget_show ( item );
4113
4114     // Add separator
4115     item = gtk_menu_item_new ();
4116     gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
4117     gtk_widget_show ( item );
4118   }
4119
4120   /* Now with icons */
4121   item = gtk_image_menu_item_new_with_mnemonic ( _("_View Layer") );
4122   gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ZOOM_FIT, GTK_ICON_SIZE_MENU) );
4123   g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_view), pass_along );
4124   gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
4125   gtk_widget_show ( item );
4126
4127   GtkWidget *view_submenu = gtk_menu_new();
4128   item = gtk_image_menu_item_new_with_mnemonic ( _("V_iew") );
4129   gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_FIND, GTK_ICON_SIZE_MENU) );
4130   gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
4131   gtk_widget_show ( item );
4132   gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), view_submenu );
4133
4134   item = gtk_menu_item_new_with_mnemonic ( _("View All _Tracks") );
4135   g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_tracks_view), pass_along );
4136   gtk_menu_shell_append (GTK_MENU_SHELL (view_submenu), item);
4137   gtk_widget_show ( item );
4138
4139   item = gtk_menu_item_new_with_mnemonic ( _("View All _Routes") );
4140   g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_routes_view), pass_along );
4141   gtk_menu_shell_append (GTK_MENU_SHELL (view_submenu), item);
4142   gtk_widget_show ( item );
4143
4144   item = gtk_menu_item_new_with_mnemonic ( _("View All _Waypoints") );
4145   g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_waypoints_view), pass_along );
4146   gtk_menu_shell_append (GTK_MENU_SHELL (view_submenu), item);
4147   gtk_widget_show ( item );
4148
4149   item = gtk_image_menu_item_new_with_mnemonic ( _("_Goto Center of Layer") );
4150   gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_JUMP_TO, GTK_ICON_SIZE_MENU) );
4151   g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_centerize), pass_along );
4152   gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
4153   gtk_widget_show ( item );
4154
4155   item = gtk_menu_item_new_with_mnemonic ( _("Goto _Waypoint...") );
4156   g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_wp), pass_along );
4157   gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
4158   gtk_widget_show ( item );
4159
4160   export_submenu = gtk_menu_new ();
4161   item = gtk_image_menu_item_new_with_mnemonic ( _("_Export Layer") );
4162   gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_HARDDISK, GTK_ICON_SIZE_MENU) );
4163   gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
4164   gtk_widget_show ( item );
4165   gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), export_submenu );
4166   
4167   item = gtk_menu_item_new_with_mnemonic ( _("Export as GPS_Point...") );
4168   g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_gpspoint), pass_along );
4169   gtk_menu_shell_append (GTK_MENU_SHELL (export_submenu), item);
4170   gtk_widget_show ( item );
4171
4172   item = gtk_menu_item_new_with_mnemonic ( _("Export as GPS_Mapper...") );
4173   g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_gpsmapper), pass_along );
4174   gtk_menu_shell_append (GTK_MENU_SHELL (export_submenu), item);
4175   gtk_widget_show ( item );
4176
4177   item = gtk_menu_item_new_with_mnemonic ( _("Export as _GPX...") );
4178   g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_gpx), pass_along );
4179   gtk_menu_shell_append (GTK_MENU_SHELL (export_submenu), item);
4180   gtk_widget_show ( item );
4181
4182   if ( a_babel_available () ) {
4183     item = gtk_menu_item_new_with_mnemonic ( _("Export as _KML...") );
4184     g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_kml), pass_along );
4185     gtk_menu_shell_append (GTK_MENU_SHELL (export_submenu), item);
4186     gtk_widget_show ( item );
4187   }
4188
4189   if ( have_geojson_export ) {
4190     item = gtk_menu_item_new_with_mnemonic ( _("Export as GEO_JSON...") );
4191     g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_geojson), pass_along );
4192     gtk_menu_shell_append (GTK_MENU_SHELL (export_submenu), item);
4193     gtk_widget_show ( item );
4194   }
4195
4196   if ( a_babel_available () ) {
4197     item = gtk_menu_item_new_with_mnemonic ( _("Export via GPSbabel...") );
4198     g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_babel), pass_along );
4199     gtk_menu_shell_append (GTK_MENU_SHELL (export_submenu), item);
4200     gtk_widget_show ( item );
4201   }
4202
4203   gchar* external1 = g_strdup_printf ( _("Open with External Program_1: %s"), a_vik_get_external_gpx_program_1() );
4204   item = gtk_menu_item_new_with_mnemonic ( external1 );
4205   g_free ( external1 );
4206   g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_external_gpx_1), pass_along );
4207   gtk_menu_shell_append (GTK_MENU_SHELL (export_submenu), item);
4208   gtk_widget_show ( item );
4209
4210   gchar* external2 = g_strdup_printf ( _("Open with External Program_2: %s"), a_vik_get_external_gpx_program_2() );
4211   item = gtk_menu_item_new_with_mnemonic ( external2 );
4212   g_free ( external2 );
4213   g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_external_gpx_2), pass_along );
4214   gtk_menu_shell_append (GTK_MENU_SHELL (export_submenu), item);
4215   gtk_widget_show ( item );
4216
4217   GtkWidget *new_submenu = gtk_menu_new();
4218   item = gtk_image_menu_item_new_with_mnemonic ( _("_New") );
4219   gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NEW, GTK_ICON_SIZE_MENU) );
4220   gtk_menu_shell_append(GTK_MENU_SHELL (menu), item);
4221   gtk_widget_show(item);
4222   gtk_menu_item_set_submenu(GTK_MENU_ITEM(item), new_submenu);
4223
4224   item = gtk_image_menu_item_new_with_mnemonic ( _("New _Waypoint...") );
4225   gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NEW, GTK_ICON_SIZE_MENU) );
4226   g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_wp), pass_along );
4227   gtk_menu_shell_append (GTK_MENU_SHELL (new_submenu), item);
4228   gtk_widget_show ( item );
4229
4230   item = gtk_image_menu_item_new_with_mnemonic ( _("New _Track") );
4231   gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NEW, GTK_ICON_SIZE_MENU) );
4232   g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_track), pass_along );
4233   gtk_menu_shell_append (GTK_MENU_SHELL (new_submenu), item);
4234   gtk_widget_show ( item );
4235   // Make it available only when a new track *not* already in progress
4236   gtk_widget_set_sensitive ( item, ! (gboolean)GPOINTER_TO_INT(vtl->current_track) );
4237
4238   item = gtk_image_menu_item_new_with_mnemonic ( _("New _Route") );
4239   gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NEW, GTK_ICON_SIZE_MENU) );
4240   g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_route), pass_along );
4241   gtk_menu_shell_append (GTK_MENU_SHELL (new_submenu), item);
4242   gtk_widget_show ( item );
4243   // Make it available only when a new track *not* already in progress
4244   gtk_widget_set_sensitive ( item, ! (gboolean)GPOINTER_TO_INT(vtl->current_track) );
4245
4246 #ifdef VIK_CONFIG_GEOTAG
4247   item = gtk_menu_item_new_with_mnemonic ( _("Geotag _Images...") );
4248   g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_geotagging), pass_along );
4249   gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
4250   gtk_widget_show ( item );
4251 #endif
4252
4253   GtkWidget *acquire_submenu = gtk_menu_new ();
4254   item = gtk_image_menu_item_new_with_mnemonic ( _("_Acquire") );
4255   gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_DOWN, GTK_ICON_SIZE_MENU) );
4256   gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
4257   gtk_widget_show ( item );
4258   gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), acquire_submenu );
4259   
4260   item = gtk_menu_item_new_with_mnemonic ( _("From _GPS...") );
4261   g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_acquire_gps_cb), pass_along );
4262   gtk_menu_shell_append (GTK_MENU_SHELL (acquire_submenu), item);
4263   gtk_widget_show ( item );
4264
4265   /* FIXME: only add menu when at least a routing engine has support for Directions */
4266   item = gtk_menu_item_new_with_mnemonic ( _("From _Directions...") );
4267   g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_acquire_routing_cb), pass_along );
4268   gtk_menu_shell_append (GTK_MENU_SHELL (acquire_submenu), item);
4269   gtk_widget_show ( item );
4270
4271 #ifdef VIK_CONFIG_OPENSTREETMAP
4272   item = gtk_menu_item_new_with_mnemonic ( _("From _OSM Traces...") );
4273   g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_acquire_osm_cb), pass_along );
4274   gtk_menu_shell_append (GTK_MENU_SHELL (acquire_submenu), item);
4275   gtk_widget_show ( item );
4276
4277   item = gtk_menu_item_new_with_mnemonic ( _("From _My OSM Traces...") );
4278   g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_acquire_osm_my_traces_cb), pass_along );
4279   gtk_menu_shell_append (GTK_MENU_SHELL (acquire_submenu), item);
4280   gtk_widget_show ( item );
4281 #endif
4282
4283   item = gtk_menu_item_new_with_mnemonic ( _("From _URL...") );
4284   g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_acquire_url_cb), pass_along );
4285   gtk_menu_shell_append (GTK_MENU_SHELL (acquire_submenu), item);
4286   gtk_widget_show ( item );
4287
4288 #ifdef VIK_CONFIG_GEONAMES
4289   GtkWidget *wikipedia_submenu = gtk_menu_new();
4290   item = gtk_image_menu_item_new_with_mnemonic ( _("From _Wikipedia Waypoints") );
4291   gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ADD, GTK_ICON_SIZE_MENU) );
4292   gtk_menu_shell_append(GTK_MENU_SHELL (acquire_submenu), item);
4293   gtk_widget_show(item);
4294   gtk_menu_item_set_submenu(GTK_MENU_ITEM(item), wikipedia_submenu);
4295
4296   item = gtk_image_menu_item_new_with_mnemonic ( _("Within _Layer Bounds") );
4297   gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ZOOM_FIT, GTK_ICON_SIZE_MENU) );
4298   g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_wikipedia_wp_layer), pass_along );
4299   gtk_menu_shell_append (GTK_MENU_SHELL (wikipedia_submenu), item);
4300   gtk_widget_show ( item );
4301
4302   item = gtk_image_menu_item_new_with_mnemonic ( _("Within _Current View") );
4303   gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ZOOM_100, GTK_ICON_SIZE_MENU) );
4304   g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_wikipedia_wp_viewport), pass_along );
4305   gtk_menu_shell_append (GTK_MENU_SHELL (wikipedia_submenu), item);
4306   gtk_widget_show ( item );
4307 #endif
4308
4309 #ifdef VIK_CONFIG_GEOCACHES
4310   item = gtk_menu_item_new_with_mnemonic ( _("From Geo_caching...") );
4311   g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_acquire_geocache_cb), pass_along );
4312   gtk_menu_shell_append (GTK_MENU_SHELL (acquire_submenu), item);
4313   gtk_widget_show ( item );
4314 #endif
4315
4316 #ifdef VIK_CONFIG_GEOTAG
4317   item = gtk_menu_item_new_with_mnemonic ( _("From Geotagged _Images...") );
4318   g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_acquire_geotagged_cb), pass_along );
4319   gtk_menu_shell_append (GTK_MENU_SHELL (acquire_submenu), item);
4320   gtk_widget_show ( item );
4321 #endif
4322
4323   if ( a_babel_available () ) {
4324     item = gtk_menu_item_new_with_mnemonic ( _("From _File...") );
4325     g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_acquire_file_cb), pass_along );
4326     gtk_menu_shell_append (GTK_MENU_SHELL (acquire_submenu), item);
4327     gtk_widget_set_tooltip_text (item, _("Import File With GPS_Babel..."));
4328     gtk_widget_show ( item );
4329   }
4330
4331   vik_ext_tool_datasources_add_menu_items_to_menu ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)), GTK_MENU (acquire_submenu) );
4332
4333   GtkWidget *upload_submenu = gtk_menu_new ();
4334   item = gtk_image_menu_item_new_with_mnemonic ( _("_Upload") );
4335   gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_UP, GTK_ICON_SIZE_MENU) );
4336   gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
4337   gtk_widget_show ( item );
4338   gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), upload_submenu );
4339
4340   item = gtk_image_menu_item_new_with_mnemonic ( _("Upload to _GPS...") );
4341   gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_FORWARD, GTK_ICON_SIZE_MENU) );
4342   g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_gps_upload), pass_along );
4343   gtk_menu_shell_append (GTK_MENU_SHELL (upload_submenu), item);
4344   gtk_widget_show ( item );
4345
4346 #ifdef VIK_CONFIG_OPENSTREETMAP 
4347   item = gtk_image_menu_item_new_with_mnemonic ( _("Upload to _OSM...") );
4348   gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_UP, GTK_ICON_SIZE_MENU) );
4349   g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_osm_traces_upload_cb), pass_along );
4350   gtk_menu_shell_append (GTK_MENU_SHELL (upload_submenu), item);
4351   gtk_widget_show ( item );
4352 #endif
4353
4354   GtkWidget *delete_submenu = gtk_menu_new ();
4355   item = gtk_image_menu_item_new_with_mnemonic ( _("De_lete") );
4356   gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU) );
4357   gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
4358   gtk_widget_show ( item );
4359   gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), delete_submenu );
4360   
4361   item = gtk_image_menu_item_new_with_mnemonic ( _("Delete All _Tracks") );
4362   gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU) );
4363   g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_all_tracks), pass_along );
4364   gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
4365   gtk_widget_show ( item );
4366   
4367   item = gtk_image_menu_item_new_with_mnemonic ( _("Delete Tracks _From Selection...") );
4368   gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
4369   g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_tracks_from_selection), pass_along );
4370   gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
4371   gtk_widget_show ( item );
4372
4373   item = gtk_image_menu_item_new_with_mnemonic ( _("Delete _All Routes") );
4374   gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU) );
4375   g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_all_routes), pass_along );
4376   gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
4377   gtk_widget_show ( item );
4378
4379   item = gtk_image_menu_item_new_with_mnemonic ( _("_Delete Routes From Selection...") );
4380   gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
4381   g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_routes_from_selection), pass_along );
4382   gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
4383   gtk_widget_show ( item );
4384   
4385   item = gtk_image_menu_item_new_with_mnemonic ( _("Delete All _Waypoints") );
4386   gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU) );
4387   g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_all_waypoints), pass_along );
4388   gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
4389   gtk_widget_show ( item );
4390   
4391   item = gtk_image_menu_item_new_with_mnemonic ( _("Delete Waypoints From _Selection...") );
4392   gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
4393   g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_waypoints_from_selection), pass_along );
4394   gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
4395   gtk_widget_show ( item );
4396   
4397   item = a_acquire_trwlayer_menu ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)), vlp,
4398                                    vik_layers_panel_get_viewport(VIK_LAYERS_PANEL(vlp)), vtl );
4399   if ( item ) {
4400     gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
4401     gtk_widget_show ( item );
4402   }  
4403
4404   item = a_acquire_trwlayer_track_menu ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)), vlp,
4405                                          vik_layers_panel_get_viewport(VIK_LAYERS_PANEL(vlp)), vtl );
4406   if ( item ) {
4407     gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
4408     gtk_widget_show ( item );
4409   }
4410
4411   item = gtk_image_menu_item_new_with_mnemonic ( _("Track _List...") );
4412   gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
4413   g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_track_list_dialog), pass_along );
4414   gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
4415   gtk_widget_show ( item );
4416   gtk_widget_set_sensitive ( item, (gboolean)(g_hash_table_size (vtl->tracks)+g_hash_table_size (vtl->routes)) );
4417
4418   item = gtk_image_menu_item_new_with_mnemonic ( _("_Waypoint List...") );
4419   gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
4420   g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_waypoint_list_dialog), pass_along );
4421   gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
4422   gtk_widget_show ( item );
4423   gtk_widget_set_sensitive ( item, (gboolean)(g_hash_table_size (vtl->waypoints)) );
4424
4425   GtkWidget *external_submenu = create_external_submenu ( menu );
4426   // TODO: Should use selected layer's centre - rather than implicitly using the current viewport
4427   vik_ext_tools_add_menu_items_to_menu ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)), GTK_MENU (external_submenu), NULL );
4428 }
4429
4430 // Fake Waypoint UUIDs vi simple increasing integer
4431 static guint wp_uuid = 0;
4432
4433 void vik_trw_layer_add_waypoint ( VikTrwLayer *vtl, gchar *name, VikWaypoint *wp )
4434 {
4435   wp_uuid++;
4436
4437   vik_waypoint_set_name (wp, name);
4438
4439   if ( VIK_LAYER(vtl)->realized )
4440   {
4441     // Do we need to create the sublayer:
4442     if ( g_hash_table_size (vtl->waypoints) == 0 ) {
4443       trw_layer_add_sublayer_waypoints ( vtl, VIK_LAYER(vtl)->vt, &(VIK_LAYER(vtl)->iter) );
4444     }
4445
4446     GtkTreeIter *iter = g_malloc(sizeof(GtkTreeIter));
4447
4448     time_t timestamp = 0;
4449     if ( wp->has_timestamp )
4450       timestamp = wp->timestamp;
4451
4452     // Visibility column always needed for waypoints
4453     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, timestamp );
4454
4455     // Actual setting of visibility dependent on the waypoint
4456     vik_treeview_item_set_visible ( VIK_LAYER(vtl)->vt, iter, wp->visible );
4457
4458     g_hash_table_insert ( vtl->waypoints_iters, GUINT_TO_POINTER(wp_uuid), iter );
4459
4460     // Sort now as post_read is not called on a realized waypoint
4461     vik_treeview_sort_children ( VIK_LAYER(vtl)->vt, &(vtl->waypoints_iter), vtl->wp_sort_order );
4462   }
4463
4464   highest_wp_number_add_wp(vtl, name);
4465   g_hash_table_insert ( vtl->waypoints, GUINT_TO_POINTER(wp_uuid), wp );
4466  
4467 }
4468
4469 // Fake Track UUIDs vi simple increasing integer
4470 static guint tr_uuid = 0;
4471
4472 void vik_trw_layer_add_track ( VikTrwLayer *vtl, gchar *name, VikTrack *t )
4473 {
4474   tr_uuid++;
4475
4476   vik_track_set_name (t, name);
4477
4478   if ( VIK_LAYER(vtl)->realized )
4479   {
4480     // Do we need to create the sublayer:
4481     if ( g_hash_table_size (vtl->tracks) == 0 ) {
4482       trw_layer_add_sublayer_tracks ( vtl, VIK_LAYER(vtl)->vt, &(VIK_LAYER(vtl)->iter) );
4483     }
4484
4485     GtkTreeIter *iter = g_malloc(sizeof(GtkTreeIter));
4486
4487     time_t timestamp = 0;
4488     VikTrackpoint *tpt = vik_track_get_tp_first(t);
4489     if ( tpt && tpt->has_timestamp )
4490       timestamp = tpt->timestamp;
4491
4492     // Visibility column always needed for tracks
4493     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, timestamp );
4494
4495     // Actual setting of visibility dependent on the track
4496     vik_treeview_item_set_visible ( VIK_LAYER(vtl)->vt, iter, t->visible );
4497
4498     g_hash_table_insert ( vtl->tracks_iters, GUINT_TO_POINTER(tr_uuid), iter );
4499
4500     // Sort now as post_read is not called on a realized track
4501     vik_treeview_sort_children ( VIK_LAYER(vtl)->vt, &(vtl->tracks_iter), vtl->track_sort_order );
4502   }
4503
4504   g_hash_table_insert ( vtl->tracks, GUINT_TO_POINTER(tr_uuid), t );
4505
4506   trw_layer_update_treeview ( vtl, t );
4507 }
4508
4509 // Fake Route UUIDs vi simple increasing integer
4510 static guint rt_uuid = 0;
4511
4512 void vik_trw_layer_add_route ( VikTrwLayer *vtl, gchar *name, VikTrack *t )
4513 {
4514   rt_uuid++;
4515
4516   vik_track_set_name (t, name);
4517
4518   if ( VIK_LAYER(vtl)->realized )
4519   {
4520     // Do we need to create the sublayer:
4521     if ( g_hash_table_size (vtl->routes) == 0 ) {
4522       trw_layer_add_sublayer_routes ( vtl, VIK_LAYER(vtl)->vt, &(VIK_LAYER(vtl)->iter) );
4523     }
4524
4525     GtkTreeIter *iter = g_malloc(sizeof(GtkTreeIter));
4526     // Visibility column always needed for routes
4527     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, 0 ); // Routes don't have times
4528     // Actual setting of visibility dependent on the route
4529     vik_treeview_item_set_visible ( VIK_LAYER(vtl)->vt, iter, t->visible );
4530
4531     g_hash_table_insert ( vtl->routes_iters, GUINT_TO_POINTER(rt_uuid), iter );
4532
4533     // Sort now as post_read is not called on a realized route
4534     vik_treeview_sort_children ( VIK_LAYER(vtl)->vt, &(vtl->routes_iter), vtl->track_sort_order );
4535   }
4536
4537   g_hash_table_insert ( vtl->routes, GUINT_TO_POINTER(rt_uuid), t );
4538
4539   trw_layer_update_treeview ( vtl, t );
4540 }
4541
4542 /* to be called whenever a track has been deleted or may have been changed. */
4543 void trw_layer_cancel_tps_of_track ( VikTrwLayer *vtl, VikTrack *trk )
4544 {
4545   if (vtl->current_tp_track == trk )
4546     trw_layer_cancel_current_tp ( vtl, FALSE );
4547 }
4548
4549 /**
4550  * Normally this is done to due the waypoint size preference having changed
4551  */
4552 void vik_trw_layer_reset_waypoints ( VikTrwLayer *vtl )
4553 {
4554   GHashTableIter iter;
4555   gpointer key, value;
4556
4557   // Foreach waypoint
4558   g_hash_table_iter_init ( &iter, vtl->waypoints );
4559   while ( g_hash_table_iter_next ( &iter, &key, &value ) ) {
4560     VikWaypoint *wp = VIK_WAYPOINT(value);
4561     if ( wp->symbol ) {
4562       // Reapply symbol setting to update the pixbuf
4563       gchar *tmp_symbol = g_strdup ( wp->symbol );
4564       vik_waypoint_set_symbol ( wp, tmp_symbol );
4565       g_free ( tmp_symbol );
4566     }
4567   }
4568 }
4569
4570 /**
4571  * trw_layer_new_unique_sublayer_name:
4572  *
4573  * Allocates a unique new name
4574  */
4575 gchar *trw_layer_new_unique_sublayer_name (VikTrwLayer *vtl, gint sublayer_type, const gchar *name)
4576 {
4577   gint i = 2;
4578   gchar *newname = g_strdup(name);
4579
4580   gpointer id = NULL;
4581   do {
4582     id = NULL;
4583     switch ( sublayer_type ) {
4584     case VIK_TRW_LAYER_SUBLAYER_TRACK:
4585       id = (gpointer) vik_trw_layer_get_track ( vtl, newname );
4586       break;
4587     case VIK_TRW_LAYER_SUBLAYER_WAYPOINT:
4588       id = (gpointer) vik_trw_layer_get_waypoint ( vtl, newname );
4589       break;
4590     default:
4591       id = (gpointer) vik_trw_layer_get_route ( vtl, newname );
4592       break;
4593     }
4594     // If found a name already in use try adding 1 to it and we try again
4595     if ( id ) {
4596       const gchar *corename = newname;
4597       gint newi = i;
4598       // If name is already of the form text#N
4599       //  set name to text and i to N+1
4600       gchar **tokens = g_regex_split_simple ( "#(\\d+)", newname, G_REGEX_CASELESS, 0 );
4601       if ( tokens ) {
4602         corename = tokens[0];
4603         if ( tokens[1] ) {
4604           newi = atoi ( tokens[1] ) + 1;
4605         }
4606       }
4607       gchar *new_newname = g_strdup_printf("%s#%d", corename, newi);
4608       g_strfreev ( tokens );
4609       g_free(newname);
4610       newname = new_newname;
4611       i++;
4612     }
4613   } while ( id != NULL);
4614
4615   return newname;
4616 }
4617
4618 void vik_trw_layer_filein_add_waypoint ( VikTrwLayer *vtl, gchar *name, VikWaypoint *wp )
4619 {
4620   // No more uniqueness of name forced when loading from a file
4621   // This now makes this function a little redunant as we just flow the parameters through
4622   vik_trw_layer_add_waypoint ( vtl, name, wp );
4623 }
4624
4625 void vik_trw_layer_filein_add_track ( VikTrwLayer *vtl, gchar *name, VikTrack *tr )
4626 {
4627   if ( vtl->route_finder_append && vtl->current_track ) {
4628     vik_track_remove_dup_points ( tr ); /* make "double point" track work to undo */
4629
4630     // enforce end of current track equal to start of tr
4631     VikTrackpoint *cur_end = vik_track_get_tp_last ( vtl->current_track );
4632     VikTrackpoint *new_start = vik_track_get_tp_first ( tr );
4633     if ( cur_end && new_start ) {
4634       if ( ! vik_coord_equals ( &cur_end->coord, &new_start->coord ) ) {
4635           vik_track_add_trackpoint ( vtl->current_track,
4636                                      vik_trackpoint_copy ( cur_end ),
4637                                      FALSE );
4638       }
4639     }
4640
4641     vik_track_steal_and_append_trackpoints ( vtl->current_track, tr );
4642     vik_track_free ( tr );
4643     vtl->route_finder_append = FALSE; /* this means we have added it */
4644   } else {
4645
4646     // No more uniqueness of name forced when loading from a file
4647     if ( tr->is_route )
4648       vik_trw_layer_add_route ( vtl, name, tr );
4649     else
4650       vik_trw_layer_add_track ( vtl, name, tr );
4651
4652     if ( vtl->route_finder_check_added_track ) {
4653       vik_track_remove_dup_points ( tr ); /* make "double point" track work to undo */
4654       vtl->route_finder_added_track = tr;
4655     }
4656   }
4657 }
4658
4659 static void trw_layer_enum_item ( gpointer id, GList **tr, GList **l )
4660 {
4661   *l = g_list_append(*l, id);
4662 }
4663
4664 /*
4665  * Move an item from one TRW layer to another TRW layer
4666  */
4667 static void trw_layer_move_item ( VikTrwLayer *vtl_src, VikTrwLayer *vtl_dest, gpointer id, gint type )
4668 {
4669   // When an item is moved the name is checked to see if it clashes with an existing name
4670   //  in the destination layer and if so then it is allocated a new name
4671
4672   // TODO reconsider strategy when moving within layer (if anything...)
4673   if ( vtl_src == vtl_dest )
4674     return;
4675
4676   if (type == VIK_TRW_LAYER_SUBLAYER_TRACK) {
4677     VikTrack *trk = g_hash_table_lookup ( vtl_src->tracks, id );
4678
4679     gchar *newname = trw_layer_new_unique_sublayer_name ( vtl_dest, type, trk->name );
4680
4681     VikTrack *trk2 = vik_track_copy ( trk, TRUE );
4682     vik_trw_layer_add_track ( vtl_dest, newname, trk2 );
4683     g_free ( newname );
4684     vik_trw_layer_delete_track ( vtl_src, trk );
4685     // Reset layer timestamps in case they have now changed
4686     vik_treeview_item_set_timestamp ( vtl_dest->vl.vt, &vtl_dest->vl.iter, trw_layer_get_timestamp(vtl_dest) );
4687     vik_treeview_item_set_timestamp ( vtl_src->vl.vt, &vtl_src->vl.iter, trw_layer_get_timestamp(vtl_src) );
4688   }
4689
4690   if (type == VIK_TRW_LAYER_SUBLAYER_ROUTE) {
4691     VikTrack *trk = g_hash_table_lookup ( vtl_src->routes, id );
4692
4693     gchar *newname = trw_layer_new_unique_sublayer_name ( vtl_dest, type, trk->name );
4694
4695     VikTrack *trk2 = vik_track_copy ( trk, TRUE );
4696     vik_trw_layer_add_route ( vtl_dest, newname, trk2 );
4697     g_free ( newname );
4698     vik_trw_layer_delete_route ( vtl_src, trk );
4699   }
4700
4701   if (type == VIK_TRW_LAYER_SUBLAYER_WAYPOINT) {
4702     VikWaypoint *wp = g_hash_table_lookup ( vtl_src->waypoints, id );
4703
4704     gchar *newname = trw_layer_new_unique_sublayer_name ( vtl_dest, type, wp->name );
4705
4706     VikWaypoint *wp2 = vik_waypoint_copy ( wp );
4707     vik_trw_layer_add_waypoint ( vtl_dest, newname, wp2 );
4708     g_free ( newname );
4709     trw_layer_delete_waypoint ( vtl_src, wp );
4710
4711     // Recalculate bounds even if not renamed as maybe dragged between layers
4712     trw_layer_calculate_bounds_waypoints ( vtl_dest );
4713     trw_layer_calculate_bounds_waypoints ( vtl_src );
4714     // Reset layer timestamps in case they have now changed
4715     vik_treeview_item_set_timestamp ( vtl_dest->vl.vt, &vtl_dest->vl.iter, trw_layer_get_timestamp(vtl_dest) );
4716     vik_treeview_item_set_timestamp ( vtl_src->vl.vt, &vtl_src->vl.iter, trw_layer_get_timestamp(vtl_src) );
4717   }
4718 }
4719
4720 static void trw_layer_drag_drop_request ( VikTrwLayer *vtl_src, VikTrwLayer *vtl_dest, GtkTreeIter *src_item_iter, GtkTreePath *dest_path )
4721 {
4722   VikTreeview *vt = VIK_LAYER(vtl_src)->vt;
4723   gint type = vik_treeview_item_get_data(vt, src_item_iter);
4724
4725   if (!vik_treeview_item_get_pointer(vt, src_item_iter)) {
4726     GList *items = NULL;
4727     GList *iter;
4728
4729     if (type==VIK_TRW_LAYER_SUBLAYER_TRACKS) {
4730       g_hash_table_foreach ( vtl_src->tracks, (GHFunc)trw_layer_enum_item, &items);
4731     } 
4732     if (type==VIK_TRW_LAYER_SUBLAYER_WAYPOINTS) {
4733       g_hash_table_foreach ( vtl_src->waypoints, (GHFunc)trw_layer_enum_item, &items);
4734     }    
4735     if (type==VIK_TRW_LAYER_SUBLAYER_ROUTES) {
4736       g_hash_table_foreach ( vtl_src->routes, (GHFunc)trw_layer_enum_item, &items);
4737     }
4738
4739     iter = items;
4740     while (iter) {
4741       if (type==VIK_TRW_LAYER_SUBLAYER_TRACKS) {
4742         trw_layer_move_item ( vtl_src, vtl_dest, iter->data, VIK_TRW_LAYER_SUBLAYER_TRACK);
4743       } else if (type==VIK_TRW_LAYER_SUBLAYER_ROUTES) {
4744         trw_layer_move_item ( vtl_src, vtl_dest, iter->data, VIK_TRW_LAYER_SUBLAYER_ROUTE);
4745       } else {
4746         trw_layer_move_item ( vtl_src, vtl_dest, iter->data, VIK_TRW_LAYER_SUBLAYER_WAYPOINT);
4747       }
4748       iter = iter->next;
4749     }
4750     if (items) 
4751       g_list_free(items);
4752   } else {
4753     gchar *name = vik_treeview_item_get_pointer(vt, src_item_iter);
4754     trw_layer_move_item(vtl_src, vtl_dest, name, type);
4755   }
4756 }
4757
4758 gboolean trw_layer_track_find_uuid ( const gpointer id, const VikTrack *trk, gpointer udata )
4759 {
4760   trku_udata *user_data = udata;
4761   if ( trk == user_data->trk ) {
4762     user_data->uuid = id;
4763     return TRUE;
4764   }
4765   return FALSE;
4766 }
4767
4768 gboolean vik_trw_layer_delete_track ( VikTrwLayer *vtl, VikTrack *trk )
4769 {
4770   gboolean was_visible = FALSE;
4771   if ( trk && trk->name ) {
4772
4773     if ( trk == vtl->current_track ) {
4774       vtl->current_track = NULL;
4775       vtl->current_tp_track = NULL;
4776       vtl->current_tp_id = NULL;
4777       vtl->moving_tp = FALSE;
4778       vtl->route_finder_started = FALSE;
4779     }
4780
4781     was_visible = trk->visible;
4782
4783     if ( trk == vtl->route_finder_added_track )
4784       vtl->route_finder_added_track = NULL;
4785
4786     trku_udata udata;
4787     udata.trk  = trk;
4788     udata.uuid = NULL;
4789
4790     // Hmmm, want key of it
4791     gpointer trkf = g_hash_table_find ( vtl->tracks, (GHRFunc) trw_layer_track_find_uuid, &udata );
4792
4793     if ( trkf && udata.uuid ) {
4794       /* could be current_tp, so we have to check */
4795       trw_layer_cancel_tps_of_track ( vtl, trk );
4796
4797       GtkTreeIter *it = g_hash_table_lookup ( vtl->tracks_iters, udata.uuid );
4798
4799       if ( it ) {
4800         vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, it );
4801         g_hash_table_remove ( vtl->tracks_iters, udata.uuid );
4802         g_hash_table_remove ( vtl->tracks, udata.uuid );
4803
4804         // If last sublayer, then remove sublayer container
4805         if ( g_hash_table_size (vtl->tracks) == 0 ) {
4806           vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, &(vtl->tracks_iter) );
4807         }
4808       }
4809       // Incase it was selected (no item delete signal ATM)
4810       vik_window_clear_highlight ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)) );
4811     }
4812   }
4813   return was_visible;
4814 }
4815
4816 gboolean vik_trw_layer_delete_route ( VikTrwLayer *vtl, VikTrack *trk )
4817 {
4818   gboolean was_visible = FALSE;
4819
4820   if ( trk && trk->name ) {
4821
4822     if ( trk == vtl->current_track ) {
4823       vtl->current_track = NULL;
4824       vtl->current_tp_track = NULL;
4825       vtl->current_tp_id = NULL;
4826       vtl->moving_tp = FALSE;
4827     }
4828
4829     was_visible = trk->visible;
4830
4831     if ( trk == vtl->route_finder_added_track )
4832       vtl->route_finder_added_track = NULL;
4833
4834     trku_udata udata;
4835     udata.trk  = trk;
4836     udata.uuid = NULL;
4837
4838     // Hmmm, want key of it
4839     gpointer trkf = g_hash_table_find ( vtl->routes, (GHRFunc) trw_layer_track_find_uuid, &udata );
4840
4841     if ( trkf && udata.uuid ) {
4842       /* could be current_tp, so we have to check */
4843       trw_layer_cancel_tps_of_track ( vtl, trk );
4844
4845       GtkTreeIter *it = g_hash_table_lookup ( vtl->routes_iters, udata.uuid );
4846
4847       if ( it ) {
4848         vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, it );
4849         g_hash_table_remove ( vtl->routes_iters, udata.uuid );
4850         g_hash_table_remove ( vtl->routes, udata.uuid );
4851
4852         // If last sublayer, then remove sublayer container
4853         if ( g_hash_table_size (vtl->routes) == 0 ) {
4854           vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, &(vtl->routes_iter) );
4855         }
4856       }
4857       // Incase it was selected (no item delete signal ATM)
4858       vik_window_clear_highlight ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)) );
4859     }
4860   }
4861   return was_visible;
4862 }
4863
4864 static gboolean trw_layer_delete_waypoint ( VikTrwLayer *vtl, VikWaypoint *wp )
4865 {
4866   gboolean was_visible = FALSE;
4867
4868   if ( wp && wp->name ) {
4869
4870     if ( wp == vtl->current_wp ) {
4871       vtl->current_wp = NULL;
4872       vtl->current_wp_id = NULL;
4873       vtl->moving_wp = FALSE;
4874     }
4875
4876     was_visible = wp->visible;
4877     
4878     wpu_udata udata;
4879     udata.wp   = wp;
4880     udata.uuid = NULL;
4881
4882     // Hmmm, want key of it
4883     gpointer wpf = g_hash_table_find ( vtl->waypoints, (GHRFunc) trw_layer_waypoint_find_uuid, (gpointer) &udata );
4884
4885     if ( wpf && udata.uuid ) {
4886       GtkTreeIter *it = g_hash_table_lookup ( vtl->waypoints_iters, udata.uuid );
4887     
4888       if ( it ) {
4889         vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, it );
4890         g_hash_table_remove ( vtl->waypoints_iters, udata.uuid );
4891
4892         highest_wp_number_remove_wp(vtl, wp->name);
4893         g_hash_table_remove ( vtl->waypoints, udata.uuid ); // last because this frees the name
4894
4895         // If last sublayer, then remove sublayer container
4896         if ( g_hash_table_size (vtl->waypoints) == 0 ) {
4897           vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, &(vtl->waypoints_iter) );
4898         }
4899       }
4900       // Incase it was selected (no item delete signal ATM)
4901       vik_window_clear_highlight ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)) );
4902     }
4903
4904   }
4905
4906   return was_visible;
4907 }
4908
4909 // Only for temporary use by trw_layer_delete_waypoint_by_name
4910 static gboolean trw_layer_waypoint_find_uuid_by_name ( const gpointer id, const VikWaypoint *wp, gpointer udata )
4911 {
4912   wpu_udata *user_data = udata;
4913   if ( ! strcmp ( wp->name, user_data->wp->name ) ) {
4914     user_data->uuid = id;
4915     return TRUE;
4916   }
4917   return FALSE;
4918 }
4919
4920 /*
4921  * Delete a waypoint by the given name
4922  * NOTE: ATM this will delete the first encountered Waypoint with the specified name
4923  *   as there be multiple waypoints with the same name
4924  */
4925 static gboolean trw_layer_delete_waypoint_by_name ( VikTrwLayer *vtl, const gchar *name )
4926 {
4927   wpu_udata udata;
4928   // Fake a waypoint with the given name
4929   udata.wp   = vik_waypoint_new ();
4930   vik_waypoint_set_name (udata.wp, name);
4931   // Currently only the name is used in this waypoint find function
4932   udata.uuid = NULL;
4933
4934   // Hmmm, want key of it
4935   gpointer wpf = g_hash_table_find ( vtl->waypoints, (GHRFunc) trw_layer_waypoint_find_uuid_by_name, (gpointer) &udata );
4936
4937   vik_waypoint_free (udata.wp);
4938
4939   if ( wpf && udata.uuid )
4940     return trw_layer_delete_waypoint (vtl, g_hash_table_lookup ( vtl->waypoints, udata.uuid ));
4941   else
4942     return FALSE;
4943 }
4944
4945 typedef struct {
4946   VikTrack *trk; // input
4947   gpointer uuid; // output
4948 } tpu_udata;
4949
4950 // Only for temporary use by trw_layer_delete_track_by_name
4951 static gboolean trw_layer_track_find_uuid_by_name ( const gpointer id, const VikTrack *trk, gpointer udata )
4952 {
4953   tpu_udata *user_data = udata;
4954   if ( ! strcmp ( trk->name, user_data->trk->name ) ) {
4955     user_data->uuid = id;
4956     return TRUE;
4957   }
4958   return FALSE;
4959 }
4960
4961 /*
4962  * Delete a track by the given name
4963  * NOTE: ATM this will delete the first encountered Track with the specified name
4964  *   as there may be multiple tracks with the same name within the specified hash table
4965  */
4966 static gboolean trw_layer_delete_track_by_name ( VikTrwLayer *vtl, const gchar *name, GHashTable *ht_tracks )
4967 {
4968   tpu_udata udata;
4969   // Fake a track with the given name
4970   udata.trk   = vik_track_new ();
4971   vik_track_set_name (udata.trk, name);
4972   // Currently only the name is used in this waypoint find function
4973   udata.uuid = NULL;
4974
4975   // Hmmm, want key of it
4976   gpointer trkf = g_hash_table_find ( ht_tracks, (GHRFunc) trw_layer_track_find_uuid_by_name, &udata );
4977
4978   vik_track_free (udata.trk);
4979
4980   if ( trkf && udata.uuid ) {
4981     // This could be a little better written...
4982     if ( vtl->tracks == ht_tracks )
4983       return vik_trw_layer_delete_track (vtl, g_hash_table_lookup ( ht_tracks, udata.uuid ));
4984     if ( vtl->routes == ht_tracks )
4985       return vik_trw_layer_delete_route (vtl, g_hash_table_lookup ( ht_tracks, udata.uuid ));
4986     return FALSE;
4987   }
4988   else
4989     return FALSE;
4990 }
4991
4992 static void remove_item_from_treeview ( const gpointer id, GtkTreeIter *it, VikTreeview * vt )
4993 {
4994     vik_treeview_item_delete (vt, it );
4995 }
4996
4997 void vik_trw_layer_delete_all_routes ( VikTrwLayer *vtl )
4998 {
4999
5000   vtl->current_track = NULL;
5001   vtl->route_finder_added_track = NULL;
5002   if (vtl->current_tp_track)
5003     trw_layer_cancel_current_tp(vtl, FALSE);
5004
5005   g_hash_table_foreach(vtl->routes_iters, (GHFunc) remove_item_from_treeview, VIK_LAYER(vtl)->vt);
5006   g_hash_table_remove_all(vtl->routes_iters);
5007
5008   if ( g_hash_table_size (vtl->routes) > 0 )
5009     vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, &(vtl->routes_iter) );
5010   g_hash_table_remove_all(vtl->routes);
5011
5012   vik_layer_emit_update ( VIK_LAYER(vtl) );
5013 }
5014
5015 void vik_trw_layer_delete_all_tracks ( VikTrwLayer *vtl )
5016 {
5017
5018   vtl->current_track = NULL;
5019   vtl->route_finder_added_track = NULL;
5020   if (vtl->current_tp_track)
5021     trw_layer_cancel_current_tp(vtl, FALSE);
5022
5023   g_hash_table_foreach(vtl->tracks_iters, (GHFunc) remove_item_from_treeview, VIK_LAYER(vtl)->vt);
5024   g_hash_table_remove_all(vtl->tracks_iters);
5025
5026   if ( g_hash_table_size (vtl->tracks) > 0 )
5027     vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, &(vtl->tracks_iter) );
5028   g_hash_table_remove_all(vtl->tracks);
5029
5030   vik_layer_emit_update ( VIK_LAYER(vtl) );
5031 }
5032
5033 void vik_trw_layer_delete_all_waypoints ( VikTrwLayer *vtl )
5034 {
5035   vtl->current_wp = NULL;
5036   vtl->current_wp_id = NULL;
5037   vtl->moving_wp = FALSE;
5038
5039   highest_wp_number_reset(vtl);
5040
5041   g_hash_table_foreach(vtl->waypoints_iters, (GHFunc) remove_item_from_treeview, VIK_LAYER(vtl)->vt);
5042   g_hash_table_remove_all(vtl->waypoints_iters);
5043
5044   if ( g_hash_table_size (vtl->waypoints) > 0 )
5045     vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, &(vtl->waypoints_iter) );
5046   g_hash_table_remove_all(vtl->waypoints);
5047
5048   vik_layer_emit_update ( VIK_LAYER(vtl) );
5049 }
5050
5051 static void trw_layer_delete_all_tracks ( menu_array_layer values )
5052 {
5053   VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
5054   // Get confirmation from the user
5055   if ( a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
5056                             _("Are you sure you want to delete all tracks in %s?"),
5057                             vik_layer_get_name ( VIK_LAYER(vtl) ) ) )
5058     vik_trw_layer_delete_all_tracks (vtl);
5059 }
5060
5061 static void trw_layer_delete_all_routes ( menu_array_layer values )
5062 {
5063   VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
5064   // Get confirmation from the user
5065   if ( a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
5066                             _("Are you sure you want to delete all routes in %s?"),
5067                             vik_layer_get_name ( VIK_LAYER(vtl) ) ) )
5068     vik_trw_layer_delete_all_routes (vtl);
5069 }
5070
5071 static void trw_layer_delete_all_waypoints ( menu_array_layer values )
5072 {
5073   VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
5074   // Get confirmation from the user
5075   if ( a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
5076                             _("Are you sure you want to delete all waypoints in %s?"),
5077                             vik_layer_get_name ( VIK_LAYER(vtl) ) ) )
5078     vik_trw_layer_delete_all_waypoints (vtl);
5079 }
5080
5081 static void trw_layer_delete_item ( menu_array_sublayer values )
5082 {
5083   VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
5084   gboolean was_visible = FALSE;
5085   if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_WAYPOINT )
5086   {
5087     VikWaypoint *wp = g_hash_table_lookup ( vtl->waypoints, values[MA_SUBLAYER_ID] );
5088     if ( wp && wp->name ) {
5089       if ( GPOINTER_TO_INT (values[MA_CONFIRM]) )
5090         // Get confirmation from the user
5091         // Maybe this Waypoint Delete should be optional as is it could get annoying...
5092         if ( ! a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
5093             _("Are you sure you want to delete the waypoint \"%s\"?"),
5094             wp->name ) )
5095           return;
5096       was_visible = trw_layer_delete_waypoint ( vtl, wp );
5097       trw_layer_calculate_bounds_waypoints ( vtl );
5098       // Reset layer timestamp in case it has now changed
5099       vik_treeview_item_set_timestamp ( vtl->vl.vt, &vtl->vl.iter, trw_layer_get_timestamp(vtl) );
5100     }
5101   }
5102   else if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_TRACK )
5103   {
5104     VikTrack *trk = g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
5105     if ( trk && trk->name ) {
5106       if ( GPOINTER_TO_INT (values[MA_CONFIRM]) )
5107         // Get confirmation from the user
5108         if ( ! a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
5109                                   _("Are you sure you want to delete the track \"%s\"?"),
5110                                   trk->name ) )
5111           return;
5112       was_visible = vik_trw_layer_delete_track ( vtl, trk );
5113       // Reset layer timestamp in case it has now changed
5114       vik_treeview_item_set_timestamp ( vtl->vl.vt, &vtl->vl.iter, trw_layer_get_timestamp(vtl) );
5115     }
5116   }
5117   else
5118   {
5119     VikTrack *trk = g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
5120     if ( trk && trk->name ) {
5121       if ( GPOINTER_TO_INT (values[MA_CONFIRM]) )
5122         // Get confirmation from the user
5123         if ( ! a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
5124                                     _("Are you sure you want to delete the route \"%s\"?"),
5125                                     trk->name ) )
5126           return;
5127       was_visible = vik_trw_layer_delete_route ( vtl, trk );
5128     }
5129   }
5130   if ( was_visible )
5131     vik_layer_emit_update ( VIK_LAYER(vtl) );
5132 }
5133
5134 /**
5135  *  Rename waypoint and maintain corresponding name of waypoint in the treeview
5136  */
5137 void trw_layer_waypoint_rename ( VikTrwLayer *vtl, VikWaypoint *wp, const gchar *new_name )
5138 {
5139   vik_waypoint_set_name ( wp, new_name );
5140
5141   // Now update the treeview as well
5142   wpu_udata udataU;
5143   udataU.wp   = wp;
5144   udataU.uuid = NULL;
5145
5146   // Need key of it for treeview update
5147   gpointer wpf = g_hash_table_find ( vtl->waypoints, (GHRFunc) trw_layer_waypoint_find_uuid, &udataU );
5148
5149   if ( wpf && udataU.uuid ) {
5150     GtkTreeIter *it = g_hash_table_lookup ( vtl->waypoints_iters, udataU.uuid );
5151
5152     if ( it ) {
5153       vik_treeview_item_set_name ( VIK_LAYER(vtl)->vt, it, new_name );
5154       vik_treeview_sort_children ( VIK_LAYER(vtl)->vt, &(vtl->waypoints_iter), vtl->wp_sort_order );
5155     }
5156   }
5157 }
5158
5159 /**
5160  *  Maintain icon of waypoint in the treeview
5161  */
5162 void trw_layer_waypoint_reset_icon ( VikTrwLayer *vtl, VikWaypoint *wp )
5163 {
5164   // update the treeview
5165   wpu_udata udataU;
5166   udataU.wp   = wp;
5167   udataU.uuid = NULL;
5168
5169   // Need key of it for treeview update
5170   gpointer wpf = g_hash_table_find ( vtl->waypoints, (GHRFunc) trw_layer_waypoint_find_uuid, &udataU );
5171
5172   if ( wpf && udataU.uuid ) {
5173     GtkTreeIter *it = g_hash_table_lookup ( vtl->waypoints_iters, udataU.uuid );
5174
5175     if ( it ) {
5176       vik_treeview_item_set_icon ( VIK_LAYER(vtl)->vt, it, get_wp_sym_small (wp->symbol) );
5177     }
5178   }
5179 }
5180
5181 static void trw_layer_properties_item ( menu_array_sublayer values )
5182 {
5183   VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
5184   if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_WAYPOINT )
5185   {
5186     VikWaypoint *wp = g_hash_table_lookup ( vtl->waypoints, values[MA_SUBLAYER_ID] );
5187
5188     if ( wp && wp->name )
5189     {
5190       gboolean updated = FALSE;
5191       gchar *new_name = a_dialog_waypoint ( VIK_GTK_WINDOW_FROM_LAYER(vtl), wp->name, vtl, wp, vtl->coord_mode, FALSE, &updated );
5192       if ( new_name )
5193         trw_layer_waypoint_rename ( vtl, wp, new_name );
5194
5195       if ( updated && values[MA_TV_ITER] )
5196         vik_treeview_item_set_icon ( VIK_LAYER(vtl)->vt, values[MA_TV_ITER], get_wp_sym_small (wp->symbol) );
5197
5198       if ( updated && VIK_LAYER(vtl)->visible )
5199         vik_layer_emit_update ( VIK_LAYER(vtl) );
5200     }
5201   }
5202   else
5203   {
5204     VikTrack *tr;
5205     if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_TRACK )
5206       tr = g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
5207     else
5208       tr = g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
5209
5210     if ( tr && tr->name )
5211     {
5212       vik_trw_layer_propwin_run ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
5213                                   vtl,
5214                                   tr,
5215                                   values[MA_VLP],
5216                                   values[MA_VVP],
5217                                   FALSE );
5218     }
5219   }
5220 }
5221
5222 /**
5223  * trw_layer_track_statistics:
5224  *
5225  * Show track statistics.
5226  * ATM jump to the stats page in the properties
5227  * TODO: consider separating the stats into an individual dialog?
5228  */
5229 static void trw_layer_track_statistics ( menu_array_sublayer values )
5230 {
5231   VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
5232   VikTrack *trk;
5233   if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_TRACK )
5234     trk = g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
5235   else
5236     trk = g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
5237
5238   if ( trk && trk->name ) {
5239     vik_trw_layer_propwin_run ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
5240                                 vtl,
5241                                 trk,
5242                                 values[MA_VLP],
5243                                 values[MA_VVP],
5244                                 TRUE );
5245   }
5246 }
5247
5248 /*
5249  * Update the treeview of the track id - primarily to update the icon
5250  */
5251 void trw_layer_update_treeview ( VikTrwLayer *vtl, VikTrack *trk )
5252 {
5253   trku_udata udata;
5254   udata.trk  = trk;
5255   udata.uuid = NULL;
5256
5257   gpointer trkf = NULL;
5258   if ( trk->is_route )
5259     trkf = g_hash_table_find ( vtl->routes, (GHRFunc) trw_layer_track_find_uuid, &udata );
5260   else
5261     trkf = g_hash_table_find ( vtl->tracks, (GHRFunc) trw_layer_track_find_uuid, &udata );
5262
5263   if ( trkf && udata.uuid ) {
5264
5265     GtkTreeIter *iter = NULL;
5266     if ( trk->is_route )
5267       iter = g_hash_table_lookup ( vtl->routes_iters, udata.uuid );
5268     else
5269       iter = g_hash_table_lookup ( vtl->tracks_iters, udata.uuid );
5270
5271     if ( iter ) {
5272       // TODO: Make this a function
5273       GdkPixbuf *pixbuf = gdk_pixbuf_new ( GDK_COLORSPACE_RGB, FALSE, 8, 18, 18);
5274       guint32 pixel = ((trk->color.red & 0xff00) << 16) |
5275         ((trk->color.green & 0xff00) << 8) |
5276         (trk->color.blue & 0xff00);
5277       gdk_pixbuf_fill ( pixbuf, pixel );
5278       vik_treeview_item_set_icon ( VIK_LAYER(vtl)->vt, iter, pixbuf );
5279       g_object_unref (pixbuf);
5280     }
5281
5282   }
5283 }
5284
5285 /*
5286    Parameter 1 -> VikLayersPanel
5287    Parameter 2 -> VikLayer
5288    Parameter 3 -> VikViewport
5289 */
5290 static void goto_coord ( gpointer *vlp, gpointer vl, gpointer vvp, const VikCoord *coord )
5291 {
5292   if ( vlp ) {
5293     vik_viewport_set_center_coord ( vik_layers_panel_get_viewport (VIK_LAYERS_PANEL(vlp)), coord, TRUE );
5294     vik_layers_panel_emit_update ( VIK_LAYERS_PANEL(vlp) );
5295   }
5296   else {
5297     /* since vlp not set, vl & vvp should be valid instead! */
5298     if ( vl && vvp ) {
5299       vik_viewport_set_center_coord ( VIK_VIEWPORT(vvp), coord, TRUE );
5300       vik_layer_emit_update ( VIK_LAYER(vl) );
5301     }
5302   }
5303 }
5304
5305 static void trw_layer_goto_track_startpoint ( menu_array_sublayer values )
5306 {
5307   VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
5308   VikTrack *track;
5309   if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5310     track = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
5311   else
5312     track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
5313
5314   if ( track && track->trackpoints )
5315     goto_coord ( values[MA_VLP], vtl, values[MA_VVP], &(vik_track_get_tp_first(track)->coord) );
5316 }
5317
5318 static void trw_layer_goto_track_center ( menu_array_sublayer values )
5319 {
5320   VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
5321   VikTrack *track;
5322   if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5323     track = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
5324   else
5325     track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
5326
5327   if ( track && track->trackpoints )
5328   {
5329     struct LatLon average, maxmin[2] = { {0,0}, {0,0} };
5330     VikCoord coord;
5331     trw_layer_find_maxmin_tracks ( NULL, track, maxmin );
5332     average.lat = (maxmin[0].lat+maxmin[1].lat)/2;
5333     average.lon = (maxmin[0].lon+maxmin[1].lon)/2;
5334     vik_coord_load_from_latlon ( &coord, vtl->coord_mode, &average );
5335     goto_coord ( values[MA_VLP], vtl, values[MA_VVP], &coord);
5336   }
5337 }
5338
5339 static void trw_layer_convert_track_route ( menu_array_sublayer values )
5340 {
5341   VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
5342   VikTrack *trk;
5343   if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5344     trk = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
5345   else
5346     trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
5347
5348   if ( !trk )
5349     return;
5350
5351   // Converting a track to a route can be a bit more complicated,
5352   //  so give a chance to change our minds:
5353   if ( !trk->is_route &&
5354        ( ( vik_track_get_segment_count ( trk ) > 1 ) ||
5355          ( vik_track_get_average_speed ( trk ) > 0.0 ) ) ) {
5356
5357     if ( ! a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
5358                                 _("Converting a track to a route removes extra track data such as segments, timestamps, etc...\nDo you want to continue?"), NULL ) )
5359       return;
5360 }
5361
5362   // Copy it
5363   VikTrack *trk_copy = vik_track_copy ( trk, TRUE );
5364
5365   // Convert
5366   trk_copy->is_route = !trk_copy->is_route;
5367
5368   // ATM can't set name to self - so must create temporary copy
5369   gchar *name = g_strdup ( trk_copy->name );
5370
5371   // Delete old one and then add new one
5372   if ( trk->is_route ) {
5373     vik_trw_layer_delete_route ( vtl, trk );
5374     vik_trw_layer_add_track ( vtl, name, trk_copy );
5375   }
5376   else {
5377     // Extra route conversion bits...
5378     vik_track_merge_segments ( trk_copy );
5379     vik_track_to_routepoints ( trk_copy );
5380
5381     vik_trw_layer_delete_track ( vtl, trk );
5382     vik_trw_layer_add_route ( vtl, name, trk_copy );
5383   }
5384   g_free ( name );
5385
5386   // Update in case color of track / route changes when moving between sublayers
5387   vik_layer_emit_update ( VIK_LAYER(vtl) );
5388 }
5389
5390 static void trw_layer_anonymize_times ( menu_array_sublayer values )
5391 {
5392   VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
5393   VikTrack *track;
5394   if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5395     track = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
5396   else
5397     track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
5398
5399   if ( track )
5400     vik_track_anonymize_times ( track );
5401 }
5402
5403 static void trw_layer_interpolate_times ( menu_array_sublayer values )
5404 {
5405   VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
5406   VikTrack *track;
5407   if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5408     track = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
5409   else
5410     track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
5411
5412   if ( track )
5413     vik_track_interpolate_times ( track );
5414 }
5415
5416 static void trw_layer_extend_track_end ( menu_array_sublayer values )
5417 {
5418   VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
5419   VikTrack *track;
5420   if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5421     track = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
5422   else
5423     track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
5424
5425   if ( !track )
5426     return;
5427
5428   vtl->current_track = track;
5429   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);
5430
5431   if ( track->trackpoints )
5432     goto_coord ( values[MA_VLP], vtl, values[MA_VVP], &(vik_track_get_tp_last(track)->coord) );
5433 }
5434
5435 /**
5436  * extend a track using route finder
5437  */
5438 static void trw_layer_extend_track_end_route_finder ( menu_array_sublayer values )
5439 {
5440   VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
5441   VikTrack *track = g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
5442   if ( !track )
5443     return;
5444
5445   vik_window_enable_layer_tool ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)), VIK_LAYER_TRW, TOOL_ROUTE_FINDER );
5446   vtl->current_track = track;
5447   vtl->route_finder_started = TRUE;
5448
5449   if ( track->trackpoints )
5450       goto_coord ( values[MA_VLP], vtl, values[MA_VVP], &vik_track_get_tp_last(track)->coord );
5451 }
5452
5453 /**
5454  *
5455  */
5456 static gboolean trw_layer_dem_test ( VikTrwLayer *vtl, VikLayersPanel *vlp )
5457 {
5458   // If have a vlp then perform a basic test to see if any DEM info available...
5459   if ( vlp ) {
5460     GList *dems = vik_layers_panel_get_all_layers_of_type (vlp, VIK_LAYER_DEM, TRUE); // Includes hidden DEM layer types
5461
5462     if ( !g_list_length(dems) ) {
5463       a_dialog_error_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), _("No DEM layers available, thus no DEM values can be applied.") );
5464       return FALSE;
5465     }
5466   }
5467   return TRUE;
5468 }
5469
5470 /**
5471  * apply_dem_data_common:
5472  *
5473  * A common function for applying the DEM values and reporting the results.
5474  */
5475 static void apply_dem_data_common ( VikTrwLayer *vtl, VikLayersPanel *vlp, VikTrack *track, gboolean skip_existing_elevations )
5476 {
5477   if ( !trw_layer_dem_test ( vtl, vlp ) )
5478     return;
5479
5480   gulong changed = vik_track_apply_dem_data ( track, skip_existing_elevations );
5481   // Inform user how much was changed
5482   gchar str[64];
5483   const gchar *tmp_str = ngettext("%ld point adjusted", "%ld points adjusted", changed);
5484   g_snprintf(str, 64, tmp_str, changed);
5485   a_dialog_info_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), str);
5486 }
5487
5488 static void trw_layer_apply_dem_data_all ( menu_array_sublayer values )
5489 {
5490   VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
5491   VikTrack *track;
5492   if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5493     track = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
5494   else
5495     track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
5496
5497   if ( track )
5498     apply_dem_data_common ( vtl, values[MA_VLP], track, FALSE );
5499 }
5500
5501 static void trw_layer_apply_dem_data_only_missing ( menu_array_sublayer values )
5502 {
5503   VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
5504   VikTrack *track;
5505   if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5506     track = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
5507   else
5508     track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
5509
5510   if ( track )
5511     apply_dem_data_common ( vtl, values[MA_VLP], track, TRUE );
5512 }
5513
5514 /**
5515  * smooth_it:
5516  *
5517  * A common function for applying the elevation smoothing and reporting the results.
5518  */
5519 static void smooth_it ( VikTrwLayer *vtl, VikTrack *track, gboolean flat )
5520 {
5521   gulong changed = vik_track_smooth_missing_elevation_data ( track, flat );
5522   // Inform user how much was changed
5523   gchar str[64];
5524   const gchar *tmp_str = ngettext("%ld point adjusted", "%ld points adjusted", changed);
5525   g_snprintf(str, 64, tmp_str, changed);
5526   a_dialog_info_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), str);
5527 }
5528
5529 /**
5530  *
5531  */
5532 static void trw_layer_missing_elevation_data_interp ( menu_array_sublayer values )
5533 {
5534   VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
5535   VikTrack *track;
5536   if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5537     track = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
5538   else
5539     track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
5540
5541   if ( !track )
5542     return;
5543
5544   smooth_it ( vtl, track, FALSE );
5545 }
5546
5547 static void trw_layer_missing_elevation_data_flat ( menu_array_sublayer values )
5548 {
5549   VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
5550   VikTrack *track;
5551   if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5552     track = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
5553   else
5554     track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
5555
5556   if ( !track )
5557     return;
5558
5559   smooth_it ( vtl, track, TRUE );
5560 }
5561
5562 /**
5563  * Commonal helper function
5564  */
5565 static void wp_changed_message ( VikTrwLayer *vtl, gint changed )
5566 {
5567   gchar str[64];
5568   const gchar *tmp_str = ngettext("%ld waypoint changed", "%ld waypoints changed", changed);
5569   g_snprintf(str, 64, tmp_str, changed);
5570   a_dialog_info_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), str);
5571 }
5572
5573 static void trw_layer_apply_dem_data_wpt_all ( menu_array_sublayer values )
5574 {
5575   VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
5576   VikLayersPanel *vlp = (VikLayersPanel *)values[MA_VLP];
5577
5578   if ( !trw_layer_dem_test ( vtl, vlp ) )
5579     return;
5580
5581   gint changed = 0;
5582   if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_WAYPOINT ) {
5583     // Single Waypoint
5584     VikWaypoint *wp = (VikWaypoint *) g_hash_table_lookup ( vtl->waypoints, values[MA_SUBLAYER_ID] );
5585     if ( wp )
5586       changed = (gint)vik_waypoint_apply_dem_data ( wp, FALSE );
5587   }
5588   else {
5589     // All waypoints
5590     GHashTableIter iter;
5591     gpointer key, value;
5592
5593     g_hash_table_iter_init ( &iter, vtl->waypoints );
5594     while ( g_hash_table_iter_next (&iter, &key, &value) ) {
5595       VikWaypoint *wp = VIK_WAYPOINT(value);
5596       changed = changed + (gint)vik_waypoint_apply_dem_data ( wp, FALSE );
5597     }
5598   }
5599   wp_changed_message ( vtl, changed );
5600 }
5601
5602 static void trw_layer_apply_dem_data_wpt_only_missing ( menu_array_sublayer values )
5603 {
5604   VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
5605   VikLayersPanel *vlp = (VikLayersPanel *)values[MA_VLP];
5606
5607   if ( !trw_layer_dem_test ( vtl, vlp ) )
5608     return;
5609
5610   gint changed = 0;
5611   if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_WAYPOINT ) {
5612     // Single Waypoint
5613     VikWaypoint *wp = (VikWaypoint *) g_hash_table_lookup ( vtl->waypoints, values[MA_SUBLAYER_ID] );
5614     if ( wp )
5615       changed = (gint)vik_waypoint_apply_dem_data ( wp, TRUE );
5616   }
5617   else {
5618     // All waypoints
5619     GHashTableIter iter;
5620     gpointer key, value;
5621
5622     g_hash_table_iter_init ( &iter, vtl->waypoints );
5623     while ( g_hash_table_iter_next (&iter, &key, &value) ) {
5624       VikWaypoint *wp = VIK_WAYPOINT(value);
5625       changed = changed + (gint)vik_waypoint_apply_dem_data ( wp, TRUE );
5626     }
5627   }
5628   wp_changed_message ( vtl, changed );
5629 }
5630
5631 static void trw_layer_goto_track_endpoint ( menu_array_sublayer values )
5632 {
5633   VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
5634   VikTrack *track;
5635   if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5636     track = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
5637   else
5638     track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
5639
5640   if ( !track )
5641     return;
5642   if ( !track->trackpoints )
5643     return;
5644   goto_coord ( values[MA_VLP], vtl, values[MA_VVP], &(vik_track_get_tp_last(track)->coord));
5645 }
5646
5647 static void trw_layer_goto_track_max_speed ( menu_array_sublayer values )
5648 {
5649   VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
5650   VikTrack *track;
5651   if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5652     track = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
5653   else
5654     track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
5655
5656   if ( !track )
5657     return;
5658
5659   VikTrackpoint* vtp = vik_track_get_tp_by_max_speed ( track );
5660   if ( !vtp )
5661     return;
5662   goto_coord ( values[MA_VLP], vtl, values[MA_VVP], &(vtp->coord));
5663 }
5664
5665 static void trw_layer_goto_track_max_alt ( menu_array_sublayer values )
5666 {
5667   VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
5668   VikTrack *track;
5669   if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5670     track = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
5671   else
5672     track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
5673
5674   if ( !track )
5675     return;
5676
5677   VikTrackpoint* vtp = vik_track_get_tp_by_max_alt ( track );
5678   if ( !vtp )
5679     return;
5680   goto_coord ( values[MA_VLP], vtl, values[MA_VVP], &(vtp->coord));
5681 }
5682
5683 static void trw_layer_goto_track_min_alt ( menu_array_sublayer values )
5684 {
5685   VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
5686   VikTrack *track;
5687   if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5688     track = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
5689   else
5690     track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
5691
5692   if ( !track )
5693     return;
5694
5695   VikTrackpoint* vtp = vik_track_get_tp_by_min_alt ( track );
5696   if ( !vtp )
5697     return;
5698   goto_coord ( values[MA_VLP], vtl, values[MA_VVP], &(vtp->coord));
5699 }
5700
5701 /*
5702  * Automatically change the viewport to center on the track and zoom to see the extent of the track
5703  */
5704 static void trw_layer_auto_track_view ( menu_array_sublayer values )
5705 {
5706   VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
5707   VikTrack *trk;
5708   if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5709     trk = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
5710   else
5711     trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
5712
5713   if ( trk && trk->trackpoints )
5714   {
5715     struct LatLon maxmin[2] = { {0,0}, {0,0} };
5716     trw_layer_find_maxmin_tracks ( NULL, trk, maxmin );
5717     trw_layer_zoom_to_show_latlons ( vtl, values[MA_VVP], maxmin );
5718     if ( values[MA_VLP] )
5719       vik_layers_panel_emit_update ( VIK_LAYERS_PANEL(values[MA_VLP]) );
5720     else
5721       vik_layer_emit_update ( VIK_LAYER(vtl) );
5722   }
5723 }
5724
5725 /*
5726  * Refine the selected track/route with a routing engine.
5727  * The routing engine is selected by the user, when requestiong the job.
5728  */
5729 static void trw_layer_route_refine ( menu_array_sublayer values )
5730 {
5731   static gint last_engine = 0;
5732   VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
5733   VikTrack *trk;
5734
5735   if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5736     trk = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
5737   else
5738     trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
5739
5740   if ( trk && trk->trackpoints )
5741   {
5742     /* Check size of the route */
5743     int nb = vik_track_get_tp_count(trk);
5744     if (nb > 100) {
5745       GtkWidget *dialog = gtk_message_dialog_new (VIK_GTK_WINDOW_FROM_LAYER (vtl),
5746                                                   GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
5747                                                   GTK_MESSAGE_WARNING,
5748                                                   GTK_BUTTONS_OK_CANCEL,
5749                                                   _("Refining a track with many points (%d) is unlikely to yield sensible results. Do you want to Continue?"),
5750                                                   nb);
5751       gint response = gtk_dialog_run ( GTK_DIALOG(dialog) );
5752       gtk_widget_destroy ( dialog );
5753       if (response != GTK_RESPONSE_OK )
5754         return;
5755     }
5756     /* Select engine from dialog */
5757     GtkWidget *dialog = gtk_dialog_new_with_buttons (_("Refine Route with Routing Engine..."),
5758                                                   VIK_GTK_WINDOW_FROM_LAYER (vtl),
5759                                                   GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
5760                                                   GTK_STOCK_CANCEL,
5761                                                   GTK_RESPONSE_REJECT,
5762                                                   GTK_STOCK_OK,
5763                                                   GTK_RESPONSE_ACCEPT,
5764                                                   NULL);
5765     GtkWidget *label = gtk_label_new ( _("Select routing engine") );
5766     gtk_widget_show_all(label);
5767
5768     gtk_box_pack_start ( GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), label, TRUE, TRUE, 0 );
5769
5770     GtkWidget * combo = vik_routing_ui_selector_new ( (Predicate)vik_routing_engine_supports_refine, NULL );
5771     gtk_combo_box_set_active (GTK_COMBO_BOX (combo), last_engine);
5772     gtk_widget_show_all(combo);
5773
5774     gtk_box_pack_start ( GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), combo, TRUE, TRUE, 0 );
5775
5776     gtk_dialog_set_default_response ( GTK_DIALOG(dialog), GTK_RESPONSE_ACCEPT );
5777
5778     if ( gtk_dialog_run ( GTK_DIALOG(dialog) ) == GTK_RESPONSE_ACCEPT )
5779     {
5780         /* Dialog validated: retrieve selected engine and do the job */
5781         last_engine = gtk_combo_box_get_active ( GTK_COMBO_BOX(combo) );
5782         VikRoutingEngine *routing = vik_routing_ui_selector_get_nth (combo, last_engine);
5783
5784         /* Change cursor */
5785         vik_window_set_busy_cursor ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)) );
5786
5787         /* Force saving track */
5788         /* FIXME: remove or rename this hack */
5789         vtl->route_finder_check_added_track = TRUE;
5790
5791         /* the job */
5792         vik_routing_engine_refine (routing, vtl, trk);
5793
5794         /* FIXME: remove or rename this hack */
5795         if ( vtl->route_finder_added_track )
5796           vik_track_calculate_bounds ( vtl->route_finder_added_track );
5797
5798         vtl->route_finder_added_track = NULL;
5799         vtl->route_finder_check_added_track = FALSE;
5800
5801         vik_layer_emit_update ( VIK_LAYER(vtl) );
5802
5803         /* Restore cursor */
5804         vik_window_clear_busy_cursor ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)) );
5805     }
5806     gtk_widget_destroy ( dialog );
5807   }
5808 }
5809
5810 static void trw_layer_edit_trackpoint ( menu_array_sublayer values )
5811 {
5812   VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
5813   trw_layer_tpwin_init ( vtl );
5814 }
5815
5816 /*************************************
5817  * merge/split by time routines 
5818  *************************************/
5819
5820 /* called for each key in track hash table.
5821  * If the current track has the same time stamp type, add it to the result,
5822  * except the one pointed by "exclude".
5823  * set exclude to NULL if there is no exclude to check.
5824  * Note that the result is in reverse (for performance reasons).
5825  */
5826 typedef struct {
5827   GList **result;
5828   VikTrack *exclude;
5829   gboolean with_timestamps;
5830 } twt_udata;
5831 static void find_tracks_with_timestamp_type(gpointer key, gpointer value, gpointer udata)
5832 {
5833   twt_udata *user_data = udata;
5834   VikTrackpoint *p1, *p2;
5835   VikTrack *trk = VIK_TRACK(value);
5836   if (trk == user_data->exclude) {
5837     return;
5838   }
5839
5840   if (trk->trackpoints) {
5841     p1 = vik_track_get_tp_first(trk);
5842     p2 = vik_track_get_tp_last(trk);
5843
5844     if ( user_data->with_timestamps ) {
5845       if (!p1->has_timestamp || !p2->has_timestamp) {
5846         return;
5847       }
5848     }
5849     else {
5850       // Don't add tracks with timestamps when getting non timestamp tracks
5851       if (p1->has_timestamp || p2->has_timestamp) {
5852         return;
5853       }
5854     }
5855   }
5856
5857   *(user_data->result) = g_list_prepend(*(user_data->result), key);
5858 }
5859
5860 /**
5861  * find_nearby_tracks_by_time:
5862  *
5863  * Called for each track in track hash table.
5864  *  If the original track (in user_data[1]) is close enough (threshold period in user_data[2])
5865  *  to the current track, then the current track is added to the list in user_data[0]
5866  */
5867 static void find_nearby_tracks_by_time (gpointer key, gpointer value, gpointer user_data)
5868 {
5869   VikTrack *trk = VIK_TRACK(value);
5870
5871   GList **nearby_tracks = ((gpointer *)user_data)[0];
5872   VikTrack *orig_trk = VIK_TRACK(((gpointer *)user_data)[1]);
5873
5874   if ( !orig_trk || !orig_trk->trackpoints )
5875     return;
5876
5877   /* outline: 
5878    * detect reasons for not merging, and return
5879    * if no reason is found not to merge, then do it.
5880    */
5881
5882   twt_udata *udata = user_data;
5883   // Exclude the original track from the compiled list
5884   if (trk == udata->exclude) {
5885     return;
5886   }
5887
5888   time_t t1 = vik_track_get_tp_first(orig_trk)->timestamp;
5889   time_t t2 = vik_track_get_tp_last(orig_trk)->timestamp;
5890
5891   if (trk->trackpoints) {
5892
5893     VikTrackpoint *p1 = vik_track_get_tp_first(trk);
5894     VikTrackpoint *p2 = vik_track_get_tp_last(trk);
5895
5896     if (!p1->has_timestamp || !p2->has_timestamp) {
5897       //g_print("no timestamp\n");
5898       return;
5899     }
5900
5901     guint threshold = GPOINTER_TO_UINT (((gpointer *)user_data)[2]);
5902     //g_print("Got track named %s, times %d, %d\n", trk->name, p1->timestamp, p2->timestamp);
5903     if (! (labs(t1 - p2->timestamp) < threshold ||
5904       /*  p1 p2      t1 t2 */
5905       labs(p1->timestamp - t2) < threshold)
5906       /*  t1 t2      p1 p2 */
5907     ) {
5908       return;
5909     }
5910   }
5911
5912   *nearby_tracks = g_list_prepend(*nearby_tracks, value);
5913 }
5914
5915 /* comparison function used to sort tracks; a and b are hash table keys */
5916 /* Not actively used - can be restored if needed
5917 static gint track_compare(gconstpointer a, gconstpointer b, gpointer user_data)
5918 {
5919   GHashTable *tracks = user_data;
5920   time_t t1, t2;
5921
5922   t1 = VIK_TRACKPOINT(VIK_TRACK(g_hash_table_lookup(tracks, a))->trackpoints->data)->timestamp;
5923   t2 = VIK_TRACKPOINT(VIK_TRACK(g_hash_table_lookup(tracks, b))->trackpoints->data)->timestamp;
5924   
5925   if (t1 < t2) return -1;
5926   if (t1 > t2) return 1;
5927   return 0;
5928 }
5929 */
5930
5931 /* comparison function used to sort trackpoints */
5932 static gint trackpoint_compare(gconstpointer a, gconstpointer b)
5933 {
5934   time_t t1 = VIK_TRACKPOINT(a)->timestamp, t2 = VIK_TRACKPOINT(b)->timestamp;
5935   
5936   if (t1 < t2) return -1;
5937   if (t1 > t2) return 1;
5938   return 0;
5939 }
5940
5941 /**
5942  * comparison function which can be used to sort tracks or waypoints by name
5943  */
5944 static gint sort_alphabetically (gconstpointer a, gconstpointer b, gpointer user_data)
5945 {
5946   const gchar* namea = (const gchar*) a;
5947   const gchar* nameb = (const gchar*) b;
5948   if ( namea == NULL || nameb == NULL)
5949     return 0;
5950   else
5951     // Same sort method as used in the vik_treeview_*_alphabetize functions
5952     return strcmp ( namea, nameb );
5953 }
5954
5955 /**
5956  * Attempt to merge selected track with other tracks specified by the user
5957  * Tracks to merge with must be of the same 'type' as the selected track -
5958  *  either all with timestamps, or all without timestamps
5959  */
5960 static void trw_layer_merge_with_other ( menu_array_sublayer values )
5961 {
5962   VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
5963   GList *other_tracks = NULL;
5964   GHashTable *ght_tracks;
5965   if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5966     ght_tracks = vtl->routes;
5967   else
5968     ght_tracks = vtl->tracks;
5969
5970   VikTrack *track = (VikTrack *) g_hash_table_lookup ( ght_tracks, values[MA_SUBLAYER_ID] );
5971
5972   if ( !track )
5973     return;
5974
5975   if ( !track->trackpoints )
5976     return;
5977
5978   twt_udata udata;
5979   udata.result = &other_tracks;
5980   udata.exclude = track;
5981   // Allow merging with 'similar' time type time tracks
5982   // i.e. either those times, or those without
5983   udata.with_timestamps = vik_track_get_tp_first(track)->has_timestamp;
5984
5985   g_hash_table_foreach(ght_tracks, find_tracks_with_timestamp_type, (gpointer)&udata);
5986   other_tracks = g_list_reverse(other_tracks);
5987
5988   if ( !other_tracks ) {
5989     if ( udata.with_timestamps )
5990       a_dialog_error_msg(VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Failed. No other tracks with timestamps in this layer found"));
5991     else
5992       a_dialog_error_msg(VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Failed. No other tracks without timestamps in this layer found"));
5993     return;
5994   }
5995
5996   // Sort alphabetically for user presentation
5997   // Convert into list of names for usage with dialog function
5998   // TODO: Need to consider how to work best when we can have multiple tracks the same name...
5999   GList *other_tracks_names = NULL;
6000   GList *iter = g_list_first ( other_tracks );
6001   while ( iter ) {
6002     other_tracks_names = g_list_append ( other_tracks_names, VIK_TRACK(g_hash_table_lookup (ght_tracks, iter->data))->name );
6003     iter = g_list_next ( iter );
6004   }
6005
6006   other_tracks_names = g_list_sort_with_data (other_tracks_names, sort_alphabetically, NULL);
6007
6008   GList *merge_list = a_dialog_select_from_list(VIK_GTK_WINDOW_FROM_LAYER(vtl),
6009                                                 other_tracks_names,
6010                                                 TRUE,
6011                                                 _("Merge with..."),
6012                                                 track->is_route ? _("Select route to merge with") : _("Select track to merge with"));
6013   g_list_free(other_tracks);
6014   g_list_free(other_tracks_names);
6015
6016   if (merge_list)
6017   {
6018     GList *l;
6019     for (l = merge_list; l != NULL; l = g_list_next(l)) {
6020       VikTrack *merge_track;
6021       if ( track->is_route )
6022         merge_track = vik_trw_layer_get_route ( vtl, l->data );
6023       else
6024         merge_track = vik_trw_layer_get_track ( vtl, l->data );
6025
6026       if (merge_track) {
6027         vik_track_steal_and_append_trackpoints ( track, merge_track );
6028         if ( track->is_route )
6029           vik_trw_layer_delete_route (vtl, merge_track);
6030         else
6031           vik_trw_layer_delete_track (vtl, merge_track);
6032         track->trackpoints = g_list_sort(track->trackpoints, trackpoint_compare);
6033       }
6034     }
6035     for (l = merge_list; l != NULL; l = g_list_next(l))
6036       g_free(l->data);
6037     g_list_free(merge_list);
6038
6039     vik_layer_emit_update( VIK_LAYER(vtl) );
6040   }
6041 }
6042
6043 // c.f. trw_layer_sorted_track_id_by_name_list
6044 //  but don't add the specified track to the list (normally current track)
6045 static void trw_layer_sorted_track_id_by_name_list_exclude_self (const gpointer id, const VikTrack *trk, gpointer udata)
6046 {
6047   twt_udata *user_data = udata;
6048
6049   // Skip self
6050   if (trk == user_data->exclude) {
6051     return;
6052   }
6053
6054   // Sort named list alphabetically
6055   *(user_data->result) = g_list_insert_sorted_with_data (*(user_data->result), trk->name, sort_alphabetically, NULL);
6056 }
6057
6058 /**
6059  * Join - this allows combining 'tracks' and 'track routes'
6060  *  i.e. doesn't care about whether tracks have consistent timestamps
6061  * ATM can only append one track at a time to the currently selected track
6062  */
6063 static void trw_layer_append_track ( menu_array_sublayer values )
6064 {
6065
6066   VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
6067   VikTrack *trk;
6068   GHashTable *ght_tracks;
6069   if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
6070     ght_tracks = vtl->routes;
6071   else
6072     ght_tracks = vtl->tracks;
6073
6074   trk = (VikTrack *) g_hash_table_lookup ( ght_tracks, values[MA_SUBLAYER_ID] );
6075
6076   if ( !trk )
6077     return;
6078
6079   GList *other_tracks_names = NULL;
6080
6081   // Sort alphabetically for user presentation
6082   // Convert into list of names for usage with dialog function
6083   // TODO: Need to consider how to work best when we can have multiple tracks the same name...
6084   twt_udata udata;
6085   udata.result = &other_tracks_names;
6086   udata.exclude = trk;
6087
6088   g_hash_table_foreach(ght_tracks, (GHFunc) trw_layer_sorted_track_id_by_name_list_exclude_self, (gpointer)&udata);
6089
6090   // Note the limit to selecting one track only
6091   //  this is to control the ordering of appending tracks, i.e. the selected track always goes after the current track
6092   //  (otherwise with multiple select the ordering would not be controllable by the user - automatically being alphabetically)
6093   GList *append_list = a_dialog_select_from_list(VIK_GTK_WINDOW_FROM_LAYER(vtl),
6094                                                  other_tracks_names,
6095                                                  FALSE,
6096                                                  trk->is_route ? _("Append Route"): _("Append Track"),
6097                                                  trk->is_route ? _("Select the route to append after the current route") :
6098                                                                  _("Select the track to append after the current track") );
6099
6100   g_list_free(other_tracks_names);
6101
6102   // It's a list, but shouldn't contain more than one other track!
6103   if ( append_list ) {
6104     GList *l;
6105     for (l = append_list; l != NULL; l = g_list_next(l)) {
6106       // TODO: at present this uses the first track found by name,
6107       //  which with potential multiple same named tracks may not be the one selected...
6108       VikTrack *append_track;
6109       if ( trk->is_route )
6110         append_track = vik_trw_layer_get_route ( vtl, l->data );
6111       else
6112         append_track = vik_trw_layer_get_track ( vtl, l->data );
6113
6114       if ( append_track ) {
6115         vik_track_steal_and_append_trackpoints ( trk, append_track );
6116         if ( trk->is_route )
6117           vik_trw_layer_delete_route (vtl, append_track);
6118         else
6119           vik_trw_layer_delete_track (vtl, append_track);
6120       }
6121     }
6122     for (l = append_list; l != NULL; l = g_list_next(l))
6123       g_free(l->data);
6124     g_list_free(append_list);
6125
6126     vik_layer_emit_update( VIK_LAYER(vtl) );
6127   }
6128 }
6129
6130 /**
6131  * Very similar to trw_layer_append_track for joining
6132  * but this allows selection from the 'other' list
6133  * If a track is selected, then is shows routes and joins the selected one
6134  * If a route is selected, then is shows tracks and joins the selected one
6135  */
6136 static void trw_layer_append_other ( menu_array_sublayer values )
6137 {
6138
6139   VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
6140   VikTrack *trk;
6141   GHashTable *ght_mykind, *ght_others;
6142   if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE ) {
6143     ght_mykind = vtl->routes;
6144     ght_others = vtl->tracks;
6145   }
6146   else {
6147     ght_mykind = vtl->tracks;
6148     ght_others = vtl->routes;
6149   }
6150
6151   trk = (VikTrack *) g_hash_table_lookup ( ght_mykind, values[MA_SUBLAYER_ID] );
6152
6153   if ( !trk )
6154     return;
6155
6156   GList *other_tracks_names = NULL;
6157
6158   // Sort alphabetically for user presentation
6159   // Convert into list of names for usage with dialog function
6160   // TODO: Need to consider how to work best when we can have multiple tracks the same name...
6161   twt_udata udata;
6162   udata.result = &other_tracks_names;
6163   udata.exclude = trk;
6164
6165   g_hash_table_foreach(ght_others, (GHFunc) trw_layer_sorted_track_id_by_name_list_exclude_self, (gpointer)&udata);
6166
6167   // Note the limit to selecting one track only
6168   //  this is to control the ordering of appending tracks, i.e. the selected track always goes after the current track
6169   //  (otherwise with multiple select the ordering would not be controllable by the user - automatically being alphabetically)
6170   GList *append_list = a_dialog_select_from_list(VIK_GTK_WINDOW_FROM_LAYER(vtl),
6171                                                  other_tracks_names,
6172                                                  FALSE,
6173                                                  trk->is_route ? _("Append Track"): _("Append Route"),
6174                                                  trk->is_route ? _("Select the track to append after the current route") :
6175                                                                  _("Select the route to append after the current track") );
6176
6177   g_list_free(other_tracks_names);
6178
6179   // It's a list, but shouldn't contain more than one other track!
6180   if ( append_list ) {
6181     GList *l;
6182     for (l = append_list; l != NULL; l = g_list_next(l)) {
6183       // TODO: at present this uses the first track found by name,
6184       //  which with potential multiple same named tracks may not be the one selected...
6185
6186       // Get FROM THE OTHER TYPE list
6187       VikTrack *append_track;
6188       if ( trk->is_route )
6189         append_track = vik_trw_layer_get_track ( vtl, l->data );
6190       else
6191         append_track = vik_trw_layer_get_route ( vtl, l->data );
6192
6193       if ( append_track ) {
6194
6195         if ( !append_track->is_route &&
6196              ( ( vik_track_get_segment_count ( append_track ) > 1 ) ||
6197                ( vik_track_get_average_speed ( append_track ) > 0.0 ) ) ) {
6198
6199           if ( a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
6200                                       _("Converting a track to a route removes extra track data such as segments, timestamps, etc...\nDo you want to continue?"), NULL ) ) {
6201             vik_track_merge_segments ( append_track );
6202             vik_track_to_routepoints ( append_track );
6203           }
6204           else {
6205             break;
6206           }
6207         }
6208
6209         vik_track_steal_and_append_trackpoints ( trk, append_track );
6210
6211         // Delete copied which is FROM THE OTHER TYPE list
6212         if ( trk->is_route )
6213           vik_trw_layer_delete_track (vtl, append_track);
6214         else
6215           vik_trw_layer_delete_route (vtl, append_track);
6216       }
6217     }
6218     for (l = append_list; l != NULL; l = g_list_next(l))
6219       g_free(l->data);
6220     g_list_free(append_list);
6221     vik_layer_emit_update( VIK_LAYER(vtl) );
6222   }
6223 }
6224
6225 /* merge by segments */
6226 static void trw_layer_merge_by_segment ( menu_array_sublayer values )
6227 {
6228   VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
6229   VikTrack *trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
6230   guint segments = vik_track_merge_segments ( trk );
6231   // NB currently no need to redraw as segments not actually shown on the display
6232   // However inform the user of what happened:
6233   gchar str[64];
6234   const gchar *tmp_str = ngettext("%d segment merged", "%d segments merged", segments);
6235   g_snprintf(str, 64, tmp_str, segments);
6236   a_dialog_info_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), str );
6237 }
6238
6239 /* merge by time routine */
6240 static void trw_layer_merge_by_timestamp ( menu_array_sublayer values )
6241 {
6242   VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
6243
6244   //time_t t1, t2;
6245
6246   GList *tracks_with_timestamp = NULL;
6247   VikTrack *orig_trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
6248   if (orig_trk->trackpoints &&
6249       !vik_track_get_tp_first(orig_trk)->has_timestamp) {
6250     a_dialog_error_msg(VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Failed. This track does not have timestamp"));
6251     return;
6252   }
6253
6254   twt_udata udata;
6255   udata.result = &tracks_with_timestamp;
6256   udata.exclude = orig_trk;
6257   udata.with_timestamps = TRUE;
6258   g_hash_table_foreach(vtl->tracks, find_tracks_with_timestamp_type, (gpointer)&udata);
6259   tracks_with_timestamp = g_list_reverse(tracks_with_timestamp);
6260
6261   if (!tracks_with_timestamp) {
6262     a_dialog_error_msg(VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Failed. No other track in this layer has timestamp"));
6263     return;
6264   }
6265   g_list_free(tracks_with_timestamp);
6266
6267   static guint threshold_in_minutes = 1;
6268   if (!a_dialog_time_threshold(VIK_GTK_WINDOW_FROM_LAYER(vtl),
6269                                _("Merge Threshold..."),
6270                                _("Merge when time between tracks less than:"),
6271                                &threshold_in_minutes)) {
6272     return;
6273   }
6274
6275   // keep attempting to merge all tracks until no merges within the time specified is possible
6276   gboolean attempt_merge = TRUE;
6277   GList *nearby_tracks = NULL;
6278   GList *trps;
6279   static gpointer params[3];
6280
6281   while ( attempt_merge ) {
6282
6283     // Don't try again unless tracks have changed
6284     attempt_merge = FALSE;
6285
6286     trps = orig_trk->trackpoints;
6287     if ( !trps )
6288       return;
6289
6290     if (nearby_tracks) {
6291       g_list_free(nearby_tracks);
6292       nearby_tracks = NULL;
6293     }
6294
6295     params[0] = &nearby_tracks;
6296     params[1] = orig_trk;
6297     params[2] = GUINT_TO_POINTER (threshold_in_minutes*60); // In seconds
6298
6299     /* get a list of adjacent-in-time tracks */
6300     g_hash_table_foreach(vtl->tracks, find_nearby_tracks_by_time, params);
6301
6302     /* merge them */
6303     GList *l = nearby_tracks;
6304     while ( l ) {
6305       /* remove trackpoints from merged track, delete track */
6306       vik_track_steal_and_append_trackpoints ( orig_trk, VIK_TRACK(l->data) );
6307       vik_trw_layer_delete_track (vtl, VIK_TRACK(l->data));
6308
6309       // Tracks have changed, therefore retry again against all the remaining tracks
6310       attempt_merge = TRUE;
6311
6312       l = g_list_next(l);
6313     }
6314
6315     orig_trk->trackpoints = g_list_sort(orig_trk->trackpoints, trackpoint_compare);
6316   }
6317
6318   g_list_free(nearby_tracks);
6319
6320   vik_layer_emit_update( VIK_LAYER(vtl) );
6321 }
6322
6323 /**
6324  * Split a track at the currently selected trackpoint
6325  */
6326 static void trw_layer_split_at_selected_trackpoint ( VikTrwLayer *vtl, gint subtype )
6327 {
6328   if ( !vtl->current_tpl )
6329     return;
6330
6331   if ( vtl->current_tpl->next && vtl->current_tpl->prev ) {
6332     gchar *name = trw_layer_new_unique_sublayer_name(vtl, subtype, vtl->current_tp_track->name);
6333     if ( name ) {
6334       VikTrack *tr = vik_track_copy ( vtl->current_tp_track, FALSE );
6335       GList *newglist = g_list_alloc ();
6336       newglist->prev = NULL;
6337       newglist->next = vtl->current_tpl->next;
6338       newglist->data = vik_trackpoint_copy(VIK_TRACKPOINT(vtl->current_tpl->data));
6339       tr->trackpoints = newglist;
6340
6341       vtl->current_tpl->next->prev = newglist; /* end old track here */
6342       vtl->current_tpl->next = NULL;
6343
6344       // Bounds of the selected track changed due to the split
6345       vik_track_calculate_bounds ( vtl->current_tp_track );
6346
6347       vtl->current_tpl = newglist; /* change tp to first of new track. */
6348       vtl->current_tp_track = tr;
6349
6350       if ( tr->is_route )
6351         vik_trw_layer_add_route ( vtl, name, tr );
6352       else
6353         vik_trw_layer_add_track ( vtl, name, tr );
6354
6355       // Bounds of the new track created by the split
6356       vik_track_calculate_bounds ( tr );
6357
6358       trku_udata udata;
6359       udata.trk  = tr;
6360       udata.uuid = NULL;
6361
6362       // Also need id of newly created track
6363       gpointer trkf;
6364       if ( tr->is_route )
6365          trkf = g_hash_table_find ( vtl->routes, (GHRFunc) trw_layer_track_find_uuid, &udata );
6366       else
6367          trkf = g_hash_table_find ( vtl->tracks, (GHRFunc) trw_layer_track_find_uuid, &udata );
6368
6369       if ( trkf && udata.uuid )
6370         vtl->current_tp_id = udata.uuid;
6371       else
6372         vtl->current_tp_id = NULL;
6373
6374       vik_layer_emit_update(VIK_LAYER(vtl));
6375     }
6376     g_free ( name );
6377   }
6378 }
6379
6380 /* split by time routine */
6381 static void trw_layer_split_by_timestamp ( menu_array_sublayer values )
6382 {
6383   VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
6384   VikTrack *track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
6385   GList *trps = track->trackpoints;
6386   GList *iter;
6387   GList *newlists = NULL;
6388   GList *newtps = NULL;
6389   static guint thr = 1;
6390
6391   time_t ts, prev_ts;
6392
6393   if ( !trps )
6394     return;
6395
6396   if (!a_dialog_time_threshold(VIK_GTK_WINDOW_FROM_LAYER(vtl), 
6397                                _("Split Threshold..."), 
6398                                _("Split when time between trackpoints exceeds:"), 
6399                                &thr)) {
6400     return;
6401   }
6402
6403   /* iterate through trackpoints, and copy them into new lists without touching original list */
6404   prev_ts = VIK_TRACKPOINT(trps->data)->timestamp;
6405   iter = trps;
6406
6407   while (iter) {
6408     ts = VIK_TRACKPOINT(iter->data)->timestamp;
6409
6410     // Check for unordered time points - this is quite a rare occurence - unless one has reversed a track.
6411     if (ts < prev_ts) {
6412       gchar tmp_str[64];
6413       strftime ( tmp_str, sizeof(tmp_str), "%c", localtime(&ts) );
6414       if ( a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
6415                                 _("Can not split track due to trackpoints not ordered in time - such as at %s.\n\nGoto this trackpoint?"),
6416                                 tmp_str ) ) {
6417         goto_coord ( values[MA_VLP], vtl, values[MA_VVP], &(VIK_TRACKPOINT(iter->data)->coord) );
6418       }
6419       return;
6420     }
6421
6422     if (ts - prev_ts > thr*60) {
6423       /* flush accumulated trackpoints into new list */
6424       newlists = g_list_append(newlists, g_list_reverse(newtps));
6425       newtps = NULL;
6426     }
6427
6428     /* accumulate trackpoint copies in newtps, in reverse order */
6429     newtps = g_list_prepend(newtps, vik_trackpoint_copy(VIK_TRACKPOINT(iter->data)));
6430     prev_ts = ts;
6431     iter = g_list_next(iter);
6432   }
6433   if (newtps) {
6434       newlists = g_list_append(newlists, g_list_reverse(newtps));
6435   }
6436
6437   /* put lists of trackpoints into tracks */
6438   iter = newlists;
6439   // Only bother updating if the split results in new tracks
6440   if (g_list_length (newlists) > 1) {
6441     while (iter) {
6442       gchar *new_tr_name;
6443       VikTrack *tr;
6444
6445       tr = vik_track_copy ( track, FALSE );
6446       tr->trackpoints = (GList *)(iter->data);
6447
6448       new_tr_name = trw_layer_new_unique_sublayer_name ( vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, track->name);
6449       vik_trw_layer_add_track(vtl, new_tr_name, tr);
6450       g_free ( new_tr_name );
6451       vik_track_calculate_bounds ( tr );
6452       iter = g_list_next(iter);
6453     }
6454     // Remove original track and then update the display
6455     vik_trw_layer_delete_track (vtl, track);
6456     vik_layer_emit_update(VIK_LAYER(vtl));
6457   }
6458   g_list_free(newlists);
6459 }
6460
6461 /**
6462  * Split a track by the number of points as specified by the user
6463  */
6464 static void trw_layer_split_by_n_points ( menu_array_sublayer values )
6465 {
6466   VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
6467   VikTrack *track;
6468   if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
6469     track = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
6470   else
6471     track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
6472
6473   if ( !track )
6474     return;
6475
6476   // Check valid track
6477   GList *trps = track->trackpoints;
6478   if ( !trps )
6479     return;
6480
6481   gint points = a_dialog_get_positive_number(VIK_GTK_WINDOW_FROM_LAYER(vtl),
6482                                              _("Split Every Nth Point"),
6483                                              _("Split on every Nth point:"),
6484                                              250,   // Default value as per typical limited track capacity of various GPS devices
6485                                              2,     // Min
6486                                              65536, // Max
6487                                              5);    // Step
6488   // Was a valid number returned?
6489   if (!points)
6490     return;
6491
6492   // Now split...
6493   GList *iter;
6494   GList *newlists = NULL;
6495   GList *newtps = NULL;
6496   gint count = 0;
6497   iter = trps;
6498
6499   while (iter) {
6500     /* accumulate trackpoint copies in newtps, in reverse order */
6501     newtps = g_list_prepend(newtps, vik_trackpoint_copy(VIK_TRACKPOINT(iter->data)));
6502     count++;
6503     if (count >= points) {
6504       /* flush accumulated trackpoints into new list */
6505       newlists = g_list_append(newlists, g_list_reverse(newtps));
6506       newtps = NULL;
6507       count = 0;
6508     }
6509     iter = g_list_next(iter);
6510   }
6511
6512   // If there is a remaining chunk put that into the new split list
6513   // This may well be the whole track if no split points were encountered
6514   if (newtps) {
6515       newlists = g_list_append(newlists, g_list_reverse(newtps));
6516   }
6517
6518   /* put lists of trackpoints into tracks */
6519   iter = newlists;
6520   // Only bother updating if the split results in new tracks
6521   if (g_list_length (newlists) > 1) {
6522     while (iter) {
6523       gchar *new_tr_name;
6524       VikTrack *tr;
6525
6526       tr = vik_track_copy ( track, FALSE );
6527       tr->trackpoints = (GList *)(iter->data);
6528
6529       if ( track->is_route ) {
6530         new_tr_name = trw_layer_new_unique_sublayer_name ( vtl, VIK_TRW_LAYER_SUBLAYER_ROUTE, track->name);
6531         vik_trw_layer_add_route(vtl, new_tr_name, tr);
6532       }
6533       else {
6534         new_tr_name = trw_layer_new_unique_sublayer_name ( vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, track->name);
6535         vik_trw_layer_add_track(vtl, new_tr_name, tr);
6536       }
6537       g_free ( new_tr_name );
6538       vik_track_calculate_bounds ( tr );
6539
6540       iter = g_list_next(iter);
6541     }
6542     // Remove original track and then update the display
6543     if ( track->is_route )
6544       vik_trw_layer_delete_route (vtl, track);
6545     else
6546       vik_trw_layer_delete_track (vtl, track);
6547     vik_layer_emit_update(VIK_LAYER(vtl));
6548   }
6549   g_list_free(newlists);
6550 }
6551
6552 /**
6553  * Split a track at the currently selected trackpoint
6554  */
6555 static void trw_layer_split_at_trackpoint ( menu_array_sublayer values )
6556 {
6557   VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
6558   gint subtype = GPOINTER_TO_INT (values[MA_SUBTYPE]);
6559   trw_layer_split_at_selected_trackpoint ( vtl, subtype );
6560 }
6561
6562 /**
6563  * Split a track by its segments
6564  * Routes do not have segments so don't call this for routes
6565  */
6566 static void trw_layer_split_segments ( menu_array_sublayer values )
6567 {
6568   VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
6569   VikTrack *trk = g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
6570
6571   if ( !trk )
6572     return;
6573
6574   guint ntracks;
6575
6576   VikTrack **tracks = vik_track_split_into_segments (trk, &ntracks);
6577   gchar *new_tr_name;
6578   guint i;
6579   for ( i = 0; i < ntracks; i++ ) {
6580     if ( tracks[i] ) {
6581       new_tr_name = trw_layer_new_unique_sublayer_name ( vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, trk->name);
6582       vik_trw_layer_add_track ( vtl, new_tr_name, tracks[i] );
6583       g_free ( new_tr_name );
6584     }
6585   }
6586   if ( tracks ) {
6587     g_free ( tracks );
6588     // Remove original track
6589     vik_trw_layer_delete_track ( vtl, trk );
6590     vik_layer_emit_update ( VIK_LAYER(vtl) );
6591   }
6592   else {
6593     a_dialog_error_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Can not split track as it has no segments"));
6594   }
6595 }
6596 /* end of split/merge routines */
6597
6598 static void trw_layer_trackpoint_selected_delete ( VikTrwLayer *vtl, VikTrack *trk )
6599 {
6600   GList *new_tpl;
6601
6602   // Find available adjacent trackpoint
6603   if ( (new_tpl = vtl->current_tpl->next) || (new_tpl = vtl->current_tpl->prev) ) {
6604     if ( VIK_TRACKPOINT(vtl->current_tpl->data)->newsegment && vtl->current_tpl->next )
6605       VIK_TRACKPOINT(vtl->current_tpl->next->data)->newsegment = TRUE; /* don't concat segments on del */
6606
6607     // Delete current trackpoint
6608     vik_trackpoint_free ( vtl->current_tpl->data );
6609     trk->trackpoints = g_list_delete_link ( trk->trackpoints, vtl->current_tpl );
6610
6611     // Set to current to the available adjacent trackpoint
6612     vtl->current_tpl = new_tpl;
6613
6614     if ( vtl->current_tp_track ) {
6615       vik_track_calculate_bounds ( vtl->current_tp_track );
6616     }
6617   }
6618   else {
6619     // Delete current trackpoint
6620     vik_trackpoint_free ( vtl->current_tpl->data );
6621     trk->trackpoints = g_list_delete_link ( trk->trackpoints, vtl->current_tpl );
6622     trw_layer_cancel_current_tp ( vtl, FALSE );
6623   }
6624 }
6625
6626 /**
6627  * Delete the selected point
6628  */
6629 static void trw_layer_delete_point_selected ( menu_array_sublayer values )
6630 {
6631   VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
6632   VikTrack *trk;
6633   if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
6634     trk = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
6635   else
6636     trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
6637
6638   if ( !trk )
6639     return;
6640
6641   if ( !vtl->current_tpl )
6642     return;
6643
6644   trw_layer_trackpoint_selected_delete ( vtl, trk );
6645
6646   // Track has been updated so update tps:
6647   trw_layer_cancel_tps_of_track ( vtl, trk );
6648
6649   vik_layer_emit_update ( VIK_LAYER(vtl) );
6650 }
6651
6652 /**
6653  * Delete adjacent track points at the same position
6654  * AKA Delete Dulplicates on the Properties Window
6655  */
6656 static void trw_layer_delete_points_same_position ( menu_array_sublayer values )
6657 {
6658   VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
6659   VikTrack *trk;
6660   if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
6661     trk = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
6662   else
6663     trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
6664
6665   if ( !trk )
6666     return;
6667
6668   gulong removed = vik_track_remove_dup_points ( trk );
6669
6670   // Track has been updated so update tps:
6671   trw_layer_cancel_tps_of_track ( vtl, trk );
6672
6673   // Inform user how much was deleted as it's not obvious from the normal view
6674   gchar str[64];
6675   const gchar *tmp_str = ngettext("Deleted %ld point", "Deleted %ld points", removed);
6676   g_snprintf(str, 64, tmp_str, removed);
6677   a_dialog_info_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), str);
6678
6679   vik_layer_emit_update ( VIK_LAYER(vtl) );
6680 }
6681
6682 /**
6683  * Delete adjacent track points with the same timestamp
6684  * Normally new tracks that are 'routes' won't have any timestamps so should be OK to clean up the track
6685  */
6686 static void trw_layer_delete_points_same_time ( menu_array_sublayer values )
6687 {
6688   VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
6689   VikTrack *trk;
6690   if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
6691     trk = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
6692   else
6693     trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
6694
6695   if ( !trk )
6696     return;
6697
6698   gulong removed = vik_track_remove_same_time_points ( trk );
6699
6700   // Track has been updated so update tps:
6701   trw_layer_cancel_tps_of_track ( vtl, trk );
6702
6703   // Inform user how much was deleted as it's not obvious from the normal view
6704   gchar str[64];
6705   const gchar *tmp_str = ngettext("Deleted %ld point", "Deleted %ld points", removed);
6706   g_snprintf(str, 64, tmp_str, removed);
6707   a_dialog_info_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), str);
6708
6709   vik_layer_emit_update ( VIK_LAYER(vtl) );
6710 }
6711
6712 /**
6713  * Insert a point
6714  */
6715 static void trw_layer_insert_point_after ( menu_array_sublayer values )
6716 {
6717   VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
6718   VikTrack *track;
6719   if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
6720     track = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
6721   else
6722     track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
6723
6724   if ( ! track )
6725     return;
6726
6727   trw_layer_insert_tp_beside_current_tp ( vtl, FALSE );
6728
6729   vik_layer_emit_update ( VIK_LAYER(vtl) );
6730 }
6731
6732 static void trw_layer_insert_point_before ( menu_array_sublayer values )
6733 {
6734   VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
6735   VikTrack *track;
6736   if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
6737     track = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
6738   else
6739     track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
6740
6741   if ( ! track )
6742     return;
6743
6744   trw_layer_insert_tp_beside_current_tp ( vtl, TRUE );
6745
6746   vik_layer_emit_update ( VIK_LAYER(vtl) );
6747 }
6748
6749 /**
6750  * Reverse a track
6751  */
6752 static void trw_layer_reverse ( menu_array_sublayer values )
6753 {
6754   VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
6755   VikTrack *track;
6756   if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
6757     track = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
6758   else
6759     track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
6760
6761   if ( ! track )
6762     return;
6763
6764   vik_track_reverse ( track );
6765  
6766   vik_layer_emit_update ( VIK_LAYER(vtl) );
6767 }
6768
6769 /**
6770  * Open a program at the specified date
6771  * Mainly for RedNotebook - http://rednotebook.sourceforge.net/
6772  * But could work with any program that accepts a command line of --date=<date>
6773  * FUTURE: Allow configuring of command line options + date format
6774  */
6775 static void trw_layer_diary_open ( VikTrwLayer *vtl, const gchar *date_str )
6776 {
6777   GError *err = NULL;
6778   gchar *cmd = g_strdup_printf ( "%s %s%s", diary_program, "--date=", date_str );
6779   if ( ! g_spawn_command_line_async ( cmd, &err ) ) {
6780     a_dialog_error_msg_extra ( VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Could not launch %s to open file."), diary_program );
6781     g_error_free ( err );
6782   }
6783   g_free ( cmd );
6784 }
6785
6786 /**
6787  * Open a diary at the date of the track or waypoint
6788  */
6789 static void trw_layer_diary ( menu_array_sublayer values )
6790 {
6791   VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
6792
6793   if ( GPOINTER_TO_INT(values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_TRACK ) {
6794     VikTrack *trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
6795     if ( ! trk )
6796       return;
6797
6798     gchar date_buf[20];
6799     date_buf[0] = '\0';
6800     if ( trk->trackpoints && VIK_TRACKPOINT(trk->trackpoints->data)->has_timestamp ) {
6801       strftime (date_buf, sizeof(date_buf), "%Y-%m-%d", gmtime(&(VIK_TRACKPOINT(trk->trackpoints->data)->timestamp)));
6802       trw_layer_diary_open ( vtl, date_buf );
6803     }
6804     else
6805       a_dialog_info_msg ( VIK_GTK_WINDOW_FROM_LAYER(vtl), _("This track has no date information.") );
6806   }
6807   else if ( GPOINTER_TO_INT(values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_WAYPOINT ) {
6808     VikWaypoint *wpt = (VikWaypoint *) g_hash_table_lookup ( vtl->waypoints, values[MA_SUBLAYER_ID] );
6809     if ( ! wpt )
6810       return;
6811
6812     gchar date_buf[20];
6813     date_buf[0] = '\0';
6814     if ( wpt->has_timestamp ) {
6815       strftime (date_buf, sizeof(date_buf), "%Y-%m-%d", gmtime(&(wpt->timestamp)));
6816       trw_layer_diary_open ( vtl, date_buf );
6817     }
6818     else
6819       a_dialog_info_msg ( VIK_GTK_WINDOW_FROM_LAYER(vtl), _("This waypoint has no date information.") );
6820   }
6821 }
6822
6823 /**
6824  * Open a program at the specified date
6825  * Mainly for Stellarium - http://stellarium.org/
6826  * But could work with any program that accepts the same command line options...
6827  * FUTURE: Allow configuring of command line options + format or parameters
6828  */
6829 static void trw_layer_astro_open ( VikTrwLayer *vtl, const gchar *date_str, const gchar *time_str, const gchar *lat_str, const gchar *lon_str, const gchar *alt_str )
6830 {
6831   GError *err = NULL;
6832   gchar *tmp;
6833   gint fd = g_file_open_tmp ( "vik-astro-XXXXXX.ini", &tmp, &err );
6834   if (fd < 0) {
6835     g_warning ( "%s: Failed to open temporary file: %s", __FUNCTION__, err->message );
6836     g_clear_error ( &err );
6837     return;
6838   }
6839   gchar *cmd = g_strdup_printf ( "%s %s %s %s %s %s %s %s %s %s %s %s %s %s",
6840                                   astro_program, "-c", tmp, "--full-screen no", "--sky-date", date_str, "--sky-time", time_str, "--latitude", lat_str, "--longitude", lon_str, "--altitude", alt_str );
6841   g_warning ( "%s", cmd );
6842   if ( ! g_spawn_command_line_async ( cmd, &err ) ) {
6843     a_dialog_error_msg_extra ( VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Could not launch %s"), astro_program );
6844     g_warning ( "%s", err->message );
6845     g_error_free ( err );
6846   }
6847   util_add_to_deletion_list ( tmp );
6848   g_free ( tmp );
6849   g_free ( cmd );
6850 }
6851
6852 // Format of stellarium lat & lon seems designed to be particularly awkward
6853 //  who uses ' & " in the parameters for the command line?!
6854 // -1d4'27.48"
6855 // +53d58'16.65"
6856 static gchar *convert_to_dms ( gdouble dec )
6857 {
6858   gdouble tmp;
6859   gchar sign_c = ' ';
6860   gint val_d, val_m;
6861   gdouble val_s;
6862   gchar *result = NULL;
6863
6864   if ( dec > 0 )
6865     sign_c = '+';
6866   else if ( dec < 0 )
6867     sign_c = '-';
6868   else // Nul value
6869     sign_c = ' ';
6870
6871   // Degrees
6872   tmp = fabs(dec);
6873   val_d = (gint)tmp;
6874
6875   // Minutes
6876   tmp = (tmp - val_d) * 60;
6877   val_m = (gint)tmp;
6878
6879   // Seconds
6880   val_s = (tmp - val_m) * 60;
6881
6882   // Format
6883   result = g_strdup_printf ( "%c%dd%d\\\'%.4f\\\"", sign_c, val_d, val_m, val_s );
6884   return result;
6885 }
6886
6887 /**
6888  * Open an astronomy program at the date & position of the track center, trackpoint or waypoint
6889  */
6890 static void trw_layer_astro ( menu_array_sublayer values )
6891 {
6892   VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
6893
6894   if ( GPOINTER_TO_INT(values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_TRACK ) {
6895     VikTrack *trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
6896     if ( ! trk )
6897       return;
6898
6899     VikTrackpoint *tp = NULL;
6900     if ( vtl->current_tpl )
6901       // Current Trackpoint
6902       tp = VIK_TRACKPOINT(vtl->current_tpl->data);
6903     else if ( trk->trackpoints )
6904       // Otherwise first trackpoint
6905       tp = VIK_TRACKPOINT(trk->trackpoints->data);
6906     else
6907       // Give up
6908       return;
6909
6910     if ( tp->has_timestamp ) {
6911       gchar date_buf[20];
6912       strftime (date_buf, sizeof(date_buf), "%Y%m%d", gmtime(&(tp->timestamp)));
6913       gchar time_buf[20];
6914       strftime (time_buf, sizeof(time_buf), "%H:%M:%S", gmtime(&(tp->timestamp)));
6915       struct LatLon ll;
6916       vik_coord_to_latlon ( &tp->coord, &ll );
6917       gchar *lat_str = convert_to_dms ( ll.lat );
6918       gchar *lon_str = convert_to_dms ( ll.lon );
6919       gchar alt_buf[20];
6920       snprintf (alt_buf, sizeof(alt_buf), "%d", (gint)round(tp->altitude) );
6921       trw_layer_astro_open ( vtl, date_buf, time_buf, lat_str, lon_str, alt_buf);
6922       g_free ( lat_str );
6923       g_free ( lon_str );
6924     }
6925     else
6926       a_dialog_info_msg ( VIK_GTK_WINDOW_FROM_LAYER(vtl), _("This track has no date information.") );
6927   }
6928   else if ( GPOINTER_TO_INT(values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_WAYPOINT ) {
6929     VikWaypoint *wpt = (VikWaypoint *) g_hash_table_lookup ( vtl->waypoints, values[MA_SUBLAYER_ID] );
6930     if ( ! wpt )
6931       return;
6932
6933     if ( wpt->has_timestamp ) {
6934       gchar date_buf[20];
6935       strftime (date_buf, sizeof(date_buf), "%Y%m%d", gmtime(&(wpt->timestamp)));
6936       gchar time_buf[20];
6937       strftime (time_buf, sizeof(time_buf), "%H:%M:%S", gmtime(&(wpt->timestamp)));
6938       struct LatLon ll;
6939       vik_coord_to_latlon ( &wpt->coord, &ll );
6940       gchar *lat_str = convert_to_dms ( ll.lat );
6941       gchar *lon_str = convert_to_dms ( ll.lon );
6942       gchar alt_buf[20];
6943       snprintf (alt_buf, sizeof(alt_buf), "%d", (gint)round(wpt->altitude) );
6944       trw_layer_astro_open ( vtl, date_buf, time_buf, lat_str, lon_str, alt_buf );
6945       g_free ( lat_str );
6946       g_free ( lon_str );
6947     }
6948     else
6949       a_dialog_info_msg ( VIK_GTK_WINDOW_FROM_LAYER(vtl), _("This waypoint has no date information.") );
6950   }
6951 }
6952
6953 /**
6954  * Similar to trw_layer_enum_item, but this uses a sorted method
6955  */
6956 /* Currently unused
6957 static void trw_layer_sorted_name_list(gpointer key, gpointer value, gpointer udata)
6958 {
6959   GList **list = (GList**)udata;
6960   // *list = g_list_prepend(*all, key); //unsorted method
6961   // Sort named list alphabetically
6962   *list = g_list_insert_sorted_with_data (*list, key, sort_alphabetically, NULL);
6963 }
6964 */
6965
6966 /**
6967  * Now Waypoint specific sort
6968  */
6969 static void trw_layer_sorted_wp_id_by_name_list (const gpointer id, const VikWaypoint *wp, gpointer udata)
6970 {
6971   GList **list = (GList**)udata;
6972   // Sort named list alphabetically
6973   *list = g_list_insert_sorted_with_data (*list, wp->name, sort_alphabetically, NULL);
6974 }
6975
6976 /**
6977  * Track specific sort
6978  */
6979 static void trw_layer_sorted_track_id_by_name_list (const gpointer id, const VikTrack *trk, gpointer udata)
6980 {
6981   GList **list = (GList**)udata;
6982   // Sort named list alphabetically
6983   *list = g_list_insert_sorted_with_data (*list, trk->name, sort_alphabetically, NULL);
6984 }
6985
6986
6987 typedef struct {
6988   gboolean    has_same_track_name;
6989   const gchar *same_track_name;
6990 } same_track_name_udata;
6991
6992 static gint check_tracks_for_same_name ( gconstpointer aa, gconstpointer bb, gpointer udata )
6993 {
6994   const gchar* namea = (const gchar*) aa;
6995   const gchar* nameb = (const gchar*) bb;
6996
6997   // the test
6998   gint result = strcmp ( namea, nameb );
6999
7000   if ( result == 0 ) {
7001     // Found two names the same
7002     same_track_name_udata *user_data = udata;
7003     user_data->has_same_track_name = TRUE;
7004     user_data->same_track_name = namea;
7005   }
7006
7007   // Leave ordering the same
7008   return 0;
7009 }
7010
7011 /**
7012  * Find out if any tracks have the same name in this hash table
7013  */
7014 static gboolean trw_layer_has_same_track_names ( GHashTable *ht_tracks )
7015 {
7016   // Sort items by name, then compare if any next to each other are the same
7017
7018   GList *track_names = NULL;
7019   g_hash_table_foreach ( ht_tracks, (GHFunc) trw_layer_sorted_track_id_by_name_list, &track_names );
7020
7021   // No tracks
7022   if ( ! track_names )
7023     return FALSE;
7024
7025   same_track_name_udata udata;
7026   udata.has_same_track_name = FALSE;
7027
7028   // Use sort routine to traverse list comparing items
7029   // Don't care how this list ends up ordered ( doesn't actually change ) - care about the returned status
7030   GList *dummy_list = g_list_sort_with_data ( track_names, check_tracks_for_same_name, &udata );
7031   // Still no tracks...
7032   if ( ! dummy_list )
7033     return FALSE;
7034
7035   return udata.has_same_track_name;
7036 }
7037
7038 /**
7039  * Force unqiue track names for the track table specified
7040  * Note the panel is a required parameter to enable the update of the names displayed
7041  * Specify if on tracks or else on routes
7042  */
7043 static void vik_trw_layer_uniquify_tracks ( VikTrwLayer *vtl, VikLayersPanel *vlp, GHashTable *track_table, gboolean ontrack )
7044 {
7045   // . Search list for an instance of repeated name
7046   // . get track of this name
7047   // . create new name
7048   // . rename track & update equiv. treeview iter
7049   // . repeat until all different
7050
7051   same_track_name_udata udata;
7052
7053   GList *track_names = NULL;
7054   udata.has_same_track_name = FALSE;
7055   udata.same_track_name = NULL;
7056
7057   g_hash_table_foreach ( track_table, (GHFunc) trw_layer_sorted_track_id_by_name_list, &track_names );
7058
7059   // No tracks
7060   if ( ! track_names )
7061     return;
7062
7063   GList *dummy_list1 = g_list_sort_with_data ( track_names, check_tracks_for_same_name, &udata );
7064
7065   // Still no tracks...
7066   if ( ! dummy_list1 )
7067     return;
7068
7069   while ( udata.has_same_track_name ) {
7070
7071     // Find a track with the same name
7072     VikTrack *trk;
7073     if ( ontrack )
7074       trk = vik_trw_layer_get_track ( vtl, (gpointer) udata.same_track_name );
7075     else
7076       trk = vik_trw_layer_get_route ( vtl, (gpointer) udata.same_track_name );
7077
7078     if ( ! trk ) {
7079       // Broken :(
7080       g_critical("Houston, we've had a problem.");
7081       vik_statusbar_set_message ( vik_window_get_statusbar (VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl))), VIK_STATUSBAR_INFO, 
7082                                   _("Internal Error in vik_trw_layer_uniquify_tracks") );
7083       return;
7084     }
7085
7086     // Rename it
7087     gchar *newname = trw_layer_new_unique_sublayer_name ( vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, udata.same_track_name );
7088     vik_track_set_name ( trk, newname );
7089
7090     trku_udata udataU;
7091     udataU.trk  = trk;
7092     udataU.uuid = NULL;
7093
7094     // Need want key of it for treeview update
7095     gpointer trkf = g_hash_table_find ( track_table, (GHRFunc) trw_layer_track_find_uuid, &udataU );
7096
7097     if ( trkf && udataU.uuid ) {
7098
7099       GtkTreeIter *it;
7100       if ( ontrack )
7101         it = g_hash_table_lookup ( vtl->tracks_iters, udataU.uuid );
7102       else
7103         it = g_hash_table_lookup ( vtl->routes_iters, udataU.uuid );
7104
7105       if ( it ) {
7106         vik_treeview_item_set_name ( VIK_LAYER(vtl)->vt, it, newname );
7107         if ( ontrack )
7108           vik_treeview_sort_children ( VIK_LAYER(vtl)->vt, &(vtl->tracks_iter), vtl->track_sort_order );
7109         else
7110           vik_treeview_sort_children ( VIK_LAYER(vtl)->vt, &(vtl->routes_iter), vtl->track_sort_order );
7111       }
7112     }
7113     g_free ( newname );
7114
7115     // Start trying to find same names again...
7116     track_names = NULL;
7117     g_hash_table_foreach ( track_table, (GHFunc) trw_layer_sorted_track_id_by_name_list, &track_names );
7118     udata.has_same_track_name = FALSE;
7119     GList *dummy_list2 = g_list_sort_with_data ( track_names, check_tracks_for_same_name, &udata );
7120
7121     // No tracks any more - give up searching
7122     if ( ! dummy_list2 )
7123       udata.has_same_track_name = FALSE;
7124   }
7125
7126   // Update
7127   vik_layers_panel_emit_update ( vlp );
7128 }
7129
7130 static void trw_layer_sort_order_specified ( VikTrwLayer *vtl, guint sublayer_type, vik_layer_sort_order_t order )
7131 {
7132   GtkTreeIter *iter;
7133
7134   switch (sublayer_type) {
7135   case VIK_TRW_LAYER_SUBLAYER_TRACKS:
7136     iter = &(vtl->tracks_iter);
7137     vtl->track_sort_order = order;
7138     break;
7139   case VIK_TRW_LAYER_SUBLAYER_ROUTES:
7140     iter = &(vtl->routes_iter);
7141     vtl->track_sort_order = order;
7142     break;
7143   default: // VIK_TRW_LAYER_SUBLAYER_WAYPOINTS:
7144     iter = &(vtl->waypoints_iter);
7145     vtl->wp_sort_order = order;
7146     break;
7147   }
7148
7149   vik_treeview_sort_children ( VIK_LAYER(vtl)->vt, iter, order );
7150 }
7151
7152 static void trw_layer_sort_order_a2z ( menu_array_sublayer values )
7153 {
7154   VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
7155   trw_layer_sort_order_specified ( vtl, GPOINTER_TO_INT(values[MA_SUBTYPE]), VL_SO_ALPHABETICAL_ASCENDING );
7156 }
7157
7158 static void trw_layer_sort_order_z2a ( menu_array_sublayer values )
7159 {
7160   VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
7161   trw_layer_sort_order_specified ( vtl, GPOINTER_TO_INT(values[MA_SUBTYPE]), VL_SO_ALPHABETICAL_DESCENDING );
7162 }
7163
7164 static void trw_layer_sort_order_timestamp_ascend ( menu_array_sublayer values )
7165 {
7166   VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
7167   trw_layer_sort_order_specified ( vtl, GPOINTER_TO_INT(values[MA_SUBTYPE]), VL_SO_DATE_ASCENDING );
7168 }
7169
7170 static void trw_layer_sort_order_timestamp_descend ( menu_array_sublayer values )
7171 {
7172   VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
7173   trw_layer_sort_order_specified ( vtl, GPOINTER_TO_INT(values[MA_SUBTYPE]), VL_SO_DATE_DESCENDING );
7174 }
7175
7176 /**
7177  *
7178  */
7179 static void trw_layer_delete_tracks_from_selection ( menu_array_layer values )
7180 {
7181   VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
7182   GList *all = NULL;
7183
7184   // Ensure list of track names offered is unique
7185   if ( trw_layer_has_same_track_names ( vtl->tracks ) ) {
7186     if ( a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
7187                               _("Multiple entries with the same name exist. This method only works with unique names. Force unique names now?"), NULL ) ) {
7188       vik_trw_layer_uniquify_tracks ( vtl, VIK_LAYERS_PANEL(values[MA_VLP]), vtl->tracks, TRUE );
7189     }
7190     else
7191       return;
7192   }
7193
7194   // Sort list alphabetically for better presentation
7195   g_hash_table_foreach(vtl->tracks, (GHFunc) trw_layer_sorted_track_id_by_name_list, &all);
7196
7197   if ( ! all ) {
7198     a_dialog_error_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), _("No tracks found"));
7199     return;
7200   }
7201
7202   // Get list of items to delete from the user
7203   GList *delete_list = a_dialog_select_from_list(VIK_GTK_WINDOW_FROM_LAYER(vtl),
7204                                                  all,
7205                                                  TRUE,
7206                                                  _("Delete Selection"),
7207                                                  _("Select tracks to delete"));
7208   g_list_free(all);
7209
7210   // Delete requested tracks
7211   // since specificly requested, IMHO no need for extra confirmation
7212   if ( delete_list ) {
7213     GList *l;
7214     for (l = delete_list; l != NULL; l = g_list_next(l)) {
7215       // This deletes first trk it finds of that name (but uniqueness is enforced above)
7216       trw_layer_delete_track_by_name (vtl, l->data, vtl->tracks);
7217     }
7218     g_list_free(delete_list);
7219     // Reset layer timestamps in case they have now changed
7220     vik_treeview_item_set_timestamp ( vtl->vl.vt, &vtl->vl.iter, trw_layer_get_timestamp(vtl) );
7221
7222     vik_layer_emit_update( VIK_LAYER(vtl) );
7223   }
7224 }
7225
7226 /**
7227  *
7228  */
7229 static void trw_layer_delete_routes_from_selection ( menu_array_layer values )
7230 {
7231   VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
7232   GList *all = NULL;
7233
7234   // Ensure list of track names offered is unique
7235   if ( trw_layer_has_same_track_names ( vtl->routes ) ) {
7236     if ( a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
7237                               _("Multiple entries with the same name exist. This method only works with unique names. Force unique names now?"), NULL ) ) {
7238       vik_trw_layer_uniquify_tracks ( vtl, VIK_LAYERS_PANEL(values[MA_VLP]), vtl->routes, FALSE );
7239     }
7240     else
7241       return;
7242   }
7243
7244   // Sort list alphabetically for better presentation
7245   g_hash_table_foreach(vtl->routes, (GHFunc) trw_layer_sorted_track_id_by_name_list, &all);
7246
7247   if ( ! all ) {
7248     a_dialog_error_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), _("No routes found"));
7249     return;
7250   }
7251
7252   // Get list of items to delete from the user
7253   GList *delete_list = a_dialog_select_from_list ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
7254                                                    all,
7255                                                    TRUE,
7256                                                    _("Delete Selection"),
7257                                                    _("Select routes to delete") );
7258   g_list_free(all);
7259
7260   // Delete requested routes
7261   // since specificly requested, IMHO no need for extra confirmation
7262   if ( delete_list ) {
7263     GList *l;
7264     for (l = delete_list; l != NULL; l = g_list_next(l)) {
7265       // This deletes first route it finds of that name (but uniqueness is enforced above)
7266       trw_layer_delete_track_by_name (vtl, l->data, vtl->routes);
7267     }
7268     g_list_free(delete_list);
7269     vik_layer_emit_update( VIK_LAYER(vtl) );
7270   }
7271 }
7272
7273 typedef struct {
7274   gboolean    has_same_waypoint_name;
7275   const gchar *same_waypoint_name;
7276 } same_waypoint_name_udata;
7277
7278 static gint check_waypoints_for_same_name ( gconstpointer aa, gconstpointer bb, gpointer udata )
7279 {
7280   const gchar* namea = (const gchar*) aa;
7281   const gchar* nameb = (const gchar*) bb;
7282
7283   // the test
7284   gint result = strcmp ( namea, nameb );
7285
7286   if ( result == 0 ) {
7287     // Found two names the same
7288     same_waypoint_name_udata *user_data = udata;
7289     user_data->has_same_waypoint_name = TRUE;
7290     user_data->same_waypoint_name = namea;
7291   }
7292
7293   // Leave ordering the same
7294   return 0;
7295 }
7296
7297 /**
7298  * Find out if any waypoints have the same name in this layer
7299  */
7300 gboolean trw_layer_has_same_waypoint_names ( VikTrwLayer *vtl )
7301 {
7302   // Sort items by name, then compare if any next to each other are the same
7303
7304   GList *waypoint_names = NULL;
7305   g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_sorted_wp_id_by_name_list, &waypoint_names );
7306
7307   // No waypoints
7308   if ( ! waypoint_names )
7309     return FALSE;
7310
7311   same_waypoint_name_udata udata;
7312   udata.has_same_waypoint_name = FALSE;
7313
7314   // Use sort routine to traverse list comparing items
7315   // Don't care how this list ends up ordered ( doesn't actually change ) - care about the returned status
7316   GList *dummy_list = g_list_sort_with_data ( waypoint_names, check_waypoints_for_same_name, &udata );
7317   // Still no waypoints...
7318   if ( ! dummy_list )
7319     return FALSE;
7320
7321   return udata.has_same_waypoint_name;
7322 }
7323
7324 /**
7325  * Force unqiue waypoint names for this layer
7326  * Note the panel is a required parameter to enable the update of the names displayed
7327  */
7328 static void vik_trw_layer_uniquify_waypoints ( VikTrwLayer *vtl, VikLayersPanel *vlp )
7329 {
7330   // . Search list for an instance of repeated name
7331   // . get waypoint of this name
7332   // . create new name
7333   // . rename waypoint & update equiv. treeview iter
7334   // . repeat until all different
7335
7336   same_waypoint_name_udata udata;
7337
7338   GList *waypoint_names = NULL;
7339   udata.has_same_waypoint_name = FALSE;
7340   udata.same_waypoint_name = NULL;
7341
7342   g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_sorted_wp_id_by_name_list, &waypoint_names );
7343
7344   // No waypoints
7345   if ( ! waypoint_names )
7346     return;
7347
7348   GList *dummy_list1 = g_list_sort_with_data ( waypoint_names, check_waypoints_for_same_name, &udata );
7349
7350   // Still no waypoints...
7351   if ( ! dummy_list1 )
7352     return;
7353
7354   while ( udata.has_same_waypoint_name ) {
7355
7356     // Find a waypoint with the same name
7357     VikWaypoint *waypoint = vik_trw_layer_get_waypoint ( vtl, (gpointer) udata.same_waypoint_name );
7358
7359     if ( ! waypoint ) {
7360       // Broken :(
7361       g_critical("Houston, we've had a problem.");
7362       vik_statusbar_set_message ( vik_window_get_statusbar (VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl))), VIK_STATUSBAR_INFO, 
7363                                   _("Internal Error in vik_trw_layer_uniquify_waypoints") );
7364       return;
7365     }
7366
7367     // Rename it
7368     gchar *newname = trw_layer_new_unique_sublayer_name ( vtl, VIK_TRW_LAYER_SUBLAYER_WAYPOINT, udata.same_waypoint_name );
7369
7370     trw_layer_waypoint_rename ( vtl, waypoint, newname );
7371
7372     g_free (newname);
7373
7374     // Start trying to find same names again...
7375     waypoint_names = NULL;
7376     g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_sorted_wp_id_by_name_list, &waypoint_names );
7377     udata.has_same_waypoint_name = FALSE;
7378     GList *dummy_list2 = g_list_sort_with_data ( waypoint_names, check_waypoints_for_same_name, &udata );
7379
7380     // No waypoints any more - give up searching
7381     if ( ! dummy_list2 )
7382       udata.has_same_waypoint_name = FALSE;
7383   }
7384
7385   // Update
7386   vik_layers_panel_emit_update ( vlp );
7387 }
7388
7389 /**
7390  *
7391  */
7392 static void trw_layer_delete_waypoints_from_selection ( menu_array_layer values )
7393 {
7394   VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
7395   GList *all = NULL;
7396
7397   // Ensure list of waypoint names offered is unique
7398   if ( trw_layer_has_same_waypoint_names ( vtl ) ) {
7399     if ( a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
7400                               _("Multiple entries with the same name exist. This method only works with unique names. Force unique names now?"), NULL ) ) {
7401       vik_trw_layer_uniquify_waypoints ( vtl, VIK_LAYERS_PANEL(values[MA_VLP]) );
7402     }
7403     else
7404       return;
7405   }
7406
7407   // Sort list alphabetically for better presentation
7408   g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_sorted_wp_id_by_name_list, &all);
7409   if ( ! all ) {
7410     a_dialog_error_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), _("No waypoints found"));
7411     return;
7412   }
7413
7414   all = g_list_sort_with_data(all, sort_alphabetically, NULL);
7415
7416   // Get list of items to delete from the user
7417   GList *delete_list = a_dialog_select_from_list(VIK_GTK_WINDOW_FROM_LAYER(vtl),
7418                                                  all,
7419                                                  TRUE,
7420                                                  _("Delete Selection"),
7421                                                  _("Select waypoints to delete"));
7422   g_list_free(all);
7423
7424   // Delete requested waypoints
7425   // since specificly requested, IMHO no need for extra confirmation
7426   if ( delete_list ) {
7427     GList *l;
7428     for (l = delete_list; l != NULL; l = g_list_next(l)) {
7429       // This deletes first waypoint it finds of that name (but uniqueness is enforced above)
7430       trw_layer_delete_waypoint_by_name (vtl, l->data);
7431     }
7432     g_list_free(delete_list);
7433
7434     trw_layer_calculate_bounds_waypoints ( vtl );
7435     // Reset layer timestamp in case it has now changed
7436     vik_treeview_item_set_timestamp ( vtl->vl.vt, &vtl->vl.iter, trw_layer_get_timestamp(vtl) );
7437     vik_layer_emit_update( VIK_LAYER(vtl) );
7438   }
7439
7440 }
7441
7442 /**
7443  *
7444  */
7445 static void trw_layer_iter_visibility_toggle ( gpointer id, GtkTreeIter *it, VikTreeview *vt )
7446 {
7447   vik_treeview_item_toggle_visible ( vt, it );
7448 }
7449
7450 /**
7451  *
7452  */
7453 static void trw_layer_iter_visibility ( gpointer id, GtkTreeIter *it, gpointer vis_data[2] )
7454 {
7455   vik_treeview_item_set_visible ( (VikTreeview*)vis_data[0], it, GPOINTER_TO_INT (vis_data[1]) );
7456 }
7457
7458 /**
7459  *
7460  */
7461 static void trw_layer_waypoints_visibility ( gpointer id, VikWaypoint *wp, gpointer on_off )
7462 {
7463   wp->visible = GPOINTER_TO_INT (on_off);
7464 }
7465
7466 /**
7467  *
7468  */
7469 static void trw_layer_waypoints_toggle_visibility ( gpointer id, VikWaypoint *wp )
7470 {
7471   wp->visible = !wp->visible;
7472 }
7473
7474 /**
7475  *
7476  */
7477 static void trw_layer_waypoints_visibility_off ( menu_array_layer values )
7478 {
7479   VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
7480   gpointer vis_data[2] = { VIK_LAYER(vtl)->vt, GINT_TO_POINTER(FALSE) };
7481   g_hash_table_foreach ( vtl->waypoints_iters, (GHFunc) trw_layer_iter_visibility, vis_data );
7482   g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_waypoints_visibility, vis_data[1] );
7483   // Redraw
7484   vik_layer_emit_update ( VIK_LAYER(vtl) );
7485 }
7486
7487 /**
7488  *
7489  */
7490 static void trw_layer_waypoints_visibility_on ( menu_array_layer values )
7491 {
7492   VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
7493   gpointer vis_data[2] = { VIK_LAYER(vtl)->vt, GINT_TO_POINTER(TRUE) };
7494   g_hash_table_foreach ( vtl->waypoints_iters, (GHFunc) trw_layer_iter_visibility, vis_data );
7495   g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_waypoints_visibility, vis_data[1] );
7496   // Redraw
7497   vik_layer_emit_update ( VIK_LAYER(vtl) );
7498 }
7499
7500 /**
7501  *
7502  */
7503 static void trw_layer_waypoints_visibility_toggle ( menu_array_layer values )
7504 {
7505   VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
7506   g_hash_table_foreach ( vtl->waypoints_iters, (GHFunc) trw_layer_iter_visibility_toggle, VIK_LAYER(vtl)->vt );
7507   g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_waypoints_toggle_visibility, NULL );
7508   // Redraw
7509   vik_layer_emit_update ( VIK_LAYER(vtl) );
7510 }
7511
7512 /**
7513  *
7514  */
7515 static void trw_layer_tracks_visibility ( gpointer id, VikTrack *trk, gpointer on_off )
7516 {
7517   trk->visible = GPOINTER_TO_INT (on_off);
7518 }
7519
7520 /**
7521  *
7522  */
7523 static void trw_layer_tracks_toggle_visibility ( gpointer id, VikTrack *trk )
7524 {
7525   trk->visible = !trk->visible;
7526 }
7527
7528 /**
7529  *
7530  */
7531 static void trw_layer_tracks_visibility_off ( menu_array_layer values )
7532 {
7533   VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
7534   gpointer vis_data[2] = { VIK_LAYER(vtl)->vt, GINT_TO_POINTER(FALSE) };
7535   g_hash_table_foreach ( vtl->tracks_iters, (GHFunc) trw_layer_iter_visibility, vis_data );
7536   g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_tracks_visibility, vis_data[1] );
7537   // Redraw
7538   vik_layer_emit_update ( VIK_LAYER(vtl) );
7539 }
7540
7541 /**
7542  *
7543  */
7544 static void trw_layer_tracks_visibility_on ( menu_array_layer values )
7545 {
7546   VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
7547   gpointer vis_data[2] = { VIK_LAYER(vtl)->vt, GINT_TO_POINTER(TRUE) };
7548   g_hash_table_foreach ( vtl->tracks_iters, (GHFunc) trw_layer_iter_visibility, vis_data );
7549   g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_tracks_visibility, vis_data[1] );
7550   // Redraw
7551   vik_layer_emit_update ( VIK_LAYER(vtl) );
7552 }
7553
7554 /**
7555  *
7556  */
7557 static void trw_layer_tracks_visibility_toggle ( menu_array_layer values )
7558 {
7559   VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
7560   g_hash_table_foreach ( vtl->tracks_iters, (GHFunc) trw_layer_iter_visibility_toggle, VIK_LAYER(vtl)->vt );
7561   g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_tracks_toggle_visibility, NULL );
7562   // Redraw
7563   vik_layer_emit_update ( VIK_LAYER(vtl) );
7564 }
7565
7566 /**
7567  *
7568  */
7569 static void trw_layer_routes_visibility_off ( menu_array_layer values )
7570 {
7571   VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
7572   gpointer vis_data[2] = { VIK_LAYER(vtl)->vt, GINT_TO_POINTER(FALSE) };
7573   g_hash_table_foreach ( vtl->routes_iters, (GHFunc) trw_layer_iter_visibility, vis_data );
7574   g_hash_table_foreach ( vtl->routes, (GHFunc) trw_layer_tracks_visibility, vis_data[1] );
7575   // Redraw
7576   vik_layer_emit_update ( VIK_LAYER(vtl) );
7577 }
7578
7579 /**
7580  *
7581  */
7582 static void trw_layer_routes_visibility_on ( menu_array_layer values )
7583 {
7584   VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
7585   gpointer vis_data[2] = { VIK_LAYER(vtl)->vt, GINT_TO_POINTER(TRUE) };
7586   g_hash_table_foreach ( vtl->routes_iters, (GHFunc) trw_layer_iter_visibility, vis_data );
7587   g_hash_table_foreach ( vtl->routes, (GHFunc) trw_layer_tracks_visibility, vis_data[1] );
7588   // Redraw
7589   vik_layer_emit_update ( VIK_LAYER(vtl) );
7590 }
7591
7592 /**
7593  *
7594  */
7595 static void trw_layer_routes_visibility_toggle ( menu_array_layer values )
7596 {
7597   VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
7598   g_hash_table_foreach ( vtl->routes_iters, (GHFunc) trw_layer_iter_visibility_toggle, VIK_LAYER(vtl)->vt );
7599   g_hash_table_foreach ( vtl->routes, (GHFunc) trw_layer_tracks_toggle_visibility, NULL );
7600   // Redraw
7601   vik_layer_emit_update ( VIK_LAYER(vtl) );
7602 }
7603
7604 /**
7605  * vik_trw_layer_build_waypoint_list_t:
7606  *
7607  * Helper function to construct a list of #vik_trw_waypoint_list_t
7608  */
7609 GList *vik_trw_layer_build_waypoint_list_t ( VikTrwLayer *vtl, GList *waypoints )
7610 {
7611   GList *waypoints_and_layers = NULL;
7612   // build waypoints_and_layers list
7613   while ( waypoints ) {
7614     vik_trw_waypoint_list_t *vtdl = g_malloc (sizeof(vik_trw_waypoint_list_t));
7615     vtdl->wpt = VIK_WAYPOINT(waypoints->data);
7616     vtdl->vtl = vtl;
7617     waypoints_and_layers = g_list_prepend ( waypoints_and_layers, vtdl );
7618     waypoints = g_list_next ( waypoints );
7619   }
7620   return waypoints_and_layers;
7621 }
7622
7623 /**
7624  * trw_layer_create_waypoint_list:
7625  *
7626  * Create the latest list of waypoints with the associated layer(s)
7627  *  Although this will always be from a single layer here
7628  */
7629 static GList* trw_layer_create_waypoint_list ( VikLayer *vl, gpointer user_data )
7630 {
7631   VikTrwLayer *vtl = VIK_TRW_LAYER(vl);
7632   GList *waypoints = g_hash_table_get_values ( vik_trw_layer_get_waypoints(vtl) );
7633
7634   return vik_trw_layer_build_waypoint_list_t ( vtl, waypoints );
7635 }
7636
7637 /**
7638  * trw_layer_analyse_close:
7639  *
7640  * Stuff to do on dialog closure
7641  */
7642 static void trw_layer_analyse_close ( GtkWidget *dialog, gint resp, VikLayer* vl )
7643 {
7644   VikTrwLayer *vtl = VIK_TRW_LAYER(vl);
7645   gtk_widget_destroy ( dialog );
7646   vtl->tracks_analysis_dialog = NULL;
7647 }
7648
7649 /**
7650  * vik_trw_layer_build_track_list_t:
7651  *
7652  * Helper function to construct a list of #vik_trw_track_list_t
7653  */
7654 GList *vik_trw_layer_build_track_list_t ( VikTrwLayer *vtl, GList *tracks )
7655 {
7656   GList *tracks_and_layers = NULL;
7657   // build tracks_and_layers list
7658   while ( tracks ) {
7659     vik_trw_track_list_t *vtdl = g_malloc (sizeof(vik_trw_track_list_t));
7660     vtdl->trk = VIK_TRACK(tracks->data);
7661     vtdl->vtl = vtl;
7662     tracks_and_layers = g_list_prepend ( tracks_and_layers, vtdl );
7663     tracks = g_list_next ( tracks );
7664   }
7665   return tracks_and_layers;
7666 }
7667
7668 /**
7669  * trw_layer_create_track_list:
7670  *
7671  * Create the latest list of tracks with the associated layer(s)
7672  *  Although this will always be from a single layer here
7673  */
7674 static GList* trw_layer_create_track_list ( VikLayer *vl, gpointer user_data )
7675 {
7676   VikTrwLayer *vtl = VIK_TRW_LAYER(vl);
7677   GList *tracks = NULL;
7678   if ( GPOINTER_TO_INT(user_data) == VIK_TRW_LAYER_SUBLAYER_TRACKS )
7679     tracks = g_hash_table_get_values ( vik_trw_layer_get_tracks(vtl) );
7680   else
7681     tracks = g_hash_table_get_values ( vik_trw_layer_get_routes(vtl) );
7682
7683   return vik_trw_layer_build_track_list_t ( vtl, tracks );
7684 }
7685
7686 static void trw_layer_tracks_stats ( menu_array_layer values )
7687 {
7688   VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
7689   // There can only be one!
7690   if ( vtl->tracks_analysis_dialog )
7691     return;
7692
7693   vtl->tracks_analysis_dialog = vik_trw_layer_analyse_this ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
7694                                                              VIK_LAYER(vtl)->name,
7695                                                              VIK_LAYER(vtl),
7696                                                              GINT_TO_POINTER(VIK_TRW_LAYER_SUBLAYER_TRACKS),
7697                                                              trw_layer_create_track_list,
7698                                                              trw_layer_analyse_close );
7699 }
7700
7701 /**
7702  *
7703  */
7704 static void trw_layer_routes_stats ( menu_array_layer values )
7705 {
7706   VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
7707   // There can only be one!
7708   if ( vtl->tracks_analysis_dialog )
7709     return;
7710
7711   vtl->tracks_analysis_dialog = vik_trw_layer_analyse_this ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
7712                                                              VIK_LAYER(vtl)->name,
7713                                                              VIK_LAYER(vtl),
7714                                                              GINT_TO_POINTER(VIK_TRW_LAYER_SUBLAYER_ROUTES),
7715                                                              trw_layer_create_track_list,
7716                                                              trw_layer_analyse_close );
7717 }
7718
7719 static void trw_layer_goto_waypoint ( menu_array_sublayer values )
7720 {
7721   VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
7722   VikWaypoint *wp = g_hash_table_lookup ( vtl->waypoints, values[MA_SUBLAYER_ID] );
7723   if ( wp )
7724     goto_coord ( values[MA_VLP], vtl, values[MA_VVP], &(wp->coord) );
7725 }
7726
7727 static void trw_layer_waypoint_gc_webpage ( menu_array_sublayer values )
7728 {
7729   VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
7730   VikWaypoint *wp = g_hash_table_lookup ( vtl->waypoints, values[MA_SUBLAYER_ID] );
7731   if ( !wp )
7732     return;
7733   gchar *webpage = g_strdup_printf("http://www.geocaching.com/seek/cache_details.aspx?wp=%s", wp->name );
7734   open_url(VIK_GTK_WINDOW_FROM_LAYER(VIK_LAYER(vtl)), webpage);
7735   g_free ( webpage );
7736 }
7737
7738 static void trw_layer_waypoint_webpage ( menu_array_sublayer values )
7739 {
7740   VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
7741   VikWaypoint *wp = g_hash_table_lookup ( vtl->waypoints, values[MA_SUBLAYER_ID] );
7742   if ( !wp )
7743     return;
7744   if ( wp->url ) {
7745     open_url(VIK_GTK_WINDOW_FROM_LAYER(VIK_LAYER(vtl)), wp->url);
7746   } else if ( !strncmp(wp->comment, "http", 4) ) {
7747     open_url(VIK_GTK_WINDOW_FROM_LAYER(VIK_LAYER(vtl)), wp->comment);
7748   } else if ( !strncmp(wp->description, "http", 4) ) {
7749     open_url(VIK_GTK_WINDOW_FROM_LAYER(VIK_LAYER(vtl)), wp->description);
7750   }
7751 }
7752
7753 static const gchar* trw_layer_sublayer_rename_request ( VikTrwLayer *l, const gchar *newname, gpointer vlp, gint subtype, gpointer sublayer, GtkTreeIter *iter )
7754 {
7755   if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT )
7756   {
7757     VikWaypoint *wp = g_hash_table_lookup ( l->waypoints, sublayer );
7758
7759     // No actual change to the name supplied
7760     if ( wp->name )
7761       if (strcmp(newname, wp->name) == 0 )
7762        return NULL;
7763
7764     VikWaypoint *wpf = vik_trw_layer_get_waypoint ( l, newname );
7765
7766     if ( wpf ) {
7767       // An existing waypoint has been found with the requested name
7768       if ( ! a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(l),
7769            _("A waypoint with the name \"%s\" already exists. Really rename to the same name?"),
7770            newname ) )
7771         return NULL;
7772     }
7773
7774     // Update WP name and refresh the treeview
7775     vik_waypoint_set_name (wp, newname);
7776
7777     vik_treeview_item_set_name ( VIK_LAYER(l)->vt, iter, newname );
7778     vik_treeview_sort_children ( VIK_LAYER(l)->vt, &(l->waypoints_iter), l->wp_sort_order );
7779
7780     vik_layers_panel_emit_update ( VIK_LAYERS_PANEL(vlp) );
7781
7782     return newname;
7783   }
7784
7785   if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
7786   {
7787     VikTrack *trk = g_hash_table_lookup ( l->tracks, sublayer );
7788
7789     // No actual change to the name supplied
7790     if ( trk->name )
7791       if (strcmp(newname, trk->name) == 0)
7792         return NULL;
7793
7794     VikTrack *trkf = vik_trw_layer_get_track ( l, (gpointer) newname );
7795
7796     if ( trkf ) {
7797       // An existing track has been found with the requested name
7798       if ( ! a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(l),
7799           _("A track with the name \"%s\" already exists. Really rename to the same name?"),
7800           newname ) )
7801         return NULL;
7802     }
7803     // Update track name and refresh GUI parts
7804     vik_track_set_name (trk, newname);
7805
7806     // Update any subwindows that could be displaying this track which has changed name
7807     // Only one Track Edit Window
7808     if ( l->current_tp_track == trk && l->tpwin ) {
7809       vik_trw_layer_tpwin_set_track_name ( l->tpwin, newname );
7810     }
7811     // Property Dialog of the track
7812     vik_trw_layer_propwin_update ( trk );
7813
7814     vik_treeview_item_set_name ( VIK_LAYER(l)->vt, iter, newname );
7815     vik_treeview_sort_children ( VIK_LAYER(l)->vt, &(l->tracks_iter), l->track_sort_order );
7816
7817     vik_layers_panel_emit_update ( VIK_LAYERS_PANEL(vlp) );
7818
7819     return newname;
7820   }
7821
7822   if ( subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE )
7823   {
7824     VikTrack *trk = g_hash_table_lookup ( l->routes, sublayer );
7825
7826     // No actual change to the name supplied
7827     if ( trk->name )
7828       if (strcmp(newname, trk->name) == 0)
7829         return NULL;
7830
7831     VikTrack *trkf = vik_trw_layer_get_route ( l, (gpointer) newname );
7832
7833     if ( trkf ) {
7834       // An existing track has been found with the requested name
7835       if ( ! a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(l),
7836           _("A route with the name \"%s\" already exists. Really rename to the same name?"),
7837           newname ) )
7838         return NULL;
7839     }
7840     // Update track name and refresh GUI parts
7841     vik_track_set_name (trk, newname);
7842
7843     // Update any subwindows that could be displaying this track which has changed name
7844     // Only one Track Edit Window
7845     if ( l->current_tp_track == trk && l->tpwin ) {
7846       vik_trw_layer_tpwin_set_track_name ( l->tpwin, newname );
7847     }
7848     // Property Dialog of the track
7849     vik_trw_layer_propwin_update ( trk );
7850
7851     vik_treeview_item_set_name ( VIK_LAYER(l)->vt, iter, newname );
7852     vik_treeview_sort_children ( VIK_LAYER(l)->vt, &(l->tracks_iter), l->track_sort_order );
7853
7854     vik_layers_panel_emit_update ( VIK_LAYERS_PANEL(vlp) );
7855
7856     return newname;
7857   }
7858   return NULL;
7859 }
7860
7861 static gboolean is_valid_geocache_name ( gchar *str )
7862 {
7863   gint len = strlen ( str );
7864   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]));
7865 }
7866
7867 #ifndef WINDOWS
7868 static void trw_layer_track_use_with_filter ( menu_array_sublayer values )
7869 {
7870   VikTrack *trk = g_hash_table_lookup ( VIK_TRW_LAYER(values[MA_VTL])->tracks, values[MA_SUBLAYER_ID] );
7871   a_acquire_set_filter_track ( trk );
7872 }
7873 #endif
7874
7875 #ifdef VIK_CONFIG_GOOGLE
7876 static gboolean is_valid_google_route ( VikTrwLayer *vtl, const gpointer track_id )
7877 {
7878   VikTrack *tr = g_hash_table_lookup ( vtl->routes, track_id );
7879   return ( tr && tr->comment && strlen(tr->comment) > 7 && !strncmp(tr->comment, "from:", 5) );
7880 }
7881
7882 static void trw_layer_google_route_webpage ( menu_array_sublayer values )
7883 {
7884   VikTrack *tr = g_hash_table_lookup ( VIK_TRW_LAYER(values[MA_VTL])->routes, values[MA_SUBLAYER_ID] );
7885   if ( tr ) {
7886     gchar *escaped = g_uri_escape_string ( tr->comment, NULL, TRUE );
7887     gchar *webpage = g_strdup_printf("http://maps.google.com/maps?f=q&hl=en&q=%s", escaped );
7888     open_url(VIK_GTK_WINDOW_FROM_LAYER(VIK_LAYER(values[MA_VTL])), webpage);
7889     g_free ( escaped );
7890     g_free ( webpage );
7891   }
7892 }
7893 #endif
7894
7895 /* vlp can be NULL if necessary - i.e. right-click from a tool */
7896 /* viewpoint is now available instead */
7897 static gboolean trw_layer_sublayer_add_menu_items ( VikTrwLayer *l, GtkMenu *menu, gpointer vlp, gint subtype, gpointer sublayer, GtkTreeIter *iter, VikViewport *vvp )
7898 {
7899   static menu_array_sublayer pass_along;
7900   GtkWidget *item;
7901   gboolean rv = FALSE;
7902
7903   pass_along[MA_VTL]         = l;
7904   pass_along[MA_VLP]         = vlp;
7905   pass_along[MA_SUBTYPE]     = GINT_TO_POINTER (subtype);
7906   pass_along[MA_SUBLAYER_ID] = sublayer;
7907   pass_along[MA_CONFIRM]     = GINT_TO_POINTER (1); // Confirm delete request
7908   pass_along[MA_VVP]         = vvp;
7909   pass_along[MA_TV_ITER]     = iter;
7910   pass_along[MA_MISC]        = NULL; // For misc purposes - maybe track or waypoint
7911
7912   if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT || subtype == VIK_TRW_LAYER_SUBLAYER_TRACK || subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE )
7913   {
7914     rv = TRUE;
7915
7916     item = gtk_image_menu_item_new_from_stock ( GTK_STOCK_PROPERTIES, NULL );
7917     g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_properties_item), pass_along );
7918     gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7919     gtk_widget_show ( item );
7920
7921     if (subtype == VIK_TRW_LAYER_SUBLAYER_TRACK) {
7922       VikTrack *tr = g_hash_table_lookup ( l->tracks, sublayer );
7923       if (tr && tr->property_dialog)
7924         gtk_widget_set_sensitive(GTK_WIDGET(item), FALSE );
7925     }
7926     if (subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE) {
7927       VikTrack *tr = g_hash_table_lookup ( l->routes, sublayer );
7928       if (tr && tr->property_dialog)
7929         gtk_widget_set_sensitive(GTK_WIDGET(item), FALSE );
7930     }
7931
7932     item = gtk_image_menu_item_new_from_stock ( GTK_STOCK_CUT, NULL );
7933     g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_cut_item_cb), pass_along );
7934     gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7935     gtk_widget_show ( item );
7936
7937     item = gtk_image_menu_item_new_from_stock ( GTK_STOCK_COPY, NULL );
7938     g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_copy_item_cb), pass_along );
7939     gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7940     gtk_widget_show ( item );
7941
7942     item = gtk_image_menu_item_new_from_stock ( GTK_STOCK_DELETE, NULL );
7943     g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_item), pass_along );
7944     gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7945     gtk_widget_show ( item );
7946
7947     if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT )
7948     {
7949       // Always create separator as now there is always at least the transform menu option
7950       item = gtk_menu_item_new ();
7951       gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7952       gtk_widget_show ( item );
7953
7954       /* could be a right-click using the tool */
7955       if ( vlp != NULL ) {
7956         item = gtk_image_menu_item_new_with_mnemonic ( _("_Goto") );
7957         gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_JUMP_TO, GTK_ICON_SIZE_MENU) );
7958         g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_waypoint), pass_along );
7959         gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7960         gtk_widget_show ( item );
7961       }
7962
7963       VikWaypoint *wp = g_hash_table_lookup ( VIK_TRW_LAYER(l)->waypoints, sublayer );
7964
7965       if ( wp && wp->name ) {
7966         if ( is_valid_geocache_name ( wp->name ) ) {
7967           item = gtk_menu_item_new_with_mnemonic ( _("_Visit Geocache Webpage") );
7968           g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_waypoint_gc_webpage), pass_along );
7969           gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7970           gtk_widget_show ( item );
7971         }
7972 #ifdef VIK_CONFIG_GEOTAG
7973         item = gtk_menu_item_new_with_mnemonic ( _("Geotag _Images...") );
7974         g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_geotagging_waypoint), pass_along );
7975         gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7976         gtk_widget_set_tooltip_text (item, _("Geotag multiple images against this waypoint"));
7977         gtk_widget_show ( item );
7978 #endif
7979       }
7980
7981       if ( wp && wp->image )
7982       {
7983         // Set up image paramater
7984         pass_along[MA_MISC] = wp->image;
7985
7986         item = gtk_image_menu_item_new_with_mnemonic ( _("_Show Picture...") );
7987         gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock ("vik-icon-Show Picture", GTK_ICON_SIZE_MENU) ); // Own icon - see stock_icons in vikwindow.c
7988         g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_show_picture), pass_along );
7989         gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7990         gtk_widget_show ( item );
7991
7992 #ifdef VIK_CONFIG_GEOTAG
7993         GtkWidget *geotag_submenu = gtk_menu_new ();
7994         item = gtk_image_menu_item_new_with_mnemonic ( _("Update Geotag on _Image") );
7995         gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REFRESH, GTK_ICON_SIZE_MENU) );
7996         gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7997         gtk_widget_show ( item );
7998         gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), geotag_submenu );
7999   
8000         item = gtk_menu_item_new_with_mnemonic ( _("_Update") );
8001         g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_geotagging_waypoint_mtime_update), pass_along );
8002         gtk_menu_shell_append (GTK_MENU_SHELL (geotag_submenu), item);
8003         gtk_widget_show ( item );
8004
8005         item = gtk_menu_item_new_with_mnemonic ( _("Update and _Keep File Timestamp") );
8006         g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_geotagging_waypoint_mtime_keep), pass_along );
8007         gtk_menu_shell_append (GTK_MENU_SHELL (geotag_submenu), item);
8008         gtk_widget_show ( item );
8009 #endif
8010       }
8011
8012       if ( wp )
8013       {
8014         if ( wp->url ||
8015              ( wp->comment && !strncmp(wp->comment, "http", 4) ) ||
8016              ( wp->description && !strncmp(wp->description, "http", 4) )) {
8017           item = gtk_image_menu_item_new_with_mnemonic ( _("Visit _Webpage") );
8018           gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NETWORK, GTK_ICON_SIZE_MENU) );
8019           g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_waypoint_webpage), pass_along );
8020           gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
8021           gtk_widget_show ( item );
8022         }
8023       }
8024     }
8025   }
8026
8027   if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINTS || subtype == VIK_TRW_LAYER_SUBLAYER_TRACKS || subtype == VIK_TRW_LAYER_SUBLAYER_ROUTES ) {
8028     item = gtk_image_menu_item_new_from_stock ( GTK_STOCK_PASTE, NULL );
8029     g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_paste_item_cb), pass_along );
8030     gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
8031     gtk_widget_show ( item );
8032     // TODO: only enable if suitable item is in clipboard - want to determine *which* sublayer type
8033     if ( a_clipboard_type ( ) == VIK_CLIPBOARD_DATA_SUBLAYER )
8034       gtk_widget_set_sensitive ( item, TRUE );
8035     else
8036       gtk_widget_set_sensitive ( item, FALSE );
8037
8038     // Add separator
8039     item = gtk_menu_item_new ();
8040     gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
8041     gtk_widget_show ( item );
8042   }
8043
8044   if ( vlp && (subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINTS || subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT) )
8045   {
8046     rv = TRUE;
8047     item = gtk_image_menu_item_new_with_mnemonic ( _("_New Waypoint...") );
8048     gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NEW, GTK_ICON_SIZE_MENU) );
8049     g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_wp), pass_along );
8050     gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
8051     gtk_widget_show ( item );
8052   }
8053
8054   if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINTS )
8055   {
8056     item = gtk_image_menu_item_new_with_mnemonic ( _("_View All Waypoints") );
8057     gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ZOOM_FIT, GTK_ICON_SIZE_MENU) );
8058     g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_waypoints_view), pass_along );
8059     gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
8060     gtk_widget_show ( item );
8061
8062     item = gtk_image_menu_item_new_with_mnemonic ( _("Goto _Waypoint...") );
8063     gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_JUMP_TO, GTK_ICON_SIZE_MENU) );
8064     g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_wp), pass_along );
8065     gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
8066     gtk_widget_show ( item );
8067
8068     item = gtk_image_menu_item_new_with_mnemonic ( _("Delete _All Waypoints") );
8069     gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU) );
8070     g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_all_waypoints), pass_along );
8071     gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
8072     gtk_widget_show ( item );
8073
8074     item = gtk_image_menu_item_new_with_mnemonic ( _("_Delete Waypoints From Selection...") );
8075     gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
8076     g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_waypoints_from_selection), pass_along );
8077     gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
8078     gtk_widget_show ( item );
8079
8080     GtkWidget *vis_submenu = gtk_menu_new ();
8081     item = gtk_menu_item_new_with_mnemonic ( _("_Visibility") );
8082     gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
8083     gtk_widget_show ( item );
8084     gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), vis_submenu );
8085
8086     item = gtk_image_menu_item_new_with_mnemonic ( _("_Show All Waypoints") );
8087     gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_APPLY, GTK_ICON_SIZE_MENU) );
8088     g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_waypoints_visibility_on), pass_along );
8089     gtk_menu_shell_append ( GTK_MENU_SHELL(vis_submenu), item );
8090     gtk_widget_show ( item );
8091
8092     item = gtk_image_menu_item_new_with_mnemonic ( _("_Hide All Waypoints") );
8093     gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_CLEAR, GTK_ICON_SIZE_MENU) );
8094     g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_waypoints_visibility_off), pass_along );
8095     gtk_menu_shell_append ( GTK_MENU_SHELL(vis_submenu), item );
8096     gtk_widget_show ( item );
8097
8098     item = gtk_image_menu_item_new_with_mnemonic ( _("_Toggle") );
8099     gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REFRESH, GTK_ICON_SIZE_MENU) );
8100     g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_waypoints_visibility_toggle), pass_along );
8101     gtk_menu_shell_append ( GTK_MENU_SHELL(vis_submenu), item );
8102     gtk_widget_show ( item );
8103
8104     item = gtk_image_menu_item_new_with_mnemonic ( _("_List Waypoints...") );
8105     gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
8106     g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_waypoint_list_dialog), pass_along );
8107     gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
8108   }
8109
8110   if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACKS )
8111   {
8112     rv = TRUE;
8113
8114     if ( l->current_track && !l->current_track->is_route ) {
8115       item = gtk_menu_item_new_with_mnemonic ( _("_Finish Track") );
8116       g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_finish_track), pass_along );
8117       gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
8118       gtk_widget_show ( item );
8119       // Add separator
8120       item = gtk_menu_item_new ();
8121       gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
8122       gtk_widget_show ( item );
8123     }
8124
8125     item = gtk_image_menu_item_new_with_mnemonic ( _("_View All Tracks") );
8126     gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ZOOM_FIT, GTK_ICON_SIZE_MENU) );
8127     g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_tracks_view), pass_along );
8128     gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
8129     gtk_widget_show ( item );
8130
8131     item = gtk_image_menu_item_new_with_mnemonic ( _("_New Track") );
8132     gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NEW, GTK_ICON_SIZE_MENU) );
8133     g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_track), pass_along );
8134     gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
8135     gtk_widget_show ( item );
8136     // Make it available only when a new track *not* already in progress
8137     gtk_widget_set_sensitive ( item, ! (gboolean)GPOINTER_TO_INT(l->current_track) );
8138
8139     item = gtk_image_menu_item_new_with_mnemonic ( _("Delete _All Tracks") );
8140     gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU) );
8141     g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_all_tracks), pass_along );
8142     gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
8143     gtk_widget_show ( item );
8144
8145     item = gtk_image_menu_item_new_with_mnemonic ( _("_Delete Tracks From Selection...") );
8146     gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
8147     g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_tracks_from_selection), pass_along );
8148     gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
8149     gtk_widget_show ( item );
8150
8151     GtkWidget *vis_submenu = gtk_menu_new ();
8152     item = gtk_menu_item_new_with_mnemonic ( _("_Visibility") );
8153     gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
8154     gtk_widget_show ( item );
8155     gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), vis_submenu );
8156
8157     item = gtk_image_menu_item_new_with_mnemonic ( _("_Show All Tracks") );
8158     gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_APPLY, GTK_ICON_SIZE_MENU) );
8159     g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_tracks_visibility_on), pass_along );
8160     gtk_menu_shell_append ( GTK_MENU_SHELL(vis_submenu), item );
8161     gtk_widget_show ( item );
8162
8163     item = gtk_image_menu_item_new_with_mnemonic ( _("_Hide All Tracks") );
8164     gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_CLEAR, GTK_ICON_SIZE_MENU) );
8165     g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_tracks_visibility_off), pass_along );
8166     gtk_menu_shell_append ( GTK_MENU_SHELL(vis_submenu), item );
8167     gtk_widget_show ( item );
8168
8169     item = gtk_image_menu_item_new_with_mnemonic ( _("_Toggle") );
8170     gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REFRESH, GTK_ICON_SIZE_MENU) );
8171     g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_tracks_visibility_toggle), pass_along );
8172     gtk_menu_shell_append ( GTK_MENU_SHELL(vis_submenu), item );
8173
8174     item = gtk_image_menu_item_new_with_mnemonic ( _("_List Tracks...") );
8175     gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
8176     g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_track_list_dialog_single), pass_along );
8177     gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
8178     gtk_widget_show ( item );
8179
8180     item = gtk_menu_item_new_with_mnemonic ( _("_Statistics") );
8181     g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_tracks_stats), pass_along );
8182     gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
8183     gtk_widget_show ( item );
8184   }
8185
8186   if ( subtype == VIK_TRW_LAYER_SUBLAYER_ROUTES )
8187   {
8188     rv = TRUE;
8189
8190     if ( l->current_track && l->current_track->is_route ) {
8191       item = gtk_menu_item_new_with_mnemonic ( _("_Finish Route") );
8192       // Reuse finish track method
8193       g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_finish_track), pass_along );
8194       gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
8195       gtk_widget_show ( item );
8196       // Add separator
8197       item = gtk_menu_item_new ();
8198       gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
8199       gtk_widget_show ( item );
8200     }
8201
8202     item = gtk_image_menu_item_new_with_mnemonic ( _("_View All Routes") );
8203     gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ZOOM_FIT, GTK_ICON_SIZE_MENU) );
8204     g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_routes_view), pass_along );
8205     gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
8206     gtk_widget_show ( item );
8207
8208     item = gtk_image_menu_item_new_with_mnemonic ( _("_New Route") );
8209     gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NEW, GTK_ICON_SIZE_MENU) );
8210     g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_route), pass_along );
8211     gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
8212     gtk_widget_show ( item );
8213     // Make it available only when a new track *not* already in progress
8214     gtk_widget_set_sensitive ( item, ! (gboolean)GPOINTER_TO_INT(l->current_track) );
8215
8216     item = gtk_image_menu_item_new_with_mnemonic ( _("Delete _All Routes") );
8217     gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU) );
8218     g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_all_routes), pass_along );
8219     gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
8220     gtk_widget_show ( item );
8221
8222     item = gtk_image_menu_item_new_with_mnemonic ( _("_Delete Routes From Selection...") );
8223     gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
8224     g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_routes_from_selection), pass_along );
8225     gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
8226     gtk_widget_show ( item );
8227
8228     GtkWidget *vis_submenu = gtk_menu_new ();
8229     item = gtk_menu_item_new_with_mnemonic ( _("_Visibility") );
8230     gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
8231     gtk_widget_show ( item );
8232     gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), vis_submenu );
8233
8234     item = gtk_image_menu_item_new_with_mnemonic ( _("_Show All Routes") );
8235     gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_APPLY, GTK_ICON_SIZE_MENU) );
8236     g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_routes_visibility_on), pass_along );
8237     gtk_menu_shell_append ( GTK_MENU_SHELL(vis_submenu), item );
8238     gtk_widget_show ( item );
8239
8240     item = gtk_image_menu_item_new_with_mnemonic ( _("_Hide All Routes") );
8241     gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_CLEAR, GTK_ICON_SIZE_MENU) );
8242     g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_routes_visibility_off), pass_along );
8243     gtk_menu_shell_append ( GTK_MENU_SHELL(vis_submenu), item );
8244     gtk_widget_show ( item );
8245
8246     item = gtk_image_menu_item_new_with_mnemonic ( _("_Toggle") );
8247     gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REFRESH, GTK_ICON_SIZE_MENU) );
8248     g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_routes_visibility_toggle), pass_along );
8249     gtk_menu_shell_append ( GTK_MENU_SHELL(vis_submenu), item );
8250
8251     item = gtk_image_menu_item_new_with_mnemonic ( _("_List Routes...") );
8252     gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
8253     g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_track_list_dialog_single), pass_along );
8254     gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
8255
8256     gtk_widget_show ( item );
8257
8258     item = gtk_menu_item_new_with_mnemonic ( _("_Statistics") );
8259     g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_routes_stats), pass_along );
8260     gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
8261     gtk_widget_show ( item );
8262   }
8263
8264
8265   if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINTS || subtype == VIK_TRW_LAYER_SUBLAYER_TRACKS || subtype == VIK_TRW_LAYER_SUBLAYER_ROUTES ) {
8266     GtkWidget *submenu_sort = gtk_menu_new ();
8267     item = gtk_image_menu_item_new_with_mnemonic ( _("_Sort") );
8268     gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REFRESH, GTK_ICON_SIZE_MENU) );
8269     gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
8270     gtk_widget_show ( item );
8271     gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), submenu_sort );
8272
8273     item = gtk_image_menu_item_new_with_mnemonic ( _("Name _Ascending") );
8274     gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_SORT_ASCENDING, GTK_ICON_SIZE_MENU) );
8275     g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_sort_order_a2z), pass_along );
8276     gtk_menu_shell_append ( GTK_MENU_SHELL(submenu_sort), item );
8277     gtk_widget_show ( item );
8278
8279     item = gtk_image_menu_item_new_with_mnemonic ( _("Name _Descending") );
8280     gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_SORT_DESCENDING, GTK_ICON_SIZE_MENU) );
8281     g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_sort_order_z2a), pass_along );
8282     gtk_menu_shell_append ( GTK_MENU_SHELL(submenu_sort), item );
8283     gtk_widget_show ( item );
8284
8285     item = gtk_image_menu_item_new_with_mnemonic ( _("Date Ascending") );
8286     gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_SORT_ASCENDING, GTK_ICON_SIZE_MENU) );
8287     g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_sort_order_timestamp_ascend), pass_along );
8288     gtk_menu_shell_append ( GTK_MENU_SHELL(submenu_sort), item );
8289     gtk_widget_show ( item );
8290
8291     item = gtk_image_menu_item_new_with_mnemonic ( _("Date Descending") );
8292     gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_SORT_DESCENDING, GTK_ICON_SIZE_MENU) );
8293     g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_sort_order_timestamp_descend), pass_along );
8294     gtk_menu_shell_append ( GTK_MENU_SHELL(submenu_sort), item );
8295     gtk_widget_show ( item );
8296   }
8297
8298   GtkWidget *upload_submenu = gtk_menu_new ();
8299
8300   if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK || subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE )
8301   {
8302     item = gtk_menu_item_new ();
8303     gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
8304     gtk_widget_show ( item );
8305
8306     if ( l->current_track && subtype == VIK_TRW_LAYER_SUBLAYER_TRACK && !l->current_track->is_route )
8307       item = gtk_menu_item_new_with_mnemonic ( _("_Finish Track") );
8308     if ( l->current_track && subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE && l->current_track->is_route )
8309       item = gtk_menu_item_new_with_mnemonic ( _("_Finish Route") );
8310     if ( l->current_track ) {
8311       g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_finish_track), pass_along );
8312       gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
8313       gtk_widget_show ( item );
8314
8315       // Add separator
8316       item = gtk_menu_item_new ();
8317       gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
8318       gtk_widget_show ( item );
8319     }
8320
8321     if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
8322       item = gtk_image_menu_item_new_with_mnemonic ( _("_View Track") );
8323     else
8324       item = gtk_image_menu_item_new_with_mnemonic ( _("_View Route") );
8325     gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ZOOM_FIT, GTK_ICON_SIZE_MENU) );
8326     g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_track_view), pass_along );
8327     gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
8328     gtk_widget_show ( item );
8329
8330     item = gtk_menu_item_new_with_mnemonic ( _("_Statistics") );
8331     g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_track_statistics), pass_along );
8332     gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
8333     gtk_widget_show ( item );
8334
8335     GtkWidget *goto_submenu;
8336     goto_submenu = gtk_menu_new ();
8337     item = gtk_image_menu_item_new_with_mnemonic ( _("_Goto") );
8338     gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_JUMP_TO, GTK_ICON_SIZE_MENU) );
8339     gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
8340     gtk_widget_show ( item );
8341     gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), goto_submenu );
8342
8343     item = gtk_image_menu_item_new_with_mnemonic ( _("_Startpoint") );
8344     gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GOTO_FIRST, GTK_ICON_SIZE_MENU) );
8345     g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_startpoint), pass_along );
8346     gtk_menu_shell_append ( GTK_MENU_SHELL(goto_submenu), item );
8347     gtk_widget_show ( item );
8348
8349     item = gtk_image_menu_item_new_with_mnemonic ( _("\"_Center\"") );
8350     gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_JUMP_TO, GTK_ICON_SIZE_MENU) );
8351     g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_center), pass_along );
8352     gtk_menu_shell_append ( GTK_MENU_SHELL(goto_submenu), item );
8353     gtk_widget_show ( item );
8354
8355     item = gtk_image_menu_item_new_with_mnemonic ( _("_Endpoint") );
8356     gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GOTO_LAST, GTK_ICON_SIZE_MENU) );
8357     g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_endpoint), pass_along );
8358     gtk_menu_shell_append ( GTK_MENU_SHELL(goto_submenu), item );
8359     gtk_widget_show ( item );
8360
8361     item = gtk_image_menu_item_new_with_mnemonic ( _("_Highest Altitude") );
8362     gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GOTO_TOP, GTK_ICON_SIZE_MENU) );
8363     g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_max_alt), pass_along );
8364     gtk_menu_shell_append ( GTK_MENU_SHELL(goto_submenu), item );
8365     gtk_widget_show ( item );
8366
8367     item = gtk_image_menu_item_new_with_mnemonic ( _("_Lowest Altitude") );
8368     gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GOTO_BOTTOM, GTK_ICON_SIZE_MENU) );
8369     g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_min_alt), pass_along );
8370     gtk_menu_shell_append ( GTK_MENU_SHELL(goto_submenu), item );
8371     gtk_widget_show ( item );
8372
8373     // Routes don't have speeds
8374     if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK ) {
8375       item = gtk_image_menu_item_new_with_mnemonic ( _("_Maximum Speed") );
8376       gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_MEDIA_FORWARD, GTK_ICON_SIZE_MENU) );
8377       g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_max_speed), pass_along );
8378       gtk_menu_shell_append ( GTK_MENU_SHELL(goto_submenu), item );
8379       gtk_widget_show ( item );
8380     }
8381
8382     GtkWidget *combine_submenu;
8383     combine_submenu = gtk_menu_new ();
8384     item = gtk_image_menu_item_new_with_mnemonic ( _("Co_mbine") );
8385     gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_CONNECT, GTK_ICON_SIZE_MENU) );
8386     gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
8387     gtk_widget_show ( item );
8388     gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), combine_submenu );
8389
8390     // Routes don't have times or segments...
8391     if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK ) {
8392       item = gtk_menu_item_new_with_mnemonic ( _("_Merge By Time...") );
8393       g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_merge_by_timestamp), pass_along );
8394       gtk_menu_shell_append ( GTK_MENU_SHELL(combine_submenu), item );
8395       gtk_widget_show ( item );
8396
8397       item = gtk_menu_item_new_with_mnemonic ( _("Merge _Segments") );
8398       g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_merge_by_segment), pass_along );
8399       gtk_menu_shell_append ( GTK_MENU_SHELL(combine_submenu), item );
8400       gtk_widget_show ( item );
8401     }
8402
8403     item = gtk_menu_item_new_with_mnemonic ( _("Merge _With Other Tracks...") );
8404     g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_merge_with_other), pass_along );
8405     gtk_menu_shell_append ( GTK_MENU_SHELL(combine_submenu), item );
8406     gtk_widget_show ( item );
8407
8408     if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
8409       item = gtk_menu_item_new_with_mnemonic ( _("_Append Track...") );
8410     else
8411       item = gtk_menu_item_new_with_mnemonic ( _("_Append Route...") );
8412     g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_append_track), pass_along );
8413     gtk_menu_shell_append ( GTK_MENU_SHELL(combine_submenu), item );
8414     gtk_widget_show ( item );
8415
8416     if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
8417       item = gtk_menu_item_new_with_mnemonic ( _("Append _Route...") );
8418     else
8419       item = gtk_menu_item_new_with_mnemonic ( _("Append _Track...") );
8420     g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_append_other), pass_along );
8421     gtk_menu_shell_append ( GTK_MENU_SHELL(combine_submenu), item );
8422     gtk_widget_show ( item );
8423
8424     GtkWidget *split_submenu;
8425     split_submenu = gtk_menu_new ();
8426     item = gtk_image_menu_item_new_with_mnemonic ( _("_Split") );
8427     gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_DISCONNECT, GTK_ICON_SIZE_MENU) );
8428     gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
8429     gtk_widget_show ( item );
8430     gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), split_submenu );
8431
8432     // Routes don't have times or segments...
8433     if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK ) {
8434       item = gtk_menu_item_new_with_mnemonic ( _("_Split By Time...") );
8435       g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_split_by_timestamp), pass_along );
8436       gtk_menu_shell_append ( GTK_MENU_SHELL(split_submenu), item );
8437       gtk_widget_show ( item );
8438
8439       // ATM always enable this entry - don't want to have to analyse the track before displaying the menu - to keep the menu speedy
8440       item = gtk_menu_item_new_with_mnemonic ( _("Split Se_gments") );
8441       g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_split_segments), pass_along );
8442       gtk_menu_shell_append ( GTK_MENU_SHELL(split_submenu), item );
8443       gtk_widget_show ( item );
8444     }
8445
8446     item = gtk_menu_item_new_with_mnemonic ( _("Split By _Number of Points...") );
8447     g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_split_by_n_points), pass_along );
8448     gtk_menu_shell_append ( GTK_MENU_SHELL(split_submenu), item );
8449     gtk_widget_show ( item );
8450
8451     item = gtk_menu_item_new_with_mnemonic ( _("Split at _Trackpoint") );
8452     g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_split_at_trackpoint), pass_along );
8453     gtk_menu_shell_append ( GTK_MENU_SHELL(split_submenu), item );
8454     gtk_widget_show ( item );
8455     // Make it available only when a trackpoint is selected.
8456     gtk_widget_set_sensitive ( item, (gboolean)GPOINTER_TO_INT(l->current_tpl) );
8457
8458     GtkWidget *insert_submenu = gtk_menu_new ();
8459     item = gtk_image_menu_item_new_with_mnemonic ( _("_Insert Points") );
8460     gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ADD, GTK_ICON_SIZE_MENU) );
8461     gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
8462     gtk_widget_show ( item );
8463     gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), insert_submenu );
8464
8465     item = gtk_menu_item_new_with_mnemonic ( _("Insert Point _Before Selected Point") );
8466     g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_insert_point_before), pass_along );
8467     gtk_menu_shell_append ( GTK_MENU_SHELL(insert_submenu), item );
8468     gtk_widget_show ( item );
8469     // Make it available only when a point is selected
8470     gtk_widget_set_sensitive ( item, (gboolean)GPOINTER_TO_INT(l->current_tpl) );
8471
8472     item = gtk_menu_item_new_with_mnemonic ( _("Insert Point _After Selected Point") );
8473     g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_insert_point_after), pass_along );
8474     gtk_menu_shell_append ( GTK_MENU_SHELL(insert_submenu), item );
8475     gtk_widget_show ( item );
8476     // Make it available only when a point is selected
8477     gtk_widget_set_sensitive ( item, (gboolean)GPOINTER_TO_INT(l->current_tpl) );
8478
8479     GtkWidget *delete_submenu;
8480     delete_submenu = gtk_menu_new ();
8481     item = gtk_image_menu_item_new_with_mnemonic ( _("Delete Poi_nts") );
8482     gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_DELETE, GTK_ICON_SIZE_MENU) );
8483     gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
8484     gtk_widget_show ( item );
8485     gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), delete_submenu );
8486
8487     item = gtk_image_menu_item_new_with_mnemonic ( _("Delete _Selected Point") );
8488     gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_DELETE, GTK_ICON_SIZE_MENU) );
8489     g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_point_selected), pass_along );
8490     gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
8491     gtk_widget_show ( item );
8492     // Make it available only when a point is selected
8493     gtk_widget_set_sensitive ( item, (gboolean)GPOINTER_TO_INT(l->current_tpl) );
8494
8495     item = gtk_menu_item_new_with_mnemonic ( _("Delete Points With The Same _Position") );
8496     g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_points_same_position), pass_along );
8497     gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
8498     gtk_widget_show ( item );
8499
8500     item = gtk_menu_item_new_with_mnemonic ( _("Delete Points With The Same _Time") );
8501     g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_points_same_time), pass_along );
8502     gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
8503     gtk_widget_show ( item );
8504
8505     GtkWidget *transform_submenu;
8506     transform_submenu = gtk_menu_new ();
8507     item = gtk_image_menu_item_new_with_mnemonic ( _("_Transform") );
8508     gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_CONVERT, GTK_ICON_SIZE_MENU) );
8509     gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
8510     gtk_widget_show ( item );
8511     gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), transform_submenu );
8512
8513     GtkWidget *dem_submenu;
8514     dem_submenu = gtk_menu_new ();
8515     item = gtk_image_menu_item_new_with_mnemonic ( _("_Apply DEM Data") );
8516     gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock ("vik-icon-DEM Download", GTK_ICON_SIZE_MENU) ); // Own icon - see stock_icons in vikwindow.c
8517     gtk_menu_shell_append ( GTK_MENU_SHELL(transform_submenu), item );
8518     gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), dem_submenu );
8519
8520     item = gtk_image_menu_item_new_with_mnemonic ( _("_Overwrite") );
8521     g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_apply_dem_data_all), pass_along );
8522     gtk_menu_shell_append ( GTK_MENU_SHELL(dem_submenu), item );
8523     gtk_widget_set_tooltip_text (item, _("Overwrite any existing elevation values with DEM values"));
8524     gtk_widget_show ( item );
8525
8526     item = gtk_image_menu_item_new_with_mnemonic ( _("_Keep Existing") );
8527     g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_apply_dem_data_only_missing), pass_along );
8528     gtk_menu_shell_append ( GTK_MENU_SHELL(dem_submenu), item );
8529     gtk_widget_set_tooltip_text (item, _("Keep existing elevation values, only attempt for missing values"));
8530     gtk_widget_show ( item );
8531
8532     GtkWidget *smooth_submenu;
8533     smooth_submenu = gtk_menu_new ();
8534     item = gtk_menu_item_new_with_mnemonic ( _("_Smooth Missing Elevation Data") );
8535     gtk_menu_shell_append ( GTK_MENU_SHELL(transform_submenu), item );
8536     gtk_widget_show ( item );
8537     gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), smooth_submenu );
8538
8539     item = gtk_image_menu_item_new_with_mnemonic ( _("_Interpolated") );
8540     g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_missing_elevation_data_interp), pass_along );
8541     gtk_menu_shell_append ( GTK_MENU_SHELL(smooth_submenu), item );
8542     gtk_widget_set_tooltip_text (item, _("Interpolate between known elevation values to derive values for the missing elevations"));
8543     gtk_widget_show ( item );
8544
8545     item = gtk_image_menu_item_new_with_mnemonic ( _("_Flat") );
8546     g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_missing_elevation_data_flat), pass_along );
8547     gtk_menu_shell_append ( GTK_MENU_SHELL(smooth_submenu), item );
8548     gtk_widget_set_tooltip_text (item, _("Set unknown elevation values to the last known value"));
8549     gtk_widget_show ( item );
8550
8551     if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
8552       item = gtk_image_menu_item_new_with_mnemonic ( _("C_onvert to a Route") );
8553     else
8554       item = gtk_image_menu_item_new_with_mnemonic ( _("C_onvert to a Track") );
8555     gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_CONVERT, GTK_ICON_SIZE_MENU) );
8556     g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_convert_track_route), pass_along );
8557     gtk_menu_shell_append ( GTK_MENU_SHELL(transform_submenu), item );
8558     gtk_widget_show ( item );
8559
8560     // Routes don't have timestamps - so these are only available for tracks
8561     if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK ) {
8562       item = gtk_image_menu_item_new_with_mnemonic ( _("_Anonymize Times") );
8563       g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_anonymize_times), pass_along );
8564       gtk_menu_shell_append ( GTK_MENU_SHELL(transform_submenu), item );
8565       gtk_widget_set_tooltip_text (item, _("Shift timestamps to a relative offset from 1901-01-01"));
8566       gtk_widget_show ( item );
8567
8568       item = gtk_image_menu_item_new_with_mnemonic ( _("_Interpolate Times") );
8569       g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_interpolate_times), pass_along );
8570       gtk_menu_shell_append ( GTK_MENU_SHELL(transform_submenu), item );
8571       gtk_widget_set_tooltip_text (item, _("Reset trackpoint timestamps between the first and last points such that track is traveled at equal speed"));
8572       gtk_widget_show ( item );
8573     }
8574
8575     if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
8576       item = gtk_image_menu_item_new_with_mnemonic ( _("_Reverse Track") );
8577     else
8578       item = gtk_image_menu_item_new_with_mnemonic ( _("_Reverse Route") );
8579     gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_BACK, GTK_ICON_SIZE_MENU) );
8580     g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_reverse), pass_along );
8581     gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
8582     gtk_widget_show ( item );
8583
8584     if ( subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE ) {
8585       item = gtk_image_menu_item_new_with_mnemonic ( _("Refine Route...") );
8586       gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_FIND, GTK_ICON_SIZE_MENU) );
8587       g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_route_refine), pass_along );
8588       gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
8589       gtk_widget_show ( item );
8590     }
8591
8592     /* ATM This function is only available via the layers panel, due to the method in finding out the maps in use */
8593     if ( vlp ) {
8594       if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
8595         item = gtk_image_menu_item_new_with_mnemonic ( _("Down_load Maps Along Track...") );
8596       else
8597         item = gtk_image_menu_item_new_with_mnemonic ( _("Down_load Maps Along Route...") );
8598       gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock ("vik-icon-Maps Download", GTK_ICON_SIZE_MENU) ); // Own icon - see stock_icons in vikwindow.c
8599       g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_download_map_along_track_cb), pass_along );
8600       gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
8601       gtk_widget_show ( item );
8602     }
8603
8604     if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
8605       item = gtk_image_menu_item_new_with_mnemonic ( _("_Export Track as GPX...") );
8606     else
8607       item = gtk_image_menu_item_new_with_mnemonic ( _("_Export Route as GPX...") );
8608     gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_HARDDISK, GTK_ICON_SIZE_MENU) );
8609     g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_gpx_track), pass_along );
8610     gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
8611     gtk_widget_show ( item );
8612
8613     if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
8614       item = gtk_image_menu_item_new_with_mnemonic ( _("E_xtend Track End") );
8615     else
8616       item = gtk_image_menu_item_new_with_mnemonic ( _("E_xtend Route End") );
8617     gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ADD, GTK_ICON_SIZE_MENU) );
8618     g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_extend_track_end), pass_along );
8619     gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
8620     gtk_widget_show ( item );
8621
8622     if ( subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE ) {
8623       item = gtk_image_menu_item_new_with_mnemonic ( _("Extend _Using Route Finder") );
8624       gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock ("vik-icon-Route Finder", GTK_ICON_SIZE_MENU) ); // Own icon - see stock_icons in vikwindow.c
8625       g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_extend_track_end_route_finder), pass_along );
8626       gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
8627       gtk_widget_show ( item );
8628     }
8629
8630     // ATM can't upload a single waypoint but can do waypoints to a GPS
8631     if ( subtype != VIK_TRW_LAYER_SUBLAYER_WAYPOINT ) {
8632       item = gtk_image_menu_item_new_with_mnemonic ( _("_Upload") );
8633       gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_UP, GTK_ICON_SIZE_MENU) );
8634       gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
8635       gtk_widget_show ( item );
8636       gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), upload_submenu );
8637
8638       item = gtk_image_menu_item_new_with_mnemonic ( _("_Upload to GPS...") );
8639       gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_FORWARD, GTK_ICON_SIZE_MENU) );
8640       g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_gps_upload_any), pass_along );
8641       gtk_menu_shell_append ( GTK_MENU_SHELL(upload_submenu), item );
8642       gtk_widget_show ( item );
8643     }
8644   }
8645
8646   GtkWidget *external_submenu = create_external_submenu ( menu );
8647
8648   // These are only made available if a suitable program is installed
8649   if ( (have_astro_program || have_diary_program) &&
8650        (subtype == VIK_TRW_LAYER_SUBLAYER_TRACK || subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT) ) {
8651
8652     if ( have_diary_program ) {
8653       item = gtk_image_menu_item_new_with_mnemonic ( _("_Diary") );
8654       gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_SPELL_CHECK, GTK_ICON_SIZE_MENU) );
8655       g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_diary), pass_along );
8656       gtk_menu_shell_append ( GTK_MENU_SHELL(external_submenu), item );
8657       gtk_widget_set_tooltip_text (item, _("Open diary program at this date"));
8658       gtk_widget_show ( item );
8659     }
8660
8661     if ( have_astro_program ) {
8662       item = gtk_image_menu_item_new_with_mnemonic ( _("_Astronomy") );
8663       g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_astro), pass_along );
8664       gtk_menu_shell_append ( GTK_MENU_SHELL(external_submenu), item );
8665       gtk_widget_set_tooltip_text (item, _("Open astronomy program at this date and location"));
8666       gtk_widget_show ( item );
8667     }
8668   }
8669
8670   if ( l->current_tpl || l->current_wp ) {
8671     // For the selected point
8672     VikCoord *vc;
8673     if ( l->current_tpl )
8674       vc = &(VIK_TRACKPOINT(l->current_tpl->data)->coord);
8675     else
8676       vc = &(l->current_wp->coord);
8677     vik_ext_tools_add_menu_items_to_menu ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(l)), GTK_MENU (external_submenu), vc );
8678   }
8679   else {
8680     // Otherwise for the selected sublayer
8681     // TODO: Should use selected items centre - rather than implicitly using the current viewport
8682     vik_ext_tools_add_menu_items_to_menu ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(l)), GTK_MENU (external_submenu), NULL );
8683   }
8684
8685
8686 #ifdef VIK_CONFIG_GOOGLE
8687   if ( subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE && is_valid_google_route ( l, sublayer ) )
8688   {
8689     item = gtk_image_menu_item_new_with_mnemonic ( _("_View Google Directions") );
8690     gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NETWORK, GTK_ICON_SIZE_MENU) );
8691     g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_google_route_webpage), pass_along );
8692     gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
8693     gtk_widget_show ( item );
8694   }
8695 #endif
8696
8697   // Some things aren't usable with routes
8698   if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK ) {
8699 #ifdef VIK_CONFIG_OPENSTREETMAP
8700     item = gtk_image_menu_item_new_with_mnemonic ( _("Upload to _OSM...") );
8701     // Convert internal pointer into track
8702     pass_along[MA_MISC] = g_hash_table_lookup ( l->tracks, sublayer);
8703     gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_UP, GTK_ICON_SIZE_MENU) );
8704     g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_osm_traces_upload_track_cb), pass_along );
8705     gtk_menu_shell_append ( GTK_MENU_SHELL(upload_submenu), item );
8706     gtk_widget_show ( item );
8707 #endif
8708
8709     // Currently filter with functions all use shellcommands and thus don't work in Windows
8710 #ifndef WINDOWS
8711     item = gtk_image_menu_item_new_with_mnemonic ( _("Use with _Filter") );
8712     gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
8713     g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_track_use_with_filter), pass_along );
8714     gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
8715     gtk_widget_show ( item );
8716 #endif
8717
8718     /* ATM This function is only available via the layers panel, due to needing a vlp */
8719     if ( vlp ) {
8720       item = a_acquire_track_menu ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(l)), vlp,
8721                                     vik_layers_panel_get_viewport(VIK_LAYERS_PANEL(vlp)),
8722                                     g_hash_table_lookup ( l->tracks, (gchar *) sublayer ) );
8723       if ( item ) {
8724         gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
8725         gtk_widget_show ( item );
8726       }
8727     }
8728
8729 #ifdef VIK_CONFIG_GEOTAG
8730     item = gtk_menu_item_new_with_mnemonic ( _("Geotag _Images...") );
8731     g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_geotagging_track), pass_along );
8732     gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
8733     gtk_widget_show ( item );
8734 #endif
8735   }
8736
8737   if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK || subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE ) {
8738     // Only show on viewport popmenu when a trackpoint is selected
8739     if ( ! vlp && l->current_tpl ) {
8740       // Add separator
8741       item = gtk_menu_item_new ();
8742       gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
8743       gtk_widget_show ( item );
8744
8745       item = gtk_image_menu_item_new_with_mnemonic ( _("_Edit Trackpoint") );
8746       gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_PROPERTIES, GTK_ICON_SIZE_MENU) );
8747       g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_edit_trackpoint), pass_along );
8748       gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
8749       gtk_widget_show ( item );
8750     }
8751   }
8752
8753   if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINTS || subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT ) {
8754     GtkWidget *transform_submenu;
8755     transform_submenu = gtk_menu_new ();
8756     item = gtk_image_menu_item_new_with_mnemonic ( _("_Transform") );
8757     gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_CONVERT, GTK_ICON_SIZE_MENU) );
8758     gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
8759     gtk_widget_show ( item );
8760     gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), transform_submenu );
8761
8762     GtkWidget *dem_submenu;
8763     dem_submenu = gtk_menu_new ();
8764     item = gtk_image_menu_item_new_with_mnemonic ( _("_Apply DEM Data") );
8765     gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock ("vik-icon-DEM Download", GTK_ICON_SIZE_MENU) ); // Own icon - see stock_icons in vikwindow.c
8766     gtk_menu_shell_append ( GTK_MENU_SHELL(transform_submenu), item );
8767     gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), dem_submenu );
8768
8769     item = gtk_image_menu_item_new_with_mnemonic ( _("_Overwrite") );
8770     g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_apply_dem_data_wpt_all), pass_along );
8771     gtk_menu_shell_append ( GTK_MENU_SHELL(dem_submenu), item );
8772     gtk_widget_set_tooltip_text (item, _("Overwrite any existing elevation values with DEM values"));
8773     gtk_widget_show ( item );
8774
8775     item = gtk_image_menu_item_new_with_mnemonic ( _("_Keep Existing") );
8776     g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_apply_dem_data_wpt_only_missing), pass_along );
8777     gtk_menu_shell_append ( GTK_MENU_SHELL(dem_submenu), item );
8778     gtk_widget_set_tooltip_text (item, _("Keep existing elevation values, only attempt for missing values"));
8779     gtk_widget_show ( item );
8780   }
8781
8782   gtk_widget_show_all ( GTK_WIDGET(menu) );
8783
8784   return rv;
8785 }
8786
8787 // TODO: Probably better to rework this track manipulation in viktrack.c
8788 static void trw_layer_insert_tp_beside_current_tp ( VikTrwLayer *vtl, gboolean before )
8789 {
8790   // sanity check
8791   if (!vtl->current_tpl)
8792     return;
8793
8794   VikTrackpoint *tp_current = VIK_TRACKPOINT(vtl->current_tpl->data);
8795   VikTrackpoint *tp_other = NULL;
8796
8797   if ( before ) {
8798     if (!vtl->current_tpl->prev)
8799       return;
8800     tp_other = VIK_TRACKPOINT(vtl->current_tpl->prev->data);
8801   } else {
8802     if (!vtl->current_tpl->next)
8803       return;
8804     tp_other = VIK_TRACKPOINT(vtl->current_tpl->next->data);
8805   }
8806
8807   // Use current and other trackpoints to form a new track point which is inserted into the tracklist
8808   if ( tp_other ) {
8809
8810     VikTrackpoint *tp_new = vik_trackpoint_new();
8811     struct LatLon ll_current, ll_other;
8812     vik_coord_to_latlon ( &tp_current->coord, &ll_current );
8813     vik_coord_to_latlon ( &tp_other->coord, &ll_other );
8814
8815     /* main positional interpolation */
8816     struct LatLon ll_new = { (ll_current.lat+ll_other.lat)/2, (ll_current.lon+ll_other.lon)/2 };
8817     vik_coord_load_from_latlon ( &(tp_new->coord), vtl->coord_mode, &ll_new );
8818
8819     /* Now other properties that can be interpolated */
8820     tp_new->altitude = (tp_current->altitude + tp_other->altitude) / 2;
8821
8822     if (tp_current->has_timestamp && tp_other->has_timestamp) {
8823       /* Note here the division is applied to each part, then added
8824          This is to avoid potential overflow issues with a 32 time_t for dates after midpoint of this Unix time on 2004/01/04 */
8825       tp_new->timestamp = (tp_current->timestamp/2) + (tp_other->timestamp/2);
8826       tp_new->has_timestamp = TRUE;
8827     }
8828
8829     if (tp_current->speed != NAN && tp_other->speed != NAN)
8830       tp_new->speed = (tp_current->speed + tp_other->speed)/2;
8831
8832     /* TODO - improve interpolation of course, as it may not be correct.
8833        if courses in degrees are 350 + 020, the mid course more likely to be 005 (not 185)
8834        [similar applies if value is in radians] */
8835     if (tp_current->course != NAN && tp_other->course != NAN)
8836       tp_new->course = (tp_current->course + tp_other->course)/2;
8837
8838     /* DOP / sat values remain at defaults as not they do not seem applicable to a dreamt up point */
8839
8840     // Insert new point into the appropriate trackpoint list, either before or after the current trackpoint as directed
8841     VikTrack *trk = g_hash_table_lookup ( vtl->tracks, vtl->current_tp_id );
8842     if ( !trk )
8843       // Otherwise try routes
8844       trk = g_hash_table_lookup ( vtl->routes, vtl->current_tp_id );
8845     if ( !trk )
8846       return;
8847
8848     gint index =  g_list_index ( trk->trackpoints, tp_current );
8849     if ( index > -1 ) {
8850       if ( !before )
8851         index = index + 1;
8852       // NB no recalculation of bounds since it is inserted between points
8853       trk->trackpoints = g_list_insert ( trk->trackpoints, tp_new, index );
8854     }
8855   }
8856 }
8857
8858 static void trw_layer_cancel_current_tp ( VikTrwLayer *vtl, gboolean destroy )
8859 {
8860   if ( vtl->tpwin )
8861   {
8862     if ( destroy)
8863     {
8864       gtk_widget_destroy ( GTK_WIDGET(vtl->tpwin) );
8865       vtl->tpwin = NULL;
8866     }
8867     else
8868       vik_trw_layer_tpwin_set_empty ( vtl->tpwin );
8869   }
8870   if ( vtl->current_tpl )
8871   {
8872     vtl->current_tpl = NULL;
8873     vtl->current_tp_track = NULL;
8874     vtl->current_tp_id = NULL;
8875     vik_layer_emit_update(VIK_LAYER(vtl));
8876   }
8877 }
8878
8879 static void my_tpwin_set_tp ( VikTrwLayer *vtl )
8880 {
8881   VikTrack *trk = vtl->current_tp_track;
8882   VikCoord vc;
8883   // Notional center of a track is simply an average of the bounding box extremities
8884   struct LatLon center = { (trk->bbox.north+trk->bbox.south)/2, (trk->bbox.east+trk->bbox.west)/2 };
8885   vik_coord_load_from_latlon ( &vc, vtl->coord_mode, &center );
8886   vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, trk->name, vtl->current_tp_track->is_route );
8887 }
8888
8889 static void trw_layer_tpwin_response ( VikTrwLayer *vtl, gint response )
8890 {
8891   g_assert ( vtl->tpwin != NULL );
8892   if ( response == VIK_TRW_LAYER_TPWIN_CLOSE )
8893     trw_layer_cancel_current_tp ( vtl, TRUE );
8894
8895   if ( vtl->current_tpl == NULL )
8896     return;
8897
8898   if ( response == VIK_TRW_LAYER_TPWIN_SPLIT && vtl->current_tpl->next && vtl->current_tpl->prev )
8899   {
8900     trw_layer_split_at_selected_trackpoint ( vtl, vtl->current_tp_track->is_route ? VIK_TRW_LAYER_SUBLAYER_ROUTE : VIK_TRW_LAYER_SUBLAYER_TRACK );
8901     my_tpwin_set_tp ( vtl );
8902   }
8903   else if ( response == VIK_TRW_LAYER_TPWIN_DELETE )
8904   {
8905     VikTrack *tr = g_hash_table_lookup ( vtl->tracks, vtl->current_tp_id );
8906     if ( tr == NULL )
8907       tr = g_hash_table_lookup ( vtl->routes, vtl->current_tp_id );
8908     if ( tr == NULL )
8909       return;
8910
8911     trw_layer_trackpoint_selected_delete ( vtl, tr );
8912
8913     if ( vtl->current_tpl )
8914       // Reset dialog with the available adjacent trackpoint
8915       my_tpwin_set_tp ( vtl );
8916
8917     vik_layer_emit_update(VIK_LAYER(vtl));
8918   }
8919   else if ( response == VIK_TRW_LAYER_TPWIN_FORWARD && vtl->current_tpl->next )
8920   {
8921     if ( vtl->current_tp_track ) {
8922       vtl->current_tpl = vtl->current_tpl->next;
8923       my_tpwin_set_tp ( vtl );
8924     }
8925     vik_layer_emit_update(VIK_LAYER(vtl)); /* TODO longone: either move or only update if tp is inside drawing window */
8926   }
8927   else if ( response == VIK_TRW_LAYER_TPWIN_BACK && vtl->current_tpl->prev )
8928   {
8929     if ( vtl->current_tp_track ) {
8930       vtl->current_tpl = vtl->current_tpl->prev;
8931       my_tpwin_set_tp ( vtl );
8932     }
8933     vik_layer_emit_update(VIK_LAYER(vtl));
8934   }
8935   else if ( response == VIK_TRW_LAYER_TPWIN_INSERT && vtl->current_tpl->next )
8936   {
8937     trw_layer_insert_tp_beside_current_tp ( vtl, FALSE );
8938     vik_layer_emit_update(VIK_LAYER(vtl));
8939   }
8940   else if ( response == VIK_TRW_LAYER_TPWIN_DATA_CHANGED )
8941     vik_layer_emit_update(VIK_LAYER(vtl));
8942 }
8943
8944 /**
8945  * trw_layer_dialog_shift:
8946  * @vertical: The reposition strategy. If Vertical moves dialog vertically, otherwise moves it horizontally
8947  *
8948  * Try to reposition a dialog if it's over the specified coord
8949  *  so to not obscure the item of interest
8950  */
8951 void trw_layer_dialog_shift ( VikTrwLayer *vtl, GtkWindow *dialog, VikCoord *coord, gboolean vertical )
8952 {
8953   GtkWindow *parent = VIK_GTK_WINDOW_FROM_LAYER(vtl); //i.e. the main window
8954
8955   // Attempt force dialog to be shown so we can find out where it is more reliably...
8956   while ( gtk_events_pending() )
8957     gtk_main_iteration ();
8958
8959   // get parent window position & size
8960   gint win_pos_x, win_pos_y;
8961   gtk_window_get_position ( parent, &win_pos_x, &win_pos_y );
8962
8963   gint win_size_x, win_size_y;
8964   gtk_window_get_size ( parent, &win_size_x, &win_size_y );
8965
8966   // get own dialog size
8967   gint dia_size_x, dia_size_y;
8968   gtk_window_get_size ( dialog, &dia_size_x, &dia_size_y );
8969
8970   // get own dialog position
8971   gint dia_pos_x, dia_pos_y;
8972   gtk_window_get_position ( dialog, &dia_pos_x, &dia_pos_y );
8973
8974   // Dialog not 'realized'/positioned - so can't really do any repositioning logic
8975   if ( dia_pos_x > 2 && dia_pos_y > 2 ) {
8976
8977     VikViewport *vvp = vik_window_viewport ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)) );
8978
8979     gint vp_xx, vp_yy; // In viewport pixels
8980     vik_viewport_coord_to_screen ( vvp, coord, &vp_xx, &vp_yy );
8981
8982     // Work out the 'bounding box' in pixel terms of the dialog and only move it when over the position
8983
8984     gint dest_x = 0;
8985     gint dest_y = 0;
8986     if ( gtk_widget_translate_coordinates ( GTK_WIDGET(vvp), GTK_WIDGET(parent), 0, 0, &dest_x, &dest_y ) ) {
8987
8988       // Transform Viewport pixels into absolute pixels
8989       gint tmp_xx = vp_xx + dest_x + win_pos_x - 10;
8990       gint tmp_yy = vp_yy + dest_y + win_pos_y - 10;
8991
8992       // Is dialog over the point (to within an  ^^ edge value)
8993       if ( (tmp_xx > dia_pos_x) && (tmp_xx < (dia_pos_x + dia_size_x)) &&
8994            (tmp_yy > dia_pos_y) && (tmp_yy < (dia_pos_y + dia_size_y)) ) {
8995
8996         if ( vertical ) {
8997           // Shift up<->down
8998           gint hh = vik_viewport_get_height ( vvp );
8999
9000           // Consider the difference in viewport to the full window
9001           gint offset_y = dest_y;
9002           // Add difference between dialog and window sizes
9003           offset_y += win_pos_y + (hh/2 - dia_size_y)/2;
9004
9005           if ( vp_yy > hh/2 ) {
9006             // Point in bottom half, move window to top half
9007             gtk_window_move ( dialog, dia_pos_x, offset_y );
9008           }
9009           else {
9010             // Point in top half, move dialog down
9011             gtk_window_move ( dialog, dia_pos_x, hh/2 + offset_y );
9012           }
9013         }
9014         else {
9015           // Shift left<->right
9016           gint ww = vik_viewport_get_width ( vvp );
9017
9018           // Consider the difference in viewport to the full window
9019           gint offset_x = dest_x;
9020           // Add difference between dialog and window sizes
9021           offset_x += win_pos_x + (ww/2 - dia_size_x)/2;
9022
9023           if ( vp_xx > ww/2 ) {
9024             // Point on right, move window to left
9025             gtk_window_move ( dialog, offset_x, dia_pos_y );
9026           }
9027           else {
9028             // Point on left, move right
9029             gtk_window_move ( dialog, ww/2 + offset_x, dia_pos_y );
9030           }
9031         }
9032       }
9033     }
9034   }
9035 }
9036
9037 static void trw_layer_tpwin_init ( VikTrwLayer *vtl )
9038 {
9039   if ( ! vtl->tpwin )
9040   {
9041     vtl->tpwin = vik_trw_layer_tpwin_new ( VIK_GTK_WINDOW_FROM_LAYER(vtl) );
9042     g_signal_connect_swapped ( GTK_DIALOG(vtl->tpwin), "response", G_CALLBACK(trw_layer_tpwin_response), vtl );
9043     /* connect signals -- DELETE SIGNAL VERY IMPORTANT TO SET TO NULL */
9044     g_signal_connect_swapped ( vtl->tpwin, "delete-event", G_CALLBACK(trw_layer_cancel_current_tp), vtl );
9045
9046     gtk_widget_show_all ( GTK_WIDGET(vtl->tpwin) );
9047
9048     if ( vtl->current_tpl ) {
9049       // get tp pixel position
9050       VikTrackpoint *tp = VIK_TRACKPOINT(vtl->current_tpl->data);
9051
9052       // Shift up<->down to try not to obscure the trackpoint.
9053       trw_layer_dialog_shift ( vtl, GTK_WINDOW(vtl->tpwin), &(tp->coord), TRUE );
9054     }
9055   }
9056
9057   if ( vtl->current_tpl )
9058     if ( vtl->current_tp_track )
9059       my_tpwin_set_tp ( vtl );
9060   /* set layer name and TP data */
9061 }
9062
9063 /***************************************************************************
9064  ** Tool code
9065  ***************************************************************************/
9066
9067 /*** Utility data structures and functions ****/
9068
9069 typedef struct {
9070   gint x, y;
9071   gint closest_x, closest_y;
9072   gboolean draw_images;
9073   gpointer closest_wp_id;
9074   VikWaypoint *closest_wp;
9075   VikViewport *vvp;
9076 } WPSearchParams;
9077
9078 typedef struct {
9079   gint x, y;
9080   gint closest_x, closest_y;
9081   gpointer closest_track_id;
9082   VikTrackpoint *closest_tp;
9083   VikViewport *vvp;
9084   GList *closest_tpl;
9085   LatLonBBox bbox;
9086 } TPSearchParams;
9087
9088 static void waypoint_search_closest_tp ( gpointer id, VikWaypoint *wp, WPSearchParams *params )
9089 {
9090   gint x, y;
9091   if ( !wp->visible )
9092     return;
9093
9094   vik_viewport_coord_to_screen ( params->vvp, &(wp->coord), &x, &y );
9095
9096   // If waypoint has an image then use the image size to select
9097   if ( params->draw_images && wp->image ) {
9098     gint slackx, slacky;
9099     slackx = wp->image_width / 2;
9100     slacky = wp->image_height / 2;
9101
9102     if (    x <= params->x + slackx && x >= params->x - slackx
9103          && y <= params->y + slacky && y >= params->y - slacky ) {
9104       params->closest_wp_id = id;
9105       params->closest_wp = wp;
9106       params->closest_x = x;
9107       params->closest_y = y;
9108     }
9109   }
9110   else if ( abs (x - params->x) <= WAYPOINT_SIZE_APPROX && abs (y - params->y) <= WAYPOINT_SIZE_APPROX &&
9111             ((!params->closest_wp) ||        /* was the old waypoint we already found closer than this one? */
9112              abs(x - params->x)+abs(y - params->y) < abs(x - params->closest_x)+abs(y - params->closest_y)))
9113     {
9114       params->closest_wp_id = id;
9115       params->closest_wp = wp;
9116       params->closest_x = x;
9117       params->closest_y = y;
9118     }
9119 }
9120
9121 static void track_search_closest_tp ( gpointer id, VikTrack *t, TPSearchParams *params )
9122 {
9123   GList *tpl = t->trackpoints;
9124   VikTrackpoint *tp;
9125
9126   if ( !t->visible )
9127     return;
9128
9129   if ( ! BBOX_INTERSECT ( t->bbox, params->bbox ) )
9130     return;
9131
9132   while (tpl)
9133   {
9134     gint x, y;
9135     tp = VIK_TRACKPOINT(tpl->data);
9136
9137     vik_viewport_coord_to_screen ( params->vvp, &(tp->coord), &x, &y );
9138  
9139     if ( abs (x - params->x) <= TRACKPOINT_SIZE_APPROX && abs (y - params->y) <= TRACKPOINT_SIZE_APPROX &&
9140         ((!params->closest_tp) ||        /* was the old trackpoint we already found closer than this one? */
9141           abs(x - params->x)+abs(y - params->y) < abs(x - params->closest_x)+abs(y - params->closest_y)))
9142     {
9143       params->closest_track_id = id;
9144       params->closest_tp = tp;
9145       params->closest_tpl = tpl;
9146       params->closest_x = x;
9147       params->closest_y = y;
9148     }
9149     tpl = tpl->next;
9150   }
9151 }
9152
9153 // ATM: Leave this as 'Track' only.
9154 //  Not overly bothered about having a snap to route trackpoint capability
9155 static VikTrackpoint *closest_tp_in_five_pixel_interval ( VikTrwLayer *vtl, VikViewport *vvp, gint x, gint y )
9156 {
9157   TPSearchParams params;
9158   params.x = x;
9159   params.y = y;
9160   params.vvp = vvp;
9161   params.closest_track_id = NULL;
9162   params.closest_tp = NULL;
9163   vik_viewport_get_min_max_lat_lon ( params.vvp, &(params.bbox.south), &(params.bbox.north), &(params.bbox.west), &(params.bbox.east) );
9164   g_hash_table_foreach ( vtl->tracks, (GHFunc) track_search_closest_tp, &params);
9165   return params.closest_tp;
9166 }
9167
9168 static VikWaypoint *closest_wp_in_five_pixel_interval ( VikTrwLayer *vtl, VikViewport *vvp, gint x, gint y )
9169 {
9170   WPSearchParams params;
9171   params.x = x;
9172   params.y = y;
9173   params.vvp = vvp;
9174   params.draw_images = vtl->drawimages;
9175   params.closest_wp = NULL;
9176   params.closest_wp_id = NULL;
9177   g_hash_table_foreach ( vtl->waypoints, (GHFunc) waypoint_search_closest_tp, &params);
9178   return params.closest_wp;
9179 }
9180
9181
9182 // Some forward declarations
9183 static void marker_begin_move ( tool_ed_t *t, gint x, gint y );
9184 static void marker_moveto ( tool_ed_t *t, gint x, gint y );
9185 static void marker_end_move ( tool_ed_t *t );
9186 //
9187
9188 static gboolean trw_layer_select_move ( VikTrwLayer *vtl, GdkEventMotion *event, VikViewport *vvp, tool_ed_t* t )
9189 {
9190   if ( t->holding ) {
9191     VikCoord new_coord;
9192     vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
9193
9194     // Here always allow snapping back to the original location
9195     //  this is useful when one decides not to move the thing afterall
9196     // If one wants to move the item only a little bit then don't hold down the 'snap' key!
9197  
9198     // snap to TP
9199     if ( event->state & GDK_CONTROL_MASK )
9200     {
9201       VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
9202       if ( tp )
9203         new_coord = tp->coord;
9204     }
9205
9206     // snap to WP
9207     if ( event->state & GDK_SHIFT_MASK )
9208     {
9209       VikWaypoint *wp = closest_wp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
9210       if ( wp )
9211         new_coord = wp->coord;
9212     }
9213     
9214     gint x, y;
9215     vik_viewport_coord_to_screen ( vvp, &new_coord, &x, &y );
9216
9217     marker_moveto ( t, x, y );
9218
9219     return TRUE;
9220   }
9221   return FALSE;
9222 }
9223
9224 static gboolean trw_layer_select_release ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp, tool_ed_t* t )
9225 {
9226   if ( t->holding && event->button == 1 )
9227   {
9228     // Prevent accidental (small) shifts when specific movement has not been requested
9229     //  (as the click release has occurred within the click object detection area)
9230     if ( !t->moving )
9231       return FALSE;
9232
9233     VikCoord new_coord;
9234     vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
9235
9236     // snap to TP
9237     if ( event->state & GDK_CONTROL_MASK )
9238     {
9239       VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
9240       if ( tp )
9241         new_coord = tp->coord;
9242     }
9243
9244     // snap to WP
9245     if ( event->state & GDK_SHIFT_MASK )
9246     {
9247       VikWaypoint *wp = closest_wp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
9248       if ( wp )
9249         new_coord = wp->coord;
9250     }
9251
9252     marker_end_move ( t );
9253
9254     // Determine if working on a waypoint or a trackpoint
9255     if ( t->is_waypoint ) {
9256       // Update waypoint position
9257       vtl->current_wp->coord = new_coord;
9258       trw_layer_calculate_bounds_waypoints ( vtl );
9259       // Reset waypoint pointer
9260       vtl->current_wp    = NULL;
9261       vtl->current_wp_id = NULL;
9262     }
9263     else {
9264       if ( vtl->current_tpl ) {
9265         VIK_TRACKPOINT(vtl->current_tpl->data)->coord = new_coord;
9266
9267         if ( vtl->current_tp_track )
9268           vik_track_calculate_bounds ( vtl->current_tp_track );
9269
9270         if ( vtl->tpwin )
9271           if ( vtl->current_tp_track )
9272             my_tpwin_set_tp ( vtl );
9273         // NB don't reset the selected trackpoint, thus ensuring it's still in the tpwin
9274       }
9275     }
9276
9277     vik_layer_emit_update ( VIK_LAYER(vtl) );
9278     return TRUE;
9279   }
9280   return FALSE;
9281 }
9282
9283 /*
9284   Returns true if a waypoint or track is found near the requested event position for this particular layer
9285   The item found is automatically selected
9286   This is a tool like feature but routed via the layer interface, since it's instigated by a 'global' layer tool in vikwindow.c
9287  */
9288 static gboolean trw_layer_select_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp, tool_ed_t* tet )
9289 {
9290   if ( event->button != 1 )
9291     return FALSE;
9292
9293   if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
9294     return FALSE;
9295
9296   if ( !vtl->tracks_visible && !vtl->waypoints_visible && !vtl->routes_visible )
9297     return FALSE;
9298
9299   LatLonBBox bbox;
9300   vik_viewport_get_min_max_lat_lon ( vvp, &(bbox.south), &(bbox.north), &(bbox.west), &(bbox.east) );
9301
9302   // Go for waypoints first as these often will be near a track, but it's likely the wp is wanted rather then the track
9303
9304   if ( vtl->waypoints_visible && BBOX_INTERSECT (vtl->waypoints_bbox, bbox ) ) {
9305     WPSearchParams wp_params;
9306     wp_params.vvp = vvp;
9307     wp_params.x = event->x;
9308     wp_params.y = event->y;
9309     wp_params.draw_images = vtl->drawimages;
9310     wp_params.closest_wp_id = NULL;
9311     wp_params.closest_wp = NULL;
9312
9313     g_hash_table_foreach ( vtl->waypoints, (GHFunc) waypoint_search_closest_tp, &wp_params);
9314
9315     if ( wp_params.closest_wp )  {
9316
9317       // Select
9318       vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, g_hash_table_lookup ( vtl->waypoints_iters, wp_params.closest_wp_id ), TRUE );
9319
9320       // Too easy to move it so must be holding shift to start immediately moving it
9321       //   or otherwise be previously selected but not have an image (otherwise clicking within image bounds (again) moves it)
9322       if ( event->state & GDK_SHIFT_MASK ||
9323            ( vtl->current_wp == wp_params.closest_wp && !vtl->current_wp->image ) ) {
9324         // Put into 'move buffer'
9325         // NB vvp & vw already set in tet
9326         tet->vtl = (gpointer)vtl;
9327         tet->is_waypoint = TRUE;
9328       
9329         marker_begin_move (tet, event->x, event->y);
9330       }
9331
9332       vtl->current_wp =    wp_params.closest_wp;
9333       vtl->current_wp_id = wp_params.closest_wp_id;
9334
9335       if ( event->type == GDK_2BUTTON_PRESS ) {
9336         if ( vtl->current_wp->image ) {
9337           menu_array_sublayer values;
9338           values[MA_VTL] = vtl;
9339           values[MA_MISC] = vtl->current_wp->image;
9340           trw_layer_show_picture ( values );
9341         }
9342       }
9343
9344       vik_layer_emit_update ( VIK_LAYER(vtl) );
9345
9346       return TRUE;
9347     }
9348   }
9349
9350   // Used for both track and route lists
9351   TPSearchParams tp_params;
9352   tp_params.vvp = vvp;
9353   tp_params.x = event->x;
9354   tp_params.y = event->y;
9355   tp_params.closest_track_id = NULL;
9356   tp_params.closest_tp = NULL;
9357   tp_params.closest_tpl = NULL;
9358   tp_params.bbox = bbox;
9359
9360   if (vtl->tracks_visible) {
9361     g_hash_table_foreach ( vtl->tracks, (GHFunc) track_search_closest_tp, &tp_params);
9362
9363     if ( tp_params.closest_tp )  {
9364
9365       // Always select + highlight the track
9366       vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, g_hash_table_lookup ( vtl->tracks_iters, tp_params.closest_track_id ), TRUE );
9367
9368       tet->is_waypoint = FALSE;
9369
9370       // Select the Trackpoint
9371       // Can move it immediately when control held or it's the previously selected tp
9372       if ( event->state & GDK_CONTROL_MASK ||
9373            vtl->current_tpl == tp_params.closest_tpl ) {
9374         // Put into 'move buffer'
9375         // NB vvp & vw already set in tet
9376         tet->vtl = (gpointer)vtl;
9377         marker_begin_move (tet, event->x, event->y);
9378       }
9379
9380       vtl->current_tpl = tp_params.closest_tpl;
9381       vtl->current_tp_id = tp_params.closest_track_id;
9382       vtl->current_tp_track = g_hash_table_lookup ( vtl->tracks, tp_params.closest_track_id );
9383
9384       set_statusbar_msg_info_trkpt ( vtl, tp_params.closest_tp );
9385
9386       if ( vtl->tpwin )
9387         my_tpwin_set_tp ( vtl );
9388
9389       vik_layer_emit_update ( VIK_LAYER(vtl) );
9390       return TRUE;
9391     }
9392   }
9393
9394   // Try again for routes
9395   if (vtl->routes_visible) {
9396     g_hash_table_foreach ( vtl->routes, (GHFunc) track_search_closest_tp, &tp_params);
9397
9398     if ( tp_params.closest_tp )  {
9399
9400       // Always select + highlight the track
9401       vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, g_hash_table_lookup ( vtl->routes_iters, tp_params.closest_track_id ), TRUE );
9402
9403       tet->is_waypoint = FALSE;
9404
9405       // Select the Trackpoint
9406       // Can move it immediately when control held or it's the previously selected tp
9407       if ( event->state & GDK_CONTROL_MASK ||
9408            vtl->current_tpl == tp_params.closest_tpl ) {
9409         // Put into 'move buffer'
9410         // NB vvp & vw already set in tet
9411         tet->vtl = (gpointer)vtl;
9412         marker_begin_move (tet, event->x, event->y);
9413       }
9414
9415       vtl->current_tpl = tp_params.closest_tpl;
9416       vtl->current_tp_id = tp_params.closest_track_id;
9417       vtl->current_tp_track = g_hash_table_lookup ( vtl->routes, tp_params.closest_track_id );
9418
9419       set_statusbar_msg_info_trkpt ( vtl, tp_params.closest_tp );
9420
9421       if ( vtl->tpwin )
9422         my_tpwin_set_tp ( vtl );
9423
9424       vik_layer_emit_update ( VIK_LAYER(vtl) );
9425       return TRUE;
9426     }
9427   }
9428
9429   /* these aren't the droids you're looking for */
9430   vtl->current_wp    = NULL;
9431   vtl->current_wp_id = NULL;
9432   trw_layer_cancel_current_tp ( vtl, FALSE );
9433
9434   // Blank info
9435   vik_statusbar_set_message ( vik_window_get_statusbar (VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl))), VIK_STATUSBAR_INFO, "" );
9436
9437   return FALSE;
9438 }
9439
9440 static gboolean trw_layer_show_selected_viewport_menu ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
9441 {
9442   if ( event->button != 3 )
9443     return FALSE;
9444
9445   if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
9446     return FALSE;
9447
9448   if ( !vtl->tracks_visible && !vtl->waypoints_visible && !vtl->routes_visible )
9449     return FALSE;
9450
9451   /* Post menu for the currently selected item */
9452
9453   /* See if a track is selected */
9454   VikTrack *track = (VikTrack*)vik_window_get_selected_track ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(vtl) );
9455   if ( track && track->visible ) {
9456
9457     if ( track->name ) {
9458
9459       if ( vtl->track_right_click_menu )
9460         g_object_ref_sink ( G_OBJECT(vtl->track_right_click_menu) );
9461
9462       vtl->track_right_click_menu = GTK_MENU ( gtk_menu_new () );
9463       
9464       trku_udata udataU;
9465       udataU.trk  = track;
9466       udataU.uuid = NULL;
9467
9468       gpointer trkf;
9469       if ( track->is_route )
9470         trkf = g_hash_table_find ( vtl->routes, (GHRFunc) trw_layer_track_find_uuid, &udataU );
9471       else
9472         trkf = g_hash_table_find ( vtl->tracks, (GHRFunc) trw_layer_track_find_uuid, &udataU );
9473
9474       if ( trkf && udataU.uuid ) {
9475
9476         GtkTreeIter *iter;
9477         if ( track->is_route )
9478           iter = g_hash_table_lookup ( vtl->routes_iters, udataU.uuid );
9479         else
9480           iter = g_hash_table_lookup ( vtl->tracks_iters, udataU.uuid );
9481
9482         trw_layer_sublayer_add_menu_items ( vtl,
9483                                             vtl->track_right_click_menu,
9484                                             NULL,
9485                                             track->is_route ? VIK_TRW_LAYER_SUBLAYER_ROUTE : VIK_TRW_LAYER_SUBLAYER_TRACK,
9486                                             udataU.uuid,
9487                                             iter,
9488                                             vvp );
9489       }
9490
9491       gtk_menu_popup ( vtl->track_right_click_menu, NULL, NULL, NULL, NULL, event->button, gtk_get_current_event_time() );
9492         
9493       return TRUE;
9494     }
9495   }
9496
9497   /* See if a waypoint is selected */
9498   VikWaypoint *waypoint = (VikWaypoint*)vik_window_get_selected_waypoint ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(vtl) );
9499   if ( waypoint && waypoint->visible ) {
9500     if ( waypoint->name ) {
9501
9502       if ( vtl->wp_right_click_menu )
9503         g_object_ref_sink ( G_OBJECT(vtl->wp_right_click_menu) );
9504
9505       vtl->wp_right_click_menu = GTK_MENU ( gtk_menu_new () );
9506
9507       wpu_udata udata;
9508       udata.wp   = waypoint;
9509       udata.uuid = NULL;
9510
9511       gpointer wpf = g_hash_table_find ( vtl->waypoints, (GHRFunc) trw_layer_waypoint_find_uuid, (gpointer) &udata );
9512
9513       if ( wpf && udata.uuid ) {
9514         GtkTreeIter *iter = g_hash_table_lookup ( vtl->waypoints_iters, udata.uuid );
9515
9516         trw_layer_sublayer_add_menu_items ( vtl,
9517                                             vtl->wp_right_click_menu,
9518                                             NULL,
9519                                             VIK_TRW_LAYER_SUBLAYER_WAYPOINT,
9520                                             udata.uuid,
9521                                             iter,
9522                                             vvp );
9523       }
9524       gtk_menu_popup ( vtl->wp_right_click_menu, NULL, NULL, NULL, NULL, event->button, gtk_get_current_event_time() );
9525
9526       return TRUE;
9527     }
9528   }
9529
9530   return FALSE;
9531 }
9532
9533 /* background drawing hook, to be passed the viewport */
9534 static gboolean tool_sync_done = TRUE;
9535
9536 static gboolean tool_sync(gpointer data)
9537 {
9538   VikViewport *vvp = data;
9539   vik_viewport_sync(vvp);
9540   tool_sync_done = TRUE;
9541   return FALSE;
9542 }
9543
9544 static void marker_begin_move ( tool_ed_t *t, gint x, gint y )
9545 {
9546   t->holding = TRUE;
9547   t->gc = vik_viewport_new_gc (t->vvp, "black", 2);
9548   gdk_gc_set_function ( t->gc, GDK_INVERT );
9549   vik_viewport_draw_rectangle ( t->vvp, t->gc, FALSE, x-3, y-3, 6, 6 );
9550   vik_viewport_sync(t->vvp);
9551   t->oldx = x;
9552   t->oldy = y;
9553   t->moving = FALSE;
9554 }
9555
9556 static void marker_moveto ( tool_ed_t *t, gint x, gint y )
9557 {
9558   VikViewport *vvp =  t->vvp;
9559   vik_viewport_draw_rectangle ( vvp, t->gc, FALSE, t->oldx-3, t->oldy-3, 6, 6 );
9560   vik_viewport_draw_rectangle ( vvp, t->gc, FALSE, x-3, y-3, 6, 6 );
9561   t->oldx = x;
9562   t->oldy = y;
9563   t->moving = TRUE;
9564
9565   if (tool_sync_done) {
9566     g_idle_add_full (G_PRIORITY_HIGH_IDLE + 10, tool_sync, vvp, NULL);
9567     tool_sync_done = FALSE;
9568   }
9569 }
9570
9571 static void marker_end_move ( tool_ed_t *t )
9572 {
9573   vik_viewport_draw_rectangle ( t->vvp, t->gc, FALSE, t->oldx-3, t->oldy-3, 6, 6 );
9574   g_object_unref ( t->gc );
9575   t->holding = FALSE;
9576   t->moving = FALSE;
9577 }
9578
9579 /*** Edit waypoint ****/
9580
9581 static gpointer tool_edit_waypoint_create ( VikWindow *vw, VikViewport *vvp)
9582 {
9583   tool_ed_t *t = g_new(tool_ed_t, 1);
9584   t->vvp = vvp;
9585   t->holding = FALSE;
9586   return t;
9587 }
9588
9589 static void tool_edit_waypoint_destroy ( tool_ed_t *t )
9590 {
9591   g_free ( t );
9592 }
9593
9594 static gboolean tool_edit_waypoint_click ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data )
9595 {
9596   WPSearchParams params;
9597   tool_ed_t *t = data;
9598   VikViewport *vvp = t->vvp;
9599
9600   if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
9601     return FALSE;
9602
9603   if ( t->holding ) {
9604     return TRUE;
9605   }
9606
9607   if ( !vtl->vl.visible || !vtl->waypoints_visible )
9608     return FALSE;
9609
9610   if ( vtl->current_wp && vtl->current_wp->visible )
9611   {
9612     /* first check if current WP is within area (other may be 'closer', but we want to move the current) */
9613     gint x, y;
9614     vik_viewport_coord_to_screen ( vvp, &(vtl->current_wp->coord), &x, &y );
9615
9616     if ( abs(x - (int)round(event->x)) <= WAYPOINT_SIZE_APPROX &&
9617          abs(y - (int)round(event->y)) <= WAYPOINT_SIZE_APPROX )
9618     {
9619       if ( event->button == 3 )
9620         vtl->waypoint_rightclick = TRUE; /* remember that we're clicking; other layers will ignore release signal */
9621       else {
9622         marker_begin_move(t, event->x, event->y);
9623       }
9624       return TRUE;
9625     }
9626   }
9627
9628   params.vvp = vvp;
9629   params.x = event->x;
9630   params.y = event->y;
9631   params.draw_images = vtl->drawimages;
9632   params.closest_wp_id = NULL;
9633   params.closest_wp = NULL;
9634   g_hash_table_foreach ( vtl->waypoints, (GHFunc) waypoint_search_closest_tp, &params);
9635   if ( vtl->current_wp && (vtl->current_wp == params.closest_wp) )
9636   {
9637     if ( event->button == 3 )
9638       vtl->waypoint_rightclick = TRUE; /* remember that we're clicking; other layers will ignore release signal */
9639     else
9640       marker_begin_move(t, event->x, event->y);
9641     return FALSE;
9642   }
9643   else if ( params.closest_wp )
9644   {
9645     if ( event->button == 3 )
9646       vtl->waypoint_rightclick = TRUE; /* remember that we're clicking; other layers will ignore release signal */
9647     else
9648       vtl->waypoint_rightclick = FALSE;
9649
9650     vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, g_hash_table_lookup ( vtl->waypoints_iters, params.closest_wp_id ), TRUE );
9651
9652     vtl->current_wp = params.closest_wp;
9653     vtl->current_wp_id = params.closest_wp_id;
9654
9655     /* could make it so don't update if old WP is off screen and new is null but oh well */
9656     vik_layer_emit_update ( VIK_LAYER(vtl) );
9657     return TRUE;
9658   }
9659
9660   vtl->current_wp = NULL;
9661   vtl->current_wp_id = NULL;
9662   vtl->waypoint_rightclick = FALSE;
9663   vik_layer_emit_update ( VIK_LAYER(vtl) );
9664   return FALSE;
9665 }
9666
9667 static gboolean tool_edit_waypoint_move ( VikTrwLayer *vtl, GdkEventMotion *event, gpointer data )
9668 {
9669   tool_ed_t *t = data;
9670   VikViewport *vvp = t->vvp;
9671
9672   if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
9673     return FALSE;
9674
9675   if ( t->holding ) {
9676     VikCoord new_coord;
9677     vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
9678
9679     /* snap to TP */
9680     if ( event->state & GDK_CONTROL_MASK )
9681     {
9682       VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
9683       if ( tp )
9684         new_coord = tp->coord;
9685     }
9686
9687     /* snap to WP */
9688     if ( event->state & GDK_SHIFT_MASK )
9689     {
9690       VikWaypoint *wp = closest_wp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
9691       if ( wp && wp != vtl->current_wp )
9692         new_coord = wp->coord;
9693     }
9694     
9695     { 
9696       gint x, y;
9697       vik_viewport_coord_to_screen ( vvp, &new_coord, &x, &y );
9698
9699       marker_moveto ( t, x, y );
9700     } 
9701     return TRUE;
9702   }
9703   return FALSE;
9704 }
9705
9706 static gboolean tool_edit_waypoint_release ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data )
9707 {
9708   tool_ed_t *t = data;
9709   VikViewport *vvp = t->vvp;
9710
9711   if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
9712     return FALSE;
9713   
9714   if ( t->holding && event->button == 1 )
9715   {
9716     VikCoord new_coord;
9717     vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
9718
9719     /* snap to TP */
9720     if ( event->state & GDK_CONTROL_MASK )
9721     {
9722       VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
9723       if ( tp )
9724         new_coord = tp->coord;
9725     }
9726
9727     /* snap to WP */
9728     if ( event->state & GDK_SHIFT_MASK )
9729     {
9730       VikWaypoint *wp = closest_wp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
9731       if ( wp && wp != vtl->current_wp )
9732         new_coord = wp->coord;
9733     }
9734
9735     marker_end_move ( t );
9736
9737     vtl->current_wp->coord = new_coord;
9738
9739     trw_layer_calculate_bounds_waypoints ( vtl );
9740     vik_layer_emit_update ( VIK_LAYER(vtl) );
9741     return TRUE;
9742   }
9743   /* PUT IN RIGHT PLACE!!! */
9744   if ( event->button == 3 && vtl->waypoint_rightclick )
9745   {
9746     if ( vtl->wp_right_click_menu )
9747       g_object_ref_sink ( G_OBJECT(vtl->wp_right_click_menu) );
9748     if ( vtl->current_wp ) {
9749       vtl->wp_right_click_menu = GTK_MENU ( gtk_menu_new () );
9750       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 );
9751       gtk_menu_popup ( vtl->wp_right_click_menu, NULL, NULL, NULL, NULL, event->button, gtk_get_current_event_time() );
9752     }
9753     vtl->waypoint_rightclick = FALSE;
9754   }
9755   return FALSE;
9756 }
9757
9758 /*** New track ****/
9759
9760 static gpointer tool_new_track_create ( VikWindow *vw, VikViewport *vvp)
9761 {
9762   return vvp;
9763 }
9764
9765 typedef struct {
9766   VikTrwLayer *vtl;
9767   GdkDrawable *drawable;
9768   GdkGC *gc;
9769   GdkPixmap *pixmap;
9770 } draw_sync_t;
9771
9772 /*
9773  * Draw specified pixmap
9774  */
9775 static gboolean draw_sync ( gpointer data )
9776 {
9777   draw_sync_t *ds = (draw_sync_t*) data;
9778   // Sometimes don't want to draw
9779   //  normally because another update has taken precedent such as panning the display
9780   //   which means this pixmap is no longer valid
9781   if ( ds->vtl->draw_sync_do ) {
9782     gdk_draw_drawable (ds->drawable,
9783                        ds->gc,
9784                        ds->pixmap,
9785                        0, 0, 0, 0, -1, -1);
9786     ds->vtl->draw_sync_done = TRUE;
9787   }
9788   g_free ( ds );
9789   return FALSE;
9790 }
9791
9792 static gchar* distance_string (gdouble distance)
9793 {
9794   gchar str[128];
9795
9796   /* draw label with distance */
9797   vik_units_distance_t dist_units = a_vik_get_units_distance ();
9798   switch (dist_units) {
9799   case VIK_UNITS_DISTANCE_MILES:
9800     if (distance >= VIK_MILES_TO_METERS(1) && distance < VIK_MILES_TO_METERS(100)) {
9801       g_sprintf(str, "%3.2f miles", VIK_METERS_TO_MILES(distance));
9802     } else if (distance < 1609.4) {
9803       g_sprintf(str, "%d yards", (int)(distance*1.0936133));
9804     } else {
9805       g_sprintf(str, "%d miles", (int)VIK_METERS_TO_MILES(distance));
9806     }
9807     break;
9808   case VIK_UNITS_DISTANCE_NAUTICAL_MILES:
9809     if (distance >= VIK_NAUTICAL_MILES_TO_METERS(1) && distance < VIK_NAUTICAL_MILES_TO_METERS(100)) {
9810       g_sprintf(str, "%3.2f NM", VIK_METERS_TO_NAUTICAL_MILES(distance));
9811     } else if (distance < VIK_NAUTICAL_MILES_TO_METERS(1)) {
9812       g_sprintf(str, "%d yards", (int)(distance*1.0936133));
9813     } else {
9814       g_sprintf(str, "%d NM", (int)VIK_METERS_TO_NAUTICAL_MILES(distance));
9815     }
9816     break;
9817   default:
9818     // VIK_UNITS_DISTANCE_KILOMETRES
9819     if (distance >= 1000 && distance < 100000) {
9820       g_sprintf(str, "%3.2f km", distance/1000.0);
9821     } else if (distance < 1000) {
9822       g_sprintf(str, "%d m", (int)distance);
9823     } else {
9824       g_sprintf(str, "%d km", (int)distance/1000);
9825     }
9826     break;
9827   }
9828   return g_strdup (str);
9829 }
9830
9831 /*
9832  * Actually set the message in statusbar
9833  */
9834 static void statusbar_write (gdouble distance, gdouble elev_gain, gdouble elev_loss, gdouble last_step, gdouble angle, VikTrwLayer *vtl )
9835 {
9836   // Only show elevation data when track has some elevation properties
9837   gchar str_gain_loss[64];
9838   str_gain_loss[0] = '\0';
9839   gchar str_last_step[64];
9840   str_last_step[0] = '\0';
9841   gchar *str_total = distance_string (distance);
9842   
9843   if ( (elev_gain > 0.1) || (elev_loss > 0.1) ) {
9844     if ( a_vik_get_units_height () == VIK_UNITS_HEIGHT_METRES )
9845       g_sprintf(str_gain_loss, _(" - Gain %dm:Loss %dm"), (int)elev_gain, (int)elev_loss);
9846     else
9847       g_sprintf(str_gain_loss, _(" - Gain %dft:Loss %dft"), (int)VIK_METERS_TO_FEET(elev_gain), (int)VIK_METERS_TO_FEET(elev_loss));
9848   }
9849   
9850   if ( last_step > 0 ) {
9851       gchar *tmp = distance_string (last_step);
9852       g_sprintf(str_last_step, _(" - Bearing %3.1f° - Step %s"), RAD2DEG(angle), tmp);
9853       g_free ( tmp );
9854   }
9855   
9856   VikWindow *vw = VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl));
9857
9858   // Write with full gain/loss information
9859   gchar *msg = g_strdup_printf ( "Total %s%s%s", str_total, str_last_step, str_gain_loss);
9860   vik_statusbar_set_message ( vik_window_get_statusbar (vw), VIK_STATUSBAR_INFO, msg );
9861   g_free ( msg );
9862   g_free ( str_total );
9863 }
9864
9865 /*
9866  * Figure out what information should be set in the statusbar and then write it
9867  */
9868 static void update_statusbar ( VikTrwLayer *vtl )
9869 {
9870   // Get elevation data
9871   gdouble elev_gain, elev_loss;
9872   vik_track_get_total_elevation_gain ( vtl->current_track, &elev_gain, &elev_loss);
9873
9874   /* Find out actual distance of current track */
9875   gdouble distance = vik_track_get_length (vtl->current_track);
9876
9877   statusbar_write (distance, elev_gain, elev_loss, 0, 0, vtl);
9878 }
9879
9880
9881 static VikLayerToolFuncStatus tool_new_track_move ( VikTrwLayer *vtl, GdkEventMotion *event, VikViewport *vvp )
9882 {
9883   /* if we haven't sync'ed yet, we don't have time to do more. */
9884   if ( vtl->draw_sync_done && vtl->current_track && vtl->current_track->trackpoints ) {
9885     VikTrackpoint *last_tpt = vik_track_get_tp_last(vtl->current_track);
9886
9887     static GdkPixmap *pixmap = NULL;
9888     int w1, h1, w2, h2;
9889     // Need to check in case window has been resized
9890     w1 = vik_viewport_get_width(vvp);
9891     h1 = vik_viewport_get_height(vvp);
9892     if (!pixmap) {
9893       pixmap = gdk_pixmap_new ( gtk_widget_get_window(GTK_WIDGET(vvp)), w1, h1, -1 );
9894     }
9895     gdk_drawable_get_size (pixmap, &w2, &h2);
9896     if (w1 != w2 || h1 != h2) {
9897       g_object_unref ( G_OBJECT ( pixmap ) );
9898       pixmap = gdk_pixmap_new ( gtk_widget_get_window(GTK_WIDGET(vvp)), w1, h1, -1 );
9899     }
9900
9901     // Reset to background
9902     gdk_draw_drawable (pixmap,
9903                        vtl->current_track_newpoint_gc,
9904                        vik_viewport_get_pixmap(vvp),
9905                        0, 0, 0, 0, -1, -1);
9906
9907     draw_sync_t *passalong;
9908     gint x1, y1;
9909
9910     vik_viewport_coord_to_screen ( vvp, &(last_tpt->coord), &x1, &y1 );
9911
9912     // FOR SCREEN OVERLAYS WE MUST DRAW INTO THIS PIXMAP (when using the reset method)
9913     //  otherwise using vik_viewport_draw_* functions puts the data into the base pixmap,
9914     //  thus when we come to reset to the background it would include what we have already drawn!!
9915     gdk_draw_line ( pixmap,
9916                     vtl->current_track_newpoint_gc,
9917                     x1, y1, event->x, event->y );
9918     // Using this reset method is more reliable than trying to undraw previous efforts via the GDK_INVERT method
9919
9920     /* Find out actual distance of current track */
9921     gdouble distance = vik_track_get_length (vtl->current_track);
9922
9923     // Now add distance to where the pointer is //
9924     VikCoord coord;
9925     struct LatLon ll;
9926     vik_viewport_screen_to_coord ( vvp, (gint) event->x, (gint) event->y, &coord );
9927     vik_coord_to_latlon ( &coord, &ll );
9928     gdouble last_step = vik_coord_diff( &coord, &(last_tpt->coord));
9929     distance = distance + last_step;
9930
9931     // Get elevation data
9932     gdouble elev_gain, elev_loss;
9933     vik_track_get_total_elevation_gain ( vtl->current_track, &elev_gain, &elev_loss);
9934
9935     // Adjust elevation data (if available) for the current pointer position
9936     gdouble elev_new;
9937     elev_new = (gdouble) a_dems_get_elev_by_coord ( &coord, VIK_DEM_INTERPOL_BEST );
9938     if ( elev_new != VIK_DEM_INVALID_ELEVATION ) {
9939       if ( last_tpt->altitude != VIK_DEFAULT_ALTITUDE ) {
9940         // Adjust elevation of last track point
9941         if ( elev_new > last_tpt->altitude )
9942           // Going up
9943           elev_gain += elev_new - last_tpt->altitude;
9944         else
9945           // Going down
9946           elev_loss += last_tpt->altitude - elev_new;
9947       }
9948     }
9949
9950     //
9951     // Display of the distance 'tooltip' during track creation is controlled by a preference
9952     //
9953     if ( a_vik_get_create_track_tooltip() ) {
9954
9955       gchar *str = distance_string (distance);
9956
9957       PangoLayout *pl = gtk_widget_create_pango_layout (GTK_WIDGET(vvp), NULL);
9958       pango_layout_set_font_description (pl, gtk_widget_get_style(GTK_WIDGET(vvp))->font_desc);
9959       pango_layout_set_text (pl, str, -1);
9960       gint wd, hd;
9961       pango_layout_get_pixel_size ( pl, &wd, &hd );
9962
9963       gint xd,yd;
9964       // offset from cursor a bit depending on font size
9965       xd = event->x + 10;
9966       yd = event->y - hd;
9967
9968       // Create a background block to make the text easier to read over the background map
9969       GdkGC *background_block_gc = vik_viewport_new_gc ( vvp, "#cccccc", 1);
9970       gdk_draw_rectangle (pixmap, background_block_gc, TRUE, xd-2, yd-2, wd+4, hd+2);
9971       gdk_draw_layout (pixmap, vtl->current_track_newpoint_gc, xd, yd, pl);
9972
9973       g_object_unref ( G_OBJECT ( pl ) );
9974       g_object_unref ( G_OBJECT ( background_block_gc ) );
9975       g_free (str);
9976     }
9977
9978     passalong = g_new(draw_sync_t,1); // freed by draw_sync()
9979     passalong->vtl = vtl;
9980     passalong->pixmap = pixmap;
9981     passalong->drawable = gtk_widget_get_window(GTK_WIDGET(vvp));
9982     passalong->gc = vtl->current_track_newpoint_gc;
9983
9984     gdouble angle;
9985     gdouble baseangle;
9986     vik_viewport_compute_bearing ( vvp, x1, y1, event->x, event->y, &angle, &baseangle );
9987
9988     // Update statusbar with full gain/loss information
9989     statusbar_write (distance, elev_gain, elev_loss, last_step, angle, vtl);
9990
9991     // draw pixmap when we have time to
9992     g_idle_add_full (G_PRIORITY_HIGH_IDLE + 10, draw_sync, passalong, NULL);
9993     vtl->draw_sync_done = FALSE;
9994     return VIK_LAYER_TOOL_ACK_GRAB_FOCUS;
9995   }
9996   return VIK_LAYER_TOOL_ACK;
9997 }
9998
9999 // NB vtl->current_track must be valid
10000 static void undo_trackpoint_add ( VikTrwLayer *vtl )
10001 {
10002   // 'undo'
10003   if ( vtl->current_track->trackpoints ) {
10004     // TODO rework this...
10005     //vik_trackpoint_free ( vik_track_get_tp_last (vtl->current_track) );
10006     GList *last = g_list_last(vtl->current_track->trackpoints);
10007     g_free ( last->data );
10008     vtl->current_track->trackpoints = g_list_remove_link ( vtl->current_track->trackpoints, last );
10009
10010     vik_track_calculate_bounds ( vtl->current_track );
10011   }
10012 }
10013
10014 static gboolean tool_new_track_key_press ( VikTrwLayer *vtl, GdkEventKey *event, VikViewport *vvp )
10015 {
10016   if ( vtl->current_track && event->keyval == GDK_Escape ) {
10017     // Bin track if only one point as it's not very useful
10018     if ( vik_track_get_tp_count(vtl->current_track) == 1 ) {
10019       if ( vtl->current_track->is_route )
10020         vik_trw_layer_delete_route ( vtl, vtl->current_track );
10021       else
10022         vik_trw_layer_delete_track ( vtl, vtl->current_track );
10023     }
10024     vtl->current_track = NULL;
10025     vik_layer_emit_update ( VIK_LAYER(vtl) );
10026     return TRUE;
10027   } else if ( vtl->current_track && event->keyval == GDK_BackSpace ) {
10028     undo_trackpoint_add ( vtl );
10029     update_statusbar ( vtl );
10030     vik_layer_emit_update ( VIK_LAYER(vtl) );
10031     return TRUE;
10032   }
10033   return FALSE;
10034 }
10035
10036 /*
10037  * Common function to handle trackpoint button requests on either a route or a track
10038  *  . enables adding a point via normal click
10039  *  . enables removal of last point via right click
10040  *  . finishing of the track or route via double clicking
10041  */
10042 static gboolean tool_new_track_or_route_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
10043 {
10044   VikTrackpoint *tp;
10045
10046   if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
10047     return FALSE;
10048
10049   if ( event->button == 2 ) {
10050     // As the display is panning, the new track pixmap is now invalid so don't draw it
10051     //  otherwise this drawing done results in flickering back to an old image
10052     vtl->draw_sync_do = FALSE;
10053     return FALSE;
10054   }
10055
10056   if ( event->button == 3 )
10057   {
10058     if ( !vtl->current_track )
10059       return FALSE;
10060     undo_trackpoint_add ( vtl );
10061     update_statusbar ( vtl );
10062     vik_layer_emit_update ( VIK_LAYER(vtl) );
10063     return TRUE;
10064   }
10065
10066   if ( event->type == GDK_2BUTTON_PRESS )
10067   {
10068     /* subtract last (duplicate from double click) tp then end */
10069     if ( vtl->current_track && vtl->current_track->trackpoints && vtl->ct_x1 == vtl->ct_x2 && vtl->ct_y1 == vtl->ct_y2 )
10070     {
10071       /* undo last, then end */
10072       undo_trackpoint_add ( vtl );
10073       vtl->current_track = NULL;
10074     }
10075     vik_layer_emit_update ( VIK_LAYER(vtl) );
10076     return TRUE;
10077   }
10078
10079   tp = vik_trackpoint_new();
10080   vik_viewport_screen_to_coord ( vvp, event->x, event->y, &(tp->coord) );
10081
10082   /* snap to other TP */
10083   if ( event->state & GDK_CONTROL_MASK )
10084   {
10085     VikTrackpoint *other_tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
10086     if ( other_tp )
10087       tp->coord = other_tp->coord;
10088   }
10089
10090   tp->newsegment = FALSE;
10091   tp->has_timestamp = FALSE;
10092   tp->timestamp = 0;
10093
10094   if ( vtl->current_track ) {
10095     vik_track_add_trackpoint ( vtl->current_track, tp, TRUE ); // Ensure bounds is updated
10096     /* Auto attempt to get elevation from DEM data (if it's available) */
10097     vik_track_apply_dem_data_last_trackpoint ( vtl->current_track );
10098   }
10099
10100   vtl->ct_x1 = vtl->ct_x2;
10101   vtl->ct_y1 = vtl->ct_y2;
10102   vtl->ct_x2 = event->x;
10103   vtl->ct_y2 = event->y;
10104
10105   vik_layer_emit_update ( VIK_LAYER(vtl) );
10106   return TRUE;
10107 }
10108
10109 static gboolean tool_new_track_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
10110 {
10111   // if we were running the route finder, cancel it
10112   vtl->route_finder_started = FALSE;
10113
10114   // ----------------------------------------------------- if current is a route - switch to new track
10115   if ( event->button == 1 && ( ! vtl->current_track || (vtl->current_track && vtl->current_track->is_route ) ))
10116   {
10117     gchar *name = trw_layer_new_unique_sublayer_name(vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, _("Track"));
10118     if ( a_vik_get_ask_for_create_track_name() ) {
10119       name = a_dialog_new_track ( VIK_GTK_WINDOW_FROM_LAYER(vtl), name, FALSE );
10120       if ( !name )
10121         return FALSE;
10122     }
10123     new_track_create_common ( vtl, name );
10124     g_free ( name );
10125   }
10126   return tool_new_track_or_route_click ( vtl, event, vvp );
10127 }
10128
10129 static void tool_new_track_release ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
10130 {
10131   if ( event->button == 2 ) {
10132     // Pan moving ended - enable potential point drawing again
10133     vtl->draw_sync_do = TRUE;
10134     vtl->draw_sync_done = TRUE;
10135   }
10136 }
10137
10138 /*** New route ****/
10139
10140 static gpointer tool_new_route_create ( VikWindow *vw, VikViewport *vvp)
10141 {
10142   return vvp;
10143 }
10144
10145 static gboolean tool_new_route_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
10146 {
10147   // if we were running the route finder, cancel it
10148   vtl->route_finder_started = FALSE;
10149
10150   // -------------------------- if current is a track - switch to new route,
10151   if ( event->button == 1 && ( ! vtl->current_track ||
10152                                (vtl->current_track && !vtl->current_track->is_route ) ) )
10153   {
10154     gchar *name = trw_layer_new_unique_sublayer_name(vtl, VIK_TRW_LAYER_SUBLAYER_ROUTE, _("Route"));
10155     if ( a_vik_get_ask_for_create_track_name() ) {
10156       name = a_dialog_new_track ( VIK_GTK_WINDOW_FROM_LAYER(vtl), name, TRUE );
10157       if ( !name )
10158         return FALSE;
10159     }
10160     new_route_create_common ( vtl, name );
10161     g_free ( name );
10162   }
10163   return tool_new_track_or_route_click ( vtl, event, vvp );
10164 }
10165
10166 /*** New waypoint ****/
10167
10168 static gpointer tool_new_waypoint_create ( VikWindow *vw, VikViewport *vvp)
10169 {
10170   return vvp;
10171 }
10172
10173 static gboolean tool_new_waypoint_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
10174 {
10175   VikCoord coord;
10176   if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
10177     return FALSE;
10178   vik_viewport_screen_to_coord ( vvp, event->x, event->y, &coord );
10179   if ( vik_trw_layer_new_waypoint (vtl, VIK_GTK_WINDOW_FROM_LAYER(vtl), &coord) ) {
10180     trw_layer_calculate_bounds_waypoints ( vtl );
10181     if ( VIK_LAYER(vtl)->visible )
10182       vik_layer_emit_update ( VIK_LAYER(vtl) );
10183   }
10184   return TRUE;
10185 }
10186
10187
10188 /*** Edit trackpoint ****/
10189
10190 static gpointer tool_edit_trackpoint_create ( VikWindow *vw, VikViewport *vvp)
10191 {
10192   tool_ed_t *t = g_new(tool_ed_t, 1);
10193   t->vvp = vvp;
10194   t->holding = FALSE;
10195   return t;
10196 }
10197
10198 static void tool_edit_trackpoint_destroy ( tool_ed_t *t )
10199 {
10200   g_free ( t );
10201 }
10202
10203 /**
10204  * tool_edit_trackpoint_click:
10205  *
10206  * On 'initial' click: search for the nearest trackpoint or routepoint and store it as the current trackpoint
10207  * Then update the viewport, statusbar and edit dialog to draw the point as being selected and it's information.
10208  * On subsequent clicks: (as the current trackpoint is defined) and the click is very near the same point
10209  *  then initiate the move operation to drag the point to a new destination.
10210  * NB The current trackpoint will get reset elsewhere.
10211  */
10212 static gboolean tool_edit_trackpoint_click ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data )
10213 {
10214   tool_ed_t *t = data;
10215   VikViewport *vvp = t->vvp;
10216   TPSearchParams params;
10217   params.vvp = vvp;
10218   params.x = event->x;
10219   params.y = event->y;
10220   params.closest_track_id = NULL;
10221   params.closest_tp = NULL;
10222   params.closest_tpl = NULL;
10223   vik_viewport_get_min_max_lat_lon ( vvp, &(params.bbox.south), &(params.bbox.north), &(params.bbox.west), &(params.bbox.east) );
10224
10225   if ( event->button != 1 ) 
10226     return FALSE;
10227
10228   if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
10229     return FALSE;
10230
10231   if ( !vtl->vl.visible || !(vtl->tracks_visible || vtl->routes_visible) )
10232     return FALSE;
10233
10234   if ( vtl->current_tpl )
10235   {
10236     /* 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.) */
10237     VikTrackpoint *tp = VIK_TRACKPOINT(vtl->current_tpl->data);
10238     VikTrack *current_tr = VIK_TRACK(g_hash_table_lookup(vtl->tracks, vtl->current_tp_id));
10239     if ( !current_tr )
10240       current_tr = VIK_TRACK(g_hash_table_lookup(vtl->routes, vtl->current_tp_id));
10241     if ( !current_tr )
10242       return FALSE;
10243
10244     gint x, y;
10245     vik_viewport_coord_to_screen ( vvp, &(tp->coord), &x, &y );
10246
10247     if ( current_tr->visible && 
10248          abs(x - (int)round(event->x)) < TRACKPOINT_SIZE_APPROX &&
10249          abs(y - (int)round(event->y)) < TRACKPOINT_SIZE_APPROX ) {
10250       marker_begin_move ( t, event->x, event->y );
10251       return TRUE;
10252     }
10253
10254   }
10255
10256   if ( vtl->tracks_visible )
10257     g_hash_table_foreach ( vtl->tracks, (GHFunc) track_search_closest_tp, &params);
10258
10259   if ( params.closest_tp )
10260   {
10261     vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, g_hash_table_lookup ( vtl->tracks_iters, params.closest_track_id ), TRUE );
10262     vtl->current_tpl = params.closest_tpl;
10263     vtl->current_tp_id = params.closest_track_id;
10264     vtl->current_tp_track = g_hash_table_lookup ( vtl->tracks, params.closest_track_id );
10265     trw_layer_tpwin_init ( vtl );
10266     set_statusbar_msg_info_trkpt ( vtl, params.closest_tp );
10267     vik_layer_emit_update ( VIK_LAYER(vtl) );
10268     return TRUE;
10269   }
10270
10271   if ( vtl->routes_visible )
10272     g_hash_table_foreach ( vtl->routes, (GHFunc) track_search_closest_tp, &params);
10273
10274   if ( params.closest_tp )
10275   {
10276     vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, g_hash_table_lookup ( vtl->routes_iters, params.closest_track_id ), TRUE );
10277     vtl->current_tpl = params.closest_tpl;
10278     vtl->current_tp_id = params.closest_track_id;
10279     vtl->current_tp_track = g_hash_table_lookup ( vtl->routes, params.closest_track_id );
10280     trw_layer_tpwin_init ( vtl );
10281     set_statusbar_msg_info_trkpt ( vtl, params.closest_tp );
10282     vik_layer_emit_update ( VIK_LAYER(vtl) );
10283     return TRUE;
10284   }
10285
10286   /* these aren't the droids you're looking for */
10287   return FALSE;
10288 }
10289
10290 static gboolean tool_edit_trackpoint_move ( VikTrwLayer *vtl, GdkEventMotion *event, gpointer data )
10291 {
10292   tool_ed_t *t = data;
10293   VikViewport *vvp = t->vvp;
10294
10295   if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
10296     return FALSE;
10297
10298   if ( t->holding )
10299   {
10300     VikCoord new_coord;
10301     vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
10302
10303     /* snap to TP */
10304     if ( event->state & GDK_CONTROL_MASK )
10305     {
10306       VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
10307       if ( tp && tp != vtl->current_tpl->data )
10308         new_coord = tp->coord;
10309     }
10310     //    VIK_TRACKPOINT(vtl->current_tpl->data)->coord = new_coord;
10311     { 
10312       gint x, y;
10313       vik_viewport_coord_to_screen ( vvp, &new_coord, &x, &y );
10314       marker_moveto ( t, x, y );
10315     } 
10316
10317     return TRUE;
10318   }
10319   return FALSE;
10320 }
10321
10322 static gboolean tool_edit_trackpoint_release ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data )
10323 {
10324   tool_ed_t *t = data;
10325   VikViewport *vvp = t->vvp;
10326
10327   if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
10328     return FALSE;
10329   if ( event->button != 1) 
10330     return FALSE;
10331
10332   if ( t->holding ) {
10333     VikCoord new_coord;
10334     vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
10335
10336     /* snap to TP */
10337     if ( event->state & GDK_CONTROL_MASK )
10338     {
10339       VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
10340       if ( tp && tp != vtl->current_tpl->data )
10341         new_coord = tp->coord;
10342     }
10343
10344     VIK_TRACKPOINT(vtl->current_tpl->data)->coord = new_coord;
10345     if ( vtl->current_tp_track )
10346       vik_track_calculate_bounds ( vtl->current_tp_track );
10347
10348     marker_end_move ( t );
10349
10350     /* diff dist is diff from orig */
10351     if ( vtl->tpwin )
10352       if ( vtl->current_tp_track )
10353         my_tpwin_set_tp ( vtl );
10354
10355     vik_layer_emit_update ( VIK_LAYER(vtl) );
10356     return TRUE;
10357   }
10358   return FALSE;
10359 }
10360
10361
10362 /*** Extended Route Finder ***/
10363
10364 static gpointer tool_extended_route_finder_create ( VikWindow *vw, VikViewport *vvp)
10365 {
10366   return vvp;
10367 }
10368
10369 static void tool_extended_route_finder_undo ( VikTrwLayer *vtl )
10370 {
10371   VikCoord *new_end;
10372   new_end = vik_track_cut_back_to_double_point ( vtl->current_track );
10373   if ( new_end ) {
10374     g_free ( new_end );
10375     vik_layer_emit_update ( VIK_LAYER(vtl) );
10376
10377     /* remove last ' to:...' */
10378     if ( vtl->current_track->comment ) {
10379       gchar *last_to = strrchr ( vtl->current_track->comment, 't' );
10380       if ( last_to && (last_to - vtl->current_track->comment > 1) ) {
10381         gchar *new_comment = g_strndup ( vtl->current_track->comment,
10382                                          last_to - vtl->current_track->comment - 1);
10383         vik_track_set_comment_no_copy ( vtl->current_track, new_comment );
10384       }
10385     }
10386   }
10387 }
10388
10389
10390 static gboolean tool_extended_route_finder_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
10391 {
10392   VikCoord tmp;
10393   if ( !vtl ) return FALSE;
10394   vik_viewport_screen_to_coord ( vvp, event->x, event->y, &tmp );
10395   if ( event->button == 3 && vtl->current_track ) {
10396     tool_extended_route_finder_undo ( vtl );
10397   }
10398   else if ( event->button == 2 ) {
10399      vtl->draw_sync_do = FALSE;
10400      return FALSE;
10401   }
10402   // if we started the track but via undo deleted all the track points, begin again
10403   else if ( vtl->current_track && vtl->current_track->is_route && ! vik_track_get_tp_first ( vtl->current_track ) ) {
10404     return tool_new_track_or_route_click ( vtl, event, vvp );
10405   }
10406   else if ( ( vtl->current_track && vtl->current_track->is_route ) ||
10407             ( event->state & GDK_CONTROL_MASK && vtl->current_track ) ) {
10408     struct LatLon start, end;
10409
10410     VikTrackpoint *tp_start = vik_track_get_tp_last ( vtl->current_track );
10411     vik_coord_to_latlon ( &(tp_start->coord), &start );
10412     vik_coord_to_latlon ( &(tmp), &end );
10413
10414     vtl->route_finder_started = TRUE;
10415     vtl->route_finder_append = TRUE;  // merge tracks. keep started true.
10416
10417     // update UI to let user know what's going on
10418     VikStatusbar *sb = vik_window_get_statusbar (VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)));
10419     VikRoutingEngine *engine = vik_routing_default_engine ( );
10420     if ( ! engine ) {
10421         vik_statusbar_set_message ( sb, VIK_STATUSBAR_INFO, "Cannot plan route without a default routing engine." );
10422         return TRUE;
10423     }
10424     gchar *msg = g_strdup_printf ( _("Querying %s for route between (%.3f, %.3f) and (%.3f, %.3f)."),
10425                                    vik_routing_engine_get_label ( engine ),
10426                                    start.lat, start.lon, end.lat, end.lon );
10427     vik_statusbar_set_message ( sb, VIK_STATUSBAR_INFO, msg );
10428     g_free ( msg );
10429     vik_window_set_busy_cursor ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)) );
10430
10431
10432     /* Give GTK a change to display the new status bar before querying the web */
10433     while ( gtk_events_pending ( ) )
10434         gtk_main_iteration ( );
10435
10436     gboolean find_status = vik_routing_default_find ( vtl, start, end );
10437
10438     /* Update UI to say we're done */
10439     vik_window_clear_busy_cursor ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)) );
10440     msg = ( find_status ) ? g_strdup_printf ( _("%s returned route between (%.3f, %.3f) and (%.3f, %.3f)."),
10441                             vik_routing_engine_get_label ( engine ),
10442                             start.lat, start.lon, end.lat, end.lon )
10443                           : g_strdup_printf ( _("Error getting route from %s."),
10444                                               vik_routing_engine_get_label ( engine ) );
10445     vik_statusbar_set_message ( sb, VIK_STATUSBAR_INFO, msg );
10446     g_free ( msg );
10447
10448     vik_layer_emit_update ( VIK_LAYER(vtl) );
10449   } else {
10450     vtl->current_track = NULL;
10451
10452     // create a new route where we will add the planned route to
10453     gboolean ret = tool_new_route_click( vtl, event, vvp );
10454
10455     vtl->route_finder_started = TRUE;
10456
10457     return ret;
10458   }
10459   return TRUE;
10460 }
10461
10462 static gboolean tool_extended_route_finder_key_press ( VikTrwLayer *vtl, GdkEventKey *event, VikViewport *vvp )
10463 {
10464   if ( vtl->current_track && event->keyval == GDK_Escape ) {
10465     vtl->route_finder_started = FALSE;
10466     vtl->current_track = NULL;
10467     vik_layer_emit_update ( VIK_LAYER(vtl) );
10468     return TRUE;
10469   } else if ( vtl->current_track && event->keyval == GDK_BackSpace ) {
10470     tool_extended_route_finder_undo ( vtl );
10471   }
10472   return FALSE;
10473 }
10474
10475
10476
10477 /*** Show picture ****/
10478
10479 static gpointer tool_show_picture_create ( VikWindow *vw, VikViewport *vvp)
10480 {
10481   return vvp;
10482 }
10483
10484 /* Params are: vvp, event, last match found or NULL */
10485 static void tool_show_picture_wp ( const gpointer id, VikWaypoint *wp, gpointer params[3] )
10486 {
10487   if ( wp->image && wp->visible )
10488   {
10489     gint x, y, slackx, slacky;
10490     GdkEventButton *event = (GdkEventButton *) params[1];
10491
10492     vik_viewport_coord_to_screen ( VIK_VIEWPORT(params[0]), &(wp->coord), &x, &y );
10493     slackx = wp->image_width / 2;
10494     slacky = wp->image_height / 2;
10495     if (    x <= event->x + slackx && x >= event->x - slackx
10496          && y <= event->y + slacky && y >= event->y - slacky )
10497     {
10498       params[2] = wp->image; /* we've found a match. however continue searching
10499                               * since we want to find the last match -- that
10500                               * is, the match that was drawn last. */
10501     }
10502   }
10503 }
10504
10505 static void trw_layer_show_picture ( menu_array_sublayer values )
10506 {
10507   /* thanks to the Gaim people for showing me ShellExecute and g_spawn_command_line_async */
10508 #ifdef WINDOWS
10509   ShellExecute(NULL, "open", (char *) values[MA_MISC], NULL, NULL, SW_SHOWNORMAL);
10510 #else /* WINDOWS */
10511   GError *err = NULL;
10512   gchar *quoted_file = g_shell_quote ( (gchar *) values[MA_MISC] );
10513   gchar *cmd = g_strdup_printf ( "%s %s", a_vik_get_image_viewer(), quoted_file );
10514   g_free ( quoted_file );
10515   if ( ! g_spawn_command_line_async ( cmd, &err ) )
10516     {
10517       a_dialog_error_msg_extra ( VIK_GTK_WINDOW_FROM_LAYER(values[MA_VTL]), _("Could not launch %s to open file."), a_vik_get_image_viewer() );
10518       g_error_free ( err );
10519     }
10520   g_free ( cmd );
10521 #endif /* WINDOWS */
10522 }
10523
10524 static gboolean tool_show_picture_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
10525 {
10526   gpointer params[3] = { vvp, event, NULL };
10527   if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
10528     return FALSE;
10529   g_hash_table_foreach ( vtl->waypoints, (GHFunc) tool_show_picture_wp, params );
10530   if ( params[2] )
10531   {
10532     static menu_array_sublayer values;
10533     values[MA_VTL] = vtl;
10534     values[MA_MISC] = params[2];
10535     trw_layer_show_picture ( values );
10536     return TRUE; /* found a match */
10537   }
10538   else
10539     return FALSE; /* go through other layers, searching for a match */
10540 }
10541
10542 /***************************************************************************
10543  ** End tool code 
10544  ***************************************************************************/
10545
10546
10547 static void image_wp_make_list ( const gpointer id, VikWaypoint *wp, GSList **pics )
10548 {
10549   if ( wp->image && ( ! a_thumbnails_exists ( wp->image ) ) )
10550     *pics = g_slist_append ( *pics, (gpointer) g_strdup ( wp->image ) );
10551 }
10552
10553 /* Structure for thumbnail creating data used in the background thread */
10554 typedef struct {
10555   VikTrwLayer *vtl; // Layer needed for redrawing
10556   GSList *pics;     // Image list
10557 } thumbnail_create_thread_data;
10558
10559 static int create_thumbnails_thread ( thumbnail_create_thread_data *tctd, gpointer threaddata )
10560 {
10561   guint total = g_slist_length(tctd->pics), done = 0;
10562   while ( tctd->pics )
10563   {
10564     a_thumbnails_create ( (gchar *) tctd->pics->data );
10565     int result = a_background_thread_progress ( threaddata, ((gdouble) ++done) / total );
10566     if ( result != 0 )
10567       return -1; /* Abort thread */
10568
10569     tctd->pics = tctd->pics->next;
10570   }
10571
10572   // Redraw to show the thumbnails as they are now created
10573   if ( IS_VIK_LAYER(tctd->vtl) )
10574     vik_layer_emit_update ( VIK_LAYER(tctd->vtl) ); // NB update from background thread
10575
10576   return 0;
10577 }
10578
10579 static void thumbnail_create_thread_free ( thumbnail_create_thread_data *tctd )
10580 {
10581   while ( tctd->pics )
10582   {
10583     g_free ( tctd->pics->data );
10584     tctd->pics = g_slist_delete_link ( tctd->pics, tctd->pics );
10585   }
10586   g_free ( tctd );
10587 }
10588
10589 void trw_layer_verify_thumbnails ( VikTrwLayer *vtl )
10590 {
10591   if ( ! vtl->has_verified_thumbnails )
10592   {
10593     GSList *pics = NULL;
10594     g_hash_table_foreach ( vtl->waypoints, (GHFunc) image_wp_make_list, &pics );
10595     if ( pics )
10596     {
10597       gint len = g_slist_length ( pics );
10598       gchar *tmp = g_strdup_printf ( _("Creating %d Image Thumbnails..."), len );
10599       thumbnail_create_thread_data *tctd = g_malloc ( sizeof(thumbnail_create_thread_data) );
10600       tctd->vtl = vtl;
10601       tctd->pics = pics;
10602       a_background_thread ( BACKGROUND_POOL_LOCAL,
10603                             VIK_GTK_WINDOW_FROM_LAYER(vtl),
10604                             tmp,
10605                             (vik_thr_func) create_thumbnails_thread,
10606                             tctd,
10607                             (vik_thr_free_func) thumbnail_create_thread_free,
10608                             NULL,
10609                             len );
10610       g_free ( tmp );
10611     }
10612   }
10613 }
10614
10615 static const gchar* my_track_colors ( gint ii )
10616 {
10617   static const gchar* colors[VIK_TRW_LAYER_TRACK_GCS] = {
10618     "#2d870a",
10619     "#135D34",
10620     "#0a8783",
10621     "#0e4d87",
10622     "#05469f",
10623     "#695CBB",
10624     "#2d059f",
10625     "#4a059f",
10626     "#5A171A",
10627     "#96059f"
10628   };
10629   // Fast and reliable way of returning a colour
10630   return colors[(ii % VIK_TRW_LAYER_TRACK_GCS)];
10631 }
10632
10633 static void trw_layer_track_alloc_colors ( VikTrwLayer *vtl )
10634 {
10635   GHashTableIter iter;
10636   gpointer key, value;
10637
10638   gint ii = 0;
10639   // Tracks
10640   g_hash_table_iter_init ( &iter, vtl->tracks );
10641
10642   while ( g_hash_table_iter_next (&iter, &key, &value) ) {
10643
10644     // Tracks get a random spread of colours if not already assigned
10645     if ( ! VIK_TRACK(value)->has_color ) {
10646       if ( vtl->drawmode == DRAWMODE_ALL_SAME_COLOR )
10647         VIK_TRACK(value)->color = vtl->track_color;
10648       else {
10649         gdk_color_parse ( my_track_colors (ii), &(VIK_TRACK(value)->color) );
10650       }
10651       VIK_TRACK(value)->has_color = TRUE;
10652     }
10653
10654     trw_layer_update_treeview ( vtl, VIK_TRACK(value) );
10655
10656     ii++;
10657     if (ii > VIK_TRW_LAYER_TRACK_GCS)
10658       ii = 0;
10659   }
10660
10661   // Routes
10662   ii = 0;
10663   g_hash_table_iter_init ( &iter, vtl->routes );
10664
10665   while ( g_hash_table_iter_next (&iter, &key, &value) ) {
10666
10667     // Routes get an intermix of reds
10668     if ( ! VIK_TRACK(value)->has_color ) {
10669       if ( ii )
10670         gdk_color_parse ( "#FF0000" , &(VIK_TRACK(value)->color) ); // Red
10671       else
10672         gdk_color_parse ( "#B40916" , &(VIK_TRACK(value)->color) ); // Dark Red
10673       VIK_TRACK(value)->has_color = TRUE;
10674     }
10675
10676     trw_layer_update_treeview ( vtl, VIK_TRACK(value) );
10677
10678     ii = !ii;
10679   }
10680 }
10681
10682 /*
10683  * (Re)Calculate the bounds of the waypoints in this layer,
10684  * This should be called whenever waypoints are changed
10685  */
10686 void trw_layer_calculate_bounds_waypoints ( VikTrwLayer *vtl )
10687 {
10688   struct LatLon topleft = { 0.0, 0.0 };
10689   struct LatLon bottomright = { 0.0, 0.0 };
10690   struct LatLon ll;
10691
10692   GHashTableIter iter;
10693   gpointer key, value;
10694
10695   g_hash_table_iter_init ( &iter, vtl->waypoints );
10696
10697   // Set bounds to first point
10698   if ( g_hash_table_iter_next (&iter, &key, &value) ) {
10699     vik_coord_to_latlon ( &(VIK_WAYPOINT(value)->coord), &topleft );
10700     vik_coord_to_latlon ( &(VIK_WAYPOINT(value)->coord), &bottomright );
10701   }
10702
10703   // Ensure there is another point...
10704   if ( g_hash_table_size ( vtl->waypoints ) > 1 ) {
10705
10706     while ( g_hash_table_iter_next (&iter, &key, &value) ) {
10707
10708       // See if this point increases the bounds.
10709       vik_coord_to_latlon ( &(VIK_WAYPOINT(value)->coord), &ll );
10710
10711       if ( ll.lat > topleft.lat) topleft.lat = ll.lat;
10712       if ( ll.lon < topleft.lon) topleft.lon = ll.lon;
10713       if ( ll.lat < bottomright.lat) bottomright.lat = ll.lat;
10714       if ( ll.lon > bottomright.lon) bottomright.lon = ll.lon;
10715     }
10716   }
10717
10718   vtl->waypoints_bbox.north = topleft.lat;
10719   vtl->waypoints_bbox.east = bottomright.lon;
10720   vtl->waypoints_bbox.south = bottomright.lat;
10721   vtl->waypoints_bbox.west = topleft.lon;
10722 }
10723
10724 static void trw_layer_calculate_bounds_track ( gpointer id, VikTrack *trk )
10725 {
10726   vik_track_calculate_bounds ( trk );
10727 }
10728
10729 void trw_layer_calculate_bounds_tracks ( VikTrwLayer *vtl )
10730 {
10731   g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_calculate_bounds_track, NULL );
10732   g_hash_table_foreach ( vtl->routes, (GHFunc) trw_layer_calculate_bounds_track, NULL );
10733 }
10734
10735 static void trw_layer_sort_all ( VikTrwLayer *vtl )
10736 {
10737   if ( ! VIK_LAYER(vtl)->vt )
10738     return;
10739
10740   // Obviously need 2 to tango - sorting with only 1 (or less) is a lonely activity!
10741   if ( g_hash_table_size (vtl->tracks) > 1 )
10742     vik_treeview_sort_children ( VIK_LAYER(vtl)->vt, &(vtl->tracks_iter), vtl->track_sort_order );
10743
10744   if ( g_hash_table_size (vtl->routes) > 1 )
10745     vik_treeview_sort_children ( VIK_LAYER(vtl)->vt, &(vtl->routes_iter), vtl->track_sort_order );
10746
10747   if ( g_hash_table_size (vtl->waypoints) > 1 )
10748     vik_treeview_sort_children ( VIK_LAYER(vtl)->vt, &(vtl->waypoints_iter), vtl->wp_sort_order );
10749 }
10750
10751 /**
10752  * Get the earliest timestamp available from all tracks
10753  */
10754 static time_t trw_layer_get_timestamp_tracks ( VikTrwLayer *vtl )
10755 {
10756   time_t timestamp = 0;
10757   GList *gl = g_hash_table_get_values ( vtl->tracks );
10758   gl = g_list_sort ( gl, vik_track_compare_timestamp );
10759   gl = g_list_first ( gl );
10760
10761   if ( gl ) {
10762     // Only need to check the first track as they have been sorted by time
10763     VikTrack *trk = (VikTrack*)gl->data;
10764     // Assume trackpoints already sorted by time
10765     VikTrackpoint *tpt = vik_track_get_tp_first(trk);
10766     if ( tpt && tpt->has_timestamp ) {
10767       timestamp = tpt->timestamp;
10768     }
10769     g_list_free ( gl );
10770   }
10771   return timestamp;
10772 }
10773
10774 /**
10775  * Get the earliest timestamp available from all waypoints
10776  */
10777 static time_t trw_layer_get_timestamp_waypoints ( VikTrwLayer *vtl )
10778 {
10779   time_t timestamp = 0;
10780   GList *gl = g_hash_table_get_values ( vtl->waypoints );
10781   GList *iter;
10782   for (iter = g_list_first (gl); iter != NULL; iter = g_list_next (iter)) {
10783     VikWaypoint *wpt = (VikWaypoint*)iter->data;
10784     if ( wpt->has_timestamp ) {
10785       // When timestamp not set yet - use the first value encountered
10786       if ( timestamp == 0 )
10787         timestamp = wpt->timestamp;
10788       else if ( timestamp > wpt->timestamp )
10789         timestamp = wpt->timestamp;
10790     }
10791   }
10792   g_list_free ( gl );
10793
10794   return timestamp;
10795 }
10796
10797 /**
10798  * Get the earliest timestamp available for this layer
10799  */
10800 static time_t trw_layer_get_timestamp ( VikTrwLayer *vtl )
10801 {
10802   time_t timestamp_tracks = trw_layer_get_timestamp_tracks ( vtl );
10803   time_t timestamp_waypoints = trw_layer_get_timestamp_waypoints ( vtl );
10804   // NB routes don't have timestamps - hence they are not considered
10805
10806   if ( !timestamp_tracks && !timestamp_waypoints ) {
10807     // Fallback to get time from the metadata when no other timestamps available
10808     GTimeVal gtv;
10809     if  ( vtl->metadata && vtl->metadata->timestamp && g_time_val_from_iso8601 ( vtl->metadata->timestamp, &gtv ) )
10810       return gtv.tv_sec;
10811   }
10812   if ( timestamp_tracks && !timestamp_waypoints )
10813     return timestamp_tracks;
10814   if ( timestamp_tracks && timestamp_waypoints && (timestamp_tracks < timestamp_waypoints) )
10815     return timestamp_tracks;
10816   return timestamp_waypoints;
10817 }
10818
10819 static void trw_layer_post_read ( VikTrwLayer *vtl, VikViewport *vvp, gboolean from_file )
10820 {
10821   if ( VIK_LAYER(vtl)->realized )
10822     trw_layer_verify_thumbnails ( vtl );
10823   trw_layer_track_alloc_colors ( vtl );
10824
10825   trw_layer_calculate_bounds_waypoints ( vtl );
10826   trw_layer_calculate_bounds_tracks ( vtl );
10827
10828   // Apply treeview sort after loading all the tracks for this layer
10829   //  (rather than sorted insert on each individual track additional)
10830   //  and after subsequent changes to the properties as the specified order may have changed.
10831   //  since the sorting of a treeview section is now very quick
10832   // NB sorting is also performed after every name change as well to maintain the list order
10833   trw_layer_sort_all ( vtl );
10834
10835   // Setting metadata time if not otherwise set
10836   if ( vtl->metadata ) {
10837
10838     gboolean need_to_set_time = TRUE;
10839     if ( vtl->metadata->timestamp ) {
10840       need_to_set_time = FALSE;
10841       if ( !g_strcmp0(vtl->metadata->timestamp, "" ) )
10842         need_to_set_time = TRUE;
10843     }
10844
10845     if ( need_to_set_time ) {
10846       GTimeVal timestamp;
10847       timestamp.tv_usec = 0;
10848       timestamp.tv_sec = trw_layer_get_timestamp ( vtl );
10849
10850       // No time found - so use 'now' for the metadata time
10851       if ( timestamp.tv_sec == 0 ) {
10852         g_get_current_time ( &timestamp );
10853       }
10854
10855       vtl->metadata->timestamp = g_time_val_to_iso8601 ( &timestamp );
10856     }
10857   }
10858 }
10859
10860 VikCoordMode vik_trw_layer_get_coord_mode ( VikTrwLayer *vtl )
10861 {
10862   return vtl->coord_mode;
10863 }
10864
10865 /**
10866  * Uniquify the whole layer
10867  * Also requires the layers panel as the names shown there need updating too
10868  * Returns whether the operation was successful or not
10869  */
10870 gboolean vik_trw_layer_uniquify ( VikTrwLayer *vtl, VikLayersPanel *vlp )
10871 {
10872   if ( vtl && vlp ) {
10873     vik_trw_layer_uniquify_tracks ( vtl, vlp, vtl->tracks, TRUE );
10874     vik_trw_layer_uniquify_tracks ( vtl, vlp, vtl->routes, FALSE );
10875     vik_trw_layer_uniquify_waypoints ( vtl, vlp );
10876     return TRUE;
10877   }
10878   return FALSE;
10879 }
10880
10881 static void waypoint_convert ( const gpointer id, VikWaypoint *wp, VikCoordMode *dest_mode )
10882 {
10883   vik_coord_convert ( &(wp->coord), *dest_mode );
10884 }
10885
10886 static void track_convert ( const gpointer id, VikTrack *tr, VikCoordMode *dest_mode )
10887 {
10888   vik_track_convert ( tr, *dest_mode );
10889 }
10890
10891 static void trw_layer_change_coord_mode ( VikTrwLayer *vtl, VikCoordMode dest_mode )
10892 {
10893   if ( vtl->coord_mode != dest_mode )
10894   {
10895     vtl->coord_mode = dest_mode;
10896     g_hash_table_foreach ( vtl->waypoints, (GHFunc) waypoint_convert, &dest_mode );
10897     g_hash_table_foreach ( vtl->tracks, (GHFunc) track_convert, &dest_mode );
10898     g_hash_table_foreach ( vtl->routes, (GHFunc) track_convert, &dest_mode );
10899   }
10900 }
10901
10902 static void trw_layer_set_menu_selection ( VikTrwLayer *vtl, guint16 selection )
10903 {
10904   vtl->menu_selection = selection;
10905 }
10906
10907 static guint16 trw_layer_get_menu_selection ( VikTrwLayer *vtl )
10908 {
10909   return (vtl->menu_selection);
10910 }
10911
10912 /* ----------- Downloading maps along tracks --------------- */
10913
10914 static int get_download_area_width(VikViewport *vvp, gdouble zoom_level, struct LatLon *wh)
10915 {
10916   /* TODO: calculating based on current size of viewport */
10917   const gdouble w_at_zoom_0_125 = 0.0013;
10918   const gdouble h_at_zoom_0_125 = 0.0011;
10919   gdouble zoom_factor = zoom_level/0.125;
10920
10921   wh->lat = h_at_zoom_0_125 * zoom_factor;
10922   wh->lon = w_at_zoom_0_125 * zoom_factor;
10923
10924   return 0;   /* all OK */
10925 }
10926
10927 static VikCoord *get_next_coord(VikCoord *from, VikCoord *to, struct LatLon *dist, gdouble gradient)
10928 {
10929   if ((dist->lon >= ABS(to->east_west - from->east_west)) &&
10930       (dist->lat >= ABS(to->north_south - from->north_south)))
10931     return NULL;
10932
10933   VikCoord *coord = g_malloc(sizeof(VikCoord));
10934   coord->mode = VIK_COORD_LATLON;
10935
10936   if (ABS(gradient) < 1) {
10937     if (from->east_west > to->east_west)
10938       coord->east_west = from->east_west - dist->lon;
10939     else
10940       coord->east_west = from->east_west + dist->lon;
10941     coord->north_south = gradient * (coord->east_west - from->east_west) + from->north_south;
10942   } else {
10943     if (from->north_south > to->north_south)
10944       coord->north_south = from->north_south - dist->lat;
10945     else
10946       coord->north_south = from->north_south + dist->lat;
10947     coord->east_west = (1/gradient) * (coord->north_south - from->north_south) + from->north_south;
10948   }
10949
10950   return coord;
10951 }
10952
10953 static GList *add_fillins(GList *list, VikCoord *from, VikCoord *to, struct LatLon *dist)
10954 {
10955   /* TODO: handle virtical track (to->east_west - from->east_west == 0) */
10956   gdouble gradient = (to->north_south - from->north_south)/(to->east_west - from->east_west);
10957
10958   VikCoord *next = from;
10959   while (TRUE) {
10960     if ((next = get_next_coord(next, to, dist, gradient)) == NULL)
10961         break;
10962     list = g_list_prepend(list, next);
10963   }
10964
10965   return list;
10966 }
10967
10968 void vik_track_download_map(VikTrack *tr, VikMapsLayer *vml, VikViewport *vvp, gdouble zoom_level)
10969 {
10970   typedef struct _Rect {
10971     VikCoord tl;
10972     VikCoord br;
10973     VikCoord center;
10974   } Rect;
10975 #define GLRECT(iter) ((Rect *)((iter)->data))
10976
10977   struct LatLon wh;
10978   GList *rects_to_download = NULL;
10979   GList *rect_iter;
10980
10981   if (get_download_area_width(vvp, zoom_level, &wh))
10982     return;
10983
10984   GList *iter = tr->trackpoints;
10985   if (!iter)
10986     return;
10987
10988   gboolean new_map = TRUE;
10989   VikCoord *cur_coord, tl, br;
10990   Rect *rect;
10991   while (iter) {
10992     cur_coord = &(VIK_TRACKPOINT(iter->data))->coord;
10993     if (new_map) {
10994       vik_coord_set_area(cur_coord, &wh, &tl, &br);
10995       rect = g_malloc(sizeof(Rect));
10996       rect->tl = tl;
10997       rect->br = br;
10998       rect->center = *cur_coord;
10999       rects_to_download = g_list_prepend(rects_to_download, rect);
11000       new_map = FALSE;
11001       iter = iter->next;
11002       continue;
11003     }
11004     gboolean found = FALSE;
11005     for (rect_iter = rects_to_download; rect_iter; rect_iter = rect_iter->next) {
11006       if (vik_coord_inside(cur_coord, &GLRECT(rect_iter)->tl, &GLRECT(rect_iter)->br)) {
11007         found = TRUE;
11008         break;
11009       }
11010     }
11011     if (found)
11012         iter = iter->next;
11013     else
11014       new_map = TRUE;
11015   }
11016
11017   GList *fillins = NULL;
11018   /* 'fillin' doesn't work in UTM mode - potentially ending up in massive loop continually allocating memory - hence don't do it */
11019   /* seems that ATM the function get_next_coord works only for LATLON */
11020   if ( cur_coord->mode == VIK_COORD_LATLON ) {
11021     /* fill-ins for far apart points */
11022     GList *cur_rect, *next_rect;
11023     for (cur_rect = rects_to_download;
11024          (next_rect = cur_rect->next) != NULL;
11025          cur_rect = cur_rect->next) {
11026       if ((wh.lon < ABS(GLRECT(cur_rect)->center.east_west - GLRECT(next_rect)->center.east_west)) ||
11027           (wh.lat < ABS(GLRECT(cur_rect)->center.north_south - GLRECT(next_rect)->center.north_south))) {
11028         fillins = add_fillins(fillins, &GLRECT(cur_rect)->center, &GLRECT(next_rect)->center, &wh);
11029       }
11030     }
11031   } else
11032     g_message("%s: this feature works only in Mercator mode", __FUNCTION__);
11033
11034   if (fillins) {
11035     GList *fiter = fillins;
11036     while (fiter) {
11037       cur_coord = (VikCoord *)(fiter->data);
11038       vik_coord_set_area(cur_coord, &wh, &tl, &br);
11039       rect = g_malloc(sizeof(Rect));
11040       rect->tl = tl;
11041       rect->br = br;
11042       rect->center = *cur_coord;
11043       rects_to_download = g_list_prepend(rects_to_download, rect);
11044       fiter = fiter->next;
11045     }
11046   }
11047
11048   for (rect_iter = rects_to_download; rect_iter; rect_iter = rect_iter->next) {
11049     vik_maps_layer_download_section (vml, vvp, &(((Rect *)(rect_iter->data))->tl), &(((Rect *)(rect_iter->data))->br), zoom_level);
11050   }
11051
11052   if (fillins) {
11053     for (iter = fillins; iter; iter = iter->next)
11054       g_free(iter->data);
11055     g_list_free(fillins);
11056   }
11057   if (rects_to_download) {
11058     for (rect_iter = rects_to_download; rect_iter; rect_iter = rect_iter->next)
11059       g_free(rect_iter->data);
11060     g_list_free(rects_to_download);
11061   }
11062 }
11063
11064 static void trw_layer_download_map_along_track_cb ( menu_array_sublayer values )
11065 {
11066   VikMapsLayer *vml;
11067   gint selected_map;
11068   gchar *zoomlist[] = {"0.125", "0.25", "0.5", "1", "2", "4", "8", "16", "32", "64", "128", "256", "512", "1024", NULL };
11069   gdouble zoom_vals[] = {0.125, 0.25, 0.5, 1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024};
11070   gint selected_zoom, default_zoom;
11071
11072   VikTrwLayer *vtl = values[MA_VTL];
11073   VikLayersPanel *vlp = values[MA_VLP];
11074   VikTrack *trk;
11075   if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
11076     trk = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
11077   else
11078     trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
11079   if ( !trk )
11080     return;
11081
11082   VikViewport *vvp = vik_window_viewport((VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl)));
11083
11084   GList *vmls = vik_layers_panel_get_all_layers_of_type(vlp, VIK_LAYER_MAPS, TRUE); // Includes hidden map layer types
11085   int num_maps = g_list_length(vmls);
11086
11087   if (!num_maps) {
11088     a_dialog_error_msg(VIK_GTK_WINDOW_FROM_LAYER(vtl), _("No map layer in use. Create one first") );
11089     return;
11090   }
11091
11092   // Convert from list of vmls to list of names. Allowing the user to select one of them
11093   gchar **map_names = g_malloc_n(1 + num_maps, sizeof(gpointer));
11094   VikMapsLayer **map_layers = g_malloc_n(1 + num_maps, sizeof(gpointer));
11095
11096   gchar **np = map_names;
11097   VikMapsLayer **lp = map_layers;
11098   int i;
11099   for (i = 0; i < num_maps; i++) {
11100     vml = (VikMapsLayer *)(vmls->data);
11101     *lp++ = vml;
11102     *np++ = vik_maps_layer_get_map_label(vml);
11103     vmls = vmls->next;
11104   }
11105   // Mark end of the array lists
11106   *lp = NULL;
11107   *np = NULL;
11108
11109   gdouble cur_zoom = vik_viewport_get_zoom(vvp);
11110   for (default_zoom = 0; default_zoom < G_N_ELEMENTS(zoom_vals); default_zoom++) {
11111     if (cur_zoom == zoom_vals[default_zoom])
11112       break;
11113   }
11114   default_zoom = (default_zoom == G_N_ELEMENTS(zoom_vals)) ? G_N_ELEMENTS(zoom_vals) - 1 : default_zoom;
11115
11116   if (!a_dialog_map_n_zoom(VIK_GTK_WINDOW_FROM_LAYER(vtl), map_names, 0, zoomlist, default_zoom, &selected_map, &selected_zoom))
11117     goto done;
11118
11119   vik_track_download_map(trk, map_layers[selected_map], vvp, zoom_vals[selected_zoom]);
11120
11121 done:
11122   for (i = 0; i < num_maps; i++)
11123     g_free(map_names[i]);
11124   g_free(map_names);
11125   g_free(map_layers);
11126
11127   g_list_free(vmls);
11128
11129 }
11130
11131 /**** lowest waypoint number calculation ***/
11132 static gint highest_wp_number_name_to_number(const gchar *name) {
11133   if ( strlen(name) == 3 ) {
11134     int n = atoi(name);
11135     if ( n < 100 && name[0] != '0' )
11136       return -1;
11137     if ( n < 10 && name[0] != '0' )
11138       return -1;
11139     return n;
11140   }
11141   return -1;
11142 }
11143
11144
11145 static void highest_wp_number_reset(VikTrwLayer *vtl)
11146 {
11147   vtl->highest_wp_number = 0;
11148 }
11149
11150 static void highest_wp_number_add_wp(VikTrwLayer *vtl, const gchar *new_wp_name)
11151 {
11152   /* if is bigger that top, add it */
11153   gint new_wp_num = highest_wp_number_name_to_number(new_wp_name);
11154   if ( new_wp_num > vtl->highest_wp_number )
11155     vtl->highest_wp_number = new_wp_num;
11156 }
11157
11158 static void highest_wp_number_remove_wp(VikTrwLayer *vtl, const gchar *old_wp_name)
11159 {
11160   /* if wasn't top, do nothing. if was top, count backwards until we find one used */
11161   gint old_wp_num = highest_wp_number_name_to_number(old_wp_name);
11162   if ( vtl->highest_wp_number == old_wp_num ) {
11163     gchar buf[4];
11164     vtl->highest_wp_number--;
11165
11166     g_snprintf(buf,4,"%03d", vtl->highest_wp_number );
11167     /* search down until we find something that *does* exist */
11168
11169     while ( vtl->highest_wp_number > 0 && ! vik_trw_layer_get_waypoint ( vtl, buf )) {
11170       vtl->highest_wp_number--;
11171       g_snprintf(buf,4,"%03d", vtl->highest_wp_number );
11172     }
11173   }
11174 }
11175
11176 /* get lowest unused number */
11177 static gchar *highest_wp_number_get(VikTrwLayer *vtl)
11178 {
11179   gchar buf[4];
11180   if ( vtl->highest_wp_number < 0 || vtl->highest_wp_number >= 999 )
11181     return NULL;
11182   g_snprintf(buf,4,"%03d", vtl->highest_wp_number+1 );
11183   return g_strdup(buf);
11184 }
11185
11186 /**
11187  * trw_layer_create_track_list_both:
11188  *
11189  * Create the latest list of tracks and routes
11190  */
11191 static GList* trw_layer_create_track_list_both ( VikLayer *vl, gpointer user_data )
11192 {
11193   VikTrwLayer *vtl = VIK_TRW_LAYER(vl);
11194   GList *tracks = NULL;
11195   tracks = g_list_concat ( tracks, g_hash_table_get_values ( vik_trw_layer_get_tracks ( vtl ) ) );
11196   tracks = g_list_concat ( tracks, g_hash_table_get_values ( vik_trw_layer_get_routes ( vtl ) ) );
11197
11198   return vik_trw_layer_build_track_list_t ( vtl, tracks );
11199 }
11200
11201 static void trw_layer_track_list_dialog_single ( menu_array_sublayer values )
11202 {
11203   VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
11204
11205   gchar *title = NULL;
11206   if ( GPOINTER_TO_INT(values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_TRACKS )
11207     title = g_strdup_printf ( _("%s: Track List"), VIK_LAYER(vtl)->name );
11208   else
11209     title = g_strdup_printf ( _("%s: Route List"), VIK_LAYER(vtl)->name );
11210
11211   vik_trw_layer_track_list_show_dialog ( title, VIK_LAYER(vtl), values[MA_SUBTYPE], trw_layer_create_track_list, FALSE );
11212   g_free ( title );
11213 }
11214
11215 static void trw_layer_track_list_dialog ( menu_array_layer values )
11216 {
11217   VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
11218
11219   gchar *title = g_strdup_printf ( _("%s: Track and Route List"), VIK_LAYER(vtl)->name );
11220   vik_trw_layer_track_list_show_dialog ( title, VIK_LAYER(vtl), NULL, trw_layer_create_track_list_both, FALSE );
11221   g_free ( title );
11222 }
11223
11224 static void trw_layer_waypoint_list_dialog ( menu_array_layer values )
11225 {
11226   VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
11227
11228   gchar *title = g_strdup_printf ( _("%s: Waypoint List"), VIK_LAYER(vtl)->name );
11229   vik_trw_layer_waypoint_list_show_dialog ( title, VIK_LAYER(vtl), NULL, trw_layer_create_waypoint_list, FALSE );
11230   g_free ( title );
11231 }