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