]> git.street.me.uk Git - andy/viking.git/blob - src/viktrwlayer.c
Fix menu use of named own Icons since the icons were renamed some time ago.
[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   gint16 elev = a_dems_get_elev_by_coord ( &(wp->coord), VIK_DEM_INTERPOL_BEST );
2948   if ( elev != VIK_DEM_INVALID_ELEVATION )
2949     wp->altitude = (gdouble)elev;
2950
2951   returned_name = a_dialog_waypoint ( w, default_name, vtl, wp, vtl->coord_mode, TRUE, &updated );
2952
2953   if ( returned_name )
2954   {
2955     wp->visible = TRUE;
2956     vik_trw_layer_add_waypoint ( vtl, returned_name, wp );
2957     g_free (default_name);
2958     g_free (returned_name);
2959     return TRUE;
2960   }
2961   g_free (default_name);
2962   vik_waypoint_free(wp);
2963   return FALSE;
2964 }
2965
2966 static void trw_layer_new_wikipedia_wp_viewport ( gpointer lav[2] )
2967 {
2968   struct LatLon maxmin[2] = { {0.0,0.0}, {0.0,0.0} };
2969   VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
2970   VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
2971   VikWindow *vw = (VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl));
2972   VikViewport *vvp =  vik_window_viewport(vw);
2973
2974   // Note the order is max part first then min part - thus reverse order of use in min_max function:
2975   vik_viewport_get_min_max_lat_lon ( vvp, &maxmin[1].lat, &maxmin[0].lat, &maxmin[1].lon, &maxmin[0].lon );
2976   a_geonames_wikipedia_box((VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl)), vtl, maxmin);
2977   trw_layer_calculate_bounds_waypoints ( vtl );
2978   vik_layers_panel_emit_update ( vlp );
2979 }
2980
2981 static void trw_layer_new_wikipedia_wp_layer ( gpointer lav[2] )
2982 {
2983   VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
2984   VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
2985   struct LatLon maxmin[2] = { {0.0,0.0}, {0.0,0.0} };
2986   
2987   trw_layer_find_maxmin (vtl, maxmin);
2988   a_geonames_wikipedia_box((VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl)), vtl, maxmin);
2989   trw_layer_calculate_bounds_waypoints ( vtl );
2990   vik_layers_panel_emit_update ( vlp );
2991 }
2992
2993 #ifdef VIK_CONFIG_GEOTAG
2994 static void trw_layer_geotagging_waypoint_mtime_keep ( gpointer pass_along[6] )
2995 {
2996   VikWaypoint *wp = g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->waypoints, pass_along[3] );
2997   if ( wp )
2998     // Update directly - not changing the mtime
2999     a_geotag_write_exif_gps ( wp->image, wp->coord, wp->altitude, TRUE );
3000 }
3001
3002 static void trw_layer_geotagging_waypoint_mtime_update ( gpointer pass_along[6] )
3003 {
3004   VikWaypoint *wp = g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->waypoints, pass_along[3] );
3005   if ( wp )
3006     // Update directly
3007     a_geotag_write_exif_gps ( wp->image, wp->coord, wp->altitude, FALSE );
3008 }
3009
3010 /*
3011  * Use code in separate file for this feature as reasonably complex
3012  */
3013 static void trw_layer_geotagging_track ( gpointer pass_along[6] )
3014 {
3015   VikTrwLayer *vtl = VIK_TRW_LAYER(pass_along[0]);
3016   VikTrack *track = g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, pass_along[3] );
3017   // Unset so can be reverified later if necessary
3018   vtl->has_verified_thumbnails = FALSE;
3019
3020   trw_layer_geotag_dialog ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
3021                             vtl,
3022                             track,
3023                             track->name );
3024 }
3025
3026 static void trw_layer_geotagging ( gpointer lav[2] )
3027 {
3028   VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
3029   // Unset so can be reverified later if necessary
3030   vtl->has_verified_thumbnails = FALSE;
3031
3032   trw_layer_geotag_dialog ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
3033                             vtl,
3034                             NULL,
3035                             NULL);
3036 }
3037 #endif
3038
3039 // 'Acquires' - Same as in File Menu -> Acquire - applies into the selected TRW Layer //
3040
3041 /*
3042  * Acquire into this TRW Layer straight from GPS Device
3043  */
3044 static void trw_layer_acquire_gps_cb ( gpointer lav[2] )
3045 {
3046   VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
3047   VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
3048   VikWindow *vw = (VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl));
3049   VikViewport *vvp =  vik_window_viewport(vw);
3050
3051   vik_datasource_gps_interface.mode = VIK_DATASOURCE_ADDTOLAYER;
3052   a_acquire ( vw, vlp, vvp, &vik_datasource_gps_interface, NULL, NULL );
3053 }
3054
3055 /*
3056  * Acquire into this TRW Layer from Directions
3057  */
3058 static void trw_layer_acquire_routing_cb ( gpointer lav[2] )
3059 {
3060   VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
3061   VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
3062   VikWindow *vw = (VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl));
3063   VikViewport *vvp =  vik_window_viewport(vw);
3064
3065   a_acquire ( vw, vlp, vvp, &vik_datasource_routing_interface, NULL, NULL );
3066 }
3067
3068 #ifdef VIK_CONFIG_OPENSTREETMAP
3069 /*
3070  * Acquire into this TRW Layer from OSM
3071  */
3072 static void trw_layer_acquire_osm_cb ( gpointer lav[2] )
3073 {
3074   VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
3075   VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
3076   VikWindow *vw = (VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl));
3077   VikViewport *vvp =  vik_window_viewport(vw);
3078
3079   a_acquire ( vw, vlp, vvp, &vik_datasource_osm_interface, NULL, NULL );
3080 }
3081
3082 /**
3083  * Acquire into this TRW Layer from OSM for 'My' Traces
3084  */
3085 static void trw_layer_acquire_osm_my_traces_cb ( gpointer lav[2] )
3086 {
3087   VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
3088   VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
3089   VikWindow *vw = (VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl));
3090   VikViewport *vvp =  vik_window_viewport(vw);
3091
3092   a_acquire ( vw, vlp, vvp, &vik_datasource_osm_my_traces_interface, NULL, NULL );
3093 }
3094 #endif
3095
3096 #ifdef VIK_CONFIG_GEOCACHES
3097 /*
3098  * Acquire into this TRW Layer from Geocaching.com
3099  */
3100 static void trw_layer_acquire_geocache_cb ( gpointer lav[2] )
3101 {
3102   VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
3103   VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
3104   VikWindow *vw = (VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl));
3105   VikViewport *vvp =  vik_window_viewport(vw);
3106
3107   a_acquire ( vw, vlp, vvp, &vik_datasource_gc_interface, NULL, NULL );
3108 }
3109 #endif
3110
3111 #ifdef VIK_CONFIG_GEOTAG
3112 /*
3113  * Acquire into this TRW Layer from images
3114  */
3115 static void trw_layer_acquire_geotagged_cb ( gpointer lav[2] )
3116 {
3117   VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
3118   VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
3119   VikWindow *vw = (VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl));
3120   VikViewport *vvp =  vik_window_viewport(vw);
3121
3122   vik_datasource_geotag_interface.mode = VIK_DATASOURCE_ADDTOLAYER;
3123   a_acquire ( vw, vlp, vvp, &vik_datasource_geotag_interface, NULL, NULL );
3124
3125   // Reverify thumbnails as they may have changed
3126   vtl->has_verified_thumbnails = FALSE;
3127   trw_layer_verify_thumbnails ( vtl, NULL );
3128 }
3129 #endif
3130
3131 static void trw_layer_gps_upload ( gpointer lav[2] )
3132 {
3133   gpointer pass_along[6];
3134   pass_along[0] = lav[0];
3135   pass_along[1] = lav[1];
3136   pass_along[2] = NULL; // No track - operate on the layer
3137   pass_along[3] = NULL;
3138   pass_along[4] = NULL;
3139   pass_along[5] = NULL;
3140
3141   trw_layer_gps_upload_any ( pass_along );
3142 }
3143
3144 /**
3145  * If pass_along[3] is defined that this will upload just that track
3146  */
3147 static void trw_layer_gps_upload_any ( gpointer pass_along[6] )
3148 {
3149   VikTrwLayer *vtl = VIK_TRW_LAYER(pass_along[0]);
3150   VikLayersPanel *vlp = VIK_LAYERS_PANEL(pass_along[1]);
3151
3152   // May not actually get a track here as pass_along[2&3] can be null
3153   VikTrack *track = NULL;
3154   vik_gps_xfer_type xfer_type = TRK; // VIK_TRW_LAYER_SUBLAYER_TRACKS = 0 so hard to test different from NULL!
3155   gboolean xfer_all = FALSE;
3156
3157   if ( pass_along[2] ) {
3158     xfer_all = FALSE;
3159     if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE ) {
3160       track = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
3161       xfer_type = RTE;
3162     }
3163     else if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_TRACK ) {
3164       track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
3165       xfer_type = TRK;
3166     }
3167     else if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_WAYPOINTS ) {
3168       xfer_type = WPT;
3169     }
3170     else if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTES ) {
3171       xfer_type = RTE;
3172     }
3173   }
3174   else if ( !pass_along[4] )
3175     xfer_all = TRUE; // i.e. whole layer
3176
3177   if (track && !track->visible) {
3178     a_dialog_error_msg ( VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Can not upload invisible track.") );
3179     return;
3180   }
3181
3182   GtkWidget *dialog = gtk_dialog_new_with_buttons ( _("GPS Upload"),
3183                                                     VIK_GTK_WINDOW_FROM_LAYER(pass_along[0]),
3184                                                     GTK_DIALOG_DESTROY_WITH_PARENT,
3185                                                     GTK_STOCK_OK,
3186                                                     GTK_RESPONSE_ACCEPT,
3187                                                     GTK_STOCK_CANCEL,
3188                                                     GTK_RESPONSE_REJECT,
3189                                                     NULL );
3190
3191   gtk_dialog_set_default_response ( GTK_DIALOG(dialog), GTK_RESPONSE_ACCEPT );
3192   GtkWidget *response_w = NULL;
3193 #if GTK_CHECK_VERSION (2, 20, 0)
3194   response_w = gtk_dialog_get_widget_for_response ( GTK_DIALOG(dialog), GTK_RESPONSE_ACCEPT );
3195 #endif
3196
3197   if ( response_w )
3198     gtk_widget_grab_focus ( response_w );
3199
3200   gpointer dgs = datasource_gps_setup ( dialog, xfer_type, xfer_all );
3201
3202   if ( gtk_dialog_run ( GTK_DIALOG(dialog) ) != GTK_RESPONSE_ACCEPT ) {
3203     datasource_gps_clean_up ( dgs );
3204     gtk_widget_destroy ( dialog );
3205     return;
3206   }
3207
3208   // Get info from reused datasource dialog widgets
3209   gchar* protocol = datasource_gps_get_protocol ( dgs );
3210   gchar* port = datasource_gps_get_descriptor ( dgs );
3211   // NB don't free the above strings as they're references to values held elsewhere
3212   gboolean do_tracks = datasource_gps_get_do_tracks ( dgs );
3213   gboolean do_routes = datasource_gps_get_do_routes ( dgs );
3214   gboolean do_waypoints = datasource_gps_get_do_waypoints ( dgs );
3215   gboolean turn_off = datasource_gps_get_off ( dgs );
3216
3217   gtk_widget_destroy ( dialog );
3218
3219   // When called from the viewport - work the corresponding layerspanel:
3220   if ( !vlp ) {
3221     vlp = vik_window_layers_panel ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)) );
3222   }
3223
3224   // Apply settings to transfer to the GPS device
3225   vik_gps_comm ( vtl,
3226                  track,
3227                  GPS_UP,
3228                  protocol,
3229                  port,
3230                  FALSE,
3231                  vik_layers_panel_get_viewport (vlp),
3232                  vlp,
3233                  do_tracks,
3234                  do_routes,
3235                  do_waypoints,
3236                  turn_off );
3237 }
3238
3239 /*
3240  * Acquire into this TRW Layer from any GPS Babel supported file
3241  */
3242 static void trw_layer_acquire_file_cb ( gpointer lav[2] )
3243 {
3244   VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
3245   VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
3246   VikWindow *vw = (VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl));
3247   VikViewport *vvp =  vik_window_viewport(vw);
3248
3249   a_acquire ( vw, vlp, vvp, &vik_datasource_file_interface, NULL, NULL );
3250 }
3251
3252 static void trw_layer_new_wp ( gpointer lav[2] )
3253 {
3254   VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
3255   VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
3256   /* TODO longone: okay, if layer above (aggregate) is invisible but vtl->visible is true, this redraws for no reason.
3257      instead return true if you want to update. */
3258   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 ) {
3259     trw_layer_calculate_bounds_waypoints ( vtl );
3260     vik_layers_panel_emit_update ( vlp );
3261   }
3262 }
3263
3264 static void new_track_create_common ( VikTrwLayer *vtl, gchar *name )
3265 {
3266   vtl->current_track = vik_track_new();
3267   vtl->current_track->visible = TRUE;
3268   if ( vtl->drawmode == DRAWMODE_ALL_SAME_COLOR )
3269     // Create track with the preferred colour from the layer properties
3270     vtl->current_track->color = vtl->track_color;
3271   else
3272     gdk_color_parse ( "#000000", &(vtl->current_track->color) );
3273   vtl->current_track->has_color = TRUE;
3274   vik_trw_layer_add_track ( vtl, name, vtl->current_track );
3275 }
3276
3277 static void trw_layer_new_track ( gpointer lav[2] )
3278 {
3279   VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
3280
3281   if ( ! vtl->current_track ) {
3282     gchar *name = trw_layer_new_unique_sublayer_name ( vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, _("Track")) ;
3283     new_track_create_common ( vtl, name );
3284     g_free ( name );
3285
3286     vik_window_enable_layer_tool ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)), VIK_LAYER_TRW, TOOL_CREATE_TRACK );
3287   }
3288 }
3289
3290 static void new_route_create_common ( VikTrwLayer *vtl, gchar *name )
3291 {
3292   vtl->current_track = vik_track_new();
3293   vtl->current_track->visible = TRUE;
3294   vtl->current_track->is_route = TRUE;
3295   // By default make all routes red
3296   vtl->current_track->has_color = TRUE;
3297   gdk_color_parse ( "red", &vtl->current_track->color );
3298   vik_trw_layer_add_route ( vtl, name, vtl->current_track );
3299 }
3300
3301 static void trw_layer_new_route ( gpointer lav[2] )
3302 {
3303   VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
3304
3305   if ( ! vtl->current_track ) {
3306     gchar *name = trw_layer_new_unique_sublayer_name ( vtl, VIK_TRW_LAYER_SUBLAYER_ROUTE, _("Route")) ;
3307     new_route_create_common ( vtl, name );
3308     g_free ( name );
3309     vik_window_enable_layer_tool ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)), VIK_LAYER_TRW, TOOL_CREATE_ROUTE );
3310   }
3311 }
3312
3313 static void trw_layer_auto_routes_view ( gpointer lav[2] )
3314 {
3315   VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
3316   VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
3317
3318   if ( g_hash_table_size (vtl->routes) > 0 ) {
3319     struct LatLon maxmin[2] = { {0,0}, {0,0} };
3320     g_hash_table_foreach ( vtl->routes, (GHFunc) trw_layer_find_maxmin_tracks, maxmin );
3321     trw_layer_zoom_to_show_latlons ( vtl, vik_layers_panel_get_viewport (vlp), maxmin );
3322     vik_layers_panel_emit_update ( vlp );
3323   }
3324 }
3325
3326
3327 static void trw_layer_finish_track ( gpointer lav[2] )
3328 {
3329   VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
3330   vtl->current_track = NULL;
3331   vik_layer_emit_update ( VIK_LAYER(vtl) );
3332 }
3333
3334 static void trw_layer_auto_tracks_view ( gpointer lav[2] )
3335 {
3336   VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
3337   VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
3338
3339   if ( g_hash_table_size (vtl->tracks) > 0 ) {
3340     struct LatLon maxmin[2] = { {0,0}, {0,0} };
3341     g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_find_maxmin_tracks, maxmin );
3342     trw_layer_zoom_to_show_latlons ( vtl, vik_layers_panel_get_viewport (vlp), maxmin );
3343     vik_layers_panel_emit_update ( vlp );
3344   }
3345 }
3346
3347 static void trw_layer_single_waypoint_jump ( const gpointer id, const VikWaypoint *wp, gpointer vvp )
3348 {
3349   /* NB do not care if wp is visible or not */
3350   vik_viewport_set_center_coord ( VIK_VIEWPORT(vvp), &(wp->coord) );
3351 }
3352
3353 static void trw_layer_auto_waypoints_view ( gpointer lav[2] )
3354 {
3355   VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
3356   VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
3357
3358   /* Only 1 waypoint - jump straight to it */
3359   if ( g_hash_table_size (vtl->waypoints) == 1 ) {
3360     VikViewport *vvp = vik_layers_panel_get_viewport (vlp);
3361     g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_single_waypoint_jump, (gpointer) vvp );
3362   }
3363   /* If at least 2 waypoints - find center and then zoom to fit */
3364   else if ( g_hash_table_size (vtl->waypoints) > 1 )
3365   {
3366     struct LatLon maxmin[2] = { {0,0}, {0,0} };
3367     maxmin[0].lat = vtl->waypoints_bbox.north;
3368     maxmin[1].lat = vtl->waypoints_bbox.south;
3369     maxmin[0].lon = vtl->waypoints_bbox.east;
3370     maxmin[1].lon = vtl->waypoints_bbox.west;
3371     trw_layer_zoom_to_show_latlons ( vtl, vik_layers_panel_get_viewport (vlp), maxmin );
3372   }
3373
3374   vik_layers_panel_emit_update ( vlp );
3375 }
3376
3377 static void trw_layer_add_menu_items ( VikTrwLayer *vtl, GtkMenu *menu, gpointer vlp )
3378 {
3379   static gpointer pass_along[2];
3380   GtkWidget *item;
3381   GtkWidget *export_submenu;
3382   pass_along[0] = vtl;
3383   pass_along[1] = vlp;
3384
3385   item = gtk_menu_item_new();
3386   gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
3387   gtk_widget_show ( item );
3388
3389   if ( vtl->current_track ) {
3390     if ( vtl->current_track->is_route )
3391       item = gtk_menu_item_new_with_mnemonic ( _("_Finish Route") );
3392     else
3393       item = gtk_menu_item_new_with_mnemonic ( _("_Finish Track") );
3394     g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_finish_track), pass_along );
3395     gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3396     gtk_widget_show ( item );
3397
3398     // Add separator
3399     item = gtk_menu_item_new ();
3400     gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
3401     gtk_widget_show ( item );
3402   }
3403
3404   /* Now with icons */
3405   item = gtk_image_menu_item_new_with_mnemonic ( _("_View Layer") );
3406   gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ZOOM_FIT, GTK_ICON_SIZE_MENU) );
3407   g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_view), pass_along );
3408   gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3409   gtk_widget_show ( item );
3410
3411   GtkWidget *view_submenu = gtk_menu_new();
3412   item = gtk_image_menu_item_new_with_mnemonic ( _("V_iew") );
3413   gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_FIND, GTK_ICON_SIZE_MENU) );
3414   gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3415   gtk_widget_show ( item );
3416   gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), view_submenu );
3417
3418   item = gtk_menu_item_new_with_mnemonic ( _("View All _Tracks") );
3419   g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_tracks_view), pass_along );
3420   gtk_menu_shell_append (GTK_MENU_SHELL (view_submenu), item);
3421   gtk_widget_show ( item );
3422
3423   item = gtk_menu_item_new_with_mnemonic ( _("View All _Routes") );
3424   g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_routes_view), pass_along );
3425   gtk_menu_shell_append (GTK_MENU_SHELL (view_submenu), item);
3426   gtk_widget_show ( item );
3427
3428   item = gtk_menu_item_new_with_mnemonic ( _("View All _Waypoints") );
3429   g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_waypoints_view), pass_along );
3430   gtk_menu_shell_append (GTK_MENU_SHELL (view_submenu), item);
3431   gtk_widget_show ( item );
3432
3433   item = gtk_image_menu_item_new_with_mnemonic ( _("_Goto Center of Layer") );
3434   gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_JUMP_TO, GTK_ICON_SIZE_MENU) );
3435   g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_centerize), pass_along );
3436   gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3437   gtk_widget_show ( item );
3438
3439   item = gtk_menu_item_new_with_mnemonic ( _("Goto _Waypoint...") );
3440   g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_wp), pass_along );
3441   gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3442   gtk_widget_show ( item );
3443
3444   export_submenu = gtk_menu_new ();
3445   item = gtk_image_menu_item_new_with_mnemonic ( _("_Export Layer") );
3446   gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_HARDDISK, GTK_ICON_SIZE_MENU) );
3447   gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3448   gtk_widget_show ( item );
3449   gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), export_submenu );
3450   
3451   item = gtk_menu_item_new_with_mnemonic ( _("Export as GPS_Point...") );
3452   g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_gpspoint), pass_along );
3453   gtk_menu_shell_append (GTK_MENU_SHELL (export_submenu), item);
3454   gtk_widget_show ( item );
3455
3456   item = gtk_menu_item_new_with_mnemonic ( _("Export as GPS_Mapper...") );
3457   g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_gpsmapper), pass_along );
3458   gtk_menu_shell_append (GTK_MENU_SHELL (export_submenu), item);
3459   gtk_widget_show ( item );
3460
3461   item = gtk_menu_item_new_with_mnemonic ( _("Export as _GPX...") );
3462   g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_gpx), pass_along );
3463   gtk_menu_shell_append (GTK_MENU_SHELL (export_submenu), item);
3464   gtk_widget_show ( item );
3465
3466   item = gtk_menu_item_new_with_mnemonic ( _("Export as _KML...") );
3467   g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_kml), pass_along );
3468   gtk_menu_shell_append (GTK_MENU_SHELL (export_submenu), item);
3469   gtk_widget_show ( item );
3470
3471   gchar* external1 = g_strconcat ( _("Open with External Program_1: "), a_vik_get_external_gpx_program_1(), NULL );
3472   item = gtk_menu_item_new_with_mnemonic ( external1 );
3473   g_free ( external1 );
3474   g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_external_gpx_1), pass_along );
3475   gtk_menu_shell_append (GTK_MENU_SHELL (export_submenu), item);
3476   gtk_widget_show ( item );
3477
3478   gchar* external2 = g_strconcat ( _("Open with External Program_2: "), a_vik_get_external_gpx_program_2(), NULL );
3479   item = gtk_menu_item_new_with_mnemonic ( external2 );
3480   g_free ( external2 );
3481   g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_external_gpx_2), pass_along );
3482   gtk_menu_shell_append (GTK_MENU_SHELL (export_submenu), item);
3483   gtk_widget_show ( item );
3484
3485   GtkWidget *new_submenu = gtk_menu_new();
3486   item = gtk_image_menu_item_new_with_mnemonic ( _("_New") );
3487   gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NEW, GTK_ICON_SIZE_MENU) );
3488   gtk_menu_shell_append(GTK_MENU_SHELL (menu), item);
3489   gtk_widget_show(item);
3490   gtk_menu_item_set_submenu(GTK_MENU_ITEM(item), new_submenu);
3491
3492   item = gtk_image_menu_item_new_with_mnemonic ( _("New _Waypoint...") );
3493   gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NEW, GTK_ICON_SIZE_MENU) );
3494   g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_wp), pass_along );
3495   gtk_menu_shell_append (GTK_MENU_SHELL (new_submenu), item);
3496   gtk_widget_show ( item );
3497
3498   item = gtk_image_menu_item_new_with_mnemonic ( _("New _Track") );
3499   gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NEW, GTK_ICON_SIZE_MENU) );
3500   g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_track), pass_along );
3501   gtk_menu_shell_append (GTK_MENU_SHELL (new_submenu), item);
3502   gtk_widget_show ( item );
3503   // Make it available only when a new track *not* already in progress
3504   gtk_widget_set_sensitive ( item, ! (gboolean)GPOINTER_TO_INT(vtl->current_track) );
3505
3506   item = gtk_image_menu_item_new_with_mnemonic ( _("New _Route") );
3507   gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NEW, GTK_ICON_SIZE_MENU) );
3508   g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_route), pass_along );
3509   gtk_menu_shell_append (GTK_MENU_SHELL (new_submenu), item);
3510   gtk_widget_show ( item );
3511   // Make it available only when a new track *not* already in progress
3512   gtk_widget_set_sensitive ( item, ! (gboolean)GPOINTER_TO_INT(vtl->current_track) );
3513
3514 #ifdef VIK_CONFIG_GEOTAG
3515   item = gtk_menu_item_new_with_mnemonic ( _("Geotag _Images...") );
3516   g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_geotagging), pass_along );
3517   gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3518   gtk_widget_show ( item );
3519 #endif
3520
3521   GtkWidget *acquire_submenu = gtk_menu_new ();
3522   item = gtk_image_menu_item_new_with_mnemonic ( _("_Acquire") );
3523   gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_DOWN, GTK_ICON_SIZE_MENU) );
3524   gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3525   gtk_widget_show ( item );
3526   gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), acquire_submenu );
3527   
3528   item = gtk_menu_item_new_with_mnemonic ( _("From _GPS...") );
3529   g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_acquire_gps_cb), pass_along );
3530   gtk_menu_shell_append (GTK_MENU_SHELL (acquire_submenu), item);
3531   gtk_widget_show ( item );
3532
3533   /* FIXME: only add menu when at least a routing engine has support for Directions */
3534   item = gtk_menu_item_new_with_mnemonic ( _("From _Directions...") );
3535   g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_acquire_routing_cb), pass_along );
3536   gtk_menu_shell_append (GTK_MENU_SHELL (acquire_submenu), item);
3537   gtk_widget_show ( item );
3538
3539 #ifdef VIK_CONFIG_OPENSTREETMAP
3540   item = gtk_menu_item_new_with_mnemonic ( _("From _OSM Traces...") );
3541   g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_acquire_osm_cb), pass_along );
3542   gtk_menu_shell_append (GTK_MENU_SHELL (acquire_submenu), item);
3543   gtk_widget_show ( item );
3544
3545   item = gtk_menu_item_new_with_mnemonic ( _("From _My OSM Traces...") );
3546   g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_acquire_osm_my_traces_cb), pass_along );
3547   gtk_menu_shell_append (GTK_MENU_SHELL (acquire_submenu), item);
3548   gtk_widget_show ( item );
3549 #endif
3550
3551 #ifdef VIK_CONFIG_GEONAMES
3552   GtkWidget *wikipedia_submenu = gtk_menu_new();
3553   item = gtk_image_menu_item_new_with_mnemonic ( _("From _Wikipedia Waypoints") );
3554   gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ADD, GTK_ICON_SIZE_MENU) );
3555   gtk_menu_shell_append(GTK_MENU_SHELL (acquire_submenu), item);
3556   gtk_widget_show(item);
3557   gtk_menu_item_set_submenu(GTK_MENU_ITEM(item), wikipedia_submenu);
3558
3559   item = gtk_image_menu_item_new_with_mnemonic ( _("Within _Layer Bounds") );
3560   gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ZOOM_FIT, GTK_ICON_SIZE_MENU) );
3561   g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_wikipedia_wp_layer), pass_along );
3562   gtk_menu_shell_append (GTK_MENU_SHELL (wikipedia_submenu), item);
3563   gtk_widget_show ( item );
3564
3565   item = gtk_image_menu_item_new_with_mnemonic ( _("Within _Current View") );
3566   gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ZOOM_100, GTK_ICON_SIZE_MENU) );
3567   g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_wikipedia_wp_viewport), pass_along );
3568   gtk_menu_shell_append (GTK_MENU_SHELL (wikipedia_submenu), item);
3569   gtk_widget_show ( item );
3570 #endif
3571
3572 #ifdef VIK_CONFIG_GEOCACHES
3573   item = gtk_menu_item_new_with_mnemonic ( _("From Geo_caching...") );
3574   g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_acquire_geocache_cb), pass_along );
3575   gtk_menu_shell_append (GTK_MENU_SHELL (acquire_submenu), item);
3576   gtk_widget_show ( item );
3577 #endif
3578
3579 #ifdef VIK_CONFIG_GEOTAG
3580   item = gtk_menu_item_new_with_mnemonic ( _("From Geotagged _Images...") );
3581   g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_acquire_geotagged_cb), pass_along );
3582   gtk_menu_shell_append (GTK_MENU_SHELL (acquire_submenu), item);
3583   gtk_widget_show ( item );
3584 #endif
3585
3586   item = gtk_menu_item_new_with_mnemonic ( _("From _File...") );
3587   g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_acquire_file_cb), pass_along );
3588   gtk_menu_shell_append (GTK_MENU_SHELL (acquire_submenu), item);
3589   gtk_widget_show ( item );
3590
3591   vik_ext_tool_datasources_add_menu_items_to_menu ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)), GTK_MENU (acquire_submenu) );
3592
3593   GtkWidget *upload_submenu = gtk_menu_new ();
3594   item = gtk_image_menu_item_new_with_mnemonic ( _("_Upload") );
3595   gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_UP, GTK_ICON_SIZE_MENU) );
3596   gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3597   gtk_widget_show ( item );
3598   gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), upload_submenu );
3599
3600   item = gtk_image_menu_item_new_with_mnemonic ( _("Upload to _GPS...") );
3601   gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_FORWARD, GTK_ICON_SIZE_MENU) );
3602   g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_gps_upload), pass_along );
3603   gtk_menu_shell_append (GTK_MENU_SHELL (upload_submenu), item);
3604   gtk_widget_show ( item );
3605
3606 #ifdef VIK_CONFIG_OPENSTREETMAP 
3607   item = gtk_image_menu_item_new_with_mnemonic ( _("Upload to _OSM...") );
3608   gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_UP, GTK_ICON_SIZE_MENU) );
3609   g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(osm_traces_upload_cb), pass_along );
3610   gtk_menu_shell_append (GTK_MENU_SHELL (upload_submenu), item);
3611   gtk_widget_show ( item );
3612 #endif
3613
3614   GtkWidget *delete_submenu = gtk_menu_new ();
3615   item = gtk_image_menu_item_new_with_mnemonic ( _("De_lete") );
3616   gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU) );
3617   gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3618   gtk_widget_show ( item );
3619   gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), delete_submenu );
3620   
3621   item = gtk_image_menu_item_new_with_mnemonic ( _("Delete All _Tracks") );
3622   gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU) );
3623   g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_all_tracks), pass_along );
3624   gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
3625   gtk_widget_show ( item );
3626   
3627   item = gtk_image_menu_item_new_with_mnemonic ( _("Delete Tracks _From Selection...") );
3628   gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
3629   g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_tracks_from_selection), pass_along );
3630   gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
3631   gtk_widget_show ( item );
3632
3633   item = gtk_image_menu_item_new_with_mnemonic ( _("Delete _All Routes") );
3634   gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU) );
3635   g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_all_routes), pass_along );
3636   gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
3637   gtk_widget_show ( item );
3638
3639   item = gtk_image_menu_item_new_with_mnemonic ( _("_Delete Routes From Selection...") );
3640   gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
3641   g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_routes_from_selection), pass_along );
3642   gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
3643   gtk_widget_show ( item );
3644   
3645   item = gtk_image_menu_item_new_with_mnemonic ( _("Delete All _Waypoints") );
3646   gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU) );
3647   g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_all_waypoints), pass_along );
3648   gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
3649   gtk_widget_show ( item );
3650   
3651   item = gtk_image_menu_item_new_with_mnemonic ( _("Delete Waypoints From _Selection...") );
3652   gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
3653   g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_waypoints_from_selection), pass_along );
3654   gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
3655   gtk_widget_show ( item );
3656   
3657   item = a_acquire_trwlayer_menu ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)), vlp,
3658                                    vik_layers_panel_get_viewport(VIK_LAYERS_PANEL(vlp)), vtl );
3659   if ( item ) {
3660     gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3661     gtk_widget_show ( item );
3662   }  
3663
3664   item = a_acquire_trwlayer_track_menu ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)), vlp,
3665                                          vik_layers_panel_get_viewport(VIK_LAYERS_PANEL(vlp)), vtl );
3666   if ( item ) {
3667     gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3668     gtk_widget_show ( item );
3669   }  
3670 }
3671
3672 // Fake Waypoint UUIDs vi simple increasing integer
3673 static guint wp_uuid = 0;
3674
3675 void vik_trw_layer_add_waypoint ( VikTrwLayer *vtl, gchar *name, VikWaypoint *wp )
3676 {
3677   wp_uuid++;
3678
3679   vik_waypoint_set_name (wp, name);
3680
3681   if ( VIK_LAYER(vtl)->realized )
3682   {
3683     // Do we need to create the sublayer:
3684     if ( g_hash_table_size (vtl->waypoints) == 0 ) {
3685       trw_layer_add_sublayer_waypoints ( vtl, VIK_LAYER(vtl)->vt, &(VIK_LAYER(vtl)->iter) );
3686     }
3687
3688     GtkTreeIter *iter = g_malloc(sizeof(GtkTreeIter));
3689
3690     // Visibility column always needed for waypoints
3691     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 );
3692
3693     // Actual setting of visibility dependent on the waypoint
3694     vik_treeview_item_set_visible ( VIK_LAYER(vtl)->vt, iter, wp->visible );
3695
3696     g_hash_table_insert ( vtl->waypoints_iters, GUINT_TO_POINTER(wp_uuid), iter );
3697
3698     // Sort now as post_read is not called on a realized waypoint
3699     vik_treeview_sort_children ( VIK_LAYER(vtl)->vt, &(vtl->waypoints_iter), vtl->wp_sort_order );
3700   }
3701
3702   highest_wp_number_add_wp(vtl, name);
3703   g_hash_table_insert ( vtl->waypoints, GUINT_TO_POINTER(wp_uuid), wp );
3704  
3705 }
3706
3707 // Fake Track UUIDs vi simple increasing integer
3708 static guint tr_uuid = 0;
3709
3710 void vik_trw_layer_add_track ( VikTrwLayer *vtl, gchar *name, VikTrack *t )
3711 {
3712   tr_uuid++;
3713
3714   vik_track_set_name (t, name);
3715
3716   if ( VIK_LAYER(vtl)->realized )
3717   {
3718     // Do we need to create the sublayer:
3719     if ( g_hash_table_size (vtl->tracks) == 0 ) {
3720       trw_layer_add_sublayer_tracks ( vtl, VIK_LAYER(vtl)->vt, &(VIK_LAYER(vtl)->iter) );
3721     }
3722
3723     GtkTreeIter *iter = g_malloc(sizeof(GtkTreeIter));
3724     // Visibility column always needed for tracks
3725     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 );
3726
3727     // Actual setting of visibility dependent on the track
3728     vik_treeview_item_set_visible ( VIK_LAYER(vtl)->vt, iter, t->visible );
3729
3730     g_hash_table_insert ( vtl->tracks_iters, GUINT_TO_POINTER(tr_uuid), iter );
3731
3732     // Sort now as post_read is not called on a realized track
3733     vik_treeview_sort_children ( VIK_LAYER(vtl)->vt, &(vtl->tracks_iter), vtl->track_sort_order );
3734   }
3735
3736   g_hash_table_insert ( vtl->tracks, GUINT_TO_POINTER(tr_uuid), t );
3737
3738   trw_layer_update_treeview ( vtl, t, GUINT_TO_POINTER(tr_uuid) );
3739 }
3740
3741 // Fake Route UUIDs vi simple increasing integer
3742 static guint rt_uuid = 0;
3743
3744 void vik_trw_layer_add_route ( VikTrwLayer *vtl, gchar *name, VikTrack *t )
3745 {
3746   rt_uuid++;
3747
3748   vik_track_set_name (t, name);
3749
3750   if ( VIK_LAYER(vtl)->realized )
3751   {
3752     // Do we need to create the sublayer:
3753     if ( g_hash_table_size (vtl->routes) == 0 ) {
3754       trw_layer_add_sublayer_routes ( vtl, VIK_LAYER(vtl)->vt, &(VIK_LAYER(vtl)->iter) );
3755     }
3756
3757     GtkTreeIter *iter = g_malloc(sizeof(GtkTreeIter));
3758     // Visibility column always needed for routes
3759     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 );
3760     // Actual setting of visibility dependent on the route
3761     vik_treeview_item_set_visible ( VIK_LAYER(vtl)->vt, iter, t->visible );
3762
3763     g_hash_table_insert ( vtl->routes_iters, GUINT_TO_POINTER(rt_uuid), iter );
3764
3765     // Sort now as post_read is not called on a realized route
3766     vik_treeview_sort_children ( VIK_LAYER(vtl)->vt, &(vtl->routes_iter), vtl->track_sort_order );
3767   }
3768
3769   g_hash_table_insert ( vtl->routes, GUINT_TO_POINTER(rt_uuid), t );
3770
3771   trw_layer_update_treeview ( vtl, t, GUINT_TO_POINTER(rt_uuid) );
3772 }
3773
3774 /* to be called whenever a track has been deleted or may have been changed. */
3775 void trw_layer_cancel_tps_of_track ( VikTrwLayer *vtl, VikTrack *trk )
3776 {
3777   if (vtl->current_tp_track == trk )
3778     trw_layer_cancel_current_tp ( vtl, FALSE );
3779 }
3780
3781 /**
3782  * Normally this is done to due the waypoint size preference having changed
3783  */
3784 void vik_trw_layer_reset_waypoints ( VikTrwLayer *vtl )
3785 {
3786   GHashTableIter iter;
3787   gpointer key, value;
3788
3789   // Foreach waypoint
3790   g_hash_table_iter_init ( &iter, vtl->waypoints );
3791   while ( g_hash_table_iter_next ( &iter, &key, &value ) ) {
3792     VikWaypoint *wp = VIK_WAYPOINT(value);
3793     if ( wp->symbol ) {
3794       // Reapply symbol setting to update the pixbuf
3795       gchar *tmp_symbol = g_strdup ( wp->symbol );
3796       vik_waypoint_set_symbol ( wp, tmp_symbol );
3797       g_free ( tmp_symbol );
3798     }
3799   }
3800 }
3801
3802 /**
3803  * trw_layer_new_unique_sublayer_name:
3804  *
3805  * Allocates a unique new name
3806  */
3807 gchar *trw_layer_new_unique_sublayer_name (VikTrwLayer *vtl, gint sublayer_type, const gchar *name)
3808 {
3809   gint i = 2;
3810   gchar *newname = g_strdup(name);
3811
3812   gpointer id = NULL;
3813   do {
3814     id = NULL;
3815     switch ( sublayer_type ) {
3816     case VIK_TRW_LAYER_SUBLAYER_TRACK:
3817       id = (gpointer) vik_trw_layer_get_track ( vtl, newname );
3818       break;
3819     case VIK_TRW_LAYER_SUBLAYER_WAYPOINT:
3820       id = (gpointer) vik_trw_layer_get_waypoint ( vtl, newname );
3821       break;
3822     default:
3823       id = (gpointer) vik_trw_layer_get_route ( vtl, newname );
3824       break;
3825     }
3826     // If found a name already in use try adding 1 to it and we try again
3827     if ( id ) {
3828       gchar *new_newname = g_strdup_printf("%s#%d", name, i);
3829       g_free(newname);
3830       newname = new_newname;
3831       i++;
3832     }
3833   } while ( id != NULL);
3834
3835   return newname;
3836 }
3837
3838 void vik_trw_layer_filein_add_waypoint ( VikTrwLayer *vtl, gchar *name, VikWaypoint *wp )
3839 {
3840   // No more uniqueness of name forced when loading from a file
3841   // This now makes this function a little redunant as we just flow the parameters through
3842   vik_trw_layer_add_waypoint ( vtl, name, wp );
3843 }
3844
3845 void vik_trw_layer_filein_add_track ( VikTrwLayer *vtl, gchar *name, VikTrack *tr )
3846 {
3847   if ( vtl->route_finder_append && vtl->route_finder_current_track ) {
3848     vik_track_remove_dup_points ( tr ); /* make "double point" track work to undo */
3849     vik_track_steal_and_append_trackpoints ( vtl->route_finder_current_track, tr );
3850     vik_track_free ( tr );
3851     vtl->route_finder_append = FALSE; /* this means we have added it */
3852   } else {
3853
3854     // No more uniqueness of name forced when loading from a file
3855     if ( tr->is_route )
3856       vik_trw_layer_add_route ( vtl, name, tr );
3857     else
3858       vik_trw_layer_add_track ( vtl, name, tr );
3859
3860     if ( vtl->route_finder_check_added_track ) {
3861       vik_track_remove_dup_points ( tr ); /* make "double point" track work to undo */
3862       vtl->route_finder_added_track = tr;
3863     }
3864   }
3865 }
3866
3867 static void trw_layer_enum_item ( gpointer id, GList **tr, GList **l )
3868 {
3869   *l = g_list_append(*l, id);
3870 }
3871
3872 /*
3873  * Move an item from one TRW layer to another TRW layer
3874  */
3875 static void trw_layer_move_item ( VikTrwLayer *vtl_src, VikTrwLayer *vtl_dest, gpointer id, gint type )
3876 {
3877   // TODO reconsider strategy when moving within layer (if anything...)
3878   gboolean rename = ( vtl_src != vtl_dest );
3879   if ( ! rename )
3880     return;
3881
3882   if (type == VIK_TRW_LAYER_SUBLAYER_TRACK) {
3883     VikTrack *trk = g_hash_table_lookup ( vtl_src->tracks, id );
3884
3885     gchar *newname;
3886     if ( rename )
3887       newname = trw_layer_new_unique_sublayer_name ( vtl_dest, type, trk->name );
3888     else
3889       newname = g_strdup ( trk->name );
3890
3891     VikTrack *trk2 = vik_track_copy ( trk, TRUE );
3892     vik_trw_layer_add_track ( vtl_dest, newname, trk2 );
3893     g_free ( newname );
3894     vik_trw_layer_delete_track ( vtl_src, trk );
3895   }
3896
3897   if (type == VIK_TRW_LAYER_SUBLAYER_ROUTE) {
3898     VikTrack *trk = g_hash_table_lookup ( vtl_src->routes, id );
3899
3900     gchar *newname;
3901     if ( rename )
3902       newname = trw_layer_new_unique_sublayer_name ( vtl_dest, type, trk->name );
3903     else
3904       newname = g_strdup ( trk->name );
3905
3906     VikTrack *trk2 = vik_track_copy ( trk, TRUE );
3907     vik_trw_layer_add_route ( vtl_dest, newname, trk2 );
3908     g_free ( newname );
3909     vik_trw_layer_delete_route ( vtl_src, trk );
3910   }
3911
3912   if (type == VIK_TRW_LAYER_SUBLAYER_WAYPOINT) {
3913     VikWaypoint *wp = g_hash_table_lookup ( vtl_src->waypoints, id );
3914
3915     gchar *newname;
3916     if ( rename )
3917       newname = trw_layer_new_unique_sublayer_name ( vtl_dest, type, wp->name );
3918     else
3919       newname = g_strdup ( wp->name );
3920
3921     VikWaypoint *wp2 = vik_waypoint_copy ( wp );
3922     vik_trw_layer_add_waypoint ( vtl_dest, newname, wp2 );
3923     g_free ( newname );
3924     trw_layer_delete_waypoint ( vtl_src, wp );
3925
3926     // Recalculate bounds even if not renamed as maybe dragged between layers
3927     trw_layer_calculate_bounds_waypoints ( vtl_dest );
3928     trw_layer_calculate_bounds_waypoints ( vtl_src );
3929   }
3930 }
3931
3932 static void trw_layer_drag_drop_request ( VikTrwLayer *vtl_src, VikTrwLayer *vtl_dest, GtkTreeIter *src_item_iter, GtkTreePath *dest_path )
3933 {
3934   VikTreeview *vt = VIK_LAYER(vtl_src)->vt;
3935   gint type = vik_treeview_item_get_data(vt, src_item_iter);
3936
3937   if (!vik_treeview_item_get_pointer(vt, src_item_iter)) {
3938     GList *items = NULL;
3939     GList *iter;
3940
3941     if (type==VIK_TRW_LAYER_SUBLAYER_TRACKS) {
3942       g_hash_table_foreach ( vtl_src->tracks, (GHFunc)trw_layer_enum_item, &items);
3943     } 
3944     if (type==VIK_TRW_LAYER_SUBLAYER_WAYPOINTS) {
3945       g_hash_table_foreach ( vtl_src->waypoints, (GHFunc)trw_layer_enum_item, &items);
3946     }    
3947     if (type==VIK_TRW_LAYER_SUBLAYER_ROUTES) {
3948       g_hash_table_foreach ( vtl_src->routes, (GHFunc)trw_layer_enum_item, &items);
3949     }
3950
3951     iter = items;
3952     while (iter) {
3953       if (type==VIK_TRW_LAYER_SUBLAYER_TRACKS) {
3954         trw_layer_move_item ( vtl_src, vtl_dest, iter->data, VIK_TRW_LAYER_SUBLAYER_TRACK);
3955       } else if (type==VIK_TRW_LAYER_SUBLAYER_ROUTES) {
3956         trw_layer_move_item ( vtl_src, vtl_dest, iter->data, VIK_TRW_LAYER_SUBLAYER_ROUTE);
3957       } else {
3958         trw_layer_move_item ( vtl_src, vtl_dest, iter->data, VIK_TRW_LAYER_SUBLAYER_WAYPOINT);
3959       }
3960       iter = iter->next;
3961     }
3962     if (items) 
3963       g_list_free(items);
3964   } else {
3965     gchar *name = vik_treeview_item_get_pointer(vt, src_item_iter);
3966     trw_layer_move_item(vtl_src, vtl_dest, name, type);
3967   }
3968 }
3969
3970 typedef struct {
3971   VikTrack *trk; // input
3972   gpointer uuid;   // output
3973 } trku_udata;
3974
3975 static gboolean trw_layer_track_find_uuid ( const gpointer id, const VikTrack *trk, gpointer udata )
3976 {
3977   trku_udata *user_data = udata;
3978   if ( trk == user_data->trk ) {
3979     user_data->uuid = id;
3980     return TRUE;
3981   }
3982   return FALSE;
3983 }
3984
3985 gboolean vik_trw_layer_delete_track ( VikTrwLayer *vtl, VikTrack *trk )
3986 {
3987   gboolean was_visible = FALSE;
3988
3989   if ( trk && trk->name ) {
3990
3991     if ( trk == vtl->current_track ) {
3992       vtl->current_track = NULL;
3993       vtl->current_tp_track = NULL;
3994       vtl->current_tp_id = NULL;
3995       vtl->moving_tp = FALSE;
3996     }
3997
3998     was_visible = trk->visible;
3999
4000     if ( trk == vtl->route_finder_current_track )
4001       vtl->route_finder_current_track = NULL;
4002
4003     if ( trk == vtl->route_finder_added_track )
4004       vtl->route_finder_added_track = NULL;
4005
4006     trku_udata udata;
4007     udata.trk  = trk;
4008     udata.uuid = NULL;
4009
4010     // Hmmm, want key of it
4011     gpointer *trkf = g_hash_table_find ( vtl->tracks, (GHRFunc) trw_layer_track_find_uuid, &udata );
4012
4013     if ( trkf && udata.uuid ) {
4014       /* could be current_tp, so we have to check */
4015       trw_layer_cancel_tps_of_track ( vtl, trk );
4016
4017       GtkTreeIter *it = g_hash_table_lookup ( vtl->tracks_iters, udata.uuid );
4018
4019       if ( it ) {
4020         vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, it );
4021         g_hash_table_remove ( vtl->tracks_iters, udata.uuid );
4022         g_hash_table_remove ( vtl->tracks, udata.uuid );
4023
4024         // If last sublayer, then remove sublayer container
4025         if ( g_hash_table_size (vtl->tracks) == 0 ) {
4026           vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, &(vtl->tracks_iter) );
4027         }
4028       }
4029     }
4030   }
4031   return was_visible;
4032 }
4033
4034 gboolean vik_trw_layer_delete_route ( VikTrwLayer *vtl, VikTrack *trk )
4035 {
4036   gboolean was_visible = FALSE;
4037
4038   if ( trk && trk->name ) {
4039
4040     if ( trk == vtl->current_track ) {
4041       vtl->current_track = NULL;
4042       vtl->current_tp_track = NULL;
4043       vtl->current_tp_id = NULL;
4044       vtl->moving_tp = FALSE;
4045     }
4046
4047     was_visible = trk->visible;
4048
4049     if ( trk == vtl->route_finder_current_track )
4050       vtl->route_finder_current_track = NULL;
4051
4052     if ( trk == vtl->route_finder_added_track )
4053       vtl->route_finder_added_track = NULL;
4054
4055     trku_udata udata;
4056     udata.trk  = trk;
4057     udata.uuid = NULL;
4058
4059     // Hmmm, want key of it
4060     gpointer *trkf = g_hash_table_find ( vtl->routes, (GHRFunc) trw_layer_track_find_uuid, &udata );
4061
4062     if ( trkf && udata.uuid ) {
4063       /* could be current_tp, so we have to check */
4064       trw_layer_cancel_tps_of_track ( vtl, trk );
4065
4066       GtkTreeIter *it = g_hash_table_lookup ( vtl->routes_iters, udata.uuid );
4067
4068       if ( it ) {
4069         vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, it );
4070         g_hash_table_remove ( vtl->routes_iters, udata.uuid );
4071         g_hash_table_remove ( vtl->routes, udata.uuid );
4072
4073         // If last sublayer, then remove sublayer container
4074         if ( g_hash_table_size (vtl->routes) == 0 ) {
4075           vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, &(vtl->routes_iter) );
4076         }
4077       }
4078     }
4079   }
4080   return was_visible;
4081 }
4082
4083 static gboolean trw_layer_delete_waypoint ( VikTrwLayer *vtl, VikWaypoint *wp )
4084 {
4085   gboolean was_visible = FALSE;
4086
4087   if ( wp && wp->name ) {
4088
4089     if ( wp == vtl->current_wp ) {
4090       vtl->current_wp = NULL;
4091       vtl->current_wp_id = NULL;
4092       vtl->moving_wp = FALSE;
4093     }
4094
4095     was_visible = wp->visible;
4096     
4097     wpu_udata udata;
4098     udata.wp   = wp;
4099     udata.uuid = NULL;
4100
4101     // Hmmm, want key of it
4102     gpointer *wpf = g_hash_table_find ( vtl->waypoints, (GHRFunc) trw_layer_waypoint_find_uuid, (gpointer) &udata );
4103
4104     if ( wpf && udata.uuid ) {
4105       GtkTreeIter *it = g_hash_table_lookup ( vtl->waypoints_iters, udata.uuid );
4106     
4107       if ( it ) {
4108         vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, it );
4109         g_hash_table_remove ( vtl->waypoints_iters, udata.uuid );
4110
4111         highest_wp_number_remove_wp(vtl, wp->name);
4112         g_hash_table_remove ( vtl->waypoints, udata.uuid ); // last because this frees the name
4113
4114         // If last sublayer, then remove sublayer container
4115         if ( g_hash_table_size (vtl->waypoints) == 0 ) {
4116           vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, &(vtl->waypoints_iter) );
4117         }
4118       }
4119     }
4120
4121   }
4122
4123   return was_visible;
4124 }
4125
4126 // Only for temporary use by trw_layer_delete_waypoint_by_name
4127 static gboolean trw_layer_waypoint_find_uuid_by_name ( const gpointer id, const VikWaypoint *wp, gpointer udata )
4128 {
4129   wpu_udata *user_data = udata;
4130   if ( ! strcmp ( wp->name, user_data->wp->name ) ) {
4131     user_data->uuid = id;
4132     return TRUE;
4133   }
4134   return FALSE;
4135 }
4136
4137 /*
4138  * Delete a waypoint by the given name
4139  * NOTE: ATM this will delete the first encountered Waypoint with the specified name
4140  *   as there be multiple waypoints with the same name
4141  */
4142 static gboolean trw_layer_delete_waypoint_by_name ( VikTrwLayer *vtl, const gchar *name )
4143 {
4144   wpu_udata udata;
4145   // Fake a waypoint with the given name
4146   udata.wp   = vik_waypoint_new ();
4147   vik_waypoint_set_name (udata.wp, name);
4148   // Currently only the name is used in this waypoint find function
4149   udata.uuid = NULL;
4150
4151   // Hmmm, want key of it
4152   gpointer *wpf = g_hash_table_find ( vtl->waypoints, (GHRFunc) trw_layer_waypoint_find_uuid_by_name, (gpointer) &udata );
4153
4154   vik_waypoint_free (udata.wp);
4155
4156   if ( wpf && udata.uuid )
4157     return trw_layer_delete_waypoint (vtl, g_hash_table_lookup ( vtl->waypoints, udata.uuid ));
4158   else
4159     return FALSE;
4160 }
4161
4162 typedef struct {
4163   VikTrack *trk; // input
4164   gpointer uuid; // output
4165 } tpu_udata;
4166
4167 // Only for temporary use by trw_layer_delete_track_by_name
4168 static gboolean trw_layer_track_find_uuid_by_name ( const gpointer id, const VikTrack *trk, gpointer udata )
4169 {
4170   tpu_udata *user_data = udata;
4171   if ( ! strcmp ( trk->name, user_data->trk->name ) ) {
4172     user_data->uuid = id;
4173     return TRUE;
4174   }
4175   return FALSE;
4176 }
4177
4178 /*
4179  * Delete a track by the given name
4180  * NOTE: ATM this will delete the first encountered Track with the specified name
4181  *   as there may be multiple tracks with the same name within the specified hash table
4182  */
4183 static gboolean trw_layer_delete_track_by_name ( VikTrwLayer *vtl, const gchar *name, GHashTable *ht_tracks )
4184 {
4185   tpu_udata udata;
4186   // Fake a track with the given name
4187   udata.trk   = vik_track_new ();
4188   vik_track_set_name (udata.trk, name);
4189   // Currently only the name is used in this waypoint find function
4190   udata.uuid = NULL;
4191
4192   // Hmmm, want key of it
4193   gpointer *trkf = g_hash_table_find ( ht_tracks, (GHRFunc) trw_layer_track_find_uuid_by_name, &udata );
4194
4195   vik_track_free (udata.trk);
4196
4197   if ( trkf && udata.uuid ) {
4198     // This could be a little better written...
4199     if ( vtl->tracks == ht_tracks )
4200       return vik_trw_layer_delete_track (vtl, g_hash_table_lookup ( ht_tracks, udata.uuid ));
4201     if ( vtl->routes == ht_tracks )
4202       return vik_trw_layer_delete_route (vtl, g_hash_table_lookup ( ht_tracks, udata.uuid ));
4203     return FALSE;
4204   }
4205   else
4206     return FALSE;
4207 }
4208
4209 static void remove_item_from_treeview ( const gpointer id, GtkTreeIter *it, VikTreeview * vt )
4210 {
4211     vik_treeview_item_delete (vt, it );
4212 }
4213
4214 void vik_trw_layer_delete_all_routes ( VikTrwLayer *vtl )
4215 {
4216
4217   vtl->current_track = NULL;
4218   vtl->route_finder_current_track = NULL;
4219   vtl->route_finder_added_track = NULL;
4220   if (vtl->current_tp_track)
4221     trw_layer_cancel_current_tp(vtl, FALSE);
4222
4223   g_hash_table_foreach(vtl->routes_iters, (GHFunc) remove_item_from_treeview, VIK_LAYER(vtl)->vt);
4224   g_hash_table_remove_all(vtl->routes_iters);
4225   g_hash_table_remove_all(vtl->routes);
4226
4227   vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, &(vtl->routes_iter) );
4228
4229   vik_layer_emit_update ( VIK_LAYER(vtl) );
4230 }
4231
4232 void vik_trw_layer_delete_all_tracks ( VikTrwLayer *vtl )
4233 {
4234
4235   vtl->current_track = NULL;
4236   vtl->route_finder_current_track = NULL;
4237   vtl->route_finder_added_track = NULL;
4238   if (vtl->current_tp_track)
4239     trw_layer_cancel_current_tp(vtl, FALSE);
4240
4241   g_hash_table_foreach(vtl->tracks_iters, (GHFunc) remove_item_from_treeview, VIK_LAYER(vtl)->vt);
4242   g_hash_table_remove_all(vtl->tracks_iters);
4243   g_hash_table_remove_all(vtl->tracks);
4244
4245   vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, &(vtl->tracks_iter) );
4246
4247   vik_layer_emit_update ( VIK_LAYER(vtl) );
4248 }
4249
4250 void vik_trw_layer_delete_all_waypoints ( VikTrwLayer *vtl )
4251 {
4252   vtl->current_wp = NULL;
4253   vtl->current_wp_id = NULL;
4254   vtl->moving_wp = FALSE;
4255
4256   highest_wp_number_reset(vtl);
4257
4258   g_hash_table_foreach(vtl->waypoints_iters, (GHFunc) remove_item_from_treeview, VIK_LAYER(vtl)->vt);
4259   g_hash_table_remove_all(vtl->waypoints_iters);
4260   g_hash_table_remove_all(vtl->waypoints);
4261
4262   vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, &(vtl->waypoints_iter) );
4263
4264   vik_layer_emit_update ( VIK_LAYER(vtl) );
4265 }
4266
4267 static void trw_layer_delete_all_tracks ( gpointer lav[2] )
4268 {
4269   VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
4270   // Get confirmation from the user
4271   if ( a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
4272                             _("Are you sure you want to delete all tracks in %s?"),
4273                             vik_layer_get_name ( VIK_LAYER(vtl) ) ) )
4274     vik_trw_layer_delete_all_tracks (vtl);
4275 }
4276
4277 static void trw_layer_delete_all_routes ( gpointer lav[2] )
4278 {
4279   VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
4280   // Get confirmation from the user
4281   if ( a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
4282                             _("Are you sure you want to delete all routes in %s?"),
4283                             vik_layer_get_name ( VIK_LAYER(vtl) ) ) )
4284     vik_trw_layer_delete_all_routes (vtl);
4285 }
4286
4287 static void trw_layer_delete_all_waypoints ( gpointer lav[2] )
4288 {
4289   VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
4290   // Get confirmation from the user
4291   if ( a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
4292                             _("Are you sure you want to delete all waypoints in %s?"),
4293                             vik_layer_get_name ( VIK_LAYER(vtl) ) ) )
4294     vik_trw_layer_delete_all_waypoints (vtl);
4295 }
4296
4297 static void trw_layer_delete_item ( gpointer pass_along[6] )
4298 {
4299   VikTrwLayer *vtl = VIK_TRW_LAYER(pass_along[0]);
4300   gboolean was_visible = FALSE;
4301   if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_WAYPOINT )
4302   {
4303     VikWaypoint *wp = g_hash_table_lookup ( vtl->waypoints, pass_along[3] );
4304     if ( wp && wp->name ) {
4305       if ( GPOINTER_TO_INT ( pass_along[4]) )
4306         // Get confirmation from the user
4307         // Maybe this Waypoint Delete should be optional as is it could get annoying...
4308         if ( ! a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
4309             _("Are you sure you want to delete the waypoint \"%s\""),
4310             wp->name ) )
4311           return;
4312       was_visible = trw_layer_delete_waypoint ( vtl, wp );
4313     }
4314   }
4315   else if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_TRACK )
4316   {
4317     VikTrack *trk = g_hash_table_lookup ( vtl->tracks, pass_along[3] );
4318     if ( trk && trk->name ) {
4319       if ( GPOINTER_TO_INT ( pass_along[4]) )
4320         // Get confirmation from the user
4321         if ( ! a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
4322                                   _("Are you sure you want to delete the track \"%s\""),
4323                                   trk->name ) )
4324           return;
4325       was_visible = vik_trw_layer_delete_track ( vtl, trk );
4326     }
4327   }
4328   else
4329   {
4330     VikTrack *trk = g_hash_table_lookup ( vtl->routes, pass_along[3] );
4331     if ( trk && trk->name ) {
4332       if ( GPOINTER_TO_INT ( pass_along[4]) )
4333         // Get confirmation from the user
4334         if ( ! a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
4335                                     _("Are you sure you want to delete the route \"%s\""),
4336                                     trk->name ) )
4337           return;
4338       was_visible = vik_trw_layer_delete_route ( vtl, trk );
4339     }
4340   }
4341   if ( was_visible )
4342     vik_layer_emit_update ( VIK_LAYER(vtl) );
4343 }
4344
4345 /**
4346  *  Rename waypoint and maintain corresponding name of waypoint in the treeview
4347  */
4348 static void trw_layer_waypoint_rename ( VikTrwLayer *vtl, VikWaypoint *wp, const gchar *new_name )
4349 {
4350   vik_waypoint_set_name ( wp, new_name );
4351
4352   // Now update the treeview as well
4353   wpu_udata udataU;
4354   udataU.wp   = wp;
4355   udataU.uuid = NULL;
4356
4357   // Need key of it for treeview update
4358   gpointer *wpf = g_hash_table_find ( vtl->waypoints, (GHRFunc) trw_layer_waypoint_find_uuid, &udataU );
4359
4360   if ( wpf && udataU.uuid ) {
4361     GtkTreeIter *it = g_hash_table_lookup ( vtl->waypoints_iters, udataU.uuid );
4362
4363     if ( it ) {
4364       vik_treeview_item_set_name ( VIK_LAYER(vtl)->vt, it, new_name );
4365       vik_treeview_sort_children ( VIK_LAYER(vtl)->vt, &(vtl->waypoints_iter), vtl->wp_sort_order );
4366     }
4367   }
4368 }
4369
4370 static void trw_layer_properties_item ( gpointer pass_along[7] )
4371 {
4372   VikTrwLayer *vtl = VIK_TRW_LAYER(pass_along[0]);
4373   if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_WAYPOINT )
4374   {
4375     VikWaypoint *wp = g_hash_table_lookup ( vtl->waypoints, pass_along[3] ); // sublayer
4376
4377     if ( wp && wp->name )
4378     {
4379       gboolean updated = FALSE;
4380       gchar *new_name = a_dialog_waypoint ( VIK_GTK_WINDOW_FROM_LAYER(vtl), wp->name, vtl, wp, vtl->coord_mode, FALSE, &updated );
4381       if ( new_name )
4382         trw_layer_waypoint_rename ( vtl, wp, new_name );
4383
4384       if ( updated && pass_along[6] )
4385         vik_treeview_item_set_icon ( VIK_LAYER(vtl)->vt, pass_along[6], get_wp_sym_small (wp->symbol) );
4386
4387       if ( updated && VIK_LAYER(vtl)->visible )
4388         vik_layer_emit_update ( VIK_LAYER(vtl) );
4389     }
4390   }
4391   else
4392   {
4393     VikTrack *tr;
4394     if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_TRACK )
4395       tr = g_hash_table_lookup ( vtl->tracks, pass_along[3] );
4396     else
4397       tr = g_hash_table_lookup ( vtl->routes, pass_along[3] );
4398
4399     if ( tr && tr->name )
4400     {
4401       vik_trw_layer_propwin_run ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
4402                                   vtl,
4403                                   tr,
4404                                   pass_along[1], /* vlp */
4405                                   pass_along[5], /* vvp */
4406                                   pass_along[6]); /* iter */
4407     }
4408   }
4409 }
4410
4411 /*
4412  * Update the treeview of the track id - primarily to update the icon
4413  */
4414 void trw_layer_update_treeview ( VikTrwLayer *vtl, VikTrack *trk, gpointer *trk_id )
4415 {
4416   trku_udata udata;
4417   udata.trk  = trk;
4418   udata.uuid = NULL;
4419
4420   gpointer *trkf = NULL;
4421   if ( trk->is_route )
4422     trkf = g_hash_table_find ( vtl->routes, (GHRFunc) trw_layer_track_find_uuid, &udata );
4423   else
4424     trkf = g_hash_table_find ( vtl->tracks, (GHRFunc) trw_layer_track_find_uuid, &udata );
4425
4426   if ( trkf && udata.uuid ) {
4427
4428     GtkTreeIter *iter = NULL;
4429     if ( trk->is_route )
4430       iter = g_hash_table_lookup ( vtl->routes_iters, udata.uuid );
4431     else
4432       iter = g_hash_table_lookup ( vtl->tracks_iters, udata.uuid );
4433
4434     if ( iter ) {
4435       // TODO: Make this a function
4436       GdkPixbuf *pixbuf = gdk_pixbuf_new ( GDK_COLORSPACE_RGB, FALSE, 8, 18, 18);
4437       guint32 pixel = ((trk->color.red & 0xff00) << 16) |
4438         ((trk->color.green & 0xff00) << 8) |
4439         (trk->color.blue & 0xff00);
4440       gdk_pixbuf_fill ( pixbuf, pixel );
4441       vik_treeview_item_set_icon ( VIK_LAYER(vtl)->vt, iter, pixbuf );
4442       g_object_unref (pixbuf);
4443     }
4444
4445   }
4446 }
4447
4448 /*
4449    Parameter 1 -> VikLayersPanel
4450    Parameter 2 -> VikLayer
4451    Parameter 3 -> VikViewport
4452 */
4453 static void goto_coord ( gpointer *vlp, gpointer vl, gpointer vvp, const VikCoord *coord )
4454 {
4455   if ( vlp ) {
4456     vik_viewport_set_center_coord ( vik_layers_panel_get_viewport (VIK_LAYERS_PANEL(vlp)), coord );
4457     vik_layers_panel_emit_update ( VIK_LAYERS_PANEL(vlp) );
4458   }
4459   else {
4460     /* since vlp not set, vl & vvp should be valid instead! */
4461     if ( vl && vvp ) {
4462       vik_viewport_set_center_coord ( VIK_VIEWPORT(vvp), coord );
4463       vik_layer_emit_update ( VIK_LAYER(vl) );
4464     }
4465   }
4466 }
4467
4468 static void trw_layer_goto_track_startpoint ( gpointer pass_along[6] )
4469 {
4470   VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
4471   VikTrack *track;
4472   if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
4473     track = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
4474   else
4475     track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
4476
4477   if ( track && track->trackpoints )
4478     goto_coord ( pass_along[1], pass_along[0], pass_along[5], &(((VikTrackpoint *) track->trackpoints->data)->coord) );
4479 }
4480
4481 static void trw_layer_goto_track_center ( gpointer pass_along[6] )
4482 {
4483   VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
4484   VikTrack *track;
4485   if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
4486     track = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
4487   else
4488     track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
4489
4490   if ( track && track->trackpoints )
4491   {
4492     struct LatLon average, maxmin[2] = { {0,0}, {0,0} };
4493     VikCoord coord;
4494     trw_layer_find_maxmin_tracks ( NULL, track, maxmin );
4495     average.lat = (maxmin[0].lat+maxmin[1].lat)/2;
4496     average.lon = (maxmin[0].lon+maxmin[1].lon)/2;
4497     vik_coord_load_from_latlon ( &coord, vtl->coord_mode, &average );
4498     goto_coord ( pass_along[1], pass_along[0], pass_along[5], &coord);
4499   }
4500 }
4501
4502 static void trw_layer_convert_track_route ( gpointer pass_along[6] )
4503 {
4504   VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
4505   VikTrack *trk;
4506   if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
4507     trk = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
4508   else
4509     trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
4510
4511   if ( !trk )
4512     return;
4513
4514   // Converting a track to a route can be a bit more complicated,
4515   //  so give a chance to change our minds:
4516   if ( !trk->is_route &&
4517        ( ( vik_track_get_segment_count ( trk ) > 1 ) ||
4518          ( vik_track_get_average_speed ( trk ) > 0.0 ) ) ) {
4519
4520     if ( ! a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
4521                                 _("Converting a track to a route removes extra track data such as segments, timestamps, etc...\nDo you want to continue?"), NULL ) )
4522       return;
4523 }
4524
4525   // Copy it
4526   VikTrack *trk_copy = vik_track_copy ( trk, TRUE );
4527
4528   // Convert
4529   trk_copy->is_route = !trk_copy->is_route;
4530
4531   // ATM can't set name to self - so must create temporary copy
4532   gchar *name = g_strdup ( trk_copy->name );
4533
4534   // Delete old one and then add new one
4535   if ( trk->is_route ) {
4536     vik_trw_layer_delete_route ( vtl, trk );
4537     vik_trw_layer_add_track ( vtl, name, trk_copy );
4538   }
4539   else {
4540     // Extra route conversion bits...
4541     vik_track_merge_segments ( trk_copy );
4542     vik_track_to_routepoints ( trk_copy );
4543
4544     vik_trw_layer_delete_track ( vtl, trk );
4545     vik_trw_layer_add_route ( vtl, name, trk_copy );
4546   }
4547   g_free ( name );
4548
4549   // Update in case color of track / route changes when moving between sublayers
4550   vik_layer_emit_update ( VIK_LAYER(pass_along[0]) );
4551 }
4552
4553
4554 static void trw_layer_extend_track_end ( gpointer pass_along[6] )
4555 {
4556   VikTrwLayer *vtl = VIK_TRW_LAYER(pass_along[0]);
4557   VikTrack *track;
4558   if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
4559     track = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
4560   else
4561     track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
4562
4563   if ( !track )
4564     return;
4565
4566   vtl->current_track = track;
4567   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);
4568
4569   if ( track->trackpoints )
4570     goto_coord ( pass_along[1], pass_along[0], pass_along[5], &(((VikTrackpoint *)g_list_last(track->trackpoints)->data)->coord) );
4571 }
4572
4573 /**
4574  * extend a track using route finder
4575  */
4576 static void trw_layer_extend_track_end_route_finder ( gpointer pass_along[6] )
4577 {
4578   VikTrwLayer *vtl = VIK_TRW_LAYER(pass_along[0]);
4579   VikTrack *track = g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->routes, pass_along[3] );
4580   if ( !track )
4581     return;
4582   VikCoord last_coord = (((VikTrackpoint *)g_list_last(track->trackpoints)->data)->coord);
4583
4584   vik_window_enable_layer_tool ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)), VIK_LAYER_TRW, TOOL_ROUTE_FINDER );
4585   vtl->route_finder_coord =  last_coord;
4586   vtl->route_finder_current_track = track;
4587   vtl->route_finder_started = TRUE;
4588
4589   if ( track->trackpoints )
4590     goto_coord ( pass_along[1], pass_along[0], pass_along[5], &last_coord) ;
4591
4592 }
4593
4594 static void trw_layer_apply_dem_data ( gpointer pass_along[6] )
4595 {
4596   /* TODO: check & warn if no DEM data, or no applicable DEM data. */
4597   /* Also warn if overwrite old elevation data */
4598   VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
4599   VikTrack *track;
4600   if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
4601     track = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
4602   else
4603     track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
4604
4605   if ( track )
4606     vik_track_apply_dem_data ( track );
4607 }
4608
4609 static void trw_layer_goto_track_endpoint ( gpointer pass_along[6] )
4610 {
4611   VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
4612   VikTrack *track;
4613   if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
4614     track = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
4615   else
4616     track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
4617
4618   if ( !track )
4619     return;
4620
4621   GList *trps = track->trackpoints;
4622   if ( !trps )
4623     return;
4624   trps = g_list_last(trps);
4625   goto_coord ( pass_along[1], pass_along[0], pass_along[5], &(((VikTrackpoint *) trps->data)->coord));
4626 }
4627
4628 static void trw_layer_goto_track_max_speed ( gpointer pass_along[6] )
4629 {
4630   VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
4631   VikTrack *track;
4632   if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
4633     track = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
4634   else
4635     track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
4636
4637   if ( !track )
4638     return;
4639
4640   VikTrackpoint* vtp = vik_track_get_tp_by_max_speed ( track );
4641   if ( !vtp )
4642     return;
4643   goto_coord ( pass_along[1], pass_along[0], pass_along[5], &(vtp->coord));
4644 }
4645
4646 static void trw_layer_goto_track_max_alt ( gpointer pass_along[6] )
4647 {
4648   VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
4649   VikTrack *track;
4650   if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
4651     track = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
4652   else
4653     track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
4654
4655   if ( !track )
4656     return;
4657
4658   VikTrackpoint* vtp = vik_track_get_tp_by_max_alt ( track );
4659   if ( !vtp )
4660     return;
4661   goto_coord ( pass_along[1], pass_along[0], pass_along[5], &(vtp->coord));
4662 }
4663
4664 static void trw_layer_goto_track_min_alt ( gpointer pass_along[6] )
4665 {
4666   VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
4667   VikTrack *track;
4668   if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
4669     track = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
4670   else
4671     track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
4672
4673   if ( !track )
4674     return;
4675
4676   VikTrackpoint* vtp = vik_track_get_tp_by_min_alt ( track );
4677   if ( !vtp )
4678     return;
4679   goto_coord ( pass_along[1], pass_along[0], pass_along[5], &(vtp->coord));
4680 }
4681
4682 /*
4683  * Automatically change the viewport to center on the track and zoom to see the extent of the track
4684  */
4685 static void trw_layer_auto_track_view ( gpointer pass_along[6] )
4686 {
4687   VikTrwLayer *vtl = VIK_TRW_LAYER(pass_along[0]);
4688   VikTrack *trk;
4689   if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
4690     trk = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
4691   else
4692     trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
4693
4694   if ( trk && trk->trackpoints )
4695   {
4696     struct LatLon maxmin[2] = { {0,0}, {0,0} };
4697     trw_layer_find_maxmin_tracks ( NULL, trk, maxmin );
4698     trw_layer_zoom_to_show_latlons ( vtl, pass_along[5], maxmin );
4699     if ( pass_along[1] )
4700       vik_layers_panel_emit_update ( VIK_LAYERS_PANEL(pass_along[1]) );
4701     else
4702       vik_layer_emit_update ( VIK_LAYER(vtl) );
4703   }
4704 }
4705
4706 static void trw_layer_edit_trackpoint ( gpointer pass_along[6] )
4707 {
4708   VikTrwLayer *vtl = VIK_TRW_LAYER(pass_along[0]);
4709   trw_layer_tpwin_init ( vtl );
4710 }
4711
4712 /*************************************
4713  * merge/split by time routines 
4714  *************************************/
4715
4716 /* called for each key in track hash table.
4717  * If the current track has the same time stamp type, add it to the result,
4718  * except the one pointed by "exclude".
4719  * set exclude to NULL if there is no exclude to check.
4720  * Note that the result is in reverse (for performance reasons).
4721  */
4722 typedef struct {
4723   GList **result;
4724   GList  *exclude;
4725   gboolean with_timestamps;
4726 } twt_udata;
4727 static void find_tracks_with_timestamp_type(gpointer key, gpointer value, gpointer udata)
4728 {
4729   twt_udata *user_data = udata;
4730   VikTrackpoint *p1, *p2;
4731
4732   if (VIK_TRACK(value)->trackpoints == user_data->exclude) {
4733     return;
4734   }
4735
4736   if (VIK_TRACK(value)->trackpoints) {
4737     p1 = VIK_TRACKPOINT(VIK_TRACK(value)->trackpoints->data);
4738     p2 = VIK_TRACKPOINT(g_list_last(VIK_TRACK(value)->trackpoints)->data);
4739
4740     if ( user_data->with_timestamps ) {
4741       if (!p1->has_timestamp || !p2->has_timestamp) {
4742         return;
4743       }
4744     }
4745     else {
4746       // Don't add tracks with timestamps when getting non timestamp tracks
4747       if (p1->has_timestamp || p2->has_timestamp) {
4748         return;
4749       }
4750     }
4751   }
4752
4753   *(user_data->result) = g_list_prepend(*(user_data->result), key);
4754 }
4755
4756 /* called for each key in track hash table. if original track user_data[1] is close enough
4757  * to the passed one, add it to list in user_data[0] 
4758  */
4759 static void find_nearby_tracks_by_time (gpointer key, gpointer value, gpointer user_data)
4760 {
4761   time_t t1, t2;
4762   VikTrackpoint *p1, *p2;
4763   VikTrack *trk = VIK_TRACK(value);
4764
4765   GList **nearby_tracks = ((gpointer *)user_data)[0];
4766   GList *tpoints = ((gpointer *)user_data)[1];
4767
4768   /* outline: 
4769    * detect reasons for not merging, and return
4770    * if no reason is found not to merge, then do it.
4771    */
4772
4773   // Exclude the original track from the compiled list
4774   if (trk->trackpoints == tpoints) {
4775     return;
4776   }
4777
4778   t1 = VIK_TRACKPOINT(g_list_first(tpoints)->data)->timestamp;
4779   t2 = VIK_TRACKPOINT(g_list_last(tpoints)->data)->timestamp;
4780
4781   if (trk->trackpoints) {
4782     p1 = VIK_TRACKPOINT(g_list_first(trk->trackpoints)->data);
4783     p2 = VIK_TRACKPOINT(g_list_last(trk->trackpoints)->data);
4784
4785     if (!p1->has_timestamp || !p2->has_timestamp) {
4786       //g_print("no timestamp\n");
4787       return;
4788     }
4789
4790     guint threshold = GPOINTER_TO_UINT (((gpointer *)user_data)[2]);
4791     //g_print("Got track named %s, times %d, %d\n", trk->name, p1->timestamp, p2->timestamp);
4792     if (! (abs(t1 - p2->timestamp) < threshold ||
4793         /*  p1 p2      t1 t2 */
4794            abs(p1->timestamp - t2) < threshold)
4795         /*  t1 t2      p1 p2 */
4796         ) {
4797       return;
4798     }
4799   }
4800
4801   *nearby_tracks = g_list_prepend(*nearby_tracks, value);
4802 }
4803
4804 /* comparison function used to sort tracks; a and b are hash table keys */
4805 /* Not actively used - can be restored if needed
4806 static gint track_compare(gconstpointer a, gconstpointer b, gpointer user_data)
4807 {
4808   GHashTable *tracks = user_data;
4809   time_t t1, t2;
4810
4811   t1 = VIK_TRACKPOINT(VIK_TRACK(g_hash_table_lookup(tracks, a))->trackpoints->data)->timestamp;
4812   t2 = VIK_TRACKPOINT(VIK_TRACK(g_hash_table_lookup(tracks, b))->trackpoints->data)->timestamp;
4813   
4814   if (t1 < t2) return -1;
4815   if (t1 > t2) return 1;
4816   return 0;
4817 }
4818 */
4819
4820 /* comparison function used to sort trackpoints */
4821 static gint trackpoint_compare(gconstpointer a, gconstpointer b)
4822 {
4823   time_t t1 = VIK_TRACKPOINT(a)->timestamp, t2 = VIK_TRACKPOINT(b)->timestamp;
4824   
4825   if (t1 < t2) return -1;
4826   if (t1 > t2) return 1;
4827   return 0;
4828 }
4829
4830 /**
4831  * comparison function which can be used to sort tracks or waypoints by name
4832  */
4833 static gint sort_alphabetically (gconstpointer a, gconstpointer b, gpointer user_data)
4834 {
4835   const gchar* namea = (const gchar*) a;
4836   const gchar* nameb = (const gchar*) b;
4837   if ( namea == NULL || nameb == NULL)
4838     return 0;
4839   else
4840     // Same sort method as used in the vik_treeview_*_alphabetize functions
4841     return strcmp ( namea, nameb );
4842 }
4843
4844 /**
4845  * Attempt to merge selected track with other tracks specified by the user
4846  * Tracks to merge with must be of the same 'type' as the selected track -
4847  *  either all with timestamps, or all without timestamps
4848  */
4849 static void trw_layer_merge_with_other ( gpointer pass_along[6] )
4850 {
4851   VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
4852   GList *other_tracks = NULL;
4853   GHashTable *ght_tracks;
4854   if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
4855     ght_tracks = vtl->routes;
4856   else
4857     ght_tracks = vtl->tracks;
4858
4859   VikTrack *track = (VikTrack *) g_hash_table_lookup ( ght_tracks, pass_along[3] );
4860
4861   if ( !track )
4862     return;
4863
4864   if ( !track->trackpoints )
4865     return;
4866
4867   twt_udata udata;
4868   udata.result = &other_tracks;
4869   udata.exclude = track->trackpoints;
4870   // Allow merging with 'similar' time type time tracks
4871   // i.e. either those times, or those without
4872   udata.with_timestamps = (VIK_TRACKPOINT(track->trackpoints->data)->has_timestamp);
4873
4874   g_hash_table_foreach(ght_tracks, find_tracks_with_timestamp_type, (gpointer)&udata);
4875   other_tracks = g_list_reverse(other_tracks);
4876
4877   if ( !other_tracks ) {
4878     if ( udata.with_timestamps )
4879       a_dialog_error_msg(VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Failed. No other tracks with timestamps in this layer found"));
4880     else
4881       a_dialog_error_msg(VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Failed. No other tracks without timestamps in this layer found"));
4882     return;
4883   }
4884
4885   // Sort alphabetically for user presentation
4886   // Convert into list of names for usage with dialog function
4887   // TODO: Need to consider how to work best when we can have multiple tracks the same name...
4888   GList *other_tracks_names = NULL;
4889   GList *iter = g_list_first ( other_tracks );
4890   while ( iter ) {
4891     other_tracks_names = g_list_append ( other_tracks_names, VIK_TRACK(g_hash_table_lookup (ght_tracks, iter->data))->name );
4892     iter = g_list_next ( iter );
4893   }
4894
4895   other_tracks_names = g_list_sort_with_data (other_tracks_names, sort_alphabetically, NULL);
4896
4897   GList *merge_list = a_dialog_select_from_list(VIK_GTK_WINDOW_FROM_LAYER(vtl),
4898                                                 other_tracks_names,
4899                                                 TRUE,
4900                                                 _("Merge with..."),
4901                                                 track->is_route ? _("Select route to merge with") : _("Select track to merge with"));
4902   g_list_free(other_tracks);
4903   g_list_free(other_tracks_names);
4904
4905   if (merge_list)
4906   {
4907     GList *l;
4908     for (l = merge_list; l != NULL; l = g_list_next(l)) {
4909       VikTrack *merge_track;
4910       if ( track->is_route )
4911         merge_track = vik_trw_layer_get_route ( vtl, l->data );
4912       else
4913         merge_track = vik_trw_layer_get_track ( vtl, l->data );
4914
4915       if (merge_track) {
4916         vik_track_steal_and_append_trackpoints ( track, merge_track );
4917         if ( track->is_route )
4918           vik_trw_layer_delete_route (vtl, merge_track);
4919         else
4920           vik_trw_layer_delete_track (vtl, merge_track);
4921         track->trackpoints = g_list_sort(track->trackpoints, trackpoint_compare);
4922       }
4923     }
4924     for (l = merge_list; l != NULL; l = g_list_next(l))
4925       g_free(l->data);
4926     g_list_free(merge_list);
4927
4928     vik_layer_emit_update( VIK_LAYER(vtl) );
4929   }
4930 }
4931
4932 // c.f. trw_layer_sorted_track_id_by_name_list
4933 //  but don't add the specified track to the list (normally current track)
4934 static void trw_layer_sorted_track_id_by_name_list_exclude_self (const gpointer id, const VikTrack *trk, gpointer udata)
4935 {
4936   twt_udata *user_data = udata;
4937
4938   // Skip self
4939   if (trk->trackpoints == user_data->exclude) {
4940     return;
4941   }
4942
4943   // Sort named list alphabetically
4944   *(user_data->result) = g_list_insert_sorted_with_data (*(user_data->result), trk->name, sort_alphabetically, NULL);
4945 }
4946
4947 /**
4948  * Join - this allows combining 'tracks' and 'track routes'
4949  *  i.e. doesn't care about whether tracks have consistent timestamps
4950  * ATM can only append one track at a time to the currently selected track
4951  */
4952 static void trw_layer_append_track ( gpointer pass_along[6] )
4953 {
4954
4955   VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
4956   VikTrack *trk;
4957   GHashTable *ght_tracks;
4958   if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
4959     ght_tracks = vtl->routes;
4960   else
4961     ght_tracks = vtl->tracks;
4962
4963   trk = (VikTrack *) g_hash_table_lookup ( ght_tracks, pass_along[3] );
4964
4965   if ( !trk )
4966     return;
4967
4968   GList *other_tracks_names = NULL;
4969
4970   // Sort alphabetically for user presentation
4971   // Convert into list of names for usage with dialog function
4972   // TODO: Need to consider how to work best when we can have multiple tracks the same name...
4973   twt_udata udata;
4974   udata.result = &other_tracks_names;
4975   udata.exclude = trk->trackpoints;
4976
4977   g_hash_table_foreach(ght_tracks, (GHFunc) trw_layer_sorted_track_id_by_name_list_exclude_self, (gpointer)&udata);
4978
4979   // Note the limit to selecting one track only
4980   //  this is to control the ordering of appending tracks, i.e. the selected track always goes after the current track
4981   //  (otherwise with multiple select the ordering would not be controllable by the user - automatically being alphabetically)
4982   GList *append_list = a_dialog_select_from_list(VIK_GTK_WINDOW_FROM_LAYER(vtl),
4983                                                  other_tracks_names,
4984                                                  FALSE,
4985                                                  trk->is_route ? _("Append Route"): _("Append Track"),
4986                                                  trk->is_route ? _("Select the route to append after the current route") :
4987                                                                  _("Select the track to append after the current track") );
4988
4989   g_list_free(other_tracks_names);
4990
4991   // It's a list, but shouldn't contain more than one other track!
4992   if ( append_list ) {
4993     GList *l;
4994     for (l = append_list; l != NULL; l = g_list_next(l)) {
4995       // TODO: at present this uses the first track found by name,
4996       //  which with potential multiple same named tracks may not be the one selected...
4997       VikTrack *append_track;
4998       if ( trk->is_route )
4999         append_track = vik_trw_layer_get_route ( vtl, l->data );
5000       else
5001         append_track = vik_trw_layer_get_track ( vtl, l->data );
5002
5003       if ( append_track ) {
5004         vik_track_steal_and_append_trackpoints ( trk, append_track );
5005         if ( trk->is_route )
5006           vik_trw_layer_delete_route (vtl, append_track);
5007         else
5008           vik_trw_layer_delete_track (vtl, append_track);
5009       }
5010     }
5011     for (l = append_list; l != NULL; l = g_list_next(l))
5012       g_free(l->data);
5013     g_list_free(append_list);
5014
5015     vik_layer_emit_update( VIK_LAYER(vtl) );
5016   }
5017 }
5018
5019 /**
5020  * Very similar to trw_layer_append_track for joining
5021  * but this allows selection from the 'other' list
5022  * If a track is selected, then is shows routes and joins the selected one
5023  * If a route is selected, then is shows tracks and joins the selected one
5024  */
5025 static void trw_layer_append_other ( gpointer pass_along[6] )
5026 {
5027
5028   VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
5029   VikTrack *trk;
5030   GHashTable *ght_mykind, *ght_others;
5031   if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE ) {
5032     ght_mykind = vtl->routes;
5033     ght_others = vtl->tracks;
5034   }
5035   else {
5036     ght_mykind = vtl->tracks;
5037     ght_others = vtl->routes;
5038   }
5039
5040   trk = (VikTrack *) g_hash_table_lookup ( ght_mykind, pass_along[3] );
5041
5042   if ( !trk )
5043     return;
5044
5045   GList *other_tracks_names = NULL;
5046
5047   // Sort alphabetically for user presentation
5048   // Convert into list of names for usage with dialog function
5049   // TODO: Need to consider how to work best when we can have multiple tracks the same name...
5050   twt_udata udata;
5051   udata.result = &other_tracks_names;
5052   udata.exclude = trk->trackpoints;
5053
5054   g_hash_table_foreach(ght_others, (GHFunc) trw_layer_sorted_track_id_by_name_list_exclude_self, (gpointer)&udata);
5055
5056   // Note the limit to selecting one track only
5057   //  this is to control the ordering of appending tracks, i.e. the selected track always goes after the current track
5058   //  (otherwise with multiple select the ordering would not be controllable by the user - automatically being alphabetically)
5059   GList *append_list = a_dialog_select_from_list(VIK_GTK_WINDOW_FROM_LAYER(vtl),
5060                                                  other_tracks_names,
5061                                                  FALSE,
5062                                                  trk->is_route ? _("Append Track"): _("Append Route"),
5063                                                  trk->is_route ? _("Select the track to append after the current route") :
5064                                                                  _("Select the route to append after the current track") );
5065
5066   g_list_free(other_tracks_names);
5067
5068   // It's a list, but shouldn't contain more than one other track!
5069   if ( append_list ) {
5070     GList *l;
5071     for (l = append_list; l != NULL; l = g_list_next(l)) {
5072       // TODO: at present this uses the first track found by name,
5073       //  which with potential multiple same named tracks may not be the one selected...
5074
5075       // Get FROM THE OTHER TYPE list
5076       VikTrack *append_track;
5077       if ( trk->is_route )
5078         append_track = vik_trw_layer_get_track ( vtl, l->data );
5079       else
5080         append_track = vik_trw_layer_get_route ( vtl, l->data );
5081
5082       if ( append_track ) {
5083
5084         if ( !append_track->is_route &&
5085              ( ( vik_track_get_segment_count ( append_track ) > 1 ) ||
5086                ( vik_track_get_average_speed ( append_track ) > 0.0 ) ) ) {
5087
5088           if ( a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
5089                                       _("Converting a track to a route removes extra track data such as segments, timestamps, etc...\nDo you want to continue?"), NULL ) ) {
5090             vik_track_merge_segments ( append_track );
5091             vik_track_to_routepoints ( append_track );
5092           }
5093           else {
5094             break;
5095           }
5096         }
5097
5098         vik_track_steal_and_append_trackpoints ( trk, append_track );
5099
5100         // Delete copied which is FROM THE OTHER TYPE list
5101         if ( trk->is_route )
5102           vik_trw_layer_delete_track (vtl, append_track);
5103         else
5104           vik_trw_layer_delete_route (vtl, append_track);
5105       }
5106     }
5107     for (l = append_list; l != NULL; l = g_list_next(l))
5108       g_free(l->data);
5109     g_list_free(append_list);
5110     vik_layer_emit_update( VIK_LAYER(vtl) );
5111   }
5112 }
5113
5114 /* merge by segments */
5115 static void trw_layer_merge_by_segment ( gpointer pass_along[6] )
5116 {
5117   VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
5118   VikTrack *trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
5119   guint segments = vik_track_merge_segments ( trk );
5120   // NB currently no need to redraw as segments not actually shown on the display
5121   // However inform the user of what happened:
5122   gchar str[64];
5123   const gchar *tmp_str = ngettext("%d segment merged", "%d segments merged", segments);
5124   g_snprintf(str, 64, tmp_str, segments);
5125   a_dialog_info_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), str );
5126 }
5127
5128 /* merge by time routine */
5129 static void trw_layer_merge_by_timestamp ( gpointer pass_along[6] )
5130 {
5131   VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
5132
5133   //time_t t1, t2;
5134
5135   GList *tracks_with_timestamp = NULL;
5136   VikTrack *orig_trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
5137   if (orig_trk->trackpoints &&
5138       !VIK_TRACKPOINT(orig_trk->trackpoints->data)->has_timestamp) {
5139     a_dialog_error_msg(VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Failed. This track does not have timestamp"));
5140     return;
5141   }
5142
5143   twt_udata udata;
5144   udata.result = &tracks_with_timestamp;
5145   udata.exclude = orig_trk->trackpoints;
5146   udata.with_timestamps = TRUE;
5147   g_hash_table_foreach(vtl->tracks, find_tracks_with_timestamp_type, (gpointer)&udata);
5148   tracks_with_timestamp = g_list_reverse(tracks_with_timestamp);
5149
5150   if (!tracks_with_timestamp) {
5151     a_dialog_error_msg(VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Failed. No other track in this layer has timestamp"));
5152     return;
5153   }
5154   g_list_free(tracks_with_timestamp);
5155
5156   static guint threshold_in_minutes = 1;
5157   if (!a_dialog_time_threshold(VIK_GTK_WINDOW_FROM_LAYER(vtl),
5158                                _("Merge Threshold..."),
5159                                _("Merge when time between tracks less than:"),
5160                                &threshold_in_minutes)) {
5161     return;
5162   }
5163
5164   // keep attempting to merge all tracks until no merges within the time specified is possible
5165   gboolean attempt_merge = TRUE;
5166   GList *nearby_tracks = NULL;
5167   GList *trps;
5168   static gpointer params[3];
5169
5170   while ( attempt_merge ) {
5171
5172     // Don't try again unless tracks have changed
5173     attempt_merge = FALSE;
5174
5175     trps = orig_trk->trackpoints;
5176     if ( !trps )
5177       return;
5178
5179     if (nearby_tracks) {
5180       g_list_free(nearby_tracks);
5181       nearby_tracks = NULL;
5182     }
5183
5184     //t1 = ((VikTrackpoint *)trps->data)->timestamp;
5185     //t2 = ((VikTrackpoint *)g_list_last(trps)->data)->timestamp;
5186     
5187     /*    g_print("Original track times: %d and %d\n", t1, t2);  */
5188     params[0] = &nearby_tracks;
5189     params[1] = (gpointer)trps;
5190     params[2] = GUINT_TO_POINTER (threshold_in_minutes*60); // In seconds
5191
5192     /* get a list of adjacent-in-time tracks */
5193     g_hash_table_foreach(vtl->tracks, find_nearby_tracks_by_time, params);
5194
5195     /* merge them */
5196     GList *l = nearby_tracks;
5197     while ( l ) {
5198        /*
5199 #define get_first_trackpoint(x) VIK_TRACKPOINT(VIK_TRACK(x)->trackpoints->data)
5200 #define get_last_trackpoint(x) VIK_TRACKPOINT(g_list_last(VIK_TRACK(x)->trackpoints)->data)
5201         time_t t1, t2;
5202         t1 = get_first_trackpoint(l)->timestamp;
5203         t2 = get_last_trackpoint(l)->timestamp;
5204 #undef get_first_trackpoint
5205 #undef get_last_trackpoint
5206         g_print("     %20s: track %d - %d\n", VIK_TRACK(l->data)->name, (int)t1, (int)t2);
5207        */
5208
5209       /* remove trackpoints from merged track, delete track */
5210       vik_track_steal_and_append_trackpoints ( orig_trk, VIK_TRACK(l->data) );
5211       vik_trw_layer_delete_track (vtl, VIK_TRACK(l->data));
5212
5213       // Tracks have changed, therefore retry again against all the remaining tracks
5214       attempt_merge = TRUE;
5215
5216       l = g_list_next(l);
5217     }
5218
5219     orig_trk->trackpoints = g_list_sort(orig_trk->trackpoints, trackpoint_compare);
5220   }
5221
5222   g_list_free(nearby_tracks);
5223
5224   vik_layer_emit_update( VIK_LAYER(vtl) );
5225 }
5226
5227 /**
5228  * Split a track at the currently selected trackpoint
5229  */
5230 static void trw_layer_split_at_selected_trackpoint ( VikTrwLayer *vtl, gint subtype )
5231 {
5232   if ( !vtl->current_tpl )
5233     return;
5234
5235   if ( vtl->current_tpl->next && vtl->current_tpl->prev ) {
5236     gchar *name = trw_layer_new_unique_sublayer_name(vtl, subtype, vtl->current_tp_track->name);
5237     if ( name ) {
5238       VikTrack *tr = vik_track_copy ( vtl->current_tp_track, FALSE );
5239       GList *newglist = g_list_alloc ();
5240       newglist->prev = NULL;
5241       newglist->next = vtl->current_tpl->next;
5242       newglist->data = vik_trackpoint_copy(VIK_TRACKPOINT(vtl->current_tpl->data));
5243       tr->trackpoints = newglist;
5244
5245       vtl->current_tpl->next->prev = newglist; /* end old track here */
5246       vtl->current_tpl->next = NULL;
5247
5248       // Bounds of the selected track changed due to the split
5249       vik_track_calculate_bounds ( vtl->current_tp_track );
5250
5251       vtl->current_tpl = newglist; /* change tp to first of new track. */
5252       vtl->current_tp_track = tr;
5253
5254       if ( tr->is_route )
5255         vik_trw_layer_add_route ( vtl, name, tr );
5256       else
5257         vik_trw_layer_add_track ( vtl, name, tr );
5258
5259       // Bounds of the new track created by the split
5260       vik_track_calculate_bounds ( tr );
5261
5262       trku_udata udata;
5263       udata.trk  = tr;
5264       udata.uuid = NULL;
5265
5266       // Also need id of newly created track
5267       gpointer *trkf;
5268       if ( tr->is_route )
5269          trkf = g_hash_table_find ( vtl->routes, (GHRFunc) trw_layer_track_find_uuid, &udata );
5270       else
5271          trkf = g_hash_table_find ( vtl->tracks, (GHRFunc) trw_layer_track_find_uuid, &udata );
5272
5273       if ( trkf && udata.uuid )
5274         vtl->current_tp_id = udata.uuid;
5275       else
5276         vtl->current_tp_id = NULL;
5277
5278       vik_layer_emit_update(VIK_LAYER(vtl));
5279     }
5280     g_free ( name );
5281   }
5282 }
5283
5284 /* split by time routine */
5285 static void trw_layer_split_by_timestamp ( gpointer pass_along[6] )
5286 {
5287   VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
5288   VikTrack *track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
5289   GList *trps = track->trackpoints;
5290   GList *iter;
5291   GList *newlists = NULL;
5292   GList *newtps = NULL;
5293   static guint thr = 1;
5294
5295   time_t ts, prev_ts;
5296
5297   if ( !trps )
5298     return;
5299
5300   if (!a_dialog_time_threshold(VIK_GTK_WINDOW_FROM_LAYER(pass_along[0]), 
5301                                _("Split Threshold..."), 
5302                                _("Split when time between trackpoints exceeds:"), 
5303                                &thr)) {
5304     return;
5305   }
5306
5307   /* iterate through trackpoints, and copy them into new lists without touching original list */
5308   prev_ts = VIK_TRACKPOINT(trps->data)->timestamp;
5309   iter = trps;
5310
5311   while (iter) {
5312     ts = VIK_TRACKPOINT(iter->data)->timestamp;
5313
5314     // Check for unordered time points - this is quite a rare occurence - unless one has reversed a track.
5315     if (ts < prev_ts) {
5316       gchar tmp_str[64];
5317       strftime ( tmp_str, sizeof(tmp_str), "%c", localtime(&ts) );
5318       if ( a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
5319                                 _("Can not split track due to trackpoints not ordered in time - such as at %s.\n\nGoto this trackpoint?"),
5320                                 tmp_str ) ) {
5321         goto_coord ( pass_along[1], vtl, pass_along[5], &(VIK_TRACKPOINT(iter->data)->coord) );
5322       }
5323       return;
5324     }
5325
5326     if (ts - prev_ts > thr*60) {
5327       /* flush accumulated trackpoints into new list */
5328       newlists = g_list_append(newlists, g_list_reverse(newtps));
5329       newtps = NULL;
5330     }
5331
5332     /* accumulate trackpoint copies in newtps, in reverse order */
5333     newtps = g_list_prepend(newtps, vik_trackpoint_copy(VIK_TRACKPOINT(iter->data)));
5334     prev_ts = ts;
5335     iter = g_list_next(iter);
5336   }
5337   if (newtps) {
5338       newlists = g_list_append(newlists, g_list_reverse(newtps));
5339   }
5340
5341   /* put lists of trackpoints into tracks */
5342   iter = newlists;
5343   // Only bother updating if the split results in new tracks
5344   if (g_list_length (newlists) > 1) {
5345     while (iter) {
5346       gchar *new_tr_name;
5347       VikTrack *tr;
5348
5349       tr = vik_track_copy ( track, FALSE );
5350       tr->trackpoints = (GList *)(iter->data);
5351
5352       new_tr_name = trw_layer_new_unique_sublayer_name ( vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, track->name);
5353       vik_trw_layer_add_track(vtl, new_tr_name, tr);
5354       /*    g_print("adding track %s, times %d - %d\n", new_tr_name, VIK_TRACKPOINT(tr->trackpoints->data)->timestamp,
5355           VIK_TRACKPOINT(g_list_last(tr->trackpoints)->data)->timestamp);*/
5356       g_free ( new_tr_name );
5357       vik_track_calculate_bounds ( tr );
5358       iter = g_list_next(iter);
5359     }
5360     // Remove original track and then update the display
5361     vik_trw_layer_delete_track (vtl, track);
5362     vik_layer_emit_update(VIK_LAYER(pass_along[0]));
5363   }
5364   g_list_free(newlists);
5365 }
5366
5367 /**
5368  * Split a track by the number of points as specified by the user
5369  */
5370 static void trw_layer_split_by_n_points ( gpointer pass_along[6] )
5371 {
5372   VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
5373   VikTrack *track;
5374   if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5375     track = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
5376   else
5377     track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
5378
5379   if ( !track )
5380     return;
5381
5382   // Check valid track
5383   GList *trps = track->trackpoints;
5384   if ( !trps )
5385     return;
5386
5387   gint points = a_dialog_get_positive_number(VIK_GTK_WINDOW_FROM_LAYER(pass_along[0]),
5388                                              _("Split Every Nth Point"),
5389                                              _("Split on every Nth point:"),
5390                                              250,   // Default value as per typical limited track capacity of various GPS devices
5391                                              2,     // Min
5392                                              65536, // Max
5393                                              5);    // Step
5394   // Was a valid number returned?
5395   if (!points)
5396     return;
5397
5398   // Now split...
5399   GList *iter;
5400   GList *newlists = NULL;
5401   GList *newtps = NULL;
5402   gint count = 0;
5403   iter = trps;
5404
5405   while (iter) {
5406     /* accumulate trackpoint copies in newtps, in reverse order */
5407     newtps = g_list_prepend(newtps, vik_trackpoint_copy(VIK_TRACKPOINT(iter->data)));
5408     count++;
5409     if (count >= points) {
5410       /* flush accumulated trackpoints into new list */
5411       newlists = g_list_append(newlists, g_list_reverse(newtps));
5412       newtps = NULL;
5413       count = 0;
5414     }
5415     iter = g_list_next(iter);
5416   }
5417
5418   // If there is a remaining chunk put that into the new split list
5419   // This may well be the whole track if no split points were encountered
5420   if (newtps) {
5421       newlists = g_list_append(newlists, g_list_reverse(newtps));
5422   }
5423
5424   /* put lists of trackpoints into tracks */
5425   iter = newlists;
5426   // Only bother updating if the split results in new tracks
5427   if (g_list_length (newlists) > 1) {
5428     while (iter) {
5429       gchar *new_tr_name;
5430       VikTrack *tr;
5431
5432       tr = vik_track_copy ( track, FALSE );
5433       tr->trackpoints = (GList *)(iter->data);
5434
5435       if ( track->is_route ) {
5436         new_tr_name = trw_layer_new_unique_sublayer_name ( vtl, VIK_TRW_LAYER_SUBLAYER_ROUTE, track->name);
5437         vik_trw_layer_add_route(vtl, new_tr_name, tr);
5438       }
5439       else {
5440         new_tr_name = trw_layer_new_unique_sublayer_name ( vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, track->name);
5441         vik_trw_layer_add_track(vtl, new_tr_name, tr);
5442       }
5443       g_free ( new_tr_name );
5444       vik_track_calculate_bounds ( tr );
5445
5446       iter = g_list_next(iter);
5447     }
5448     // Remove original track and then update the display
5449     if ( track->is_route )
5450       vik_trw_layer_delete_route (vtl, track);
5451     else
5452       vik_trw_layer_delete_track (vtl, track);
5453     vik_layer_emit_update(VIK_LAYER(pass_along[0]));
5454   }
5455   g_list_free(newlists);
5456 }
5457
5458 /**
5459  * Split a track at the currently selected trackpoint
5460  */
5461 static void trw_layer_split_at_trackpoint ( gpointer pass_along[6] )
5462 {
5463   VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
5464   gint subtype = GPOINTER_TO_INT (pass_along[2]);
5465   trw_layer_split_at_selected_trackpoint ( vtl, subtype );
5466 }
5467
5468 /**
5469  * Split a track by its segments
5470  * Routes do not have segments so don't call this for routes
5471  */
5472 static void trw_layer_split_segments ( gpointer pass_along[6] )
5473 {
5474   VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
5475   VikTrack *trk = g_hash_table_lookup ( vtl->tracks, pass_along[3] );
5476
5477   if ( !trk )
5478     return;
5479
5480   guint ntracks;
5481
5482   VikTrack **tracks = vik_track_split_into_segments (trk, &ntracks);
5483   gchar *new_tr_name;
5484   guint i;
5485   for ( i = 0; i < ntracks; i++ ) {
5486     if ( tracks[i] ) {
5487       new_tr_name = trw_layer_new_unique_sublayer_name ( vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, trk->name);
5488       vik_trw_layer_add_track ( vtl, new_tr_name, tracks[i] );
5489       g_free ( new_tr_name );
5490     }
5491   }
5492   if ( tracks ) {
5493     g_free ( tracks );
5494     // Remove original track
5495     vik_trw_layer_delete_track ( vtl, trk );
5496     vik_layer_emit_update ( VIK_LAYER(vtl) );
5497   }
5498   else {
5499     a_dialog_error_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Can not split track as it has no segments"));
5500   }
5501 }
5502 /* end of split/merge routines */
5503
5504 /**
5505  * Delete adjacent track points at the same position
5506  * AKA Delete Dulplicates on the Properties Window
5507  */
5508 static void trw_layer_delete_points_same_position ( gpointer pass_along[6] )
5509 {
5510   VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
5511   VikTrack *trk;
5512   if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5513     trk = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
5514   else
5515     trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
5516
5517   if ( !trk )
5518     return;
5519
5520   gulong removed = vik_track_remove_dup_points ( trk );
5521
5522   // Track has been updated so update tps:
5523   trw_layer_cancel_tps_of_track ( vtl, trk );
5524
5525   // Inform user how much was deleted as it's not obvious from the normal view
5526   gchar str[64];
5527   const gchar *tmp_str = ngettext("Deleted %ld point", "Deleted %ld points", removed);
5528   g_snprintf(str, 64, tmp_str, removed);
5529   a_dialog_info_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), str);
5530
5531   vik_layer_emit_update ( VIK_LAYER(vtl) );
5532 }
5533
5534 /**
5535  * Delete adjacent track points with the same timestamp
5536  * Normally new tracks that are 'routes' won't have any timestamps so should be OK to clean up the track
5537  */
5538 static void trw_layer_delete_points_same_time ( gpointer pass_along[6] )
5539 {
5540   VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
5541   VikTrack *trk;
5542   if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5543     trk = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
5544   else
5545     trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
5546
5547   if ( !trk )
5548     return;
5549
5550   gulong removed = vik_track_remove_same_time_points ( trk );
5551
5552   // Track has been updated so update tps:
5553   trw_layer_cancel_tps_of_track ( vtl, trk );
5554
5555   // Inform user how much was deleted as it's not obvious from the normal view
5556   gchar str[64];
5557   const gchar *tmp_str = ngettext("Deleted %ld point", "Deleted %ld points", removed);
5558   g_snprintf(str, 64, tmp_str, removed);
5559   a_dialog_info_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), str);
5560
5561   vik_layer_emit_update ( VIK_LAYER(vtl) );
5562 }
5563
5564 /**
5565  * Reverse a track
5566  */
5567 static void trw_layer_reverse ( gpointer pass_along[6] )
5568 {
5569   VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
5570   VikTrack *track;
5571   if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5572     track = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
5573   else
5574     track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
5575
5576   if ( ! track )
5577     return;
5578
5579   vik_track_reverse ( track );
5580  
5581   vik_layer_emit_update ( VIK_LAYER(pass_along[0]) );
5582 }
5583
5584 /**
5585  * Similar to trw_layer_enum_item, but this uses a sorted method
5586  */
5587 /* Currently unused
5588 static void trw_layer_sorted_name_list(gpointer key, gpointer value, gpointer udata)
5589 {
5590   GList **list = (GList**)udata;
5591   // *list = g_list_prepend(*all, key); //unsorted method
5592   // Sort named list alphabetically
5593   *list = g_list_insert_sorted_with_data (*list, key, sort_alphabetically, NULL);
5594 }
5595 */
5596
5597 /**
5598  * Now Waypoint specific sort
5599  */
5600 static void trw_layer_sorted_wp_id_by_name_list (const gpointer id, const VikWaypoint *wp, gpointer udata)
5601 {
5602   GList **list = (GList**)udata;
5603   // Sort named list alphabetically
5604   *list = g_list_insert_sorted_with_data (*list, wp->name, sort_alphabetically, NULL);
5605 }
5606
5607 /**
5608  * Track specific sort
5609  */
5610 static void trw_layer_sorted_track_id_by_name_list (const gpointer id, const VikTrack *trk, gpointer udata)
5611 {
5612   GList **list = (GList**)udata;
5613   // Sort named list alphabetically
5614   *list = g_list_insert_sorted_with_data (*list, trk->name, sort_alphabetically, NULL);
5615 }
5616
5617
5618 typedef struct {
5619   gboolean    has_same_track_name;
5620   const gchar *same_track_name;
5621 } same_track_name_udata;
5622
5623 static gint check_tracks_for_same_name ( gconstpointer aa, gconstpointer bb, gpointer udata )
5624 {
5625   const gchar* namea = (const gchar*) aa;
5626   const gchar* nameb = (const gchar*) bb;
5627
5628   // the test
5629   gint result = strcmp ( namea, nameb );
5630
5631   if ( result == 0 ) {
5632     // Found two names the same
5633     same_track_name_udata *user_data = udata;
5634     user_data->has_same_track_name = TRUE;
5635     user_data->same_track_name = namea;
5636   }
5637
5638   // Leave ordering the same
5639   return 0;
5640 }
5641
5642 /**
5643  * Find out if any tracks have the same name in this hash table
5644  */
5645 static gboolean trw_layer_has_same_track_names ( GHashTable *ht_tracks )
5646 {
5647   // Sort items by name, then compare if any next to each other are the same
5648
5649   GList *track_names = NULL;
5650   g_hash_table_foreach ( ht_tracks, (GHFunc) trw_layer_sorted_track_id_by_name_list, &track_names );
5651
5652   // No tracks
5653   if ( ! track_names )
5654     return FALSE;
5655
5656   same_track_name_udata udata;
5657   udata.has_same_track_name = FALSE;
5658
5659   // Use sort routine to traverse list comparing items
5660   // Don't care how this list ends up ordered ( doesn't actually change ) - care about the returned status
5661   GList *dummy_list = g_list_sort_with_data ( track_names, check_tracks_for_same_name, &udata );
5662   // Still no tracks...
5663   if ( ! dummy_list )
5664     return FALSE;
5665
5666   return udata.has_same_track_name;
5667 }
5668
5669 /**
5670  * Force unqiue track names for the track table specified
5671  * Note the panel is a required parameter to enable the update of the names displayed
5672  * Specify if on tracks or else on routes
5673  */
5674 static void vik_trw_layer_uniquify_tracks ( VikTrwLayer *vtl, VikLayersPanel *vlp, GHashTable *track_table, gboolean ontrack )
5675 {
5676   // . Search list for an instance of repeated name
5677   // . get track of this name
5678   // . create new name
5679   // . rename track & update equiv. treeview iter
5680   // . repeat until all different
5681
5682   same_track_name_udata udata;
5683
5684   GList *track_names = NULL;
5685   udata.has_same_track_name = FALSE;
5686   udata.same_track_name = NULL;
5687
5688   g_hash_table_foreach ( track_table, (GHFunc) trw_layer_sorted_track_id_by_name_list, &track_names );
5689
5690   // No tracks
5691   if ( ! track_names )
5692     return;
5693
5694   GList *dummy_list1 = g_list_sort_with_data ( track_names, check_tracks_for_same_name, &udata );
5695
5696   // Still no tracks...
5697   if ( ! dummy_list1 )
5698     return;
5699
5700   while ( udata.has_same_track_name ) {
5701
5702     // Find a track with the same name
5703     VikTrack *trk;
5704     if ( ontrack )
5705       trk = vik_trw_layer_get_track ( vtl, (gpointer) udata.same_track_name );
5706     else
5707       trk = vik_trw_layer_get_route ( vtl, (gpointer) udata.same_track_name );
5708
5709     if ( ! trk ) {
5710       // Broken :(
5711       g_critical("Houston, we've had a problem.");
5712       vik_statusbar_set_message ( vik_window_get_statusbar (VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl))), VIK_STATUSBAR_INFO, 
5713                                   _("Internal Error in vik_trw_layer_uniquify_tracks") );
5714       return;
5715     }
5716
5717     // Rename it
5718     gchar *newname = trw_layer_new_unique_sublayer_name ( vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, udata.same_track_name );
5719     vik_track_set_name ( trk, newname );
5720
5721     trku_udata udataU;
5722     udataU.trk  = trk;
5723     udataU.uuid = NULL;
5724
5725     // Need want key of it for treeview update
5726     gpointer *trkf = g_hash_table_find ( track_table, (GHRFunc) trw_layer_track_find_uuid, &udataU );
5727
5728     if ( trkf && udataU.uuid ) {
5729
5730       GtkTreeIter *it;
5731       if ( ontrack )
5732         it = g_hash_table_lookup ( vtl->tracks_iters, udataU.uuid );
5733       else
5734         it = g_hash_table_lookup ( vtl->routes_iters, udataU.uuid );
5735
5736       if ( it ) {
5737         vik_treeview_item_set_name ( VIK_LAYER(vtl)->vt, it, newname );
5738         if ( ontrack )
5739           vik_treeview_sort_children ( VIK_LAYER(vtl)->vt, &(vtl->tracks_iter), vtl->wp_sort_order );
5740         else
5741           vik_treeview_sort_children ( VIK_LAYER(vtl)->vt, &(vtl->routes_iter), vtl->wp_sort_order );
5742       }
5743     }
5744
5745     // Start trying to find same names again...
5746     track_names = NULL;
5747     g_hash_table_foreach ( track_table, (GHFunc) trw_layer_sorted_track_id_by_name_list, &track_names );
5748     udata.has_same_track_name = FALSE;
5749     GList *dummy_list2 = g_list_sort_with_data ( track_names, check_tracks_for_same_name, &udata );
5750
5751     // No tracks any more - give up searching
5752     if ( ! dummy_list2 )
5753       udata.has_same_track_name = FALSE;
5754   }
5755
5756   // Update
5757   vik_layers_panel_emit_update ( vlp );
5758 }
5759
5760 static void trw_layer_sort_order_a2z ( gpointer pass_along[6] )
5761 {
5762   VikTrwLayer *vtl = VIK_TRW_LAYER(pass_along[0]);
5763   GtkTreeIter *iter;
5764
5765   switch (GPOINTER_TO_INT (pass_along[2])) {
5766   case VIK_TRW_LAYER_SUBLAYER_TRACKS:
5767     iter = &(vtl->tracks_iter);
5768     vtl->track_sort_order = VL_SO_ALPHABETICAL_ASCENDING;
5769     break;
5770   case VIK_TRW_LAYER_SUBLAYER_ROUTES:
5771     iter = &(vtl->routes_iter);
5772     vtl->track_sort_order = VL_SO_ALPHABETICAL_ASCENDING;
5773     break;
5774   default: // VIK_TRW_LAYER_SUBLAYER_WAYPOINTS:
5775     iter = &(vtl->waypoints_iter);
5776     vtl->wp_sort_order = VL_SO_ALPHABETICAL_ASCENDING;
5777     break;
5778   }
5779
5780   vik_treeview_sort_children ( VIK_LAYER(vtl)->vt, iter, VL_SO_ALPHABETICAL_ASCENDING );
5781 }
5782
5783 static void trw_layer_sort_order_z2a ( gpointer pass_along[6] )
5784 {
5785   VikTrwLayer *vtl = VIK_TRW_LAYER(pass_along[0]);
5786   GtkTreeIter *iter;
5787
5788   switch (GPOINTER_TO_INT (pass_along[2])) {
5789   case VIK_TRW_LAYER_SUBLAYER_TRACKS:
5790     iter = &(vtl->tracks_iter);
5791     vtl->track_sort_order = VL_SO_ALPHABETICAL_DESCENDING;
5792     break;
5793   case VIK_TRW_LAYER_SUBLAYER_ROUTES:
5794     iter = &(vtl->routes_iter);
5795     vtl->track_sort_order = VL_SO_ALPHABETICAL_DESCENDING;
5796     break;
5797   default: // VIK_TRW_LAYER_SUBLAYER_WAYPOINTS:
5798     iter = &(vtl->waypoints_iter);
5799     vtl->wp_sort_order = VL_SO_ALPHABETICAL_DESCENDING;
5800     break;
5801   }
5802
5803   vik_treeview_sort_children ( VIK_LAYER(vtl)->vt, iter, VL_SO_ALPHABETICAL_DESCENDING );
5804 }
5805
5806 /**
5807  *
5808  */
5809 static void trw_layer_delete_tracks_from_selection ( gpointer lav[2] )
5810 {
5811   VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
5812   GList *all = NULL;
5813
5814   // Ensure list of track names offered is unique
5815   if ( trw_layer_has_same_track_names ( vtl->tracks ) ) {
5816     if ( a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
5817                               _("Multiple entries with the same name exist. This method only works with unique names. Force unique names now?"), NULL ) ) {
5818       vik_trw_layer_uniquify_tracks ( vtl, VIK_LAYERS_PANEL(lav[1]), vtl->tracks, TRUE );
5819     }
5820     else
5821       return;
5822   }
5823
5824   // Sort list alphabetically for better presentation
5825   g_hash_table_foreach(vtl->tracks, (GHFunc) trw_layer_sorted_track_id_by_name_list, &all);
5826
5827   if ( ! all ) {
5828     a_dialog_error_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), _("No tracks found"));
5829     return;
5830   }
5831
5832   // Get list of items to delete from the user
5833   GList *delete_list = a_dialog_select_from_list(VIK_GTK_WINDOW_FROM_LAYER(vtl),
5834                                                  all,
5835                                                  TRUE,
5836                                                  _("Delete Selection"),
5837                                                  _("Select tracks to delete"));
5838   g_list_free(all);
5839
5840   // Delete requested tracks
5841   // since specificly requested, IMHO no need for extra confirmation
5842   if ( delete_list ) {
5843     GList *l;
5844     for (l = delete_list; l != NULL; l = g_list_next(l)) {
5845       // This deletes first trk it finds of that name (but uniqueness is enforced above)
5846       trw_layer_delete_track_by_name (vtl, l->data, vtl->tracks);
5847     }
5848     g_list_free(delete_list);
5849     vik_layer_emit_update( VIK_LAYER(vtl) );
5850   }
5851 }
5852
5853 /**
5854  *
5855  */
5856 static void trw_layer_delete_routes_from_selection ( gpointer lav[2] )
5857 {
5858   VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
5859   GList *all = NULL;
5860
5861   // Ensure list of track names offered is unique
5862   if ( trw_layer_has_same_track_names ( vtl->routes ) ) {
5863     if ( a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
5864                               _("Multiple entries with the same name exist. This method only works with unique names. Force unique names now?"), NULL ) ) {
5865       vik_trw_layer_uniquify_tracks ( vtl, VIK_LAYERS_PANEL(lav[1]), vtl->routes, FALSE );
5866     }
5867     else
5868       return;
5869   }
5870
5871   // Sort list alphabetically for better presentation
5872   g_hash_table_foreach(vtl->routes, (GHFunc) trw_layer_sorted_track_id_by_name_list, &all);
5873
5874   if ( ! all ) {
5875     a_dialog_error_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), _("No routes found"));
5876     return;
5877   }
5878
5879   // Get list of items to delete from the user
5880   GList *delete_list = a_dialog_select_from_list ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
5881                                                    all,
5882                                                    TRUE,
5883                                                    _("Delete Selection"),
5884                                                    _("Select routes to delete") );
5885   g_list_free(all);
5886
5887   // Delete requested routes
5888   // since specificly requested, IMHO no need for extra confirmation
5889   if ( delete_list ) {
5890     GList *l;
5891     for (l = delete_list; l != NULL; l = g_list_next(l)) {
5892       // This deletes first route it finds of that name (but uniqueness is enforced above)
5893       trw_layer_delete_track_by_name (vtl, l->data, vtl->routes);
5894     }
5895     g_list_free(delete_list);
5896     vik_layer_emit_update( VIK_LAYER(vtl) );
5897   }
5898 }
5899
5900 typedef struct {
5901   gboolean    has_same_waypoint_name;
5902   const gchar *same_waypoint_name;
5903 } same_waypoint_name_udata;
5904
5905 static gint check_waypoints_for_same_name ( gconstpointer aa, gconstpointer bb, gpointer udata )
5906 {
5907   const gchar* namea = (const gchar*) aa;
5908   const gchar* nameb = (const gchar*) bb;
5909
5910   // the test
5911   gint result = strcmp ( namea, nameb );
5912
5913   if ( result == 0 ) {
5914     // Found two names the same
5915     same_waypoint_name_udata *user_data = udata;
5916     user_data->has_same_waypoint_name = TRUE;
5917     user_data->same_waypoint_name = namea;
5918   }
5919
5920   // Leave ordering the same
5921   return 0;
5922 }
5923
5924 /**
5925  * Find out if any waypoints have the same name in this layer
5926  */
5927 gboolean trw_layer_has_same_waypoint_names ( VikTrwLayer *vtl )
5928 {
5929   // Sort items by name, then compare if any next to each other are the same
5930
5931   GList *waypoint_names = NULL;
5932   g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_sorted_wp_id_by_name_list, &waypoint_names );
5933
5934   // No waypoints
5935   if ( ! waypoint_names )
5936     return FALSE;
5937
5938   same_waypoint_name_udata udata;
5939   udata.has_same_waypoint_name = FALSE;
5940
5941   // Use sort routine to traverse list comparing items
5942   // Don't care how this list ends up ordered ( doesn't actually change ) - care about the returned status
5943   GList *dummy_list = g_list_sort_with_data ( waypoint_names, check_waypoints_for_same_name, &udata );
5944   // Still no waypoints...
5945   if ( ! dummy_list )
5946     return FALSE;
5947
5948   return udata.has_same_waypoint_name;
5949 }
5950
5951 /**
5952  * Force unqiue waypoint names for this layer
5953  * Note the panel is a required parameter to enable the update of the names displayed
5954  */
5955 static void vik_trw_layer_uniquify_waypoints ( VikTrwLayer *vtl, VikLayersPanel *vlp )
5956 {
5957   // . Search list for an instance of repeated name
5958   // . get waypoint of this name
5959   // . create new name
5960   // . rename waypoint & update equiv. treeview iter
5961   // . repeat until all different
5962
5963   same_waypoint_name_udata udata;
5964
5965   GList *waypoint_names = NULL;
5966   udata.has_same_waypoint_name = FALSE;
5967   udata.same_waypoint_name = NULL;
5968
5969   g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_sorted_wp_id_by_name_list, &waypoint_names );
5970
5971   // No waypoints
5972   if ( ! waypoint_names )
5973     return;
5974
5975   GList *dummy_list1 = g_list_sort_with_data ( waypoint_names, check_waypoints_for_same_name, &udata );
5976
5977   // Still no waypoints...
5978   if ( ! dummy_list1 )
5979     return;
5980
5981   while ( udata.has_same_waypoint_name ) {
5982
5983     // Find a waypoint with the same name
5984     VikWaypoint *waypoint = vik_trw_layer_get_waypoint ( vtl, (gpointer) udata.same_waypoint_name );
5985
5986     if ( ! waypoint ) {
5987       // Broken :(
5988       g_critical("Houston, we've had a problem.");
5989       vik_statusbar_set_message ( vik_window_get_statusbar (VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl))), VIK_STATUSBAR_INFO, 
5990                                   _("Internal Error in vik_trw_layer_uniquify_waypoints") );
5991       return;
5992     }
5993
5994     // Rename it
5995     gchar *newname = trw_layer_new_unique_sublayer_name ( vtl, VIK_TRW_LAYER_SUBLAYER_WAYPOINT, udata.same_waypoint_name );
5996
5997     trw_layer_waypoint_rename ( vtl, waypoint, newname );
5998
5999     // Start trying to find same names again...
6000     waypoint_names = NULL;
6001     g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_sorted_wp_id_by_name_list, &waypoint_names );
6002     udata.has_same_waypoint_name = FALSE;
6003     GList *dummy_list2 = g_list_sort_with_data ( waypoint_names, check_waypoints_for_same_name, &udata );
6004
6005     // No waypoints any more - give up searching
6006     if ( ! dummy_list2 )
6007       udata.has_same_waypoint_name = FALSE;
6008   }
6009
6010   // Update
6011   vik_layers_panel_emit_update ( vlp );
6012 }
6013
6014 /**
6015  *
6016  */
6017 static void trw_layer_delete_waypoints_from_selection ( gpointer lav[2] )
6018 {
6019   VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
6020   GList *all = NULL;
6021
6022   // Ensure list of waypoint names offered is unique
6023   if ( trw_layer_has_same_waypoint_names ( vtl ) ) {
6024     if ( a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
6025                               _("Multiple entries with the same name exist. This method only works with unique names. Force unique names now?"), NULL ) ) {
6026       vik_trw_layer_uniquify_waypoints ( vtl, VIK_LAYERS_PANEL(lav[1]) );
6027     }
6028     else
6029       return;
6030   }
6031
6032   // Sort list alphabetically for better presentation
6033   g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_sorted_wp_id_by_name_list, &all);
6034   if ( ! all ) {
6035     a_dialog_error_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), _("No waypoints found"));
6036     return;
6037   }
6038
6039   all = g_list_sort_with_data(all, sort_alphabetically, NULL);
6040
6041   // Get list of items to delete from the user
6042   GList *delete_list = a_dialog_select_from_list(VIK_GTK_WINDOW_FROM_LAYER(vtl),
6043                                                  all,
6044                                                  TRUE,
6045                                                  _("Delete Selection"),
6046                                                  _("Select waypoints to delete"));
6047   g_list_free(all);
6048
6049   // Delete requested waypoints
6050   // since specificly requested, IMHO no need for extra confirmation
6051   if ( delete_list ) {
6052     GList *l;
6053     for (l = delete_list; l != NULL; l = g_list_next(l)) {
6054       // This deletes first waypoint it finds of that name (but uniqueness is enforced above)
6055       trw_layer_delete_waypoint_by_name (vtl, l->data);
6056     }
6057     g_list_free(delete_list);
6058
6059     trw_layer_calculate_bounds_waypoints ( vtl );
6060     vik_layer_emit_update( VIK_LAYER(vtl) );
6061   }
6062
6063 }
6064
6065 /**
6066  *
6067  */
6068 static void trw_layer_iter_visibility_toggle ( gpointer id, GtkTreeIter *it, VikTreeview *vt )
6069 {
6070   vik_treeview_item_toggle_visible ( vt, it );
6071 }
6072
6073 /**
6074  *
6075  */
6076 static void trw_layer_iter_visibility ( gpointer id, GtkTreeIter *it, gpointer vis_data[2] )
6077 {
6078   vik_treeview_item_set_visible ( (VikTreeview*)vis_data[0], it, GPOINTER_TO_INT (vis_data[1]) );
6079 }
6080
6081 /**
6082  *
6083  */
6084 static void trw_layer_waypoints_visibility ( gpointer id, VikWaypoint *wp, gpointer on_off )
6085 {
6086   wp->visible = GPOINTER_TO_INT (on_off);
6087 }
6088
6089 /**
6090  *
6091  */
6092 static void trw_layer_waypoints_toggle_visibility ( gpointer id, VikWaypoint *wp )
6093 {
6094   wp->visible = !wp->visible;
6095 }
6096
6097 /**
6098  *
6099  */
6100 static void trw_layer_waypoints_visibility_off ( gpointer lav[2] )
6101 {
6102   VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
6103   gpointer vis_data[2] = { VIK_LAYER(vtl)->vt, GINT_TO_POINTER(FALSE) };
6104   g_hash_table_foreach ( vtl->waypoints_iters, (GHFunc) trw_layer_iter_visibility, vis_data );
6105   g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_waypoints_visibility, vis_data[1] );
6106   // Redraw
6107   vik_layer_emit_update ( VIK_LAYER(vtl) );
6108 }
6109
6110 /**
6111  *
6112  */
6113 static void trw_layer_waypoints_visibility_on ( gpointer lav[2] )
6114 {
6115   VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
6116   gpointer vis_data[2] = { VIK_LAYER(vtl)->vt, GINT_TO_POINTER(TRUE) };
6117   g_hash_table_foreach ( vtl->waypoints_iters, (GHFunc) trw_layer_iter_visibility, vis_data );
6118   g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_waypoints_visibility, vis_data[1] );
6119   // Redraw
6120   vik_layer_emit_update ( VIK_LAYER(vtl) );
6121 }
6122
6123 /**
6124  *
6125  */
6126 static void trw_layer_waypoints_visibility_toggle ( gpointer lav[2] )
6127 {
6128   VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
6129   g_hash_table_foreach ( vtl->waypoints_iters, (GHFunc) trw_layer_iter_visibility_toggle, VIK_LAYER(vtl)->vt );
6130   g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_waypoints_toggle_visibility, NULL );
6131   // Redraw
6132   vik_layer_emit_update ( VIK_LAYER(vtl) );
6133 }
6134
6135 /**
6136  *
6137  */
6138 static void trw_layer_tracks_visibility ( gpointer id, VikTrack *trk, gpointer on_off )
6139 {
6140   trk->visible = GPOINTER_TO_INT (on_off);
6141 }
6142
6143 /**
6144  *
6145  */
6146 static void trw_layer_tracks_toggle_visibility ( gpointer id, VikTrack *trk )
6147 {
6148   trk->visible = !trk->visible;
6149 }
6150
6151 /**
6152  *
6153  */
6154 static void trw_layer_tracks_visibility_off ( gpointer lav[2] )
6155 {
6156   VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
6157   gpointer vis_data[2] = { VIK_LAYER(vtl)->vt, GINT_TO_POINTER(FALSE) };
6158   g_hash_table_foreach ( vtl->tracks_iters, (GHFunc) trw_layer_iter_visibility, vis_data );
6159   g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_tracks_visibility, vis_data[1] );
6160   // Redraw
6161   vik_layer_emit_update ( VIK_LAYER(vtl) );
6162 }
6163
6164 /**
6165  *
6166  */
6167 static void trw_layer_tracks_visibility_on ( gpointer lav[2] )
6168 {
6169   VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
6170   gpointer vis_data[2] = { VIK_LAYER(vtl)->vt, GINT_TO_POINTER(TRUE) };
6171   g_hash_table_foreach ( vtl->tracks_iters, (GHFunc) trw_layer_iter_visibility, vis_data );
6172   g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_tracks_visibility, vis_data[1] );
6173   // Redraw
6174   vik_layer_emit_update ( VIK_LAYER(vtl) );
6175 }
6176
6177 /**
6178  *
6179  */
6180 static void trw_layer_tracks_visibility_toggle ( gpointer lav[2] )
6181 {
6182   VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
6183   g_hash_table_foreach ( vtl->tracks_iters, (GHFunc) trw_layer_iter_visibility_toggle, VIK_LAYER(vtl)->vt );
6184   g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_tracks_toggle_visibility, NULL );
6185   // Redraw
6186   vik_layer_emit_update ( VIK_LAYER(vtl) );
6187 }
6188
6189 /**
6190  *
6191  */
6192 static void trw_layer_routes_visibility_off ( gpointer lav[2] )
6193 {
6194   VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
6195   gpointer vis_data[2] = { VIK_LAYER(vtl)->vt, GINT_TO_POINTER(FALSE) };
6196   g_hash_table_foreach ( vtl->routes_iters, (GHFunc) trw_layer_iter_visibility, vis_data );
6197   g_hash_table_foreach ( vtl->routes, (GHFunc) trw_layer_tracks_visibility, vis_data[1] );
6198   // Redraw
6199   vik_layer_emit_update ( VIK_LAYER(vtl) );
6200 }
6201
6202 /**
6203  *
6204  */
6205 static void trw_layer_routes_visibility_on ( gpointer lav[2] )
6206 {
6207   VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
6208   gpointer vis_data[2] = { VIK_LAYER(vtl)->vt, GINT_TO_POINTER(TRUE) };
6209   g_hash_table_foreach ( vtl->routes_iters, (GHFunc) trw_layer_iter_visibility, vis_data );
6210   g_hash_table_foreach ( vtl->routes, (GHFunc) trw_layer_tracks_visibility, vis_data[1] );
6211   // Redraw
6212   vik_layer_emit_update ( VIK_LAYER(vtl) );
6213 }
6214
6215 /**
6216  *
6217  */
6218 static void trw_layer_routes_visibility_toggle ( gpointer lav[2] )
6219 {
6220   VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
6221   g_hash_table_foreach ( vtl->routes_iters, (GHFunc) trw_layer_iter_visibility_toggle, VIK_LAYER(vtl)->vt );
6222   g_hash_table_foreach ( vtl->routes, (GHFunc) trw_layer_tracks_toggle_visibility, NULL );
6223   // Redraw
6224   vik_layer_emit_update ( VIK_LAYER(vtl) );
6225 }
6226
6227 /**
6228  * trw_layer_analyse_close:
6229  *
6230  * Stuff to do on dialog closure
6231  */
6232 static void trw_layer_analyse_close ( GtkWidget *dialog, gint resp, VikLayer* vl )
6233 {
6234   VikTrwLayer *vtl = VIK_TRW_LAYER(vl);
6235   gtk_widget_destroy ( dialog );
6236   vtl->tracks_analysis_dialog = NULL;
6237 }
6238
6239 /**
6240  * trw_layer_analyse_create_list:
6241  *
6242  * Create the latest list of tracks with the associated layer(s)
6243  *  Although this will always be from a single layer here
6244  */
6245 static GList* trw_layer_analyse_create_list ( VikLayer *vl, gpointer user_data )
6246 {
6247   VikTrwLayer *vtl = VIK_TRW_LAYER(vl);
6248   GList *tracks = NULL;
6249   if ( GPOINTER_TO_INT(user_data) == VIK_TRW_LAYER_SUBLAYER_TRACKS )
6250     tracks = g_hash_table_get_values ( vik_trw_layer_get_tracks(vtl) );
6251   else
6252     tracks = g_hash_table_get_values ( vik_trw_layer_get_routes(vtl) );
6253
6254   GList *tracks_and_layers = NULL;
6255   // build tracks_and_layers list
6256   tracks = g_list_first ( tracks );
6257   while ( tracks ) {
6258     vik_trw_track_list_t *vtdl = g_malloc (sizeof(vik_trw_track_list_t));
6259     vtdl->trk = VIK_TRACK(tracks->data);
6260     vtdl->vtl = vtl;
6261     tracks_and_layers = g_list_prepend ( tracks_and_layers, vtdl );
6262     tracks = g_list_next ( tracks );
6263   }
6264
6265   return tracks_and_layers;
6266 }
6267
6268 static void trw_layer_tracks_stats ( gpointer lav[2] )
6269 {
6270   VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
6271   // There can only be one!
6272   if ( vtl->tracks_analysis_dialog )
6273     return;
6274
6275   vtl->tracks_analysis_dialog = vik_trw_layer_analyse_this ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
6276                                                              VIK_LAYER(vtl)->name,
6277                                                              VIK_LAYER(vtl),
6278                                                              GINT_TO_POINTER(VIK_TRW_LAYER_SUBLAYER_TRACKS),
6279                                                              trw_layer_analyse_create_list,
6280                                                              trw_layer_analyse_close );
6281 }
6282
6283 /**
6284  *
6285  */
6286 static void trw_layer_routes_stats ( gpointer lav[2] )
6287 {
6288   VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
6289   // There can only be one!
6290   if ( vtl->tracks_analysis_dialog )
6291     return;
6292
6293   vtl->tracks_analysis_dialog = vik_trw_layer_analyse_this ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
6294                                                              VIK_LAYER(vtl)->name,
6295                                                              VIK_LAYER(vtl),
6296                                                              GINT_TO_POINTER(VIK_TRW_LAYER_SUBLAYER_ROUTES),
6297                                                              trw_layer_analyse_create_list,
6298                                                              trw_layer_analyse_close );
6299 }
6300
6301 static void trw_layer_goto_waypoint ( gpointer pass_along[6] )
6302 {
6303   VikWaypoint *wp = g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->waypoints, pass_along[3] );
6304   if ( wp )
6305     goto_coord ( pass_along[1], pass_along[0], pass_along[5], &(wp->coord) );
6306 }
6307
6308 static void trw_layer_waypoint_gc_webpage ( gpointer pass_along[6] )
6309 {
6310   VikWaypoint *wp = g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->waypoints, pass_along[3] );
6311   if ( !wp )
6312     return;
6313   gchar *webpage = g_strdup_printf("http://www.geocaching.com/seek/cache_details.aspx?wp=%s", wp->name );
6314   open_url(VIK_GTK_WINDOW_FROM_LAYER(VIK_LAYER(pass_along[0])), webpage);
6315   g_free ( webpage );
6316 }
6317
6318 static void trw_layer_waypoint_webpage ( gpointer pass_along[6] )
6319 {
6320   VikWaypoint *wp = g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->waypoints, pass_along[3] );
6321   if ( !wp )
6322     return;
6323   if ( !strncmp(wp->comment, "http", 4) ) {
6324     open_url(VIK_GTK_WINDOW_FROM_LAYER(VIK_LAYER(pass_along[0])), wp->comment);
6325   } else if ( !strncmp(wp->description, "http", 4) ) {
6326     open_url(VIK_GTK_WINDOW_FROM_LAYER(VIK_LAYER(pass_along[0])), wp->description);
6327   }
6328 }
6329
6330 static const gchar* trw_layer_sublayer_rename_request ( VikTrwLayer *l, const gchar *newname, gpointer vlp, gint subtype, gpointer sublayer, GtkTreeIter *iter )
6331 {
6332   if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT )
6333   {
6334     VikWaypoint *wp = g_hash_table_lookup ( l->waypoints, sublayer );
6335
6336     // No actual change to the name supplied
6337     if ( wp->name )
6338       if (strcmp(newname, wp->name) == 0 )
6339        return NULL;
6340
6341     VikWaypoint *wpf = vik_trw_layer_get_waypoint ( l, newname );
6342
6343     if ( wpf ) {
6344       // An existing waypoint has been found with the requested name
6345       if ( ! a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(l),
6346            _("A waypoint with the name \"%s\" already exists. Really rename to the same name?"),
6347            newname ) )
6348         return NULL;
6349     }
6350
6351     // Update WP name and refresh the treeview
6352     vik_waypoint_set_name (wp, newname);
6353
6354     vik_treeview_item_set_name ( VIK_LAYER(l)->vt, iter, newname );
6355     vik_treeview_sort_children ( VIK_LAYER(l)->vt, &(l->waypoints_iter), l->wp_sort_order );
6356
6357     vik_layers_panel_emit_update ( VIK_LAYERS_PANEL(vlp) );
6358
6359     return newname;
6360   }
6361
6362   if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
6363   {
6364     VikTrack *trk = g_hash_table_lookup ( l->tracks, sublayer );
6365
6366     // No actual change to the name supplied
6367     if ( trk->name )
6368       if (strcmp(newname, trk->name) == 0)
6369         return NULL;
6370
6371     VikTrack *trkf = vik_trw_layer_get_track ( l, (gpointer) newname );
6372
6373     if ( trkf ) {
6374       // An existing track has been found with the requested name
6375       if ( ! a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(l),
6376           _("A track with the name \"%s\" already exists. Really rename to the same name?"),
6377           newname ) )
6378         return NULL;
6379     }
6380     // Update track name and refresh GUI parts
6381     vik_track_set_name (trk, newname);
6382
6383     // Update any subwindows that could be displaying this track which has changed name
6384     // Only one Track Edit Window
6385     if ( l->current_tp_track == trk && l->tpwin ) {
6386       vik_trw_layer_tpwin_set_track_name ( l->tpwin, newname );
6387     }
6388     // Property Dialog of the track
6389     vik_trw_layer_propwin_update ( trk );
6390
6391     vik_treeview_item_set_name ( VIK_LAYER(l)->vt, iter, newname );
6392     vik_treeview_sort_children ( VIK_LAYER(l)->vt, &(l->tracks_iter), l->track_sort_order );
6393
6394     vik_layers_panel_emit_update ( VIK_LAYERS_PANEL(vlp) );
6395
6396     return newname;
6397   }
6398
6399   if ( subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE )
6400   {
6401     VikTrack *trk = g_hash_table_lookup ( l->routes, sublayer );
6402
6403     // No actual change to the name supplied
6404     if ( trk->name )
6405       if (strcmp(newname, trk->name) == 0)
6406         return NULL;
6407
6408     VikTrack *trkf = vik_trw_layer_get_route ( l, (gpointer) newname );
6409
6410     if ( trkf ) {
6411       // An existing track has been found with the requested name
6412       if ( ! a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(l),
6413           _("A route with the name \"%s\" already exists. Really rename to the same name?"),
6414           newname ) )
6415         return NULL;
6416     }
6417     // Update track name and refresh GUI parts
6418     vik_track_set_name (trk, newname);
6419
6420     // Update any subwindows that could be displaying this track which has changed name
6421     // Only one Track Edit Window
6422     if ( l->current_tp_track == trk && l->tpwin ) {
6423       vik_trw_layer_tpwin_set_track_name ( l->tpwin, newname );
6424     }
6425     // Property Dialog of the track
6426     vik_trw_layer_propwin_update ( trk );
6427
6428     vik_treeview_item_set_name ( VIK_LAYER(l)->vt, iter, newname );
6429     vik_treeview_sort_children ( VIK_LAYER(l)->vt, &(l->tracks_iter), l->track_sort_order );
6430
6431     vik_layers_panel_emit_update ( VIK_LAYERS_PANEL(vlp) );
6432
6433     return newname;
6434   }
6435   return NULL;
6436 }
6437
6438 static gboolean is_valid_geocache_name ( gchar *str )
6439 {
6440   gint len = strlen ( str );
6441   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]));
6442 }
6443
6444 static void trw_layer_track_use_with_filter ( gpointer pass_along[6] )
6445 {
6446   VikTrack *trk = g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, pass_along[3] );
6447   a_acquire_set_filter_track ( trk );
6448 }
6449
6450 #ifdef VIK_CONFIG_GOOGLE
6451 static gboolean is_valid_google_route ( VikTrwLayer *vtl, const gpointer track_id )
6452 {
6453   VikTrack *tr = g_hash_table_lookup ( vtl->routes, track_id );
6454   return ( tr && tr->comment && strlen(tr->comment) > 7 && !strncmp(tr->comment, "from:", 5) );
6455 }
6456
6457 static void trw_layer_google_route_webpage ( gpointer pass_along[6] )
6458 {
6459   VikTrack *tr = g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->routes, pass_along[3] );
6460   if ( tr ) {
6461     gchar *escaped = uri_escape ( tr->comment );
6462     gchar *webpage = g_strdup_printf("http://maps.google.com/maps?f=q&hl=en&q=%s", escaped );
6463     open_url(VIK_GTK_WINDOW_FROM_LAYER(VIK_LAYER(pass_along[0])), webpage);
6464     g_free ( escaped );
6465     g_free ( webpage );
6466   }
6467 }
6468 #endif
6469
6470 /* vlp can be NULL if necessary - i.e. right-click from a tool */
6471 /* viewpoint is now available instead */
6472 static gboolean trw_layer_sublayer_add_menu_items ( VikTrwLayer *l, GtkMenu *menu, gpointer vlp, gint subtype, gpointer sublayer, GtkTreeIter *iter, VikViewport *vvp )
6473 {
6474   static gpointer pass_along[8];
6475   GtkWidget *item;
6476   gboolean rv = FALSE;
6477
6478   pass_along[0] = l;
6479   pass_along[1] = vlp;
6480   pass_along[2] = GINT_TO_POINTER (subtype);
6481   pass_along[3] = sublayer;
6482   pass_along[4] = GINT_TO_POINTER (1); // Confirm delete request
6483   pass_along[5] = vvp;
6484   pass_along[6] = iter;
6485   pass_along[7] = NULL; // For misc purposes - maybe track or waypoint
6486
6487   if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT || subtype == VIK_TRW_LAYER_SUBLAYER_TRACK || subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE )
6488   {
6489     rv = TRUE;
6490
6491     item = gtk_image_menu_item_new_from_stock ( GTK_STOCK_PROPERTIES, NULL );
6492     g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_properties_item), pass_along );
6493     gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6494     gtk_widget_show ( item );
6495
6496     if (subtype == VIK_TRW_LAYER_SUBLAYER_TRACK) {
6497       VikTrack *tr = g_hash_table_lookup ( l->tracks, sublayer );
6498       if (tr && tr->property_dialog)
6499         gtk_widget_set_sensitive(GTK_WIDGET(item), FALSE );
6500     }
6501     if (subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE) {
6502       VikTrack *tr = g_hash_table_lookup ( l->routes, sublayer );
6503       if (tr && tr->property_dialog)
6504         gtk_widget_set_sensitive(GTK_WIDGET(item), FALSE );
6505     }
6506
6507     item = gtk_image_menu_item_new_from_stock ( GTK_STOCK_CUT, NULL );
6508     g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_cut_item_cb), pass_along );
6509     gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6510     gtk_widget_show ( item );
6511
6512     item = gtk_image_menu_item_new_from_stock ( GTK_STOCK_COPY, NULL );
6513     g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_copy_item_cb), pass_along );
6514     gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6515     gtk_widget_show ( item );
6516
6517     item = gtk_image_menu_item_new_from_stock ( GTK_STOCK_DELETE, NULL );
6518     g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_item), pass_along );
6519     gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6520     gtk_widget_show ( item );
6521
6522     if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT )
6523     {
6524       gboolean separator_created = FALSE;
6525
6526       /* could be a right-click using the tool */
6527       if ( vlp != NULL ) {
6528         item = gtk_menu_item_new ();
6529         gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6530         gtk_widget_show ( item );
6531
6532         separator_created = TRUE;
6533
6534         item = gtk_image_menu_item_new_with_mnemonic ( _("_Goto") );
6535         gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_JUMP_TO, GTK_ICON_SIZE_MENU) );
6536         g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_waypoint), pass_along );
6537         gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6538         gtk_widget_show ( item );
6539       }
6540
6541       VikWaypoint *wp = g_hash_table_lookup ( VIK_TRW_LAYER(l)->waypoints, sublayer );
6542
6543       if ( wp && wp->name ) {
6544         if ( is_valid_geocache_name ( wp->name ) ) {
6545
6546           if ( !separator_created ) {
6547             item = gtk_menu_item_new ();
6548             gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6549             gtk_widget_show ( item );
6550             separator_created = TRUE;
6551           }
6552
6553           item = gtk_menu_item_new_with_mnemonic ( _("_Visit Geocache Webpage") );
6554           g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_waypoint_gc_webpage), pass_along );
6555           gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6556           gtk_widget_show ( item );
6557         }
6558       }
6559
6560       if ( wp && wp->image )
6561       {
6562         if ( !separator_created ) {
6563           item = gtk_menu_item_new ();
6564           gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6565           gtk_widget_show ( item );
6566           separator_created = TRUE;
6567         }
6568
6569         // Set up image paramater
6570         pass_along[5] = wp->image;
6571
6572         item = gtk_image_menu_item_new_with_mnemonic ( _("_Show Picture...") );
6573         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
6574         g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_show_picture), pass_along );
6575         gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6576         gtk_widget_show ( item );
6577
6578 #ifdef VIK_CONFIG_GEOTAG
6579         GtkWidget *geotag_submenu = gtk_menu_new ();
6580         item = gtk_image_menu_item_new_with_mnemonic ( _("Update Geotag on _Image") );
6581         gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REFRESH, GTK_ICON_SIZE_MENU) );
6582         gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
6583         gtk_widget_show ( item );
6584         gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), geotag_submenu );
6585   
6586         item = gtk_menu_item_new_with_mnemonic ( _("_Update") );
6587         g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_geotagging_waypoint_mtime_update), pass_along );
6588         gtk_menu_shell_append (GTK_MENU_SHELL (geotag_submenu), item);
6589         gtk_widget_show ( item );
6590
6591         item = gtk_menu_item_new_with_mnemonic ( _("Update and _Keep File Timestamp") );
6592         g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_geotagging_waypoint_mtime_keep), pass_along );
6593         gtk_menu_shell_append (GTK_MENU_SHELL (geotag_submenu), item);
6594         gtk_widget_show ( item );
6595 #endif
6596       }
6597
6598       if ( wp )
6599       {
6600         if ( ( wp->comment && !strncmp(wp->comment, "http", 4) ) ||
6601              ( wp->description && !strncmp(wp->description, "http", 4) )) {
6602           item = gtk_image_menu_item_new_with_mnemonic ( _("Visit _Webpage") );
6603           gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NETWORK, GTK_ICON_SIZE_MENU) );
6604           g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_waypoint_webpage), pass_along );
6605           gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
6606           gtk_widget_show ( item );
6607         }
6608       }
6609
6610     }
6611   }
6612
6613   if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINTS || subtype == VIK_TRW_LAYER_SUBLAYER_TRACKS || subtype == VIK_TRW_LAYER_SUBLAYER_ROUTES ) {
6614     item = gtk_image_menu_item_new_from_stock ( GTK_STOCK_PASTE, NULL );
6615     g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_paste_item_cb), pass_along );
6616     gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6617     gtk_widget_show ( item );
6618     // TODO: only enable if suitable item is in clipboard - want to determine *which* sublayer type
6619     if ( a_clipboard_type ( ) == VIK_CLIPBOARD_DATA_SUBLAYER )
6620       gtk_widget_set_sensitive ( item, TRUE );
6621     else
6622       gtk_widget_set_sensitive ( item, FALSE );
6623
6624     // Add separator
6625     item = gtk_menu_item_new ();
6626     gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6627     gtk_widget_show ( item );
6628   }
6629
6630   if ( vlp && (subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINTS || subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT) )
6631   {
6632     rv = TRUE;
6633     item = gtk_image_menu_item_new_with_mnemonic ( _("_New Waypoint...") );
6634     gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NEW, GTK_ICON_SIZE_MENU) );
6635     g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_wp), pass_along );
6636     gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
6637     gtk_widget_show ( item );
6638   }
6639
6640   if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINTS )
6641   {
6642     item = gtk_image_menu_item_new_with_mnemonic ( _("_View All Waypoints") );
6643     gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ZOOM_FIT, GTK_ICON_SIZE_MENU) );
6644     g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_waypoints_view), pass_along );
6645     gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
6646     gtk_widget_show ( item );
6647
6648     item = gtk_image_menu_item_new_with_mnemonic ( _("Goto _Waypoint...") );
6649     gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_JUMP_TO, GTK_ICON_SIZE_MENU) );
6650     g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_wp), pass_along );
6651     gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
6652     gtk_widget_show ( item );
6653
6654     item = gtk_image_menu_item_new_with_mnemonic ( _("Delete _All Waypoints") );
6655     gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU) );
6656     g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_all_waypoints), pass_along );
6657     gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6658     gtk_widget_show ( item );
6659
6660     item = gtk_image_menu_item_new_with_mnemonic ( _("_Delete Waypoints From Selection...") );
6661     gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
6662     g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_waypoints_from_selection), pass_along );
6663     gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6664     gtk_widget_show ( item );
6665
6666     GtkWidget *vis_submenu = gtk_menu_new ();
6667     item = gtk_menu_item_new_with_mnemonic ( _("_Visibility") );
6668     gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
6669     gtk_widget_show ( item );
6670     gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), vis_submenu );
6671
6672     item = gtk_image_menu_item_new_with_mnemonic ( _("_Show All Waypoints") );
6673     gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_APPLY, GTK_ICON_SIZE_MENU) );
6674     g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_waypoints_visibility_on), pass_along );
6675     gtk_menu_shell_append ( GTK_MENU_SHELL(vis_submenu), item );
6676     gtk_widget_show ( item );
6677
6678     item = gtk_image_menu_item_new_with_mnemonic ( _("_Hide All Waypoints") );
6679     gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_CLEAR, GTK_ICON_SIZE_MENU) );
6680     g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_waypoints_visibility_off), pass_along );
6681     gtk_menu_shell_append ( GTK_MENU_SHELL(vis_submenu), item );
6682     gtk_widget_show ( item );
6683
6684     item = gtk_image_menu_item_new_with_mnemonic ( _("_Toggle") );
6685     gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REFRESH, GTK_ICON_SIZE_MENU) );
6686     g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_waypoints_visibility_toggle), pass_along );
6687     gtk_menu_shell_append ( GTK_MENU_SHELL(vis_submenu), item );
6688     gtk_widget_show ( item );
6689   }
6690
6691   if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACKS )
6692   {
6693     rv = TRUE;
6694
6695     if ( l->current_track && !l->current_track->is_route ) {
6696       item = gtk_menu_item_new_with_mnemonic ( _("_Finish Track") );
6697       g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_finish_track), pass_along );
6698       gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
6699       gtk_widget_show ( item );
6700       // Add separator
6701       item = gtk_menu_item_new ();
6702       gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6703       gtk_widget_show ( item );
6704     }
6705
6706     item = gtk_image_menu_item_new_with_mnemonic ( _("_View All Tracks") );
6707     gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ZOOM_FIT, GTK_ICON_SIZE_MENU) );
6708     g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_tracks_view), pass_along );
6709     gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
6710     gtk_widget_show ( item );
6711
6712     item = gtk_image_menu_item_new_with_mnemonic ( _("_New Track") );
6713     gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NEW, GTK_ICON_SIZE_MENU) );
6714     g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_track), pass_along );
6715     gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
6716     gtk_widget_show ( item );
6717     // Make it available only when a new track *not* already in progress
6718     gtk_widget_set_sensitive ( item, ! (gboolean)GPOINTER_TO_INT(l->current_track) );
6719
6720     item = gtk_image_menu_item_new_with_mnemonic ( _("Delete _All Tracks") );
6721     gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU) );
6722     g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_all_tracks), pass_along );
6723     gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6724     gtk_widget_show ( item );
6725
6726     item = gtk_image_menu_item_new_with_mnemonic ( _("_Delete Tracks From Selection...") );
6727     gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
6728     g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_tracks_from_selection), pass_along );
6729     gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6730     gtk_widget_show ( item );
6731
6732     GtkWidget *vis_submenu = gtk_menu_new ();
6733     item = gtk_menu_item_new_with_mnemonic ( _("_Visibility") );
6734     gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
6735     gtk_widget_show ( item );
6736     gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), vis_submenu );
6737
6738     item = gtk_image_menu_item_new_with_mnemonic ( _("_Show All Tracks") );
6739     gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_APPLY, GTK_ICON_SIZE_MENU) );
6740     g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_tracks_visibility_on), pass_along );
6741     gtk_menu_shell_append ( GTK_MENU_SHELL(vis_submenu), item );
6742     gtk_widget_show ( item );
6743
6744     item = gtk_image_menu_item_new_with_mnemonic ( _("_Hide All Tracks") );
6745     gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_CLEAR, GTK_ICON_SIZE_MENU) );
6746     g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_tracks_visibility_off), pass_along );
6747     gtk_menu_shell_append ( GTK_MENU_SHELL(vis_submenu), item );
6748     gtk_widget_show ( item );
6749
6750     item = gtk_image_menu_item_new_with_mnemonic ( _("_Toggle") );
6751     gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REFRESH, GTK_ICON_SIZE_MENU) );
6752     g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_tracks_visibility_toggle), pass_along );
6753     gtk_menu_shell_append ( GTK_MENU_SHELL(vis_submenu), item );
6754     gtk_widget_show ( item );
6755
6756     item = gtk_menu_item_new_with_mnemonic ( _("_Statistics") );
6757     g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_tracks_stats), pass_along );
6758     gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6759     gtk_widget_show ( item );
6760   }
6761
6762   if ( subtype == VIK_TRW_LAYER_SUBLAYER_ROUTES )
6763   {
6764     rv = TRUE;
6765
6766     if ( l->current_track && l->current_track->is_route ) {
6767       item = gtk_menu_item_new_with_mnemonic ( _("_Finish Route") );
6768       // Reuse finish track method
6769       g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_finish_track), pass_along );
6770       gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
6771       gtk_widget_show ( item );
6772       // Add separator
6773       item = gtk_menu_item_new ();
6774       gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6775       gtk_widget_show ( item );
6776     }
6777
6778     item = gtk_image_menu_item_new_with_mnemonic ( _("_View All Routes") );
6779     gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ZOOM_FIT, GTK_ICON_SIZE_MENU) );
6780     g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_routes_view), pass_along );
6781     gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
6782     gtk_widget_show ( item );
6783
6784     item = gtk_image_menu_item_new_with_mnemonic ( _("_New Route") );
6785     gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NEW, GTK_ICON_SIZE_MENU) );
6786     g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_route), pass_along );
6787     gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
6788     gtk_widget_show ( item );
6789     // Make it available only when a new track *not* already in progress
6790     gtk_widget_set_sensitive ( item, ! (gboolean)GPOINTER_TO_INT(l->current_track) );
6791
6792     item = gtk_image_menu_item_new_with_mnemonic ( _("Delete _All Routes") );
6793     gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU) );
6794     g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_all_routes), pass_along );
6795     gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6796     gtk_widget_show ( item );
6797
6798     item = gtk_image_menu_item_new_with_mnemonic ( _("_Delete Routes From Selection...") );
6799     gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
6800     g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_routes_from_selection), pass_along );
6801     gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6802     gtk_widget_show ( item );
6803
6804     GtkWidget *vis_submenu = gtk_menu_new ();
6805     item = gtk_menu_item_new_with_mnemonic ( _("_Visibility") );
6806     gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
6807     gtk_widget_show ( item );
6808     gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), vis_submenu );
6809
6810     item = gtk_image_menu_item_new_with_mnemonic ( _("_Show All Routes") );
6811     gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_APPLY, GTK_ICON_SIZE_MENU) );
6812     g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_routes_visibility_on), pass_along );
6813     gtk_menu_shell_append ( GTK_MENU_SHELL(vis_submenu), item );
6814     gtk_widget_show ( item );
6815
6816     item = gtk_image_menu_item_new_with_mnemonic ( _("_Hide All Routes") );
6817     gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_CLEAR, GTK_ICON_SIZE_MENU) );
6818     g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_routes_visibility_off), pass_along );
6819     gtk_menu_shell_append ( GTK_MENU_SHELL(vis_submenu), item );
6820     gtk_widget_show ( item );
6821
6822     item = gtk_image_menu_item_new_with_mnemonic ( _("_Toggle") );
6823     gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REFRESH, GTK_ICON_SIZE_MENU) );
6824     g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_routes_visibility_toggle), pass_along );
6825     gtk_menu_shell_append ( GTK_MENU_SHELL(vis_submenu), item );
6826     gtk_widget_show ( item );
6827
6828     item = gtk_menu_item_new_with_mnemonic ( _("_Statistics") );
6829     g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_routes_stats), pass_along );
6830     gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6831     gtk_widget_show ( item );
6832   }
6833
6834
6835   if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINTS || subtype == VIK_TRW_LAYER_SUBLAYER_TRACKS || subtype == VIK_TRW_LAYER_SUBLAYER_ROUTES ) {
6836     GtkWidget *submenu_sort = gtk_menu_new ();
6837     item = gtk_image_menu_item_new_with_mnemonic ( _("_Sort") );
6838     gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REFRESH, GTK_ICON_SIZE_MENU) );
6839     gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
6840     gtk_widget_show ( item );
6841     gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), submenu_sort );
6842
6843     item = gtk_image_menu_item_new_with_mnemonic ( _("Name _Ascending") );
6844     gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_SORT_ASCENDING, GTK_ICON_SIZE_MENU) );
6845     g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_sort_order_a2z), pass_along );
6846     gtk_menu_shell_append ( GTK_MENU_SHELL(submenu_sort), item );
6847     gtk_widget_show ( item );
6848
6849     item = gtk_image_menu_item_new_with_mnemonic ( _("Name _Descending") );
6850     gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_SORT_DESCENDING, GTK_ICON_SIZE_MENU) );
6851     g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_sort_order_z2a), pass_along );
6852     gtk_menu_shell_append ( GTK_MENU_SHELL(submenu_sort), item );
6853     gtk_widget_show ( item );
6854   }
6855
6856   GtkWidget *upload_submenu = gtk_menu_new ();
6857
6858   if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK || subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE )
6859   {
6860     item = gtk_menu_item_new ();
6861     gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6862     gtk_widget_show ( item );
6863
6864     if ( l->current_track && subtype == VIK_TRW_LAYER_SUBLAYER_TRACK && !l->current_track->is_route )
6865       item = gtk_menu_item_new_with_mnemonic ( _("_Finish Track") );
6866     if ( l->current_track && subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE && l->current_track->is_route )
6867       item = gtk_menu_item_new_with_mnemonic ( _("_Finish Route") );
6868     if ( l->current_track ) {
6869       g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_finish_track), pass_along );
6870       gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
6871       gtk_widget_show ( item );
6872
6873       // Add separator
6874       item = gtk_menu_item_new ();
6875       gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6876       gtk_widget_show ( item );
6877     }
6878
6879     if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
6880       item = gtk_image_menu_item_new_with_mnemonic ( _("_View Track") );
6881     else
6882       item = gtk_image_menu_item_new_with_mnemonic ( _("_View Route") );
6883     gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ZOOM_FIT, GTK_ICON_SIZE_MENU) );
6884     g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_track_view), pass_along );
6885     gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
6886     gtk_widget_show ( item );
6887
6888     GtkWidget *goto_submenu;
6889     goto_submenu = gtk_menu_new ();
6890     item = gtk_image_menu_item_new_with_mnemonic ( _("_Goto") );
6891     gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_JUMP_TO, GTK_ICON_SIZE_MENU) );
6892     gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
6893     gtk_widget_show ( item );
6894     gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), goto_submenu );
6895
6896     item = gtk_image_menu_item_new_with_mnemonic ( _("_Startpoint") );
6897     gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GOTO_FIRST, GTK_ICON_SIZE_MENU) );
6898     g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_startpoint), pass_along );
6899     gtk_menu_shell_append ( GTK_MENU_SHELL(goto_submenu), item );
6900     gtk_widget_show ( item );
6901
6902     item = gtk_image_menu_item_new_with_mnemonic ( _("\"_Center\"") );
6903     gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_JUMP_TO, GTK_ICON_SIZE_MENU) );
6904     g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_center), pass_along );
6905     gtk_menu_shell_append ( GTK_MENU_SHELL(goto_submenu), item );
6906     gtk_widget_show ( item );
6907
6908     item = gtk_image_menu_item_new_with_mnemonic ( _("_Endpoint") );
6909     gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GOTO_LAST, GTK_ICON_SIZE_MENU) );
6910     g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_endpoint), pass_along );
6911     gtk_menu_shell_append ( GTK_MENU_SHELL(goto_submenu), item );
6912     gtk_widget_show ( item );
6913
6914     item = gtk_image_menu_item_new_with_mnemonic ( _("_Highest Altitude") );
6915     gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GOTO_TOP, GTK_ICON_SIZE_MENU) );
6916     g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_max_alt), pass_along );
6917     gtk_menu_shell_append ( GTK_MENU_SHELL(goto_submenu), item );
6918     gtk_widget_show ( item );
6919
6920     item = gtk_image_menu_item_new_with_mnemonic ( _("_Lowest Altitude") );
6921     gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GOTO_BOTTOM, GTK_ICON_SIZE_MENU) );
6922     g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_min_alt), pass_along );
6923     gtk_menu_shell_append ( GTK_MENU_SHELL(goto_submenu), item );
6924     gtk_widget_show ( item );
6925
6926     // Routes don't have speeds
6927     if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK ) {
6928       item = gtk_image_menu_item_new_with_mnemonic ( _("_Maximum Speed") );
6929       gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_MEDIA_FORWARD, GTK_ICON_SIZE_MENU) );
6930       g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_max_speed), pass_along );
6931       gtk_menu_shell_append ( GTK_MENU_SHELL(goto_submenu), item );
6932       gtk_widget_show ( item );
6933     }
6934
6935     GtkWidget *combine_submenu;
6936     combine_submenu = gtk_menu_new ();
6937     item = gtk_image_menu_item_new_with_mnemonic ( _("Co_mbine") );
6938     gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_CONNECT, GTK_ICON_SIZE_MENU) );
6939     gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
6940     gtk_widget_show ( item );
6941     gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), combine_submenu );
6942
6943     // Routes don't have times or segments...
6944     if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK ) {
6945       item = gtk_menu_item_new_with_mnemonic ( _("_Merge By Time...") );
6946       g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_merge_by_timestamp), pass_along );
6947       gtk_menu_shell_append ( GTK_MENU_SHELL(combine_submenu), item );
6948       gtk_widget_show ( item );
6949
6950       item = gtk_menu_item_new_with_mnemonic ( _("Merge _Segments") );
6951       g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_merge_by_segment), pass_along );
6952       gtk_menu_shell_append ( GTK_MENU_SHELL(combine_submenu), item );
6953       gtk_widget_show ( item );
6954     }
6955
6956     item = gtk_menu_item_new_with_mnemonic ( _("Merge _With Other Tracks...") );
6957     g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_merge_with_other), pass_along );
6958     gtk_menu_shell_append ( GTK_MENU_SHELL(combine_submenu), item );
6959     gtk_widget_show ( item );
6960
6961     if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
6962       item = gtk_menu_item_new_with_mnemonic ( _("_Append Track...") );
6963     else
6964       item = gtk_menu_item_new_with_mnemonic ( _("_Append Route...") );
6965     g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_append_track), pass_along );
6966     gtk_menu_shell_append ( GTK_MENU_SHELL(combine_submenu), item );
6967     gtk_widget_show ( item );
6968
6969     if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
6970       item = gtk_menu_item_new_with_mnemonic ( _("Append _Route...") );
6971     else
6972       item = gtk_menu_item_new_with_mnemonic ( _("Append _Track...") );
6973     g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_append_other), pass_along );
6974     gtk_menu_shell_append ( GTK_MENU_SHELL(combine_submenu), item );
6975     gtk_widget_show ( item );
6976
6977     GtkWidget *split_submenu;
6978     split_submenu = gtk_menu_new ();
6979     item = gtk_image_menu_item_new_with_mnemonic ( _("_Split") );
6980     gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_DISCONNECT, GTK_ICON_SIZE_MENU) );
6981     gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
6982     gtk_widget_show ( item );
6983     gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), split_submenu );
6984
6985     // Routes don't have times or segments...
6986     if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK ) {
6987       item = gtk_menu_item_new_with_mnemonic ( _("_Split By Time...") );
6988       g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_split_by_timestamp), pass_along );
6989       gtk_menu_shell_append ( GTK_MENU_SHELL(split_submenu), item );
6990       gtk_widget_show ( item );
6991
6992       // ATM always enable this entry - don't want to have to analyse the track before displaying the menu - to keep the menu speedy
6993       item = gtk_menu_item_new_with_mnemonic ( _("Split Se_gments") );
6994       g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_split_segments), pass_along );
6995       gtk_menu_shell_append ( GTK_MENU_SHELL(split_submenu), item );
6996       gtk_widget_show ( item );
6997     }
6998
6999     item = gtk_menu_item_new_with_mnemonic ( _("Split By _Number of Points...") );
7000     g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_split_by_n_points), pass_along );
7001     gtk_menu_shell_append ( GTK_MENU_SHELL(split_submenu), item );
7002     gtk_widget_show ( item );
7003
7004     item = gtk_menu_item_new_with_mnemonic ( _("Split at _Trackpoint") );
7005     g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_split_at_trackpoint), pass_along );
7006     gtk_menu_shell_append ( GTK_MENU_SHELL(split_submenu), item );
7007     gtk_widget_show ( item );
7008     // Make it available only when a trackpoint is selected.
7009     gtk_widget_set_sensitive ( item, (gboolean)GPOINTER_TO_INT(l->current_tpl) );
7010
7011     GtkWidget *delete_submenu;
7012     delete_submenu = gtk_menu_new ();
7013     item = gtk_image_menu_item_new_with_mnemonic ( _("Delete Poi_nts") );
7014     gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_DELETE, GTK_ICON_SIZE_MENU) );
7015     gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7016     gtk_widget_show ( item );
7017     gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), delete_submenu );
7018
7019     item = gtk_menu_item_new_with_mnemonic ( _("Delete Points With The Same _Position") );
7020     g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_points_same_position), pass_along );
7021     gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
7022     gtk_widget_show ( item );
7023
7024     item = gtk_menu_item_new_with_mnemonic ( _("Delete Points With The Same _Time") );
7025     g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_points_same_time), pass_along );
7026     gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
7027     gtk_widget_show ( item );
7028
7029     if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
7030       item = gtk_image_menu_item_new_with_mnemonic ( _("_Reverse Track") );
7031     else
7032       item = gtk_image_menu_item_new_with_mnemonic ( _("_Reverse Route") );
7033     gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_BACK, GTK_ICON_SIZE_MENU) );
7034     g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_reverse), pass_along );
7035     gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7036     gtk_widget_show ( item );
7037
7038     /* ATM This function is only available via the layers panel, due to the method in finding out the maps in use */
7039     if ( vlp ) {
7040       if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
7041         item = gtk_image_menu_item_new_with_mnemonic ( _("Down_load Maps Along Track...") );
7042       else
7043         item = gtk_image_menu_item_new_with_mnemonic ( _("Down_load Maps Along Route...") );
7044       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
7045       g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_download_map_along_track_cb), pass_along );
7046       gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7047       gtk_widget_show ( item );
7048     }
7049
7050     item = gtk_image_menu_item_new_with_mnemonic ( _("_Apply DEM Data") );
7051     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
7052     g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_apply_dem_data), pass_along );
7053     gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7054     gtk_widget_show ( item );
7055
7056     if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
7057       item = gtk_image_menu_item_new_with_mnemonic ( _("_Export Track as GPX...") );
7058     else
7059       item = gtk_image_menu_item_new_with_mnemonic ( _("_Export Route as GPX...") );
7060     gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_HARDDISK, GTK_ICON_SIZE_MENU) );
7061     g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_gpx_track), pass_along );
7062     gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7063     gtk_widget_show ( item );
7064
7065     if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
7066       item = gtk_image_menu_item_new_with_mnemonic ( _("E_xtend Track End") );
7067     else
7068       item = gtk_image_menu_item_new_with_mnemonic ( _("E_xtend Route End") );
7069     gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ADD, GTK_ICON_SIZE_MENU) );
7070     g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_extend_track_end), pass_along );
7071     gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7072     gtk_widget_show ( item );
7073
7074     if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
7075       item = gtk_image_menu_item_new_with_mnemonic ( _("C_onvert to a Route") );
7076     else
7077       item = gtk_image_menu_item_new_with_mnemonic ( _("C_onvert to a Track") );
7078     gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_CONVERT, GTK_ICON_SIZE_MENU) );
7079     g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_convert_track_route), pass_along );
7080     gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7081     gtk_widget_show ( item );
7082
7083     if ( subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE ) {
7084       item = gtk_image_menu_item_new_with_mnemonic ( _("Extend _Using Route Finder") );
7085       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
7086       g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_extend_track_end_route_finder), pass_along );
7087       gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7088       gtk_widget_show ( item );
7089     }
7090
7091     // ATM can't upload a single waypoint but can do waypoints to a GPS
7092     if ( subtype != VIK_TRW_LAYER_SUBLAYER_WAYPOINT ) {
7093       item = gtk_image_menu_item_new_with_mnemonic ( _("_Upload") );
7094       gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_UP, GTK_ICON_SIZE_MENU) );
7095       gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7096       gtk_widget_show ( item );
7097       gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), upload_submenu );
7098
7099       item = gtk_image_menu_item_new_with_mnemonic ( _("_Upload to GPS...") );
7100       gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_FORWARD, GTK_ICON_SIZE_MENU) );
7101       g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_gps_upload_any), pass_along );
7102       gtk_menu_shell_append ( GTK_MENU_SHELL(upload_submenu), item );
7103       gtk_widget_show ( item );
7104     }
7105   }
7106
7107 #ifdef VIK_CONFIG_GOOGLE
7108   if ( subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE && is_valid_google_route ( l, sublayer ) )
7109   {
7110     item = gtk_image_menu_item_new_with_mnemonic ( _("_View Google Directions") );
7111     gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NETWORK, GTK_ICON_SIZE_MENU) );
7112     g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_google_route_webpage), pass_along );
7113     gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7114     gtk_widget_show ( item );
7115   }
7116 #endif
7117
7118   // Some things aren't usable with routes
7119   if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK ) {
7120 #ifdef VIK_CONFIG_OPENSTREETMAP
7121     item = gtk_image_menu_item_new_with_mnemonic ( _("Upload to _OSM...") );
7122     // Convert internal pointer into actual track for usage outside this file
7123     pass_along[7] = g_hash_table_lookup ( l->tracks, sublayer);
7124     gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_UP, GTK_ICON_SIZE_MENU) );
7125     g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(osm_traces_upload_track_cb), pass_along );
7126     gtk_menu_shell_append ( GTK_MENU_SHELL(upload_submenu), item );
7127     gtk_widget_show ( item );
7128 #endif
7129
7130     item = gtk_image_menu_item_new_with_mnemonic ( _("Use with _Filter") );
7131     gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
7132     g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_track_use_with_filter), pass_along );
7133     gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7134     gtk_widget_show ( item );
7135
7136     /* ATM This function is only available via the layers panel, due to needing a vlp */
7137     if ( vlp ) {
7138       item = a_acquire_track_menu ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(l)), vlp,
7139                                     vik_layers_panel_get_viewport(VIK_LAYERS_PANEL(vlp)),
7140                                     g_hash_table_lookup ( l->tracks, (gchar *) sublayer ) );
7141       if ( item ) {
7142         gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7143         gtk_widget_show ( item );
7144       }
7145     }
7146
7147 #ifdef VIK_CONFIG_GEOTAG
7148     item = gtk_menu_item_new_with_mnemonic ( _("Geotag _Images...") );
7149     g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_geotagging_track), pass_along );
7150     gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7151     gtk_widget_show ( item );
7152 #endif
7153   }
7154
7155   if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK || subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE ) {
7156     // Only show on viewport popmenu when a trackpoint is selected
7157     if ( ! vlp && l->current_tpl ) {
7158       // Add separator
7159       item = gtk_menu_item_new ();
7160       gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7161       gtk_widget_show ( item );
7162
7163       item = gtk_image_menu_item_new_with_mnemonic ( _("_Edit Trackpoint") );
7164       gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_PROPERTIES, GTK_ICON_SIZE_MENU) );
7165       g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_edit_trackpoint), pass_along );
7166       gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7167       gtk_widget_show ( item );
7168     }
7169   }
7170
7171   return rv;
7172 }
7173
7174 static void trw_layer_insert_tp_after_current_tp ( VikTrwLayer *vtl )
7175 {
7176   /* sanity checks */
7177   if (!vtl->current_tpl)
7178     return;
7179   if (!vtl->current_tpl->next)
7180     return;
7181
7182   VikTrackpoint *tp_current = VIK_TRACKPOINT(vtl->current_tpl->data);
7183   VikTrackpoint *tp_next = VIK_TRACKPOINT(vtl->current_tpl->next->data);
7184
7185   /* Use current and next trackpoints to form a new track point which is inserted into the tracklist */
7186   if ( tp_next ) {
7187
7188     VikTrackpoint *tp_new = vik_trackpoint_new();
7189     struct LatLon ll_current, ll_next;
7190     vik_coord_to_latlon ( &tp_current->coord, &ll_current );
7191     vik_coord_to_latlon ( &tp_next->coord, &ll_next );
7192
7193     /* main positional interpolation */
7194     struct LatLon ll_new = { (ll_current.lat+ll_next.lat)/2, (ll_current.lon+ll_next.lon)/2 };
7195     vik_coord_load_from_latlon ( &(tp_new->coord), vtl->coord_mode, &ll_new );
7196
7197     /* Now other properties that can be interpolated */
7198     tp_new->altitude = (tp_current->altitude + tp_next->altitude) / 2;
7199
7200     if (tp_current->has_timestamp && tp_next->has_timestamp) {
7201       /* Note here the division is applied to each part, then added
7202          This is to avoid potential overflow issues with a 32 time_t for dates after midpoint of this Unix time on 2004/01/04 */
7203       tp_new->timestamp = (tp_current->timestamp/2) + (tp_next->timestamp/2);
7204       tp_new->has_timestamp = TRUE;
7205     }
7206
7207     if (tp_current->speed != NAN && tp_next->speed != NAN)
7208       tp_new->speed = (tp_current->speed + tp_next->speed)/2;
7209
7210     /* TODO - improve interpolation of course, as it may not be correct.
7211        if courses in degrees are 350 + 020, the mid course more likely to be 005 (not 185)
7212        [similar applies if value is in radians] */
7213     if (tp_current->course != NAN && tp_next->course != NAN)
7214       tp_new->course = (tp_current->course + tp_next->course)/2;
7215
7216     /* DOP / sat values remain at defaults as not they do not seem applicable to a dreamt up point */
7217
7218     /* Insert new point into the trackpoints list after the current TP */
7219     VikTrack *trk = g_hash_table_lookup ( vtl->tracks, vtl->current_tp_id );
7220     if ( !trk )
7221       // Otherwise try routes
7222       trk = g_hash_table_lookup ( vtl->routes, vtl->current_tp_id );
7223     if ( !trk )
7224       return;
7225
7226     gint index =  g_list_index ( trk->trackpoints, tp_current );
7227     if ( index > -1 ) {
7228       // NB no recalculation of bounds since it is inserted between points
7229       trk->trackpoints = g_list_insert ( trk->trackpoints, tp_new, index+1 );
7230     }
7231   }
7232 }
7233
7234 static void trw_layer_cancel_current_tp ( VikTrwLayer *vtl, gboolean destroy )
7235 {
7236   if ( vtl->tpwin )
7237   {
7238     if ( destroy)
7239     {
7240       gtk_widget_destroy ( GTK_WIDGET(vtl->tpwin) );
7241       vtl->tpwin = NULL;
7242     }
7243     else
7244       vik_trw_layer_tpwin_set_empty ( vtl->tpwin );
7245   }
7246   if ( vtl->current_tpl )
7247   {
7248     vtl->current_tpl = NULL;
7249     vtl->current_tp_track = NULL;
7250     vtl->current_tp_id = NULL;
7251     vik_layer_emit_update(VIK_LAYER(vtl));
7252   }
7253 }
7254
7255 static void trw_layer_tpwin_response ( VikTrwLayer *vtl, gint response )
7256 {
7257   g_assert ( vtl->tpwin != NULL );
7258   if ( response == VIK_TRW_LAYER_TPWIN_CLOSE )
7259     trw_layer_cancel_current_tp ( vtl, TRUE );
7260
7261   if ( vtl->current_tpl == NULL )
7262     return;
7263
7264   if ( response == VIK_TRW_LAYER_TPWIN_SPLIT && vtl->current_tpl->next && vtl->current_tpl->prev )
7265   {
7266     trw_layer_split_at_selected_trackpoint ( vtl, vtl->current_tp_track->is_route ? VIK_TRW_LAYER_SUBLAYER_ROUTE : VIK_TRW_LAYER_SUBLAYER_TRACK );
7267     vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, vtl->current_tp_track->name );
7268   }
7269   else if ( response == VIK_TRW_LAYER_TPWIN_DELETE )
7270   {
7271     VikTrack *tr = g_hash_table_lookup ( vtl->tracks, vtl->current_tp_id );
7272     if ( tr == NULL )
7273       tr = g_hash_table_lookup ( vtl->routes, vtl->current_tp_id );
7274     if ( tr == NULL )
7275       return;
7276
7277     GList *new_tpl;
7278
7279     // Find available adjacent trackpoint
7280     if ( (new_tpl = vtl->current_tpl->next) || (new_tpl = vtl->current_tpl->prev) )
7281     {
7282       if ( VIK_TRACKPOINT(vtl->current_tpl->data)->newsegment && vtl->current_tpl->next )
7283         VIK_TRACKPOINT(vtl->current_tpl->next->data)->newsegment = TRUE; /* don't concat segments on del */
7284
7285       // Delete current trackpoint
7286       vik_trackpoint_free ( vtl->current_tpl->data );
7287       tr->trackpoints = g_list_delete_link ( tr->trackpoints, vtl->current_tpl );
7288
7289       // Set to current to the available adjacent trackpoint
7290       vtl->current_tpl = new_tpl;
7291
7292       // Reset dialog with the available adjacent trackpoint
7293       if ( vtl->current_tp_track ) {
7294         vik_track_calculate_bounds ( vtl->current_tp_track );
7295         vik_trw_layer_tpwin_set_tp ( vtl->tpwin, new_tpl, vtl->current_tp_track->name );
7296       }
7297
7298       vik_layer_emit_update(VIK_LAYER(vtl));
7299     }
7300     else
7301     {
7302       // Delete current trackpoint
7303       vik_trackpoint_free ( vtl->current_tpl->data );
7304       tr->trackpoints = g_list_delete_link ( tr->trackpoints, vtl->current_tpl );
7305       trw_layer_cancel_current_tp ( vtl, FALSE );
7306     }
7307   }
7308   else if ( response == VIK_TRW_LAYER_TPWIN_FORWARD && vtl->current_tpl->next )
7309   {
7310     if ( vtl->current_tp_track )
7311       vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl = vtl->current_tpl->next, vtl->current_tp_track->name );
7312     vik_layer_emit_update(VIK_LAYER(vtl)); /* TODO longone: either move or only update if tp is inside drawing window */
7313   }
7314   else if ( response == VIK_TRW_LAYER_TPWIN_BACK && vtl->current_tpl->prev )
7315   {
7316     if ( vtl->current_tp_track )
7317       vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl = vtl->current_tpl->prev, vtl->current_tp_track->name );
7318     vik_layer_emit_update(VIK_LAYER(vtl));
7319   }
7320   else if ( response == VIK_TRW_LAYER_TPWIN_INSERT && vtl->current_tpl->next )
7321   {
7322     trw_layer_insert_tp_after_current_tp ( vtl );
7323     vik_layer_emit_update(VIK_LAYER(vtl));
7324   }
7325   else if ( response == VIK_TRW_LAYER_TPWIN_DATA_CHANGED )
7326     vik_layer_emit_update(VIK_LAYER(vtl));
7327 }
7328
7329 /**
7330  * trw_layer_dialog_shift:
7331  * @vertical: The reposition strategy. If Vertical moves dialog vertically, otherwise moves it horizontally
7332  *
7333  * Try to reposition a dialog if it's over the specified coord
7334  *  so to not obscure the item of interest
7335  */
7336 void trw_layer_dialog_shift ( VikTrwLayer *vtl, GtkWindow *dialog, VikCoord *coord, gboolean vertical )
7337 {
7338   GtkWindow *parent = VIK_GTK_WINDOW_FROM_LAYER(vtl); //i.e. the main window
7339
7340   // Attempt force dialog to be shown so we can find out where it is more reliably...
7341   while ( gtk_events_pending() )
7342     gtk_main_iteration ();
7343
7344   // get parent window position & size
7345   gint win_pos_x, win_pos_y;
7346   gtk_window_get_position ( parent, &win_pos_x, &win_pos_y );
7347
7348   gint win_size_x, win_size_y;
7349   gtk_window_get_size ( parent, &win_size_x, &win_size_y );
7350
7351   // get own dialog size
7352   gint dia_size_x, dia_size_y;
7353   gtk_window_get_size ( dialog, &dia_size_x, &dia_size_y );
7354
7355   // get own dialog position
7356   gint dia_pos_x, dia_pos_y;
7357   gtk_window_get_position ( dialog, &dia_pos_x, &dia_pos_y );
7358
7359   // Dialog not 'realized'/positioned - so can't really do any repositioning logic
7360   if ( dia_pos_x > 2 && dia_pos_y > 2 ) {
7361
7362     VikViewport *vvp = vik_window_viewport ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)) );
7363
7364     gint vp_xx, vp_yy; // In viewport pixels
7365     vik_viewport_coord_to_screen ( vvp, coord, &vp_xx, &vp_yy );
7366
7367     // Work out the 'bounding box' in pixel terms of the dialog and only move it when over the position
7368
7369     gint dest_x = 0;
7370     gint dest_y = 0;
7371     if ( gtk_widget_translate_coordinates ( GTK_WIDGET(vvp), GTK_WIDGET(parent), 0, 0, &dest_x, &dest_y ) ) {
7372
7373       // Transform Viewport pixels into absolute pixels
7374       gint tmp_xx = vp_xx + dest_x + win_pos_x - 10;
7375       gint tmp_yy = vp_yy + dest_y + win_pos_y - 10;
7376
7377       // Is dialog over the point (to within an  ^^ edge value)
7378       if ( (tmp_xx > dia_pos_x) && (tmp_xx < (dia_pos_x + dia_size_x)) &&
7379            (tmp_yy > dia_pos_y) && (tmp_yy < (dia_pos_y + dia_size_y)) ) {
7380
7381         if ( vertical ) {
7382           // Shift up<->down
7383           gint hh = vik_viewport_get_height ( vvp );
7384
7385           // Consider the difference in viewport to the full window
7386           gint offset_y = dest_y;
7387           // Add difference between dialog and window sizes
7388           offset_y += win_pos_y + (hh/2 - dia_size_y)/2;
7389
7390           if ( vp_yy > hh/2 ) {
7391             // Point in bottom half, move window to top half
7392             gtk_window_move ( dialog, dia_pos_x, offset_y );
7393           }
7394           else {
7395             // Point in top half, move dialog down
7396             gtk_window_move ( dialog, dia_pos_x, hh/2 + offset_y );
7397           }
7398         }
7399         else {
7400           // Shift left<->right
7401           gint ww = vik_viewport_get_width ( vvp );
7402
7403           // Consider the difference in viewport to the full window
7404           gint offset_x = dest_x;
7405           // Add difference between dialog and window sizes
7406           offset_x += win_pos_x + (ww/2 - dia_size_x)/2;
7407
7408           if ( vp_xx > ww/2 ) {
7409             // Point on right, move window to left
7410             gtk_window_move ( dialog, offset_x, dia_pos_y );
7411           }
7412           else {
7413             // Point on left, move right
7414             gtk_window_move ( dialog, ww/2 + offset_x, dia_pos_y );
7415           }
7416         }
7417       }
7418     }
7419   }
7420 }
7421
7422 static void trw_layer_tpwin_init ( VikTrwLayer *vtl )
7423 {
7424   if ( ! vtl->tpwin )
7425   {
7426     vtl->tpwin = vik_trw_layer_tpwin_new ( VIK_GTK_WINDOW_FROM_LAYER(vtl) );
7427     g_signal_connect_swapped ( GTK_DIALOG(vtl->tpwin), "response", G_CALLBACK(trw_layer_tpwin_response), vtl );
7428     /* connect signals -- DELETE SIGNAL VERY IMPORTANT TO SET TO NULL */
7429     g_signal_connect_swapped ( vtl->tpwin, "delete-event", G_CALLBACK(trw_layer_cancel_current_tp), vtl );
7430
7431     gtk_widget_show_all ( GTK_WIDGET(vtl->tpwin) );
7432
7433     if ( vtl->current_tpl ) {
7434       // get tp pixel position
7435       VikTrackpoint *tp = VIK_TRACKPOINT(vtl->current_tpl->data);
7436
7437       // Shift up<->down to try not to obscure the trackpoint.
7438       trw_layer_dialog_shift ( vtl, GTK_WINDOW(vtl->tpwin), &(tp->coord), TRUE );
7439     }
7440   }
7441
7442   if ( vtl->current_tpl )
7443     if ( vtl->current_tp_track )
7444       vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, vtl->current_tp_track->name );
7445   /* set layer name and TP data */
7446 }
7447
7448 /***************************************************************************
7449  ** Tool code
7450  ***************************************************************************/
7451
7452 /*** Utility data structures and functions ****/
7453
7454 typedef struct {
7455   gint x, y;
7456   gint closest_x, closest_y;
7457   gboolean draw_images;
7458   gpointer *closest_wp_id;
7459   VikWaypoint *closest_wp;
7460   VikViewport *vvp;
7461 } WPSearchParams;
7462
7463 typedef struct {
7464   gint x, y;
7465   gint closest_x, closest_y;
7466   gpointer closest_track_id;
7467   VikTrackpoint *closest_tp;
7468   VikViewport *vvp;
7469   GList *closest_tpl;
7470   LatLonBBox bbox;
7471 } TPSearchParams;
7472
7473 static void waypoint_search_closest_tp ( gpointer id, VikWaypoint *wp, WPSearchParams *params )
7474 {
7475   gint x, y;
7476   if ( !wp->visible )
7477     return;
7478
7479   vik_viewport_coord_to_screen ( params->vvp, &(wp->coord), &x, &y );
7480
7481   // If waypoint has an image then use the image size to select
7482   if ( params->draw_images && wp->image ) {
7483     gint slackx, slacky;
7484     slackx = wp->image_width / 2;
7485     slacky = wp->image_height / 2;
7486
7487     if (    x <= params->x + slackx && x >= params->x - slackx
7488          && y <= params->y + slacky && y >= params->y - slacky ) {
7489       params->closest_wp_id = id;
7490       params->closest_wp = wp;
7491       params->closest_x = x;
7492       params->closest_y = y;
7493     }
7494   }
7495   else if ( abs (x - params->x) <= WAYPOINT_SIZE_APPROX && abs (y - params->y) <= WAYPOINT_SIZE_APPROX &&
7496             ((!params->closest_wp) ||        /* was the old waypoint we already found closer than this one? */
7497              abs(x - params->x)+abs(y - params->y) < abs(x - params->closest_x)+abs(y - params->closest_y)))
7498     {
7499       params->closest_wp_id = id;
7500       params->closest_wp = wp;
7501       params->closest_x = x;
7502       params->closest_y = y;
7503     }
7504 }
7505
7506 static void track_search_closest_tp ( gpointer id, VikTrack *t, TPSearchParams *params )
7507 {
7508   GList *tpl = t->trackpoints;
7509   VikTrackpoint *tp;
7510
7511   if ( !t->visible )
7512     return;
7513
7514   if ( ! BBOX_INTERSECT ( t->bbox, params->bbox ) )
7515     return;
7516
7517   while (tpl)
7518   {
7519     gint x, y;
7520     tp = VIK_TRACKPOINT(tpl->data);
7521
7522     vik_viewport_coord_to_screen ( params->vvp, &(tp->coord), &x, &y );
7523  
7524     if ( abs (x - params->x) <= TRACKPOINT_SIZE_APPROX && abs (y - params->y) <= TRACKPOINT_SIZE_APPROX &&
7525         ((!params->closest_tp) ||        /* was the old trackpoint we already found closer than this one? */
7526           abs(x - params->x)+abs(y - params->y) < abs(x - params->closest_x)+abs(y - params->closest_y)))
7527     {
7528       params->closest_track_id = id;
7529       params->closest_tp = tp;
7530       params->closest_tpl = tpl;
7531       params->closest_x = x;
7532       params->closest_y = y;
7533     }
7534     tpl = tpl->next;
7535   }
7536 }
7537
7538 // ATM: Leave this as 'Track' only.
7539 //  Not overly bothered about having a snap to route trackpoint capability
7540 static VikTrackpoint *closest_tp_in_five_pixel_interval ( VikTrwLayer *vtl, VikViewport *vvp, gint x, gint y )
7541 {
7542   TPSearchParams params;
7543   params.x = x;
7544   params.y = y;
7545   params.vvp = vvp;
7546   params.closest_track_id = NULL;
7547   params.closest_tp = NULL;
7548   vik_viewport_get_min_max_lat_lon ( params.vvp, &(params.bbox.south), &(params.bbox.north), &(params.bbox.west), &(params.bbox.east) );
7549   g_hash_table_foreach ( vtl->tracks, (GHFunc) track_search_closest_tp, &params);
7550   return params.closest_tp;
7551 }
7552
7553 static VikWaypoint *closest_wp_in_five_pixel_interval ( VikTrwLayer *vtl, VikViewport *vvp, gint x, gint y )
7554 {
7555   WPSearchParams params;
7556   params.x = x;
7557   params.y = y;
7558   params.vvp = vvp;
7559   params.draw_images = vtl->drawimages;
7560   params.closest_wp = NULL;
7561   params.closest_wp_id = NULL;
7562   g_hash_table_foreach ( vtl->waypoints, (GHFunc) waypoint_search_closest_tp, &params);
7563   return params.closest_wp;
7564 }
7565
7566
7567 // Some forward declarations
7568 static void marker_begin_move ( tool_ed_t *t, gint x, gint y );
7569 static void marker_moveto ( tool_ed_t *t, gint x, gint y );
7570 static void marker_end_move ( tool_ed_t *t );
7571 //
7572
7573 static gboolean trw_layer_select_move ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp, tool_ed_t* t )
7574 {
7575   if ( t->holding ) {
7576     VikCoord new_coord;
7577     vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
7578
7579     // Here always allow snapping back to the original location
7580     //  this is useful when one decides not to move the thing afterall
7581     // If one wants to move the item only a little bit then don't hold down the 'snap' key!
7582  
7583     // snap to TP
7584     if ( event->state & GDK_CONTROL_MASK )
7585     {
7586       VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
7587       if ( tp )
7588         new_coord = tp->coord;
7589     }
7590
7591     // snap to WP
7592     if ( event->state & GDK_SHIFT_MASK )
7593     {
7594       VikWaypoint *wp = closest_wp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
7595       if ( wp )
7596         new_coord = wp->coord;
7597     }
7598     
7599     gint x, y;
7600     vik_viewport_coord_to_screen ( vvp, &new_coord, &x, &y );
7601
7602     marker_moveto ( t, x, y );
7603
7604     return TRUE;
7605   }
7606   return FALSE;
7607 }
7608
7609 static gboolean trw_layer_select_release ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp, tool_ed_t* t )
7610 {
7611   if ( t->holding && event->button == 1 )
7612   {
7613     VikCoord new_coord;
7614     vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
7615
7616     // snap to TP
7617     if ( event->state & GDK_CONTROL_MASK )
7618     {
7619       VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
7620       if ( tp )
7621         new_coord = tp->coord;
7622     }
7623
7624     // snap to WP
7625     if ( event->state & GDK_SHIFT_MASK )
7626     {
7627       VikWaypoint *wp = closest_wp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
7628       if ( wp )
7629         new_coord = wp->coord;
7630     }
7631
7632     marker_end_move ( t );
7633
7634     // Determine if working on a waypoint or a trackpoint
7635     if ( t->is_waypoint ) {
7636       vtl->current_wp->coord = new_coord;
7637       trw_layer_calculate_bounds_waypoints ( vtl );
7638     }
7639     else {
7640       if ( vtl->current_tpl ) {
7641         VIK_TRACKPOINT(vtl->current_tpl->data)->coord = new_coord;
7642
7643         if ( vtl->current_tp_track )
7644           vik_track_calculate_bounds ( vtl->current_tp_track );
7645
7646         if ( vtl->tpwin )
7647           if ( vtl->current_tp_track )
7648             vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, vtl->current_tp_track->name );
7649       }
7650     }
7651
7652     // Reset
7653     vtl->current_wp    = NULL;
7654     vtl->current_wp_id = NULL;
7655     trw_layer_cancel_current_tp ( vtl, FALSE );
7656
7657     vik_layer_emit_update ( VIK_LAYER(vtl) );
7658     return TRUE;
7659   }
7660   return FALSE;
7661 }
7662
7663 /*
7664   Returns true if a waypoint or track is found near the requested event position for this particular layer
7665   The item found is automatically selected
7666   This is a tool like feature but routed via the layer interface, since it's instigated by a 'global' layer tool in vikwindow.c
7667  */
7668 static gboolean trw_layer_select_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp, tool_ed_t* tet )
7669 {
7670   if ( event->button != 1 )
7671     return FALSE;
7672
7673   if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
7674     return FALSE;
7675
7676   if ( !vtl->tracks_visible && !vtl->waypoints_visible && !vtl->routes_visible )
7677     return FALSE;
7678
7679   LatLonBBox bbox;
7680   vik_viewport_get_min_max_lat_lon ( vvp, &(bbox.south), &(bbox.north), &(bbox.west), &(bbox.east) );
7681
7682   // Go for waypoints first as these often will be near a track, but it's likely the wp is wanted rather then the track
7683
7684   if ( vtl->waypoints_visible && BBOX_INTERSECT (vtl->waypoints_bbox, bbox ) ) {
7685     WPSearchParams wp_params;
7686     wp_params.vvp = vvp;
7687     wp_params.x = event->x;
7688     wp_params.y = event->y;
7689     wp_params.draw_images = vtl->drawimages;
7690     wp_params.closest_wp_id = NULL;
7691     wp_params.closest_wp = NULL;
7692
7693     g_hash_table_foreach ( vtl->waypoints, (GHFunc) waypoint_search_closest_tp, &wp_params);
7694
7695     if ( wp_params.closest_wp )  {
7696
7697       // Select
7698       vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, g_hash_table_lookup ( vtl->waypoints_iters, wp_params.closest_wp_id ), TRUE );
7699
7700       // Too easy to move it so must be holding shift to start immediately moving it
7701       //   or otherwise be previously selected but not have an image (otherwise clicking within image bounds (again) moves it)
7702       if ( event->state & GDK_SHIFT_MASK ||
7703            ( vtl->current_wp == wp_params.closest_wp && !vtl->current_wp->image ) ) {
7704         // Put into 'move buffer'
7705         // NB vvp & vw already set in tet
7706         tet->vtl = (gpointer)vtl;
7707         tet->is_waypoint = TRUE;
7708       
7709         marker_begin_move (tet, event->x, event->y);
7710       }
7711
7712       vtl->current_wp =    wp_params.closest_wp;
7713       vtl->current_wp_id = wp_params.closest_wp_id;
7714
7715       vik_layer_emit_update ( VIK_LAYER(vtl) );
7716
7717       return TRUE;
7718     }
7719   }
7720
7721   // Used for both track and route lists
7722   TPSearchParams tp_params;
7723   tp_params.vvp = vvp;
7724   tp_params.x = event->x;
7725   tp_params.y = event->y;
7726   tp_params.closest_track_id = NULL;
7727   tp_params.closest_tp = NULL;
7728   tp_params.closest_tpl = NULL;
7729   tp_params.bbox = bbox;
7730
7731   if (vtl->tracks_visible) {
7732     g_hash_table_foreach ( vtl->tracks, (GHFunc) track_search_closest_tp, &tp_params);
7733
7734     if ( tp_params.closest_tp )  {
7735
7736       // Always select + highlight the track
7737       vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, g_hash_table_lookup ( vtl->tracks_iters, tp_params.closest_track_id ), TRUE );
7738
7739       tet->is_waypoint = FALSE;
7740
7741       // Select the Trackpoint
7742       // Can move it immediately when control held or it's the previously selected tp
7743       if ( event->state & GDK_CONTROL_MASK ||
7744            vtl->current_tpl == tp_params.closest_tpl ) {
7745         // Put into 'move buffer'
7746         // NB vvp & vw already set in tet
7747         tet->vtl = (gpointer)vtl;
7748         marker_begin_move (tet, event->x, event->y);
7749       }
7750
7751       vtl->current_tpl = tp_params.closest_tpl;
7752       vtl->current_tp_id = tp_params.closest_track_id;
7753       vtl->current_tp_track = g_hash_table_lookup ( vtl->tracks, tp_params.closest_track_id );
7754
7755       set_statusbar_msg_info_trkpt ( vtl, tp_params.closest_tp );
7756
7757       if ( vtl->tpwin )
7758         vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, vtl->current_tp_track->name );
7759
7760       vik_layer_emit_update ( VIK_LAYER(vtl) );
7761       return TRUE;
7762     }
7763   }
7764
7765   // Try again for routes
7766   if (vtl->routes_visible) {
7767     g_hash_table_foreach ( vtl->routes, (GHFunc) track_search_closest_tp, &tp_params);
7768
7769     if ( tp_params.closest_tp )  {
7770
7771       // Always select + highlight the track
7772       vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, g_hash_table_lookup ( vtl->routes_iters, tp_params.closest_track_id ), TRUE );
7773
7774       tet->is_waypoint = FALSE;
7775
7776       // Select the Trackpoint
7777       // Can move it immediately when control held or it's the previously selected tp
7778       if ( event->state & GDK_CONTROL_MASK ||
7779            vtl->current_tpl == tp_params.closest_tpl ) {
7780         // Put into 'move buffer'
7781         // NB vvp & vw already set in tet
7782         tet->vtl = (gpointer)vtl;
7783         marker_begin_move (tet, event->x, event->y);
7784       }
7785
7786       vtl->current_tpl = tp_params.closest_tpl;
7787       vtl->current_tp_id = tp_params.closest_track_id;
7788       vtl->current_tp_track = g_hash_table_lookup ( vtl->routes, tp_params.closest_track_id );
7789
7790       set_statusbar_msg_info_trkpt ( vtl, tp_params.closest_tp );
7791
7792       if ( vtl->tpwin )
7793         vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, vtl->current_tp_track->name );
7794
7795       vik_layer_emit_update ( VIK_LAYER(vtl) );
7796       return TRUE;
7797     }
7798   }
7799
7800   /* these aren't the droids you're looking for */
7801   vtl->current_wp    = NULL;
7802   vtl->current_wp_id = NULL;
7803   trw_layer_cancel_current_tp ( vtl, FALSE );
7804
7805   // Blank info
7806   vik_statusbar_set_message ( vik_window_get_statusbar (VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl))), VIK_STATUSBAR_INFO, "" );
7807
7808   return FALSE;
7809 }
7810
7811 static gboolean trw_layer_show_selected_viewport_menu ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
7812 {
7813   if ( event->button != 3 )
7814     return FALSE;
7815
7816   if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
7817     return FALSE;
7818
7819   if ( !vtl->tracks_visible && !vtl->waypoints_visible && !vtl->routes_visible )
7820     return FALSE;
7821
7822   /* Post menu for the currently selected item */
7823
7824   /* See if a track is selected */
7825   VikTrack *track = (VikTrack*)vik_window_get_selected_track ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(vtl) );
7826   if ( track && track->visible ) {
7827
7828     if ( track->name ) {
7829
7830       if ( vtl->track_right_click_menu )
7831         g_object_ref_sink ( G_OBJECT(vtl->track_right_click_menu) );
7832
7833       vtl->track_right_click_menu = GTK_MENU ( gtk_menu_new () );
7834       
7835       trku_udata udataU;
7836       udataU.trk  = track;
7837       udataU.uuid = NULL;
7838
7839       gpointer *trkf;
7840       if ( track->is_route )
7841         trkf = g_hash_table_find ( vtl->routes, (GHRFunc) trw_layer_track_find_uuid, &udataU );
7842       else
7843         trkf = g_hash_table_find ( vtl->tracks, (GHRFunc) trw_layer_track_find_uuid, &udataU );
7844
7845       if ( trkf && udataU.uuid ) {
7846
7847         GtkTreeIter *iter;
7848         if ( track->is_route )
7849           iter = g_hash_table_lookup ( vtl->routes_iters, udataU.uuid );
7850         else
7851           iter = g_hash_table_lookup ( vtl->tracks_iters, udataU.uuid );
7852
7853         trw_layer_sublayer_add_menu_items ( vtl,
7854                                             vtl->track_right_click_menu,
7855                                             NULL,
7856                                             track->is_route ? VIK_TRW_LAYER_SUBLAYER_ROUTE : VIK_TRW_LAYER_SUBLAYER_TRACK,
7857                                             udataU.uuid,
7858                                             iter,
7859                                             vvp );
7860       }
7861
7862       gtk_menu_popup ( vtl->track_right_click_menu, NULL, NULL, NULL, NULL, event->button, gtk_get_current_event_time() );
7863         
7864       return TRUE;
7865     }
7866   }
7867
7868   /* See if a waypoint is selected */
7869   VikWaypoint *waypoint = (VikWaypoint*)vik_window_get_selected_waypoint ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(vtl) );
7870   if ( waypoint && waypoint->visible ) {
7871     if ( waypoint->name ) {
7872
7873       if ( vtl->wp_right_click_menu )
7874         g_object_ref_sink ( G_OBJECT(vtl->wp_right_click_menu) );
7875
7876       vtl->wp_right_click_menu = GTK_MENU ( gtk_menu_new () );
7877
7878       wpu_udata udata;
7879       udata.wp   = waypoint;
7880       udata.uuid = NULL;
7881
7882       gpointer *wpf = g_hash_table_find ( vtl->waypoints, (GHRFunc) trw_layer_waypoint_find_uuid, (gpointer) &udata );
7883
7884       if ( wpf && udata.uuid ) {
7885         GtkTreeIter *iter = g_hash_table_lookup ( vtl->waypoints_iters, udata.uuid );
7886
7887         trw_layer_sublayer_add_menu_items ( vtl,
7888                                             vtl->wp_right_click_menu,
7889                                             NULL,
7890                                             VIK_TRW_LAYER_SUBLAYER_WAYPOINT,
7891                                             udata.uuid,
7892                                             iter,
7893                                             vvp );
7894       }
7895       gtk_menu_popup ( vtl->wp_right_click_menu, NULL, NULL, NULL, NULL, event->button, gtk_get_current_event_time() );
7896
7897       return TRUE;
7898     }
7899   }
7900
7901   return FALSE;
7902 }
7903
7904 /* background drawing hook, to be passed the viewport */
7905 static gboolean tool_sync_done = TRUE;
7906
7907 static gboolean tool_sync(gpointer data)
7908 {
7909   VikViewport *vvp = data;
7910   gdk_threads_enter();
7911   vik_viewport_sync(vvp);
7912   tool_sync_done = TRUE;
7913   gdk_threads_leave();
7914   return FALSE;
7915 }
7916
7917 static void marker_begin_move ( tool_ed_t *t, gint x, gint y )
7918 {
7919   t->holding = TRUE;
7920   t->gc = vik_viewport_new_gc (t->vvp, "black", 2);
7921   gdk_gc_set_function ( t->gc, GDK_INVERT );
7922   vik_viewport_draw_rectangle ( t->vvp, t->gc, FALSE, x-3, y-3, 6, 6 );
7923   vik_viewport_sync(t->vvp);
7924   t->oldx = x;
7925   t->oldy = y;
7926 }
7927
7928 static void marker_moveto ( tool_ed_t *t, gint x, gint y )
7929 {
7930   VikViewport *vvp =  t->vvp;
7931   vik_viewport_draw_rectangle ( vvp, t->gc, FALSE, t->oldx-3, t->oldy-3, 6, 6 );
7932   vik_viewport_draw_rectangle ( vvp, t->gc, FALSE, x-3, y-3, 6, 6 );
7933   t->oldx = x;
7934   t->oldy = y;
7935
7936   if (tool_sync_done) {
7937     g_idle_add_full (G_PRIORITY_HIGH_IDLE + 10, tool_sync, vvp, NULL);
7938     tool_sync_done = FALSE;
7939   }
7940 }
7941
7942 static void marker_end_move ( tool_ed_t *t )
7943 {
7944   vik_viewport_draw_rectangle ( t->vvp, t->gc, FALSE, t->oldx-3, t->oldy-3, 6, 6 );
7945   g_object_unref ( t->gc );
7946   t->holding = FALSE;
7947 }
7948
7949 /*** Edit waypoint ****/
7950
7951 static gpointer tool_edit_waypoint_create ( VikWindow *vw, VikViewport *vvp)
7952 {
7953   tool_ed_t *t = g_new(tool_ed_t, 1);
7954   t->vvp = vvp;
7955   t->holding = FALSE;
7956   return t;
7957 }
7958
7959 static void tool_edit_waypoint_destroy ( tool_ed_t *t )
7960 {
7961   g_free ( t );
7962 }
7963
7964 static gboolean tool_edit_waypoint_click ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data )
7965 {
7966   WPSearchParams params;
7967   tool_ed_t *t = data;
7968   VikViewport *vvp = t->vvp;
7969
7970   if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
7971     return FALSE;
7972
7973   if ( t->holding ) {
7974     return TRUE;
7975   }
7976
7977   if ( !vtl->vl.visible || !vtl->waypoints_visible )
7978     return FALSE;
7979
7980   if ( vtl->current_wp && vtl->current_wp->visible )
7981   {
7982     /* first check if current WP is within area (other may be 'closer', but we want to move the current) */
7983     gint x, y;
7984     vik_viewport_coord_to_screen ( vvp, &(vtl->current_wp->coord), &x, &y );
7985
7986     if ( abs(x - event->x) <= WAYPOINT_SIZE_APPROX &&
7987          abs(y - event->y) <= WAYPOINT_SIZE_APPROX )
7988     {
7989       if ( event->button == 3 )
7990         vtl->waypoint_rightclick = TRUE; /* remember that we're clicking; other layers will ignore release signal */
7991       else {
7992         marker_begin_move(t, event->x, event->y);
7993       }
7994       return TRUE;
7995     }
7996   }
7997
7998   params.vvp = vvp;
7999   params.x = event->x;
8000   params.y = event->y;
8001   params.draw_images = vtl->drawimages;
8002   params.closest_wp_id = NULL;
8003   /* TODO: should get track listitem so we can break it up, make a new track, mess it up, all that. */
8004   params.closest_wp = NULL;
8005   g_hash_table_foreach ( vtl->waypoints, (GHFunc) waypoint_search_closest_tp, &params);
8006   if ( vtl->current_wp == params.closest_wp && vtl->current_wp != NULL )
8007   {
8008     // how do we get here?
8009     marker_begin_move(t, event->x, event->y);
8010     g_critical("shouldn't be here");
8011     return FALSE;
8012   }
8013   else if ( params.closest_wp )
8014   {
8015     if ( event->button == 3 )
8016       vtl->waypoint_rightclick = TRUE; /* remember that we're clicking; other layers will ignore release signal */
8017     else
8018       vtl->waypoint_rightclick = FALSE;
8019
8020     vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, g_hash_table_lookup ( vtl->waypoints_iters, params.closest_wp_id ), TRUE );
8021
8022     vtl->current_wp = params.closest_wp;
8023     vtl->current_wp_id = params.closest_wp_id;
8024
8025     /* could make it so don't update if old WP is off screen and new is null but oh well */
8026     vik_layer_emit_update ( VIK_LAYER(vtl) );
8027     return TRUE;
8028   }
8029
8030   vtl->current_wp = NULL;
8031   vtl->current_wp_id = NULL;
8032   vtl->waypoint_rightclick = FALSE;
8033   vik_layer_emit_update ( VIK_LAYER(vtl) );
8034   return FALSE;
8035 }
8036
8037 static gboolean tool_edit_waypoint_move ( VikTrwLayer *vtl, GdkEventMotion *event, gpointer data )
8038 {
8039   tool_ed_t *t = data;
8040   VikViewport *vvp = t->vvp;
8041
8042   if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
8043     return FALSE;
8044
8045   if ( t->holding ) {
8046     VikCoord new_coord;
8047     vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
8048
8049     /* snap to TP */
8050     if ( event->state & GDK_CONTROL_MASK )
8051     {
8052       VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
8053       if ( tp )
8054         new_coord = tp->coord;
8055     }
8056
8057     /* snap to WP */
8058     if ( event->state & GDK_SHIFT_MASK )
8059     {
8060       VikWaypoint *wp = closest_wp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
8061       if ( wp && wp != vtl->current_wp )
8062         new_coord = wp->coord;
8063     }
8064     
8065     { 
8066       gint x, y;
8067       vik_viewport_coord_to_screen ( vvp, &new_coord, &x, &y );
8068
8069       marker_moveto ( t, x, y );
8070     } 
8071     return TRUE;
8072   }
8073   return FALSE;
8074 }
8075
8076 static gboolean tool_edit_waypoint_release ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data )
8077 {
8078   tool_ed_t *t = data;
8079   VikViewport *vvp = t->vvp;
8080
8081   if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
8082     return FALSE;
8083   
8084   if ( t->holding && event->button == 1 )
8085   {
8086     VikCoord new_coord;
8087     vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
8088
8089     /* snap to TP */
8090     if ( event->state & GDK_CONTROL_MASK )
8091     {
8092       VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
8093       if ( tp )
8094         new_coord = tp->coord;
8095     }
8096
8097     /* snap to WP */
8098     if ( event->state & GDK_SHIFT_MASK )
8099     {
8100       VikWaypoint *wp = closest_wp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
8101       if ( wp && wp != vtl->current_wp )
8102         new_coord = wp->coord;
8103     }
8104
8105     marker_end_move ( t );
8106
8107     vtl->current_wp->coord = new_coord;
8108
8109     trw_layer_calculate_bounds_waypoints ( vtl );
8110     vik_layer_emit_update ( VIK_LAYER(vtl) );
8111     return TRUE;
8112   }
8113   /* PUT IN RIGHT PLACE!!! */
8114   if ( event->button == 3 && vtl->waypoint_rightclick )
8115   {
8116     if ( vtl->wp_right_click_menu )
8117       g_object_ref_sink ( G_OBJECT(vtl->wp_right_click_menu) );
8118     if ( vtl->current_wp ) {
8119       vtl->wp_right_click_menu = GTK_MENU ( gtk_menu_new () );
8120       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 );
8121       gtk_menu_popup ( vtl->wp_right_click_menu, NULL, NULL, NULL, NULL, event->button, gtk_get_current_event_time() );
8122     }
8123     vtl->waypoint_rightclick = FALSE;
8124   }
8125   return FALSE;
8126 }
8127
8128 /*** New track ****/
8129
8130 static gpointer tool_new_track_create ( VikWindow *vw, VikViewport *vvp)
8131 {
8132   return vvp;
8133 }
8134
8135 typedef struct {
8136   VikTrwLayer *vtl;
8137   GdkDrawable *drawable;
8138   GdkGC *gc;
8139   GdkPixmap *pixmap;
8140 } draw_sync_t;
8141
8142 /*
8143  * Draw specified pixmap
8144  */
8145 static gboolean draw_sync ( gpointer data )
8146 {
8147   draw_sync_t *ds = (draw_sync_t*) data;
8148   // Sometimes don't want to draw
8149   //  normally because another update has taken precedent such as panning the display
8150   //   which means this pixmap is no longer valid
8151   if ( ds->vtl->draw_sync_do ) {
8152     gdk_threads_enter();
8153     gdk_draw_drawable (ds->drawable,
8154                        ds->gc,
8155                        ds->pixmap,
8156                        0, 0, 0, 0, -1, -1);
8157     ds->vtl->draw_sync_done = TRUE;
8158     gdk_threads_leave();
8159   }
8160   g_free ( ds );
8161   return FALSE;
8162 }
8163
8164 static gchar* distance_string (gdouble distance)
8165 {
8166   gchar str[128];
8167
8168   /* draw label with distance */
8169   vik_units_distance_t dist_units = a_vik_get_units_distance ();
8170   switch (dist_units) {
8171   case VIK_UNITS_DISTANCE_MILES:
8172     if (distance >= VIK_MILES_TO_METERS(1) && distance < VIK_MILES_TO_METERS(100)) {
8173       g_sprintf(str, "%3.2f miles", VIK_METERS_TO_MILES(distance));
8174     } else if (distance < 1609.4) {
8175       g_sprintf(str, "%d yards", (int)(distance*1.0936133));
8176     } else {
8177       g_sprintf(str, "%d miles", (int)VIK_METERS_TO_MILES(distance));
8178     }
8179     break;
8180   default:
8181     // VIK_UNITS_DISTANCE_KILOMETRES
8182     if (distance >= 1000 && distance < 100000) {
8183       g_sprintf(str, "%3.2f km", distance/1000.0);
8184     } else if (distance < 1000) {
8185       g_sprintf(str, "%d m", (int)distance);
8186     } else {
8187       g_sprintf(str, "%d km", (int)distance/1000);
8188     }
8189     break;
8190   }
8191   return g_strdup (str);
8192 }
8193
8194 /*
8195  * Actually set the message in statusbar
8196  */
8197 static void statusbar_write (gdouble distance, gdouble elev_gain, gdouble elev_loss, gdouble last_step, gdouble angle, VikTrwLayer *vtl )
8198 {
8199   // Only show elevation data when track has some elevation properties
8200   gchar str_gain_loss[64];
8201   str_gain_loss[0] = '\0';
8202   gchar str_last_step[64];
8203   str_last_step[0] = '\0';
8204   gchar *str_total = distance_string (distance);
8205   
8206   if ( (elev_gain > 0.1) || (elev_loss > 0.1) ) {
8207     if ( a_vik_get_units_height () == VIK_UNITS_HEIGHT_METRES )
8208       g_sprintf(str_gain_loss, _(" - Gain %dm:Loss %dm"), (int)elev_gain, (int)elev_loss);
8209     else
8210       g_sprintf(str_gain_loss, _(" - Gain %dft:Loss %dft"), (int)VIK_METERS_TO_FEET(elev_gain), (int)VIK_METERS_TO_FEET(elev_loss));
8211   }
8212   
8213   if ( last_step > 0 ) {
8214       gchar *tmp = distance_string (last_step);
8215       g_sprintf(str_last_step, _(" - Bearing %3.1f° - Step %s"), RAD2DEG(angle), tmp);
8216       g_free ( tmp );
8217   }
8218   
8219   VikWindow *vw = VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl));
8220
8221   // Write with full gain/loss information
8222   gchar *msg = g_strdup_printf ( "Total %s%s%s", str_total, str_last_step, str_gain_loss);
8223   vik_statusbar_set_message ( vik_window_get_statusbar (vw), VIK_STATUSBAR_INFO, msg );
8224   g_free ( msg );
8225   g_free ( str_total );
8226 }
8227
8228 /*
8229  * Figure out what information should be set in the statusbar and then write it
8230  */
8231 static void update_statusbar ( VikTrwLayer *vtl )
8232 {
8233   // Get elevation data
8234   gdouble elev_gain, elev_loss;
8235   vik_track_get_total_elevation_gain ( vtl->current_track, &elev_gain, &elev_loss);
8236
8237   /* Find out actual distance of current track */
8238   gdouble distance = vik_track_get_length (vtl->current_track);
8239
8240   statusbar_write (distance, elev_gain, elev_loss, 0, 0, vtl);
8241 }
8242
8243
8244 static VikLayerToolFuncStatus tool_new_track_move ( VikTrwLayer *vtl, GdkEventMotion *event, VikViewport *vvp )
8245 {
8246   /* if we haven't sync'ed yet, we don't have time to do more. */
8247   if ( vtl->draw_sync_done && vtl->current_track && vtl->current_track->trackpoints ) {
8248     GList *iter = g_list_last ( vtl->current_track->trackpoints );
8249     VikTrackpoint *last_tpt = VIK_TRACKPOINT(iter->data);
8250
8251     static GdkPixmap *pixmap = NULL;
8252     int w1, h1, w2, h2;
8253     // Need to check in case window has been resized
8254     w1 = vik_viewport_get_width(vvp);
8255     h1 = vik_viewport_get_height(vvp);
8256     if (!pixmap) {
8257       pixmap = gdk_pixmap_new ( gtk_widget_get_window(GTK_WIDGET(vvp)), w1, h1, -1 );
8258     }
8259     gdk_drawable_get_size (pixmap, &w2, &h2);
8260     if (w1 != w2 || h1 != h2) {
8261       g_object_unref ( G_OBJECT ( pixmap ) );
8262       pixmap = gdk_pixmap_new ( gtk_widget_get_window(GTK_WIDGET(vvp)), w1, h1, -1 );
8263     }
8264
8265     // Reset to background
8266     gdk_draw_drawable (pixmap,
8267                        vtl->current_track_newpoint_gc,
8268                        vik_viewport_get_pixmap(vvp),
8269                        0, 0, 0, 0, -1, -1);
8270
8271     draw_sync_t *passalong;
8272     gint x1, y1;
8273
8274     vik_viewport_coord_to_screen ( vvp, &(last_tpt->coord), &x1, &y1 );
8275
8276     // FOR SCREEN OVERLAYS WE MUST DRAW INTO THIS PIXMAP (when using the reset method)
8277     //  otherwise using vik_viewport_draw_* functions puts the data into the base pixmap,
8278     //  thus when we come to reset to the background it would include what we have already drawn!!
8279     gdk_draw_line ( pixmap,
8280                     vtl->current_track_newpoint_gc,
8281                     x1, y1, event->x, event->y );
8282     // Using this reset method is more reliable than trying to undraw previous efforts via the GDK_INVERT method
8283
8284     /* Find out actual distance of current track */
8285     gdouble distance = vik_track_get_length (vtl->current_track);
8286
8287     // Now add distance to where the pointer is //
8288     VikCoord coord;
8289     struct LatLon ll;
8290     vik_viewport_screen_to_coord ( vvp, (gint) event->x, (gint) event->y, &coord );
8291     vik_coord_to_latlon ( &coord, &ll );
8292     gdouble last_step = vik_coord_diff( &coord, &(last_tpt->coord));
8293     distance = distance + last_step;
8294
8295     // Get elevation data
8296     gdouble elev_gain, elev_loss;
8297     vik_track_get_total_elevation_gain ( vtl->current_track, &elev_gain, &elev_loss);
8298
8299     // Adjust elevation data (if available) for the current pointer position
8300     gdouble elev_new;
8301     elev_new = (gdouble) a_dems_get_elev_by_coord ( &coord, VIK_DEM_INTERPOL_BEST );
8302     if ( elev_new != VIK_DEM_INVALID_ELEVATION ) {
8303       if ( last_tpt->altitude != VIK_DEFAULT_ALTITUDE ) {
8304         // Adjust elevation of last track point
8305         if ( elev_new > last_tpt->altitude )
8306           // Going up
8307           elev_gain += elev_new - last_tpt->altitude;
8308         else
8309           // Going down
8310           elev_loss += last_tpt->altitude - elev_new;
8311       }
8312     }
8313
8314     //
8315     // Display of the distance 'tooltip' during track creation is controlled by a preference
8316     //
8317     if ( a_vik_get_create_track_tooltip() ) {
8318
8319       gchar *str = distance_string (distance);
8320
8321       PangoLayout *pl = gtk_widget_create_pango_layout (GTK_WIDGET(vvp), NULL);
8322       pango_layout_set_font_description (pl, gtk_widget_get_style(GTK_WIDGET(vvp))->font_desc);
8323       pango_layout_set_text (pl, str, -1);
8324       gint wd, hd;
8325       pango_layout_get_pixel_size ( pl, &wd, &hd );
8326
8327       gint xd,yd;
8328       // offset from cursor a bit depending on font size
8329       xd = event->x + 10;
8330       yd = event->y - hd;
8331
8332       // Create a background block to make the text easier to read over the background map
8333       GdkGC *background_block_gc = vik_viewport_new_gc ( vvp, "#cccccc", 1);
8334       gdk_draw_rectangle (pixmap, background_block_gc, TRUE, xd-2, yd-2, wd+4, hd+2);
8335       gdk_draw_layout (pixmap, vtl->current_track_newpoint_gc, xd, yd, pl);
8336
8337       g_object_unref ( G_OBJECT ( pl ) );
8338       g_object_unref ( G_OBJECT ( background_block_gc ) );
8339       g_free (str);
8340     }
8341
8342     passalong = g_new(draw_sync_t,1); // freed by draw_sync()
8343     passalong->vtl = vtl;
8344     passalong->pixmap = pixmap;
8345     passalong->drawable = gtk_widget_get_window(GTK_WIDGET(vvp));
8346     passalong->gc = vtl->current_track_newpoint_gc;
8347
8348     gdouble angle;
8349     gdouble baseangle;
8350     vik_viewport_compute_bearing ( vvp, x1, y1, event->x, event->y, &angle, &baseangle );
8351
8352     // Update statusbar with full gain/loss information
8353     statusbar_write (distance, elev_gain, elev_loss, last_step, angle, vtl);
8354
8355     // draw pixmap when we have time to
8356     g_idle_add_full (G_PRIORITY_HIGH_IDLE + 10, draw_sync, passalong, NULL);
8357     vtl->draw_sync_done = FALSE;
8358     return VIK_LAYER_TOOL_ACK_GRAB_FOCUS;
8359   }
8360   return VIK_LAYER_TOOL_ACK;
8361 }
8362
8363 static gboolean tool_new_track_key_press ( VikTrwLayer *vtl, GdkEventKey *event, VikViewport *vvp )
8364 {
8365   if ( vtl->current_track && event->keyval == GDK_Escape ) {
8366     vtl->current_track = NULL;
8367     vik_layer_emit_update ( VIK_LAYER(vtl) );
8368     return TRUE;
8369   } else if ( vtl->current_track && event->keyval == GDK_BackSpace ) {
8370     /* undo */
8371     if ( vtl->current_track->trackpoints )
8372     {
8373       GList *last = g_list_last(vtl->current_track->trackpoints);
8374       g_free ( last->data );
8375       vtl->current_track->trackpoints = g_list_remove_link ( vtl->current_track->trackpoints, last );
8376     }
8377
8378     update_statusbar ( vtl );
8379
8380     vik_layer_emit_update ( VIK_LAYER(vtl) );
8381     return TRUE;
8382   }
8383   return FALSE;
8384 }
8385
8386 /*
8387  * Common function to handle trackpoint button requests on either a route or a track
8388  *  . enables adding a point via normal click
8389  *  . enables removal of last point via right click
8390  *  . finishing of the track or route via double clicking
8391  */
8392 static gboolean tool_new_track_or_route_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
8393 {
8394   VikTrackpoint *tp;
8395
8396   if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
8397     return FALSE;
8398
8399   if ( event->button == 2 ) {
8400     // As the display is panning, the new track pixmap is now invalid so don't draw it
8401     //  otherwise this drawing done results in flickering back to an old image
8402     vtl->draw_sync_do = FALSE;
8403     return FALSE;
8404   }
8405
8406   if ( event->button == 3 )
8407   {
8408     if ( !vtl->current_track )
8409       return FALSE;
8410     /* undo */
8411     if ( vtl->current_track->trackpoints )
8412     {
8413       GList *last = g_list_last(vtl->current_track->trackpoints);
8414       g_free ( last->data );
8415       vtl->current_track->trackpoints = g_list_remove_link ( vtl->current_track->trackpoints, last );
8416     }
8417     vik_track_calculate_bounds ( vtl->current_track );
8418     update_statusbar ( vtl );
8419
8420     vik_layer_emit_update ( VIK_LAYER(vtl) );
8421     return TRUE;
8422   }
8423
8424   if ( event->type == GDK_2BUTTON_PRESS )
8425   {
8426     /* subtract last (duplicate from double click) tp then end */
8427     if ( vtl->current_track && vtl->current_track->trackpoints && vtl->ct_x1 == vtl->ct_x2 && vtl->ct_y1 == vtl->ct_y2 )
8428     {
8429       GList *last = g_list_last(vtl->current_track->trackpoints);
8430       g_free ( last->data );
8431       vtl->current_track->trackpoints = g_list_remove_link ( vtl->current_track->trackpoints, last );
8432       /* undo last, then end */
8433       vtl->current_track = NULL;
8434     }
8435     vik_layer_emit_update ( VIK_LAYER(vtl) );
8436     return TRUE;
8437   }
8438
8439   tp = vik_trackpoint_new();
8440   vik_viewport_screen_to_coord ( vvp, event->x, event->y, &(tp->coord) );
8441
8442   /* snap to other TP */
8443   if ( event->state & GDK_CONTROL_MASK )
8444   {
8445     VikTrackpoint *other_tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
8446     if ( other_tp )
8447       tp->coord = other_tp->coord;
8448   }
8449
8450   tp->newsegment = FALSE;
8451   tp->has_timestamp = FALSE;
8452   tp->timestamp = 0;
8453
8454   if ( vtl->current_track ) {
8455     vik_track_add_trackpoint ( vtl->current_track, tp, TRUE ); // Ensure bounds is updated
8456     /* Auto attempt to get elevation from DEM data (if it's available) */
8457     vik_track_apply_dem_data_last_trackpoint ( vtl->current_track );
8458   }
8459
8460   vtl->ct_x1 = vtl->ct_x2;
8461   vtl->ct_y1 = vtl->ct_y2;
8462   vtl->ct_x2 = event->x;
8463   vtl->ct_y2 = event->y;
8464
8465   vik_layer_emit_update ( VIK_LAYER(vtl) );
8466   return TRUE;
8467 }
8468
8469 static gboolean tool_new_track_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
8470 {
8471   // ----------------------------------------------------- if current is a route - switch to new track
8472   if ( event->button == 1 && ( ! vtl->current_track || (vtl->current_track && vtl->current_track->is_route ) ))
8473   {
8474     gchar *name = trw_layer_new_unique_sublayer_name(vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, _("Track"));
8475     if ( ( name = a_dialog_new_track ( VIK_GTK_WINDOW_FROM_LAYER(vtl), vtl->tracks, name, FALSE ) ) )
8476     {
8477       new_track_create_common ( vtl, name );
8478       g_free ( name );
8479     }
8480     else
8481       return TRUE;
8482   }
8483   return tool_new_track_or_route_click ( vtl, event, vvp );
8484 }
8485
8486 static void tool_new_track_release ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
8487 {
8488   if ( event->button == 2 ) {
8489     // Pan moving ended - enable potential point drawing again
8490     vtl->draw_sync_do = TRUE;
8491     vtl->draw_sync_done = TRUE;
8492   }
8493 }
8494
8495 /*** New route ****/
8496
8497 static gpointer tool_new_route_create ( VikWindow *vw, VikViewport *vvp)
8498 {
8499   return vvp;
8500 }
8501
8502 static gboolean tool_new_route_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
8503 {
8504   // -------------------------- if current is a track - switch to new route
8505   if ( event->button == 1 && ( ! vtl->current_track || (vtl->current_track && !vtl->current_track->is_route ) ) )
8506   {
8507     gchar *name = trw_layer_new_unique_sublayer_name(vtl, VIK_TRW_LAYER_SUBLAYER_ROUTE, _("Route"));
8508     if ( ( name = a_dialog_new_track ( VIK_GTK_WINDOW_FROM_LAYER(vtl), vtl->routes, name, TRUE ) ) ) {
8509       new_route_create_common ( vtl, name );
8510       g_free ( name );
8511     }
8512     else
8513       return TRUE;
8514   }
8515   return tool_new_track_or_route_click ( vtl, event, vvp );
8516 }
8517
8518 /*** New waypoint ****/
8519
8520 static gpointer tool_new_waypoint_create ( VikWindow *vw, VikViewport *vvp)
8521 {
8522   return vvp;
8523 }
8524
8525 static gboolean tool_new_waypoint_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
8526 {
8527   VikCoord coord;
8528   if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
8529     return FALSE;
8530   vik_viewport_screen_to_coord ( vvp, event->x, event->y, &coord );
8531   if (vik_trw_layer_new_waypoint ( vtl, VIK_GTK_WINDOW_FROM_LAYER(vtl), &coord ) && VIK_LAYER(vtl)->visible) {
8532     trw_layer_calculate_bounds_waypoints ( vtl );
8533     vik_layer_emit_update ( VIK_LAYER(vtl) );
8534   }
8535   return TRUE;
8536 }
8537
8538
8539 /*** Edit trackpoint ****/
8540
8541 static gpointer tool_edit_trackpoint_create ( VikWindow *vw, VikViewport *vvp)
8542 {
8543   tool_ed_t *t = g_new(tool_ed_t, 1);
8544   t->vvp = vvp;
8545   t->holding = FALSE;
8546   return t;
8547 }
8548
8549 static void tool_edit_trackpoint_destroy ( tool_ed_t *t )
8550 {
8551   g_free ( t );
8552 }
8553
8554 static gboolean tool_edit_trackpoint_click ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data )
8555 {
8556   tool_ed_t *t = data;
8557   VikViewport *vvp = t->vvp;
8558   TPSearchParams params;
8559   /* OUTDATED DOCUMENTATION:
8560    find 5 pixel range on each side. then put these UTM, and a pointer
8561    to the winning track name (and maybe the winning track itself), and a
8562    pointer to the winning trackpoint, inside an array or struct. pass 
8563    this along, do a foreach on the tracks which will do a foreach on the 
8564    trackpoints. */
8565   params.vvp = vvp;
8566   params.x = event->x;
8567   params.y = event->y;
8568   params.closest_track_id = NULL;
8569   /* TODO: should get track listitem so we can break it up, make a new track, mess it up, all that. */
8570   params.closest_tp = NULL;
8571   params.closest_tpl = NULL;
8572   vik_viewport_get_min_max_lat_lon ( vvp, &(params.bbox.south), &(params.bbox.north), &(params.bbox.west), &(params.bbox.east) );
8573
8574   if ( event->button != 1 ) 
8575     return FALSE;
8576
8577   if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
8578     return FALSE;
8579
8580   if ( !vtl->vl.visible || !vtl->tracks_visible || !vtl->routes_visible )
8581     return FALSE;
8582
8583   if ( vtl->current_tpl )
8584   {
8585     /* 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.) */
8586     VikTrackpoint *tp = VIK_TRACKPOINT(vtl->current_tpl->data);
8587     VikTrack *current_tr = VIK_TRACK(g_hash_table_lookup(vtl->tracks, vtl->current_tp_id));
8588     if ( !current_tr )
8589       return FALSE;
8590
8591     gint x, y;
8592     vik_viewport_coord_to_screen ( vvp, &(tp->coord), &x, &y );
8593
8594     if ( current_tr->visible && 
8595          abs(x - event->x) < TRACKPOINT_SIZE_APPROX &&
8596          abs(y - event->y) < TRACKPOINT_SIZE_APPROX ) {
8597       marker_begin_move ( t, event->x, event->y );
8598       return TRUE;
8599     }
8600
8601   }
8602
8603   if ( vtl->tracks_visible )
8604     g_hash_table_foreach ( vtl->tracks, (GHFunc) track_search_closest_tp, &params);
8605
8606   if ( params.closest_tp )
8607   {
8608     vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, g_hash_table_lookup ( vtl->tracks_iters, params.closest_track_id ), TRUE );
8609     vtl->current_tpl = params.closest_tpl;
8610     vtl->current_tp_id = params.closest_track_id;
8611     vtl->current_tp_track = g_hash_table_lookup ( vtl->tracks, params.closest_track_id );
8612     trw_layer_tpwin_init ( vtl );
8613     set_statusbar_msg_info_trkpt ( vtl, params.closest_tp );
8614     vik_layer_emit_update ( VIK_LAYER(vtl) );
8615     return TRUE;
8616   }
8617
8618   if ( vtl->routes_visible )
8619     g_hash_table_foreach ( vtl->routes, (GHFunc) track_search_closest_tp, &params);
8620
8621   if ( params.closest_tp )
8622   {
8623     vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, g_hash_table_lookup ( vtl->routes_iters, params.closest_track_id ), TRUE );
8624     vtl->current_tpl = params.closest_tpl;
8625     vtl->current_tp_id = params.closest_track_id;
8626     vtl->current_tp_track = g_hash_table_lookup ( vtl->routes, params.closest_track_id );
8627     trw_layer_tpwin_init ( vtl );
8628     set_statusbar_msg_info_trkpt ( vtl, params.closest_tp );
8629     vik_layer_emit_update ( VIK_LAYER(vtl) );
8630     return TRUE;
8631   }
8632
8633   /* these aren't the droids you're looking for */
8634   return FALSE;
8635 }
8636
8637 static gboolean tool_edit_trackpoint_move ( VikTrwLayer *vtl, GdkEventMotion *event, gpointer data )
8638 {
8639   tool_ed_t *t = data;
8640   VikViewport *vvp = t->vvp;
8641
8642   if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
8643     return FALSE;
8644
8645   if ( t->holding )
8646   {
8647     VikCoord new_coord;
8648     vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
8649
8650     /* snap to TP */
8651     if ( event->state & GDK_CONTROL_MASK )
8652     {
8653       VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
8654       if ( tp && tp != vtl->current_tpl->data )
8655         new_coord = tp->coord;
8656     }
8657     //    VIK_TRACKPOINT(vtl->current_tpl->data)->coord = new_coord;
8658     { 
8659       gint x, y;
8660       vik_viewport_coord_to_screen ( vvp, &new_coord, &x, &y );
8661       marker_moveto ( t, x, y );
8662     } 
8663
8664     return TRUE;
8665   }
8666   return FALSE;
8667 }
8668
8669 static gboolean tool_edit_trackpoint_release ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data )
8670 {
8671   tool_ed_t *t = data;
8672   VikViewport *vvp = t->vvp;
8673
8674   if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
8675     return FALSE;
8676   if ( event->button != 1) 
8677     return FALSE;
8678
8679   if ( t->holding ) {
8680     VikCoord new_coord;
8681     vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
8682
8683     /* snap to TP */
8684     if ( event->state & GDK_CONTROL_MASK )
8685     {
8686       VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
8687       if ( tp && tp != vtl->current_tpl->data )
8688         new_coord = tp->coord;
8689     }
8690
8691     VIK_TRACKPOINT(vtl->current_tpl->data)->coord = new_coord;
8692     if ( vtl->current_tp_track )
8693       vik_track_calculate_bounds ( vtl->current_tp_track );
8694
8695     marker_end_move ( t );
8696
8697     /* diff dist is diff from orig */
8698     if ( vtl->tpwin )
8699       vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, vtl->current_tp_track->name );
8700
8701     vik_layer_emit_update ( VIK_LAYER(vtl) );
8702     return TRUE;
8703   }
8704   return FALSE;
8705 }
8706
8707
8708 /*** Route Finder ***/
8709 static gpointer tool_route_finder_create ( VikWindow *vw, VikViewport *vvp)
8710 {
8711   return vvp;
8712 }
8713
8714 static gboolean tool_route_finder_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
8715 {
8716   VikCoord tmp;
8717   if ( !vtl ) return FALSE;
8718   vik_viewport_screen_to_coord ( vvp, event->x, event->y, &tmp );
8719   if ( event->button == 3 && vtl->route_finder_current_track ) {
8720     VikCoord *new_end;
8721     new_end = vik_track_cut_back_to_double_point ( vtl->route_finder_current_track );
8722     if ( new_end ) {
8723       vtl->route_finder_coord = *new_end;
8724       g_free ( new_end );
8725       vik_layer_emit_update ( VIK_LAYER(vtl) );
8726       /* remove last ' to:...' */
8727       if ( vtl->route_finder_current_track->comment ) {
8728         gchar *last_to = strrchr ( vtl->route_finder_current_track->comment, 't' );
8729         if ( last_to && (last_to - vtl->route_finder_current_track->comment > 1) ) {
8730           gchar *new_comment = g_strndup ( vtl->route_finder_current_track->comment,
8731                                            last_to - vtl->route_finder_current_track->comment - 1);
8732           vik_track_set_comment_no_copy ( vtl->route_finder_current_track, new_comment );
8733         }
8734       }
8735     }
8736   }
8737   else if ( vtl->route_finder_started || (event->state & GDK_CONTROL_MASK && vtl->route_finder_current_track) ) {
8738     struct LatLon start, end;
8739
8740     vik_coord_to_latlon ( &(vtl->route_finder_coord), &start );
8741     vik_coord_to_latlon ( &(tmp), &end );
8742     vtl->route_finder_coord = tmp; /* for continuations */
8743
8744     /* these are checked when adding a track from a file (vik_trw_layer_filein_add_track) */
8745     if ( event->state & GDK_CONTROL_MASK && vtl->route_finder_current_track ) {
8746       vtl->route_finder_append = TRUE;  // merge tracks. keep started true.
8747     } else {
8748       vtl->route_finder_check_added_track = TRUE;
8749       vtl->route_finder_started = FALSE;
8750     }
8751
8752     vik_routing_default_find ( vtl, start, end);
8753
8754     /* see if anything was done -- a track was added or appended to */
8755     if ( vtl->route_finder_check_added_track && vtl->route_finder_added_track ) {
8756       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 ) );
8757     } else if ( vtl->route_finder_append == FALSE && vtl->route_finder_current_track ) {
8758       /* route_finder_append was originally TRUE but set to FALSE by filein_add_track */
8759       gchar *new_comment = g_strdup_printf("%s to: %f,%f", vtl->route_finder_current_track->comment, end.lat, end.lon );
8760       vik_track_set_comment_no_copy ( vtl->route_finder_current_track, new_comment );
8761     }
8762
8763     if ( vtl->route_finder_added_track )
8764       vik_track_calculate_bounds ( vtl->route_finder_added_track );
8765
8766     vtl->route_finder_added_track = NULL;
8767     vtl->route_finder_check_added_track = FALSE;
8768     vtl->route_finder_append = FALSE;
8769
8770     vik_layer_emit_update ( VIK_LAYER(vtl) );
8771   } else {
8772     vtl->route_finder_started = TRUE;
8773     vtl->route_finder_coord = tmp;
8774     vtl->route_finder_current_track = NULL;
8775   }
8776   return TRUE;
8777 }
8778
8779 /*** Show picture ****/
8780
8781 static gpointer tool_show_picture_create ( VikWindow *vw, VikViewport *vvp)
8782 {
8783   return vvp;
8784 }
8785
8786 /* Params are: vvp, event, last match found or NULL */
8787 static void tool_show_picture_wp ( const gpointer id, VikWaypoint *wp, gpointer params[3] )
8788 {
8789   if ( wp->image && wp->visible )
8790   {
8791     gint x, y, slackx, slacky;
8792     GdkEventButton *event = (GdkEventButton *) params[1];
8793
8794     vik_viewport_coord_to_screen ( VIK_VIEWPORT(params[0]), &(wp->coord), &x, &y );
8795     slackx = wp->image_width / 2;
8796     slacky = wp->image_height / 2;
8797     if (    x <= event->x + slackx && x >= event->x - slackx
8798          && y <= event->y + slacky && y >= event->y - slacky )
8799     {
8800       params[2] = wp->image; /* we've found a match. however continue searching
8801                               * since we want to find the last match -- that
8802                               * is, the match that was drawn last. */
8803     }
8804   }
8805 }
8806
8807 static void trw_layer_show_picture ( gpointer pass_along[6] )
8808 {
8809   /* thanks to the Gaim people for showing me ShellExecute and g_spawn_command_line_async */
8810 #ifdef WINDOWS
8811   ShellExecute(NULL, "open", (char *) pass_along[5], NULL, NULL, SW_SHOWNORMAL);
8812 #else /* WINDOWS */
8813   GError *err = NULL;
8814   gchar *quoted_file = g_shell_quote ( (gchar *) pass_along[5] );
8815   gchar *cmd = g_strdup_printf ( "%s %s", a_vik_get_image_viewer(), quoted_file );
8816   g_free ( quoted_file );
8817   if ( ! g_spawn_command_line_async ( cmd, &err ) )
8818     {
8819       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() );
8820       g_error_free ( err );
8821     }
8822   g_free ( cmd );
8823 #endif /* WINDOWS */
8824 }
8825
8826 static gboolean tool_show_picture_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
8827 {
8828   gpointer params[3] = { vvp, event, NULL };
8829   if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
8830     return FALSE;
8831   g_hash_table_foreach ( vtl->waypoints, (GHFunc) tool_show_picture_wp, params );
8832   if ( params[2] )
8833   {
8834     static gpointer pass_along[6];
8835     pass_along[0] = vtl;
8836     pass_along[5] = params[2];
8837     trw_layer_show_picture ( pass_along );
8838     return TRUE; /* found a match */
8839   }
8840   else
8841     return FALSE; /* go through other layers, searching for a match */
8842 }
8843
8844 /***************************************************************************
8845  ** End tool code 
8846  ***************************************************************************/
8847
8848
8849 static void image_wp_make_list ( const gpointer id, VikWaypoint *wp, GSList **pics )
8850 {
8851   if ( wp->image && ( ! a_thumbnails_exists ( wp->image ) ) )
8852     *pics = g_slist_append ( *pics, (gpointer) g_strdup ( wp->image ) );
8853 }
8854
8855 /* Structure for thumbnail creating data used in the background thread */
8856 typedef struct {
8857   VikTrwLayer *vtl; // Layer needed for redrawing
8858   GSList *pics;     // Image list
8859 } thumbnail_create_thread_data;
8860
8861 static int create_thumbnails_thread ( thumbnail_create_thread_data *tctd, gpointer threaddata )
8862 {
8863   guint total = g_slist_length(tctd->pics), done = 0;
8864   while ( tctd->pics )
8865   {
8866     a_thumbnails_create ( (gchar *) tctd->pics->data );
8867     int result = a_background_thread_progress ( threaddata, ((gdouble) ++done) / total );
8868     if ( result != 0 )
8869       return -1; /* Abort thread */
8870
8871     tctd->pics = tctd->pics->next;
8872   }
8873
8874   // Redraw to show the thumbnails as they are now created
8875   if ( IS_VIK_LAYER(tctd->vtl) )
8876     vik_layer_emit_update ( VIK_LAYER(tctd->vtl) ); // NB update from background thread
8877
8878   return 0;
8879 }
8880
8881 static void thumbnail_create_thread_free ( thumbnail_create_thread_data *tctd )
8882 {
8883   while ( tctd->pics )
8884   {
8885     g_free ( tctd->pics->data );
8886     tctd->pics = g_slist_delete_link ( tctd->pics, tctd->pics );
8887   }
8888   g_free ( tctd );
8889 }
8890
8891 void trw_layer_verify_thumbnails ( VikTrwLayer *vtl, GtkWidget *vp )
8892 {
8893   if ( ! vtl->has_verified_thumbnails )
8894   {
8895     GSList *pics = NULL;
8896     g_hash_table_foreach ( vtl->waypoints, (GHFunc) image_wp_make_list, &pics );
8897     if ( pics )
8898     {
8899       gint len = g_slist_length ( pics );
8900       gchar *tmp = g_strdup_printf ( _("Creating %d Image Thumbnails..."), len );
8901       thumbnail_create_thread_data *tctd = g_malloc ( sizeof(thumbnail_create_thread_data) );
8902       tctd->vtl = vtl;
8903       tctd->pics = pics;
8904       a_background_thread ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
8905                             tmp,
8906                             (vik_thr_func) create_thumbnails_thread,
8907                             tctd,
8908                             (vik_thr_free_func) thumbnail_create_thread_free,
8909                             NULL,
8910                             len );
8911       g_free ( tmp );
8912     }
8913   }
8914 }
8915
8916 static const gchar* my_track_colors ( gint ii )
8917 {
8918   static const gchar* colors[VIK_TRW_LAYER_TRACK_GCS] = {
8919     "#2d870a",
8920     "#135D34",
8921     "#0a8783",
8922     "#0e4d87",
8923     "#05469f",
8924     "#695CBB",
8925     "#2d059f",
8926     "#4a059f",
8927     "#5A171A",
8928     "#96059f"
8929   };
8930   // Fast and reliable way of returning a colour
8931   return colors[(ii % VIK_TRW_LAYER_TRACK_GCS)];
8932 }
8933
8934 static void trw_layer_track_alloc_colors ( VikTrwLayer *vtl )
8935 {
8936   GHashTableIter iter;
8937   gpointer key, value;
8938
8939   gint ii = 0;
8940   // Tracks
8941   g_hash_table_iter_init ( &iter, vtl->tracks );
8942
8943   while ( g_hash_table_iter_next (&iter, &key, &value) ) {
8944
8945     // Tracks get a random spread of colours if not already assigned
8946     if ( ! VIK_TRACK(value)->has_color ) {
8947       if ( vtl->drawmode == DRAWMODE_ALL_SAME_COLOR )
8948         VIK_TRACK(value)->color = vtl->track_color;
8949       else {
8950         gdk_color_parse ( my_track_colors (ii), &(VIK_TRACK(value)->color) );
8951       }
8952       VIK_TRACK(value)->has_color = TRUE;
8953     }
8954
8955     trw_layer_update_treeview ( vtl, VIK_TRACK(value), key );
8956
8957     ii++;
8958     if (ii > VIK_TRW_LAYER_TRACK_GCS)
8959       ii = 0;
8960   }
8961
8962   // Routes
8963   ii = 0;
8964   g_hash_table_iter_init ( &iter, vtl->routes );
8965
8966   while ( g_hash_table_iter_next (&iter, &key, &value) ) {
8967
8968     // Routes get an intermix of reds
8969     if ( ! VIK_TRACK(value)->has_color ) {
8970       if ( ii )
8971         gdk_color_parse ( "#FF0000" , &(VIK_TRACK(value)->color) ); // Red
8972       else
8973         gdk_color_parse ( "#B40916" , &(VIK_TRACK(value)->color) ); // Dark Red
8974       VIK_TRACK(value)->has_color = TRUE;
8975     }
8976
8977     trw_layer_update_treeview ( vtl, VIK_TRACK(value), key );
8978
8979     ii = !ii;
8980   }
8981 }
8982
8983 /*
8984  * (Re)Calculate the bounds of the waypoints in this layer,
8985  * This should be called whenever waypoints are changed
8986  */
8987 static void trw_layer_calculate_bounds_waypoints ( VikTrwLayer *vtl )
8988 {
8989   struct LatLon topleft = { 0.0, 0.0 };
8990   struct LatLon bottomright = { 0.0, 0.0 };
8991   struct LatLon ll;
8992
8993   GHashTableIter iter;
8994   gpointer key, value;
8995
8996   g_hash_table_iter_init ( &iter, vtl->waypoints );
8997
8998   // Set bounds to first point
8999   if ( g_hash_table_iter_next (&iter, &key, &value) ) {
9000     vik_coord_to_latlon ( &(VIK_WAYPOINT(value)->coord), &topleft );
9001     vik_coord_to_latlon ( &(VIK_WAYPOINT(value)->coord), &bottomright );
9002   }
9003
9004   // Ensure there is another point...
9005   if ( g_hash_table_size ( vtl->waypoints ) > 1 ) {
9006
9007     while ( g_hash_table_iter_next (&iter, &key, &value) ) {
9008
9009       // See if this point increases the bounds.
9010       vik_coord_to_latlon ( &(VIK_WAYPOINT(value)->coord), &ll );
9011
9012       if ( ll.lat > topleft.lat) topleft.lat = ll.lat;
9013       if ( ll.lon < topleft.lon) topleft.lon = ll.lon;
9014       if ( ll.lat < bottomright.lat) bottomright.lat = ll.lat;
9015       if ( ll.lon > bottomright.lon) bottomright.lon = ll.lon;
9016     }
9017   }
9018
9019   vtl->waypoints_bbox.north = topleft.lat;
9020   vtl->waypoints_bbox.east = bottomright.lon;
9021   vtl->waypoints_bbox.south = bottomright.lat;
9022   vtl->waypoints_bbox.west = topleft.lon;
9023 }
9024
9025 static void trw_layer_calculate_bounds_track ( gpointer id, VikTrack *trk )
9026 {
9027   vik_track_calculate_bounds ( trk );
9028 }
9029
9030 static void trw_layer_calculate_bounds_tracks ( VikTrwLayer *vtl )
9031 {
9032   g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_calculate_bounds_track, NULL );
9033   g_hash_table_foreach ( vtl->routes, (GHFunc) trw_layer_calculate_bounds_track, NULL );
9034 }
9035
9036 static void trw_layer_sort_all ( VikTrwLayer *vtl )
9037 {
9038   if ( ! VIK_LAYER(vtl)->vt )
9039     return;
9040
9041   // Obviously need 2 to tango - sorting with only 1 (or less) is a lonely activity!
9042   if ( g_hash_table_size (vtl->tracks) > 1 )
9043     vik_treeview_sort_children ( VIK_LAYER(vtl)->vt, &(vtl->tracks_iter), vtl->track_sort_order );
9044
9045   if ( g_hash_table_size (vtl->routes) > 1 )
9046     vik_treeview_sort_children ( VIK_LAYER(vtl)->vt, &(vtl->routes_iter), vtl->track_sort_order );
9047
9048   if ( g_hash_table_size (vtl->waypoints) > 1 )
9049     vik_treeview_sort_children ( VIK_LAYER(vtl)->vt, &(vtl->waypoints_iter), vtl->wp_sort_order );
9050 }
9051
9052 static void trw_layer_post_read ( VikTrwLayer *vtl, GtkWidget *vvp, gboolean from_file )
9053 {
9054   trw_layer_verify_thumbnails ( vtl, vvp );
9055   trw_layer_track_alloc_colors ( vtl );
9056
9057   trw_layer_calculate_bounds_waypoints ( vtl );
9058   trw_layer_calculate_bounds_tracks ( vtl );
9059
9060   // Apply treeview sort after loading all the tracks for this layer
9061   //  (rather than sorted insert on each individual track additional)
9062   //  and after subsequent changes to the properties as the specified order may have changed.
9063   //  since the sorting of a treeview section is now very quick
9064   // NB sorting is also performed after every name change as well to maintain the list order
9065   trw_layer_sort_all ( vtl );
9066 }
9067
9068 VikCoordMode vik_trw_layer_get_coord_mode ( VikTrwLayer *vtl )
9069 {
9070   return vtl->coord_mode;
9071 }
9072
9073 /**
9074  * Uniquify the whole layer
9075  * Also requires the layers panel as the names shown there need updating too
9076  * Returns whether the operation was successful or not
9077  */
9078 gboolean vik_trw_layer_uniquify ( VikTrwLayer *vtl, VikLayersPanel *vlp )
9079 {
9080   if ( vtl && vlp ) {
9081     vik_trw_layer_uniquify_tracks ( vtl, vlp, vtl->tracks, TRUE );
9082     vik_trw_layer_uniquify_tracks ( vtl, vlp, vtl->routes, FALSE );
9083     vik_trw_layer_uniquify_waypoints ( vtl, vlp );
9084     return TRUE;
9085   }
9086   return FALSE;
9087 }
9088
9089 static void waypoint_convert ( const gpointer id, VikWaypoint *wp, VikCoordMode *dest_mode )
9090 {
9091   vik_coord_convert ( &(wp->coord), *dest_mode );
9092 }
9093
9094 static void track_convert ( const gpointer id, VikTrack *tr, VikCoordMode *dest_mode )
9095 {
9096   vik_track_convert ( tr, *dest_mode );
9097 }
9098
9099 static void trw_layer_change_coord_mode ( VikTrwLayer *vtl, VikCoordMode dest_mode )
9100 {
9101   if ( vtl->coord_mode != dest_mode )
9102   {
9103     vtl->coord_mode = dest_mode;
9104     g_hash_table_foreach ( vtl->waypoints, (GHFunc) waypoint_convert, &dest_mode );
9105     g_hash_table_foreach ( vtl->tracks, (GHFunc) track_convert, &dest_mode );
9106     g_hash_table_foreach ( vtl->routes, (GHFunc) track_convert, &dest_mode );
9107   }
9108 }
9109
9110 static void trw_layer_set_menu_selection ( VikTrwLayer *vtl, guint16 selection )
9111 {
9112   vtl->menu_selection = selection;
9113 }
9114
9115 static guint16 trw_layer_get_menu_selection ( VikTrwLayer *vtl )
9116 {
9117   return (vtl->menu_selection);
9118 }
9119
9120 /* ----------- Downloading maps along tracks --------------- */
9121
9122 static int get_download_area_width(VikViewport *vvp, gdouble zoom_level, struct LatLon *wh)
9123 {
9124   /* TODO: calculating based on current size of viewport */
9125   const gdouble w_at_zoom_0_125 = 0.0013;
9126   const gdouble h_at_zoom_0_125 = 0.0011;
9127   gdouble zoom_factor = zoom_level/0.125;
9128
9129   wh->lat = h_at_zoom_0_125 * zoom_factor;
9130   wh->lon = w_at_zoom_0_125 * zoom_factor;
9131
9132   return 0;   /* all OK */
9133 }
9134
9135 static VikCoord *get_next_coord(VikCoord *from, VikCoord *to, struct LatLon *dist, gdouble gradient)
9136 {
9137   if ((dist->lon >= ABS(to->east_west - from->east_west)) &&
9138       (dist->lat >= ABS(to->north_south - from->north_south)))
9139     return NULL;
9140
9141   VikCoord *coord = g_malloc(sizeof(VikCoord));
9142   coord->mode = VIK_COORD_LATLON;
9143
9144   if (ABS(gradient) < 1) {
9145     if (from->east_west > to->east_west)
9146       coord->east_west = from->east_west - dist->lon;
9147     else
9148       coord->east_west = from->east_west + dist->lon;
9149     coord->north_south = gradient * (coord->east_west - from->east_west) + from->north_south;
9150   } else {
9151     if (from->north_south > to->north_south)
9152       coord->north_south = from->north_south - dist->lat;
9153     else
9154       coord->north_south = from->north_south + dist->lat;
9155     coord->east_west = (1/gradient) * (coord->north_south - from->north_south) + from->north_south;
9156   }
9157
9158   return coord;
9159 }
9160
9161 static GList *add_fillins(GList *list, VikCoord *from, VikCoord *to, struct LatLon *dist)
9162 {
9163   /* TODO: handle virtical track (to->east_west - from->east_west == 0) */
9164   gdouble gradient = (to->north_south - from->north_south)/(to->east_west - from->east_west);
9165
9166   VikCoord *next = from;
9167   while (TRUE) {
9168     if ((next = get_next_coord(next, to, dist, gradient)) == NULL)
9169         break;
9170     list = g_list_prepend(list, next);
9171   }
9172
9173   return list;
9174 }
9175
9176 void vik_track_download_map(VikTrack *tr, VikMapsLayer *vml, VikViewport *vvp, gdouble zoom_level)
9177 {
9178   typedef struct _Rect {
9179     VikCoord tl;
9180     VikCoord br;
9181     VikCoord center;
9182   } Rect;
9183 #define GLRECT(iter) ((Rect *)((iter)->data))
9184
9185   struct LatLon wh;
9186   GList *rects_to_download = NULL;
9187   GList *rect_iter;
9188
9189   if (get_download_area_width(vvp, zoom_level, &wh))
9190     return;
9191
9192   GList *iter = tr->trackpoints;
9193   if (!iter)
9194     return;
9195
9196   gboolean new_map = TRUE;
9197   VikCoord *cur_coord, tl, br;
9198   Rect *rect;
9199   while (iter) {
9200     cur_coord = &(VIK_TRACKPOINT(iter->data))->coord;
9201     if (new_map) {
9202       vik_coord_set_area(cur_coord, &wh, &tl, &br);
9203       rect = g_malloc(sizeof(Rect));
9204       rect->tl = tl;
9205       rect->br = br;
9206       rect->center = *cur_coord;
9207       rects_to_download = g_list_prepend(rects_to_download, rect);
9208       new_map = FALSE;
9209       iter = iter->next;
9210       continue;
9211     }
9212     gboolean found = FALSE;
9213     for (rect_iter = rects_to_download; rect_iter; rect_iter = rect_iter->next) {
9214       if (vik_coord_inside(cur_coord, &GLRECT(rect_iter)->tl, &GLRECT(rect_iter)->br)) {
9215         found = TRUE;
9216         break;
9217       }
9218     }
9219     if (found)
9220         iter = iter->next;
9221     else
9222       new_map = TRUE;
9223   }
9224
9225   GList *fillins = NULL;
9226   /* 'fillin' doesn't work in UTM mode - potentially ending up in massive loop continually allocating memory - hence don't do it */
9227   /* seems that ATM the function get_next_coord works only for LATLON */
9228   if ( cur_coord->mode == VIK_COORD_LATLON ) {
9229     /* fill-ins for far apart points */
9230     GList *cur_rect, *next_rect;
9231     for (cur_rect = rects_to_download;
9232          (next_rect = cur_rect->next) != NULL;
9233          cur_rect = cur_rect->next) {
9234       if ((wh.lon < ABS(GLRECT(cur_rect)->center.east_west - GLRECT(next_rect)->center.east_west)) ||
9235           (wh.lat < ABS(GLRECT(cur_rect)->center.north_south - GLRECT(next_rect)->center.north_south))) {
9236         fillins = add_fillins(fillins, &GLRECT(cur_rect)->center, &GLRECT(next_rect)->center, &wh);
9237       }
9238     }
9239   } else
9240     g_message("%s: this feature works only in Mercator mode", __FUNCTION__);
9241
9242   if (fillins) {
9243     GList *iter = fillins;
9244     while (iter) {
9245       cur_coord = (VikCoord *)(iter->data);
9246       vik_coord_set_area(cur_coord, &wh, &tl, &br);
9247       rect = g_malloc(sizeof(Rect));
9248       rect->tl = tl;
9249       rect->br = br;
9250       rect->center = *cur_coord;
9251       rects_to_download = g_list_prepend(rects_to_download, rect);
9252       iter = iter->next;
9253     }
9254   }
9255
9256   for (rect_iter = rects_to_download; rect_iter; rect_iter = rect_iter->next) {
9257     vik_maps_layer_download_section (vml, vvp, &(((Rect *)(rect_iter->data))->tl), &(((Rect *)(rect_iter->data))->br), zoom_level);
9258   }
9259
9260   if (fillins) {
9261     for (iter = fillins; iter; iter = iter->next)
9262       g_free(iter->data);
9263     g_list_free(fillins);
9264   }
9265   if (rects_to_download) {
9266     for (rect_iter = rects_to_download; rect_iter; rect_iter = rect_iter->next)
9267       g_free(rect_iter->data);
9268     g_list_free(rects_to_download);
9269   }
9270 }
9271
9272 static void trw_layer_download_map_along_track_cb ( gpointer pass_along[6] )
9273 {
9274   VikMapsLayer *vml;
9275   gint selected_map, default_map;
9276   gchar *zoomlist[] = {"0.125", "0.25", "0.5", "1", "2", "4", "8", "16", "32", "64", "128", "256", "512", "1024", NULL };
9277   gdouble zoom_vals[] = {0.125, 0.25, 0.5, 1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024};
9278   gint selected_zoom, default_zoom;
9279   int i,j;
9280
9281
9282   VikTrwLayer *vtl = pass_along[0];
9283   VikLayersPanel *vlp = pass_along[1];
9284   VikTrack *trk;
9285   if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
9286     trk = (VikTrack *) g_hash_table_lookup ( vtl->routes, pass_along[3] );
9287   else
9288     trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
9289   if ( !trk )
9290     return;
9291
9292   VikViewport *vvp = vik_window_viewport((VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl)));
9293
9294   GList *vmls = vik_layers_panel_get_all_layers_of_type(vlp, VIK_LAYER_MAPS, TRUE); // Includes hidden map layer types
9295   int num_maps = g_list_length(vmls);
9296
9297   if (!num_maps) {
9298     a_dialog_msg(VIK_GTK_WINDOW_FROM_LAYER(vtl), GTK_MESSAGE_ERROR, _("No map layer in use. Create one first"), NULL);
9299     return;
9300   }
9301
9302   gchar **map_names = g_malloc(1 + num_maps * sizeof(gpointer));
9303   VikMapsLayer **map_layers = g_malloc(1 + num_maps * sizeof(gpointer));
9304
9305   gchar **np = map_names;
9306   VikMapsLayer **lp = map_layers;
9307   for (i = 0; i < num_maps; i++) {
9308     gboolean dup = FALSE;
9309     vml = (VikMapsLayer *)(vmls->data);
9310     for (j = 0; j < i; j++) { /* no duplicate allowed */
9311       if (vik_maps_layer_get_map_type(vml) == vik_maps_layer_get_map_type(map_layers[j])) {
9312         dup = TRUE;
9313         break;
9314       }
9315     }
9316     if (!dup) {
9317       *lp++ = vml;
9318       *np++ = vik_maps_layer_get_map_label(vml);
9319     }
9320     vmls = vmls->next;
9321   }
9322   *lp = NULL;
9323   *np = NULL;
9324   num_maps = lp - map_layers;
9325
9326   for (default_map = 0; default_map < num_maps; default_map++) {
9327     /* TODO: check for parent layer's visibility */
9328     if (VIK_LAYER(map_layers[default_map])->visible)
9329       break;
9330   }
9331   default_map = (default_map == num_maps) ? 0 : default_map;
9332
9333   gdouble cur_zoom = vik_viewport_get_zoom(vvp);
9334   for (default_zoom = 0; default_zoom < sizeof(zoom_vals)/sizeof(gdouble); default_zoom++) {
9335     if (cur_zoom == zoom_vals[default_zoom])
9336       break;
9337   }
9338   default_zoom = (default_zoom == sizeof(zoom_vals)/sizeof(gdouble)) ? sizeof(zoom_vals)/sizeof(gdouble) - 1 : default_zoom;
9339
9340   if (!a_dialog_map_n_zoom(VIK_GTK_WINDOW_FROM_LAYER(vtl), map_names, default_map, zoomlist, default_zoom, &selected_map, &selected_zoom))
9341     goto done;
9342
9343   vik_track_download_map(trk, map_layers[selected_map], vvp, zoom_vals[selected_zoom]);
9344
9345 done:
9346   for (i = 0; i < num_maps; i++)
9347     g_free(map_names[i]);
9348   g_free(map_names);
9349   g_free(map_layers);
9350
9351   g_list_free(vmls);
9352
9353 }
9354
9355 /**** lowest waypoint number calculation ***/
9356 static gint highest_wp_number_name_to_number(const gchar *name) {
9357   if ( strlen(name) == 3 ) {
9358     int n = atoi(name);
9359     if ( n < 100 && name[0] != '0' )
9360       return -1;
9361     if ( n < 10 && name[0] != '0' )
9362       return -1;
9363     return n;
9364   }
9365   return -1;
9366 }
9367
9368
9369 static void highest_wp_number_reset(VikTrwLayer *vtl)
9370 {
9371   vtl->highest_wp_number = -1;
9372 }
9373
9374 static void highest_wp_number_add_wp(VikTrwLayer *vtl, const gchar *new_wp_name)
9375 {
9376   /* if is bigger that top, add it */
9377   gint new_wp_num = highest_wp_number_name_to_number(new_wp_name);
9378   if ( new_wp_num > vtl->highest_wp_number )
9379     vtl->highest_wp_number = new_wp_num;
9380 }
9381
9382 static void highest_wp_number_remove_wp(VikTrwLayer *vtl, const gchar *old_wp_name)
9383 {
9384   /* if wasn't top, do nothing. if was top, count backwards until we find one used */
9385   gint old_wp_num = highest_wp_number_name_to_number(old_wp_name);
9386   if ( vtl->highest_wp_number == old_wp_num ) {
9387     gchar buf[4];
9388     vtl->highest_wp_number--;
9389
9390     g_snprintf(buf,4,"%03d", vtl->highest_wp_number );
9391     /* search down until we find something that *does* exist */
9392
9393     while ( vtl->highest_wp_number > 0 && ! vik_trw_layer_get_waypoint ( vtl, buf )) {
9394       vtl->highest_wp_number--;
9395       g_snprintf(buf,4,"%03d", vtl->highest_wp_number );
9396     }
9397   }
9398 }
9399
9400 /* get lowest unused number */
9401 static gchar *highest_wp_number_get(VikTrwLayer *vtl)
9402 {
9403   gchar buf[4];
9404   if ( vtl->highest_wp_number < 0 || vtl->highest_wp_number >= 999 )
9405     return NULL;
9406   g_snprintf(buf,4,"%03d", vtl->highest_wp_number+1 );
9407   return g_strdup(buf);
9408 }