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