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