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