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