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