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