]> git.street.me.uk Git - andy/viking.git/blob - src/viktrwlayer.c
Add method to uniquify track names in a TRW layer and give the option to use it withi...
[andy/viking.git] / src / viktrwlayer.c
1 /*
2  * viking -- GPS Data and Topo Analyzer, Explorer, and Manager
3  *
4  * Copyright (C) 2003-2007, Evan Battaglia <gtoevan@gmx.net>
5  * Copyright (C) 2005-2008, Alex Foobarian <foobarian@gmail.com>
6  * Copyright (C) 2007, Quy Tonthat <qtonthat@gmail.com>
7  * Copyright (C) 2009, Hein Ragas <viking@ragas.nl>
8  * Copyright (c) 2012, Rob Norris <rw_norris@hotmail.com>
9  *
10  * This program is free software; you can redistribute it and/or modify
11  * it under the terms of the GNU General Public License as published by
12  * the Free Software Foundation; either version 2 of the License, or
13  * (at your option) any later version.
14  *
15  * This program is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU General Public License for more details.
19  *
20  * You should have received a copy of the GNU General Public License
21  * along with this program; if not, write to the Free Software
22  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
23  *
24  */
25
26 #define WAYPOINT_FONT "Sans 8"
27
28 /* WARNING: If you go beyond this point, we are NOT responsible for any ill effects on your sanity */
29 /* viktrwlayer.c -- 5000+ lines can make a difference in the state of things */
30
31 #ifdef HAVE_CONFIG_H
32 #include "config.h"
33 #endif
34
35 #include "viking.h"
36 #include "vikmapslayer.h"
37 #include "viktrwlayer_tpwin.h"
38 #include "viktrwlayer_propwin.h"
39 #ifdef VIK_CONFIG_GEOTAG
40 #include "viktrwlayer_geotag.h"
41 #include "geotag_exif.h"
42 #endif
43 #include "garminsymbols.h"
44 #include "thumbnails.h"
45 #include "background.h"
46 #include "gpx.h"
47 #include "babel.h"
48 #include "dem.h"
49 #include "dems.h"
50 #include "geonamessearch.h"
51 #ifdef VIK_CONFIG_OPENSTREETMAP
52 #include "osm-traces.h"
53 #endif
54 #include "acquire.h"
55 #include "datasources.h"
56 #include "util.h"
57
58 #include "icons/icons.h"
59
60 #ifdef HAVE_MATH_H
61 #include <math.h>
62 #endif
63 #ifdef HAVE_STRING_H
64 #include <string.h>
65 #endif
66 #ifdef HAVE_STDLIB_H
67 #include <stdlib.h>
68 #endif
69 #include <stdio.h>
70 #include <ctype.h>
71
72 #include <gdk/gdkkeysyms.h>
73 #include <glib.h>
74 #include <glib/gstdio.h>
75 #include <glib/gi18n.h>
76
77 /* Relax some dependencies */
78 #if ! GLIB_CHECK_VERSION(2,12,0)
79 static gboolean return_true (gpointer a, gpointer b, gpointer c) { return TRUE; }
80 static g_hash_table_remove_all (GHashTable *ght) { g_hash_table_foreach_remove ( ght, (GHRFunc) return_true, FALSE ); }
81 #endif
82
83 #define GOOGLE_DIRECTIONS_STRING "maps.google.com/maps?q=from:%s,%s+to:%s,%s&output=kml"
84 #define VIK_TRW_LAYER_TRACK_GC 13
85 #define VIK_TRW_LAYER_TRACK_GC_RATES 10
86 #define VIK_TRW_LAYER_TRACK_GC_MIN 0
87 #define VIK_TRW_LAYER_TRACK_GC_MAX 11
88 #define VIK_TRW_LAYER_TRACK_GC_BLACK 12
89
90 #define DRAWMODE_BY_TRACK 0
91 #define DRAWMODE_BY_VELOCITY 1
92 #define DRAWMODE_ALL_BLACK 2
93
94 #define POINTS 1
95 #define LINES 2
96
97 /* this is how it knows when you click if you are clicking close to a trackpoint. */
98 #define TRACKPOINT_SIZE_APPROX 5
99 #define WAYPOINT_SIZE_APPROX 5
100
101 #define MIN_STOP_LENGTH 15
102 #define MAX_STOP_LENGTH 86400
103 #define DRAW_ELEVATION_FACTOR 30 /* height of elevation plotting, sort of relative to zoom level ("mpp" that isn't mpp necessarily) */
104                                  /* this is multiplied by user-inputted value from 1-100. */
105 enum {
106 VIK_TRW_LAYER_SUBLAYER_TRACKS,
107 VIK_TRW_LAYER_SUBLAYER_WAYPOINTS,
108 VIK_TRW_LAYER_SUBLAYER_TRACK,
109 VIK_TRW_LAYER_SUBLAYER_WAYPOINT
110 };
111
112 enum { WP_SYMBOL_FILLED_SQUARE, WP_SYMBOL_SQUARE, WP_SYMBOL_CIRCLE, WP_SYMBOL_X, WP_NUM_SYMBOLS };
113
114 struct _VikTrwLayer {
115   VikLayer vl;
116   GHashTable *tracks;
117   GHashTable *tracks_iters;
118   GHashTable *waypoints_iters;
119   GHashTable *waypoints;
120   GtkTreeIter waypoints_iter, tracks_iter;
121   gboolean tracks_visible, waypoints_visible;
122   guint8 drawmode;
123   guint8 drawpoints;
124   guint8 drawelevation;
125   guint8 elevation_factor;
126   guint8 drawstops;
127   guint32 stop_length;
128   guint8 drawlines;
129   guint8 line_thickness;
130   guint8 bg_line_thickness;
131
132   guint8 wp_symbol;
133   guint8 wp_size;
134   gboolean wp_draw_symbols;
135
136   gdouble velocity_min, velocity_max;
137   GArray *track_gc;
138   guint16 track_gc_iter;
139   GdkGC *current_track_gc;
140   GdkGC *track_bg_gc;
141   GdkGC *waypoint_gc;
142   GdkGC *waypoint_text_gc;
143   GdkGC *waypoint_bg_gc;
144   GdkFont *waypoint_font;
145   VikTrack *current_track;
146   guint16 ct_x1, ct_y1, ct_x2, ct_y2;
147   gboolean ct_sync_done;
148
149
150   VikCoordMode coord_mode;
151
152   /* wp editing tool */
153   VikWaypoint *current_wp;
154   gpointer current_wp_id;
155   gboolean moving_wp;
156   gboolean waypoint_rightclick;
157
158   /* track editing tool */
159   GList *current_tpl;
160   VikTrack *current_tp_track;
161   gpointer current_tp_id;
162   VikTrwLayerTpwin *tpwin;
163
164   /* weird hack for joining tracks */
165   GList *last_tpl;
166   VikTrack *last_tp_track;
167
168   /* track editing tool -- more specifically, moving tps */
169   gboolean moving_tp;
170
171   /* route finder tool */
172   gboolean route_finder_started;
173   VikCoord route_finder_coord;
174   gboolean route_finder_check_added_track;
175   VikTrack *route_finder_added_track;
176   VikTrack *route_finder_current_track;
177   gboolean route_finder_append;
178
179   gboolean drawlabels;
180   gboolean drawimages;
181   guint8 image_alpha;
182   GQueue *image_cache;
183   guint8 image_size;
184   guint16 image_cache_size;
185
186   /* for waypoint text */
187   PangoLayout *wplabellayout;
188
189   gboolean has_verified_thumbnails;
190
191   GtkMenu *wp_right_click_menu;
192   GtkMenu *track_right_click_menu;
193
194   /* menu */
195   VikStdLayerMenuItem menu_selection;
196
197   gint highest_wp_number;
198 };
199
200 /* A caached waypoint image. */
201 typedef struct {
202   GdkPixbuf *pixbuf;
203   gchar *image; /* filename */
204 } CachedPixbuf;
205
206 struct DrawingParams {
207   VikViewport *vp;
208   VikTrwLayer *vtl;
209   gdouble xmpp, ympp;
210   guint16 width, height;
211   const VikCoord *center;
212   gint track_gc_iter;
213   gboolean one_zone, lat_lon;
214   gdouble ce1, ce2, cn1, cn2;
215 };
216
217 static gboolean trw_layer_delete_waypoint ( VikTrwLayer *vtl, VikWaypoint *wp );
218
219 static void trw_layer_delete_item ( gpointer pass_along[6] );
220 static void trw_layer_copy_item_cb ( gpointer pass_along[6] );
221 static void trw_layer_cut_item_cb ( gpointer pass_along[6] );
222
223 static void trw_layer_find_maxmin_waypoints ( const gchar *name, const VikWaypoint *w, struct LatLon maxmin[2] );
224 static void trw_layer_find_maxmin_tracks ( const gchar *name, GList **t, struct LatLon maxmin[2] );     
225 static void trw_layer_find_maxmin (VikTrwLayer *vtl, struct LatLon maxmin[2]);
226
227 static void trw_layer_new_track_gcs ( VikTrwLayer *vtl, VikViewport *vp );
228 static void trw_layer_free_track_gcs ( VikTrwLayer *vtl );
229
230 static gint calculate_velocity ( VikTrwLayer *vtl, VikTrackpoint *tp1, VikTrackpoint *tp2 );
231 static void trw_layer_draw_track_cb ( const gchar *name, VikTrack *track, struct DrawingParams *dp );
232 static void trw_layer_draw_waypoint ( const gchar *name, VikWaypoint *wp, struct DrawingParams *dp );
233
234 static void goto_coord ( gpointer *vlp, gpointer vvp, gpointer vl, const VikCoord *coord );
235 static void trw_layer_goto_track_startpoint ( gpointer pass_along[6] );
236 static void trw_layer_goto_track_endpoint ( gpointer pass_along[6] );
237 static void trw_layer_goto_track_max_speed ( gpointer pass_along[6] );
238 static void trw_layer_goto_track_max_alt ( gpointer pass_along[6] );
239 static void trw_layer_goto_track_min_alt ( gpointer pass_along[6] );
240 static void trw_layer_goto_track_center ( gpointer pass_along[6] );
241 static void trw_layer_merge_by_timestamp ( gpointer pass_along[6] );
242 static void trw_layer_merge_with_other ( gpointer pass_along[6] );
243 static void trw_layer_split_by_timestamp ( gpointer pass_along[6] );
244 static void trw_layer_split_by_n_points ( gpointer pass_along[6] );
245 static void trw_layer_reverse ( gpointer pass_along[6] );
246 static void trw_layer_download_map_along_track_cb ( gpointer pass_along[6] );
247 static void trw_layer_edit_trackpoint ( gpointer pass_along[6] );
248 static void trw_layer_show_picture ( gpointer pass_along[6] );
249
250 static void trw_layer_centerize ( gpointer layer_and_vlp[2] );
251 static void trw_layer_auto_view ( gpointer layer_and_vlp[2] );
252 static void trw_layer_export ( gpointer layer_and_vlp[2], const gchar* title, const gchar* default_name, VikTrack* trk, guint file_type );
253 static void trw_layer_goto_wp ( gpointer layer_and_vlp[2] );
254 static void trw_layer_new_wp ( gpointer lav[2] );
255 static void trw_layer_auto_waypoints_view ( gpointer lav[2] );
256 static void trw_layer_auto_tracks_view ( gpointer lav[2] );
257 static void trw_layer_delete_all_tracks ( gpointer lav[2] );
258 static void trw_layer_delete_tracks_from_selection ( gpointer lav[2] );
259 static void trw_layer_delete_all_waypoints ( gpointer lav[2] );
260 static void trw_layer_delete_waypoints_from_selection ( gpointer lav[2] );
261 static void trw_layer_new_wikipedia_wp_viewport ( gpointer lav[2] );
262 static void trw_layer_new_wikipedia_wp_layer ( gpointer lav[2] );
263 #ifdef VIK_CONFIG_GEOTAG
264 static void trw_layer_geotagging_waypoint_mtime_keep ( gpointer pass_along[6] );
265 static void trw_layer_geotagging_waypoint_mtime_update ( gpointer pass_along[6] );
266 static void trw_layer_geotagging_track ( gpointer pass_along[6] );
267 static void trw_layer_geotagging ( gpointer lav[2] );
268 #endif
269 static void trw_layer_acquire_gps_cb ( gpointer lav[2] );
270 static void trw_layer_acquire_google_cb ( gpointer lav[2] );
271 #ifdef VIK_CONFIG_OPENSTREETMAP
272 static void trw_layer_acquire_osm_cb ( gpointer lav[2] );
273 #endif
274 #ifdef VIK_CONFIG_GEOCACHES
275 static void trw_layer_acquire_geocache_cb ( gpointer lav[2] );
276 #endif
277 #ifdef VIK_CONFIG_GEOTAG
278 static void trw_layer_acquire_geotagged_cb ( gpointer lav[2] );
279 #endif
280
281 /* pop-up items */
282 static void trw_layer_properties_item ( gpointer pass_along[7] );
283 static void trw_layer_goto_waypoint ( gpointer pass_along[6] );
284 static void trw_layer_waypoint_gc_webpage ( gpointer pass_along[6] );
285
286 static void trw_layer_realize_waypoint ( gpointer id, VikWaypoint *wp, gpointer pass_along[5] );
287 static void trw_layer_realize_track ( gpointer id, VikTrack *track, gpointer pass_along[5] );
288 static void init_drawing_params ( struct DrawingParams *dp, VikViewport *vp );
289
290 static void trw_layer_insert_tp_after_current_tp ( VikTrwLayer *vtl );
291 static void trw_layer_cancel_last_tp ( VikTrwLayer *vtl );
292 static void trw_layer_cancel_current_tp ( VikTrwLayer *vtl, gboolean destroy );
293 static void trw_layer_tpwin_response ( VikTrwLayer *vtl, gint response );
294 static void trw_layer_tpwin_init ( VikTrwLayer *vtl );
295
296 static gpointer tool_edit_trackpoint_create ( VikWindow *vw, VikViewport *vvp);
297 static gboolean tool_edit_trackpoint_click ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data );
298 static gboolean tool_edit_trackpoint_move ( VikTrwLayer *vtl, GdkEventMotion *event, gpointer data );
299 static gboolean tool_edit_trackpoint_release ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data );
300 static gpointer tool_show_picture_create ( VikWindow *vw, VikViewport *vvp);
301 static gboolean tool_show_picture_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp ); 
302 static gpointer tool_edit_waypoint_create ( VikWindow *vw, VikViewport *vvp);
303 static gboolean tool_edit_waypoint_click ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data );
304 static gboolean tool_edit_waypoint_move ( VikTrwLayer *vtl, GdkEventMotion *event, gpointer data );
305 static gboolean tool_edit_waypoint_release ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data );
306 static gpointer tool_begin_track_create ( VikWindow *vw, VikViewport *vvp);
307 static gboolean tool_begin_track_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp ); 
308 static gpointer tool_new_track_create ( VikWindow *vw, VikViewport *vvp);
309 static gboolean tool_new_track_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp ); 
310 static VikLayerToolFuncStatus tool_new_track_move ( VikTrwLayer *vtl, GdkEventMotion *event, VikViewport *vvp ); 
311 static gboolean tool_new_track_key_press ( VikTrwLayer *vtl, GdkEventKey *event, VikViewport *vvp ); 
312 static gpointer tool_new_waypoint_create ( VikWindow *vw, VikViewport *vvp);
313 static gboolean tool_new_waypoint_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp );
314 static gpointer tool_route_finder_create ( VikWindow *vw, VikViewport *vvp);
315 static gboolean tool_route_finder_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp );
316
317
318 static void cached_pixbuf_free ( CachedPixbuf *cp );
319 static gint cached_pixbuf_cmp ( CachedPixbuf *cp, const gchar *name );
320
321 static VikTrackpoint *closest_tp_in_five_pixel_interval ( VikTrwLayer *vtl, VikViewport *vvp, gint x, gint y );
322 static VikWaypoint *closest_wp_in_five_pixel_interval ( VikTrwLayer *vtl, VikViewport *vvp, gint x, gint y );
323
324 static gchar *get_new_unique_sublayer_name (VikTrwLayer *vtl, gint sublayer_type, const gchar *name);
325 static void waypoint_convert ( const gpointer id, VikWaypoint *wp, VikCoordMode *dest_mode );
326 static void track_convert ( const gchar *name, VikTrack *tr, VikCoordMode *dest_mode );
327
328 static gchar *highest_wp_number_get(VikTrwLayer *vtl);
329 static void highest_wp_number_reset(VikTrwLayer *vtl);
330 static void highest_wp_number_add_wp(VikTrwLayer *vtl, const gchar *new_wp_name);
331 static void highest_wp_number_remove_wp(VikTrwLayer *vtl, const gchar *old_wp_name);
332
333
334 static VikToolInterface trw_layer_tools[] = {
335   { N_("Create Waypoint"), (VikToolConstructorFunc) tool_new_waypoint_create,    NULL, NULL, NULL, 
336     (VikToolMouseFunc) tool_new_waypoint_click,    NULL, NULL, (VikToolKeyFunc) NULL, GDK_CURSOR_IS_PIXMAP, &cursor_addwp_pixbuf },
337
338   { N_("Create Track"),    (VikToolConstructorFunc) tool_new_track_create,       NULL, NULL, NULL, 
339     (VikToolMouseFunc) tool_new_track_click, (VikToolMouseMoveFunc) tool_new_track_move, NULL,
340     (VikToolKeyFunc) tool_new_track_key_press, GDK_CURSOR_IS_PIXMAP, &cursor_addtr_pixbuf },
341
342   { N_("Begin Track"),    (VikToolConstructorFunc) tool_begin_track_create,       NULL, NULL, NULL, 
343     (VikToolMouseFunc) tool_begin_track_click,       NULL, NULL, (VikToolKeyFunc) NULL, GDK_CURSOR_IS_PIXMAP, &cursor_begintr_pixbuf },
344
345   { N_("Edit Waypoint"),   (VikToolConstructorFunc) tool_edit_waypoint_create,   NULL, NULL, NULL, 
346     (VikToolMouseFunc) tool_edit_waypoint_click,   
347     (VikToolMouseMoveFunc) tool_edit_waypoint_move,
348     (VikToolMouseFunc) tool_edit_waypoint_release, (VikToolKeyFunc) NULL, GDK_CURSOR_IS_PIXMAP, &cursor_edwp_pixbuf },
349
350   { N_("Edit Trackpoint"), (VikToolConstructorFunc) tool_edit_trackpoint_create, NULL, NULL, NULL, 
351     (VikToolMouseFunc) tool_edit_trackpoint_click,
352     (VikToolMouseMoveFunc) tool_edit_trackpoint_move,
353     (VikToolMouseFunc) tool_edit_trackpoint_release, (VikToolKeyFunc) NULL, GDK_CURSOR_IS_PIXMAP, &cursor_edtr_pixbuf },
354
355   { N_("Show Picture"),    (VikToolConstructorFunc) tool_show_picture_create,    NULL, NULL, NULL, 
356     (VikToolMouseFunc) tool_show_picture_click,    NULL, NULL, (VikToolKeyFunc) NULL, GDK_CURSOR_IS_PIXMAP, &cursor_showpic_pixbuf },
357
358   { N_("Route Finder"),  (VikToolConstructorFunc) tool_route_finder_create,  NULL, NULL, NULL,
359     (VikToolMouseFunc) tool_route_finder_click, NULL, NULL, (VikToolKeyFunc) NULL, GDK_CURSOR_IS_PIXMAP, &cursor_route_finder_pixbuf },
360 };
361 enum { TOOL_CREATE_WAYPOINT=0, TOOL_CREATE_TRACK, TOOL_BEGIN_TRACK, TOOL_EDIT_WAYPOINT, TOOL_EDIT_TRACKPOINT, TOOL_SHOW_PICTURE, NUM_TOOLS };
362
363 /****** PARAMETERS ******/
364
365 static gchar *params_groups[] = { N_("Waypoints"), N_("Tracks"), N_("Waypoint Images") };
366 enum { GROUP_WAYPOINTS, GROUP_TRACKS, GROUP_IMAGES };
367
368 static gchar *params_drawmodes[] = { N_("Draw by Track"), N_("Draw by Velocity"), N_("All Tracks Black"), 0 };
369 static gchar *params_wpsymbols[] = { N_("Filled Square"), N_("Square"), N_("Circle"), N_("X"), 0 };
370
371
372 static VikLayerParamScale params_scales[] = {
373  /* min  max    step digits */
374  {  1,   10,    1,   0 }, /* line_thickness */
375  {  0.0, 99.0,  1,   2 }, /* velocity_min */
376  {  1.0, 100.0, 1.0, 2 }, /* velocity_max */
377                 /* 5 * step == how much to turn */
378  {  16,   128,  3.2, 0 }, /* image_size */
379  {   0,   255,  5,   0 }, /* image alpha */
380  {   5,   500,  5,   0 }, /* image cache_size */
381  {   0,   8,    1,   0 }, /* image cache_size */
382  {   1,  64,    1,   0 }, /* wpsize */
383  {   MIN_STOP_LENGTH, MAX_STOP_LENGTH, 1,   0 }, /* stop_length */
384  {   1, 100, 1,   0 }, /* stop_length */
385 };
386
387 VikLayerParam trw_layer_params[] = {
388   { "tracks_visible", VIK_LAYER_PARAM_BOOLEAN, VIK_LAYER_NOT_IN_PROPERTIES },
389   { "waypoints_visible", VIK_LAYER_PARAM_BOOLEAN, VIK_LAYER_NOT_IN_PROPERTIES },
390
391   { "drawmode", VIK_LAYER_PARAM_UINT, GROUP_TRACKS, N_("Track Drawing Mode:"), VIK_LAYER_WIDGET_RADIOGROUP, NULL },
392   { "drawlines", VIK_LAYER_PARAM_BOOLEAN, GROUP_TRACKS, N_("Draw Track Lines"), VIK_LAYER_WIDGET_CHECKBUTTON },
393   { "drawpoints", VIK_LAYER_PARAM_BOOLEAN, GROUP_TRACKS, N_("Draw Trackpoints"), VIK_LAYER_WIDGET_CHECKBUTTON },
394   { "drawelevation", VIK_LAYER_PARAM_BOOLEAN, GROUP_TRACKS, N_("Draw Elevation"), VIK_LAYER_WIDGET_CHECKBUTTON },
395   { "elevation_factor", VIK_LAYER_PARAM_UINT, GROUP_TRACKS, N_("Draw Elevation Height %:"), VIK_LAYER_WIDGET_HSCALE, params_scales + 9 },
396
397   { "drawstops", VIK_LAYER_PARAM_BOOLEAN, GROUP_TRACKS, N_("Draw Stops"), VIK_LAYER_WIDGET_CHECKBUTTON },
398   { "stop_length", VIK_LAYER_PARAM_UINT, GROUP_TRACKS, N_("Min Stop Length (seconds):"), VIK_LAYER_WIDGET_SPINBUTTON, params_scales + 8 },
399
400   { "line_thickness", VIK_LAYER_PARAM_UINT, GROUP_TRACKS, N_("Track Thickness:"), VIK_LAYER_WIDGET_SPINBUTTON, params_scales + 0 },
401   { "bg_line_thickness", VIK_LAYER_PARAM_UINT, GROUP_TRACKS, N_("Track BG Thickness:"), VIK_LAYER_WIDGET_SPINBUTTON, params_scales + 6 },
402   { "trackbgcolor", VIK_LAYER_PARAM_COLOR, GROUP_TRACKS, N_("Track Background Color"), VIK_LAYER_WIDGET_COLOR, 0 },
403   { "velocity_min", VIK_LAYER_PARAM_DOUBLE, GROUP_TRACKS, N_("Min Track Velocity:"), VIK_LAYER_WIDGET_SPINBUTTON, params_scales + 1 },
404   { "velocity_max", VIK_LAYER_PARAM_DOUBLE, GROUP_TRACKS, N_("Max Track Velocity:"), VIK_LAYER_WIDGET_SPINBUTTON, params_scales + 2 },
405
406   { "drawlabels", VIK_LAYER_PARAM_BOOLEAN, GROUP_WAYPOINTS, N_("Draw Labels"), VIK_LAYER_WIDGET_CHECKBUTTON },
407   { "wpcolor", VIK_LAYER_PARAM_COLOR, GROUP_WAYPOINTS, N_("Waypoint Color:"), VIK_LAYER_WIDGET_COLOR, 0 },
408   { "wptextcolor", VIK_LAYER_PARAM_COLOR, GROUP_WAYPOINTS, N_("Waypoint Text:"), VIK_LAYER_WIDGET_COLOR, 0 },
409   { "wpbgcolor", VIK_LAYER_PARAM_COLOR, GROUP_WAYPOINTS, N_("Background:"), VIK_LAYER_WIDGET_COLOR, 0 },
410   { "wpbgand", VIK_LAYER_PARAM_BOOLEAN, GROUP_WAYPOINTS, N_("Fake BG Color Translucency:"), VIK_LAYER_WIDGET_CHECKBUTTON, 0 },
411   { "wpsymbol", VIK_LAYER_PARAM_UINT, GROUP_WAYPOINTS, N_("Waypoint marker:"), VIK_LAYER_WIDGET_RADIOGROUP, NULL },
412   { "wpsize", VIK_LAYER_PARAM_UINT, GROUP_WAYPOINTS, N_("Waypoint size:"), VIK_LAYER_WIDGET_SPINBUTTON, params_scales + 7 },
413   { "wpsyms", VIK_LAYER_PARAM_BOOLEAN, GROUP_WAYPOINTS, N_("Draw Waypoint Symbols:"), VIK_LAYER_WIDGET_CHECKBUTTON },
414
415   { "drawimages", VIK_LAYER_PARAM_BOOLEAN, GROUP_IMAGES, N_("Draw Waypoint Images"), VIK_LAYER_WIDGET_CHECKBUTTON },
416   { "image_size", VIK_LAYER_PARAM_UINT, GROUP_IMAGES, N_("Image Size (pixels):"), VIK_LAYER_WIDGET_HSCALE, params_scales + 3 },
417   { "image_alpha", VIK_LAYER_PARAM_UINT, GROUP_IMAGES, N_("Image Alpha:"), VIK_LAYER_WIDGET_HSCALE, params_scales + 4 },
418   { "image_cache_size", VIK_LAYER_PARAM_UINT, GROUP_IMAGES, N_("Image Memory Cache Size:"), VIK_LAYER_WIDGET_HSCALE, params_scales + 5 },
419 };
420
421 enum { PARAM_TV, PARAM_WV, PARAM_DM, PARAM_DL, PARAM_DP, PARAM_DE, PARAM_EF, PARAM_DS, PARAM_SL, PARAM_LT, PARAM_BLT, PARAM_TBGC, PARAM_VMIN, PARAM_VMAX, PARAM_DLA, PARAM_WPC, PARAM_WPTC, PARAM_WPBC, PARAM_WPBA, PARAM_WPSYM, PARAM_WPSIZE, PARAM_WPSYMS, PARAM_DI, PARAM_IS, PARAM_IA, PARAM_ICS, NUM_PARAMS };
422
423 /*** TO ADD A PARAM:
424  *** 1) Add to trw_layer_params and enumeration
425  *** 2) Handle in get_param & set_param (presumably adding on to VikTrwLayer struct)
426  ***/
427
428 /****** END PARAMETERS ******/
429
430 static VikTrwLayer* trw_layer_new ( gint drawmode );
431 /* Layer Interface function definitions */
432 static VikTrwLayer* trw_layer_create ( VikViewport *vp );
433 static void trw_layer_realize ( VikTrwLayer *vtl, VikTreeview *vt, GtkTreeIter *layer_iter );
434 static void trw_layer_free ( VikTrwLayer *trwlayer );
435 static void trw_layer_draw ( VikTrwLayer *l, gpointer data );
436 static void trw_layer_change_coord_mode ( VikTrwLayer *vtl, VikCoordMode dest_mode );
437 static void trw_layer_set_menu_selection ( VikTrwLayer *vtl, guint16 );
438 static guint16 trw_layer_get_menu_selection ( VikTrwLayer *vtl );
439 static void trw_layer_add_menu_items ( VikTrwLayer *vtl, GtkMenu *menu, gpointer vlp );
440 static gboolean trw_layer_sublayer_add_menu_items ( VikTrwLayer *l, GtkMenu *menu, gpointer vlp, gint subtype, gpointer sublayer, GtkTreeIter *iter, VikViewport *vvp );
441 static const gchar* trw_layer_sublayer_rename_request ( VikTrwLayer *l, const gchar *newname, gpointer vlp, gint subtype, gpointer sublayer, GtkTreeIter *iter );
442 static gboolean trw_layer_sublayer_toggle_visible ( VikTrwLayer *l, gint subtype, gpointer sublayer );
443 static const gchar* trw_layer_layer_tooltip ( VikTrwLayer *vtl );
444 static const gchar* trw_layer_sublayer_tooltip ( VikTrwLayer *l, gint subtype, gpointer sublayer );
445 static gboolean trw_layer_selected ( VikTrwLayer *l, gint subtype, gpointer sublayer, gint type, gpointer vlp );
446 static void trw_layer_marshall ( VikTrwLayer *vtl, guint8 **data, gint *len );
447 static VikTrwLayer *trw_layer_unmarshall ( guint8 *data, gint len, VikViewport *vvp );
448 static gboolean trw_layer_set_param ( VikTrwLayer *vtl, guint16 id, VikLayerParamData data, VikViewport *vp, gboolean is_file_operation );
449 static VikLayerParamData trw_layer_get_param ( VikTrwLayer *vtl, guint16 id, gboolean is_file_operation );
450 static void trw_layer_del_item ( VikTrwLayer *vtl, gint subtype, gpointer sublayer );
451 static void trw_layer_cut_item ( VikTrwLayer *vtl, gint subtype, gpointer sublayer );
452 static void trw_layer_copy_item ( VikTrwLayer *vtl, gint subtype, gpointer sublayer, guint8 **item, guint *len );
453 static gboolean trw_layer_paste_item ( VikTrwLayer *vtl, gint subtype, guint8 *item, guint len );
454 static void trw_layer_free_copied_item ( gint subtype, gpointer item );
455 static void trw_layer_drag_drop_request ( VikTrwLayer *vtl_src, VikTrwLayer *vtl_dest, GtkTreeIter *src_item_iter, GtkTreePath *dest_path );
456 static gboolean trw_layer_select_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp, tool_ed_t *t );
457 static gboolean trw_layer_select_move ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp, tool_ed_t *t );
458 static gboolean trw_layer_select_release ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp, tool_ed_t *t );
459 static gboolean trw_layer_show_selected_viewport_menu ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp );
460 /* End Layer Interface function definitions */
461
462 VikLayerInterface vik_trw_layer_interface = {
463   "TrackWaypoint",
464   &viktrwlayer_pixbuf,
465
466   trw_layer_tools,
467   sizeof(trw_layer_tools) / sizeof(VikToolInterface),
468
469   trw_layer_params,
470   NUM_PARAMS,
471   params_groups, /* params_groups */
472   sizeof(params_groups)/sizeof(params_groups[0]),    /* number of groups */
473
474   VIK_MENU_ITEM_ALL,
475
476   (VikLayerFuncCreate)                  trw_layer_create,
477   (VikLayerFuncRealize)                 trw_layer_realize,
478   (VikLayerFuncPostRead)                trw_layer_verify_thumbnails,
479   (VikLayerFuncFree)                    trw_layer_free,
480
481   (VikLayerFuncProperties)              NULL,
482   (VikLayerFuncDraw)                    trw_layer_draw,
483   (VikLayerFuncChangeCoordMode)         trw_layer_change_coord_mode,
484
485   (VikLayerFuncSetMenuItemsSelection)   trw_layer_set_menu_selection,
486   (VikLayerFuncGetMenuItemsSelection)   trw_layer_get_menu_selection,
487
488   (VikLayerFuncAddMenuItems)            trw_layer_add_menu_items,
489   (VikLayerFuncSublayerAddMenuItems)    trw_layer_sublayer_add_menu_items,
490
491   (VikLayerFuncSublayerRenameRequest)   trw_layer_sublayer_rename_request,
492   (VikLayerFuncSublayerToggleVisible)   trw_layer_sublayer_toggle_visible,
493   (VikLayerFuncSublayerTooltip)         trw_layer_sublayer_tooltip,
494   (VikLayerFuncLayerTooltip)            trw_layer_layer_tooltip,
495   (VikLayerFuncLayerSelected)           trw_layer_selected,
496
497   (VikLayerFuncMarshall)                trw_layer_marshall,
498   (VikLayerFuncUnmarshall)              trw_layer_unmarshall,
499
500   (VikLayerFuncSetParam)                trw_layer_set_param,
501   (VikLayerFuncGetParam)                trw_layer_get_param,
502
503   (VikLayerFuncReadFileData)            a_gpspoint_read_file,
504   (VikLayerFuncWriteFileData)           a_gpspoint_write_file,
505
506   (VikLayerFuncDeleteItem)              trw_layer_del_item,
507   (VikLayerFuncCutItem)                 trw_layer_cut_item,
508   (VikLayerFuncCopyItem)                trw_layer_copy_item,
509   (VikLayerFuncPasteItem)               trw_layer_paste_item,
510   (VikLayerFuncFreeCopiedItem)          trw_layer_free_copied_item,
511   
512   (VikLayerFuncDragDropRequest)         trw_layer_drag_drop_request,
513
514   (VikLayerFuncSelectClick)             trw_layer_select_click,
515   (VikLayerFuncSelectMove)              trw_layer_select_move,
516   (VikLayerFuncSelectRelease)           trw_layer_select_release,
517   (VikLayerFuncSelectedViewportMenu)    trw_layer_show_selected_viewport_menu,
518 };
519
520 // for copy & paste
521 typedef struct {
522   guint len;
523   guint8 data[0];
524 } FlatItem;
525
526 GType vik_trw_layer_get_type ()
527 {
528   static GType vtl_type = 0;
529
530   if (!vtl_type)
531   {
532     static const GTypeInfo vtl_info =
533     {
534       sizeof (VikTrwLayerClass),
535       NULL, /* base_init */
536       NULL, /* base_finalize */
537       NULL, /* class init */
538       NULL, /* class_finalize */
539       NULL, /* class_data */
540       sizeof (VikTrwLayer),
541       0,
542       NULL /* instance init */
543     };
544     vtl_type = g_type_register_static ( VIK_LAYER_TYPE, "VikTrwLayer", &vtl_info, 0 );
545   }
546
547   return vtl_type;
548 }
549
550 static void trw_layer_del_item ( VikTrwLayer *vtl, gint subtype, gpointer sublayer )
551 {
552   static gpointer pass_along[6];
553   if (!sublayer) {
554     return;
555   }
556   
557   pass_along[0] = vtl;
558   pass_along[1] = NULL;
559   pass_along[2] = GINT_TO_POINTER (subtype);
560   pass_along[3] = sublayer;
561   pass_along[4] = GINT_TO_POINTER (1); // Confirm delete request
562   pass_along[5] = NULL;
563
564   trw_layer_delete_item ( pass_along );
565 }
566
567 static void trw_layer_cut_item ( VikTrwLayer *vtl, gint subtype, gpointer sublayer )
568 {
569   static gpointer pass_along[6];
570   if (!sublayer) {
571     return;
572   }
573
574   pass_along[0] = vtl;
575   pass_along[1] = NULL;
576   pass_along[2] = GINT_TO_POINTER (subtype);
577   pass_along[3] = sublayer;
578   pass_along[4] = GINT_TO_POINTER (0); // No delete confirmation needed for auto delete
579   pass_along[5] = NULL;
580
581   trw_layer_copy_item_cb(pass_along);
582   trw_layer_cut_item_cb(pass_along);
583 }
584
585 static void trw_layer_copy_item_cb ( gpointer pass_along[6])
586 {
587   VikTrwLayer *vtl = VIK_TRW_LAYER(pass_along[0]);
588   gint subtype = GPOINTER_TO_INT (pass_along[2]);
589   gpointer * sublayer = pass_along[3];
590   guint8 *data = NULL;
591   guint len;
592
593   trw_layer_copy_item( vtl, subtype, sublayer, &data, &len);
594
595   if (data) {
596     const gchar* name;
597     if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT ) {
598       VikWaypoint *wp = g_hash_table_lookup ( vtl->waypoints, sublayer);
599       if ( wp && wp->name )
600         name = wp->name;
601       else
602         name = NULL; // Broken :(
603     }
604     else {
605       VikTrack *trk = g_hash_table_lookup ( vtl->tracks, sublayer);
606       if ( trk && trk->name )
607         name = trk->name;
608       else
609         name = NULL; // Broken :(
610     }
611
612     a_clipboard_copy( VIK_CLIPBOARD_DATA_SUBLAYER, VIK_LAYER_TRW,
613                       subtype, len, name, data);
614   }
615 }
616
617 static void trw_layer_cut_item_cb ( gpointer pass_along[6])
618 {
619   trw_layer_copy_item_cb(pass_along);
620   pass_along[4] = GINT_TO_POINTER (0); // Never need to confirm automatic delete
621   trw_layer_delete_item(pass_along);
622 }
623
624 static void trw_layer_copy_item ( VikTrwLayer *vtl, gint subtype, gpointer sublayer, guint8 **item, guint *len )
625 {
626   FlatItem *fi;
627   guint8 *id;
628   guint il;
629
630   if (!sublayer) {
631     *item = NULL;
632     return;
633   }
634
635   if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT )
636   {
637     vik_waypoint_marshall ( g_hash_table_lookup ( vtl->waypoints, sublayer ), &id, &il );
638     // 'Simple' memory copy of byte array from the marshalling above
639     *len = sizeof(FlatItem) + 1 + il; // not sure what the 1 is for yet...
640     fi = g_malloc ( *len );
641     fi->len = *len;
642     memcpy(fi->data, id, il);
643   } else {
644
645     vik_track_marshall ( g_hash_table_lookup ( vtl->tracks, sublayer ), &id, &il );
646     // less magic than before...
647     *len = sizeof(FlatItem) + 1 + il;
648     fi = g_malloc ( *len );
649     fi->len = *len;
650     memcpy(fi->data, id, il);
651   }
652
653   g_free(id);
654   *item = (guint8 *)fi;
655 }
656
657 static gboolean trw_layer_paste_item ( VikTrwLayer *vtl, gint subtype, guint8 *item, guint len )
658 {
659   FlatItem *fi = (FlatItem *) item;
660
661   if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT && fi )
662   {
663     VikWaypoint *w;
664     gchar *name;
665
666     w = vik_waypoint_unmarshall(fi->data, fi->len);
667     // When copying - we'll create a new name based on the original
668     name = get_new_unique_sublayer_name(vtl, VIK_TRW_LAYER_SUBLAYER_WAYPOINT, w->name);
669     vik_trw_layer_add_waypoint ( vtl, name, w );
670     waypoint_convert (NULL, w, &vtl->coord_mode);
671
672     // Consider if redraw necessary for the new item
673     if ( vtl->vl.visible && vtl->waypoints_visible && w->visible )
674       vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
675     return TRUE;
676   }
677   if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK && fi )
678   {
679     VikTrack *t;
680     gchar *name;
681
682     t = vik_track_unmarshall(fi->data, fi->len);
683     // When copying - we'll create a new name based on the original
684     name = get_new_unique_sublayer_name(vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, t->name);
685     vik_trw_layer_add_track ( vtl, name, t );
686     track_convert (name, t, &vtl->coord_mode);
687
688     // Consider if redraw necessary for the new item
689     if ( vtl->vl.visible && vtl->tracks_visible && t->visible )
690       vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
691     return TRUE;
692   }
693   return FALSE;
694 }
695
696 static void trw_layer_free_copied_item ( gint subtype, gpointer item )
697 {
698   if (item) {
699     g_free(item);
700   }
701 }
702
703 static gboolean trw_layer_set_param ( VikTrwLayer *vtl, guint16 id, VikLayerParamData data, VikViewport *vp, gboolean is_file_operation )
704 {
705   switch ( id )
706   {
707     case PARAM_TV: vtl->tracks_visible = data.b; break;
708     case PARAM_WV: vtl->waypoints_visible = data.b; break;
709     case PARAM_DM: vtl->drawmode = data.u; break;
710     case PARAM_DP: vtl->drawpoints = data.b; break;
711     case PARAM_DE: vtl->drawelevation = data.b; break;
712     case PARAM_DS: vtl->drawstops = data.b; break;
713     case PARAM_DL: vtl->drawlines = data.b; break;
714     case PARAM_SL: if ( data.u >= MIN_STOP_LENGTH && data.u <= MAX_STOP_LENGTH )
715                      vtl->stop_length = data.u;
716                    break;
717     case PARAM_EF: if ( data.u >= 1 && data.u <= 100 )
718                      vtl->elevation_factor = data.u;
719                    break;
720     case PARAM_LT: if ( data.u > 0 && data.u < 15 && data.u != vtl->line_thickness )
721                    {
722                      vtl->line_thickness = data.u;
723                      trw_layer_new_track_gcs ( vtl, vp );
724                    }
725                    break;
726     case PARAM_BLT: if ( data.u >= 0 && data.u <= 8 && data.u != vtl->bg_line_thickness )
727                    {
728                      vtl->bg_line_thickness = data.u;
729                      trw_layer_new_track_gcs ( vtl, vp );
730                    }
731                    break;
732     case PARAM_VMIN:
733     {
734       /* Convert to store internally
735          NB file operation always in internal units (metres per second) */
736       vik_units_speed_t speed_units = a_vik_get_units_speed ();
737       if ( is_file_operation || speed_units == VIK_UNITS_SPEED_METRES_PER_SECOND )
738         vtl->velocity_min = data.d;
739       else if ( speed_units == VIK_UNITS_SPEED_KILOMETRES_PER_HOUR )
740         vtl->velocity_min = VIK_KPH_TO_MPS(data.d);
741       else if ( speed_units == VIK_UNITS_SPEED_MILES_PER_HOUR )
742         vtl->velocity_min = VIK_MPH_TO_MPS(data.d);
743       else
744         /* Knots */
745         vtl->velocity_min = VIK_KNOTS_TO_MPS(data.d);
746       break;
747     }
748     case PARAM_VMAX:
749     {
750       /* Convert to store internally
751          NB file operation always in internal units (metres per second) */
752       vik_units_speed_t speed_units = a_vik_get_units_speed ();
753       if ( is_file_operation || speed_units == VIK_UNITS_SPEED_METRES_PER_SECOND )
754         vtl->velocity_max = data.d;
755       else if ( speed_units == VIK_UNITS_SPEED_KILOMETRES_PER_HOUR )
756         vtl->velocity_max = VIK_KPH_TO_MPS(data.d);
757       else if ( speed_units == VIK_UNITS_SPEED_MILES_PER_HOUR )
758         vtl->velocity_max = VIK_MPH_TO_MPS(data.d);
759       else
760         /* Knots */
761         vtl->velocity_max = VIK_KNOTS_TO_MPS(data.d);
762       break;
763     }
764     case PARAM_TBGC: gdk_gc_set_rgb_fg_color(vtl->track_bg_gc, &(data.c)); break;
765     case PARAM_DLA: vtl->drawlabels = data.b; break;
766     case PARAM_DI: vtl->drawimages = data.b; break;
767     case PARAM_IS: if ( data.u != vtl->image_size )
768       {
769         vtl->image_size = data.u;
770         g_list_foreach ( vtl->image_cache->head, (GFunc) cached_pixbuf_free, NULL );
771         g_queue_free ( vtl->image_cache );
772         vtl->image_cache = g_queue_new ();
773       }
774       break;
775     case PARAM_IA: vtl->image_alpha = data.u; break;
776     case PARAM_ICS: vtl->image_cache_size = data.u;
777       while ( vtl->image_cache->length > vtl->image_cache_size ) /* if shrinking cache_size, free pixbuf ASAP */
778           cached_pixbuf_free ( g_queue_pop_tail ( vtl->image_cache ) );
779       break;
780     case PARAM_WPC: gdk_gc_set_rgb_fg_color(vtl->waypoint_gc, &(data.c)); break;
781     case PARAM_WPTC: gdk_gc_set_rgb_fg_color(vtl->waypoint_text_gc, &(data.c)); break;
782     case PARAM_WPBC: gdk_gc_set_rgb_fg_color(vtl->waypoint_bg_gc, &(data.c)); break;
783     case PARAM_WPBA: gdk_gc_set_function(vtl->waypoint_bg_gc, data.b ? GDK_AND : GDK_COPY ); break;
784     case PARAM_WPSYM: if ( data.u < WP_NUM_SYMBOLS ) vtl->wp_symbol = data.u; break;
785     case PARAM_WPSIZE: if ( data.u > 0 && data.u <= 64 ) vtl->wp_size = data.u; break;
786     case PARAM_WPSYMS: vtl->wp_draw_symbols = data.b; break;
787   }
788   return TRUE;
789 }
790
791 static VikLayerParamData trw_layer_get_param ( VikTrwLayer *vtl, guint16 id, gboolean is_file_operation )
792 {
793   VikLayerParamData rv;
794   switch ( id )
795   {
796     case PARAM_TV: rv.b = vtl->tracks_visible; break;
797     case PARAM_WV: rv.b = vtl->waypoints_visible; break;
798     case PARAM_DM: rv.u = vtl->drawmode; break;
799     case PARAM_DP: rv.b = vtl->drawpoints; break;
800     case PARAM_DE: rv.b = vtl->drawelevation; break;
801     case PARAM_EF: rv.u = vtl->elevation_factor; break;
802     case PARAM_DS: rv.b = vtl->drawstops; break;
803     case PARAM_SL: rv.u = vtl->stop_length; break;
804     case PARAM_DL: rv.b = vtl->drawlines; break;
805     case PARAM_LT: rv.u = vtl->line_thickness; break;
806     case PARAM_BLT: rv.u = vtl->bg_line_thickness; break;
807     case PARAM_VMIN:
808     {
809       /* Convert to store internally
810          NB file operation always in internal units (metres per second) */
811       vik_units_speed_t speed_units = a_vik_get_units_speed ();
812       if ( is_file_operation || speed_units == VIK_UNITS_SPEED_METRES_PER_SECOND )
813         rv.d = vtl->velocity_min;
814       else if ( speed_units == VIK_UNITS_SPEED_KILOMETRES_PER_HOUR )
815         rv.d = VIK_MPS_TO_KPH(vtl->velocity_min);
816       else if ( speed_units == VIK_UNITS_SPEED_MILES_PER_HOUR )
817         rv.d = VIK_MPS_TO_MPH(vtl->velocity_min);
818       else
819         /* Knots */
820         rv.d = VIK_MPS_TO_KNOTS(vtl->velocity_min);
821       break;
822     }
823     case PARAM_VMAX:
824     {
825       /* Convert to store internally
826          NB file operation always in internal units (metres per second) */
827       vik_units_speed_t speed_units = a_vik_get_units_speed ();
828       if ( is_file_operation || speed_units == VIK_UNITS_SPEED_METRES_PER_SECOND )
829         rv.d = vtl->velocity_max;
830       else if ( speed_units == VIK_UNITS_SPEED_KILOMETRES_PER_HOUR )
831         rv.d = VIK_MPS_TO_KPH(vtl->velocity_max);
832       else if ( speed_units == VIK_UNITS_SPEED_MILES_PER_HOUR )
833         rv.d = VIK_MPS_TO_MPH(vtl->velocity_max);
834       else
835         /* Knots */
836         rv.d = VIK_MPS_TO_KNOTS(vtl->velocity_max);
837       break;
838     }
839     case PARAM_DLA: rv.b = vtl->drawlabels; break;
840     case PARAM_DI: rv.b = vtl->drawimages; break;
841     case PARAM_TBGC: vik_gc_get_fg_color(vtl->track_bg_gc, &(rv.c)); break;
842     case PARAM_IS: rv.u = vtl->image_size; break;
843     case PARAM_IA: rv.u = vtl->image_alpha; break;
844     case PARAM_ICS: rv.u = vtl->image_cache_size; break;
845     case PARAM_WPC: vik_gc_get_fg_color(vtl->waypoint_gc, &(rv.c)); break;
846     case PARAM_WPTC: vik_gc_get_fg_color(vtl->waypoint_text_gc, &(rv.c)); break;
847     case PARAM_WPBC: vik_gc_get_fg_color(vtl->waypoint_bg_gc, &(rv.c)); break;
848     case PARAM_WPBA: rv.b = (vik_gc_get_function(vtl->waypoint_bg_gc)==GDK_AND); break;
849     case PARAM_WPSYM: rv.u = vtl->wp_symbol; break;
850     case PARAM_WPSIZE: rv.u = vtl->wp_size; break;
851     case PARAM_WPSYMS: rv.b = vtl->wp_draw_symbols; break;
852   }
853   return rv;
854 }
855
856 static void trw_layer_marshall( VikTrwLayer *vtl, guint8 **data, gint *len )
857 {
858   guint8 *pd;
859   gchar *dd;
860   gsize dl;
861   gint pl;
862   gchar *tmpname;
863   FILE *f;
864
865   *data = NULL;
866
867   if ((f = fdopen(g_file_open_tmp (NULL, &tmpname, NULL), "r+"))) {
868     a_gpx_write_file(vtl, f);
869     vik_layer_marshall_params(VIK_LAYER(vtl), &pd, &pl);
870     fclose(f);
871     f = NULL;
872     g_file_get_contents(tmpname, &dd, &dl, NULL);
873     *len = sizeof(pl) + pl + dl;
874     *data = g_malloc(*len);
875     memcpy(*data, &pl, sizeof(pl));
876     memcpy(*data + sizeof(pl), pd, pl);
877     memcpy(*data + sizeof(pl) + pl, dd, dl);
878     
879     g_free(pd);
880     g_free(dd);
881     g_remove(tmpname);
882     g_free(tmpname);
883   }
884 }
885
886 static VikTrwLayer *trw_layer_unmarshall( guint8 *data, gint len, VikViewport *vvp )
887 {
888   VikTrwLayer *rv = VIK_TRW_LAYER(vik_layer_create ( VIK_LAYER_TRW, vvp, NULL, FALSE ));
889   gint pl;
890   gchar *tmpname;
891   FILE *f;
892
893
894   memcpy(&pl, data, sizeof(pl));
895   data += sizeof(pl);
896   vik_layer_unmarshall_params ( VIK_LAYER(rv), data, pl, vvp );
897   data += pl;
898
899   if (!(f = fdopen(g_file_open_tmp (NULL, &tmpname, NULL), "r+"))) {
900     g_critical("couldn't open temp file");
901     exit(1);
902   }
903   fwrite(data, len - pl - sizeof(pl), 1, f);
904   rewind(f);
905   a_gpx_read_file(rv, f);
906   fclose(f);
907   f = NULL;
908   g_remove(tmpname);
909   g_free(tmpname);
910   return rv;
911 }
912
913 static GList * str_array_to_glist(gchar* data[])
914 {
915   GList *gl = NULL;
916   gpointer * p;
917   for (p = (gpointer)data; *p; p++)
918     gl = g_list_prepend(gl, *p);
919   return(g_list_reverse(gl));
920 }
921
922 // Keep interesting hash function at least visible
923 /*
924 static guint strcase_hash(gconstpointer v)
925 {
926   // 31 bit hash function
927   int i;
928   const gchar *t = v;
929   gchar s[128];   // malloc is too slow for reading big files
930   gchar *p = s;
931
932   for (i = 0; (i < (sizeof(s)- 1)) && t[i]; i++)
933       p[i] = toupper(t[i]);
934   p[i] = '\0';
935
936   p = s;
937   guint32 h = *p;
938   if (h) {
939     for (p += 1; *p != '\0'; p++)
940       h = (h << 5) - h + *p;
941   }
942
943   return h;  
944 }
945 */
946
947 static VikTrwLayer* trw_layer_new ( gint drawmode )
948 {
949   if (trw_layer_params[PARAM_DM].widget_data == NULL)
950     trw_layer_params[PARAM_DM].widget_data = str_array_to_glist(params_drawmodes);
951   if (trw_layer_params[PARAM_WPSYM].widget_data == NULL)
952     trw_layer_params[PARAM_WPSYM].widget_data = str_array_to_glist(params_wpsymbols);
953
954   VikTrwLayer *rv = VIK_TRW_LAYER ( g_object_new ( VIK_TRW_LAYER_TYPE, NULL ) );
955   vik_layer_init ( VIK_LAYER(rv), VIK_LAYER_TRW );
956
957   // It's not entirely clear the benefits of hash tables usage here - possibly the simplicity of first implementation for unique names
958   // Now with the name of the item stored as part of the item - these tables are effectively straightforward lists
959
960   // For this reworking I've choosen to keep the use of hash tables since for the expected data sizes
961   // - even many hundreds of waypoints and tracks is quite small in the grand scheme of things,
962   //  and with normal PC processing capabilities - it has negligibile performance impact
963   // This also minimized the amount of rework - as the management of the hash tables already exists.
964
965   // The hash tables are indexed by simple integers acting as a UUID hash, which again shouldn't affect performance much
966   //   we have to maintain a uniqueness (as before when multiple names where not allowed),
967   //   this is to ensure it refers to the same item in the data structures used on the viewport and on the layers panel
968
969   rv->waypoints = g_hash_table_new_full ( g_direct_hash, g_direct_equal, NULL, (GDestroyNotify) vik_waypoint_free );
970   rv->waypoints_iters = g_hash_table_new_full ( g_direct_hash, g_direct_equal, NULL, g_free );
971   rv->tracks = g_hash_table_new_full ( g_direct_hash, g_direct_equal, NULL, (GDestroyNotify) vik_track_free );
972   rv->tracks_iters = g_hash_table_new_full ( g_direct_hash, g_direct_equal, NULL, g_free );
973
974   /* TODO: constants at top */
975   rv->waypoints_visible = rv->tracks_visible = TRUE;
976   rv->drawmode = drawmode;
977   rv->drawpoints = TRUE;
978   rv->drawstops = FALSE;
979   rv->drawelevation = FALSE;
980   rv->elevation_factor = 30;
981   rv->stop_length = 60;
982   rv->drawlines = TRUE;
983   rv->wplabellayout = NULL;
984   rv->wp_right_click_menu = NULL;
985   rv->track_right_click_menu = NULL;
986   rv->waypoint_gc = NULL;
987   rv->waypoint_text_gc = NULL;
988   rv->waypoint_bg_gc = NULL;
989   rv->track_gc = NULL;
990   rv->velocity_max = 5.0;
991   rv->velocity_min = 0.0;
992   rv->line_thickness = 1;
993   rv->bg_line_thickness = 0;
994   rv->current_wp = NULL;
995   rv->current_wp_id = NULL;
996   rv->current_track = NULL;
997   rv->current_tpl = NULL;
998   rv->current_tp_track = NULL;
999   rv->current_tp_id = NULL;
1000   rv->moving_tp = FALSE;
1001   rv->moving_wp = FALSE;
1002
1003   rv->ct_sync_done = TRUE;
1004
1005   rv->route_finder_started = FALSE;
1006   rv->route_finder_check_added_track = FALSE;
1007   rv->route_finder_current_track = NULL;
1008   rv->route_finder_append = FALSE;
1009
1010   rv->waypoint_rightclick = FALSE;
1011   rv->last_tpl = NULL;
1012   rv->last_tp_track = NULL;
1013   rv->tpwin = NULL;
1014   rv->image_cache = g_queue_new();
1015   rv->image_size = 64;
1016   rv->image_alpha = 255;
1017   rv->image_cache_size = 300;
1018   rv->drawimages = TRUE;
1019   rv->drawlabels = TRUE;
1020   return rv;
1021 }
1022
1023
1024 static void trw_layer_free ( VikTrwLayer *trwlayer )
1025 {
1026   g_hash_table_destroy(trwlayer->waypoints);
1027   g_hash_table_destroy(trwlayer->tracks);
1028
1029   /* ODC: replace with GArray */
1030   trw_layer_free_track_gcs ( trwlayer );
1031
1032   if ( trwlayer->wp_right_click_menu )
1033     g_object_ref_sink ( G_OBJECT(trwlayer->wp_right_click_menu) );
1034
1035   if ( trwlayer->track_right_click_menu )
1036     gtk_object_sink ( GTK_OBJECT(trwlayer->track_right_click_menu) );
1037
1038   if ( trwlayer->wplabellayout != NULL)
1039     g_object_unref ( G_OBJECT ( trwlayer->wplabellayout ) );
1040
1041   if ( trwlayer->waypoint_gc != NULL )
1042     g_object_unref ( G_OBJECT ( trwlayer->waypoint_gc ) );
1043
1044   if ( trwlayer->waypoint_text_gc != NULL )
1045     g_object_unref ( G_OBJECT ( trwlayer->waypoint_text_gc ) );
1046
1047   if ( trwlayer->waypoint_bg_gc != NULL )
1048     g_object_unref ( G_OBJECT ( trwlayer->waypoint_bg_gc ) );
1049
1050   if ( trwlayer->waypoint_font != NULL )
1051     gdk_font_unref ( trwlayer->waypoint_font );
1052
1053   if ( trwlayer->tpwin != NULL )
1054     gtk_widget_destroy ( GTK_WIDGET(trwlayer->tpwin) );
1055
1056   g_list_foreach ( trwlayer->image_cache->head, (GFunc) cached_pixbuf_free, NULL );
1057   g_queue_free ( trwlayer->image_cache );
1058 }
1059
1060 static void init_drawing_params ( struct DrawingParams *dp, VikViewport *vp )
1061 {
1062   dp->vp = vp;
1063   dp->xmpp = vik_viewport_get_xmpp ( vp );
1064   dp->ympp = vik_viewport_get_ympp ( vp );
1065   dp->width = vik_viewport_get_width ( vp );
1066   dp->height = vik_viewport_get_height ( vp );
1067   dp->center = vik_viewport_get_center ( vp );
1068   dp->one_zone = vik_viewport_is_one_zone ( vp ); /* false if some other projection besides UTM */
1069   dp->lat_lon = vik_viewport_get_coord_mode ( vp ) == VIK_COORD_LATLON;
1070
1071   if ( dp->one_zone )
1072   {
1073     gint w2, h2;
1074     w2 = dp->xmpp * (dp->width / 2) + 1600 / dp->xmpp; 
1075     h2 = dp->ympp * (dp->height / 2) + 1600 / dp->ympp;
1076     /* leniency -- for tracks. Obviously for waypoints this SHOULD be a lot smaller */
1077  
1078     dp->ce1 = dp->center->east_west-w2; 
1079     dp->ce2 = dp->center->east_west+w2;
1080     dp->cn1 = dp->center->north_south-h2;
1081     dp->cn2 = dp->center->north_south+h2;
1082   } else if ( dp->lat_lon ) {
1083     VikCoord upperleft, bottomright;
1084     /* quick & dirty calculation; really want to check all corners due to lat/lon smaller at top in northern hemisphere */
1085     /* this also DOESN'T WORK if you are crossing 180/-180 lon. I don't plan to in the near future...  */
1086     vik_viewport_screen_to_coord ( vp, -500, -500, &upperleft );
1087     vik_viewport_screen_to_coord ( vp, dp->width+500, dp->height+500, &bottomright );
1088     dp->ce1 = upperleft.east_west;
1089     dp->ce2 = bottomright.east_west;
1090     dp->cn1 = bottomright.north_south;
1091     dp->cn2 = upperleft.north_south;
1092   }
1093
1094   dp->track_gc_iter = 0;
1095 }
1096
1097 static gint calculate_velocity ( VikTrwLayer *vtl, VikTrackpoint *tp1, VikTrackpoint *tp2 )
1098 {
1099   static gdouble rv = 0;
1100   if ( tp1->has_timestamp && tp2->has_timestamp )
1101   {
1102     rv = ( vik_coord_diff ( &(tp1->coord), &(tp2->coord) )
1103            / (tp1->timestamp - tp2->timestamp) ) - vtl->velocity_min;
1104
1105     if ( rv < 0 )
1106       return VIK_TRW_LAYER_TRACK_GC_MIN;
1107     else if ( vtl->velocity_min >= vtl->velocity_max )
1108       return VIK_TRW_LAYER_TRACK_GC_MAX;
1109
1110     rv *= (VIK_TRW_LAYER_TRACK_GC_RATES / (vtl->velocity_max - vtl->velocity_min));
1111
1112     if ( rv >= VIK_TRW_LAYER_TRACK_GC_MAX )
1113       return VIK_TRW_LAYER_TRACK_GC_MAX;
1114     return (gint) rv;
1115  }
1116  else
1117    return VIK_TRW_LAYER_TRACK_GC_BLACK;
1118 }
1119
1120 void draw_utm_skip_insignia ( VikViewport *vvp, GdkGC *gc, gint x, gint y )
1121 {
1122   vik_viewport_draw_line ( vvp, gc, x+5, y, x-5, y );
1123   vik_viewport_draw_line ( vvp, gc, x, y+5, x, y-5 );
1124   vik_viewport_draw_line ( vvp, gc, x+5, y+5, x-5, y-5 );
1125   vik_viewport_draw_line ( vvp, gc, x+5, y-5, x-5, y+5 );
1126 }
1127
1128 static void trw_layer_draw_track ( const gchar *name, VikTrack *track, struct DrawingParams *dp, gboolean drawing_white_background )
1129 {
1130   /* TODO: this function is a mess, get rid of any redundancy */
1131   GList *list = track->trackpoints;
1132   GdkGC *main_gc;
1133   gboolean useoldvals = TRUE;
1134
1135   gboolean drawpoints;
1136   gboolean drawstops;
1137   gboolean drawelevation;
1138   gdouble min_alt, max_alt, alt_diff = 0;
1139
1140   const guint8 tp_size_reg = 2;
1141   const guint8 tp_size_cur = 4;
1142   guint8 tp_size;
1143
1144   if ( dp->vtl->drawelevation )
1145   {
1146     /* assume if it has elevation at the beginning, it has it throughout. not ness a true good assumption */
1147     if ( ( drawelevation = vik_track_get_minmax_alt ( track, &min_alt, &max_alt ) ) )
1148       alt_diff = max_alt - min_alt;
1149   }
1150
1151   if ( ! track->visible )
1152     return;
1153
1154   /* admittedly this is not an efficient way to do it because we go through the whole GC thing all over... */
1155   if ( dp->vtl->bg_line_thickness && !drawing_white_background )
1156     trw_layer_draw_track ( name, track, dp, TRUE );
1157
1158   if ( drawing_white_background )
1159     drawpoints = drawstops = FALSE;
1160   else {
1161     drawpoints = dp->vtl->drawpoints;
1162     drawstops = dp->vtl->drawstops;
1163   }
1164
1165   /* Current track - used for creation */
1166   if ( track == dp->vtl->current_track )
1167     main_gc = dp->vtl->current_track_gc;
1168   else {
1169     if ( vik_viewport_get_draw_highlight ( dp->vp ) ) {
1170       /* Draw all tracks of the layer in special colour */
1171       /* if track is member of selected layer or is the current selected track
1172          then draw in the highlight colour.
1173          NB this supercedes the drawmode */
1174       if ( dp->vtl && ( ( dp->vtl == vik_window_get_selected_trw_layer ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(dp->vtl) ) ) ||
1175                         ( dp->vtl->tracks == vik_window_get_selected_tracks ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(dp->vtl) ) ) ||
1176                         track == vik_window_get_selected_track ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(dp->vtl) ) ) ) {
1177         main_gc = vik_viewport_get_gc_highlight (dp->vp);
1178       }
1179       else {
1180         if ( dp->vtl->drawmode == DRAWMODE_ALL_BLACK )
1181           dp->track_gc_iter = VIK_TRW_LAYER_TRACK_GC_BLACK;
1182
1183         main_gc = g_array_index(dp->vtl->track_gc, GdkGC *, dp->track_gc_iter);
1184       }
1185     }
1186     else {
1187       if ( dp->vtl->drawmode == DRAWMODE_ALL_BLACK )
1188         dp->track_gc_iter = VIK_TRW_LAYER_TRACK_GC_BLACK;
1189           
1190       main_gc = g_array_index(dp->vtl->track_gc, GdkGC *, dp->track_gc_iter);
1191     }
1192   }
1193
1194   if (list) {
1195     int x, y, oldx, oldy;
1196     VikTrackpoint *tp = VIK_TRACKPOINT(list->data);
1197   
1198     tp_size = (list == dp->vtl->current_tpl) ? tp_size_cur : tp_size_reg;
1199
1200     vik_viewport_coord_to_screen ( dp->vp, &(tp->coord), &x, &y );
1201
1202     if ( (drawpoints) && dp->track_gc_iter < VIK_TRW_LAYER_TRACK_GC )
1203     {
1204       GdkPoint trian[3] = { { x, y-(3*tp_size) }, { x-(2*tp_size), y+(2*tp_size) }, {x+(2*tp_size), y+(2*tp_size)} };
1205       vik_viewport_draw_polygon ( dp->vp, main_gc, TRUE, trian, 3 );
1206     }
1207
1208     oldx = x;
1209     oldy = y;
1210
1211     while ((list = g_list_next(list)))
1212     {
1213       tp = VIK_TRACKPOINT(list->data);
1214       tp_size = (list == dp->vtl->current_tpl) ? tp_size_cur : tp_size_reg;
1215
1216       /* check some stuff -- but only if we're in UTM and there's only ONE ZONE; or lat lon */
1217       if ( (!dp->one_zone && !dp->lat_lon) ||     /* UTM & zones; do everything */
1218              ( ((!dp->one_zone) || tp->coord.utm_zone == dp->center->utm_zone) &&   /* only check zones if UTM & one_zone */
1219              tp->coord.east_west < dp->ce2 && tp->coord.east_west > dp->ce1 &&  /* both UTM and lat lon */
1220              tp->coord.north_south > dp->cn1 && tp->coord.north_south < dp->cn2 ) )
1221       {
1222         vik_viewport_coord_to_screen ( dp->vp, &(tp->coord), &x, &y );
1223
1224         /*
1225          * If points are the same in display coordinates, don't draw.
1226          */
1227         if ( useoldvals && x == oldx && y == oldy )
1228         {
1229           // Still need to process points to ensure 'stops' are drawn if required
1230           if ( drawstops && drawpoints && ! drawing_white_background && list->next &&
1231                (VIK_TRACKPOINT(list->next->data)->timestamp - VIK_TRACKPOINT(list->data)->timestamp > dp->vtl->stop_length) )
1232             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 );
1233
1234           goto skip;
1235         }
1236
1237         if ( drawpoints && ! drawing_white_background )
1238         {
1239           if ( list->next ) {
1240             /*
1241              * The concept of drawing stops is that a trackpoint
1242              * that is if the next trackpoint has a timestamp far into
1243              * the future, we draw a circle of 6x trackpoint size,
1244              * instead of a rectangle of 2x trackpoint size.
1245              * This is drawn first so the trackpoint will be drawn on top
1246              */
1247             /* stops */
1248             if ( drawstops && VIK_TRACKPOINT(list->next->data)->timestamp - VIK_TRACKPOINT(list->data)->timestamp > dp->vtl->stop_length )
1249               /* Stop point.  Draw 6x circle. */
1250               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 );
1251
1252             /* Regular point - draw 2x square. */
1253             vik_viewport_draw_rectangle ( dp->vp, main_gc, TRUE, x-tp_size, y-tp_size, 2*tp_size, 2*tp_size );
1254           }
1255           else
1256             /* Final point - draw 4x circle. */
1257             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 );
1258         }
1259
1260         if ((!tp->newsegment) && (dp->vtl->drawlines))
1261         {
1262           VikTrackpoint *tp2 = VIK_TRACKPOINT(list->prev->data);
1263
1264           /* UTM only: zone check */
1265           if ( drawpoints && dp->vtl->coord_mode == VIK_COORD_UTM && tp->coord.utm_zone != dp->center->utm_zone )
1266             draw_utm_skip_insignia (  dp->vp, main_gc, x, y);
1267
1268           if ( dp->vtl->drawmode == DRAWMODE_BY_VELOCITY ) {
1269             dp->track_gc_iter = calculate_velocity ( dp->vtl, tp, tp2 );
1270             main_gc = g_array_index(dp->vtl->track_gc, GdkGC *, dp->track_gc_iter);
1271           }
1272
1273           if (!useoldvals)
1274             vik_viewport_coord_to_screen ( dp->vp, &(tp2->coord), &oldx, &oldy );
1275
1276           if ( drawing_white_background ) {
1277             vik_viewport_draw_line ( dp->vp, dp->vtl->track_bg_gc, oldx, oldy, x, y);
1278           }
1279           else {
1280
1281             vik_viewport_draw_line ( dp->vp, main_gc, oldx, oldy, x, y);
1282             if ( dp->vtl->drawelevation && list->next && VIK_TRACKPOINT(list->next->data)->altitude != VIK_DEFAULT_ALTITUDE ) {
1283               GdkPoint tmp[4];
1284               #define FIXALTITUDE(what) ((VIK_TRACKPOINT((what))->altitude-min_alt)/alt_diff*DRAW_ELEVATION_FACTOR*dp->vtl->elevation_factor/dp->xmpp)
1285
1286               tmp[0].x = oldx;
1287               tmp[0].y = oldy;
1288               tmp[1].x = oldx;
1289               tmp[1].y = oldy-FIXALTITUDE(list->data);
1290               tmp[2].x = x;
1291               tmp[2].y = y-FIXALTITUDE(list->next->data);
1292               tmp[3].x = x;
1293               tmp[3].y = y;
1294
1295               GdkGC *tmp_gc;
1296               if ( ((oldx - x) > 0 && (oldy - y) > 0) || ((oldx - x) < 0 && (oldy - y) < 0))
1297                 tmp_gc = GTK_WIDGET(dp->vp)->style->light_gc[3];
1298               else
1299                 tmp_gc = GTK_WIDGET(dp->vp)->style->dark_gc[0];
1300               vik_viewport_draw_polygon ( dp->vp, tmp_gc, TRUE, tmp, 4);
1301
1302               vik_viewport_draw_line ( dp->vp, main_gc, oldx, oldy-FIXALTITUDE(list->data), x, y-FIXALTITUDE(list->next->data));
1303             }
1304           }
1305         }
1306
1307       skip:
1308         oldx = x;
1309         oldy = y;
1310         useoldvals = TRUE;
1311       }
1312       else {
1313         if (useoldvals && dp->vtl->drawlines && (!tp->newsegment))
1314         {
1315           VikTrackpoint *tp2 = VIK_TRACKPOINT(list->prev->data);
1316           if ( dp->vtl->coord_mode != VIK_COORD_UTM || tp->coord.utm_zone == dp->center->utm_zone )
1317           {
1318             vik_viewport_coord_to_screen ( dp->vp, &(tp->coord), &x, &y );
1319             if ( dp->vtl->drawmode == DRAWMODE_BY_VELOCITY ) {
1320               dp->track_gc_iter = calculate_velocity ( dp->vtl, tp, tp2 );
1321               main_gc = g_array_index(dp->vtl->track_gc, GdkGC *, dp->track_gc_iter);
1322             }
1323
1324             /*
1325              * If points are the same in display coordinates, don't draw.
1326              */
1327             if ( x != oldx || y != oldy )
1328               {
1329                 if ( drawing_white_background )
1330                   vik_viewport_draw_line ( dp->vp, dp->vtl->track_bg_gc, oldx, oldy, x, y);
1331                 else
1332                   vik_viewport_draw_line ( dp->vp, main_gc, oldx, oldy, x, y);
1333               }
1334           }
1335           else 
1336           {
1337             /*
1338              * If points are the same in display coordinates, don't draw.
1339              */
1340             if ( x != oldx && y != oldy )
1341               {
1342                 vik_viewport_coord_to_screen ( dp->vp, &(tp2->coord), &x, &y );
1343                 draw_utm_skip_insignia ( dp->vp, main_gc, x, y );
1344               }
1345           }
1346         }
1347         useoldvals = FALSE;
1348       }
1349     }
1350   }
1351   if ( dp->vtl->drawmode == DRAWMODE_BY_TRACK )
1352     if ( ++(dp->track_gc_iter) >= VIK_TRW_LAYER_TRACK_GC )
1353       dp->track_gc_iter = 0;
1354 }
1355
1356 /* the only reason this exists is so that trw_layer_draw_track can first call itself to draw the white track background */
1357 static void trw_layer_draw_track_cb ( const gchar *name, VikTrack *track, struct DrawingParams *dp )
1358 {
1359   trw_layer_draw_track ( name, track, dp, FALSE );
1360 }
1361
1362 static void cached_pixbuf_free ( CachedPixbuf *cp )
1363 {
1364   g_object_unref ( G_OBJECT(cp->pixbuf) );
1365   g_free ( cp->image );
1366 }
1367
1368 static gint cached_pixbuf_cmp ( CachedPixbuf *cp, const gchar *name )
1369 {
1370   return strcmp ( cp->image, name );
1371 }
1372
1373 static void trw_layer_draw_waypoint ( const gchar *name, VikWaypoint *wp, struct DrawingParams *dp )
1374 {
1375   if ( wp->visible )
1376   if ( (!dp->one_zone && !dp->lat_lon) || ( ( dp->lat_lon || wp->coord.utm_zone == dp->center->utm_zone ) && 
1377              wp->coord.east_west < dp->ce2 && wp->coord.east_west > dp->ce1 && 
1378              wp->coord.north_south > dp->cn1 && wp->coord.north_south < dp->cn2 ) )
1379   {
1380     gint x, y;
1381     GdkPixbuf *sym = NULL;
1382     vik_viewport_coord_to_screen ( dp->vp, &(wp->coord), &x, &y );
1383
1384     /* if in shrunken_cache, get that. If not, get and add to shrunken_cache */
1385
1386     if ( wp->image && dp->vtl->drawimages )
1387     {
1388       GdkPixbuf *pixbuf = NULL;
1389       GList *l;
1390
1391       if ( dp->vtl->image_alpha == 0)
1392         return;
1393
1394       l = g_list_find_custom ( dp->vtl->image_cache->head, wp->image, (GCompareFunc) cached_pixbuf_cmp );
1395       if ( l )
1396         pixbuf = ((CachedPixbuf *) l->data)->pixbuf;
1397       else
1398       {
1399         gchar *image = wp->image;
1400         GdkPixbuf *regularthumb = a_thumbnails_get ( wp->image );
1401         if ( ! regularthumb )
1402         {
1403           regularthumb = a_thumbnails_get_default (); /* cache one 'not yet loaded' for all thumbs not loaded */
1404           image = "\x12\x00"; /* this shouldn't occur naturally. */
1405         }
1406         if ( regularthumb )
1407         {
1408           CachedPixbuf *cp = NULL;
1409           cp = g_malloc ( sizeof ( CachedPixbuf ) );
1410           if ( dp->vtl->image_size == 128 )
1411             cp->pixbuf = regularthumb;
1412           else
1413           {
1414             cp->pixbuf = a_thumbnails_scale_pixbuf(regularthumb, dp->vtl->image_size, dp->vtl->image_size);
1415             g_assert ( cp->pixbuf );
1416             g_object_unref ( G_OBJECT(regularthumb) );
1417           }
1418           cp->image = g_strdup ( image );
1419
1420           /* needed so 'click picture' tool knows how big the pic is; we don't
1421            * store it in cp because they may have been freed already. */
1422           wp->image_width = gdk_pixbuf_get_width ( cp->pixbuf );
1423           wp->image_height = gdk_pixbuf_get_height ( cp->pixbuf );
1424
1425           g_queue_push_head ( dp->vtl->image_cache, cp );
1426           if ( dp->vtl->image_cache->length > dp->vtl->image_cache_size )
1427             cached_pixbuf_free ( g_queue_pop_tail ( dp->vtl->image_cache ) );
1428
1429           pixbuf = cp->pixbuf;
1430         }
1431         else
1432         {
1433           pixbuf = a_thumbnails_get_default (); /* thumbnail not yet loaded */
1434         }
1435       }
1436       if ( pixbuf )
1437       {
1438         gint w, h;
1439         w = gdk_pixbuf_get_width ( pixbuf );
1440         h = gdk_pixbuf_get_height ( pixbuf );
1441
1442         if ( x+(w/2) > 0 && y+(h/2) > 0 && x-(w/2) < dp->width && y-(h/2) < dp->height ) /* always draw within boundaries */
1443         {
1444           if ( vik_viewport_get_draw_highlight ( dp->vp ) ) {
1445             if ( dp->vtl == vik_window_get_selected_trw_layer ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(dp->vtl) ) ||
1446                  dp->vtl->waypoints == vik_window_get_selected_waypoints ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(dp->vtl) ) ||
1447                  wp == vik_window_get_selected_waypoint ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(dp->vtl) ) ) {
1448               // Highlighted - so draw a little border around the chosen one
1449               // single line seems a little weak so draw 2 of them
1450               vik_viewport_draw_rectangle (dp->vp, vik_viewport_get_gc_highlight (dp->vp), FALSE,
1451                                            x - (w/2) - 1, y - (h/2) - 1, w + 2, h + 2 );
1452               vik_viewport_draw_rectangle (dp->vp, vik_viewport_get_gc_highlight (dp->vp), FALSE,
1453                                            x - (w/2) - 2, y - (h/2) - 2, w + 4, h + 4 );
1454             }
1455           }
1456           if ( dp->vtl->image_alpha == 255 )
1457             vik_viewport_draw_pixbuf ( dp->vp, pixbuf, 0, 0, x - (w/2), y - (h/2), w, h );
1458           else
1459             vik_viewport_draw_pixbuf_with_alpha ( dp->vp, pixbuf, dp->vtl->image_alpha, 0, 0, x - (w/2), y - (h/2), w, h );
1460         }
1461         return; /* if failed to draw picture, default to drawing regular waypoint (below) */
1462       }
1463     }
1464
1465     /* DRAW ACTUAL DOT */
1466     if ( dp->vtl->wp_draw_symbols && wp->symbol && (sym = a_get_wp_sym(wp->symbol)) ) {
1467       vik_viewport_draw_pixbuf ( dp->vp, sym, 0, 0, x - gdk_pixbuf_get_width(sym)/2, y - gdk_pixbuf_get_height(sym)/2, -1, -1 );
1468     } 
1469     else if ( wp == dp->vtl->current_wp ) {
1470       switch ( dp->vtl->wp_symbol ) {
1471         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;
1472         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;
1473         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;
1474         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 );
1475                           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 );
1476       }
1477     }
1478     else {
1479       switch ( dp->vtl->wp_symbol ) {
1480         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;
1481         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;
1482         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;
1483         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 );
1484                           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;
1485       }
1486     }
1487
1488     if ( dp->vtl->drawlabels )
1489     {
1490       /* thanks to the GPSDrive people (Fritz Ganter et al.) for hints on this part ... yah, I'm too lazy to study documentation */
1491       gint label_x, label_y;
1492       gint width, height;
1493       pango_layout_set_text ( dp->vtl->wplabellayout, wp->name, -1 );
1494       pango_layout_get_pixel_size ( dp->vtl->wplabellayout, &width, &height );
1495       label_x = x - width/2;
1496       if (sym)
1497         label_y = y - height - 2 - gdk_pixbuf_get_height(sym)/2;
1498       else
1499         label_y = y - dp->vtl->wp_size - height - 2;
1500
1501       /* if highlight mode on, then draw background text in highlight colour */
1502       if ( vik_viewport_get_draw_highlight ( dp->vp ) ) {
1503         if ( dp->vtl == vik_window_get_selected_trw_layer ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(dp->vtl) ) ||
1504              dp->vtl->waypoints == vik_window_get_selected_waypoints ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(dp->vtl) ) ||
1505              wp == vik_window_get_selected_waypoint ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(dp->vtl) ) )
1506           vik_viewport_draw_rectangle ( dp->vp, vik_viewport_get_gc_highlight (dp->vp), TRUE, label_x - 1, label_y-1,width+2,height+2);
1507         else
1508           vik_viewport_draw_rectangle ( dp->vp, dp->vtl->waypoint_bg_gc, TRUE, label_x - 1, label_y-1,width+2,height+2);
1509       }
1510       else {
1511         vik_viewport_draw_rectangle ( dp->vp, dp->vtl->waypoint_bg_gc, TRUE, label_x - 1, label_y-1,width+2,height+2);
1512       }
1513       vik_viewport_draw_layout ( dp->vp, dp->vtl->waypoint_text_gc, label_x, label_y, dp->vtl->wplabellayout );
1514     }
1515   }
1516 }
1517
1518 static void trw_layer_draw ( VikTrwLayer *l, gpointer data )
1519 {
1520   static struct DrawingParams dp;
1521   g_assert ( l != NULL );
1522
1523   init_drawing_params ( &dp, VIK_VIEWPORT(data) );
1524   dp.vtl = l;
1525
1526   if ( l->tracks_visible )
1527     g_hash_table_foreach ( l->tracks, (GHFunc) trw_layer_draw_track_cb, &dp );
1528
1529   if (l->waypoints_visible)
1530     g_hash_table_foreach ( l->waypoints, (GHFunc) trw_layer_draw_waypoint, &dp );
1531 }
1532
1533 static void trw_layer_free_track_gcs ( VikTrwLayer *vtl )
1534 {
1535   int i;
1536   if ( vtl->track_bg_gc ) 
1537   {
1538     g_object_unref ( vtl->track_bg_gc );
1539     vtl->track_bg_gc = NULL;
1540   }
1541   if ( vtl->current_track_gc ) 
1542   {
1543     g_object_unref ( vtl->current_track_gc );
1544     vtl->current_track_gc = NULL;
1545   }
1546
1547   if ( ! vtl->track_gc )
1548     return;
1549   for ( i = vtl->track_gc->len - 1; i >= 0; i-- )
1550     g_object_unref ( g_array_index ( vtl->track_gc, GObject *, i ) );
1551   g_array_free ( vtl->track_gc, TRUE );
1552   vtl->track_gc = NULL;
1553 }
1554
1555 static void trw_layer_new_track_gcs ( VikTrwLayer *vtl, VikViewport *vp )
1556 {
1557   GdkGC *gc[ VIK_TRW_LAYER_TRACK_GC ];
1558   gint width = vtl->line_thickness;
1559
1560   if ( vtl->track_gc )
1561     trw_layer_free_track_gcs ( vtl );
1562
1563   if ( vtl->track_bg_gc )
1564     g_object_unref ( vtl->track_bg_gc );
1565   vtl->track_bg_gc = vik_viewport_new_gc ( vp, "#FFFFFF", width + vtl->bg_line_thickness );
1566
1567   if ( vtl->current_track_gc )
1568     g_object_unref ( vtl->current_track_gc );
1569   vtl->current_track_gc = vik_viewport_new_gc ( vp, "#FF0000", 2 );
1570   gdk_gc_set_line_attributes ( vtl->current_track_gc, 2, GDK_LINE_ON_OFF_DASH, GDK_CAP_ROUND, GDK_JOIN_ROUND );
1571
1572   vtl->track_gc = g_array_sized_new ( FALSE, FALSE, sizeof ( GdkGC * ), VIK_TRW_LAYER_TRACK_GC );
1573
1574   gc[0] = vik_viewport_new_gc ( vp, "#2d870a", width ); /* below range */
1575
1576   gc[1] = vik_viewport_new_gc ( vp, "#0a8742", width );
1577   gc[2] = vik_viewport_new_gc ( vp, "#0a8783", width );
1578   gc[3] = vik_viewport_new_gc ( vp, "#0a4d87", width );
1579   gc[4] = vik_viewport_new_gc ( vp, "#05469f", width );
1580   gc[5] = vik_viewport_new_gc ( vp, "#1b059f", width );
1581   gc[6] = vik_viewport_new_gc ( vp, "#2d059f", width );
1582   gc[7] = vik_viewport_new_gc ( vp, "#4a059f", width );
1583   gc[8] = vik_viewport_new_gc ( vp, "#84059f", width );
1584   gc[9] = vik_viewport_new_gc ( vp, "#96059f", width );
1585   gc[10] = vik_viewport_new_gc ( vp, "#f22ef2", width );
1586
1587   gc[11] = vik_viewport_new_gc ( vp, "#874200", width ); /* above range */
1588
1589   gc[12] = vik_viewport_new_gc ( vp, "#000000", width ); /* black / no speed data */
1590
1591   g_array_append_vals ( vtl->track_gc, gc, VIK_TRW_LAYER_TRACK_GC );
1592 }
1593
1594 static VikTrwLayer* trw_layer_create ( VikViewport *vp )
1595 {
1596   VikTrwLayer *rv = trw_layer_new ( DRAWMODE_BY_TRACK );
1597   vik_layer_rename ( VIK_LAYER(rv), vik_trw_layer_interface.name );
1598
1599   if ( vp == NULL || GTK_WIDGET(vp)->window == NULL ) {
1600     /* early exit, as the rest is GUI related */
1601     return rv;
1602   }
1603
1604   PangoFontDescription *pfd;
1605   rv->wplabellayout = gtk_widget_create_pango_layout (GTK_WIDGET(vp), NULL);
1606   pfd = pango_font_description_from_string (WAYPOINT_FONT);
1607   pango_layout_set_font_description (rv->wplabellayout, pfd);
1608   /* freeing PangoFontDescription, cause it has been copied by prev. call */
1609   pango_font_description_free (pfd);
1610
1611   trw_layer_new_track_gcs ( rv, vp );
1612
1613   rv->waypoint_gc = vik_viewport_new_gc ( vp, "#000000", 2 );
1614   rv->waypoint_text_gc = vik_viewport_new_gc ( vp, "#FFFFFF", 1 );
1615   rv->waypoint_bg_gc = vik_viewport_new_gc ( vp, "#8383C4", 1 );
1616   gdk_gc_set_function ( rv->waypoint_bg_gc, GDK_AND );
1617
1618   rv->waypoint_font = gdk_font_load ( "-*-helvetica-bold-r-normal-*-*-100-*-*-p-*-iso8859-1" );
1619
1620   rv->has_verified_thumbnails = FALSE;
1621   rv->wp_symbol = WP_SYMBOL_FILLED_SQUARE;
1622   rv->wp_size = 4;
1623   rv->wp_draw_symbols = TRUE;
1624
1625   rv->coord_mode = vik_viewport_get_coord_mode ( vp );
1626
1627   rv->menu_selection = vik_layer_get_interface(VIK_LAYER(rv)->type)->menu_items_selection;
1628
1629   return rv;
1630 }
1631
1632 static void trw_layer_realize_track ( gpointer id, VikTrack *track, gpointer pass_along[5] )
1633 {
1634   GtkTreeIter *new_iter = g_malloc(sizeof(GtkTreeIter));
1635
1636 #ifdef VIK_CONFIG_ALPHABETIZED_TRW
1637   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]), NULL, TRUE, TRUE );
1638 #else
1639   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]), NULL, TRUE, TRUE );
1640 #endif
1641
1642   *new_iter = *((GtkTreeIter *) pass_along[1]);
1643   g_hash_table_insert ( VIK_TRW_LAYER(pass_along[2])->tracks_iters, id, new_iter );
1644
1645   if ( ! track->visible )
1646     vik_treeview_item_set_visible ( (VikTreeview *) pass_along[3], (GtkTreeIter *) pass_along[1], FALSE );
1647 }
1648
1649 static void trw_layer_realize_waypoint ( gpointer id, VikWaypoint *wp, gpointer pass_along[5] )
1650 {
1651   GtkTreeIter *new_iter = g_malloc(sizeof(GtkTreeIter));
1652 #ifdef VIK_CONFIG_ALPHABETIZED_TRW
1653   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]), NULL, TRUE, TRUE );
1654 #else
1655   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]), NULL, TRUE, TRUE );
1656 #endif
1657
1658   *new_iter = *((GtkTreeIter *) pass_along[1]);
1659   g_hash_table_insert ( VIK_TRW_LAYER(pass_along[2])->waypoints_iters, id, new_iter );
1660
1661   if ( ! wp->visible )
1662     vik_treeview_item_set_visible ( (VikTreeview *) pass_along[3], (GtkTreeIter *) pass_along[1], FALSE );
1663 }
1664
1665
1666 static void trw_layer_realize ( VikTrwLayer *vtl, VikTreeview *vt, GtkTreeIter *layer_iter )
1667 {
1668   GtkTreeIter iter2;
1669   gpointer pass_along[5] = { &(vtl->tracks_iter), &iter2, vtl, vt, GINT_TO_POINTER(VIK_TRW_LAYER_SUBLAYER_TRACK) };
1670
1671 #ifdef VIK_CONFIG_ALPHABETIZED_TRW
1672   vik_treeview_add_sublayer_alphabetized ( (VikTreeview *) vt, layer_iter, &(vtl->tracks_iter), _("Tracks"), vtl, NULL, VIK_TRW_LAYER_SUBLAYER_TRACKS, NULL, TRUE, FALSE );
1673 #else
1674   vik_treeview_add_sublayer ( (VikTreeview *) vt, layer_iter, &(vtl->tracks_iter), _("Tracks"), vtl, NULL, VIK_TRW_LAYER_SUBLAYER_TRACKS, NULL, TRUE, FALSE );
1675 #endif
1676   if ( ! vtl->tracks_visible )
1677     vik_treeview_item_set_visible ( (VikTreeview *) vt, &(vtl->tracks_iter), FALSE ); 
1678
1679   g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_realize_track, pass_along );
1680
1681 #ifdef VIK_CONFIG_ALPHABETIZED_TRW
1682   vik_treeview_add_sublayer_alphabetized ( (VikTreeview *) vt, layer_iter, &(vtl->waypoints_iter), _("Waypoints"), vtl, NULL, VIK_TRW_LAYER_SUBLAYER_WAYPOINTS, NULL, TRUE, FALSE );
1683 #else
1684   vik_treeview_add_sublayer ( (VikTreeview *) vt, layer_iter, &(vtl->waypoints_iter), _("Waypoints"), vtl, NULL, VIK_TRW_LAYER_SUBLAYER_WAYPOINTS, NULL, TRUE, FALSE );
1685 #endif
1686
1687   if ( ! vtl->waypoints_visible )
1688     vik_treeview_item_set_visible ( (VikTreeview *) vt, &(vtl->waypoints_iter), FALSE ); 
1689
1690   pass_along[0] = &(vtl->waypoints_iter);
1691   pass_along[4] = GINT_TO_POINTER(VIK_TRW_LAYER_SUBLAYER_WAYPOINT);
1692
1693   g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_realize_waypoint, pass_along );
1694
1695 }
1696
1697 static gboolean trw_layer_sublayer_toggle_visible ( VikTrwLayer *l, gint subtype, gpointer sublayer )
1698 {
1699   switch ( subtype )
1700   {
1701     case VIK_TRW_LAYER_SUBLAYER_TRACKS: return (l->tracks_visible ^= 1);
1702     case VIK_TRW_LAYER_SUBLAYER_WAYPOINTS: return (l->waypoints_visible ^= 1);
1703     case VIK_TRW_LAYER_SUBLAYER_TRACK:
1704     {
1705       VikTrack *t = g_hash_table_lookup ( l->tracks, sublayer );
1706       if (t)
1707         return (t->visible ^= 1);
1708       else
1709         return TRUE;
1710     }
1711     case VIK_TRW_LAYER_SUBLAYER_WAYPOINT:
1712     {
1713       VikWaypoint *t = g_hash_table_lookup ( l->waypoints, sublayer );
1714       if (t)
1715         return (t->visible ^= 1);
1716       else
1717         return TRUE;
1718     }
1719   }
1720   return TRUE;
1721 }
1722
1723 /*
1724  * Return a property about tracks for this layer
1725  */
1726 gint vik_trw_layer_get_property_tracks_line_thickness ( VikTrwLayer *vtl )
1727 {
1728   return vtl->line_thickness;
1729 }
1730
1731 // Structure to hold multiple track information for a layer
1732 typedef struct {
1733   gdouble length;
1734   time_t  start_time;
1735   time_t  end_time;
1736   gint    duration;
1737 } tooltip_tracks;
1738
1739 /*
1740  * Build up layer multiple track information via updating the tooltip_tracks structure
1741  */
1742 static void trw_layer_tracks_tooltip ( const gchar *name, VikTrack *tr, tooltip_tracks *tt )
1743 {
1744   tt->length = tt->length + vik_track_get_length (tr);
1745
1746   // Ensure times are available
1747   if ( tr->trackpoints &&
1748        VIK_TRACKPOINT(tr->trackpoints->data)->has_timestamp &&
1749        VIK_TRACKPOINT(g_list_last(tr->trackpoints)->data)->has_timestamp ) {
1750
1751     time_t t1, t2;
1752     t1 = VIK_TRACKPOINT(tr->trackpoints->data)->timestamp;
1753     t2 = VIK_TRACKPOINT(g_list_last(tr->trackpoints)->data)->timestamp;
1754
1755     // Assume never actually have a track with a time of 0 (1st Jan 1970)
1756     // Hence initialize to the first 'proper' value
1757     if ( tt->start_time == 0 )
1758         tt->start_time = t1;
1759     if ( tt->end_time == 0 )
1760         tt->end_time = t2;
1761
1762     // Update find the earliest / last times
1763     if ( t1 < tt->start_time )
1764         tt->start_time = t1;
1765     if ( t2 > tt->end_time )
1766         tt->end_time = t2;
1767
1768     // Keep track of total time
1769     //  there maybe gaps within a track (eg segments)
1770     //  but this should be generally good enough for a simple indicator
1771     tt->duration = tt->duration + (int)(t2-t1);
1772   }
1773 }
1774
1775 /*
1776  * Generate tooltip text for the layer.
1777  * This is relatively complicated as it considers information for
1778  *   no tracks, a single track or multiple tracks
1779  *     (which may or may not have timing information)
1780  */
1781 static const gchar* trw_layer_layer_tooltip ( VikTrwLayer *vtl )
1782 {
1783   gchar tbuf1[32];
1784   gchar tbuf2[64];
1785   gchar tbuf3[64];
1786   gchar tbuf4[10];
1787   tbuf1[0] = '\0';
1788   tbuf2[0] = '\0';
1789   tbuf3[0] = '\0';
1790   tbuf4[0] = '\0';
1791
1792   static gchar tmp_buf[128];
1793   tmp_buf[0] = '\0';
1794
1795   // For compact date format I'm using '%x'     [The preferred date representation for the current locale without the time.]
1796
1797   // Safety check - I think these should always be valid
1798   if ( vtl->tracks && vtl->waypoints ) {
1799     tooltip_tracks tt = { 0.0, 0, 0 };
1800     g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_tracks_tooltip, &tt );
1801
1802     GDate* gdate_start = g_date_new ();
1803     g_date_set_time_t (gdate_start, tt.start_time);
1804
1805     GDate* gdate_end = g_date_new ();
1806     g_date_set_time_t (gdate_end, tt.end_time);
1807
1808     if ( g_date_compare (gdate_start, gdate_end) ) {
1809       // Dates differ so print range on separate line
1810       g_date_strftime (tbuf1, sizeof(tbuf1), "%x", gdate_start);
1811       g_date_strftime (tbuf2, sizeof(tbuf2), "%x", gdate_end);
1812       g_snprintf (tbuf3, sizeof(tbuf3), "%s to %s\n", tbuf1, tbuf2);
1813     }
1814     else {
1815       // Same date so just show it and keep rest of text on the same line - provided it's a valid time!
1816       if ( tt.start_time != 0 )
1817         g_date_strftime (tbuf3, sizeof(tbuf3), "%x: ", gdate_start);
1818     }
1819
1820     tbuf2[0] = '\0';
1821     if ( tt.length > 0.0 ) {
1822       gdouble len_in_units;
1823
1824       // Setup info dependent on distance units
1825       if ( a_vik_get_units_distance() == VIK_UNITS_DISTANCE_MILES ) {
1826         g_snprintf (tbuf4, sizeof(tbuf4), "miles");
1827         len_in_units = VIK_METERS_TO_MILES(tt.length);
1828       }
1829       else {
1830         g_snprintf (tbuf4, sizeof(tbuf4), "kms");
1831         len_in_units = tt.length/1000.0;
1832       }
1833
1834       // Timing information if available
1835       tbuf1[0] = '\0';
1836       if ( tt.duration > 0 ) {
1837         g_snprintf (tbuf1, sizeof(tbuf1),
1838                     _(" in %d:%02d hrs:mins"),
1839                     (int)round(tt.duration/3600), (int)round((tt.duration/60)%60));
1840       }
1841       g_snprintf (tbuf2, sizeof(tbuf2),
1842                   _("\n%sTotal Length %.1f %s%s"),
1843                   tbuf3, len_in_units, tbuf4, tbuf1);
1844     }
1845
1846     // Put together all the elements to form compact tooltip text
1847     g_snprintf (tmp_buf, sizeof(tmp_buf),
1848                 _("Tracks: %d - Waypoints: %d%s"),
1849                 g_hash_table_size (vtl->tracks), g_hash_table_size (vtl->waypoints), tbuf2);
1850
1851     g_date_free (gdate_start);
1852     g_date_free (gdate_end);
1853
1854   }
1855
1856   return tmp_buf;
1857 }
1858
1859 static const gchar* trw_layer_sublayer_tooltip ( VikTrwLayer *l, gint subtype, gpointer sublayer )
1860 {
1861   switch ( subtype )
1862   {
1863     case VIK_TRW_LAYER_SUBLAYER_TRACKS: return NULL;
1864     case VIK_TRW_LAYER_SUBLAYER_WAYPOINTS: return NULL;
1865     case VIK_TRW_LAYER_SUBLAYER_TRACK:
1866     {
1867       VikTrack *tr = g_hash_table_lookup ( l->tracks, sublayer );
1868       if ( tr ) {
1869         // Could be a better way of handling strings - but this works...
1870         gchar time_buf1[20];
1871         gchar time_buf2[20];
1872         time_buf1[0] = '\0';
1873         time_buf2[0] = '\0';
1874         static gchar tmp_buf[100];
1875         // Compact info: Short date eg (11/20/99), duration and length
1876         // Hopefully these are the things that are most useful and so promoted into the tooltip
1877         if ( tr->trackpoints && VIK_TRACKPOINT(tr->trackpoints->data)->has_timestamp ) {
1878           // %x     The preferred date representation for the current locale without the time.
1879           strftime (time_buf1, sizeof(time_buf1), "%x: ", gmtime(&(VIK_TRACKPOINT(tr->trackpoints->data)->timestamp)));
1880           if ( VIK_TRACKPOINT(g_list_last(tr->trackpoints)->data)->has_timestamp ) {
1881             gint dur = ( (VIK_TRACKPOINT(g_list_last(tr->trackpoints)->data)->timestamp) - (VIK_TRACKPOINT(tr->trackpoints->data)->timestamp) );
1882             if ( dur > 0 )
1883               g_snprintf ( time_buf2, sizeof(time_buf2), _("- %d:%02d hrs:mins"), (int)round(dur/3600), (int)round((dur/60)%60) );
1884           }
1885         }
1886         // Get length and consider the appropriate distance units
1887         gdouble tr_len = vik_track_get_length(tr);
1888         vik_units_distance_t dist_units = a_vik_get_units_distance ();
1889         switch (dist_units) {
1890         case VIK_UNITS_DISTANCE_KILOMETRES:
1891           g_snprintf (tmp_buf, sizeof(tmp_buf), _("%s%.1f km %s"), time_buf1, tr_len/1000.0, time_buf2);
1892           break;
1893         case VIK_UNITS_DISTANCE_MILES:
1894           g_snprintf (tmp_buf, sizeof(tmp_buf), _("%s%.1f miles %s"), time_buf1, VIK_METERS_TO_MILES(tr_len), time_buf2);
1895           break;
1896         default:
1897           break;
1898         }
1899         return tmp_buf;
1900       }
1901     }
1902     break;
1903     case VIK_TRW_LAYER_SUBLAYER_WAYPOINT:
1904     {
1905       VikWaypoint *w = g_hash_table_lookup ( l->waypoints, sublayer );
1906       // NB It's OK to return NULL
1907       if ( w )
1908         return w->comment;
1909     }
1910     break;
1911     default: break;
1912   }
1913   return NULL;
1914 }
1915
1916 /*
1917  * Function to show basic track point information on the statusbar
1918  */
1919 static void set_statusbar_msg_info_trkpt ( VikTrwLayer *vtl, VikTrackpoint *trkpt )
1920 {
1921   gchar tmp_buf1[64];
1922   switch (a_vik_get_units_height ()) {
1923   case VIK_UNITS_HEIGHT_FEET:
1924     g_snprintf(tmp_buf1, sizeof(tmp_buf1), _("Trkpt: Alt %dft"), (int)round(VIK_METERS_TO_FEET(trkpt->altitude)));
1925     break;
1926   default:
1927     //VIK_UNITS_HEIGHT_METRES:
1928     g_snprintf(tmp_buf1, sizeof(tmp_buf1), _("Trkpt: Alt %dm"), (int)round(trkpt->altitude));
1929   }
1930   
1931   gchar tmp_buf2[64];
1932   tmp_buf2[0] = '\0';
1933   if ( trkpt->has_timestamp ) {
1934     // Compact date time format
1935     strftime (tmp_buf2, sizeof(tmp_buf2), _(" | Time %x %X"), localtime(&(trkpt->timestamp)));
1936   }
1937
1938   // Position part
1939   // Position is put later on, as this bit may not be seen if the display is not big enough,
1940   //   one can easily use the current pointer position to see this if needed
1941   gchar *lat = NULL, *lon = NULL;
1942   static struct LatLon ll;
1943   vik_coord_to_latlon (&(trkpt->coord), &ll);
1944   a_coords_latlon_to_string ( &ll, &lat, &lon );
1945
1946   // Track name
1947   // Again is put later on, as this bit may not be seen if the display is not big enough
1948   //  trackname can be seen from the treeview (when enabled)
1949   // Also name could be very long to not leave room for anything else
1950   gchar tmp_buf3[64];
1951   tmp_buf3[0] = '\0';
1952   if ( vtl->current_tp_track ) {
1953     g_snprintf(tmp_buf3, sizeof(tmp_buf3),  _(" | Track: %s"), vtl->current_tp_track->name );
1954   }
1955
1956   // Combine parts to make overall message
1957   gchar *msg = g_strdup_printf (_("%s%s | %s %s %s"), tmp_buf1, tmp_buf2, lat, lon, tmp_buf3);
1958   vik_statusbar_set_message ( vik_window_get_statusbar (VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl))), VIK_STATUSBAR_INFO, msg );
1959   g_free ( lat );
1960   g_free ( lon );
1961   g_free ( msg );
1962 }
1963
1964 /*
1965  * Function to show basic waypoint information on the statusbar
1966  */
1967 static void set_statusbar_msg_info_wpt ( VikTrwLayer *vtl, VikWaypoint *wpt )
1968 {
1969   gchar tmp_buf1[64];
1970   switch (a_vik_get_units_height ()) {
1971   case VIK_UNITS_HEIGHT_FEET:
1972     g_snprintf(tmp_buf1, sizeof(tmp_buf1), _("Wpt: Alt %dft"), (int)round(VIK_METERS_TO_FEET(wpt->altitude)));
1973     break;
1974   default:
1975     //VIK_UNITS_HEIGHT_METRES:
1976     g_snprintf(tmp_buf1, sizeof(tmp_buf1), _("Wpt: Alt %dm"), (int)round(wpt->altitude));
1977   }
1978   
1979   // Position part
1980   // Position is put last, as this bit is most likely not to be seen if the display is not big enough,
1981   //   one can easily use the current pointer position to see this if needed
1982   gchar *lat = NULL, *lon = NULL;
1983   static struct LatLon ll;
1984   vik_coord_to_latlon (&(wpt->coord), &ll);
1985   a_coords_latlon_to_string ( &ll, &lat, &lon );
1986
1987   // Combine parts to make overall message
1988   gchar *msg;
1989   if ( wpt->comment )
1990     // Add comment if available
1991     msg = g_strdup_printf ( _("%s | %s %s | Comment: %s"), tmp_buf1, lat, lon, wpt->comment );
1992   else
1993     msg = g_strdup_printf ( _("%s | %s %s"), tmp_buf1, lat, lon );
1994   vik_statusbar_set_message ( vik_window_get_statusbar (VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl))), VIK_STATUSBAR_INFO, msg );
1995   g_free ( lat );
1996   g_free ( lon );
1997   g_free ( msg );
1998 }
1999
2000 /**
2001  * General layer selection function, find out which bit is selected and take appropriate action
2002  */
2003 static gboolean trw_layer_selected ( VikTrwLayer *l, gint subtype, gpointer sublayer, gint type, gpointer vlp )
2004 {
2005   // Reset
2006   l->current_wp    = NULL;
2007   l->current_wp_id = NULL;
2008   trw_layer_cancel_current_tp ( l, FALSE );
2009
2010   // Clear statusbar
2011   vik_statusbar_set_message ( vik_window_get_statusbar (VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(l))), VIK_STATUSBAR_INFO, "" );
2012
2013   switch ( type )
2014     {
2015     case VIK_TREEVIEW_TYPE_LAYER:
2016       {
2017         vik_window_set_selected_trw_layer ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l), l );
2018         /* Mark for redraw */
2019         return TRUE;
2020       }
2021       break;
2022
2023     case VIK_TREEVIEW_TYPE_SUBLAYER:
2024       {
2025         switch ( subtype )
2026           {
2027           case VIK_TRW_LAYER_SUBLAYER_TRACKS:
2028             {
2029               vik_window_set_selected_tracks ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l), l->tracks, l );
2030               /* Mark for redraw */
2031               return TRUE;
2032             }
2033             break;
2034           case VIK_TRW_LAYER_SUBLAYER_TRACK:
2035             {
2036               VikTrack *track = g_hash_table_lookup ( l->tracks, sublayer );
2037               vik_window_set_selected_track ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l), (gpointer)track, l, sublayer );
2038               /* Mark for redraw */
2039               return TRUE;
2040             }
2041             break;
2042           case VIK_TRW_LAYER_SUBLAYER_WAYPOINTS:
2043             {
2044               vik_window_set_selected_waypoints ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l), l->waypoints, l );
2045               /* Mark for redraw */
2046               return TRUE;
2047             }
2048             break;
2049           case VIK_TRW_LAYER_SUBLAYER_WAYPOINT:
2050             {
2051               VikWaypoint *wpt = g_hash_table_lookup ( l->waypoints, sublayer );
2052               if ( wpt ) {
2053             vik_window_set_selected_waypoint ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l), (gpointer)wpt, l, sublayer );
2054             // Show some waypoint info
2055             set_statusbar_msg_info_wpt ( l, wpt );
2056             /* Mark for redraw */
2057             return TRUE;
2058               }
2059             }
2060             break;
2061           default:
2062             {
2063               return vik_window_clear_highlight ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l) );
2064             }
2065             break;
2066           }
2067         return FALSE;
2068       }
2069       break;
2070
2071     default:
2072       return vik_window_clear_highlight ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l) );
2073       break;
2074     }
2075 }
2076
2077 GHashTable *vik_trw_layer_get_tracks ( VikTrwLayer *l )
2078 {
2079   return l->tracks;
2080 }
2081
2082 GHashTable *vik_trw_layer_get_waypoints ( VikTrwLayer *l )
2083 {
2084   return l->waypoints;
2085 }
2086
2087 /*
2088  * ATM use a case sensitive find
2089  * Finds the first one
2090  */
2091 static gboolean trw_layer_waypoint_find ( const gpointer id, const VikWaypoint *wp, const gchar *name )
2092 {
2093   if ( wp && wp->name )
2094     if ( ! strcmp ( wp->name, name ) )
2095       return TRUE;
2096   return FALSE;
2097 }
2098
2099 /*
2100  * Get waypoint by name - not guaranteed to be unique
2101  * Finds the first one
2102  */
2103 VikWaypoint *vik_trw_layer_get_waypoint ( VikTrwLayer *vtl, const gchar *name )
2104 {
2105   return g_hash_table_find ( vtl->waypoints, (GHRFunc) trw_layer_waypoint_find, (gpointer) name );
2106 }
2107
2108 /*
2109  * ATM use a case sensitive find
2110  * Finds the first one
2111  */
2112 static gboolean trw_layer_track_find ( const gpointer id, const VikTrack *trk, const gchar *name )
2113 {
2114   if ( trk && trk->name )
2115     if ( ! strcmp ( trk->name, name ) )
2116       return TRUE;
2117   return FALSE;
2118 }
2119
2120 /*
2121  * Get track by name - not guaranteed to be unique
2122  * Finds the first one
2123  */
2124 VikTrack *vik_trw_layer_get_track ( VikTrwLayer *vtl, const gchar *name )
2125 {
2126   return g_hash_table_find ( vtl->tracks, (GHRFunc) trw_layer_track_find, (gpointer) name );
2127 }
2128
2129 static void trw_layer_find_maxmin_waypoints ( const gchar *name, const VikWaypoint *w, struct LatLon maxmin[2] )
2130 {
2131   static VikCoord fixme;
2132   vik_coord_copy_convert ( &(w->coord), VIK_COORD_LATLON, &fixme );
2133   if ( VIK_LATLON(&fixme)->lat > maxmin[0].lat || maxmin[0].lat == 0.0 )
2134     maxmin[0].lat = VIK_LATLON(&fixme)->lat;
2135   if ( VIK_LATLON(&fixme)->lat < maxmin[1].lat || maxmin[1].lat == 0.0 )
2136     maxmin[1].lat = VIK_LATLON(&fixme)->lat;
2137   if ( VIK_LATLON(&fixme)->lon > maxmin[0].lon || maxmin[0].lon == 0.0 )
2138     maxmin[0].lon = VIK_LATLON(&fixme)->lon;
2139   if ( VIK_LATLON(&fixme)->lon < maxmin[1].lon || maxmin[1].lon == 0.0 )
2140     maxmin[1].lon = VIK_LATLON(&fixme)->lon;
2141 }
2142
2143 static void trw_layer_find_maxmin_tracks ( const gchar *name, GList **t, struct LatLon maxmin[2] )
2144 {
2145   GList *tr = *t;
2146   static VikCoord fixme;
2147
2148   while ( tr )
2149   {
2150     vik_coord_copy_convert ( &(VIK_TRACKPOINT(tr->data)->coord), VIK_COORD_LATLON, &fixme );
2151     if ( VIK_LATLON(&fixme)->lat > maxmin[0].lat || maxmin[0].lat == 0.0 )
2152       maxmin[0].lat = VIK_LATLON(&fixme)->lat;
2153     if ( VIK_LATLON(&fixme)->lat < maxmin[1].lat || maxmin[1].lat == 0.0 )
2154       maxmin[1].lat = VIK_LATLON(&fixme)->lat;
2155     if ( VIK_LATLON(&fixme)->lon > maxmin[0].lon || maxmin[0].lon == 0.0 )
2156       maxmin[0].lon = VIK_LATLON(&fixme)->lon;
2157     if ( VIK_LATLON(&fixme)->lon < maxmin[1].lon || maxmin[1].lon == 0.0 )
2158       maxmin[1].lon = VIK_LATLON(&fixme)->lon;
2159     tr = tr->next;
2160   }
2161 }
2162
2163 static void trw_layer_find_maxmin (VikTrwLayer *vtl, struct LatLon maxmin[2])
2164 {
2165   struct LatLon wpt_maxmin[2] = { {0.0,0.0}, {0.0,0.0} };
2166   struct LatLon trk_maxmin[2] = { {0.0,0.0}, {0.0,0.0} };
2167   
2168   g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_find_maxmin_waypoints, wpt_maxmin );
2169   g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_find_maxmin_tracks, trk_maxmin );
2170   if ((wpt_maxmin[0].lat != 0.0 && wpt_maxmin[0].lat > trk_maxmin[0].lat) || trk_maxmin[0].lat == 0.0) {
2171     maxmin[0].lat = wpt_maxmin[0].lat;
2172   }
2173   else {
2174     maxmin[0].lat = trk_maxmin[0].lat;
2175   }
2176   if ((wpt_maxmin[0].lon != 0.0 && wpt_maxmin[0].lon > trk_maxmin[0].lon) || trk_maxmin[0].lon == 0.0) {
2177     maxmin[0].lon = wpt_maxmin[0].lon;
2178   }
2179   else {
2180     maxmin[0].lon = trk_maxmin[0].lon;
2181   }
2182   if ((wpt_maxmin[1].lat != 0.0 && wpt_maxmin[1].lat < trk_maxmin[1].lat) || trk_maxmin[1].lat == 0.0) {
2183     maxmin[1].lat = wpt_maxmin[1].lat;
2184   }
2185   else {
2186     maxmin[1].lat = trk_maxmin[1].lat;
2187   }
2188   if ((wpt_maxmin[1].lon != 0.0 && wpt_maxmin[1].lon < trk_maxmin[1].lon) || trk_maxmin[1].lon == 0.0) {
2189     maxmin[1].lon = wpt_maxmin[1].lon;
2190   }
2191   else {
2192     maxmin[1].lon = trk_maxmin[1].lon;
2193   }
2194 }
2195
2196 gboolean vik_trw_layer_find_center ( VikTrwLayer *vtl, VikCoord *dest )
2197 {
2198   /* 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... */
2199   struct LatLon maxmin[2] = { {0.0,0.0}, {0.0,0.0} };
2200   trw_layer_find_maxmin (vtl, maxmin);
2201   if (maxmin[0].lat == 0.0 && maxmin[0].lon == 0.0 && maxmin[1].lat == 0.0 && maxmin[1].lon == 0.0)
2202     return FALSE;
2203   else
2204   {
2205     struct LatLon average = { (maxmin[0].lat+maxmin[1].lat)/2, (maxmin[0].lon+maxmin[1].lon)/2 };
2206     vik_coord_load_from_latlon ( dest, vtl->coord_mode, &average );
2207     return TRUE;
2208   }
2209 }
2210
2211 static void trw_layer_centerize ( gpointer layer_and_vlp[2] )
2212 {
2213   VikCoord coord;
2214   if ( vik_trw_layer_find_center ( VIK_TRW_LAYER(layer_and_vlp[0]), &coord ) )
2215     goto_coord ( layer_and_vlp[1], NULL, NULL, &coord );
2216   else
2217     a_dialog_info_msg ( VIK_GTK_WINDOW_FROM_LAYER(layer_and_vlp[0]), _("This layer has no waypoints or trackpoints.") );
2218 }
2219
2220 static void trw_layer_zoom_to_show_latlons ( VikTrwLayer *vtl, VikViewport *vvp, struct LatLon maxmin[2] )
2221 {
2222   /* First set the center [in case previously viewing from elsewhere] */
2223   /* Then loop through zoom levels until provided positions are in view */
2224   /* This method is not particularly fast - but should work well enough */
2225   struct LatLon average = { (maxmin[0].lat+maxmin[1].lat)/2, (maxmin[0].lon+maxmin[1].lon)/2 };
2226   VikCoord coord;
2227   vik_coord_load_from_latlon ( &coord, vtl->coord_mode, &average );
2228   vik_viewport_set_center_coord ( vvp, &coord );
2229
2230   /* Convert into definite 'smallest' and 'largest' positions */
2231   struct LatLon minmin;
2232   if ( maxmin[0].lat < maxmin[1].lat )
2233     minmin.lat = maxmin[0].lat;
2234   else
2235     minmin.lat = maxmin[1].lat;
2236
2237   struct LatLon maxmax;
2238   if ( maxmin[0].lon > maxmin[1].lon )
2239     maxmax.lon = maxmin[0].lon;
2240   else
2241     maxmax.lon = maxmin[1].lon;
2242
2243   /* Never zoom in too far - generally not that useful, as too close ! */
2244   /* Always recalculate the 'best' zoom level */
2245   gdouble zoom = 1.0;
2246   vik_viewport_set_zoom ( vvp, zoom );
2247
2248   gdouble min_lat, max_lat, min_lon, max_lon;
2249   /* Should only be a maximum of about 18 iterations from min to max zoom levels */
2250   while ( zoom <= VIK_VIEWPORT_MAX_ZOOM ) {
2251     vik_viewport_get_min_max_lat_lon ( vvp, &min_lat, &max_lat, &min_lon, &max_lon );
2252     /* NB I think the logic used in this test to determine if the bounds is within view
2253        fails if track goes across 180 degrees longitude.
2254        Hopefully that situation is not too common...
2255        Mind you viking doesn't really do edge locations to well anyway */
2256     if ( min_lat < minmin.lat &&
2257          max_lat > minmin.lat &&
2258          min_lon < maxmax.lon &&
2259          max_lon > maxmax.lon )
2260       /* Found within zoom level */
2261       break;
2262
2263     /* Try next */
2264     zoom = zoom * 2;
2265     vik_viewport_set_zoom ( vvp, zoom );
2266   }
2267 }
2268
2269 gboolean vik_trw_layer_auto_set_view ( VikTrwLayer *vtl, VikViewport *vvp )
2270 {
2271   /* TODO: what if there's only one waypoint @ 0,0, it will think nothing found. */
2272   struct LatLon maxmin[2] = { {0.0,0.0}, {0.0,0.0} };
2273   trw_layer_find_maxmin (vtl, maxmin);
2274   if (maxmin[0].lat == 0.0 && maxmin[0].lon == 0.0 && maxmin[1].lat == 0.0 && maxmin[1].lon == 0.0)
2275     return FALSE;
2276   else {
2277     trw_layer_zoom_to_show_latlons ( vtl, vvp, maxmin );
2278     return TRUE;
2279   }
2280 }
2281
2282 static void trw_layer_auto_view ( gpointer layer_and_vlp[2] )
2283 {
2284   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])) ) ) {
2285     vik_layers_panel_emit_update ( VIK_LAYERS_PANEL(layer_and_vlp[1]) );
2286   }
2287   else
2288     a_dialog_info_msg ( VIK_GTK_WINDOW_FROM_LAYER(layer_and_vlp[0]), _("This layer has no waypoints or trackpoints.") );
2289 }
2290
2291 static void trw_layer_export ( gpointer layer_and_vlp[2], const gchar *title, const gchar* default_name, VikTrack* trk, guint file_type )
2292 {
2293   GtkWidget *file_selector;
2294   const gchar *fn;
2295   gboolean failed = FALSE;
2296   file_selector = gtk_file_chooser_dialog_new (title,
2297                                                NULL,
2298                                                GTK_FILE_CHOOSER_ACTION_SAVE,
2299                                                GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
2300                                                GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT,
2301                                                NULL);
2302   gtk_file_chooser_set_current_name (GTK_FILE_CHOOSER(file_selector), default_name);
2303
2304   while ( gtk_dialog_run ( GTK_DIALOG(file_selector) ) == GTK_RESPONSE_ACCEPT )
2305   {
2306     fn = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER(file_selector) );
2307     if ( g_file_test ( fn, G_FILE_TEST_EXISTS ) == FALSE )
2308     {
2309       gtk_widget_hide ( file_selector );
2310       failed = ! a_file_export ( VIK_TRW_LAYER(layer_and_vlp[0]), fn, file_type, trk );
2311       break;
2312     }
2313     else
2314     {
2315       if ( a_dialog_yes_or_no ( GTK_WINDOW(file_selector), _("The file \"%s\" exists, do you wish to overwrite it?"), a_file_basename ( fn ) ) )
2316       {
2317         gtk_widget_hide ( file_selector );
2318         failed = ! a_file_export ( VIK_TRW_LAYER(layer_and_vlp[0]), fn, file_type, trk );
2319         break;
2320       }
2321     }
2322   }
2323   gtk_widget_destroy ( file_selector );
2324   if ( failed )
2325     a_dialog_error_msg ( VIK_GTK_WINDOW_FROM_LAYER(layer_and_vlp[0]), _("The filename you requested could not be opened for writing.") );
2326 }
2327
2328 static void trw_layer_export_gpspoint ( gpointer layer_and_vlp[2] )
2329 {
2330   trw_layer_export ( layer_and_vlp, _("Export Layer"), vik_layer_get_name(VIK_LAYER(layer_and_vlp[0])), NULL, FILE_TYPE_GPSPOINT );
2331 }
2332
2333 static void trw_layer_export_gpsmapper ( gpointer layer_and_vlp[2] )
2334 {
2335   trw_layer_export ( layer_and_vlp, _("Export Layer"), vik_layer_get_name(VIK_LAYER(layer_and_vlp[0])), NULL, FILE_TYPE_GPSMAPPER );
2336 }
2337
2338 static void trw_layer_export_gpx ( gpointer layer_and_vlp[2] )
2339 {
2340   /* Auto append '.gpx' to track name (providing it's not already there) for the default filename */
2341   gchar *auto_save_name = g_strdup ( vik_layer_get_name(VIK_LAYER(layer_and_vlp[0])) );
2342   if ( ! check_file_ext ( auto_save_name, ".gpx" ) )
2343     auto_save_name = g_strconcat ( auto_save_name, ".gpx", NULL );
2344
2345   trw_layer_export ( layer_and_vlp, _("Export Layer"), auto_save_name, NULL, FILE_TYPE_GPX );
2346
2347   g_free ( auto_save_name );
2348 }
2349
2350 static void trw_layer_export_kml ( gpointer layer_and_vlp[2] )
2351 {
2352   /* Auto append '.kml' to the name (providing it's not already there) for the default filename */
2353   gchar *auto_save_name = g_strdup ( vik_layer_get_name(VIK_LAYER(layer_and_vlp[0])) );
2354   if ( ! check_file_ext ( auto_save_name, ".kml" ) )
2355     auto_save_name = g_strconcat ( auto_save_name, ".kml", NULL );
2356
2357   trw_layer_export ( layer_and_vlp, _("Export Layer"), auto_save_name, NULL, FILE_TYPE_KML );
2358
2359   g_free ( auto_save_name );
2360 }
2361
2362 /**
2363  * Convert the given TRW layer into a temporary GPX file and open it with the specified program
2364  *
2365  */
2366 static void trw_layer_export_external_gpx ( gpointer layer_and_vlp[2], const gchar* external_program )
2367 {
2368   gchar *name_used = NULL;
2369   int fd;
2370
2371   if ((fd = g_file_open_tmp("tmp-viking.XXXXXX.gpx", &name_used, NULL)) >= 0) {
2372     gboolean failed = ! a_file_export ( VIK_TRW_LAYER(layer_and_vlp[0]), name_used, FILE_TYPE_GPX, NULL);
2373     if (failed) {
2374       a_dialog_error_msg (VIK_GTK_WINDOW_FROM_LAYER(layer_and_vlp[0]), _("Could not create temporary file for export.") );
2375     }
2376     else {
2377       GError *err = NULL;
2378       gchar *quoted_file = g_shell_quote ( name_used );
2379       gchar *cmd = g_strdup_printf ( "%s %s", external_program, quoted_file );
2380       g_free ( quoted_file );
2381       if ( ! g_spawn_command_line_async ( cmd, &err ) )
2382         {
2383           a_dialog_error_msg_extra ( VIK_GTK_WINDOW_FROM_LAYER( layer_and_vlp[0]), _("Could not launch %s."), external_program );
2384           g_error_free ( err );
2385         }
2386       g_free ( cmd );
2387     }
2388     // Note ATM the 'temporary' file is not deleted, as loading via another program is not instantaneous
2389     //g_remove ( name_used );
2390     // Perhaps should be deleted when the program ends?
2391     // For now leave it to the user to delete it / use system temp cleanup methods.
2392     g_free ( name_used );
2393   }
2394 }
2395
2396 static void trw_layer_export_external_gpx_1 ( gpointer layer_and_vlp[2] )
2397 {
2398   trw_layer_export_external_gpx ( layer_and_vlp, a_vik_get_external_gpx_program_1() );
2399 }
2400
2401 static void trw_layer_export_external_gpx_2 ( gpointer layer_and_vlp[2] )
2402 {
2403   trw_layer_export_external_gpx ( layer_and_vlp, a_vik_get_external_gpx_program_2() );
2404 }
2405
2406 static void trw_layer_export_gpx_track ( gpointer pass_along[6] )
2407 {
2408   gpointer layer_and_vlp[2];
2409   layer_and_vlp[0] = pass_along[0];
2410   layer_and_vlp[1] = pass_along[1];
2411   VikTrack *trk = g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, pass_along[3] );
2412
2413   if ( !trk || !trk->name )
2414     return;
2415
2416   /* Auto append '.gpx' to track name (providing it's not already there) for the default filename */
2417   gchar *auto_save_name = g_strdup ( trk->name );
2418   if ( ! check_file_ext ( auto_save_name, ".gpx" ) )
2419     auto_save_name = g_strconcat ( auto_save_name, ".gpx", NULL );
2420
2421   trw_layer_export ( layer_and_vlp, _("Export Track as GPX"), auto_save_name, trk, FILE_TYPE_GPX );
2422
2423   g_free ( auto_save_name );
2424 }
2425
2426 typedef struct {
2427   VikWaypoint *wp; // input
2428   gpointer uuid;   // output
2429 } wpu_udata;
2430
2431 static gboolean trw_layer_waypoint_find_uuid ( const gpointer id, const VikWaypoint *wp, gpointer udata )
2432 {
2433   wpu_udata *user_data = udata;
2434   if ( wp == user_data->wp ) {
2435     user_data->uuid = id;
2436     return TRUE;
2437   }
2438   return FALSE;
2439 }
2440
2441 static void trw_layer_goto_wp ( gpointer layer_and_vlp[2] )
2442 {
2443   GtkWidget *dia = gtk_dialog_new_with_buttons (_("Find"),
2444                                                  VIK_GTK_WINDOW_FROM_LAYER(layer_and_vlp[0]),
2445                                                  GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
2446                                                  GTK_STOCK_CANCEL,
2447                                                  GTK_RESPONSE_REJECT,
2448                                                  GTK_STOCK_OK,
2449                                                  GTK_RESPONSE_ACCEPT,
2450                                                  NULL);
2451
2452   GtkWidget *label, *entry;
2453   label = gtk_label_new(_("Waypoint Name:"));
2454   entry = gtk_entry_new();
2455
2456   gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dia)->vbox), label, FALSE, FALSE, 0);
2457   gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dia)->vbox), entry, FALSE, FALSE, 0);
2458   gtk_widget_show_all ( label );
2459   gtk_widget_show_all ( entry );
2460
2461   gtk_dialog_set_default_response ( GTK_DIALOG(dia), GTK_RESPONSE_ACCEPT );
2462
2463   while ( gtk_dialog_run ( GTK_DIALOG(dia) ) == GTK_RESPONSE_ACCEPT )
2464   {
2465     gchar *name = g_strdup(gtk_entry_get_text(GTK_ENTRY(entry)));
2466     // Find *first* wp with the given name
2467     VikWaypoint *wp = vik_trw_layer_get_waypoint ( VIK_TRW_LAYER(layer_and_vlp[0]), name );
2468
2469     if ( !wp )
2470       a_dialog_error_msg ( VIK_GTK_WINDOW_FROM_LAYER(layer_and_vlp[0]), _("Waypoint not found in this layer.") );
2471     else
2472     {
2473       vik_viewport_set_center_coord ( vik_layers_panel_get_viewport(VIK_LAYERS_PANEL(layer_and_vlp[1])), &(wp->coord) );
2474       vik_layers_panel_emit_update ( VIK_LAYERS_PANEL(layer_and_vlp[1]) );
2475
2476       // Find and select on the side panel
2477       wpu_udata udata;
2478       udata.wp   = wp;
2479       udata.uuid = NULL;
2480
2481       // Hmmm, want key of it
2482       gpointer *wpf = g_hash_table_find ( VIK_TRW_LAYER(layer_and_vlp[0])->waypoints, (GHRFunc) trw_layer_waypoint_find_uuid, (gpointer) &udata );
2483
2484       if ( wpf && udata.uuid ) {
2485         GtkTreeIter *it = g_hash_table_lookup ( VIK_TRW_LAYER(layer_and_vlp[0])->waypoints_iters, udata.uuid );
2486         vik_treeview_select_iter ( VIK_LAYER(layer_and_vlp[0])->vt, it, TRUE );
2487       }
2488
2489       break;
2490     }
2491
2492     g_free ( name );
2493
2494   }
2495   gtk_widget_destroy ( dia );
2496 }
2497
2498 gboolean vik_trw_layer_new_waypoint ( VikTrwLayer *vtl, GtkWindow *w, const VikCoord *def_coord )
2499 {
2500   gchar *default_name = highest_wp_number_get(vtl);
2501   VikWaypoint *wp = vik_waypoint_new();
2502   gchar *returned_name;
2503   gboolean updated;
2504   wp->coord = *def_coord;
2505   
2506   // Attempt to auto set height if DEM data is available
2507   gint16 elev = a_dems_get_elev_by_coord ( &(wp->coord), VIK_DEM_INTERPOL_BEST );
2508   if ( elev != VIK_DEM_INVALID_ELEVATION )
2509     wp->altitude = (gdouble)elev;
2510
2511   returned_name = a_dialog_waypoint ( w, default_name, wp, vtl->coord_mode, TRUE, &updated );
2512
2513   if ( returned_name )
2514   {
2515     wp->visible = TRUE;
2516     vik_trw_layer_add_waypoint ( vtl, returned_name, wp );
2517     g_free (default_name);
2518     g_free (returned_name);
2519     return TRUE;
2520   }
2521   g_free (default_name);
2522   vik_waypoint_free(wp);
2523   return FALSE;
2524 }
2525
2526 static void trw_layer_new_wikipedia_wp_viewport ( gpointer lav[2] )
2527 {
2528   VikCoord one, two;
2529   struct LatLon one_ll, two_ll;
2530   struct LatLon maxmin[2] = { {0.0,0.0}, {0.0,0.0} };
2531
2532   VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
2533   VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
2534   VikWindow *vw = (VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl));
2535   VikViewport *vvp =  vik_window_viewport(vw);
2536   vik_viewport_screen_to_coord ( vvp, 0, 0, &one);
2537   vik_viewport_screen_to_coord ( vvp, vik_viewport_get_width(vvp), vik_viewport_get_height(vvp), &two);
2538   vik_coord_to_latlon(&one, &one_ll);
2539   vik_coord_to_latlon(&two, &two_ll);
2540   if (one_ll.lat > two_ll.lat) {
2541     maxmin[0].lat = one_ll.lat;
2542     maxmin[1].lat = two_ll.lat;
2543   }
2544   else {
2545     maxmin[0].lat = two_ll.lat;
2546     maxmin[1].lat = one_ll.lat;
2547   }
2548   if (one_ll.lon > two_ll.lon) {
2549     maxmin[0].lon = one_ll.lon;
2550     maxmin[1].lon = two_ll.lon;
2551   }
2552   else {
2553     maxmin[0].lon = two_ll.lon;
2554     maxmin[1].lon = one_ll.lon;
2555   }
2556   a_geonames_wikipedia_box((VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl)), vtl, vlp, maxmin);
2557 }
2558
2559 static void trw_layer_new_wikipedia_wp_layer ( gpointer lav[2] )
2560 {
2561   VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
2562   VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
2563   struct LatLon maxmin[2] = { {0.0,0.0}, {0.0,0.0} };
2564   
2565   trw_layer_find_maxmin (vtl, maxmin);
2566   a_geonames_wikipedia_box((VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl)), vtl, vlp, maxmin);
2567 }
2568
2569 #ifdef VIK_CONFIG_GEOTAG
2570 static void trw_layer_geotagging_waypoint_mtime_keep ( gpointer pass_along[6] )
2571 {
2572   VikWaypoint *wp = g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->waypoints, pass_along[3] );
2573   if ( wp )
2574     // Update directly - not changing the mtime
2575     a_geotag_write_exif_gps ( wp->image, wp->coord, wp->altitude, TRUE );
2576 }
2577
2578 static void trw_layer_geotagging_waypoint_mtime_update ( gpointer pass_along[6] )
2579 {
2580   VikWaypoint *wp = g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->waypoints, pass_along[3] );
2581   if ( wp )
2582     // Update directly
2583     a_geotag_write_exif_gps ( wp->image, wp->coord, wp->altitude, FALSE );
2584 }
2585
2586 /*
2587  * Use code in separate file for this feature as reasonably complex
2588  */
2589 static void trw_layer_geotagging_track ( gpointer pass_along[6] )
2590 {
2591   VikTrwLayer *vtl = VIK_TRW_LAYER(pass_along[0]);
2592   VikTrack *track = g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, pass_along[3] );
2593   // Unset so can be reverified later if necessary
2594   vtl->has_verified_thumbnails = FALSE;
2595
2596   trw_layer_geotag_dialog ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
2597                             vtl,
2598                             track,
2599                             pass_along[3] );
2600 }
2601
2602 static void trw_layer_geotagging ( gpointer lav[2] )
2603 {
2604   VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
2605   // Unset so can be reverified later if necessary
2606   vtl->has_verified_thumbnails = FALSE;
2607
2608   trw_layer_geotag_dialog ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
2609                             vtl,
2610                             NULL,
2611                             NULL);
2612 }
2613 #endif
2614
2615 // 'Acquires' - Same as in File Menu -> Acquire - applies into the selected TRW Layer //
2616
2617 /*
2618  * Acquire into this TRW Layer straight from GPS Device
2619  */
2620 static void trw_layer_acquire_gps_cb ( gpointer lav[2] )
2621 {
2622   VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
2623   VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
2624   VikWindow *vw = (VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl));
2625   VikViewport *vvp =  vik_window_viewport(vw);
2626
2627   vik_datasource_gps_interface.mode = VIK_DATASOURCE_ADDTOLAYER;
2628   a_acquire ( vw, vlp, vvp, &vik_datasource_gps_interface );
2629 }
2630
2631 /*
2632  * Acquire into this TRW Layer from Google Directions
2633  */
2634 static void trw_layer_acquire_google_cb ( gpointer lav[2] )
2635 {
2636   VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
2637   VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
2638   VikWindow *vw = (VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl));
2639   VikViewport *vvp =  vik_window_viewport(vw);
2640
2641   a_acquire ( vw, vlp, vvp, &vik_datasource_google_interface );
2642 }
2643
2644 #ifdef VIK_CONFIG_OPENSTREETMAP
2645 /*
2646  * Acquire into this TRW Layer from OSM
2647  */
2648 static void trw_layer_acquire_osm_cb ( gpointer lav[2] )
2649 {
2650   VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
2651   VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
2652   VikWindow *vw = (VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl));
2653   VikViewport *vvp =  vik_window_viewport(vw);
2654
2655   a_acquire ( vw, vlp, vvp, &vik_datasource_osm_interface );
2656 }
2657 #endif
2658
2659 #ifdef VIK_CONFIG_GEOCACHES
2660 /*
2661  * Acquire into this TRW Layer from Geocaching.com
2662  */
2663 static void trw_layer_acquire_geocache_cb ( gpointer lav[2] )
2664 {
2665   VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
2666   VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
2667   VikWindow *vw = (VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl));
2668   VikViewport *vvp =  vik_window_viewport(vw);
2669
2670   a_acquire ( vw, vlp, vvp, &vik_datasource_gc_interface );
2671 }
2672 #endif
2673
2674 #ifdef VIK_CONFIG_GEOTAG
2675 /*
2676  * Acquire into this TRW Layer from images
2677  */
2678 static void trw_layer_acquire_geotagged_cb ( gpointer lav[2] )
2679 {
2680   VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
2681   VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
2682   VikWindow *vw = (VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl));
2683   VikViewport *vvp =  vik_window_viewport(vw);
2684
2685   vik_datasource_geotag_interface.mode = VIK_DATASOURCE_ADDTOLAYER;
2686   a_acquire ( vw, vlp, vvp, &vik_datasource_geotag_interface );
2687
2688   // Reverify thumbnails as they may have changed
2689   vtl->has_verified_thumbnails = FALSE;
2690   trw_layer_verify_thumbnails ( vtl, NULL );
2691 }
2692 #endif
2693
2694 static void trw_layer_new_wp ( gpointer lav[2] )
2695 {
2696   VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
2697   VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
2698   /* TODO longone: okay, if layer above (aggregate) is invisible but vtl->visible is true, this redraws for no reason.
2699      instead return true if you want to update. */
2700   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 )
2701     vik_layers_panel_emit_update ( vlp );
2702 }
2703
2704 static void trw_layer_auto_tracks_view ( gpointer lav[2] )
2705 {
2706   VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
2707   VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
2708
2709   if ( g_hash_table_size (vtl->tracks) > 0 ) {
2710     struct LatLon maxmin[2] = { {0,0}, {0,0} };
2711     g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_find_maxmin_tracks, maxmin );
2712     trw_layer_zoom_to_show_latlons ( vtl, vik_layers_panel_get_viewport (vlp), maxmin );
2713     vik_layers_panel_emit_update ( vlp );
2714   }
2715 }
2716
2717 static void trw_layer_single_waypoint_jump ( const gchar *name, const VikWaypoint *wp, gpointer vvp )
2718 {
2719   /* NB do not care if wp is visible or not */
2720   vik_viewport_set_center_coord ( VIK_VIEWPORT(vvp), &(wp->coord) );
2721 }
2722
2723 static void trw_layer_auto_waypoints_view ( gpointer lav[2] )
2724 {
2725   VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
2726   VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
2727
2728   /* Only 1 waypoint - jump straight to it */
2729   if ( g_hash_table_size (vtl->waypoints) == 1 ) {
2730     VikViewport *vvp = vik_layers_panel_get_viewport (vlp);
2731     g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_single_waypoint_jump, (gpointer) vvp );
2732   }
2733   /* If at least 2 waypoints - find center and then zoom to fit */
2734   else if ( g_hash_table_size (vtl->waypoints) > 1 )
2735   {
2736     struct LatLon maxmin[2] = { {0,0}, {0,0} };
2737     g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_find_maxmin_waypoints, maxmin );
2738     trw_layer_zoom_to_show_latlons ( vtl, vik_layers_panel_get_viewport (vlp), maxmin );
2739   }
2740
2741   vik_layers_panel_emit_update ( vlp );
2742 }
2743
2744 static void trw_layer_add_menu_items ( VikTrwLayer *vtl, GtkMenu *menu, gpointer vlp )
2745 {
2746   static gpointer pass_along[2];
2747   GtkWidget *item;
2748   GtkWidget *export_submenu;
2749   pass_along[0] = vtl;
2750   pass_along[1] = vlp;
2751
2752   item = gtk_menu_item_new();
2753   gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
2754   gtk_widget_show ( item );
2755
2756   /* Now with icons */
2757   item = gtk_image_menu_item_new_with_mnemonic ( _("_View Layer") );
2758   gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ZOOM_FIT, GTK_ICON_SIZE_MENU) );
2759   g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_view), pass_along );
2760   gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
2761   gtk_widget_show ( item );
2762
2763   item = gtk_menu_item_new_with_mnemonic ( _("View All Trac_ks") );
2764   g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_tracks_view), pass_along );
2765   gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
2766   gtk_widget_show ( item );
2767
2768   item = gtk_menu_item_new_with_mnemonic ( _("V_iew All Waypoints") );
2769   g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_waypoints_view), pass_along );
2770   gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
2771   gtk_widget_show ( item );
2772
2773   item = gtk_image_menu_item_new_with_mnemonic ( _("_Goto Center of Layer") );
2774   gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_JUMP_TO, GTK_ICON_SIZE_MENU) );
2775   g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_centerize), pass_along );
2776   gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
2777   gtk_widget_show ( item );
2778
2779   item = gtk_menu_item_new_with_mnemonic ( _("Goto _Waypoint...") );
2780   g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_wp), pass_along );
2781   gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
2782   gtk_widget_show ( item );
2783
2784   export_submenu = gtk_menu_new ();
2785   item = gtk_image_menu_item_new_with_mnemonic ( _("_Export Layer") );
2786   gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_HARDDISK, GTK_ICON_SIZE_MENU) );
2787   gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
2788   gtk_widget_show ( item );
2789   gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), export_submenu );
2790   
2791   item = gtk_menu_item_new_with_mnemonic ( _("Export as GPS_Point...") );
2792   g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_gpspoint), pass_along );
2793   gtk_menu_shell_append (GTK_MENU_SHELL (export_submenu), item);
2794   gtk_widget_show ( item );
2795
2796   item = gtk_menu_item_new_with_mnemonic ( _("Export as GPS_Mapper...") );
2797   g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_gpsmapper), pass_along );
2798   gtk_menu_shell_append (GTK_MENU_SHELL (export_submenu), item);
2799   gtk_widget_show ( item );
2800
2801   item = gtk_menu_item_new_with_mnemonic ( _("Export as _GPX...") );
2802   g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_gpx), pass_along );
2803   gtk_menu_shell_append (GTK_MENU_SHELL (export_submenu), item);
2804   gtk_widget_show ( item );
2805
2806   item = gtk_menu_item_new_with_mnemonic ( _("Export as _KML...") );
2807   g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_kml), pass_along );
2808   gtk_menu_shell_append (GTK_MENU_SHELL (export_submenu), item);
2809   gtk_widget_show ( item );
2810
2811   gchar* external1 = g_strconcat ( _("Open with External Program_1: "), a_vik_get_external_gpx_program_1(), NULL );
2812   item = gtk_menu_item_new_with_mnemonic ( external1 );
2813   g_free ( external1 );
2814   g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_external_gpx_1), pass_along );
2815   gtk_menu_shell_append (GTK_MENU_SHELL (export_submenu), item);
2816   gtk_widget_show ( item );
2817
2818   gchar* external2 = g_strconcat ( _("Open with External Program_2: "), a_vik_get_external_gpx_program_2(), NULL );
2819   item = gtk_menu_item_new_with_mnemonic ( external2 );
2820   g_free ( external2 );
2821   g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_external_gpx_2), pass_along );
2822   gtk_menu_shell_append (GTK_MENU_SHELL (export_submenu), item);
2823   gtk_widget_show ( item );
2824
2825   item = gtk_image_menu_item_new_with_mnemonic ( _("_New Waypoint...") );
2826   gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NEW, GTK_ICON_SIZE_MENU) );
2827   g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_wp), pass_along );
2828   gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
2829   gtk_widget_show ( item );
2830
2831 #ifdef VIK_CONFIG_GEONAMES
2832   GtkWidget *wikipedia_submenu = gtk_menu_new();
2833   item = gtk_image_menu_item_new_with_mnemonic ( _("_Add Wikipedia Waypoints") );
2834   gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ADD, GTK_ICON_SIZE_MENU) );
2835   gtk_menu_shell_append(GTK_MENU_SHELL (menu), item);
2836   gtk_widget_show(item);
2837   gtk_menu_item_set_submenu(GTK_MENU_ITEM(item), wikipedia_submenu);
2838
2839   item = gtk_image_menu_item_new_with_mnemonic ( _("Within _Layer Bounds") );
2840   gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ZOOM_FIT, GTK_ICON_SIZE_MENU) );
2841   g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_wikipedia_wp_layer), pass_along );
2842   gtk_menu_shell_append (GTK_MENU_SHELL (wikipedia_submenu), item);
2843   gtk_widget_show ( item );
2844
2845   item = gtk_image_menu_item_new_with_mnemonic ( _("Within _Current View") );
2846   gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ZOOM_100, GTK_ICON_SIZE_MENU) );
2847   g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_wikipedia_wp_viewport), pass_along );
2848   gtk_menu_shell_append (GTK_MENU_SHELL (wikipedia_submenu), item);
2849   gtk_widget_show ( item );
2850 #endif
2851
2852 #ifdef VIK_CONFIG_GEOTAG
2853   item = gtk_menu_item_new_with_mnemonic ( _("Geotag _Images...") );
2854   g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_geotagging), pass_along );
2855   gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
2856   gtk_widget_show ( item );
2857 #endif
2858
2859   GtkWidget *acquire_submenu = gtk_menu_new ();
2860   item = gtk_image_menu_item_new_with_mnemonic ( _("Ac_quire") );
2861   gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_DOWN, GTK_ICON_SIZE_MENU) );
2862   gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
2863   gtk_widget_show ( item );
2864   gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), acquire_submenu );
2865   
2866   item = gtk_menu_item_new_with_mnemonic ( _("From _GPS...") );
2867   g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_acquire_gps_cb), pass_along );
2868   gtk_menu_shell_append (GTK_MENU_SHELL (acquire_submenu), item);
2869   gtk_widget_show ( item );
2870
2871   item = gtk_menu_item_new_with_mnemonic ( _("From G_oogle Directions...") );
2872   g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_acquire_google_cb), pass_along );
2873   gtk_menu_shell_append (GTK_MENU_SHELL (acquire_submenu), item);
2874   gtk_widget_show ( item );
2875
2876 #ifdef VIK_CONFIG_OPENSTREETMAP
2877   item = gtk_menu_item_new_with_mnemonic ( _("From _OSM Traces...") );
2878   g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_acquire_osm_cb), pass_along );
2879   gtk_menu_shell_append (GTK_MENU_SHELL (acquire_submenu), item);
2880   gtk_widget_show ( item );
2881 #endif
2882
2883 #ifdef VIK_CONFIG_GEOCACHES
2884   item = gtk_menu_item_new_with_mnemonic ( _("From Geo_caching...") );
2885   g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_acquire_geocache_cb), pass_along );
2886   gtk_menu_shell_append (GTK_MENU_SHELL (acquire_submenu), item);
2887   gtk_widget_show ( item );
2888 #endif
2889
2890 #ifdef VIK_CONFIG_GEOTAG
2891   item = gtk_menu_item_new_with_mnemonic ( _("From Geotagged _Images...") );
2892   g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_acquire_geotagged_cb), pass_along );
2893   gtk_menu_shell_append (GTK_MENU_SHELL (acquire_submenu), item);
2894   gtk_widget_show ( item );
2895 #endif
2896
2897 #ifdef VIK_CONFIG_OPENSTREETMAP 
2898   item = gtk_image_menu_item_new_with_mnemonic ( _("Upload to _OSM...") );
2899   gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_UP, GTK_ICON_SIZE_MENU) );
2900   g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(osm_traces_upload_cb), pass_along );
2901   gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
2902   gtk_widget_show ( item );
2903 #endif
2904
2905   GtkWidget *delete_submenu = gtk_menu_new ();
2906   item = gtk_image_menu_item_new_with_mnemonic ( _("De_lete") );
2907   gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU) );
2908   gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
2909   gtk_widget_show ( item );
2910   gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), delete_submenu );
2911   
2912   item = gtk_image_menu_item_new_with_mnemonic ( _("Delete All _Tracks") );
2913   gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU) );
2914   g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_all_tracks), pass_along );
2915   gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
2916   gtk_widget_show ( item );
2917   
2918   item = gtk_image_menu_item_new_with_mnemonic ( _("Delete Tracks _From Selection...") );
2919   gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
2920   g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_tracks_from_selection), pass_along );
2921   gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
2922   gtk_widget_show ( item );
2923   
2924   item = gtk_image_menu_item_new_with_mnemonic ( _("Delete All _Waypoints") );
2925   gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU) );
2926   g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_all_waypoints), pass_along );
2927   gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
2928   gtk_widget_show ( item );
2929   
2930   item = gtk_image_menu_item_new_with_mnemonic ( _("Delete Waypoints From _Selection...") );
2931   gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
2932   g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_waypoints_from_selection), pass_along );
2933   gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
2934   gtk_widget_show ( item );
2935   
2936   item = a_acquire_trwlayer_menu ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)), vlp,
2937                                    vik_layers_panel_get_viewport(VIK_LAYERS_PANEL(vlp)), vtl );
2938   if ( item ) {
2939     gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
2940     gtk_widget_show ( item );
2941   }  
2942
2943   item = a_acquire_trwlayer_track_menu ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)), vlp,
2944                                          vik_layers_panel_get_viewport(VIK_LAYERS_PANEL(vlp)), vtl );
2945   if ( item ) {
2946     gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
2947     gtk_widget_show ( item );
2948   }  
2949 }
2950
2951 // Fake Waypoint UUIDs vi simple increasing integer
2952 static guint wp_uuid = 0;
2953
2954 void vik_trw_layer_add_waypoint ( VikTrwLayer *vtl, gchar *name, VikWaypoint *wp )
2955 {
2956   wp_uuid++;
2957
2958   vik_waypoint_set_name (wp, name);
2959
2960   if ( VIK_LAYER(vtl)->realized )
2961   {
2962     GtkTreeIter *iter = g_malloc(sizeof(GtkTreeIter));
2963
2964     // Visibility column always needed for waypoints
2965 #ifdef VIK_CONFIG_ALPHABETIZED_TRW
2966     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, NULL, TRUE, TRUE );
2967 #else
2968     vik_treeview_add_sublayer ( VIK_LAYER(vtl)->vt, &(vtl->waypoints_iter), iter, name, vtl, GUINT_TO_POINTER(wp_uuid), VIK_TRW_LAYER_SUBLAYER_WAYPOINT, NULL, TRUE, TRUE );
2969 #endif
2970     // Actual setting of visibility dependent on the waypoint
2971     vik_treeview_item_set_visible ( VIK_LAYER(vtl)->vt, iter, wp->visible );
2972
2973     g_hash_table_insert ( vtl->waypoints_iters, GUINT_TO_POINTER(wp_uuid), iter );
2974   }
2975
2976   highest_wp_number_add_wp(vtl, name);
2977   g_hash_table_insert ( vtl->waypoints, GUINT_TO_POINTER(wp_uuid), wp );
2978  
2979 }
2980
2981 // Fake Track UUIDs vi simple increasing integer
2982 static guint tr_uuid = 0;
2983
2984 void vik_trw_layer_add_track ( VikTrwLayer *vtl, gchar *name, VikTrack *t )
2985 {
2986   tr_uuid++;
2987
2988   vik_track_set_name (t, name);
2989
2990   if ( VIK_LAYER(vtl)->realized )
2991   {
2992     GtkTreeIter *iter = g_malloc(sizeof(GtkTreeIter));
2993     // Visibility column always needed for tracks
2994 #ifdef VIK_CONFIG_ALPHABETIZED_TRW
2995     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 );
2996 #else
2997     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 );
2998 #endif
2999     // Actual setting of visibility dependent on the track
3000     vik_treeview_item_set_visible ( VIK_LAYER(vtl)->vt, iter, t->visible );
3001
3002     g_hash_table_insert ( vtl->tracks_iters, GUINT_TO_POINTER(tr_uuid), iter );
3003   }
3004
3005   g_hash_table_insert ( vtl->tracks, GUINT_TO_POINTER(tr_uuid), t );
3006  
3007 }
3008
3009 /* to be called whenever a track has been deleted or may have been changed. */
3010 void trw_layer_cancel_tps_of_track ( VikTrwLayer *vtl, VikTrack *trk )
3011 {
3012   if (vtl->current_tp_track == trk )
3013     trw_layer_cancel_current_tp ( vtl, FALSE );
3014   else if (vtl->last_tp_track == trk )
3015     trw_layer_cancel_last_tp ( vtl );
3016 }
3017         
3018 static gchar *get_new_unique_sublayer_name (VikTrwLayer *vtl, gint sublayer_type, const gchar *name)
3019 {
3020  gint i = 2;
3021  gchar *newname = g_strdup(name);
3022  while ((sublayer_type == VIK_TRW_LAYER_SUBLAYER_TRACK) ?
3023          (void *)vik_trw_layer_get_track(vtl, newname) : (void *)vik_trw_layer_get_waypoint(vtl, newname)) {
3024     gchar *new_newname = g_strdup_printf("%s#%d", name, i);
3025     g_free(newname);
3026     newname = new_newname;
3027     i++;
3028   }
3029   return newname;
3030 }
3031
3032 void vik_trw_layer_filein_add_waypoint ( VikTrwLayer *vtl, gchar *name, VikWaypoint *wp )
3033 {
3034   // No more uniqueness of name forced when loading from a file
3035   // This now makes this function a little redunant as we just flow the parameters through
3036   vik_trw_layer_add_waypoint ( vtl, name, wp );
3037 }
3038
3039 void vik_trw_layer_filein_add_track ( VikTrwLayer *vtl, gchar *name, VikTrack *tr )
3040 {
3041   if ( vtl->route_finder_append && vtl->route_finder_current_track ) {
3042     vik_track_remove_dup_points ( tr ); /* make "double point" track work to undo */
3043     vik_track_steal_and_append_trackpoints ( vtl->route_finder_current_track, tr );
3044     vik_track_free ( tr );
3045     vtl->route_finder_append = FALSE; /* this means we have added it */
3046   } else {
3047
3048     // No more uniqueness of name forced when loading from a file
3049     vik_trw_layer_add_track ( vtl, name, tr );
3050
3051     if ( vtl->route_finder_check_added_track ) {
3052       vik_track_remove_dup_points ( tr ); /* make "double point" track work to undo */
3053       vtl->route_finder_added_track = tr;
3054     }
3055   }
3056 }
3057
3058 static void trw_layer_enum_item ( gpointer id, GList **tr, GList **l )
3059 {
3060   *l = g_list_append(*l, id);
3061 }
3062
3063 /*
3064  * Move an item from one TRW layer to another TRW layer
3065  */
3066 static void trw_layer_move_item ( VikTrwLayer *vtl_src, VikTrwLayer *vtl_dest, gpointer id, gint type )
3067 {
3068   if (type == VIK_TRW_LAYER_SUBLAYER_TRACK) {
3069     VikTrack *trk = g_hash_table_lookup ( vtl_src->tracks, id );
3070
3071     gchar *newname = get_new_unique_sublayer_name(vtl_dest, type, trk->name);
3072
3073     VikTrack *trk2 = vik_track_copy ( trk );
3074     vik_trw_layer_add_track ( vtl_dest, newname, trk2 );
3075     vik_trw_layer_delete_track ( vtl_src, trk );
3076   }
3077
3078   if (type == VIK_TRW_LAYER_SUBLAYER_WAYPOINT) {
3079     VikWaypoint *wp = g_hash_table_lookup ( vtl_src->waypoints, id );
3080
3081     gchar *newname = get_new_unique_sublayer_name(vtl_dest, type, wp->name);
3082
3083     VikWaypoint *wp2 = vik_waypoint_copy ( wp );
3084     vik_trw_layer_add_waypoint ( vtl_dest, newname, wp2 );
3085     trw_layer_delete_waypoint ( vtl_src, wp );
3086   }
3087 }
3088
3089 static void trw_layer_drag_drop_request ( VikTrwLayer *vtl_src, VikTrwLayer *vtl_dest, GtkTreeIter *src_item_iter, GtkTreePath *dest_path )
3090 {
3091   VikTreeview *vt = VIK_LAYER(vtl_src)->vt;
3092   gint type = vik_treeview_item_get_data(vt, src_item_iter);
3093
3094   if (!vik_treeview_item_get_pointer(vt, src_item_iter)) {
3095     GList *items = NULL;
3096     GList *iter;
3097
3098     if (type==VIK_TRW_LAYER_SUBLAYER_TRACKS) {
3099       g_hash_table_foreach ( vtl_src->tracks, (GHFunc)trw_layer_enum_item, &items);
3100     } 
3101     if (type==VIK_TRW_LAYER_SUBLAYER_WAYPOINTS) {
3102       g_hash_table_foreach ( vtl_src->waypoints, (GHFunc)trw_layer_enum_item, &items);
3103     }    
3104       
3105     iter = items;
3106     while (iter) {
3107       if (type==VIK_TRW_LAYER_SUBLAYER_TRACKS) {
3108         trw_layer_move_item ( vtl_src, vtl_dest, iter->data, VIK_TRW_LAYER_SUBLAYER_TRACK);
3109       } else {
3110         trw_layer_move_item ( vtl_src, vtl_dest, iter->data, VIK_TRW_LAYER_SUBLAYER_WAYPOINT);
3111       }
3112       iter = iter->next;
3113     }
3114     if (items) 
3115       g_list_free(items);
3116   } else {
3117     gchar *name = vik_treeview_item_get_pointer(vt, src_item_iter);
3118     trw_layer_move_item(vtl_src, vtl_dest, name, type);
3119   }
3120 }
3121
3122 typedef struct {
3123   VikTrack *trk; // input
3124   gpointer uuid;   // output
3125 } trku_udata;
3126
3127 static gboolean trw_layer_track_find_uuid ( const gpointer id, const VikTrack *trk, gpointer udata )
3128 {
3129   trku_udata *user_data = udata;
3130   if ( trk == user_data->trk ) {
3131     user_data->uuid = id;
3132     return TRUE;
3133   }
3134   return FALSE;
3135 }
3136
3137 gboolean vik_trw_layer_delete_track ( VikTrwLayer *vtl, VikTrack *trk )
3138 {
3139   gboolean was_visible = FALSE;
3140
3141   if ( trk && trk->name ) {
3142
3143     if ( trk == vtl->current_track ) {
3144       vtl->current_track = NULL;
3145       vtl->current_tp_track = NULL;
3146       vtl->current_tp_id = NULL;
3147       vtl->moving_tp = FALSE;
3148     }
3149
3150     was_visible = trk->visible;
3151
3152     if ( trk == vtl->route_finder_current_track )
3153       vtl->route_finder_current_track = NULL;
3154
3155     if ( trk == vtl->route_finder_added_track )
3156       vtl->route_finder_added_track = NULL;
3157
3158     trku_udata udata;
3159     udata.trk  = trk;
3160     udata.uuid = NULL;
3161
3162     // Hmmm, want key of it
3163     gpointer *trkf = g_hash_table_find ( vtl->tracks, (GHRFunc) trw_layer_track_find_uuid, &udata );
3164
3165     if ( trkf && udata.uuid ) {
3166       /* could be current_tp, so we have to check */
3167       trw_layer_cancel_tps_of_track ( vtl, trk );
3168
3169       GtkTreeIter *it = g_hash_table_lookup ( vtl->tracks_iters, udata.uuid );
3170
3171       if ( it ) {
3172         vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, it );
3173         g_hash_table_remove ( vtl->tracks_iters, udata.uuid );
3174         g_hash_table_remove ( vtl->tracks, udata.uuid );
3175       }
3176     }
3177   }
3178   return was_visible;
3179 }
3180
3181 static gboolean trw_layer_delete_waypoint ( VikTrwLayer *vtl, VikWaypoint *wp )
3182 {
3183   gboolean was_visible = FALSE;
3184
3185   if ( wp && wp->name ) {
3186
3187     if ( wp == vtl->current_wp ) {
3188       vtl->current_wp = NULL;
3189       vtl->current_wp_id = NULL;
3190       vtl->moving_wp = FALSE;
3191     }
3192
3193     was_visible = wp->visible;
3194     
3195     wpu_udata udata;
3196     udata.wp   = wp;
3197     udata.uuid = NULL;
3198
3199     // Hmmm, want key of it
3200     gpointer *wpf = g_hash_table_find ( vtl->waypoints, (GHRFunc) trw_layer_waypoint_find_uuid, (gpointer) &udata );
3201
3202     if ( wpf && udata.uuid ) {
3203       GtkTreeIter *it = g_hash_table_lookup ( vtl->waypoints_iters, udata.uuid );
3204     
3205       if ( it ) {
3206         vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, it );
3207         g_hash_table_remove ( vtl->waypoints_iters, udata.uuid );
3208
3209         highest_wp_number_remove_wp(vtl, wp->name);
3210         g_hash_table_remove ( vtl->waypoints, udata.uuid ); // last because this frees the name
3211       }
3212     }
3213
3214   }
3215
3216   return was_visible;
3217 }
3218
3219 // Only for temporary use by trw_layer_delete_waypoint_by_name
3220 static gboolean trw_layer_waypoint_find_uuid_by_name ( const gpointer id, const VikWaypoint *wp, gpointer udata )
3221 {
3222   wpu_udata *user_data = udata;
3223   if ( ! strcmp ( wp->name, user_data->wp->name ) ) {
3224     user_data->uuid = id;
3225     return TRUE;
3226   }
3227   return FALSE;
3228 }
3229
3230 /*
3231  * Delete a waypoint by the given name
3232  * NOTE: ATM this will delete the first encountered Waypoint with the specified name
3233  *   as there be multiple waypoints with the same name
3234  */
3235 static gboolean trw_layer_delete_waypoint_by_name ( VikTrwLayer *vtl, const gchar *name )
3236 {
3237   wpu_udata udata;
3238   // Fake a waypoint with the given name
3239   udata.wp   = vik_waypoint_new ();
3240   vik_waypoint_set_name (udata.wp, name);
3241   // Currently only the name is used in this waypoint find function
3242   udata.uuid = NULL;
3243
3244   // Hmmm, want key of it
3245   gpointer *wpf = g_hash_table_find ( vtl->waypoints, (GHRFunc) trw_layer_waypoint_find_uuid_by_name, (gpointer) &udata );
3246
3247   vik_waypoint_free (udata.wp);
3248
3249   if ( wpf && udata.uuid )
3250     return trw_layer_delete_waypoint (vtl, g_hash_table_lookup ( vtl->waypoints, udata.uuid ));
3251   else
3252     return FALSE;
3253 }
3254
3255 typedef struct {
3256   VikTrack *trk; // input
3257   gpointer uuid; // output
3258 } tpu_udata;
3259
3260 // Only for temporary use by trw_layer_delete_track_by_name
3261 static gboolean trw_layer_track_find_uuid_by_name ( const gpointer id, const VikTrack *trk, gpointer udata )
3262 {
3263   tpu_udata *user_data = udata;
3264   if ( ! strcmp ( trk->name, user_data->trk->name ) ) {
3265     user_data->uuid = id;
3266     return TRUE;
3267   }
3268   return FALSE;
3269 }
3270
3271 /*
3272  * Delete a track by the given name
3273  * NOTE: ATM this will delete the first encountered Track with the specified name
3274  *   as there be multiple track with the same name
3275  */
3276 static gboolean trw_layer_delete_track_by_name ( VikTrwLayer *vtl, const gchar *name )
3277 {
3278   tpu_udata udata;
3279   // Fake a track with the given name
3280   udata.trk   = vik_track_new ();
3281   vik_track_set_name (udata.trk, name);
3282   // Currently only the name is used in this waypoint find function
3283   udata.uuid = NULL;
3284
3285   // Hmmm, want key of it
3286   gpointer *trkf = g_hash_table_find ( vtl->tracks, (GHRFunc) trw_layer_track_find_uuid_by_name, &udata );
3287
3288   vik_track_free (udata.trk);
3289
3290   if ( trkf && udata.uuid )
3291     return vik_trw_layer_delete_track (vtl, g_hash_table_lookup ( vtl->tracks, udata.uuid ));
3292   else
3293     return FALSE;
3294 }
3295
3296 static void remove_item_from_treeview(const gchar *name, GtkTreeIter *it, VikTreeview * vt)
3297 {
3298     vik_treeview_item_delete (vt, it );
3299 }
3300
3301 void vik_trw_layer_delete_all_tracks ( VikTrwLayer *vtl )
3302 {
3303
3304   vtl->current_track = NULL;
3305   vtl->route_finder_current_track = NULL;
3306   vtl->route_finder_added_track = NULL;
3307   if (vtl->current_tp_track)
3308     trw_layer_cancel_current_tp(vtl, FALSE);
3309   if (vtl->last_tp_track)
3310     trw_layer_cancel_last_tp ( vtl );
3311
3312   g_hash_table_foreach(vtl->tracks_iters, (GHFunc) remove_item_from_treeview, VIK_LAYER(vtl)->vt);
3313   g_hash_table_remove_all(vtl->tracks_iters);
3314   g_hash_table_remove_all(vtl->tracks);
3315
3316   vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
3317 }
3318
3319 void vik_trw_layer_delete_all_waypoints ( VikTrwLayer *vtl )
3320 {
3321   vtl->current_wp = NULL;
3322   vtl->current_wp_id = NULL;
3323   vtl->moving_wp = FALSE;
3324
3325   highest_wp_number_reset(vtl);
3326
3327   g_hash_table_foreach(vtl->waypoints_iters, (GHFunc) remove_item_from_treeview, VIK_LAYER(vtl)->vt);
3328   g_hash_table_remove_all(vtl->waypoints_iters);
3329   g_hash_table_remove_all(vtl->waypoints);
3330
3331   vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
3332 }
3333
3334 static void trw_layer_delete_all_tracks ( gpointer lav[2] )
3335 {
3336   VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
3337   // Get confirmation from the user
3338   if ( a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
3339                             _("Are you sure you want to delete all tracks in %s?"),
3340                             vik_layer_get_name ( VIK_LAYER(vtl) ) ) )
3341     vik_trw_layer_delete_all_tracks (vtl);
3342 }
3343
3344 static void trw_layer_delete_all_waypoints ( gpointer lav[2] )
3345 {
3346   VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
3347   // Get confirmation from the user
3348   if ( a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
3349                             _("Are you sure you want to delete all waypoints in %s?"),
3350                             vik_layer_get_name ( VIK_LAYER(vtl) ) ) )
3351     vik_trw_layer_delete_all_waypoints (vtl);
3352 }
3353
3354 static void trw_layer_delete_item ( gpointer pass_along[6] )
3355 {
3356   VikTrwLayer *vtl = VIK_TRW_LAYER(pass_along[0]);
3357   gboolean was_visible = FALSE;
3358   if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_WAYPOINT )
3359   {
3360     VikWaypoint *wp = g_hash_table_lookup ( vtl->waypoints, pass_along[3] );
3361     if ( wp && wp->name ) {
3362       if ( GPOINTER_TO_INT ( pass_along[4]) )
3363         // Get confirmation from the user
3364         // Maybe this Waypoint Delete should be optional as is it could get annoying...
3365         if ( ! a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
3366             _("Are you sure you want to delete the waypoint \"%s\""),
3367             wp->name ) )
3368           return;
3369       was_visible = trw_layer_delete_waypoint ( vtl, wp );
3370     }
3371   }
3372   else
3373   {
3374     VikTrack *trk = g_hash_table_lookup ( vtl->tracks, pass_along[3] );
3375     if ( trk && trk->name ) {
3376       if ( GPOINTER_TO_INT ( pass_along[4]) )
3377         // Get confirmation from the user
3378         if ( ! a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
3379                                   _("Are you sure you want to delete the track \"%s\""),
3380                                   trk->name ) )
3381           return;
3382       was_visible = vik_trw_layer_delete_track ( vtl, trk );
3383     }
3384   }
3385   if ( was_visible )
3386     vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
3387 }
3388
3389
3390 static void trw_layer_properties_item ( gpointer pass_along[7] )
3391 {
3392   VikTrwLayer *vtl = VIK_TRW_LAYER(pass_along[0]);
3393   if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_WAYPOINT )
3394   {
3395     VikWaypoint *wp = g_hash_table_lookup ( vtl->waypoints, pass_along[3] ); // sublayer
3396
3397     if ( wp && wp->name )
3398     {
3399       gboolean updated = FALSE;
3400       a_dialog_waypoint ( VIK_GTK_WINDOW_FROM_LAYER(vtl), wp->name, wp, vtl->coord_mode, FALSE, &updated );
3401
3402       if ( updated && VIK_LAYER(vtl)->visible )
3403         vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
3404     }
3405   }
3406   else
3407   {
3408     VikTrack *tr = g_hash_table_lookup ( vtl->tracks, pass_along[3] );
3409     if ( tr && tr->name )
3410     {
3411       vik_trw_layer_propwin_run ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
3412                                   vtl, tr,
3413                                   pass_along[1], /* vlp */
3414                                   pass_along[5] );  /* vvp */
3415     }
3416   }
3417 }
3418
3419 /*
3420    Parameter 1 -> VikLayersPanel
3421    Parameter 2 -> VikLayer
3422    Parameter 3 -> VikViewport
3423 */
3424 static void goto_coord ( gpointer *vlp, gpointer vl, gpointer vvp, const VikCoord *coord )
3425 {
3426   if ( vlp ) {
3427     vik_viewport_set_center_coord ( vik_layers_panel_get_viewport (VIK_LAYERS_PANEL(vlp)), coord );
3428     vik_layers_panel_emit_update ( VIK_LAYERS_PANEL(vlp) );
3429   }
3430   else {
3431     /* since vlp not set, vl & vvp should be valid instead! */
3432     if ( vl && vvp ) {
3433       vik_viewport_set_center_coord ( VIK_VIEWPORT(vvp), coord );
3434       vik_layer_emit_update ( VIK_LAYER(vl), FALSE );
3435     }
3436   }
3437 }
3438
3439 static void trw_layer_goto_track_startpoint ( gpointer pass_along[6] )
3440 {
3441   GList *trps = ((VikTrack *) g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, pass_along[3] ))->trackpoints;
3442   if ( trps && trps->data )
3443     goto_coord ( pass_along[1], pass_along[0], pass_along[5], &(((VikTrackpoint *) trps->data)->coord));
3444 }
3445
3446 static void trw_layer_goto_track_center ( gpointer pass_along[6] )
3447 {
3448   /* FIXME: get this into viktrack.c, and should be ->trackpoints right? */
3449   GList **trps = g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, pass_along[3] );
3450   if ( trps && *trps )
3451   {
3452     struct LatLon average, maxmin[2] = { {0,0}, {0,0} };
3453     VikCoord coord;
3454     trw_layer_find_maxmin_tracks ( NULL, trps, maxmin );
3455     average.lat = (maxmin[0].lat+maxmin[1].lat)/2;
3456     average.lon = (maxmin[0].lon+maxmin[1].lon)/2;
3457     vik_coord_load_from_latlon ( &coord, VIK_TRW_LAYER(pass_along[0])->coord_mode, &average );
3458     goto_coord ( pass_along[1], pass_along[0], pass_along[5], &coord);
3459   }
3460 }
3461
3462 static void trw_layer_extend_track_end ( gpointer pass_along[6] )
3463 {
3464   VikTrwLayer *vtl = VIK_TRW_LAYER(pass_along[0]);
3465   VikTrack *track = g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, pass_along[3] );
3466
3467   vtl->current_track = track;
3468   vik_window_enable_layer_tool ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)), VIK_LAYER_TRW, TOOL_CREATE_TRACK);
3469
3470   if ( track->trackpoints )
3471     goto_coord ( pass_along[1], pass_along[0], pass_along[5], &(((VikTrackpoint *)g_list_last(track->trackpoints)->data)->coord) );
3472 }
3473
3474 /**
3475  * extend a track using route finder
3476  */
3477 static void trw_layer_extend_track_end_route_finder ( gpointer pass_along[6] )
3478 {
3479   VikTrwLayer *vtl = VIK_TRW_LAYER(pass_along[0]);
3480   VikTrack *track = g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, pass_along[3] );
3481   VikCoord last_coord = (((VikTrackpoint *)g_list_last(track->trackpoints)->data)->coord);
3482
3483   vik_window_enable_layer_tool ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)), VIK_LAYER_TRW, NUM_TOOLS );
3484   vtl->route_finder_coord =  last_coord;
3485   vtl->route_finder_current_track = track;
3486   vtl->route_finder_started = TRUE;
3487
3488   if ( track->trackpoints )
3489     goto_coord ( pass_along[1], pass_along[0], pass_along[5], &last_coord) ;
3490
3491 }
3492
3493 static void trw_layer_apply_dem_data ( gpointer pass_along[6] )
3494 {
3495   /* TODO: check & warn if no DEM data, or no applicable DEM data. */
3496   /* Also warn if overwrite old elevation data */
3497   VikTrack *track = (VikTrack *) g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, pass_along[3] );
3498
3499   vik_track_apply_dem_data ( track );
3500 }
3501
3502 static void trw_layer_goto_track_endpoint ( gpointer pass_along[6] )
3503 {
3504   GList *trps = ((VikTrack *) g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, pass_along[3] ))->trackpoints;
3505   if ( !trps )
3506     return;
3507   trps = g_list_last(trps);
3508   goto_coord ( pass_along[1], pass_along[0], pass_along[5], &(((VikTrackpoint *) trps->data)->coord));
3509 }
3510
3511 static void trw_layer_goto_track_max_speed ( gpointer pass_along[6] )
3512 {
3513   VikTrackpoint* vtp = vik_track_get_tp_by_max_speed ( g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, pass_along[3] ) );
3514   if ( !vtp )
3515     return;
3516   goto_coord ( pass_along[1], pass_along[0], pass_along[5], &(vtp->coord));
3517 }
3518
3519 static void trw_layer_goto_track_max_alt ( gpointer pass_along[6] )
3520 {
3521   VikTrackpoint* vtp = vik_track_get_tp_by_max_alt ( g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, pass_along[3] ) );
3522   if ( !vtp )
3523     return;
3524   goto_coord ( pass_along[1], pass_along[0], pass_along[5], &(vtp->coord));
3525 }
3526
3527 static void trw_layer_goto_track_min_alt ( gpointer pass_along[6] )
3528 {
3529   VikTrackpoint* vtp = vik_track_get_tp_by_min_alt ( g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, pass_along[3] ) );
3530   if ( !vtp )
3531     return;
3532   goto_coord ( pass_along[1], pass_along[0], pass_along[5], &(vtp->coord));
3533 }
3534
3535 /*
3536  * Automatically change the viewport to center on the track and zoom to see the extent of the track
3537  */
3538 static void trw_layer_auto_track_view ( gpointer pass_along[6] )
3539 {
3540   GList **trps = g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, pass_along[3] );
3541   if ( trps && *trps )
3542   {
3543     struct LatLon maxmin[2] = { {0,0}, {0,0} };
3544     trw_layer_find_maxmin_tracks ( NULL, trps, maxmin );
3545     trw_layer_zoom_to_show_latlons ( VIK_TRW_LAYER(pass_along[0]), pass_along[5], maxmin );
3546     if ( pass_along[1] )
3547       vik_layers_panel_emit_update ( VIK_LAYERS_PANEL(pass_along[1]) );
3548     else
3549       vik_layer_emit_update ( VIK_LAYER(pass_along[0]), FALSE );
3550   }
3551 }
3552
3553 static void trw_layer_edit_trackpoint ( gpointer pass_along[6] )
3554 {
3555   VikTrwLayer *vtl = VIK_TRW_LAYER(pass_along[0]);
3556   trw_layer_tpwin_init ( vtl );
3557 }
3558
3559 /*************************************
3560  * merge/split by time routines 
3561  *************************************/
3562
3563 /* called for each key in track hash table.
3564  * If the current track has the same time stamp type, add it to the result,
3565  * except the one pointed by "exclude".
3566  * set exclude to NULL if there is no exclude to check.
3567  * Note that the result is in reverse (for performance reasons).
3568  */
3569 typedef struct {
3570   GList **result;
3571   GList  *exclude;
3572   gboolean with_timestamps;
3573 } twt_udata;
3574 static void find_tracks_with_timestamp_type(gpointer key, gpointer value, gpointer udata)
3575 {
3576   twt_udata *user_data = udata;
3577   VikTrackpoint *p1, *p2;
3578
3579   if (VIK_TRACK(value)->trackpoints == user_data->exclude) {
3580     return;
3581   }
3582
3583   if (VIK_TRACK(value)->trackpoints) {
3584     p1 = VIK_TRACKPOINT(VIK_TRACK(value)->trackpoints->data);
3585     p2 = VIK_TRACKPOINT(g_list_last(VIK_TRACK(value)->trackpoints)->data);
3586
3587     if ( user_data->with_timestamps ) {
3588       if (!p1->has_timestamp || !p2->has_timestamp) {
3589         return;
3590       }
3591     }
3592     else {
3593       // Don't add tracks with timestamps when getting non timestamp tracks
3594       if (p1->has_timestamp || p2->has_timestamp) {
3595         return;
3596       }
3597     }
3598   }
3599
3600   *(user_data->result) = g_list_prepend(*(user_data->result), key);
3601 }
3602
3603 /* called for each key in track hash table. if original track user_data[1] is close enough
3604  * to the passed one, add it to list in user_data[0] 
3605  */
3606 static void find_nearby_track(gpointer key, gpointer value, gpointer user_data)
3607 {
3608   time_t t1, t2;
3609   VikTrackpoint *p1, *p2;
3610
3611   GList **nearby_tracks = ((gpointer *)user_data)[0];
3612   GList *orig_track = ((gpointer *)user_data)[1];
3613   guint thr = GPOINTER_TO_UINT (((gpointer *)user_data)[2]);
3614
3615   /* outline: 
3616    * detect reasons for not merging, and return
3617    * if no reason is found not to merge, then do it.
3618    */
3619
3620   if (VIK_TRACK(value)->trackpoints == orig_track) {
3621     return;
3622   }
3623
3624   t1 = VIK_TRACKPOINT(orig_track->data)->timestamp;
3625   t2 = VIK_TRACKPOINT(g_list_last(orig_track)->data)->timestamp;
3626
3627   if (VIK_TRACK(value)->trackpoints) {
3628     p1 = VIK_TRACKPOINT(VIK_TRACK(value)->trackpoints->data);
3629     p2 = VIK_TRACKPOINT(g_list_last(VIK_TRACK(value)->trackpoints)->data);
3630
3631     if (!p1->has_timestamp || !p2->has_timestamp) {
3632       g_print("no timestamp\n");
3633       return;
3634     }
3635
3636     /*  g_print("Got track named %s, times %d, %d\n", (gchar *)key, p1->timestamp, p2->timestamp); */
3637     if (! (abs(t1 - p2->timestamp) < thr*60 ||
3638         /*  p1 p2      t1 t2 */
3639            abs(p1->timestamp - t2) < thr*60)
3640         /*  t1 t2      p1 p2 */
3641         ) {
3642       return;
3643     }
3644   }
3645
3646   *nearby_tracks = g_list_prepend(*nearby_tracks, key);
3647 }
3648
3649 /* comparison function used to sort tracks; a and b are hash table keys */
3650 /* Not actively used - can be restored if needed
3651 static gint track_compare(gconstpointer a, gconstpointer b, gpointer user_data)
3652 {
3653   GHashTable *tracks = user_data;
3654   time_t t1, t2;
3655
3656   t1 = VIK_TRACKPOINT(VIK_TRACK(g_hash_table_lookup(tracks, a))->trackpoints->data)->timestamp;
3657   t2 = VIK_TRACKPOINT(VIK_TRACK(g_hash_table_lookup(tracks, b))->trackpoints->data)->timestamp;
3658   
3659   if (t1 < t2) return -1;
3660   if (t1 > t2) return 1;
3661   return 0;
3662 }
3663 */
3664
3665 /* comparison function used to sort trackpoints */
3666 static gint trackpoint_compare(gconstpointer a, gconstpointer b)
3667 {
3668   time_t t1 = VIK_TRACKPOINT(a)->timestamp, t2 = VIK_TRACKPOINT(b)->timestamp;
3669   
3670   if (t1 < t2) return -1;
3671   if (t1 > t2) return 1;
3672   return 0;
3673 }
3674
3675 /**
3676  * comparison function which can be used to sort tracks or waypoints by name
3677  */
3678 static gint sort_alphabetically (gconstpointer a, gconstpointer b, gpointer user_data)
3679 {
3680   const gchar* namea = (const gchar*) a;
3681   const gchar* nameb = (const gchar*) b;
3682   if ( namea == NULL || nameb == NULL)
3683     return 0;
3684   else
3685     // Same sort method as used in the vik_treeview_*_alphabetize functions
3686     return strcmp ( namea, nameb );
3687 }
3688
3689 /**
3690  * Attempt to merge selected track with other tracks specified by the user
3691  * Tracks to merge with must be of the same 'type' as the selected track -
3692  *  either all with timestamps, or all without timestamps
3693  */
3694 static void trw_layer_merge_with_other ( gpointer pass_along[6] )
3695 {
3696   VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
3697   GList *other_tracks = NULL;
3698   VikTrack *track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
3699
3700   if ( !track->trackpoints )
3701     return;
3702
3703   twt_udata udata;
3704   udata.result = &other_tracks;
3705   udata.exclude = track->trackpoints;
3706   // Allow merging with 'similar' time type time tracks
3707   // i.e. either those times, or those without
3708   udata.with_timestamps = (VIK_TRACKPOINT(track->trackpoints->data)->has_timestamp);
3709
3710   g_hash_table_foreach(vtl->tracks, find_tracks_with_timestamp_type, (gpointer)&udata);
3711   other_tracks = g_list_reverse(other_tracks);
3712
3713   if ( !other_tracks ) {
3714     if ( udata.with_timestamps )
3715       a_dialog_error_msg(VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Failed. No other tracks with timestamps in this layer found"));
3716     else
3717       a_dialog_error_msg(VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Failed. No other tracks without timestamps in this layer found"));
3718     return;
3719   }
3720
3721   // Sort alphabetically for user presentation
3722   // Convert into list of names for usage with dialog function
3723   // TODO: Need to consider how to work best when we can have multiple tracks the same name...
3724   GList *other_tracks_names = NULL;
3725   GList *iter = g_list_first ( other_tracks );
3726   while ( iter ) {
3727     other_tracks_names = g_list_append ( other_tracks_names, VIK_TRACK(g_hash_table_lookup (vtl->tracks, iter->data))->name );
3728     iter = g_list_next ( iter );
3729   }
3730
3731   other_tracks_names = g_list_sort_with_data (other_tracks_names, sort_alphabetically, NULL);
3732
3733   GList *merge_list = a_dialog_select_from_list(VIK_GTK_WINDOW_FROM_LAYER(vtl),
3734                                                 other_tracks_names, TRUE,
3735                                                 _("Merge with..."), _("Select track to merge with"));
3736   g_list_free(other_tracks);
3737   g_list_free(other_tracks_names);
3738
3739   if (merge_list)
3740   {
3741     GList *l;
3742     for (l = merge_list; l != NULL; l = g_list_next(l)) {
3743       VikTrack *merge_track = vik_trw_layer_get_track ( vtl, l->data );
3744       if (merge_track) {
3745         track->trackpoints = g_list_concat(track->trackpoints, merge_track->trackpoints);
3746         merge_track->trackpoints = NULL;
3747         vik_trw_layer_delete_track (vtl, merge_track);
3748         track->trackpoints = g_list_sort(track->trackpoints, trackpoint_compare);
3749       }
3750     }
3751     /* TODO: free data before free merge_list */
3752     for (l = merge_list; l != NULL; l = g_list_next(l))
3753       g_free(l->data);
3754     g_list_free(merge_list);
3755     vik_layer_emit_update( VIK_LAYER(vtl), FALSE );
3756   }
3757 }
3758
3759 /* merge by time routine */
3760 static void trw_layer_merge_by_timestamp ( gpointer pass_along[6] )
3761 {
3762   VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
3763
3764   //time_t t1, t2;
3765   GList *nearby_tracks;
3766   GList *trps;
3767   static  guint thr = 1;
3768   guint track_count = 0;
3769
3770   GList *tracks_with_timestamp = NULL;
3771   VikTrack *orig_trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
3772   if (orig_trk->trackpoints &&
3773       !VIK_TRACKPOINT(orig_trk->trackpoints->data)->has_timestamp) {
3774     a_dialog_error_msg(VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Failed. This track does not have timestamp"));
3775     return;
3776   }
3777
3778   twt_udata udata;
3779   udata.result = &tracks_with_timestamp;
3780   udata.exclude = orig_trk->trackpoints;
3781   udata.with_timestamps = TRUE;
3782   g_hash_table_foreach(vtl->tracks, find_tracks_with_timestamp_type, (gpointer)&udata);
3783   tracks_with_timestamp = g_list_reverse(tracks_with_timestamp);
3784
3785   if (!tracks_with_timestamp) {
3786     a_dialog_error_msg(VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Failed. No other track in this layer has timestamp"));
3787     return;
3788   }
3789   g_list_free(tracks_with_timestamp);
3790
3791   if (!a_dialog_time_threshold(VIK_GTK_WINDOW_FROM_LAYER(vtl), 
3792                                _("Merge Threshold..."), 
3793                                _("Merge when time between tracks less than:"), 
3794                                &thr)) {
3795     return;
3796   }
3797
3798   /* merge tracks until we can't */
3799   //VikTrack *track;
3800   nearby_tracks = NULL;
3801   do {
3802     gpointer params[3];
3803
3804     // Need to refind original track incase we've deleted and recreated it??
3805     //track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
3806     trps = orig_trk->trackpoints;
3807     if ( !trps )
3808       return;
3809
3810
3811     if (nearby_tracks) {
3812       g_list_free(nearby_tracks);
3813       nearby_tracks = NULL;
3814     }
3815
3816     //t1 = ((VikTrackpoint *)trps->data)->timestamp;
3817     //t2 = ((VikTrackpoint *)g_list_last(trps)->data)->timestamp;
3818     
3819     /*    g_print("Original track times: %d and %d\n", t1, t2);  */
3820     params[0] = &nearby_tracks;
3821     params[1] = trps;
3822     params[2] = GUINT_TO_POINTER (thr);
3823
3824     /* get a list of adjacent-in-time tracks */
3825     g_hash_table_foreach(vtl->tracks, find_nearby_track, (gpointer)params);
3826
3827     /* add original track */
3828     nearby_tracks = g_list_prepend(nearby_tracks, orig_trk);
3829
3830     /* merge them */
3831     { 
3832 #define get_track(x) VIK_TRACK(g_hash_table_lookup(vtl->tracks, ((x)->data)))
3833       GList *l = nearby_tracks;
3834       //      VikTrack *tr = vik_track_new();
3835       //tr->visible = track->visible;
3836       track_count = 0;
3837       while (l) {
3838         /*
3839 #define get_first_trackpoint(x) VIK_TRACKPOINT(get_track(x)->trackpoints->data)
3840 #define get_last_trackpoint(x) VIK_TRACKPOINT(g_list_last(get_track(x)->trackpoints)->data)
3841         time_t t1, t2;
3842         t1 = get_first_trackpoint(l)->timestamp;
3843         t2 = get_last_trackpoint(l)->timestamp;
3844 #undef get_first_trackpoint
3845 #undef get_last_trackpoint
3846         g_print("     %20s: track %d - %d\n", (char *)l->data, (int)t1, (int)t2);
3847         */
3848
3849
3850         /* remove trackpoints from merged track, delete track */
3851         orig_trk->trackpoints = g_list_concat(orig_trk->trackpoints, get_track(l)->trackpoints);
3852         get_track(l)->trackpoints = NULL;
3853         vik_trw_layer_delete_track (vtl, l->data);
3854
3855         track_count++;
3856         l = g_list_next(l);
3857       }
3858 #undef get_track
3859       orig_trk->trackpoints = g_list_sort(orig_trk->trackpoints, trackpoint_compare);
3860       //vik_trw_layer_add_track(vtl, strdup(orig_track_name), tr);
3861
3862     }
3863   } while (track_count > 1);
3864   g_list_free(nearby_tracks);
3865   vik_layer_emit_update( VIK_LAYER(vtl), FALSE );
3866 }
3867
3868 /* split by time routine */
3869 static void trw_layer_split_by_timestamp ( gpointer pass_along[6] )
3870 {
3871   VikTrack *track = (VikTrack *) g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, pass_along[3] );
3872   GList *trps = track->trackpoints;
3873   GList *iter;
3874   GList *newlists = NULL;
3875   GList *newtps = NULL;
3876   guint i;
3877   static guint thr = 1;
3878
3879   time_t ts, prev_ts;
3880
3881   if ( !trps )
3882     return;
3883
3884   if (!a_dialog_time_threshold(VIK_GTK_WINDOW_FROM_LAYER(pass_along[0]), 
3885                                _("Split Threshold..."), 
3886                                _("Split when time between trackpoints exceeds:"), 
3887                                &thr)) {
3888     return;
3889   }
3890
3891   /* iterate through trackpoints, and copy them into new lists without touching original list */
3892   prev_ts = VIK_TRACKPOINT(trps->data)->timestamp;
3893   iter = trps;
3894
3895   while (iter) {
3896     ts = VIK_TRACKPOINT(iter->data)->timestamp;
3897     if (ts < prev_ts) {
3898       g_print("panic: ts < prev_ts: this should never happen!\n");
3899       return;
3900     }
3901     if (ts - prev_ts > thr*60) {
3902       /* flush accumulated trackpoints into new list */
3903       newlists = g_list_append(newlists, g_list_reverse(newtps));
3904       newtps = NULL;
3905     }
3906
3907     /* accumulate trackpoint copies in newtps, in reverse order */
3908     newtps = g_list_prepend(newtps, vik_trackpoint_copy(VIK_TRACKPOINT(iter->data)));
3909     prev_ts = ts;
3910     iter = g_list_next(iter);
3911   }
3912   if (newtps) {
3913       newlists = g_list_append(newlists, g_list_reverse(newtps));
3914   }
3915
3916   /* put lists of trackpoints into tracks */
3917   iter = newlists;
3918   i = 1;
3919   // Only bother updating if the split results in new tracks
3920   if (g_list_length (newlists) > 1) {
3921     while (iter) {
3922       gchar *new_tr_name;
3923       VikTrack *tr;
3924
3925       tr = vik_track_new();
3926       tr->visible = track->visible;
3927       tr->trackpoints = (GList *)(iter->data);
3928
3929       new_tr_name = g_strdup_printf ("%s #%d", track->name, i++);
3930       vik_trw_layer_add_track(VIK_TRW_LAYER(pass_along[0]), new_tr_name, tr);
3931       /*    g_print("adding track %s, times %d - %d\n", new_tr_name, VIK_TRACKPOINT(tr->trackpoints->data)->timestamp,
3932           VIK_TRACKPOINT(g_list_last(tr->trackpoints)->data)->timestamp);*/
3933
3934       iter = g_list_next(iter);
3935     }
3936     // Remove original track and then update the display
3937     vik_trw_layer_delete_track (VIK_TRW_LAYER(pass_along[0]), track);
3938     vik_layer_emit_update(VIK_LAYER(pass_along[0]), FALSE);
3939   }
3940   g_list_free(newlists);
3941 }
3942
3943 /**
3944  * Split a track by the number of points as specified by the user
3945  */
3946 static void trw_layer_split_by_n_points ( gpointer pass_along[6] )
3947 {
3948   VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
3949   VikTrack *track = (VikTrack *)g_hash_table_lookup ( vtl->tracks, pass_along[3] );
3950
3951   // Check valid track
3952   GList *trps = track->trackpoints;
3953   if ( !trps )
3954     return;
3955
3956   gint points = a_dialog_get_positive_number(VIK_GTK_WINDOW_FROM_LAYER(pass_along[0]),
3957                                              _("Split Every Nth Point"),
3958                                              _("Split on every Nth point:"),
3959                                              250,   // Default value as per typical limited track capacity of various GPS devices
3960                                              2,     // Min
3961                                              65536, // Max
3962                                              5);    // Step
3963   // Was a valid number returned?
3964   if (!points)
3965     return;
3966
3967   // Now split...
3968   GList *iter;
3969   GList *newlists = NULL;
3970   GList *newtps = NULL;
3971   gint count = 0;
3972   iter = trps;
3973
3974   while (iter) {
3975     /* accumulate trackpoint copies in newtps, in reverse order */
3976     newtps = g_list_prepend(newtps, vik_trackpoint_copy(VIK_TRACKPOINT(iter->data)));
3977     count++;
3978     if (count >= points) {
3979       /* flush accumulated trackpoints into new list */
3980       newlists = g_list_append(newlists, g_list_reverse(newtps));
3981       newtps = NULL;
3982       count = 0;
3983     }
3984     iter = g_list_next(iter);
3985   }
3986
3987   // If there is a remaining chunk put that into the new split list
3988   // This may well be the whole track if no split points were encountered
3989   if (newtps) {
3990       newlists = g_list_append(newlists, g_list_reverse(newtps));
3991   }
3992
3993   /* put lists of trackpoints into tracks */
3994   iter = newlists;
3995   guint i = 1;
3996   // Only bother updating if the split results in new tracks
3997   if (g_list_length (newlists) > 1) {
3998     while (iter) {
3999       gchar *new_tr_name;
4000       VikTrack *tr;
4001
4002       tr = vik_track_new();
4003       tr->visible = track->visible;
4004       tr->trackpoints = (GList *)(iter->data);
4005
4006       new_tr_name = g_strdup_printf ("%s #%d", track->name, i++);
4007       vik_trw_layer_add_track(VIK_TRW_LAYER(pass_along[0]), new_tr_name, tr);
4008
4009       iter = g_list_next(iter);
4010     }
4011     // Remove original track and then update the display
4012     vik_trw_layer_delete_track (VIK_TRW_LAYER(pass_along[0]), track);
4013     vik_layer_emit_update(VIK_LAYER(pass_along[0]), FALSE);
4014   }
4015   g_list_free(newlists);
4016 }
4017
4018 /* end of split/merge routines */
4019
4020 /**
4021  * Reverse a track
4022  */
4023 static void trw_layer_reverse ( gpointer pass_along[6] )
4024 {
4025   VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
4026   VikTrack *track = (VikTrack *)g_hash_table_lookup ( vtl->tracks, pass_along[3] );
4027
4028   // Check valid track
4029   GList *trps = track->trackpoints;
4030   if ( !trps )
4031     return;
4032
4033   vik_track_reverse ( track );
4034  
4035   vik_layer_emit_update ( VIK_LAYER(pass_along[0]), FALSE );
4036 }
4037
4038 /**
4039  * Similar to trw_layer_enum_item, but this uses a sorted method
4040  */
4041 /* Currently unused
4042 static void trw_layer_sorted_name_list(gpointer key, gpointer value, gpointer udata)
4043 {
4044   GList **list = (GList**)udata;
4045   // *list = g_list_prepend(*all, key); //unsorted method
4046   // Sort named list alphabetically
4047   *list = g_list_insert_sorted_with_data (*list, key, sort_alphabetically, NULL);
4048 }
4049 */
4050
4051 /**
4052  * Now Waypoint specific sort
4053  */
4054 static void trw_layer_sorted_wp_id_by_name_list (const gpointer id, const VikWaypoint *wp, gpointer udata)
4055 {
4056   GList **list = (GList**)udata;
4057   // Sort named list alphabetically
4058   *list = g_list_insert_sorted_with_data (*list, wp->name, sort_alphabetically, NULL);
4059 }
4060
4061 /**
4062  * Track specific sort
4063  */
4064 static void trw_layer_sorted_track_id_by_name_list (const gpointer id, const VikTrack *trk, gpointer udata)
4065 {
4066   GList **list = (GList**)udata;
4067   // Sort named list alphabetically
4068   *list = g_list_insert_sorted_with_data (*list, trk->name, sort_alphabetically, NULL);
4069 }
4070
4071
4072 typedef struct {
4073   gboolean    has_same_track_name;
4074   const gchar *same_track_name;
4075 } same_track_name_udata;
4076
4077 static gint check_tracks_for_same_name ( gconstpointer aa, gconstpointer bb, gpointer udata )
4078 {
4079   const gchar* namea = (const gchar*) aa;
4080   const gchar* nameb = (const gchar*) bb;
4081
4082   // the test
4083   gint result = strcmp ( namea, nameb );
4084
4085   if ( result == 0 ) {
4086     // Found two names the same
4087     same_track_name_udata *user_data = udata;
4088     user_data->has_same_track_name = TRUE;
4089     user_data->same_track_name = namea;
4090   }
4091
4092   // Leave ordering the same
4093   return 0;
4094 }
4095
4096 /**
4097  * Find out if any tracks have the same name in this layer
4098  */
4099 static gboolean trw_layer_has_same_track_names ( VikTrwLayer *vtl )
4100 {
4101   // Sort items by name, then compare if any next to each other are the same
4102
4103   GList *track_names = NULL;
4104   g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_sorted_track_id_by_name_list, &track_names );
4105
4106   // No tracks
4107   if ( ! track_names )
4108     return FALSE;
4109
4110   same_track_name_udata udata;
4111   udata.has_same_track_name = FALSE;
4112
4113   // Use sort routine to traverse list comparing items
4114   // Don't care how this list ends up ordered ( doesn't actually change ) - care about the returned status
4115   GList *dummy_list = g_list_sort_with_data ( track_names, check_tracks_for_same_name, &udata );
4116   // Still no tracks...
4117   if ( ! dummy_list )
4118     return FALSE;
4119
4120   return udata.has_same_track_name;
4121 }
4122
4123 /**
4124  * Force unqiue track names for this layer
4125  * Note the panel is a required parameter to enable the update of the names displayed
4126  */
4127 static void vik_trw_layer_uniquify_tracks ( VikTrwLayer *vtl, VikLayersPanel *vlp )
4128 {
4129   // . Search list for an instance of repeated name
4130   // . get track of this name
4131   // . create new name
4132   // . rename track & update equiv. treeview iter
4133   // . repeat until all different
4134
4135   same_track_name_udata udata;
4136
4137   GList *track_names = NULL;
4138   udata.has_same_track_name = FALSE;
4139   udata.same_track_name = NULL;
4140
4141   g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_sorted_track_id_by_name_list, &track_names );
4142
4143   // No tracks
4144   if ( ! track_names )
4145     return;
4146
4147   GList *dummy_list1 = g_list_sort_with_data ( track_names, check_tracks_for_same_name, &udata );
4148
4149   // Still no tracks...
4150   if ( ! dummy_list1 )
4151     return;
4152
4153   while ( udata.has_same_track_name ) {
4154
4155     // Find a track with the same name
4156     VikTrack *trk = vik_trw_layer_get_track ( vtl, (gpointer) udata.same_track_name );
4157
4158     if ( ! trk ) {
4159       // Broken :(
4160       g_critical("Houston, we've had a problem.");
4161       vik_statusbar_set_message ( vik_window_get_statusbar (VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl))), VIK_STATUSBAR_INFO, 
4162                                   _("Internal Error in vik_trw_layer_uniquify_tracks") );
4163       return;
4164     }
4165
4166     // Rename it
4167     gchar *newname = get_new_unique_sublayer_name ( vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, udata.same_track_name );
4168     vik_track_set_name ( trk, newname );
4169
4170     trku_udata udataU;
4171     udataU.trk  = trk;
4172     udataU.uuid = NULL;
4173
4174     // Need want key of it for treeview update
4175     gpointer *trkf = g_hash_table_find ( vtl->tracks, (GHRFunc) trw_layer_track_find_uuid, &udataU );
4176
4177     if ( trkf && udataU.uuid ) {
4178
4179       GtkTreeIter *it = g_hash_table_lookup ( vtl->tracks_iters, udataU.uuid );
4180
4181       if ( it ) {
4182         vik_treeview_item_set_name ( VIK_LAYER(vtl)->vt, it, newname );
4183 #ifdef VIK_CONFIG_ALPHABETIZED_TRW
4184         vik_treeview_sublayer_realphabetize ( VIK_LAYER(vtl)->vt, it, newname );
4185 #endif
4186       }
4187     }
4188
4189     // Start trying to find same names again...
4190     track_names = NULL;
4191     g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_sorted_track_id_by_name_list, &track_names );
4192     udata.has_same_track_name = FALSE;
4193     GList *dummy_list2 = g_list_sort_with_data ( track_names, check_tracks_for_same_name, &udata );
4194
4195     // No tracks any more - give up searching
4196     if ( ! dummy_list2 )
4197       udata.has_same_track_name = FALSE;
4198   }
4199
4200   // Update
4201   vik_layers_panel_emit_update ( vlp );
4202 }
4203
4204 /**
4205  *
4206  */
4207 static void trw_layer_delete_tracks_from_selection ( gpointer lav[2] )
4208 {
4209   VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
4210   GList *all = NULL;
4211
4212   // Ensure list of track names offered is unique
4213   if ( trw_layer_has_same_track_names ( vtl ) ) {
4214     if ( a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
4215                               _("Multiple entries with the same name exist. This method only works with unique names. Force unique names now?"), NULL ) ) {
4216       vik_trw_layer_uniquify_tracks ( vtl, VIK_LAYERS_PANEL(lav[1]) );
4217     }
4218     else
4219       return;
4220   }
4221
4222   // Sort list alphabetically for better presentation
4223   g_hash_table_foreach(vtl->tracks, (GHFunc) trw_layer_sorted_track_id_by_name_list, &all);
4224
4225   if ( ! all ) {
4226     a_dialog_error_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), _("No tracks found"));
4227     return;
4228   }
4229
4230   // Get list of items to delete from the user
4231   GList *delete_list = a_dialog_select_from_list(VIK_GTK_WINDOW_FROM_LAYER(vtl),
4232                                                  all,
4233                                                  TRUE,
4234                                                  _("Delete Selection"),
4235                                                  _("Select tracks to delete"));
4236   g_list_free(all);
4237
4238   // Delete requested tracks
4239   // since specificly requested, IMHO no need for extra confirmation
4240   if ( delete_list ) {
4241     GList *l;
4242     for (l = delete_list; l != NULL; l = g_list_next(l)) {
4243       // This deletes first trk it finds of that name (but uniqueness is enforced above)
4244       trw_layer_delete_track_by_name (vtl, l->data);
4245     }
4246     g_list_free(delete_list);
4247     vik_layer_emit_update( VIK_LAYER(vtl), FALSE );
4248   }
4249 }
4250
4251 /**
4252  *
4253  */
4254 static void trw_layer_delete_waypoints_from_selection ( gpointer lav[2] )
4255 {
4256   VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
4257   GList *all = NULL;
4258
4259   // TODO consider disabling this with warning message about
4260   //  not working due to multiple same names
4261   //  enable calling (yet to be defined) uniqify method?
4262
4263   // Sort list alphabetically for better presentation
4264   g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_sorted_wp_id_by_name_list, &all);
4265   if ( ! all ) {
4266     a_dialog_error_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), _("No waypoints found"));
4267     return;
4268   }
4269
4270   all = g_list_sort_with_data(all, sort_alphabetically, NULL);
4271
4272   // Get list of items to delete from the user
4273   GList *delete_list = a_dialog_select_from_list(VIK_GTK_WINDOW_FROM_LAYER(vtl),
4274                                                  all,
4275                                                  TRUE,
4276                                                  _("Delete Selection"),
4277                                                  _("Select waypoints to delete"));
4278   g_list_free(all);
4279
4280   // Delete requested waypoints
4281   // since specificly requested, IMHO no need for extra confirmation
4282   if ( delete_list ) {
4283     GList *l;
4284     for (l = delete_list; l != NULL; l = g_list_next(l)) {
4285       // TODO Hmmm conversion needed here -- not 1:1 relationship any more of name to reference
4286       //   -- new functionality / or need to extend list to have uuid with it / or have uuids and ways to find out the name for display
4287       //    may need to rework the general dialog functions
4288       // ARM This deletes first WP it finds of that name
4289       trw_layer_delete_waypoint_by_name (vtl, l->data);
4290     }
4291     g_list_free(delete_list);
4292     vik_layer_emit_update( VIK_LAYER(vtl), FALSE );
4293   }
4294
4295 }
4296
4297 static void trw_layer_goto_waypoint ( gpointer pass_along[6] )
4298 {
4299   VikWaypoint *wp = g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->waypoints, pass_along[3] );
4300   if ( wp )
4301     goto_coord ( pass_along[1], pass_along[0], pass_along[5], &(wp->coord) );
4302 }
4303
4304 static void trw_layer_waypoint_gc_webpage ( gpointer pass_along[6] )
4305 {
4306   gchar *webpage = g_strdup_printf("http://www.geocaching.com/seek/cache_details.aspx?wp=%s", (gchar *) pass_along[3] );
4307   open_url(VIK_GTK_WINDOW_FROM_LAYER(VIK_LAYER(pass_along[0])), webpage);
4308   g_free ( webpage );
4309 }
4310
4311 static const gchar* trw_layer_sublayer_rename_request ( VikTrwLayer *l, const gchar *newname, gpointer vlp, gint subtype, gpointer sublayer, GtkTreeIter *iter )
4312 {
4313   if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT )
4314   {
4315     VikWaypoint *wp = g_hash_table_lookup ( l->waypoints, sublayer );
4316
4317     // No actual change to the name supplied
4318     if (strcmp(newname, wp->name) == 0 )
4319       return NULL;
4320
4321     VikWaypoint *wpf = vik_trw_layer_get_waypoint ( l, newname );
4322
4323     if ( wpf ) {
4324       // An existing waypoint has been found with the requested name
4325       if ( ! a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(l),
4326            _("A waypoint with the name \"%s\" already exists. Really create one with the same name?"), 
4327            newname ) )
4328         return NULL;
4329     }
4330
4331     // Update WP name and refresh the treeview
4332     vik_waypoint_set_name (wp, newname);
4333
4334 #ifdef VIK_CONFIG_ALPHABETIZED_TRW
4335     vik_treeview_sublayer_realphabetize ( VIK_LAYER(l)->vt, iter, newname );
4336 #endif
4337
4338     vik_layers_panel_emit_update ( VIK_LAYERS_PANEL(vlp) );
4339
4340     return newname;
4341   }
4342
4343   if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
4344   {
4345     VikTrack *trk = g_hash_table_lookup ( l->tracks, sublayer );
4346
4347     // No actual change to the name supplied
4348     if (strcmp(newname, trk->name) == 0)
4349       return NULL;
4350
4351     VikTrack *trkf = vik_trw_layer_get_track ( l, (gpointer) newname );
4352
4353     if ( trkf ) {
4354       // An existing track has been found with the requested name
4355       if ( ! a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(l),
4356           _("A track with the name \"%s\" already exists. Really create one with the same name?"),
4357           newname ) )
4358         return NULL;
4359     }
4360     // Update track name and refresh GUI parts
4361     vik_track_set_name (trk, newname);
4362
4363     // Update any subwindows that could be displaying this track which has changed name
4364     // Only one Track Edit Window
4365     if ( l->current_tp_track == trk && l->tpwin ) {
4366       vik_trw_layer_tpwin_set_track_name ( l->tpwin, newname );
4367     }
4368     // Property Dialog of the track
4369     vik_trw_layer_propwin_update ( trk );
4370
4371 #ifdef VIK_CONFIG_ALPHABETIZED_TRW
4372     vik_treeview_sublayer_realphabetize ( VIK_LAYER(l)->vt, iter, newname );
4373 #endif
4374
4375     vik_layers_panel_emit_update ( VIK_LAYERS_PANEL(vlp) );
4376
4377     return newname;
4378   }
4379   return NULL;
4380 }
4381
4382 static gboolean is_valid_geocache_name ( gchar *str )
4383 {
4384   gint len = strlen ( str );
4385   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]));
4386 }
4387
4388 static void trw_layer_track_use_with_filter ( gpointer pass_along[6] )
4389 {
4390   VikTrack *trk = g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, pass_along[3] );
4391   a_acquire_set_filter_track ( trk );
4392 }
4393
4394 static gboolean is_valid_google_route ( VikTrwLayer *vtl, const gpointer track_id )
4395 {
4396   VikTrack *tr = g_hash_table_lookup ( vtl->tracks, track_id );
4397   return ( tr && tr->comment && strlen(tr->comment) > 7 && !strncmp(tr->comment, "from:", 5) );
4398 }
4399
4400 static void trw_layer_track_google_route_webpage ( gpointer pass_along[6] )
4401 {
4402   VikTrack *tr = g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, pass_along[3] );
4403   if ( tr ) {
4404     gchar *escaped = uri_escape ( tr->comment );
4405     gchar *webpage = g_strdup_printf("http://maps.google.com/maps?f=q&hl=en&q=%s", escaped );
4406     open_url(VIK_GTK_WINDOW_FROM_LAYER(VIK_LAYER(pass_along[0])), webpage);
4407     g_free ( escaped );
4408     g_free ( webpage );
4409   }
4410 }
4411
4412 /* vlp can be NULL if necessary - i.e. right-click from a tool */
4413 /* viewpoint is now available instead */
4414 static gboolean trw_layer_sublayer_add_menu_items ( VikTrwLayer *l, GtkMenu *menu, gpointer vlp, gint subtype, gpointer sublayer, GtkTreeIter *iter, VikViewport *vvp )
4415 {
4416   static gpointer pass_along[8];
4417   GtkWidget *item;
4418   gboolean rv = FALSE;
4419
4420   pass_along[0] = l;
4421   pass_along[1] = vlp;
4422   pass_along[2] = GINT_TO_POINTER (subtype);
4423   pass_along[3] = sublayer;
4424   pass_along[4] = GINT_TO_POINTER (1); // Confirm delete request
4425   pass_along[5] = vvp;
4426   pass_along[6] = iter;
4427   pass_along[7] = NULL; // For misc purposes - maybe track or waypoint
4428
4429   if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT || subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
4430   {
4431     rv = TRUE;
4432
4433     item = gtk_image_menu_item_new_from_stock ( GTK_STOCK_PROPERTIES, NULL );
4434     g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_properties_item), pass_along );
4435     gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
4436     gtk_widget_show ( item );
4437
4438     if (subtype == VIK_TRW_LAYER_SUBLAYER_TRACK) {
4439       VikTrwLayer *vtl = l;
4440       VikTrack *tr = g_hash_table_lookup ( vtl->tracks, sublayer );
4441       if (tr && tr->property_dialog)
4442         gtk_widget_set_sensitive(GTK_WIDGET(item), FALSE );
4443     }
4444
4445     item = gtk_image_menu_item_new_from_stock ( GTK_STOCK_CUT, NULL );
4446     g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_cut_item_cb), pass_along );
4447     gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
4448     gtk_widget_show ( item );
4449
4450     item = gtk_image_menu_item_new_from_stock ( GTK_STOCK_COPY, NULL );
4451     g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_copy_item_cb), pass_along );
4452     gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
4453     gtk_widget_show ( item );
4454
4455     item = gtk_image_menu_item_new_from_stock ( GTK_STOCK_DELETE, NULL );
4456     g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_item), pass_along );
4457     gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
4458     gtk_widget_show ( item );
4459
4460     if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT )
4461     {
4462       gboolean separator_created = FALSE;
4463
4464       /* could be a right-click using the tool */
4465       if ( vlp != NULL ) {
4466         item = gtk_menu_item_new ();
4467         gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
4468         gtk_widget_show ( item );
4469
4470         separator_created = TRUE;
4471
4472         item = gtk_image_menu_item_new_with_mnemonic ( _("_Goto") );
4473         gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_JUMP_TO, GTK_ICON_SIZE_MENU) );
4474         g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_waypoint), pass_along );
4475         gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
4476         gtk_widget_show ( item );
4477       }
4478
4479       VikWaypoint *wp = g_hash_table_lookup ( VIK_TRW_LAYER(l)->waypoints, sublayer );
4480
4481       if ( wp && wp->name ) {
4482         if ( is_valid_geocache_name ( wp->name ) ) {
4483
4484           if ( !separator_created ) {
4485             item = gtk_menu_item_new ();
4486             gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
4487             gtk_widget_show ( item );
4488             separator_created = TRUE;
4489           }
4490
4491           item = gtk_menu_item_new_with_mnemonic ( _("_Visit Geocache Webpage") );
4492           g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_waypoint_gc_webpage), pass_along );
4493           gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
4494           gtk_widget_show ( item );
4495         }
4496       }
4497
4498       if ( wp && wp->image )
4499       {
4500         if ( !separator_created ) {
4501           item = gtk_menu_item_new ();
4502           gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
4503           gtk_widget_show ( item );
4504           separator_created = TRUE;
4505         }
4506
4507         // Set up image paramater
4508         pass_along[5] = wp->image;
4509
4510         item = gtk_image_menu_item_new_with_mnemonic ( _("_Show Picture...") );
4511         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
4512         g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_show_picture), pass_along );
4513         gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
4514         gtk_widget_show ( item );
4515
4516 #ifdef VIK_CONFIG_GEOTAG
4517         GtkWidget *geotag_submenu = gtk_menu_new ();
4518         item = gtk_image_menu_item_new_with_mnemonic ( _("Update Geotag on _Image") );
4519         gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REFRESH, GTK_ICON_SIZE_MENU) );
4520         gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
4521         gtk_widget_show ( item );
4522         gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), geotag_submenu );
4523   
4524         item = gtk_menu_item_new_with_mnemonic ( _("_Update") );
4525         g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_geotagging_waypoint_mtime_update), pass_along );
4526         gtk_menu_shell_append (GTK_MENU_SHELL (geotag_submenu), item);
4527         gtk_widget_show ( item );
4528
4529         item = gtk_menu_item_new_with_mnemonic ( _("Update and _Keep File Timestamp") );
4530         g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_geotagging_waypoint_mtime_keep), pass_along );
4531         gtk_menu_shell_append (GTK_MENU_SHELL (geotag_submenu), item);
4532         gtk_widget_show ( item );
4533 #endif
4534       }
4535
4536     }
4537   }
4538
4539   if ( vlp && (subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINTS || subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT) )
4540   {
4541     rv = TRUE;
4542     item = gtk_image_menu_item_new_with_mnemonic ( _("_New Waypoint...") );
4543     gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NEW, GTK_ICON_SIZE_MENU) );
4544     g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_wp), pass_along );
4545     gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
4546     gtk_widget_show ( item );
4547   }
4548
4549   if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINTS )
4550   {
4551     item = gtk_image_menu_item_new_with_mnemonic ( _("_View All Waypoints") );
4552     gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ZOOM_FIT, GTK_ICON_SIZE_MENU) );
4553     g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_waypoints_view), pass_along );
4554     gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
4555     gtk_widget_show ( item );
4556
4557     item = gtk_image_menu_item_new_with_mnemonic ( _("Goto _Waypoint...") );
4558     gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_JUMP_TO, GTK_ICON_SIZE_MENU) );
4559     g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_wp), pass_along );
4560     gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
4561     gtk_widget_show ( item );
4562
4563     item = gtk_image_menu_item_new_with_mnemonic ( _("Delete _All Waypoints") );
4564     gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU) );
4565     g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_all_waypoints), pass_along );
4566     gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
4567     gtk_widget_show ( item );
4568
4569     item = gtk_image_menu_item_new_with_mnemonic ( _("_Delete Waypoints From Selection...") );
4570     gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
4571     g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_waypoints_from_selection), pass_along );
4572     gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
4573     gtk_widget_show ( item );
4574   }
4575
4576   if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACKS )
4577   {
4578     rv = TRUE;
4579
4580     item = gtk_image_menu_item_new_with_mnemonic ( _("_View All Tracks") );
4581     gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ZOOM_FIT, GTK_ICON_SIZE_MENU) );
4582     g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_tracks_view), pass_along );
4583     gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
4584     gtk_widget_show ( item );
4585
4586     item = gtk_image_menu_item_new_with_mnemonic ( _("Delete _All Tracks") );
4587     gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU) );
4588     g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_all_tracks), pass_along );
4589     gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
4590     gtk_widget_show ( item );
4591
4592     item = gtk_image_menu_item_new_with_mnemonic ( _("_Delete Tracks From Selection...") );
4593     gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
4594     g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_tracks_from_selection), pass_along );
4595     gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
4596     gtk_widget_show ( item );
4597   }
4598
4599   if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
4600   {
4601     GtkWidget *goto_submenu;
4602     item = gtk_menu_item_new ();
4603     gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
4604     gtk_widget_show ( item );
4605
4606     goto_submenu = gtk_menu_new ();
4607     item = gtk_image_menu_item_new_with_mnemonic ( _("_Goto") );
4608     gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_JUMP_TO, GTK_ICON_SIZE_MENU) );
4609     gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
4610     gtk_widget_show ( item );
4611     gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), goto_submenu );
4612
4613     item = gtk_image_menu_item_new_with_mnemonic ( _("_Startpoint") );
4614     gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GOTO_FIRST, GTK_ICON_SIZE_MENU) );
4615     g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_startpoint), pass_along );
4616     gtk_menu_shell_append ( GTK_MENU_SHELL(goto_submenu), item );
4617     gtk_widget_show ( item );
4618
4619     item = gtk_image_menu_item_new_with_mnemonic ( _("\"_Center\"") );
4620     gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_JUMP_TO, GTK_ICON_SIZE_MENU) );
4621     g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_center), pass_along );
4622     gtk_menu_shell_append ( GTK_MENU_SHELL(goto_submenu), item );
4623     gtk_widget_show ( item );
4624
4625     item = gtk_image_menu_item_new_with_mnemonic ( _("_Endpoint") );
4626     gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GOTO_LAST, GTK_ICON_SIZE_MENU) );
4627     g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_endpoint), pass_along );
4628     gtk_menu_shell_append ( GTK_MENU_SHELL(goto_submenu), item );
4629     gtk_widget_show ( item );
4630
4631     item = gtk_image_menu_item_new_with_mnemonic ( _("_Highest Altitude") );
4632     gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GOTO_TOP, GTK_ICON_SIZE_MENU) );
4633     g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_max_alt), pass_along );
4634     gtk_menu_shell_append ( GTK_MENU_SHELL(goto_submenu), item );
4635     gtk_widget_show ( item );
4636
4637     item = gtk_image_menu_item_new_with_mnemonic ( _("_Lowest Altitude") );
4638     gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GOTO_BOTTOM, GTK_ICON_SIZE_MENU) );
4639     g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_min_alt), pass_along );
4640     gtk_menu_shell_append ( GTK_MENU_SHELL(goto_submenu), item );
4641     gtk_widget_show ( item );
4642
4643     item = gtk_image_menu_item_new_with_mnemonic ( _("_Maximum Speed") );
4644     gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_MEDIA_FORWARD, GTK_ICON_SIZE_MENU) );
4645     g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_max_speed), pass_along );
4646     gtk_menu_shell_append ( GTK_MENU_SHELL(goto_submenu), item );
4647     gtk_widget_show ( item );
4648
4649     item = gtk_image_menu_item_new_with_mnemonic ( _("_View Track") );
4650     gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ZOOM_FIT, GTK_ICON_SIZE_MENU) );
4651     g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_track_view), pass_along );
4652     gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
4653     gtk_widget_show ( item );
4654
4655     item = gtk_menu_item_new_with_mnemonic ( _("_Merge By Time...") );
4656     g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_merge_by_timestamp), pass_along );
4657     gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
4658     gtk_widget_show ( item );
4659
4660     item = gtk_menu_item_new_with_mnemonic ( _("Merge _With Other Tracks...") );
4661     g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_merge_with_other), pass_along );
4662     gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
4663     gtk_widget_show ( item );
4664
4665     item = gtk_menu_item_new_with_mnemonic ( _("_Split By Time...") );
4666     g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_split_by_timestamp), pass_along );
4667     gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
4668     gtk_widget_show ( item );
4669
4670     item = gtk_menu_item_new_with_mnemonic ( _("Split By _Number of Points...") );
4671     g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_split_by_n_points), pass_along );
4672     gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
4673     gtk_widget_show ( item );
4674
4675     item = gtk_image_menu_item_new_with_mnemonic ( _("_Reverse Track") );
4676     gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_BACK, GTK_ICON_SIZE_MENU) );
4677     g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_reverse), pass_along );
4678     gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
4679     gtk_widget_show ( item );
4680
4681     /* ATM This function is only available via the layers panel, due to the method in finding out the maps in use */
4682     if ( vlp ) {
4683       item = gtk_image_menu_item_new_with_mnemonic ( _("Down_load Maps Along Track...") );
4684       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
4685       g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_download_map_along_track_cb), pass_along );
4686       gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
4687       gtk_widget_show ( item );
4688     }
4689
4690     item = gtk_image_menu_item_new_with_mnemonic ( _("_Apply DEM Data") );
4691     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
4692     g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_apply_dem_data), pass_along );
4693     gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
4694     gtk_widget_show ( item );
4695
4696     item = gtk_image_menu_item_new_with_mnemonic ( _("Export Trac_k as GPX...") );
4697     gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_HARDDISK, GTK_ICON_SIZE_MENU) );
4698     g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_gpx_track), pass_along );
4699     gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
4700     gtk_widget_show ( item );
4701
4702     item = gtk_image_menu_item_new_with_mnemonic ( _("E_xtend Track End") );
4703     gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ADD, GTK_ICON_SIZE_MENU) );
4704     g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_extend_track_end), pass_along );
4705     gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
4706     gtk_widget_show ( item );
4707
4708     item = gtk_image_menu_item_new_with_mnemonic ( _("Extend _Using Route Finder") );
4709     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
4710     g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_extend_track_end_route_finder), pass_along );
4711     gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
4712     gtk_widget_show ( item );
4713
4714 #ifdef VIK_CONFIG_OPENSTREETMAP
4715     item = gtk_image_menu_item_new_with_mnemonic ( _("Upload to _OSM...") );
4716     // Convert internal pointer into actual track for usage outside this file
4717     pass_along[7] = g_hash_table_lookup ( l->tracks, sublayer);
4718     g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(osm_traces_upload_track_cb), pass_along );
4719     gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_UP, GTK_ICON_SIZE_MENU) );
4720     gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
4721     gtk_widget_show ( item );
4722 #endif
4723
4724     if ( is_valid_google_route ( l, sublayer ) )
4725     {
4726       item = gtk_image_menu_item_new_with_mnemonic ( _("_View Google Directions") );
4727       gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NETWORK, GTK_ICON_SIZE_MENU) );
4728       g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_track_google_route_webpage), pass_along );
4729       gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
4730       gtk_widget_show ( item );
4731     }
4732
4733     item = gtk_image_menu_item_new_with_mnemonic ( _("Use with _Filter") );
4734     gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
4735     g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_track_use_with_filter), pass_along );
4736     gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
4737     gtk_widget_show ( item );
4738
4739     /* ATM This function is only available via the layers panel, due to needing a vlp */
4740     if ( vlp ) {
4741       item = a_acquire_track_menu ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(l)), vlp,
4742                                     vik_layers_panel_get_viewport(VIK_LAYERS_PANEL(vlp)),
4743                                     g_hash_table_lookup ( l->tracks, (gchar *) sublayer ) );
4744       if ( item ) {
4745         gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
4746         gtk_widget_show ( item );
4747       }
4748     }
4749
4750 #ifdef VIK_CONFIG_GEOTAG
4751   item = gtk_menu_item_new_with_mnemonic ( _("Geotag _Images...") );
4752   g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_geotagging_track), pass_along );
4753   gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
4754   gtk_widget_show ( item );
4755 #endif
4756
4757     // Only show on viewport popmenu when a trackpoint is selected
4758     if ( ! vlp && l->current_tpl ) {
4759       // Add separator
4760       item = gtk_menu_item_new ();
4761       gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
4762       gtk_widget_show ( item );
4763
4764       item = gtk_image_menu_item_new_with_mnemonic ( _("_Edit Trackpoint") );
4765       gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_PROPERTIES, GTK_ICON_SIZE_MENU) );
4766       g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_edit_trackpoint), pass_along );
4767       gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
4768       gtk_widget_show ( item );
4769     }
4770
4771   }
4772
4773   return rv;
4774 }
4775
4776 static void trw_layer_insert_tp_after_current_tp ( VikTrwLayer *vtl )
4777 {
4778   /* sanity checks */
4779   if (!vtl->current_tpl)
4780     return;
4781   if (!vtl->current_tpl->next)
4782     return;
4783
4784   VikTrackpoint *tp_current = VIK_TRACKPOINT(vtl->current_tpl->data);
4785   VikTrackpoint *tp_next = VIK_TRACKPOINT(vtl->current_tpl->next->data);
4786
4787   /* Use current and next trackpoints to form a new track point which is inserted into the tracklist */
4788   if ( tp_next ) {
4789
4790     VikTrackpoint *tp_new = vik_trackpoint_new();
4791     struct LatLon ll_current, ll_next;
4792     vik_coord_to_latlon ( &tp_current->coord, &ll_current );
4793     vik_coord_to_latlon ( &tp_next->coord, &ll_next );
4794
4795     /* main positional interpolation */
4796     struct LatLon ll_new = { (ll_current.lat+ll_next.lat)/2, (ll_current.lon+ll_next.lon)/2 };
4797     vik_coord_load_from_latlon ( &(tp_new->coord), vtl->coord_mode, &ll_new );
4798
4799     /* Now other properties that can be interpolated */
4800     tp_new->altitude = (tp_current->altitude + tp_next->altitude) / 2;
4801
4802     if (tp_current->has_timestamp && tp_next->has_timestamp) {
4803       /* Note here the division is applied to each part, then added
4804          This is to avoid potential overflow issues with a 32 time_t for dates after midpoint of this Unix time on 2004/01/04 */
4805       tp_new->timestamp = (tp_current->timestamp/2) + (tp_next->timestamp/2);
4806       tp_new->has_timestamp = TRUE;
4807     }
4808
4809     if (tp_current->speed != NAN && tp_next->speed != NAN)
4810       tp_new->speed = (tp_current->speed + tp_next->speed)/2;
4811
4812     /* TODO - improve interpolation of course, as it may not be correct.
4813        if courses in degrees are 350 + 020, the mid course more likely to be 005 (not 185)
4814        [similar applies if value is in radians] */
4815     if (tp_current->course != NAN && tp_next->course != NAN)
4816       tp_new->speed = (tp_current->course + tp_next->course)/2;
4817
4818     /* DOP / sat values remain at defaults as not they do not seem applicable to a dreamt up point */
4819
4820     /* Insert new point into the trackpoints list after the current TP */
4821     VikTrack *tr = g_hash_table_lookup ( vtl->tracks, vtl->current_tp_id );
4822     gint index =  g_list_index ( tr->trackpoints, tp_current );
4823     if ( index > -1 ) {
4824       tr->trackpoints = g_list_insert (tr->trackpoints, tp_new, index+1 );
4825     }
4826   }
4827 }
4828
4829 /* to be called when last_tpl no longer exists. */
4830 static void trw_layer_cancel_last_tp ( VikTrwLayer *vtl )
4831 {
4832   if ( vtl->tpwin ) /* can't join with a non-existant TP. */
4833     vik_trw_layer_tpwin_disable_join ( vtl->tpwin );
4834   vtl->last_tpl = NULL;
4835   vtl->last_tp_track = NULL;
4836 }
4837
4838 static void trw_layer_cancel_current_tp ( VikTrwLayer *vtl, gboolean destroy )
4839 {
4840   if ( vtl->tpwin )
4841   {
4842     if ( destroy)
4843     {
4844       gtk_widget_destroy ( GTK_WIDGET(vtl->tpwin) );
4845       vtl->tpwin = NULL;
4846     }
4847     else
4848       vik_trw_layer_tpwin_set_empty ( vtl->tpwin );
4849   }
4850   if ( vtl->current_tpl )
4851   {
4852     vtl->current_tpl = NULL;
4853     vtl->current_tp_track = NULL;
4854     vtl->current_tp_id = NULL;
4855     vik_layer_emit_update(VIK_LAYER(vtl), FALSE);
4856   }
4857 }
4858
4859 static void trw_layer_tpwin_response ( VikTrwLayer *vtl, gint response )
4860 {
4861   g_assert ( vtl->tpwin != NULL );
4862   if ( response == VIK_TRW_LAYER_TPWIN_CLOSE )
4863     trw_layer_cancel_current_tp ( vtl, TRUE );
4864
4865   if ( vtl->current_tpl == NULL )
4866     return;
4867
4868   if ( response == VIK_TRW_LAYER_TPWIN_SPLIT && vtl->current_tpl->next && vtl->current_tpl->prev )
4869   {
4870     gchar *name = get_new_unique_sublayer_name(vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, vtl->current_tp_track->name);
4871     if ( ( name = a_dialog_new_track ( GTK_WINDOW(vtl->tpwin), vtl->tracks, name ) ) )
4872     {
4873       VikTrack *tr = vik_track_new ();
4874       GList *newglist = g_list_alloc ();
4875       newglist->prev = NULL;
4876       newglist->next = vtl->current_tpl->next;
4877       newglist->data = vik_trackpoint_copy(VIK_TRACKPOINT(vtl->current_tpl->data));
4878       tr->trackpoints = newglist;
4879
4880       vtl->current_tpl->next->prev = newglist; /* end old track here */
4881       vtl->current_tpl->next = NULL;
4882
4883       vtl->current_tpl = newglist; /* change tp to first of new track. */
4884       vtl->current_tp_track->name = name;
4885
4886       vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, vtl->current_tp_track->name );
4887
4888       tr->visible = TRUE;
4889
4890       vik_trw_layer_add_track ( vtl, name, tr );
4891       vik_layer_emit_update(VIK_LAYER(vtl), FALSE);
4892     }
4893   }
4894   else if ( response == VIK_TRW_LAYER_TPWIN_DELETE )
4895   {
4896     VikTrack *tr = g_hash_table_lookup ( vtl->tracks, vtl->current_tp_id );
4897     if ( tr == NULL )
4898       return;
4899
4900     GList *new_tpl;
4901     /* can't join with a non-existent trackpoint */
4902     vtl->last_tpl = NULL;
4903     vtl->last_tp_track = NULL;
4904
4905     if ( (new_tpl = vtl->current_tpl->next) || (new_tpl = vtl->current_tpl->prev) )
4906     {
4907       if ( VIK_TRACKPOINT(vtl->current_tpl->data)->newsegment && vtl->current_tpl->next )
4908         VIK_TRACKPOINT(vtl->current_tpl->next->data)->newsegment = TRUE; /* don't concat segments on del */
4909
4910       tr->trackpoints = g_list_remove_link ( tr->trackpoints, vtl->current_tpl ); /* this nulls current_tpl->prev and next */
4911
4912       /* at this point the old trackpoint exists, but the list links are correct (new), so it is safe to do this. */
4913       if ( vtl->current_tp_track )
4914         vik_trw_layer_tpwin_set_tp ( vtl->tpwin, new_tpl, vtl->current_tp_track->name );
4915
4916       trw_layer_cancel_last_tp ( vtl );
4917
4918       g_free ( vtl->current_tpl->data ); /* TODO: vik_trackpoint_free() */
4919       g_list_free_1 ( vtl->current_tpl );
4920       vtl->current_tpl = new_tpl;
4921       vik_layer_emit_update(VIK_LAYER(vtl), FALSE);
4922     }
4923     else
4924     {
4925       tr->trackpoints = g_list_remove_link ( tr->trackpoints, vtl->current_tpl );
4926       g_free ( vtl->current_tpl->data ); /* TODO longone: vik_trackpoint_new() and vik_trackpoint_free() */
4927       g_list_free_1 ( vtl->current_tpl );
4928       trw_layer_cancel_current_tp ( vtl, FALSE );
4929     }
4930   }
4931   else if ( response == VIK_TRW_LAYER_TPWIN_FORWARD && vtl->current_tpl->next )
4932   {
4933     vtl->last_tpl = vtl->current_tpl;
4934     if ( vtl->current_tp_track )
4935       vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl = vtl->current_tpl->next, vtl->current_tp_track->name );
4936     vik_layer_emit_update(VIK_LAYER(vtl), FALSE); /* TODO longone: either move or only update if tp is inside drawing window */
4937   }
4938   else if ( response == VIK_TRW_LAYER_TPWIN_BACK && vtl->current_tpl->prev )
4939   {
4940     vtl->last_tpl = vtl->current_tpl;
4941     if ( vtl->current_tp_track )
4942       vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl = vtl->current_tpl->prev, vtl->current_tp_track->name );
4943     vik_layer_emit_update(VIK_LAYER(vtl), FALSE);
4944   }
4945   else if ( response == VIK_TRW_LAYER_TPWIN_JOIN )
4946   {
4947     // Check tracks exist and are different before joining
4948     if ( ! vtl->last_tp_track || ! vtl->current_tp_track || vtl->last_tp_track == vtl->current_tp_track )
4949       return;
4950
4951     VikTrack *tr1 = vtl->last_tp_track;
4952     VikTrack *tr2 = vtl->current_tp_track;
4953
4954     VikTrack *tr_first = tr1, *tr_last = tr2;
4955
4956     if ( (!vtl->last_tpl->next) && (!vtl->current_tpl->next) ) /* both endpoints */
4957       vik_track_reverse ( tr2 ); /* reverse the second, that way second track clicked will be later. */
4958     else if ( (!vtl->last_tpl->prev) && (!vtl->current_tpl->prev) )
4959       vik_track_reverse ( tr1 );
4960     else if ( (!vtl->last_tpl->prev) && (!vtl->current_tpl->next) ) /* clicked startpoint, then endpoint -- concat end to start */
4961     {
4962       tr_first = tr2;
4963       tr_last = tr1;
4964     }
4965     /* default -- clicked endpoint then startpoint -- connect endpoint to startpoint */
4966
4967     if ( tr_last->trackpoints ) /* deleting this part here joins them into 1 segmented track. useful but there's no place in the UI for this feature. segments should be deprecated anyway. */
4968       VIK_TRACKPOINT(tr_last->trackpoints->data)->newsegment = FALSE;
4969     tr1->trackpoints = g_list_concat ( tr_first->trackpoints, tr_last->trackpoints );
4970     tr2->trackpoints = NULL;
4971
4972     vtl->current_tp_track = vtl->last_tp_track; /* current_tp stays the same (believe it or not!) */
4973     vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, vtl->current_tp_track->name );
4974
4975     /* if we did this before, trw_layer_delete_track would have canceled the current tp because
4976      * it was the current track. canceling the current tp would have set vtl->current_tpl to NULL */
4977     vik_trw_layer_delete_track ( vtl, vtl->current_tp_track );
4978
4979     trw_layer_cancel_last_tp ( vtl ); /* same TP, can't join. */
4980     vik_layer_emit_update(VIK_LAYER(vtl), FALSE);
4981   }
4982   else if ( response == VIK_TRW_LAYER_TPWIN_INSERT && vtl->current_tpl->next )
4983   {
4984     trw_layer_insert_tp_after_current_tp ( vtl );
4985     vik_layer_emit_update(VIK_LAYER(vtl), FALSE);
4986   }
4987   else if ( response == VIK_TRW_LAYER_TPWIN_DATA_CHANGED )
4988     vik_layer_emit_update(VIK_LAYER(vtl), FALSE);
4989 }
4990
4991 static void trw_layer_tpwin_init ( VikTrwLayer *vtl )
4992 {
4993   if ( ! vtl->tpwin )
4994   {
4995     vtl->tpwin = vik_trw_layer_tpwin_new ( VIK_GTK_WINDOW_FROM_LAYER(vtl) );
4996     g_signal_connect_swapped ( GTK_DIALOG(vtl->tpwin), "response", G_CALLBACK(trw_layer_tpwin_response), vtl );
4997     /* connect signals -- DELETE SIGNAL VERY IMPORTANT TO SET TO NULL */
4998     g_signal_connect_swapped ( vtl->tpwin, "delete-event", G_CALLBACK(trw_layer_cancel_current_tp), vtl );
4999     gtk_widget_show_all ( GTK_WIDGET(vtl->tpwin) );
5000   }
5001   if ( vtl->current_tpl )
5002     if ( vtl->current_tp_track )
5003       vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, vtl->current_tp_track->name );
5004   /* set layer name and TP data */
5005 }
5006
5007 /***************************************************************************
5008  ** Tool code
5009  ***************************************************************************/
5010
5011 /*** Utility data structures and functions ****/
5012
5013 typedef struct {
5014   gint x, y;
5015   gint closest_x, closest_y;
5016   gpointer *closest_wp_id;
5017   VikWaypoint *closest_wp;
5018   VikViewport *vvp;
5019 } WPSearchParams;
5020
5021 typedef struct {
5022   gint x, y;
5023   gint closest_x, closest_y;
5024   gpointer closest_track_id;
5025   VikTrackpoint *closest_tp;
5026   VikViewport *vvp;
5027   GList *closest_tpl;
5028 } TPSearchParams;
5029
5030 static void waypoint_search_closest_tp ( gpointer id, VikWaypoint *wp, WPSearchParams *params )
5031 {
5032   gint x, y;
5033   if ( !wp->visible )
5034     return;
5035
5036   vik_viewport_coord_to_screen ( params->vvp, &(wp->coord), &x, &y );
5037
5038   // If waypoint has an image then use the image size to select
5039   if ( wp->image ) {
5040     gint slackx, slacky;
5041     slackx = wp->image_width / 2;
5042     slacky = wp->image_height / 2;
5043
5044     if (    x <= params->x + slackx && x >= params->x - slackx
5045          && y <= params->y + slacky && y >= params->y - slacky ) {
5046       params->closest_wp_id = id;
5047       params->closest_wp = wp;
5048       params->closest_x = x;
5049       params->closest_y = y;
5050     }
5051   }
5052   else if ( abs (x - params->x) <= WAYPOINT_SIZE_APPROX && abs (y - params->y) <= WAYPOINT_SIZE_APPROX &&
5053             ((!params->closest_wp) ||        /* was the old waypoint we already found closer than this one? */
5054              abs(x - params->x)+abs(y - params->y) < abs(x - params->closest_x)+abs(y - params->closest_y)))
5055     {
5056       params->closest_wp_id = id;
5057       params->closest_wp = wp;
5058       params->closest_x = x;
5059       params->closest_y = y;
5060     }
5061 }
5062
5063 static void track_search_closest_tp ( gpointer id, VikTrack *t, TPSearchParams *params )
5064 {
5065   GList *tpl = t->trackpoints;
5066   VikTrackpoint *tp;
5067
5068   if ( !t->visible )
5069     return;
5070
5071   while (tpl)
5072   {
5073     gint x, y;
5074     tp = VIK_TRACKPOINT(tpl->data);
5075
5076     vik_viewport_coord_to_screen ( params->vvp, &(tp->coord), &x, &y );
5077  
5078     if ( abs (x - params->x) <= TRACKPOINT_SIZE_APPROX && abs (y - params->y) <= TRACKPOINT_SIZE_APPROX &&
5079         ((!params->closest_tp) ||        /* was the old trackpoint we already found closer than this one? */
5080           abs(x - params->x)+abs(y - params->y) < abs(x - params->closest_x)+abs(y - params->closest_y)))
5081     {
5082       params->closest_track_id = id;
5083       params->closest_tp = tp;
5084       params->closest_tpl = tpl;
5085       params->closest_x = x;
5086       params->closest_y = y;
5087     }
5088     tpl = tpl->next;
5089   }
5090 }
5091
5092 static VikTrackpoint *closest_tp_in_five_pixel_interval ( VikTrwLayer *vtl, VikViewport *vvp, gint x, gint y )
5093 {
5094   TPSearchParams params;
5095   params.x = x;
5096   params.y = y;
5097   params.vvp = vvp;
5098   params.closest_track_id = NULL;
5099   params.closest_tp = NULL;
5100   g_hash_table_foreach ( vtl->tracks, (GHFunc) track_search_closest_tp, &params);
5101   return params.closest_tp;
5102 }
5103
5104 static VikWaypoint *closest_wp_in_five_pixel_interval ( VikTrwLayer *vtl, VikViewport *vvp, gint x, gint y )
5105 {
5106   WPSearchParams params;
5107   params.x = x;
5108   params.y = y;
5109   params.vvp = vvp;
5110   params.closest_wp = NULL;
5111   params.closest_wp_id = NULL;
5112   g_hash_table_foreach ( vtl->waypoints, (GHFunc) waypoint_search_closest_tp, &params);
5113   return params.closest_wp;
5114 }
5115
5116
5117 // Some forward declarations
5118 static void marker_begin_move ( tool_ed_t *t, gint x, gint y );
5119 static void marker_moveto ( tool_ed_t *t, gint x, gint y );
5120 static void marker_end_move ( tool_ed_t *t );
5121 //
5122
5123 static gboolean trw_layer_select_move ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp, tool_ed_t* t )
5124 {
5125   if ( t->holding ) {
5126     VikCoord new_coord;
5127     vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
5128
5129     // Here always allow snapping back to the original location
5130     //  this is useful when one decides not to move the thing afterall
5131     // If one wants to move the item only a little bit then don't hold down the 'snap' key!
5132  
5133     // snap to TP
5134     if ( event->state & GDK_CONTROL_MASK )
5135     {
5136       VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
5137       if ( tp )
5138         new_coord = tp->coord;
5139     }
5140
5141     // snap to WP
5142     if ( event->state & GDK_SHIFT_MASK )
5143     {
5144       VikWaypoint *wp = closest_wp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
5145       if ( wp )
5146         new_coord = wp->coord;
5147     }
5148     
5149     gint x, y;
5150     vik_viewport_coord_to_screen ( vvp, &new_coord, &x, &y );
5151
5152     marker_moveto ( t, x, y );
5153
5154     return TRUE;
5155   }
5156   return FALSE;
5157 }
5158
5159 static gboolean trw_layer_select_release ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp, tool_ed_t* t )
5160 {
5161   if ( t->holding && event->button == 1 )
5162   {
5163     VikCoord new_coord;
5164     vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
5165
5166     // snap to TP
5167     if ( event->state & GDK_CONTROL_MASK )
5168     {
5169       VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
5170       if ( tp )
5171         new_coord = tp->coord;
5172     }
5173
5174     // snap to WP
5175     if ( event->state & GDK_SHIFT_MASK )
5176     {
5177       VikWaypoint *wp = closest_wp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
5178       if ( wp )
5179         new_coord = wp->coord;
5180     }
5181
5182     marker_end_move ( t );
5183
5184     // Determine if working on a waypoint or a trackpoint
5185     if ( t->is_waypoint )
5186       vtl->current_wp->coord = new_coord;
5187     else {
5188       if ( vtl->current_tpl ) {
5189         VIK_TRACKPOINT(vtl->current_tpl->data)->coord = new_coord;
5190       
5191         if ( vtl->tpwin )
5192           if ( vtl->current_tp_track )
5193             vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, vtl->current_tp_track->name );
5194
5195         // Don't really know what this is for but seems like it might be handy...
5196         /* can't join with itself! */
5197         trw_layer_cancel_last_tp ( vtl );
5198       }
5199     }
5200
5201     // Reset
5202     vtl->current_wp    = NULL;
5203     vtl->current_wp_id = NULL;
5204     trw_layer_cancel_current_tp ( vtl, FALSE );
5205
5206     vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
5207     return TRUE;
5208   }
5209   return FALSE;
5210 }
5211
5212 /*
5213   Returns true if a waypoint or track is found near the requested event position for this particular layer
5214   The item found is automatically selected
5215   This is a tool like feature but routed via the layer interface, since it's instigated by a 'global' layer tool in vikwindow.c
5216  */
5217 static gboolean trw_layer_select_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp, tool_ed_t* tet )
5218 {
5219   if ( event->button != 1 )
5220     return FALSE;
5221
5222   if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
5223     return FALSE;
5224
5225   if ( !vtl->tracks_visible && !vtl->waypoints_visible )
5226     return FALSE;
5227
5228   // Go for waypoints first as these often will be near a track, but it's likely the wp is wanted rather then the track
5229
5230   if (vtl->waypoints_visible) {
5231     WPSearchParams wp_params;
5232     wp_params.vvp = vvp;
5233     wp_params.x = event->x;
5234     wp_params.y = event->y;
5235     wp_params.closest_wp_id = NULL;
5236     wp_params.closest_wp = NULL;
5237
5238     g_hash_table_foreach ( vtl->waypoints, (GHFunc) waypoint_search_closest_tp, &wp_params);
5239
5240     if ( wp_params.closest_wp )  {
5241
5242       // Select
5243       vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, g_hash_table_lookup ( vtl->waypoints_iters, wp_params.closest_wp_id ), TRUE );
5244
5245       // Too easy to move it so must be holding shift to start immediately moving it
5246       //   or otherwise be previously selected but not have an image (otherwise clicking within image bounds (again) moves it)
5247       if ( event->state & GDK_SHIFT_MASK ||
5248            ( vtl->current_wp == wp_params.closest_wp && !vtl->current_wp->image ) ) {
5249         // Put into 'move buffer'
5250         // NB vvp & vw already set in tet
5251         tet->vtl = (gpointer)vtl;
5252         tet->is_waypoint = TRUE;
5253       
5254         marker_begin_move (tet, event->x, event->y);
5255       }
5256
5257       vtl->current_wp =    wp_params.closest_wp;
5258       vtl->current_wp_id = wp_params.closest_wp_id;
5259
5260       vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
5261
5262       return TRUE;
5263     }
5264   }
5265
5266   if (vtl->tracks_visible) {
5267     TPSearchParams tp_params;
5268     tp_params.vvp = vvp;
5269     tp_params.x = event->x;
5270     tp_params.y = event->y;
5271     tp_params.closest_track_id = NULL;
5272     tp_params.closest_tp = NULL;
5273
5274     g_hash_table_foreach ( vtl->tracks, (GHFunc) track_search_closest_tp, &tp_params);
5275
5276     if ( tp_params.closest_tp )  {
5277
5278       // Always select + highlight the track
5279       vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, g_hash_table_lookup ( vtl->tracks_iters, tp_params.closest_track_id ), TRUE );
5280
5281       tet->is_waypoint = FALSE;
5282
5283       // Select the Trackpoint
5284       // Can move it immediately when control held or it's the previously selected tp
5285       if ( event->state & GDK_CONTROL_MASK ||
5286            vtl->current_tpl == tp_params.closest_tpl ) {
5287         // Put into 'move buffer'
5288         // NB vvp & vw already set in tet
5289         tet->vtl = (gpointer)vtl;
5290         marker_begin_move (tet, event->x, event->y);
5291       }
5292
5293       vtl->current_tpl = tp_params.closest_tpl;
5294       vtl->current_tp_id = tp_params.closest_track_id;
5295       vtl->current_tp_track = g_hash_table_lookup ( vtl->tracks, tp_params.closest_track_id );
5296
5297       set_statusbar_msg_info_trkpt ( vtl, tp_params.closest_tp );
5298
5299       if ( vtl->tpwin )
5300         vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, vtl->current_tp_track->name );
5301
5302       vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
5303       return TRUE;
5304     }
5305   }
5306
5307   /* these aren't the droids you're looking for */
5308   vtl->current_wp    = NULL;
5309   vtl->current_wp_id = NULL;
5310   trw_layer_cancel_current_tp ( vtl, FALSE );
5311
5312   // Blank info
5313   vik_statusbar_set_message ( vik_window_get_statusbar (VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl))), VIK_STATUSBAR_INFO, "" );
5314
5315   return FALSE;
5316 }
5317
5318 static gboolean trw_layer_show_selected_viewport_menu ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
5319 {
5320   if ( event->button != 3 )
5321     return FALSE;
5322
5323   if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
5324     return FALSE;
5325
5326   if ( !vtl->tracks_visible && !vtl->waypoints_visible )
5327     return FALSE;
5328
5329   /* Post menu for the currently selected item */
5330
5331   /* See if a track is selected */
5332   VikTrack *track = (VikTrack*)vik_window_get_selected_track ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(vtl) );
5333   if ( track && track->visible ) {
5334
5335     if ( vik_window_get_selected_name ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(vtl) ) ) {
5336
5337       if ( vtl->track_right_click_menu )
5338         gtk_object_sink ( GTK_OBJECT(vtl->track_right_click_menu) );
5339
5340       vtl->track_right_click_menu = GTK_MENU ( gtk_menu_new () );
5341       
5342       trw_layer_sublayer_add_menu_items ( vtl,
5343                                           vtl->track_right_click_menu,
5344                                           NULL,
5345                                           VIK_TRW_LAYER_SUBLAYER_TRACK,
5346                                           vik_window_get_selected_name ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(vtl) ),
5347                                           g_hash_table_lookup ( vtl->tracks_iters, vik_window_get_selected_name ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(vtl) ) ),
5348                                           vvp);
5349
5350       gtk_menu_popup ( vtl->track_right_click_menu, NULL, NULL, NULL, NULL, event->button, gtk_get_current_event_time() );
5351         
5352       return TRUE;
5353     }
5354   }
5355
5356   /* See if a waypoint is selected */
5357   VikWaypoint *waypoint = (VikWaypoint*)vik_window_get_selected_waypoint ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(vtl) );
5358   if ( waypoint && waypoint->visible ) {
5359     if ( vik_window_get_selected_name ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(vtl) ) ) {
5360
5361       if ( vtl->wp_right_click_menu )
5362         gtk_object_sink ( GTK_OBJECT(vtl->wp_right_click_menu) );
5363
5364       vtl->wp_right_click_menu = GTK_MENU ( gtk_menu_new () );
5365       trw_layer_sublayer_add_menu_items ( vtl,
5366                                           vtl->wp_right_click_menu,
5367                                           NULL,
5368                                           VIK_TRW_LAYER_SUBLAYER_WAYPOINT,
5369                                           vik_window_get_selected_name ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(vtl) ),
5370                                           g_hash_table_lookup ( vtl->waypoints_iters, vik_window_get_selected_name ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(vtl) ) ),
5371                                           vvp);
5372       gtk_menu_popup ( vtl->wp_right_click_menu, NULL, NULL, NULL, NULL, event->button, gtk_get_current_event_time() );
5373
5374       return TRUE;
5375     }
5376   }
5377
5378   return FALSE;
5379 }
5380
5381 /* background drawing hook, to be passed the viewport */
5382 static gboolean tool_sync_done = TRUE;
5383
5384 static gboolean tool_sync(gpointer data)
5385 {
5386   VikViewport *vvp = data;
5387   gdk_threads_enter();
5388   vik_viewport_sync(vvp);
5389   tool_sync_done = TRUE;
5390   gdk_threads_leave();
5391   return FALSE;
5392 }
5393
5394 static void marker_begin_move ( tool_ed_t *t, gint x, gint y )
5395 {
5396   t->holding = TRUE;
5397   t->gc = vik_viewport_new_gc (t->vvp, "black", 2);
5398   gdk_gc_set_function ( t->gc, GDK_INVERT );
5399   vik_viewport_draw_rectangle ( t->vvp, t->gc, FALSE, x-3, y-3, 6, 6 );
5400   vik_viewport_sync(t->vvp);
5401   t->oldx = x;
5402   t->oldy = y;
5403 }
5404
5405 static void marker_moveto ( tool_ed_t *t, gint x, gint y )
5406 {
5407   VikViewport *vvp =  t->vvp;
5408   vik_viewport_draw_rectangle ( vvp, t->gc, FALSE, t->oldx-3, t->oldy-3, 6, 6 );
5409   vik_viewport_draw_rectangle ( vvp, t->gc, FALSE, x-3, y-3, 6, 6 );
5410   t->oldx = x;
5411   t->oldy = y;
5412
5413   if (tool_sync_done) {
5414     g_idle_add_full (G_PRIORITY_HIGH_IDLE + 10, tool_sync, vvp, NULL);
5415     tool_sync_done = FALSE;
5416   }
5417 }
5418
5419 static void marker_end_move ( tool_ed_t *t )
5420 {
5421   vik_viewport_draw_rectangle ( t->vvp, t->gc, FALSE, t->oldx-3, t->oldy-3, 6, 6 );
5422   g_object_unref ( t->gc );
5423   t->holding = FALSE;
5424 }
5425
5426 /*** Edit waypoint ****/
5427
5428 static gpointer tool_edit_waypoint_create ( VikWindow *vw, VikViewport *vvp)
5429 {
5430   tool_ed_t *t = g_new(tool_ed_t, 1);
5431   t->vvp = vvp;
5432   t->holding = FALSE;
5433   return t;
5434 }
5435
5436 static gboolean tool_edit_waypoint_click ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data )
5437 {
5438   WPSearchParams params;
5439   tool_ed_t *t = data;
5440   VikViewport *vvp = t->vvp;
5441
5442   if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
5443     return FALSE;
5444
5445   if ( t->holding ) {
5446     return TRUE;
5447   }
5448
5449   if ( !vtl->vl.visible || !vtl->waypoints_visible )
5450     return FALSE;
5451
5452   if ( vtl->current_wp && vtl->current_wp->visible )
5453   {
5454     /* first check if current WP is within area (other may be 'closer', but we want to move the current) */
5455     gint x, y;
5456     vik_viewport_coord_to_screen ( vvp, &(vtl->current_wp->coord), &x, &y );
5457
5458     if ( abs(x - event->x) <= WAYPOINT_SIZE_APPROX &&
5459          abs(y - event->y) <= WAYPOINT_SIZE_APPROX )
5460     {
5461       if ( event->button == 3 )
5462         vtl->waypoint_rightclick = TRUE; /* remember that we're clicking; other layers will ignore release signal */
5463       else {
5464         marker_begin_move(t, event->x, event->y);
5465       }
5466       return TRUE;
5467     }
5468   }
5469
5470   params.vvp = vvp;
5471   params.x = event->x;
5472   params.y = event->y;
5473   params.closest_wp_id = NULL;
5474   /* TODO: should get track listitem so we can break it up, make a new track, mess it up, all that. */
5475   params.closest_wp = NULL;
5476   g_hash_table_foreach ( vtl->waypoints, (GHFunc) waypoint_search_closest_tp, &params);
5477   if ( vtl->current_wp == params.closest_wp && vtl->current_wp != NULL )
5478   {
5479     /* how do we get here? I'm putting in the abort until we can figure it out. -alex */
5480     marker_begin_move(t, event->x, event->y);
5481     g_critical("shouldn't be here");
5482     exit(1);
5483   }
5484   else if ( params.closest_wp )
5485   {
5486     if ( event->button == 3 )
5487       vtl->waypoint_rightclick = TRUE; /* remember that we're clicking; other layers will ignore release signal */
5488     else
5489       vtl->waypoint_rightclick = FALSE;
5490
5491     vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, g_hash_table_lookup ( vtl->waypoints_iters, params.closest_wp_id ), TRUE );
5492
5493     vtl->current_wp = params.closest_wp;
5494     vtl->current_wp_id = params.closest_wp_id;
5495
5496     /* could make it so don't update if old WP is off screen and new is null but oh well */
5497     vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
5498     return TRUE;
5499   }
5500
5501   vtl->current_wp = NULL;
5502   vtl->current_wp_id = NULL;
5503   vtl->waypoint_rightclick = FALSE;
5504   vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
5505   return FALSE;
5506 }
5507
5508 static gboolean tool_edit_waypoint_move ( VikTrwLayer *vtl, GdkEventMotion *event, gpointer data )
5509 {
5510   tool_ed_t *t = data;
5511   VikViewport *vvp = t->vvp;
5512
5513   if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
5514     return FALSE;
5515
5516   if ( t->holding ) {
5517     VikCoord new_coord;
5518     vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
5519
5520     /* snap to TP */
5521     if ( event->state & GDK_CONTROL_MASK )
5522     {
5523       VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
5524       if ( tp )
5525         new_coord = tp->coord;
5526     }
5527
5528     /* snap to WP */
5529     if ( event->state & GDK_SHIFT_MASK )
5530     {
5531       VikWaypoint *wp = closest_wp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
5532       if ( wp && wp != vtl->current_wp )
5533         new_coord = wp->coord;
5534     }
5535     
5536     { 
5537       gint x, y;
5538       vik_viewport_coord_to_screen ( vvp, &new_coord, &x, &y );
5539
5540       marker_moveto ( t, x, y );
5541     } 
5542     return TRUE;
5543   }
5544   return FALSE;
5545 }
5546
5547 static gboolean tool_edit_waypoint_release ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data )
5548 {
5549   tool_ed_t *t = data;
5550   VikViewport *vvp = t->vvp;
5551
5552   if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
5553     return FALSE;
5554   
5555   if ( t->holding && event->button == 1 )
5556   {
5557     VikCoord new_coord;
5558     vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
5559
5560     /* snap to TP */
5561     if ( event->state & GDK_CONTROL_MASK )
5562     {
5563       VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
5564       if ( tp )
5565         new_coord = tp->coord;
5566     }
5567
5568     /* snap to WP */
5569     if ( event->state & GDK_SHIFT_MASK )
5570     {
5571       VikWaypoint *wp = closest_wp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
5572       if ( wp && wp != vtl->current_wp )
5573         new_coord = wp->coord;
5574     }
5575
5576     marker_end_move ( t );
5577
5578     vtl->current_wp->coord = new_coord;
5579     vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
5580     return TRUE;
5581   }
5582   /* PUT IN RIGHT PLACE!!! */
5583   if ( event->button == 3 && vtl->waypoint_rightclick )
5584   {
5585     if ( vtl->wp_right_click_menu )
5586       g_object_ref_sink ( G_OBJECT(vtl->wp_right_click_menu) );
5587     if ( vtl->current_wp ) {
5588       vtl->wp_right_click_menu = GTK_MENU ( gtk_menu_new () );
5589       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 );
5590       gtk_menu_popup ( vtl->wp_right_click_menu, NULL, NULL, NULL, NULL, event->button, gtk_get_current_event_time() );
5591     }
5592     vtl->waypoint_rightclick = FALSE;
5593   }
5594   return FALSE;
5595 }
5596
5597 /**** Begin track ***/
5598 static gpointer tool_begin_track_create ( VikWindow *vw, VikViewport *vvp)
5599 {
5600   return vvp;
5601 }
5602
5603 static gboolean tool_begin_track_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
5604 {
5605   vtl->current_track = NULL;
5606   return tool_new_track_click ( vtl, event, vvp );
5607 }
5608
5609 /*** New track ****/
5610
5611 static gpointer tool_new_track_create ( VikWindow *vw, VikViewport *vvp)
5612 {
5613   return vvp;
5614 }
5615
5616 typedef struct {
5617   VikTrwLayer *vtl;
5618   VikViewport *vvp;
5619   gint x1,y1,x2,y2,x3,y3;
5620   const gchar* str;
5621 } new_track_move_passalong_t;
5622
5623 /* sync and undraw, but only when we have time */
5624 static gboolean ct_sync ( gpointer passalong )
5625 {
5626   new_track_move_passalong_t *p = (new_track_move_passalong_t *) passalong;
5627
5628   vik_viewport_sync ( p->vvp );
5629   gdk_gc_set_function ( p->vtl->current_track_gc, GDK_INVERT );
5630   vik_viewport_draw_line ( p->vvp, p->vtl->current_track_gc, p->x1, p->y1, p->x2, p->y2 );
5631   vik_viewport_draw_string (p->vvp, gdk_font_from_description (pango_font_description_from_string ("Sans 8")), p->vtl->current_track_gc, p->x3, p->y3, p->str);
5632   gdk_gc_set_function ( p->vtl->current_track_gc, GDK_COPY );
5633
5634   g_free ( (gpointer) p->str ) ;
5635   p->vtl->ct_sync_done = TRUE;
5636   g_free ( p );
5637   return FALSE;
5638 }
5639
5640 static const gchar* distance_string (gdouble distance)
5641 {
5642   gchar str[128];
5643
5644   /* draw label with distance */
5645   vik_units_distance_t dist_units = a_vik_get_units_distance ();
5646   switch (dist_units) {
5647   case VIK_UNITS_DISTANCE_MILES:
5648     if (distance >= VIK_MILES_TO_METERS(1) && distance < VIK_MILES_TO_METERS(100)) {
5649       g_sprintf(str, "%3.2f miles", VIK_METERS_TO_MILES(distance));
5650     } else if (distance < 1609.4) {
5651       g_sprintf(str, "%d yards", (int)(distance*1.0936133));
5652     } else {
5653       g_sprintf(str, "%d miles", (int)VIK_METERS_TO_MILES(distance));
5654     }
5655     break;
5656   default:
5657     // VIK_UNITS_DISTANCE_KILOMETRES
5658     if (distance >= 1000 && distance < 100000) {
5659       g_sprintf(str, "%3.2f km", distance/1000.0);
5660     } else if (distance < 1000) {
5661       g_sprintf(str, "%d m", (int)distance);
5662     } else {
5663       g_sprintf(str, "%d km", (int)distance/1000);
5664     }
5665     break;
5666   }
5667   return g_strdup (str);
5668 }
5669
5670 /*
5671  * Actually set the message in statusbar
5672  */
5673 static void statusbar_write (const gchar *distance_string, gdouble elev_gain, gdouble elev_loss, VikTrwLayer *vtl )
5674 {
5675   // Only show elevation data when track has some elevation properties
5676   gchar str_gain_loss[64];
5677   str_gain_loss[0] = '\0';
5678   
5679   if ( (elev_gain > 0.1) || (elev_loss > 0.1) ) {
5680     if ( a_vik_get_units_height () == VIK_UNITS_HEIGHT_METRES )
5681       g_sprintf(str_gain_loss, _(" - Gain %dm:Loss %dm"), (int)elev_gain, (int)elev_loss);
5682     else
5683       g_sprintf(str_gain_loss, _(" - Gain %dft:Loss %dft"), (int)VIK_METERS_TO_FEET(elev_gain), (int)VIK_METERS_TO_FEET(elev_loss));
5684   }
5685
5686   // Write with full gain/loss information
5687   gchar *msg = g_strdup_printf ( "%s%s", distance_string, str_gain_loss);
5688   vik_statusbar_set_message ( vik_window_get_statusbar (VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl))), VIK_STATUSBAR_INFO, msg );
5689   g_free ( msg );
5690 }
5691
5692 /*
5693  * Figure out what information should be set in the statusbar and then write it
5694  */
5695 static void update_statusbar ( VikTrwLayer *vtl )
5696 {
5697   // Get elevation data
5698   gdouble elev_gain, elev_loss;
5699   vik_track_get_total_elevation_gain ( vtl->current_track, &elev_gain, &elev_loss);
5700
5701   /* Find out actual distance of current track */
5702   gdouble distance = vik_track_get_length (vtl->current_track);
5703   const gchar *str = distance_string (distance);
5704
5705   statusbar_write (str, elev_gain, elev_loss, vtl);
5706
5707   g_free ((gpointer)str);
5708 }
5709
5710
5711 static VikLayerToolFuncStatus tool_new_track_move ( VikTrwLayer *vtl, GdkEventMotion *event, VikViewport *vvp )
5712 {
5713   /* if we haven't sync'ed yet, we don't have time to do more. */
5714   if ( vtl->ct_sync_done && vtl->current_track && vtl->current_track->trackpoints ) {
5715     GList *iter = vtl->current_track->trackpoints;
5716     new_track_move_passalong_t *passalong;
5717     gint x1, y1;
5718
5719     while ( iter->next )
5720       iter = iter->next;
5721     gdk_gc_set_function ( vtl->current_track_gc, GDK_INVERT );
5722     vik_viewport_coord_to_screen ( vvp, &(VIK_TRACKPOINT(iter->data)->coord), &x1, &y1 );
5723     vik_viewport_draw_line ( vvp, vtl->current_track_gc, x1, y1, event->x, event->y );
5724
5725     /* Find out actual distance of current track */
5726     gdouble distance = vik_track_get_length (vtl->current_track);
5727
5728     // Now add distance to where the pointer is //
5729     VikCoord coord;
5730     struct LatLon ll;
5731     vik_viewport_screen_to_coord ( vvp, (gint) event->x, (gint) event->y, &coord );
5732     vik_coord_to_latlon ( &coord, &ll );
5733     distance = distance + vik_coord_diff( &coord, &(VIK_TRACKPOINT(iter->data)->coord));
5734
5735     // Get elevation data
5736     gdouble elev_gain, elev_loss;
5737     vik_track_get_total_elevation_gain ( vtl->current_track, &elev_gain, &elev_loss);
5738
5739     // Adjust elevation data (if available) for the current pointer position
5740     gdouble elev_new;
5741     elev_new = (gdouble) a_dems_get_elev_by_coord ( &coord, VIK_DEM_INTERPOL_BEST );
5742     if ( elev_new != VIK_DEM_INVALID_ELEVATION ) {
5743       if ( VIK_TRACKPOINT(iter->data)->altitude != VIK_DEFAULT_ALTITUDE ) {
5744         // Adjust elevation of last track point
5745         if ( elev_new > VIK_TRACKPOINT(iter->data)->altitude )
5746           // Going up
5747           elev_gain += elev_new - VIK_TRACKPOINT(iter->data)->altitude;
5748         else
5749           // Going down
5750           elev_loss += VIK_TRACKPOINT(iter->data)->altitude - elev_new;
5751       }
5752     }
5753       
5754     const gchar *str = distance_string (distance);
5755     gint xd,yd;
5756     /* offset from cursor a bit */
5757     xd = event->x + 10;
5758     yd = event->y - 10;
5759     /* note attempted setting text using pango layouts - but the 'undraw' technique didn't work */
5760     vik_viewport_draw_string (vvp, gdk_font_from_description (pango_font_description_from_string ("Sans 8")), vtl->current_track_gc, xd, yd, str);
5761
5762     gdk_gc_set_function ( vtl->current_track_gc, GDK_COPY );
5763
5764     passalong = g_new(new_track_move_passalong_t,1); /* freed by sync */
5765     passalong->vtl = vtl;
5766     passalong->vvp = vvp;
5767     passalong->x1 = x1;
5768     passalong->y1 = y1;
5769     passalong->x2 = event->x;
5770     passalong->y2 = event->y;
5771     passalong->x3 = xd;
5772     passalong->y3 = yd;
5773     passalong->str = str;
5774
5775     // Update statusbar with full gain/loss information
5776     statusbar_write (str, elev_gain, elev_loss, vtl);
5777
5778     /* this will sync and undraw when we have time to */
5779     g_idle_add_full (G_PRIORITY_HIGH_IDLE + 10, ct_sync, passalong, NULL);
5780     vtl->ct_sync_done = FALSE;
5781     return VIK_LAYER_TOOL_ACK_GRAB_FOCUS;
5782   }
5783   return VIK_LAYER_TOOL_ACK;
5784 }
5785
5786 static gboolean tool_new_track_key_press ( VikTrwLayer *vtl, GdkEventKey *event, VikViewport *vvp )
5787 {
5788   if ( vtl->current_track && event->keyval == GDK_Escape ) {
5789     vtl->current_track = NULL;
5790     vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
5791     return TRUE;
5792   } else if ( vtl->current_track && event->keyval == GDK_BackSpace ) {
5793     /* undo */
5794     if ( vtl->current_track->trackpoints )
5795     {
5796       GList *last = g_list_last(vtl->current_track->trackpoints);
5797       g_free ( last->data );
5798       vtl->current_track->trackpoints = g_list_remove_link ( vtl->current_track->trackpoints, last );
5799     }
5800     
5801     update_statusbar ( vtl );
5802
5803     vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
5804     return TRUE;
5805   }
5806   return FALSE;
5807 }
5808
5809 static gboolean tool_new_track_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
5810 {
5811   VikTrackpoint *tp;
5812
5813   if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
5814     return FALSE;
5815
5816   if ( event->button == 3 && vtl->current_track )
5817   {
5818     /* undo */
5819     if ( vtl->current_track->trackpoints )
5820     {
5821       GList *last = g_list_last(vtl->current_track->trackpoints);
5822       g_free ( last->data );
5823       vtl->current_track->trackpoints = g_list_remove_link ( vtl->current_track->trackpoints, last );
5824     }
5825     update_statusbar ( vtl );
5826
5827     vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
5828     return TRUE;
5829   }
5830
5831   if ( event->type == GDK_2BUTTON_PRESS )
5832   {
5833     /* subtract last (duplicate from double click) tp then end */
5834     if ( vtl->current_track && vtl->current_track->trackpoints && vtl->ct_x1 == vtl->ct_x2 && vtl->ct_y1 == vtl->ct_y2 )
5835     {
5836       GList *last = g_list_last(vtl->current_track->trackpoints);
5837       g_free ( last->data );
5838       vtl->current_track->trackpoints = g_list_remove_link ( vtl->current_track->trackpoints, last );
5839       /* undo last, then end */
5840       vtl->current_track = NULL;
5841     }
5842     vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
5843     return TRUE;
5844   }
5845
5846   if ( ! vtl->current_track )
5847   {
5848     gchar *name = get_new_unique_sublayer_name(vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, _("Track"));
5849     if ( ( name = a_dialog_new_track ( VIK_GTK_WINDOW_FROM_LAYER(vtl), vtl->tracks, name ) ) )
5850     {
5851       vtl->current_track = vik_track_new();
5852       vtl->current_track->visible = TRUE;
5853       vik_trw_layer_add_track ( vtl, name, vtl->current_track );
5854
5855       /* incase it was created by begin track */
5856       vik_window_enable_layer_tool ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)), VIK_LAYER_TRW, TOOL_CREATE_TRACK );
5857     }
5858     else
5859       return TRUE;
5860   }
5861   tp = vik_trackpoint_new();
5862   vik_viewport_screen_to_coord ( vvp, event->x, event->y, &(tp->coord) );
5863
5864   /* snap to other TP */
5865   if ( event->state & GDK_CONTROL_MASK )
5866   {
5867     VikTrackpoint *other_tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
5868     if ( other_tp )
5869       tp->coord = other_tp->coord;
5870   }
5871
5872   tp->newsegment = FALSE;
5873   tp->has_timestamp = FALSE;
5874   tp->timestamp = 0;
5875   vtl->current_track->trackpoints = g_list_append ( vtl->current_track->trackpoints, tp );
5876   /* Auto attempt to get elevation from DEM data (if it's available) */
5877   vik_track_apply_dem_data_last_trackpoint ( vtl->current_track );
5878
5879   vtl->ct_x1 = vtl->ct_x2;
5880   vtl->ct_y1 = vtl->ct_y2;
5881   vtl->ct_x2 = event->x;
5882   vtl->ct_y2 = event->y;
5883
5884   vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
5885   return TRUE;
5886 }
5887
5888 /*** New waypoint ****/
5889
5890 static gpointer tool_new_waypoint_create ( VikWindow *vw, VikViewport *vvp)
5891 {
5892   return vvp;
5893 }
5894
5895 static gboolean tool_new_waypoint_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
5896 {
5897   VikCoord coord;
5898   if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
5899     return FALSE;
5900   vik_viewport_screen_to_coord ( vvp, event->x, event->y, &coord );
5901   if (vik_trw_layer_new_waypoint ( vtl, VIK_GTK_WINDOW_FROM_LAYER(vtl), &coord ) && VIK_LAYER(vtl)->visible)
5902     vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
5903   return TRUE;
5904 }
5905
5906
5907 /*** Edit trackpoint ****/
5908
5909 static gpointer tool_edit_trackpoint_create ( VikWindow *vw, VikViewport *vvp)
5910 {
5911   tool_ed_t *t = g_new(tool_ed_t, 1);
5912   t->vvp = vvp;
5913   t->holding = FALSE;
5914   return t;
5915 }
5916
5917 static gboolean tool_edit_trackpoint_click ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data )
5918 {
5919   tool_ed_t *t = data;
5920   VikViewport *vvp = t->vvp;
5921   TPSearchParams params;
5922   /* OUTDATED DOCUMENTATION:
5923    find 5 pixel range on each side. then put these UTM, and a pointer
5924    to the winning track name (and maybe the winning track itself), and a
5925    pointer to the winning trackpoint, inside an array or struct. pass 
5926    this along, do a foreach on the tracks which will do a foreach on the 
5927    trackpoints. */
5928   params.vvp = vvp;
5929   params.x = event->x;
5930   params.y = event->y;
5931   params.closest_track_id = NULL;
5932   /* TODO: should get track listitem so we can break it up, make a new track, mess it up, all that. */
5933   params.closest_tp = NULL;
5934
5935   if ( event->button != 1 ) 
5936     return FALSE;
5937
5938   if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
5939     return FALSE;
5940
5941   if ( !vtl->vl.visible || !vtl->tracks_visible )
5942     return FALSE;
5943
5944   if ( vtl->current_tpl )
5945   {
5946     /* 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.) */
5947     VikTrackpoint *tp = VIK_TRACKPOINT(vtl->current_tpl->data);
5948     VikTrack *current_tr = VIK_TRACK(g_hash_table_lookup(vtl->tracks, vtl->current_tp_id));
5949     if ( !current_tr )
5950       return FALSE;
5951
5952     gint x, y;
5953     vik_viewport_coord_to_screen ( vvp, &(tp->coord), &x, &y );
5954
5955     if ( current_tr->visible && 
5956          abs(x - event->x) < TRACKPOINT_SIZE_APPROX &&
5957          abs(y - event->y) < TRACKPOINT_SIZE_APPROX ) {
5958       marker_begin_move ( t, event->x, event->y );
5959       return TRUE;
5960     }
5961
5962     vtl->last_tpl = vtl->current_tpl;
5963     vtl->last_tp_track = vtl->current_tp_track;
5964   }
5965
5966   g_hash_table_foreach ( vtl->tracks, (GHFunc) track_search_closest_tp, &params);
5967
5968   if ( params.closest_tp )
5969   {
5970     vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, g_hash_table_lookup ( vtl->tracks_iters, params.closest_track_id ), TRUE );
5971     vtl->current_tpl = params.closest_tpl;
5972     vtl->current_tp_id = params.closest_track_id;
5973     trw_layer_tpwin_init ( vtl );
5974     set_statusbar_msg_info_trkpt ( vtl, params.closest_tp );
5975     vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
5976     return TRUE;
5977   }
5978
5979   /* these aren't the droids you're looking for */
5980   return FALSE;
5981 }
5982
5983 static gboolean tool_edit_trackpoint_move ( VikTrwLayer *vtl, GdkEventMotion *event, gpointer data )
5984 {
5985   tool_ed_t *t = data;
5986   VikViewport *vvp = t->vvp;
5987
5988   if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
5989     return FALSE;
5990
5991   if ( t->holding )
5992   {
5993     VikCoord new_coord;
5994     vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
5995
5996     /* snap to TP */
5997     if ( event->state & GDK_CONTROL_MASK )
5998     {
5999       VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
6000       if ( tp && tp != vtl->current_tpl->data )
6001         new_coord = tp->coord;
6002     }
6003     //    VIK_TRACKPOINT(vtl->current_tpl->data)->coord = new_coord;
6004     { 
6005       gint x, y;
6006       vik_viewport_coord_to_screen ( vvp, &new_coord, &x, &y );
6007       marker_moveto ( t, x, y );
6008     } 
6009
6010     return TRUE;
6011   }
6012   return FALSE;
6013 }
6014
6015 static gboolean tool_edit_trackpoint_release ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data )
6016 {
6017   tool_ed_t *t = data;
6018   VikViewport *vvp = t->vvp;
6019
6020   if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
6021     return FALSE;
6022   if ( event->button != 1) 
6023     return FALSE;
6024
6025   if ( t->holding ) {
6026     VikCoord new_coord;
6027     vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
6028
6029     /* snap to TP */
6030     if ( event->state & GDK_CONTROL_MASK )
6031     {
6032       VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
6033       if ( tp && tp != vtl->current_tpl->data )
6034         new_coord = tp->coord;
6035     }
6036
6037     VIK_TRACKPOINT(vtl->current_tpl->data)->coord = new_coord;
6038
6039     marker_end_move ( t );
6040
6041     /* diff dist is diff from orig */
6042     if ( vtl->tpwin )
6043       vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, vtl->current_tp_track->name );
6044     /* can't join with itself! */
6045     trw_layer_cancel_last_tp ( vtl );
6046
6047     vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
6048     return TRUE;
6049   }
6050   return FALSE;
6051 }
6052
6053
6054 /*** Route Finder ***/
6055 static gpointer tool_route_finder_create ( VikWindow *vw, VikViewport *vvp)
6056 {
6057   return vvp;
6058 }
6059
6060 static gboolean tool_route_finder_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
6061 {
6062   VikCoord tmp;
6063   if ( !vtl ) return FALSE;
6064   vik_viewport_screen_to_coord ( vvp, event->x, event->y, &tmp );
6065   if ( event->button == 3 && vtl->route_finder_current_track ) {
6066     VikCoord *new_end;
6067     new_end = vik_track_cut_back_to_double_point ( vtl->route_finder_current_track );
6068     if ( new_end ) {
6069       vtl->route_finder_coord = *new_end;
6070       g_free ( new_end );
6071       vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
6072       /* remove last ' to:...' */
6073       if ( vtl->route_finder_current_track->comment ) {
6074         gchar *last_to = strrchr ( vtl->route_finder_current_track->comment, 't' );
6075         if ( last_to && (last_to - vtl->route_finder_current_track->comment > 1) ) {
6076           gchar *new_comment = g_strndup ( vtl->route_finder_current_track->comment,
6077                                            last_to - vtl->route_finder_current_track->comment - 1);
6078           vik_track_set_comment_no_copy ( vtl->route_finder_current_track, new_comment );
6079         }
6080       }
6081     }
6082   }
6083   else if ( vtl->route_finder_started || (event->state & GDK_CONTROL_MASK && vtl->route_finder_current_track) ) {
6084     struct LatLon start, end;
6085     gchar startlat[G_ASCII_DTOSTR_BUF_SIZE], startlon[G_ASCII_DTOSTR_BUF_SIZE];
6086     gchar endlat[G_ASCII_DTOSTR_BUF_SIZE], endlon[G_ASCII_DTOSTR_BUF_SIZE];
6087     gchar *url;
6088
6089     vik_coord_to_latlon ( &(vtl->route_finder_coord), &start );
6090     vik_coord_to_latlon ( &(tmp), &end );
6091     vtl->route_finder_coord = tmp; /* for continuations */
6092
6093     /* these are checked when adding a track from a file (vik_trw_layer_filein_add_track) */
6094     if ( event->state & GDK_CONTROL_MASK && vtl->route_finder_current_track ) {
6095       vtl->route_finder_append = TRUE;  // merge tracks. keep started true.
6096     } else {
6097       vtl->route_finder_check_added_track = TRUE;
6098       vtl->route_finder_started = FALSE;
6099     }
6100
6101     url = g_strdup_printf(GOOGLE_DIRECTIONS_STRING,
6102                           g_ascii_dtostr (startlat, G_ASCII_DTOSTR_BUF_SIZE, (gdouble) start.lat),
6103                           g_ascii_dtostr (startlon, G_ASCII_DTOSTR_BUF_SIZE, (gdouble) start.lon),
6104                           g_ascii_dtostr (endlat, G_ASCII_DTOSTR_BUF_SIZE, (gdouble) end.lat),
6105                           g_ascii_dtostr (endlon, G_ASCII_DTOSTR_BUF_SIZE, (gdouble) end.lon));
6106     a_babel_convert_from_url ( vtl, url, "kml", NULL, NULL );
6107     g_free ( url );
6108
6109     /* see if anything was done -- a track was added or appended to */
6110     if ( vtl->route_finder_check_added_track && vtl->route_finder_added_track ) {
6111       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 ) );
6112     } else if ( vtl->route_finder_append == FALSE && vtl->route_finder_current_track ) {
6113       /* route_finder_append was originally TRUE but set to FALSE by filein_add_track */
6114       gchar *new_comment = g_strdup_printf("%s to: %f,%f", vtl->route_finder_current_track->comment, end.lat, end.lon );
6115       vik_track_set_comment_no_copy ( vtl->route_finder_current_track, new_comment );
6116     }
6117     vtl->route_finder_added_track = NULL;
6118     vtl->route_finder_check_added_track = FALSE;
6119     vtl->route_finder_append = FALSE;
6120
6121     vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
6122   } else {
6123     vtl->route_finder_started = TRUE;
6124     vtl->route_finder_coord = tmp;
6125     vtl->route_finder_current_track = NULL;
6126   }
6127   return TRUE;
6128 }
6129
6130 /*** Show picture ****/
6131
6132 static gpointer tool_show_picture_create ( VikWindow *vw, VikViewport *vvp)
6133 {
6134   return vvp;
6135 }
6136
6137 /* Params are: vvp, event, last match found or NULL */
6138 static void tool_show_picture_wp ( char *name, VikWaypoint *wp, gpointer params[3] )
6139 {
6140   if ( wp->image && wp->visible )
6141   {
6142     gint x, y, slackx, slacky;
6143     GdkEventButton *event = (GdkEventButton *) params[1];
6144
6145     vik_viewport_coord_to_screen ( VIK_VIEWPORT(params[0]), &(wp->coord), &x, &y );
6146     slackx = wp->image_width / 2;
6147     slacky = wp->image_height / 2;
6148     if (    x <= event->x + slackx && x >= event->x - slackx
6149          && y <= event->y + slacky && y >= event->y - slacky )
6150     {
6151       params[2] = wp->image; /* we've found a match. however continue searching
6152                               * since we want to find the last match -- that
6153                               * is, the match that was drawn last. */
6154     }
6155   }
6156 }
6157
6158 static void trw_layer_show_picture ( gpointer pass_along[6] )
6159 {
6160   /* thanks to the Gaim people for showing me ShellExecute and g_spawn_command_line_async */
6161 #ifdef WINDOWS
6162   ShellExecute(NULL, NULL, (char *) pass_along[5], NULL, ".\\", 0);
6163 #else /* WINDOWS */
6164   GError *err = NULL;
6165   gchar *quoted_file = g_shell_quote ( (gchar *) pass_along[5] );
6166   gchar *cmd = g_strdup_printf ( "%s %s", a_vik_get_image_viewer(), quoted_file );
6167   g_free ( quoted_file );
6168   if ( ! g_spawn_command_line_async ( cmd, &err ) )
6169     {
6170       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() );
6171       g_error_free ( err );
6172     }
6173   g_free ( cmd );
6174 #endif /* WINDOWS */
6175 }
6176
6177 static gboolean tool_show_picture_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
6178 {
6179   gpointer params[3] = { vvp, event, NULL };
6180   if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
6181     return FALSE;
6182   g_hash_table_foreach ( vtl->waypoints, (GHFunc) tool_show_picture_wp, params );
6183   if ( params[2] )
6184   {
6185     static gpointer pass_along[6];
6186     pass_along[0] = vtl;
6187     pass_along[5] = params[2];
6188     trw_layer_show_picture ( pass_along );
6189     return TRUE; /* found a match */
6190   }
6191   else
6192     return FALSE; /* go through other layers, searching for a match */
6193 }
6194
6195 /***************************************************************************
6196  ** End tool code 
6197  ***************************************************************************/
6198
6199
6200
6201
6202
6203 static void image_wp_make_list ( char *name, VikWaypoint *wp, GSList **pics )
6204 {
6205   if ( wp->image && ( ! a_thumbnails_exists ( wp->image ) ) )
6206     *pics = g_slist_append ( *pics, (gpointer) g_strdup ( wp->image ) );
6207 }
6208
6209 /* Structure for thumbnail creating data used in the background thread */
6210 typedef struct {
6211   VikTrwLayer *vtl; // Layer needed for redrawing
6212   GSList *pics;     // Image list
6213 } thumbnail_create_thread_data;
6214
6215 static int create_thumbnails_thread ( thumbnail_create_thread_data *tctd, gpointer threaddata )
6216 {
6217   guint total = g_slist_length(tctd->pics), done = 0;
6218   while ( tctd->pics )
6219   {
6220     a_thumbnails_create ( (gchar *) tctd->pics->data );
6221     int result = a_background_thread_progress ( threaddata, ((gdouble) ++done) / total );
6222     if ( result != 0 )
6223       return -1; /* Abort thread */
6224
6225     tctd->pics = tctd->pics->next;
6226   }
6227
6228   // Redraw to show the thumbnails as they are now created
6229   if ( IS_VIK_LAYER(tctd->vtl) )
6230     vik_layer_emit_update ( VIK_LAYER(tctd->vtl), TRUE ); // Yes update from background thread
6231
6232   return 0;
6233 }
6234
6235 static void thumbnail_create_thread_free ( thumbnail_create_thread_data *tctd )
6236 {
6237   while ( tctd->pics )
6238   {
6239     g_free ( tctd->pics->data );
6240     tctd->pics = g_slist_delete_link ( tctd->pics, tctd->pics );
6241   }
6242   g_free ( tctd );
6243 }
6244
6245 void trw_layer_verify_thumbnails ( VikTrwLayer *vtl, GtkWidget *vp )
6246 {
6247   if ( ! vtl->has_verified_thumbnails )
6248   {
6249     GSList *pics = NULL;
6250     g_hash_table_foreach ( vtl->waypoints, (GHFunc) image_wp_make_list, &pics );
6251     if ( pics )
6252     {
6253       gint len = g_slist_length ( pics );
6254       gchar *tmp = g_strdup_printf ( _("Creating %d Image Thumbnails..."), len );
6255       thumbnail_create_thread_data *tctd = g_malloc ( sizeof(thumbnail_create_thread_data) );
6256       tctd->vtl = vtl;
6257       tctd->pics = pics;
6258       a_background_thread ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
6259                             tmp,
6260                             (vik_thr_func) create_thumbnails_thread,
6261                             tctd,
6262                             (vik_thr_free_func) thumbnail_create_thread_free,
6263                             NULL,
6264                             len );
6265       g_free ( tmp );
6266     }
6267   }
6268 }
6269
6270 VikCoordMode vik_trw_layer_get_coord_mode ( VikTrwLayer *vtl )
6271 {
6272   return vtl->coord_mode;
6273 }
6274
6275
6276
6277 static void waypoint_convert ( const gpointer id, VikWaypoint *wp, VikCoordMode *dest_mode )
6278 {
6279   vik_coord_convert ( &(wp->coord), *dest_mode );
6280 }
6281
6282 static void track_convert ( const gchar *name, VikTrack *tr, VikCoordMode *dest_mode )
6283 {
6284   vik_track_convert ( tr, *dest_mode );
6285 }
6286
6287 static void trw_layer_change_coord_mode ( VikTrwLayer *vtl, VikCoordMode dest_mode )
6288 {
6289   if ( vtl->coord_mode != dest_mode )
6290   {
6291     vtl->coord_mode = dest_mode;
6292     g_hash_table_foreach ( vtl->waypoints, (GHFunc) waypoint_convert, &dest_mode );
6293     g_hash_table_foreach ( vtl->tracks, (GHFunc) track_convert, &dest_mode );
6294   }
6295 }
6296
6297 static void trw_layer_set_menu_selection ( VikTrwLayer *vtl, guint16 selection )
6298 {
6299   vtl->menu_selection = selection;
6300 }
6301
6302 static guint16 trw_layer_get_menu_selection ( VikTrwLayer *vtl )
6303 {
6304   return (vtl->menu_selection);
6305 }
6306
6307 /* ----------- Downloading maps along tracks --------------- */
6308
6309 static int get_download_area_width(VikViewport *vvp, gdouble zoom_level, struct LatLon *wh)
6310 {
6311   /* TODO: calculating based on current size of viewport */
6312   const gdouble w_at_zoom_0_125 = 0.0013;
6313   const gdouble h_at_zoom_0_125 = 0.0011;
6314   gdouble zoom_factor = zoom_level/0.125;
6315
6316   wh->lat = h_at_zoom_0_125 * zoom_factor;
6317   wh->lon = w_at_zoom_0_125 * zoom_factor;
6318
6319   return 0;   /* all OK */
6320 }
6321
6322 static VikCoord *get_next_coord(VikCoord *from, VikCoord *to, struct LatLon *dist, gdouble gradient)
6323 {
6324   if ((dist->lon >= ABS(to->east_west - from->east_west)) &&
6325       (dist->lat >= ABS(to->north_south - from->north_south)))
6326     return NULL;
6327
6328   VikCoord *coord = g_malloc(sizeof(VikCoord));
6329   coord->mode = VIK_COORD_LATLON;
6330
6331   if (ABS(gradient) < 1) {
6332     if (from->east_west > to->east_west)
6333       coord->east_west = from->east_west - dist->lon;
6334     else
6335       coord->east_west = from->east_west + dist->lon;
6336     coord->north_south = gradient * (coord->east_west - from->east_west) + from->north_south;
6337   } else {
6338     if (from->north_south > to->north_south)
6339       coord->north_south = from->north_south - dist->lat;
6340     else
6341       coord->north_south = from->north_south + dist->lat;
6342     coord->east_west = (1/gradient) * (coord->north_south - from->north_south) + from->north_south;
6343   }
6344
6345   return coord;
6346 }
6347
6348 static GList *add_fillins(GList *list, VikCoord *from, VikCoord *to, struct LatLon *dist)
6349 {
6350   /* TODO: handle virtical track (to->east_west - from->east_west == 0) */
6351   gdouble gradient = (to->north_south - from->north_south)/(to->east_west - from->east_west);
6352
6353   VikCoord *next = from;
6354   while (TRUE) {
6355     if ((next = get_next_coord(next, to, dist, gradient)) == NULL)
6356         break;
6357     list = g_list_prepend(list, next);
6358   }
6359
6360   return list;
6361 }
6362
6363 void vik_track_download_map(VikTrack *tr, VikMapsLayer *vml, VikViewport *vvp, gdouble zoom_level)
6364 {
6365   typedef struct _Rect {
6366     VikCoord tl;
6367     VikCoord br;
6368     VikCoord center;
6369   } Rect;
6370 #define GLRECT(iter) ((Rect *)((iter)->data))
6371
6372   struct LatLon wh;
6373   GList *rects_to_download = NULL;
6374   GList *rect_iter;
6375
6376   if (get_download_area_width(vvp, zoom_level, &wh))
6377     return;
6378
6379   GList *iter = tr->trackpoints;
6380   if (!iter)
6381     return;
6382
6383   gboolean new_map = TRUE;
6384   VikCoord *cur_coord, tl, br;
6385   Rect *rect;
6386   while (iter) {
6387     cur_coord = &(VIK_TRACKPOINT(iter->data))->coord;
6388     if (new_map) {
6389       vik_coord_set_area(cur_coord, &wh, &tl, &br);
6390       rect = g_malloc(sizeof(Rect));
6391       rect->tl = tl;
6392       rect->br = br;
6393       rect->center = *cur_coord;
6394       rects_to_download = g_list_prepend(rects_to_download, rect);
6395       new_map = FALSE;
6396       iter = iter->next;
6397       continue;
6398     }
6399     gboolean found = FALSE;
6400     for (rect_iter = rects_to_download; rect_iter; rect_iter = rect_iter->next) {
6401       if (vik_coord_inside(cur_coord, &GLRECT(rect_iter)->tl, &GLRECT(rect_iter)->br)) {
6402         found = TRUE;
6403         break;
6404       }
6405     }
6406     if (found)
6407         iter = iter->next;
6408     else
6409       new_map = TRUE;
6410   }
6411
6412   GList *fillins = NULL;
6413   /* 'fillin' doesn't work in UTM mode - potentially ending up in massive loop continually allocating memory - hence don't do it */
6414   /* seems that ATM the function get_next_coord works only for LATLON */
6415   if ( cur_coord->mode == VIK_COORD_LATLON ) {
6416     /* fill-ins for far apart points */
6417     GList *cur_rect, *next_rect;
6418     for (cur_rect = rects_to_download;
6419          (next_rect = cur_rect->next) != NULL;
6420          cur_rect = cur_rect->next) {
6421       if ((wh.lon < ABS(GLRECT(cur_rect)->center.east_west - GLRECT(next_rect)->center.east_west)) ||
6422           (wh.lat < ABS(GLRECT(cur_rect)->center.north_south - GLRECT(next_rect)->center.north_south))) {
6423         fillins = add_fillins(fillins, &GLRECT(cur_rect)->center, &GLRECT(next_rect)->center, &wh);
6424       }
6425     }
6426   } else
6427     g_message("%s: this feature works only in Mercator mode", __FUNCTION__);
6428
6429   if (fillins) {
6430     GList *iter = fillins;
6431     while (iter) {
6432       cur_coord = (VikCoord *)(iter->data);
6433       vik_coord_set_area(cur_coord, &wh, &tl, &br);
6434       rect = g_malloc(sizeof(Rect));
6435       rect->tl = tl;
6436       rect->br = br;
6437       rect->center = *cur_coord;
6438       rects_to_download = g_list_prepend(rects_to_download, rect);
6439       iter = iter->next;
6440     }
6441   }
6442
6443   for (rect_iter = rects_to_download; rect_iter; rect_iter = rect_iter->next) {
6444     maps_layer_download_section_without_redraw(vml, vvp, &(((Rect *)(rect_iter->data))->tl), &(((Rect *)(rect_iter->data))->br), zoom_level);
6445   }
6446
6447   if (fillins) {
6448     for (iter = fillins; iter; iter = iter->next)
6449       g_free(iter->data);
6450     g_list_free(fillins);
6451   }
6452   if (rects_to_download) {
6453     for (rect_iter = rects_to_download; rect_iter; rect_iter = rect_iter->next)
6454       g_free(rect_iter->data);
6455     g_list_free(rects_to_download);
6456   }
6457 }
6458
6459 static void trw_layer_download_map_along_track_cb ( gpointer pass_along[6] )
6460 {
6461   VikMapsLayer *vml;
6462   gint selected_map, default_map;
6463   gchar *zoomlist[] = {"0.125", "0.25", "0.5", "1", "2", "4", "8", "16", "32", "64", "128", "256", "512", "1024", NULL };
6464   gdouble zoom_vals[] = {0.125, 0.25, 0.5, 1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024};
6465   gint selected_zoom, default_zoom;
6466   int i,j;
6467
6468
6469   VikTrwLayer *vtl = pass_along[0];
6470   VikLayersPanel *vlp = pass_along[1];
6471   VikTrack *tr = (VikTrack *) g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, pass_along[3] );
6472   VikViewport *vvp = vik_window_viewport((VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl)));
6473
6474   GList *vmls = vik_layers_panel_get_all_layers_of_type(vlp, VIK_LAYER_MAPS, TRUE); // Includes hidden map layer types
6475   int num_maps = g_list_length(vmls);
6476
6477   if (!num_maps) {
6478     a_dialog_msg(VIK_GTK_WINDOW_FROM_LAYER(vtl), GTK_MESSAGE_ERROR, _("No map layer in use. Create one first"), NULL);
6479     return;
6480   }
6481
6482   gchar **map_names = g_malloc(1 + num_maps * sizeof(gpointer));
6483   VikMapsLayer **map_layers = g_malloc(1 + num_maps * sizeof(gpointer));
6484
6485   gchar **np = map_names;
6486   VikMapsLayer **lp = map_layers;
6487   for (i = 0; i < num_maps; i++) {
6488     gboolean dup = FALSE;
6489     vml = (VikMapsLayer *)(vmls->data);
6490     for (j = 0; j < i; j++) { /* no duplicate allowed */
6491       if (vik_maps_layer_get_map_type(vml) == vik_maps_layer_get_map_type(map_layers[j])) {
6492         dup = TRUE;
6493         break;
6494       }
6495     }
6496     if (!dup) {
6497       *lp++ = vml;
6498       *np++ = vik_maps_layer_get_map_label(vml);
6499     }
6500     vmls = vmls->next;
6501   }
6502   *lp = NULL;
6503   *np = NULL;
6504   num_maps = lp - map_layers;
6505
6506   for (default_map = 0; default_map < num_maps; default_map++) {
6507     /* TODO: check for parent layer's visibility */
6508     if (VIK_LAYER(map_layers[default_map])->visible)
6509       break;
6510   }
6511   default_map = (default_map == num_maps) ? 0 : default_map;
6512
6513   gdouble cur_zoom = vik_viewport_get_zoom(vvp);
6514   for (default_zoom = 0; default_zoom < sizeof(zoom_vals)/sizeof(gdouble); default_zoom++) {
6515     if (cur_zoom == zoom_vals[default_zoom])
6516       break;
6517   }
6518   default_zoom = (default_zoom == sizeof(zoom_vals)/sizeof(gdouble)) ? sizeof(zoom_vals)/sizeof(gdouble) - 1 : default_zoom;
6519
6520   if (!a_dialog_map_n_zoom(VIK_GTK_WINDOW_FROM_LAYER(vtl), map_names, default_map, zoomlist, default_zoom, &selected_map, &selected_zoom))
6521     goto done;
6522
6523   vik_track_download_map(tr, map_layers[selected_map], vvp, zoom_vals[selected_zoom]);
6524
6525 done:
6526   for (i = 0; i < num_maps; i++)
6527     g_free(map_names[i]);
6528   g_free(map_names);
6529   g_free(map_layers);
6530
6531   g_list_free(vmls);
6532
6533 }
6534
6535 /**** lowest waypoint number calculation ***/
6536 static gint highest_wp_number_name_to_number(const gchar *name) {
6537   if ( strlen(name) == 3 ) {
6538     int n = atoi(name);
6539     if ( n < 100 && name[0] != '0' )
6540       return -1;
6541     if ( n < 10 && name[0] != '0' )
6542       return -1;
6543     return n;
6544   }
6545   return -1;
6546 }
6547
6548
6549 static void highest_wp_number_reset(VikTrwLayer *vtl)
6550 {
6551   vtl->highest_wp_number = -1;
6552 }
6553
6554 static void highest_wp_number_add_wp(VikTrwLayer *vtl, const gchar *new_wp_name)
6555 {
6556   /* if is bigger that top, add it */
6557   gint new_wp_num = highest_wp_number_name_to_number(new_wp_name);
6558   if ( new_wp_num > vtl->highest_wp_number )
6559     vtl->highest_wp_number = new_wp_num;
6560 }
6561
6562 static void highest_wp_number_remove_wp(VikTrwLayer *vtl, const gchar *old_wp_name)
6563 {
6564   /* if wasn't top, do nothing. if was top, count backwards until we find one used */
6565   gint old_wp_num = highest_wp_number_name_to_number(old_wp_name);
6566   if ( vtl->highest_wp_number == old_wp_num ) {
6567     gchar buf[4];
6568     vtl->highest_wp_number--;
6569
6570     g_snprintf(buf,4,"%03d", vtl->highest_wp_number );
6571     /* search down until we find something that *does* exist */
6572
6573     while ( vtl->highest_wp_number > 0 && ! vik_trw_layer_get_waypoint ( vtl, buf )) {
6574       vtl->highest_wp_number--;
6575       g_snprintf(buf,4,"%03d", vtl->highest_wp_number );
6576     }
6577   }
6578 }
6579
6580 /* get lowest unused number */
6581 static gchar *highest_wp_number_get(VikTrwLayer *vtl)
6582 {
6583   gchar buf[4];
6584   if ( vtl->highest_wp_number < 0 || vtl->highest_wp_number >= 999 )
6585     return NULL;
6586   g_snprintf(buf,4,"%03d", vtl->highest_wp_number+1 );
6587   return g_strdup(buf);
6588 }