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