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