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