]> git.street.me.uk Git - andy/viking.git/blob - src/viktrwlayer.c
Fix build when --disable-alphabetized-trw is specified
[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   vik_layer_rename ( VIK_LAYER(rv), vik_trw_layer_interface.name );
1560
1561   if ( vp == NULL || GTK_WIDGET(vp)->window == NULL ) {
1562     /* early exit, as the rest is GUI related */
1563     return rv;
1564   }
1565
1566   PangoFontDescription *pfd;
1567   rv->wplabellayout = gtk_widget_create_pango_layout (GTK_WIDGET(vp), NULL);
1568   pfd = pango_font_description_from_string (WAYPOINT_FONT);
1569   pango_layout_set_font_description (rv->wplabellayout, pfd);
1570   /* freeing PangoFontDescription, cause it has been copied by prev. call */
1571   pango_font_description_free (pfd);
1572
1573   trw_layer_new_track_gcs ( rv, vp );
1574
1575   rv->waypoint_gc = vik_viewport_new_gc ( vp, "#000000", 2 );
1576   rv->waypoint_text_gc = vik_viewport_new_gc ( vp, "#FFFFFF", 1 );
1577   rv->waypoint_bg_gc = vik_viewport_new_gc ( vp, "#8383C4", 1 );
1578   gdk_gc_set_function ( rv->waypoint_bg_gc, GDK_AND );
1579
1580   rv->waypoint_font = gdk_font_load ( "-*-helvetica-bold-r-normal-*-*-100-*-*-p-*-iso8859-1" );
1581
1582   rv->has_verified_thumbnails = FALSE;
1583   rv->wp_symbol = WP_SYMBOL_FILLED_SQUARE;
1584   rv->wp_size = 4;
1585   rv->wp_draw_symbols = TRUE;
1586
1587   rv->coord_mode = vik_viewport_get_coord_mode ( vp );
1588
1589   rv->menu_selection = vik_layer_get_interface(VIK_LAYER(rv)->type)->menu_items_selection;
1590
1591   return rv;
1592 }
1593
1594 static void trw_layer_realize_track ( gchar *name, VikTrack *track, gpointer pass_along[5] )
1595 {
1596   GtkTreeIter *new_iter = g_malloc(sizeof(GtkTreeIter));
1597
1598 #ifdef VIK_CONFIG_ALPHABETIZED_TRW
1599   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 );
1600 #else
1601   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 );
1602 #endif
1603
1604   *new_iter = *((GtkTreeIter *) pass_along[1]);
1605   g_hash_table_insert ( VIK_TRW_LAYER(pass_along[2])->tracks_iters, name, new_iter );
1606
1607   if ( ! track->visible )
1608     vik_treeview_item_set_visible ( (VikTreeview *) pass_along[3], (GtkTreeIter *) pass_along[1], FALSE );
1609 }
1610
1611 static void trw_layer_realize_waypoint ( gchar *name, VikWaypoint *wp, gpointer pass_along[5] )
1612 {
1613   GtkTreeIter *new_iter = g_malloc(sizeof(GtkTreeIter));
1614 #ifdef VIK_CONFIG_ALPHABETIZED_TRW
1615   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 );
1616 #else
1617   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 );
1618 #endif
1619
1620   *new_iter = *((GtkTreeIter *) pass_along[1]);
1621   g_hash_table_insert ( VIK_TRW_LAYER(pass_along[2])->waypoints_iters, name, new_iter );
1622
1623   if ( ! wp->visible )
1624     vik_treeview_item_set_visible ( (VikTreeview *) pass_along[3], (GtkTreeIter *) pass_along[1], FALSE );
1625 }
1626
1627
1628 static void trw_layer_realize ( VikTrwLayer *vtl, VikTreeview *vt, GtkTreeIter *layer_iter )
1629 {
1630   GtkTreeIter iter2;
1631   gpointer pass_along[5] = { &(vtl->tracks_iter), &iter2, vtl, vt, (gpointer) VIK_TRW_LAYER_SUBLAYER_TRACK };
1632
1633 #ifdef VIK_CONFIG_ALPHABETIZED_TRW
1634   vik_treeview_add_sublayer_alphabetized ( (VikTreeview *) vt, layer_iter, &(vtl->tracks_iter), _("Tracks"), vtl, NULL, VIK_TRW_LAYER_SUBLAYER_TRACKS, NULL, TRUE, FALSE );
1635 #else
1636   vik_treeview_add_sublayer ( (VikTreeview *) vt, layer_iter, &(vtl->tracks_iter), _("Tracks"), vtl, NULL, VIK_TRW_LAYER_SUBLAYER_TRACKS, NULL, TRUE, FALSE );
1637 #endif
1638   if ( ! vtl->tracks_visible )
1639     vik_treeview_item_set_visible ( (VikTreeview *) vt, &(vtl->tracks_iter), FALSE ); 
1640
1641   g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_realize_track, pass_along );
1642
1643 #ifdef VIK_CONFIG_ALPHABETIZED_TRW
1644   vik_treeview_add_sublayer_alphabetized ( (VikTreeview *) vt, layer_iter, &(vtl->waypoints_iter), _("Waypoints"), vtl, NULL, VIK_TRW_LAYER_SUBLAYER_WAYPOINTS, NULL, TRUE, FALSE );
1645 #else
1646   vik_treeview_add_sublayer ( (VikTreeview *) vt, layer_iter, &(vtl->waypoints_iter), _("Waypoints"), vtl, NULL, VIK_TRW_LAYER_SUBLAYER_WAYPOINTS, NULL, TRUE, FALSE );
1647 #endif
1648
1649   if ( ! vtl->waypoints_visible )
1650     vik_treeview_item_set_visible ( (VikTreeview *) vt, &(vtl->waypoints_iter), FALSE ); 
1651
1652   pass_along[0] = &(vtl->waypoints_iter);
1653   pass_along[4] = (gpointer) VIK_TRW_LAYER_SUBLAYER_WAYPOINT;
1654
1655   g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_realize_waypoint, pass_along );
1656
1657 }
1658
1659 static gboolean trw_layer_sublayer_toggle_visible ( VikTrwLayer *l, gint subtype, gpointer sublayer )
1660 {
1661   switch ( subtype )
1662   {
1663     case VIK_TRW_LAYER_SUBLAYER_TRACKS: return (l->tracks_visible ^= 1);
1664     case VIK_TRW_LAYER_SUBLAYER_WAYPOINTS: return (l->waypoints_visible ^= 1);
1665     case VIK_TRW_LAYER_SUBLAYER_TRACK:
1666     {
1667       VikTrack *t = g_hash_table_lookup ( l->tracks, sublayer );
1668       if (t)
1669         return (t->visible ^= 1);
1670       else
1671         return TRUE;
1672     }
1673     case VIK_TRW_LAYER_SUBLAYER_WAYPOINT:
1674     {
1675       VikWaypoint *t = g_hash_table_lookup ( l->waypoints, sublayer );
1676       if (t)
1677         return (t->visible ^= 1);
1678       else
1679         return TRUE;
1680     }
1681   }
1682   return TRUE;
1683 }
1684
1685 /*
1686  * Return a property about tracks for this layer
1687  */
1688 gint vik_trw_layer_get_property_tracks_line_thickness ( VikTrwLayer *vtl )
1689 {
1690   return vtl->line_thickness;
1691 }
1692
1693 // Structure to hold multiple track information for a layer
1694 typedef struct {
1695   gdouble length;
1696   time_t  start_time;
1697   time_t  end_time;
1698   gint    duration;
1699 } tooltip_tracks;
1700
1701 /*
1702  * Build up layer multiple track information via updating the tooltip_tracks structure
1703  */
1704 static void trw_layer_tracks_tooltip ( const gchar *name, VikTrack *tr, tooltip_tracks *tt )
1705 {
1706   tt->length = tt->length + vik_track_get_length (tr);
1707
1708   // Ensure times are available
1709   if ( tr->trackpoints &&
1710        VIK_TRACKPOINT(tr->trackpoints->data)->has_timestamp &&
1711        VIK_TRACKPOINT(g_list_last(tr->trackpoints)->data)->has_timestamp ) {
1712
1713     time_t t1, t2;
1714     t1 = VIK_TRACKPOINT(tr->trackpoints->data)->timestamp;
1715     t2 = VIK_TRACKPOINT(g_list_last(tr->trackpoints)->data)->timestamp;
1716
1717     // Assume never actually have a track with a time of 0 (1st Jan 1970)
1718     // Hence initialize to the first 'proper' value
1719     if ( tt->start_time == 0 )
1720         tt->start_time = t1;
1721     if ( tt->end_time == 0 )
1722         tt->end_time = t2;
1723
1724     // Update find the earliest / last times
1725     if ( t1 < tt->start_time )
1726         tt->start_time = t1;
1727     if ( t2 > tt->end_time )
1728         tt->end_time = t2;
1729
1730     // Keep track of total time
1731     //  there maybe gaps within a track (eg segments)
1732     //  but this should be generally good enough for a simple indicator
1733     tt->duration = tt->duration + (int)(t2-t1);
1734   }
1735 }
1736
1737 /*
1738  * Generate tooltip text for the layer.
1739  * This is relatively complicated as it considers information for
1740  *   no tracks, a single track or multiple tracks
1741  *     (which may or may not have timing information)
1742  */
1743 static const gchar* trw_layer_layer_tooltip ( VikTrwLayer *vtl )
1744 {
1745   gchar tbuf1[32];
1746   gchar tbuf2[64];
1747   gchar tbuf3[64];
1748   gchar tbuf4[10];
1749   tbuf1[0] = '\0';
1750   tbuf2[0] = '\0';
1751   tbuf3[0] = '\0';
1752   tbuf4[0] = '\0';
1753
1754   static gchar tmp_buf[128];
1755   tmp_buf[0] = '\0';
1756
1757   // For compact date format I'm using '%x'     [The preferred date representation for the current locale without the time.]
1758
1759   // Safety check - I think these should always be valid
1760   if ( vtl->tracks && vtl->waypoints ) {
1761     tooltip_tracks tt = { 0.0, 0, 0 };
1762     g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_tracks_tooltip, &tt );
1763
1764     GDate* gdate_start = g_date_new ();
1765     g_date_set_time_t (gdate_start, tt.start_time);
1766
1767     GDate* gdate_end = g_date_new ();
1768     g_date_set_time_t (gdate_end, tt.end_time);
1769
1770     if ( g_date_compare (gdate_start, gdate_end) ) {
1771       // Dates differ so print range on separate line
1772       g_date_strftime (tbuf1, sizeof(tbuf1), "%x", gdate_start);
1773       g_date_strftime (tbuf2, sizeof(tbuf2), "%x", gdate_end);
1774       g_snprintf (tbuf3, sizeof(tbuf3), "%s to %s\n", tbuf1, tbuf2);
1775     }
1776     else {
1777       // Same date so just show it and keep rest of text on the same line - provided it's a valid time!
1778       if ( tt.start_time != 0 )
1779         g_date_strftime (tbuf3, sizeof(tbuf3), "%x: ", gdate_start);
1780     }
1781
1782     tbuf2[0] = '\0';
1783     if ( tt.length > 0.0 ) {
1784       gdouble len_in_units;
1785
1786       // Setup info dependent on distance units
1787       if ( a_vik_get_units_distance() == VIK_UNITS_DISTANCE_MILES ) {
1788         g_snprintf (tbuf4, sizeof(tbuf4), "miles");
1789         len_in_units = VIK_METERS_TO_MILES(tt.length);
1790       }
1791       else {
1792         g_snprintf (tbuf4, sizeof(tbuf4), "kms");
1793         len_in_units = tt.length/1000.0;
1794       }
1795
1796       // Timing information if available
1797       tbuf1[0] = '\0';
1798       if ( tt.duration > 0 ) {
1799         g_snprintf (tbuf1, sizeof(tbuf1),
1800                     _(" in %d:%02d hrs:mins"),
1801                     (int)round(tt.duration/3600), (int)round((tt.duration/60)%60));
1802       }
1803       g_snprintf (tbuf2, sizeof(tbuf2),
1804                   _("\n%sTotal Length %.1f %s%s"),
1805                   tbuf3, len_in_units, tbuf4, tbuf1);
1806     }
1807
1808     // Put together all the elements to form compact tooltip text
1809     g_snprintf (tmp_buf, sizeof(tmp_buf),
1810                 _("Tracks: %d - Waypoints: %d%s"),
1811                 g_hash_table_size (vtl->tracks), g_hash_table_size (vtl->waypoints), tbuf2);
1812
1813     g_date_free (gdate_start);
1814     g_date_free (gdate_end);
1815
1816   }
1817
1818   return tmp_buf;
1819 }
1820
1821 static const gchar* trw_layer_sublayer_tooltip ( VikTrwLayer *l, gint subtype, gpointer sublayer )
1822 {
1823   switch ( subtype )
1824   {
1825     case VIK_TRW_LAYER_SUBLAYER_TRACKS: return NULL;
1826     case VIK_TRW_LAYER_SUBLAYER_WAYPOINTS: return NULL;
1827     case VIK_TRW_LAYER_SUBLAYER_TRACK:
1828     {
1829       VikTrack *tr = g_hash_table_lookup ( l->tracks, sublayer );
1830       if ( tr ) {
1831         // Could be a better way of handling strings - but this works...
1832         gchar time_buf1[20];
1833         gchar time_buf2[20];
1834         time_buf1[0] = '\0';
1835         time_buf2[0] = '\0';
1836         static gchar tmp_buf[100];
1837         // Compact info: Short date eg (11/20/99), duration and length
1838         // Hopefully these are the things that are most useful and so promoted into the tooltip
1839         if ( tr->trackpoints && VIK_TRACKPOINT(tr->trackpoints->data)->has_timestamp ) {
1840           // %x     The preferred date representation for the current locale without the time.
1841           strftime (time_buf1, sizeof(time_buf1), "%x: ", gmtime(&(VIK_TRACKPOINT(tr->trackpoints->data)->timestamp)));
1842           if ( VIK_TRACKPOINT(g_list_last(tr->trackpoints)->data)->has_timestamp ) {
1843             gint dur = ( (VIK_TRACKPOINT(g_list_last(tr->trackpoints)->data)->timestamp) - (VIK_TRACKPOINT(tr->trackpoints->data)->timestamp) );
1844             if ( dur > 0 )
1845               g_snprintf ( time_buf2, sizeof(time_buf2), _("- %d:%02d hrs:mins"), (int)round(dur/3600), (int)round((dur/60)%60) );
1846           }
1847         }
1848         // Get length and consider the appropriate distance units
1849         gdouble tr_len = vik_track_get_length(tr);
1850         vik_units_distance_t dist_units = a_vik_get_units_distance ();
1851         switch (dist_units) {
1852         case VIK_UNITS_DISTANCE_KILOMETRES:
1853           g_snprintf (tmp_buf, sizeof(tmp_buf), _("%s%.1f km %s"), time_buf1, tr_len/1000.0, time_buf2);
1854           break;
1855         case VIK_UNITS_DISTANCE_MILES:
1856           g_snprintf (tmp_buf, sizeof(tmp_buf), _("%s%.1f miles %s"), time_buf1, VIK_METERS_TO_MILES(tr_len), time_buf2);
1857           break;
1858         default:
1859           break;
1860         }
1861         return tmp_buf;
1862       }
1863     }
1864     break;
1865     case VIK_TRW_LAYER_SUBLAYER_WAYPOINT:
1866     {
1867       VikWaypoint *w = g_hash_table_lookup ( l->waypoints, sublayer );
1868       // NB It's OK to return NULL
1869       return w->comment;
1870     }
1871     break;
1872     default: break;
1873   }
1874   return NULL;
1875 }
1876
1877 /*
1878  * Function to show basic track point information on the statusbar
1879  */
1880 static void set_statusbar_msg_info_trkpt ( VikTrwLayer *vtl, VikTrackpoint *trkpt )
1881 {
1882   gchar tmp_buf1[64];
1883   switch (a_vik_get_units_height ()) {
1884   case VIK_UNITS_HEIGHT_FEET:
1885     g_snprintf(tmp_buf1, sizeof(tmp_buf1), _("Trkpt: Alt %dft"), (int)round(VIK_METERS_TO_FEET(trkpt->altitude)));
1886     break;
1887   default:
1888     //VIK_UNITS_HEIGHT_METRES:
1889     g_snprintf(tmp_buf1, sizeof(tmp_buf1), _("Trkpt: Alt %dm"), (int)round(trkpt->altitude));
1890   }
1891   
1892   gchar tmp_buf2[64];
1893   tmp_buf2[0] = '\0';
1894   if ( trkpt->has_timestamp ) {
1895     // Compact date time format
1896     strftime (tmp_buf2, sizeof(tmp_buf2), _(" | Time %x %X"), localtime(&(trkpt->timestamp)));
1897   }
1898
1899   // Position part
1900   // Position is put later on, as this bit may not be seen if the display is not big enough,
1901   //   one can easily use the current pointer position to see this if needed
1902   gchar *lat = NULL, *lon = NULL;
1903   static struct LatLon ll;
1904   vik_coord_to_latlon (&(trkpt->coord), &ll);
1905   a_coords_latlon_to_string ( &ll, &lat, &lon );
1906
1907   // Track name
1908   // Again is put later on, as this bit may not be seen if the display is not big enough
1909   //  trackname can be seen from the treeview (when enabled)
1910   // Also name could be very long to not leave room for anything else
1911   gchar tmp_buf3[64];
1912   tmp_buf3[0] = '\0';
1913   if ( vtl->current_tp_track_name ) {
1914     g_snprintf(tmp_buf3, sizeof(tmp_buf3),  _(" | Track: %s"), vtl->current_tp_track_name );
1915   }
1916
1917   // Combine parts to make overall message
1918   gchar *msg = g_strdup_printf (_("%s%s | %s %s %s"), tmp_buf1, tmp_buf2, lat, lon, tmp_buf3);
1919   vik_statusbar_set_message ( vik_window_get_statusbar (VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl))), VIK_STATUSBAR_INFO, msg );
1920   g_free ( lat );
1921   g_free ( lon );
1922   g_free ( msg );
1923 }
1924
1925 /*
1926  * Function to show basic waypoint information on the statusbar
1927  */
1928 static void set_statusbar_msg_info_wpt ( VikTrwLayer *vtl, VikWaypoint *wpt )
1929 {
1930   gchar tmp_buf1[64];
1931   switch (a_vik_get_units_height ()) {
1932   case VIK_UNITS_HEIGHT_FEET:
1933     g_snprintf(tmp_buf1, sizeof(tmp_buf1), _("Wpt: Alt %dft"), (int)round(VIK_METERS_TO_FEET(wpt->altitude)));
1934     break;
1935   default:
1936     //VIK_UNITS_HEIGHT_METRES:
1937     g_snprintf(tmp_buf1, sizeof(tmp_buf1), _("Wpt: Alt %dm"), (int)round(wpt->altitude));
1938   }
1939   
1940   // Position part
1941   // Position is put last, as this bit is most likely not to be seen if the display is not big enough,
1942   //   one can easily use the current pointer position to see this if needed
1943   gchar *lat = NULL, *lon = NULL;
1944   static struct LatLon ll;
1945   vik_coord_to_latlon (&(wpt->coord), &ll);
1946   a_coords_latlon_to_string ( &ll, &lat, &lon );
1947
1948   // Combine parts to make overall message
1949   gchar *msg;
1950   if ( wpt->comment )
1951     // Add comment if available
1952     msg = g_strdup_printf ( _("%s | %s %s | Comment: %s"), tmp_buf1, lat, lon, wpt->comment );
1953   else
1954     msg = g_strdup_printf ( _("%s | %s %s"), tmp_buf1, lat, lon );
1955   vik_statusbar_set_message ( vik_window_get_statusbar (VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl))), VIK_STATUSBAR_INFO, msg );
1956   g_free ( lat );
1957   g_free ( lon );
1958   g_free ( msg );
1959 }
1960
1961 /**
1962  * General layer selection function, find out which bit is selected and take appropriate action
1963  */
1964 static gboolean trw_layer_selected ( VikTrwLayer *l, gint subtype, gpointer sublayer, gint type, gpointer vlp )
1965 {
1966   // Reset
1967   l->current_wp      = NULL;
1968   l->current_wp_name = NULL;
1969   trw_layer_cancel_current_tp ( l, FALSE );
1970
1971   // Clear statusbar
1972   vik_statusbar_set_message ( vik_window_get_statusbar (VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(l))), VIK_STATUSBAR_INFO, "" );
1973
1974   switch ( type )
1975     {
1976     case VIK_TREEVIEW_TYPE_LAYER:
1977       {
1978         vik_window_set_selected_trw_layer ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l), l );
1979         /* Mark for redraw */
1980         return TRUE;
1981       }
1982       break;
1983
1984     case VIK_TREEVIEW_TYPE_SUBLAYER:
1985       {
1986         switch ( subtype )
1987           {
1988           case VIK_TRW_LAYER_SUBLAYER_TRACKS:
1989             {
1990               vik_window_set_selected_tracks ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l), l->tracks, l );
1991               /* Mark for redraw */
1992               return TRUE;
1993             }
1994             break;
1995           case VIK_TRW_LAYER_SUBLAYER_TRACK:
1996             {
1997               VikTrack *track = g_hash_table_lookup ( l->tracks, sublayer );
1998               vik_window_set_selected_track ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l), (gpointer)track, l, sublayer );
1999               /* Mark for redraw */
2000               return TRUE;
2001             }
2002             break;
2003           case VIK_TRW_LAYER_SUBLAYER_WAYPOINTS:
2004             {
2005               vik_window_set_selected_waypoints ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l), l->waypoints, l );
2006               /* Mark for redraw */
2007               return TRUE;
2008             }
2009             break;
2010           case VIK_TRW_LAYER_SUBLAYER_WAYPOINT:
2011             {
2012               VikWaypoint *wpt = g_hash_table_lookup ( l->waypoints, sublayer );
2013               vik_window_set_selected_waypoint ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l), (gpointer)wpt, l, sublayer );
2014               // Show some waypoint info
2015               set_statusbar_msg_info_wpt ( l, wpt );
2016               /* Mark for redraw */
2017               return TRUE;
2018             }
2019             break;
2020           default:
2021             {
2022               return vik_window_clear_highlight ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l) );
2023             }
2024             break;
2025           }
2026         return FALSE;
2027       }
2028       break;
2029
2030     default:
2031       return vik_window_clear_highlight ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l) );
2032       break;
2033     }
2034 }
2035
2036 GHashTable *vik_trw_layer_get_tracks ( VikTrwLayer *l )
2037 {
2038   return l->tracks;
2039 }
2040
2041 GHashTable *vik_trw_layer_get_waypoints ( VikTrwLayer *l )
2042 {
2043   return l->waypoints;
2044 }
2045
2046 static void trw_layer_find_maxmin_waypoints ( const gchar *name, const VikWaypoint *w, struct LatLon maxmin[2] )
2047 {
2048   static VikCoord fixme;
2049   vik_coord_copy_convert ( &(w->coord), VIK_COORD_LATLON, &fixme );
2050   if ( VIK_LATLON(&fixme)->lat > maxmin[0].lat || maxmin[0].lat == 0.0 )
2051     maxmin[0].lat = VIK_LATLON(&fixme)->lat;
2052   if ( VIK_LATLON(&fixme)->lat < maxmin[1].lat || maxmin[1].lat == 0.0 )
2053     maxmin[1].lat = VIK_LATLON(&fixme)->lat;
2054   if ( VIK_LATLON(&fixme)->lon > maxmin[0].lon || maxmin[0].lon == 0.0 )
2055     maxmin[0].lon = VIK_LATLON(&fixme)->lon;
2056   if ( VIK_LATLON(&fixme)->lon < maxmin[1].lon || maxmin[1].lon == 0.0 )
2057     maxmin[1].lon = VIK_LATLON(&fixme)->lon;
2058 }
2059
2060 static void trw_layer_find_maxmin_tracks ( const gchar *name, GList **t, struct LatLon maxmin[2] )
2061 {
2062   GList *tr = *t;
2063   static VikCoord fixme;
2064
2065   while ( tr )
2066   {
2067     vik_coord_copy_convert ( &(VIK_TRACKPOINT(tr->data)->coord), VIK_COORD_LATLON, &fixme );
2068     if ( VIK_LATLON(&fixme)->lat > maxmin[0].lat || maxmin[0].lat == 0.0 )
2069       maxmin[0].lat = VIK_LATLON(&fixme)->lat;
2070     if ( VIK_LATLON(&fixme)->lat < maxmin[1].lat || maxmin[1].lat == 0.0 )
2071       maxmin[1].lat = VIK_LATLON(&fixme)->lat;
2072     if ( VIK_LATLON(&fixme)->lon > maxmin[0].lon || maxmin[0].lon == 0.0 )
2073       maxmin[0].lon = VIK_LATLON(&fixme)->lon;
2074     if ( VIK_LATLON(&fixme)->lon < maxmin[1].lon || maxmin[1].lon == 0.0 )
2075       maxmin[1].lon = VIK_LATLON(&fixme)->lon;
2076     tr = tr->next;
2077   }
2078 }
2079
2080 static void trw_layer_find_maxmin (VikTrwLayer *vtl, struct LatLon maxmin[2])
2081 {
2082   struct LatLon wpt_maxmin[2] = { {0.0,0.0}, {0.0,0.0} };
2083   struct LatLon trk_maxmin[2] = { {0.0,0.0}, {0.0,0.0} };
2084   
2085   g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_find_maxmin_waypoints, wpt_maxmin );
2086   g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_find_maxmin_tracks, trk_maxmin );
2087   if ((wpt_maxmin[0].lat != 0.0 && wpt_maxmin[0].lat > trk_maxmin[0].lat) || trk_maxmin[0].lat == 0.0) {
2088     maxmin[0].lat = wpt_maxmin[0].lat;
2089   }
2090   else {
2091     maxmin[0].lat = trk_maxmin[0].lat;
2092   }
2093   if ((wpt_maxmin[0].lon != 0.0 && wpt_maxmin[0].lon > trk_maxmin[0].lon) || trk_maxmin[0].lon == 0.0) {
2094     maxmin[0].lon = wpt_maxmin[0].lon;
2095   }
2096   else {
2097     maxmin[0].lon = trk_maxmin[0].lon;
2098   }
2099   if ((wpt_maxmin[1].lat != 0.0 && wpt_maxmin[1].lat < trk_maxmin[1].lat) || trk_maxmin[1].lat == 0.0) {
2100     maxmin[1].lat = wpt_maxmin[1].lat;
2101   }
2102   else {
2103     maxmin[1].lat = trk_maxmin[1].lat;
2104   }
2105   if ((wpt_maxmin[1].lon != 0.0 && wpt_maxmin[1].lon < trk_maxmin[1].lon) || trk_maxmin[1].lon == 0.0) {
2106     maxmin[1].lon = wpt_maxmin[1].lon;
2107   }
2108   else {
2109     maxmin[1].lon = trk_maxmin[1].lon;
2110   }
2111 }
2112
2113 gboolean vik_trw_layer_find_center ( VikTrwLayer *vtl, VikCoord *dest )
2114 {
2115   /* 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... */
2116   struct LatLon maxmin[2] = { {0.0,0.0}, {0.0,0.0} };
2117   trw_layer_find_maxmin (vtl, maxmin);
2118   if (maxmin[0].lat == 0.0 && maxmin[0].lon == 0.0 && maxmin[1].lat == 0.0 && maxmin[1].lon == 0.0)
2119     return FALSE;
2120   else
2121   {
2122     struct LatLon average = { (maxmin[0].lat+maxmin[1].lat)/2, (maxmin[0].lon+maxmin[1].lon)/2 };
2123     vik_coord_load_from_latlon ( dest, vtl->coord_mode, &average );
2124     return TRUE;
2125   }
2126 }
2127
2128 static void trw_layer_centerize ( gpointer layer_and_vlp[2] )
2129 {
2130   VikCoord coord;
2131   if ( vik_trw_layer_find_center ( VIK_TRW_LAYER(layer_and_vlp[0]), &coord ) )
2132     goto_coord ( layer_and_vlp[1], NULL, NULL, &coord );
2133   else
2134     a_dialog_info_msg ( VIK_GTK_WINDOW_FROM_LAYER(layer_and_vlp[0]), _("This layer has no waypoints or trackpoints.") );
2135 }
2136
2137 static void trw_layer_zoom_to_show_latlons ( VikTrwLayer *vtl, VikViewport *vvp, struct LatLon maxmin[2] )
2138 {
2139   /* First set the center [in case previously viewing from elsewhere] */
2140   /* Then loop through zoom levels until provided positions are in view */
2141   /* This method is not particularly fast - but should work well enough */
2142   struct LatLon average = { (maxmin[0].lat+maxmin[1].lat)/2, (maxmin[0].lon+maxmin[1].lon)/2 };
2143   VikCoord coord;
2144   vik_coord_load_from_latlon ( &coord, vtl->coord_mode, &average );
2145   vik_viewport_set_center_coord ( vvp, &coord );
2146
2147   /* Convert into definite 'smallest' and 'largest' positions */
2148   struct LatLon minmin;
2149   if ( maxmin[0].lat < maxmin[1].lat )
2150     minmin.lat = maxmin[0].lat;
2151   else
2152     minmin.lat = maxmin[1].lat;
2153
2154   struct LatLon maxmax;
2155   if ( maxmin[0].lon > maxmin[1].lon )
2156     maxmax.lon = maxmin[0].lon;
2157   else
2158     maxmax.lon = maxmin[1].lon;
2159
2160   /* Never zoom in too far - generally not that useful, as too close ! */
2161   /* Always recalculate the 'best' zoom level */
2162   gdouble zoom = 1.0;
2163   vik_viewport_set_zoom ( vvp, zoom );
2164
2165   gdouble min_lat, max_lat, min_lon, max_lon;
2166   /* Should only be a maximum of about 18 iterations from min to max zoom levels */
2167   while ( zoom <= VIK_VIEWPORT_MAX_ZOOM ) {
2168     vik_viewport_get_min_max_lat_lon ( vvp, &min_lat, &max_lat, &min_lon, &max_lon );
2169     /* NB I think the logic used in this test to determine if the bounds is within view
2170        fails if track goes across 180 degrees longitude.
2171        Hopefully that situation is not too common...
2172        Mind you viking doesn't really do edge locations to well anyway */
2173     if ( min_lat < minmin.lat &&
2174          max_lat > minmin.lat &&
2175          min_lon < maxmax.lon &&
2176          max_lon > maxmax.lon )
2177       /* Found within zoom level */
2178       break;
2179
2180     /* Try next */
2181     zoom = zoom * 2;
2182     vik_viewport_set_zoom ( vvp, zoom );
2183   }
2184 }
2185
2186 gboolean vik_trw_layer_auto_set_view ( VikTrwLayer *vtl, VikViewport *vvp )
2187 {
2188   /* TODO: what if there's only one waypoint @ 0,0, it will think nothing found. */
2189   struct LatLon maxmin[2] = { {0.0,0.0}, {0.0,0.0} };
2190   trw_layer_find_maxmin (vtl, maxmin);
2191   if (maxmin[0].lat == 0.0 && maxmin[0].lon == 0.0 && maxmin[1].lat == 0.0 && maxmin[1].lon == 0.0)
2192     return FALSE;
2193   else {
2194     trw_layer_zoom_to_show_latlons ( vtl, vvp, maxmin );
2195     return TRUE;
2196   }
2197 }
2198
2199 static void trw_layer_auto_view ( gpointer layer_and_vlp[2] )
2200 {
2201   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])) ) ) {
2202     vik_layers_panel_emit_update ( VIK_LAYERS_PANEL(layer_and_vlp[1]) );
2203   }
2204   else
2205     a_dialog_info_msg ( VIK_GTK_WINDOW_FROM_LAYER(layer_and_vlp[0]), _("This layer has no waypoints or trackpoints.") );
2206 }
2207
2208 static void trw_layer_export ( gpointer layer_and_vlp[2], const gchar *title, const gchar* default_name, const gchar* trackname, guint file_type )
2209 {
2210   GtkWidget *file_selector;
2211   const gchar *fn;
2212   gboolean failed = FALSE;
2213   file_selector = gtk_file_chooser_dialog_new (title,
2214                                                NULL,
2215                                                GTK_FILE_CHOOSER_ACTION_SAVE,
2216                                                GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
2217                                                GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT,
2218                                                NULL);
2219   gtk_file_chooser_set_current_name (GTK_FILE_CHOOSER(file_selector), default_name);
2220
2221   while ( gtk_dialog_run ( GTK_DIALOG(file_selector) ) == GTK_RESPONSE_ACCEPT )
2222   {
2223     fn = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER(file_selector) );
2224     if ( g_file_test ( fn, G_FILE_TEST_EXISTS ) == FALSE )
2225     {
2226       gtk_widget_hide ( file_selector );
2227       failed = ! a_file_export ( VIK_TRW_LAYER(layer_and_vlp[0]), fn, file_type, trackname);
2228       break;
2229     }
2230     else
2231     {
2232       if ( a_dialog_yes_or_no ( GTK_WINDOW(file_selector), _("The file \"%s\" exists, do you wish to overwrite it?"), a_file_basename ( fn ) ) )
2233       {
2234         gtk_widget_hide ( file_selector );
2235         failed = ! a_file_export ( VIK_TRW_LAYER(layer_and_vlp[0]), fn, file_type, trackname);
2236         break;
2237       }
2238     }
2239   }
2240   gtk_widget_destroy ( file_selector );
2241   if ( failed )
2242     a_dialog_error_msg ( VIK_GTK_WINDOW_FROM_LAYER(layer_and_vlp[0]), _("The filename you requested could not be opened for writing.") );
2243 }
2244
2245 static void trw_layer_export_gpspoint ( 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_GPSPOINT );
2248 }
2249
2250 static void trw_layer_export_gpsmapper ( gpointer layer_and_vlp[2] )
2251 {
2252   trw_layer_export ( layer_and_vlp, _("Export Layer"), vik_layer_get_name(VIK_LAYER(layer_and_vlp[0])), NULL, FILE_TYPE_GPSMAPPER );
2253 }
2254
2255 static void trw_layer_export_gpx ( gpointer layer_and_vlp[2] )
2256 {
2257   /* Auto append '.gpx' to track name (providing it's not already there) for the default filename */
2258   gchar *auto_save_name = g_strdup ( vik_layer_get_name(VIK_LAYER(layer_and_vlp[0])) );
2259   if ( ! check_file_ext ( auto_save_name, ".gpx" ) )
2260     auto_save_name = g_strconcat ( auto_save_name, ".gpx", NULL );
2261
2262   trw_layer_export ( layer_and_vlp, _("Export Layer"), auto_save_name, NULL, FILE_TYPE_GPX );
2263
2264   g_free ( auto_save_name );
2265 }
2266
2267 static void trw_layer_export_kml ( gpointer layer_and_vlp[2] )
2268 {
2269   /* Auto append '.kml' to the name (providing it's not already there) for the default filename */
2270   gchar *auto_save_name = g_strdup ( vik_layer_get_name(VIK_LAYER(layer_and_vlp[0])) );
2271   if ( ! check_file_ext ( auto_save_name, ".kml" ) )
2272     auto_save_name = g_strconcat ( auto_save_name, ".kml", NULL );
2273
2274   trw_layer_export ( layer_and_vlp, _("Export Layer"), auto_save_name, NULL, FILE_TYPE_KML );
2275
2276   g_free ( auto_save_name );
2277 }
2278
2279 /**
2280  * Convert the given TRW layer into a temporary GPX file and open it with the specified program
2281  *
2282  */
2283 static void trw_layer_export_external_gpx ( gpointer layer_and_vlp[2], const gchar* external_program )
2284 {
2285   gchar *name_used = NULL;
2286   int fd;
2287
2288   if ((fd = g_file_open_tmp("tmp-viking.XXXXXX.gpx", &name_used, NULL)) >= 0) {
2289     gboolean failed = ! a_file_export ( VIK_TRW_LAYER(layer_and_vlp[0]), name_used, FILE_TYPE_GPX, NULL);
2290     if (failed) {
2291       a_dialog_error_msg (VIK_GTK_WINDOW_FROM_LAYER(layer_and_vlp[0]), _("Could not create temporary file for export.") );
2292     }
2293     else {
2294       GError *err = NULL;
2295       gchar *quoted_file = g_shell_quote ( name_used );
2296       gchar *cmd = g_strdup_printf ( "%s %s", external_program, quoted_file );
2297       g_free ( quoted_file );
2298       if ( ! g_spawn_command_line_async ( cmd, &err ) )
2299         {
2300           a_dialog_error_msg_extra ( VIK_GTK_WINDOW_FROM_LAYER( layer_and_vlp[0]), _("Could not launch %s."), external_program );
2301           g_error_free ( err );
2302         }
2303       g_free ( cmd );
2304     }
2305     // Note ATM the 'temporary' file is not deleted, as loading via another program is not instantaneous
2306     //g_remove ( name_used );
2307     // Perhaps should be deleted when the program ends?
2308     // For now leave it to the user to delete it / use system temp cleanup methods.
2309     g_free ( name_used );
2310   }
2311 }
2312
2313 static void trw_layer_export_external_gpx_1 ( gpointer layer_and_vlp[2] )
2314 {
2315   trw_layer_export_external_gpx ( layer_and_vlp, a_vik_get_external_gpx_program_1() );
2316 }
2317
2318 static void trw_layer_export_external_gpx_2 ( gpointer layer_and_vlp[2] )
2319 {
2320   trw_layer_export_external_gpx ( layer_and_vlp, a_vik_get_external_gpx_program_2() );
2321 }
2322
2323 static void trw_layer_export_gpx_track ( gpointer pass_along[6] )
2324 {
2325   gpointer layer_and_vlp[2];
2326   layer_and_vlp[0] = pass_along[0];
2327   layer_and_vlp[1] = pass_along[1];
2328
2329   /* Auto append '.gpx' to track name (providing it's not already there) for the default filename */
2330   gchar *auto_save_name = g_strdup ( pass_along[3] );
2331   if ( ! check_file_ext ( auto_save_name, ".gpx" ) )
2332     auto_save_name = g_strconcat ( auto_save_name, ".gpx", NULL );
2333
2334   trw_layer_export ( layer_and_vlp, _("Export Track as GPX"), auto_save_name, pass_along[3], FILE_TYPE_GPX );
2335
2336   g_free ( auto_save_name );
2337 }
2338
2339 static void trw_layer_goto_wp ( gpointer layer_and_vlp[2] )
2340 {
2341   GHashTable *wps = vik_trw_layer_get_waypoints ( VIK_TRW_LAYER(layer_and_vlp[0]) );
2342   GtkWidget *dia = gtk_dialog_new_with_buttons (_("Find"),
2343                                                  VIK_GTK_WINDOW_FROM_LAYER(layer_and_vlp[0]),
2344                                                  GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
2345                                                  GTK_STOCK_CANCEL,
2346                                                  GTK_RESPONSE_REJECT,
2347                                                  GTK_STOCK_OK,
2348                                                  GTK_RESPONSE_ACCEPT,
2349                                                  NULL);
2350
2351   GtkWidget *label, *entry;
2352   label = gtk_label_new(_("Waypoint Name:"));
2353   entry = gtk_entry_new();
2354
2355   gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dia)->vbox), label, FALSE, FALSE, 0);
2356   gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dia)->vbox), entry, FALSE, FALSE, 0);
2357   gtk_widget_show_all ( label );
2358   gtk_widget_show_all ( entry );
2359
2360   gtk_dialog_set_default_response ( GTK_DIALOG(dia), GTK_RESPONSE_ACCEPT );
2361
2362   while ( gtk_dialog_run ( GTK_DIALOG(dia) ) == GTK_RESPONSE_ACCEPT )
2363   {
2364     VikWaypoint *wp;
2365     gchar *upname = g_strdup(gtk_entry_get_text(GTK_ENTRY(entry)));
2366     int i;
2367
2368     for ( i = strlen(upname)-1; i >= 0; i-- )
2369       upname[i] = toupper(upname[i]);
2370
2371     wp = g_hash_table_lookup ( wps, upname );
2372
2373     if (!wp)
2374       a_dialog_error_msg ( VIK_GTK_WINDOW_FROM_LAYER(layer_and_vlp[0]), _("Waypoint not found in this layer.") );
2375     else
2376     {
2377       vik_viewport_set_center_coord ( vik_layers_panel_get_viewport(VIK_LAYERS_PANEL(layer_and_vlp[1])), &(wp->coord) );
2378       vik_layers_panel_emit_update ( VIK_LAYERS_PANEL(layer_and_vlp[1]) );
2379       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 );
2380       break;
2381     }
2382
2383     g_free ( upname );
2384
2385   }
2386   gtk_widget_destroy ( dia );
2387 }
2388
2389 gboolean vik_trw_layer_new_waypoint ( VikTrwLayer *vtl, GtkWindow *w, const VikCoord *def_coord )
2390 {
2391   gchar *default_name = highest_wp_number_get(vtl);
2392   VikWaypoint *wp = vik_waypoint_new();
2393   gchar *returned_name;
2394   gboolean updated;
2395   wp->coord = *def_coord;
2396   
2397   // Attempt to auto set height if DEM data is available
2398   gint16 elev = a_dems_get_elev_by_coord ( &(wp->coord), VIK_DEM_INTERPOL_BEST );
2399   if ( elev != VIK_DEM_INVALID_ELEVATION )
2400     wp->altitude = (gdouble)elev;
2401
2402   if ( ( returned_name = a_dialog_waypoint ( w, default_name, wp, vik_trw_layer_get_waypoints ( vtl ), vtl->coord_mode, TRUE, &updated ) ) )
2403   {
2404     wp->visible = TRUE;
2405     vik_trw_layer_add_waypoint ( vtl, returned_name, wp );
2406     g_free (default_name);
2407     return TRUE;
2408   }
2409   g_free (default_name);
2410   vik_waypoint_free(wp);
2411   return FALSE;
2412 }
2413
2414 static void trw_layer_new_wikipedia_wp_viewport ( gpointer lav[2] )
2415 {
2416   VikCoord one, two;
2417   struct LatLon one_ll, two_ll;
2418   struct LatLon maxmin[2] = { {0.0,0.0}, {0.0,0.0} };
2419
2420   VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
2421   VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
2422   VikWindow *vw = (VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl));
2423   VikViewport *vvp =  vik_window_viewport(vw);
2424   vik_viewport_screen_to_coord ( vvp, 0, 0, &one);
2425   vik_viewport_screen_to_coord ( vvp, vik_viewport_get_width(vvp), vik_viewport_get_height(vvp), &two);
2426   vik_coord_to_latlon(&one, &one_ll);
2427   vik_coord_to_latlon(&two, &two_ll);
2428   if (one_ll.lat > two_ll.lat) {
2429     maxmin[0].lat = one_ll.lat;
2430     maxmin[1].lat = two_ll.lat;
2431   }
2432   else {
2433     maxmin[0].lat = two_ll.lat;
2434     maxmin[1].lat = one_ll.lat;
2435   }
2436   if (one_ll.lon > two_ll.lon) {
2437     maxmin[0].lon = one_ll.lon;
2438     maxmin[1].lon = two_ll.lon;
2439   }
2440   else {
2441     maxmin[0].lon = two_ll.lon;
2442     maxmin[1].lon = one_ll.lon;
2443   }
2444   a_geonames_wikipedia_box((VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl)), vtl, vlp, maxmin);
2445 }
2446
2447 static void trw_layer_new_wikipedia_wp_layer ( gpointer lav[2] )
2448 {
2449   VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
2450   VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
2451   struct LatLon maxmin[2] = { {0.0,0.0}, {0.0,0.0} };
2452   
2453   trw_layer_find_maxmin (vtl, maxmin);
2454   a_geonames_wikipedia_box((VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl)), vtl, vlp, maxmin);
2455 }
2456
2457 #ifdef VIK_CONFIG_GEOTAG
2458 static void trw_layer_geotagging_waypoint_mtime_keep ( gpointer pass_along[6] )
2459 {
2460   VikWaypoint *wp = g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->waypoints, pass_along[3] );
2461   if ( wp )
2462     // Update directly - not changing the mtime
2463     a_geotag_write_exif_gps ( wp->image, wp->coord, wp->altitude, TRUE );
2464 }
2465
2466 static void trw_layer_geotagging_waypoint_mtime_update ( gpointer pass_along[6] )
2467 {
2468   VikWaypoint *wp = g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->waypoints, pass_along[3] );
2469   if ( wp )
2470     // Update directly
2471     a_geotag_write_exif_gps ( wp->image, wp->coord, wp->altitude, FALSE );
2472 }
2473
2474 /*
2475  * Use code in separate file for this feature as reasonably complex
2476  */
2477 static void trw_layer_geotagging_track ( gpointer pass_along[6] )
2478 {
2479   VikTrwLayer *vtl = VIK_TRW_LAYER(pass_along[0]);
2480   VikTrack *track = g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, pass_along[3] );
2481   // Unset so can be reverified later if necessary
2482   vtl->has_verified_thumbnails = FALSE;
2483
2484   trw_layer_geotag_dialog ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
2485                             vtl,
2486                             track,
2487                             pass_along[3] );
2488 }
2489
2490 static void trw_layer_geotagging ( gpointer lav[2] )
2491 {
2492   VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
2493   // Unset so can be reverified later if necessary
2494   vtl->has_verified_thumbnails = FALSE;
2495
2496   trw_layer_geotag_dialog ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
2497                             vtl,
2498                             NULL,
2499                             NULL);
2500 }
2501 #endif
2502
2503 // 'Acquires' - Same as in File Menu -> Acquire - applies into the selected TRW Layer //
2504
2505 /*
2506  * Acquire into this TRW Layer straight from GPS Device
2507  */
2508 static void trw_layer_acquire_gps_cb ( gpointer lav[2] )
2509 {
2510   VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
2511   VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
2512   VikWindow *vw = (VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl));
2513   VikViewport *vvp =  vik_window_viewport(vw);
2514
2515   vik_datasource_gps_interface.mode = VIK_DATASOURCE_ADDTOLAYER;
2516   a_acquire ( vw, vlp, vvp, &vik_datasource_gps_interface );
2517 }
2518
2519 /*
2520  * Acquire into this TRW Layer from Google Directions
2521  */
2522 static void trw_layer_acquire_google_cb ( gpointer lav[2] )
2523 {
2524   VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
2525   VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
2526   VikWindow *vw = (VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl));
2527   VikViewport *vvp =  vik_window_viewport(vw);
2528
2529   a_acquire ( vw, vlp, vvp, &vik_datasource_google_interface );
2530 }
2531
2532 #ifdef VIK_CONFIG_OPENSTREETMAP
2533 /*
2534  * Acquire into this TRW Layer from OSM
2535  */
2536 static void trw_layer_acquire_osm_cb ( gpointer lav[2] )
2537 {
2538   VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
2539   VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
2540   VikWindow *vw = (VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl));
2541   VikViewport *vvp =  vik_window_viewport(vw);
2542
2543   a_acquire ( vw, vlp, vvp, &vik_datasource_osm_interface );
2544 }
2545 #endif
2546
2547 #ifdef VIK_CONFIG_GEOCACHES
2548 /*
2549  * Acquire into this TRW Layer from Geocaching.com
2550  */
2551 static void trw_layer_acquire_geocache_cb ( gpointer lav[2] )
2552 {
2553   VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
2554   VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
2555   VikWindow *vw = (VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl));
2556   VikViewport *vvp =  vik_window_viewport(vw);
2557
2558   a_acquire ( vw, vlp, vvp, &vik_datasource_gc_interface );
2559 }
2560 #endif
2561
2562 #ifdef VIK_CONFIG_GEOTAG
2563 /*
2564  * Acquire into this TRW Layer from images
2565  */
2566 static void trw_layer_acquire_geotagged_cb ( gpointer lav[2] )
2567 {
2568   VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
2569   VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
2570   VikWindow *vw = (VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl));
2571   VikViewport *vvp =  vik_window_viewport(vw);
2572
2573   vik_datasource_geotag_interface.mode = VIK_DATASOURCE_ADDTOLAYER;
2574   a_acquire ( vw, vlp, vvp, &vik_datasource_geotag_interface );
2575
2576   // Reverify thumbnails as they may have changed
2577   vtl->has_verified_thumbnails = FALSE;
2578   trw_layer_verify_thumbnails ( vtl, NULL );
2579 }
2580 #endif
2581
2582 static void trw_layer_new_wp ( gpointer lav[2] )
2583 {
2584   VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
2585   VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
2586   /* TODO longone: okay, if layer above (aggregate) is invisible but vtl->visible is true, this redraws for no reason.
2587      instead return true if you want to update. */
2588   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 )
2589     vik_layers_panel_emit_update ( vlp );
2590 }
2591
2592 static void trw_layer_auto_tracks_view ( gpointer lav[2] )
2593 {
2594   VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
2595   VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
2596
2597   if ( g_hash_table_size (vtl->tracks) > 0 ) {
2598     struct LatLon maxmin[2] = { {0,0}, {0,0} };
2599     g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_find_maxmin_tracks, maxmin );
2600     trw_layer_zoom_to_show_latlons ( vtl, vik_layers_panel_get_viewport (vlp), maxmin );
2601     vik_layers_panel_emit_update ( vlp );
2602   }
2603 }
2604
2605 static void trw_layer_single_waypoint_jump ( const gchar *name, const VikWaypoint *wp, gpointer vvp )
2606 {
2607   /* NB do not care if wp is visible or not */
2608   vik_viewport_set_center_coord ( VIK_VIEWPORT(vvp), &(wp->coord) );
2609 }
2610
2611 static void trw_layer_auto_waypoints_view ( gpointer lav[2] )
2612 {
2613   VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
2614   VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
2615
2616   /* Only 1 waypoint - jump straight to it */
2617   if ( g_hash_table_size (vtl->waypoints) == 1 ) {
2618     VikViewport *vvp = vik_layers_panel_get_viewport (vlp);
2619     g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_single_waypoint_jump, (gpointer) vvp );
2620   }
2621   /* If at least 2 waypoints - find center and then zoom to fit */
2622   else if ( g_hash_table_size (vtl->waypoints) > 1 )
2623   {
2624     struct LatLon maxmin[2] = { {0,0}, {0,0} };
2625     g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_find_maxmin_waypoints, maxmin );
2626     trw_layer_zoom_to_show_latlons ( vtl, vik_layers_panel_get_viewport (vlp), maxmin );
2627   }
2628
2629   vik_layers_panel_emit_update ( vlp );
2630 }
2631
2632 static void trw_layer_add_menu_items ( VikTrwLayer *vtl, GtkMenu *menu, gpointer vlp )
2633 {
2634   static gpointer pass_along[2];
2635   GtkWidget *item;
2636   GtkWidget *export_submenu;
2637   pass_along[0] = vtl;
2638   pass_along[1] = vlp;
2639
2640   item = gtk_menu_item_new();
2641   gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
2642   gtk_widget_show ( item );
2643
2644   /* Now with icons */
2645   item = gtk_image_menu_item_new_with_mnemonic ( _("_View Layer") );
2646   gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ZOOM_FIT, GTK_ICON_SIZE_MENU) );
2647   g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_view), pass_along );
2648   gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
2649   gtk_widget_show ( item );
2650
2651   item = gtk_menu_item_new_with_mnemonic ( _("View All Trac_ks") );
2652   g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_tracks_view), pass_along );
2653   gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
2654   gtk_widget_show ( item );
2655
2656   item = gtk_menu_item_new_with_mnemonic ( _("V_iew All Waypoints") );
2657   g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_waypoints_view), pass_along );
2658   gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
2659   gtk_widget_show ( item );
2660
2661   item = gtk_image_menu_item_new_with_mnemonic ( _("_Goto Center of Layer") );
2662   gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_JUMP_TO, GTK_ICON_SIZE_MENU) );
2663   g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_centerize), pass_along );
2664   gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
2665   gtk_widget_show ( item );
2666
2667   item = gtk_menu_item_new_with_mnemonic ( _("Goto _Waypoint...") );
2668   g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_wp), pass_along );
2669   gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
2670   gtk_widget_show ( item );
2671
2672   export_submenu = gtk_menu_new ();
2673   item = gtk_image_menu_item_new_with_mnemonic ( _("_Export Layer") );
2674   gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_HARDDISK, GTK_ICON_SIZE_MENU) );
2675   gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
2676   gtk_widget_show ( item );
2677   gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), export_submenu );
2678   
2679   item = gtk_menu_item_new_with_mnemonic ( _("Export as GPS_Point...") );
2680   g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_gpspoint), pass_along );
2681   gtk_menu_shell_append (GTK_MENU_SHELL (export_submenu), item);
2682   gtk_widget_show ( item );
2683
2684   item = gtk_menu_item_new_with_mnemonic ( _("Export as GPS_Mapper...") );
2685   g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_gpsmapper), pass_along );
2686   gtk_menu_shell_append (GTK_MENU_SHELL (export_submenu), item);
2687   gtk_widget_show ( item );
2688
2689   item = gtk_menu_item_new_with_mnemonic ( _("Export as _GPX...") );
2690   g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_gpx), pass_along );
2691   gtk_menu_shell_append (GTK_MENU_SHELL (export_submenu), item);
2692   gtk_widget_show ( item );
2693
2694   item = gtk_menu_item_new_with_mnemonic ( _("Export as _KML...") );
2695   g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_kml), pass_along );
2696   gtk_menu_shell_append (GTK_MENU_SHELL (export_submenu), item);
2697   gtk_widget_show ( item );
2698
2699   gchar* external1 = g_strconcat ( _("Open with External Program_1: "), a_vik_get_external_gpx_program_1(), NULL );
2700   item = gtk_menu_item_new_with_mnemonic ( external1 );
2701   g_free ( external1 );
2702   g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_external_gpx_1), pass_along );
2703   gtk_menu_shell_append (GTK_MENU_SHELL (export_submenu), item);
2704   gtk_widget_show ( item );
2705
2706   gchar* external2 = g_strconcat ( _("Open with External Program_2: "), a_vik_get_external_gpx_program_2(), NULL );
2707   item = gtk_menu_item_new_with_mnemonic ( external2 );
2708   g_free ( external2 );
2709   g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_external_gpx_2), pass_along );
2710   gtk_menu_shell_append (GTK_MENU_SHELL (export_submenu), item);
2711   gtk_widget_show ( item );
2712
2713   item = gtk_image_menu_item_new_with_mnemonic ( _("_New Waypoint...") );
2714   gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NEW, GTK_ICON_SIZE_MENU) );
2715   g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_wp), pass_along );
2716   gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
2717   gtk_widget_show ( item );
2718
2719 #ifdef VIK_CONFIG_GEONAMES
2720   GtkWidget *wikipedia_submenu = gtk_menu_new();
2721   item = gtk_image_menu_item_new_with_mnemonic ( _("_Add Wikipedia Waypoints") );
2722   gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ADD, GTK_ICON_SIZE_MENU) );
2723   gtk_menu_shell_append(GTK_MENU_SHELL (menu), item);
2724   gtk_widget_show(item);
2725   gtk_menu_item_set_submenu(GTK_MENU_ITEM(item), wikipedia_submenu);
2726
2727   item = gtk_image_menu_item_new_with_mnemonic ( _("Within _Layer Bounds") );
2728   gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ZOOM_FIT, GTK_ICON_SIZE_MENU) );
2729   g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_wikipedia_wp_layer), pass_along );
2730   gtk_menu_shell_append (GTK_MENU_SHELL (wikipedia_submenu), item);
2731   gtk_widget_show ( item );
2732
2733   item = gtk_image_menu_item_new_with_mnemonic ( _("Within _Current View") );
2734   gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ZOOM_100, GTK_ICON_SIZE_MENU) );
2735   g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_wikipedia_wp_viewport), pass_along );
2736   gtk_menu_shell_append (GTK_MENU_SHELL (wikipedia_submenu), item);
2737   gtk_widget_show ( item );
2738 #endif
2739
2740 #ifdef VIK_CONFIG_GEOTAG
2741   item = gtk_menu_item_new_with_mnemonic ( _("Geotag _Images...") );
2742   g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_geotagging), pass_along );
2743   gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
2744   gtk_widget_show ( item );
2745 #endif
2746
2747   GtkWidget *acquire_submenu = gtk_menu_new ();
2748   item = gtk_image_menu_item_new_with_mnemonic ( _("Ac_quire") );
2749   gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_DOWN, GTK_ICON_SIZE_MENU) );
2750   gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
2751   gtk_widget_show ( item );
2752   gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), acquire_submenu );
2753   
2754   item = gtk_menu_item_new_with_mnemonic ( _("From _GPS...") );
2755   g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_acquire_gps_cb), pass_along );
2756   gtk_menu_shell_append (GTK_MENU_SHELL (acquire_submenu), item);
2757   gtk_widget_show ( item );
2758
2759   item = gtk_menu_item_new_with_mnemonic ( _("From G_oogle Directions...") );
2760   g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_acquire_google_cb), pass_along );
2761   gtk_menu_shell_append (GTK_MENU_SHELL (acquire_submenu), item);
2762   gtk_widget_show ( item );
2763
2764 #ifdef VIK_CONFIG_OPENSTREETMAP
2765   item = gtk_menu_item_new_with_mnemonic ( _("From _OSM Traces...") );
2766   g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_acquire_osm_cb), pass_along );
2767   gtk_menu_shell_append (GTK_MENU_SHELL (acquire_submenu), item);
2768   gtk_widget_show ( item );
2769 #endif
2770
2771 #ifdef VIK_CONFIG_GEOCACHES
2772   item = gtk_menu_item_new_with_mnemonic ( _("From Geo_caching...") );
2773   g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_acquire_geocache_cb), pass_along );
2774   gtk_menu_shell_append (GTK_MENU_SHELL (acquire_submenu), item);
2775   gtk_widget_show ( item );
2776 #endif
2777
2778 #ifdef VIK_CONFIG_GEOTAG
2779   item = gtk_menu_item_new_with_mnemonic ( _("From Geotagged _Images...") );
2780   g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_acquire_geotagged_cb), pass_along );
2781   gtk_menu_shell_append (GTK_MENU_SHELL (acquire_submenu), item);
2782   gtk_widget_show ( item );
2783 #endif
2784
2785 #ifdef VIK_CONFIG_OPENSTREETMAP 
2786   item = gtk_image_menu_item_new_with_mnemonic ( _("Upload to _OSM...") );
2787   gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_UP, GTK_ICON_SIZE_MENU) );
2788   g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(osm_traces_upload_cb), pass_along );
2789   gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
2790   gtk_widget_show ( item );
2791 #endif
2792
2793   GtkWidget *delete_submenu = gtk_menu_new ();
2794   item = gtk_image_menu_item_new_with_mnemonic ( _("De_lete") );
2795   gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU) );
2796   gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
2797   gtk_widget_show ( item );
2798   gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), delete_submenu );
2799   
2800   item = gtk_image_menu_item_new_with_mnemonic ( _("Delete All _Tracks") );
2801   gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU) );
2802   g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_all_tracks), pass_along );
2803   gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
2804   gtk_widget_show ( item );
2805   
2806   item = gtk_image_menu_item_new_with_mnemonic ( _("Delete Tracks _From Selection...") );
2807   gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
2808   g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_tracks_from_selection), pass_along );
2809   gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
2810   gtk_widget_show ( item );
2811   
2812   item = gtk_image_menu_item_new_with_mnemonic ( _("Delete All _Waypoints") );
2813   gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU) );
2814   g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_all_waypoints), pass_along );
2815   gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
2816   gtk_widget_show ( item );
2817   
2818   item = gtk_image_menu_item_new_with_mnemonic ( _("Delete Waypoints From _Selection...") );
2819   gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
2820   g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_waypoints_from_selection), pass_along );
2821   gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
2822   gtk_widget_show ( item );
2823   
2824   item = a_acquire_trwlayer_menu ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)), vlp,
2825                                    vik_layers_panel_get_viewport(VIK_LAYERS_PANEL(vlp)), vtl );
2826   if ( item ) {
2827     gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
2828     gtk_widget_show ( item );
2829   }  
2830
2831   item = a_acquire_trwlayer_track_menu ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)), vlp,
2832                                          vik_layers_panel_get_viewport(VIK_LAYERS_PANEL(vlp)), vtl );
2833   if ( item ) {
2834     gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
2835     gtk_widget_show ( item );
2836   }  
2837 }
2838
2839 void vik_trw_layer_add_waypoint ( VikTrwLayer *vtl, gchar *name, VikWaypoint *wp )
2840 {
2841   if ( VIK_LAYER(vtl)->realized )
2842   {
2843     VikWaypoint *oldwp = VIK_WAYPOINT ( g_hash_table_lookup ( vtl->waypoints, name ) );
2844     if ( oldwp )
2845       wp->visible = oldwp->visible; /* same visibility so we don't have to update viktreeview */
2846     else
2847     {
2848       GtkTreeIter *iter = g_malloc(sizeof(GtkTreeIter));
2849       // Visibility column always needed for waypoints
2850 #ifdef VIK_CONFIG_ALPHABETIZED_TRW
2851       vik_treeview_add_sublayer_alphabetized ( VIK_LAYER(vtl)->vt, &(vtl->waypoints_iter), iter, name, vtl, name, VIK_TRW_LAYER_SUBLAYER_WAYPOINT, NULL, TRUE, TRUE );
2852 #else
2853       vik_treeview_add_sublayer ( VIK_LAYER(vtl)->vt, &(vtl->waypoints_iter), iter, name, vtl, name, VIK_TRW_LAYER_SUBLAYER_WAYPOINT, NULL, TRUE, TRUE );
2854 #endif
2855       // Actual setting of visibility dependent on the waypoint
2856       vik_treeview_item_set_visible ( VIK_LAYER(vtl)->vt, iter, wp->visible );
2857       g_hash_table_insert ( vtl->waypoints_iters, name, iter );
2858     }
2859   }
2860
2861   highest_wp_number_add_wp(vtl, name);
2862   g_hash_table_insert ( vtl->waypoints, name, wp );
2863  
2864 }
2865
2866 void vik_trw_layer_add_track ( VikTrwLayer *vtl, gchar *name, VikTrack *t )
2867 {
2868   if ( VIK_LAYER(vtl)->realized )
2869   {
2870     VikTrack *oldt = VIK_TRACK ( g_hash_table_lookup ( vtl->tracks, name ) );
2871     if ( oldt )
2872       t->visible = oldt->visible; /* same visibility so we don't have to update viktreeview */
2873     else
2874     {
2875       GtkTreeIter *iter = g_malloc(sizeof(GtkTreeIter));
2876       // Visibility column always needed for tracks
2877 #ifdef VIK_CONFIG_ALPHABETIZED_TRW
2878       vik_treeview_add_sublayer_alphabetized ( VIK_LAYER(vtl)->vt, &(vtl->tracks_iter), iter, name, vtl, name, VIK_TRW_LAYER_SUBLAYER_TRACK, NULL, TRUE, TRUE );
2879 #else
2880       vik_treeview_add_sublayer ( VIK_LAYER(vtl)->vt, &(vtl->tracks_iter), iter, name, vtl, name, VIK_TRW_LAYER_SUBLAYER_TRACK, NULL, TRUE, TRUE );
2881 #endif
2882       // Actual setting of visibility dependent on the track
2883       vik_treeview_item_set_visible ( VIK_LAYER(vtl)->vt, iter, t->visible );
2884       g_hash_table_insert ( vtl->tracks_iters, name, iter );
2885     }
2886   }
2887
2888   g_hash_table_insert ( vtl->tracks, name, t );
2889  
2890 }
2891
2892 /* to be called whenever a track has been deleted or may have been changed. */
2893 void trw_layer_cancel_tps_of_track ( VikTrwLayer *vtl, const gchar *trk_name )
2894 {
2895   if (vtl->current_tp_track_name && g_strcasecmp(trk_name, vtl->current_tp_track_name) == 0)
2896     trw_layer_cancel_current_tp ( vtl, FALSE );
2897   else if (vtl->last_tp_track_name && g_strcasecmp(trk_name, vtl->last_tp_track_name) == 0)
2898     trw_layer_cancel_last_tp ( vtl );
2899 }
2900         
2901 static gchar *get_new_unique_sublayer_name (VikTrwLayer *vtl, gint sublayer_type, const gchar *name)
2902 {
2903  gint i = 2;
2904  gchar *newname = g_strdup(name);
2905  while ((sublayer_type == VIK_TRW_LAYER_SUBLAYER_TRACK) ?
2906          (void *)vik_trw_layer_get_track(vtl, newname) : (void *)vik_trw_layer_get_waypoint(vtl, newname)) {
2907     gchar *new_newname = g_strdup_printf("%s#%d", name, i);
2908     g_free(newname);
2909     newname = new_newname;
2910     i++;
2911   }
2912   return newname;
2913 }
2914
2915 void vik_trw_layer_filein_add_waypoint ( VikTrwLayer *vtl, gchar *name, VikWaypoint *wp )
2916 {
2917   vik_trw_layer_add_waypoint ( vtl,
2918                         get_new_unique_sublayer_name(vtl, VIK_TRW_LAYER_SUBLAYER_WAYPOINT, name),
2919                         wp );
2920 }
2921 void vik_trw_layer_filein_add_track ( VikTrwLayer *vtl, gchar *name, VikTrack *tr )
2922 {
2923   if ( vtl->route_finder_append && vtl->route_finder_current_track ) {
2924     vik_track_remove_dup_points ( tr ); /* make "double point" track work to undo */
2925     vik_track_steal_and_append_trackpoints ( vtl->route_finder_current_track, tr );
2926     vik_track_free ( tr );
2927     vtl->route_finder_append = FALSE; /* this means we have added it */
2928   } else {
2929     gchar *new_name = get_new_unique_sublayer_name(vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, name);
2930     vik_trw_layer_add_track ( vtl, new_name, tr );
2931
2932     if ( vtl->route_finder_check_added_track ) {
2933       vik_track_remove_dup_points ( tr ); /* make "double point" track work to undo */
2934       if ( vtl->route_finder_added_track_name ) /* for google routes */
2935         g_free ( vtl->route_finder_added_track_name );
2936       vtl->route_finder_added_track_name = g_strdup(new_name);
2937     }
2938   }
2939 }
2940
2941 static void trw_layer_enum_item ( const gchar *name, GList **tr, GList **l )
2942 {
2943   *l = g_list_append(*l, (gpointer)name);
2944 }
2945
2946 static void trw_layer_move_item ( VikTrwLayer *vtl_src, VikTrwLayer *vtl_dest, gchar *name, gint type )
2947 {
2948   gchar *newname = get_new_unique_sublayer_name(vtl_dest, type, name);
2949   if (type == VIK_TRW_LAYER_SUBLAYER_TRACK) {
2950     VikTrack *t;
2951     t = vik_track_copy(vik_trw_layer_get_track(vtl_src, name));
2952     vik_trw_layer_delete_track(vtl_src, name);
2953     vik_trw_layer_add_track(vtl_dest, newname, t);
2954   }
2955   if (type==VIK_TRW_LAYER_SUBLAYER_WAYPOINT) {
2956     VikWaypoint *w;
2957     w = vik_waypoint_copy(vik_trw_layer_get_waypoint(vtl_src, name));
2958     vik_trw_layer_delete_waypoint(vtl_src, name);
2959     vik_trw_layer_add_waypoint(vtl_dest, newname, w);
2960   }
2961 }
2962
2963 static void trw_layer_drag_drop_request ( VikTrwLayer *vtl_src, VikTrwLayer *vtl_dest, GtkTreeIter *src_item_iter, GtkTreePath *dest_path )
2964 {
2965   VikTreeview *vt = VIK_LAYER(vtl_src)->vt;
2966   gint type = vik_treeview_item_get_data(vt, src_item_iter);
2967
2968   if (!vik_treeview_item_get_pointer(vt, src_item_iter)) {
2969     GList *items = NULL;
2970     GList *iter;
2971
2972     if (type==VIK_TRW_LAYER_SUBLAYER_TRACKS) {
2973       g_hash_table_foreach ( vtl_src->tracks, (GHFunc)trw_layer_enum_item, &items);
2974     } 
2975     if (type==VIK_TRW_LAYER_SUBLAYER_WAYPOINTS) {
2976       g_hash_table_foreach ( vtl_src->waypoints, (GHFunc)trw_layer_enum_item, &items);
2977     }    
2978       
2979     iter = items;
2980     while (iter) {
2981       if (type==VIK_TRW_LAYER_SUBLAYER_TRACKS) {
2982         trw_layer_move_item ( vtl_src, vtl_dest, iter->data, VIK_TRW_LAYER_SUBLAYER_TRACK);
2983       } else {
2984         trw_layer_move_item ( vtl_src, vtl_dest, iter->data, VIK_TRW_LAYER_SUBLAYER_WAYPOINT);
2985       }
2986       iter = iter->next;
2987     }
2988     if (items) 
2989       g_list_free(items);
2990   } else {
2991     gchar *name = vik_treeview_item_get_pointer(vt, src_item_iter);
2992     trw_layer_move_item(vtl_src, vtl_dest, name, type);
2993   }
2994 }
2995
2996 gboolean vik_trw_layer_delete_track ( VikTrwLayer *vtl, const gchar *trk_name )
2997 {
2998   VikTrack *t = g_hash_table_lookup ( vtl->tracks, trk_name );
2999   gboolean was_visible = FALSE;
3000   if ( t )
3001   {
3002     GtkTreeIter *it;
3003     was_visible = t->visible;
3004     if ( t == vtl->current_track ) {
3005       vtl->current_track = NULL;
3006     }
3007     if ( t == vtl->route_finder_current_track )
3008       vtl->route_finder_current_track = NULL;
3009
3010     /* could be current_tp, so we have to check */
3011     trw_layer_cancel_tps_of_track ( vtl, trk_name );
3012
3013     g_assert ( ( it = g_hash_table_lookup ( vtl->tracks_iters, trk_name ) ) );
3014     vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, it );
3015     g_hash_table_remove ( vtl->tracks_iters, trk_name );
3016
3017     /* do this last because trk_name may be pointing to actual orig key */
3018     g_hash_table_remove ( vtl->tracks, trk_name );
3019   }
3020   return was_visible;
3021 }
3022
3023 gboolean vik_trw_layer_delete_waypoint ( VikTrwLayer *vtl, const gchar *wp_name )
3024 {
3025   gboolean was_visible = FALSE;
3026   VikWaypoint *wp;
3027
3028   wp = g_hash_table_lookup ( vtl->waypoints, wp_name );
3029   if ( wp ) {
3030     GtkTreeIter *it;
3031
3032     if ( wp == vtl->current_wp ) {
3033       vtl->current_wp = NULL;
3034       vtl->current_wp_name = NULL;
3035       vtl->moving_wp = FALSE;
3036     }
3037
3038     was_visible = wp->visible;
3039     g_assert ( ( it = g_hash_table_lookup ( vtl->waypoints_iters, (gchar *) wp_name ) ) );
3040     vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, it );
3041     g_hash_table_remove ( vtl->waypoints_iters, (gchar *) wp_name );
3042
3043     highest_wp_number_remove_wp(vtl, wp_name);
3044     g_hash_table_remove ( vtl->waypoints, wp_name ); /* last because this frees name */
3045   }
3046
3047   return was_visible;
3048 }
3049
3050 static void remove_item_from_treeview(const gchar *name, GtkTreeIter *it, VikTreeview * vt)
3051 {
3052     vik_treeview_item_delete (vt, it );
3053 }
3054
3055 void vik_trw_layer_delete_all_tracks ( VikTrwLayer *vtl )
3056 {
3057
3058   vtl->current_track = NULL;
3059   vtl->route_finder_current_track = NULL;
3060   if (vtl->current_tp_track_name)
3061     trw_layer_cancel_current_tp(vtl, FALSE);
3062   if (vtl->last_tp_track_name)
3063     trw_layer_cancel_last_tp ( vtl );
3064
3065   g_hash_table_foreach(vtl->tracks_iters, (GHFunc) remove_item_from_treeview, VIK_LAYER(vtl)->vt);
3066   g_hash_table_remove_all(vtl->tracks_iters);
3067   g_hash_table_remove_all(vtl->tracks);
3068
3069   vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
3070 }
3071
3072 void vik_trw_layer_delete_all_waypoints ( VikTrwLayer *vtl )
3073 {
3074   vtl->current_wp = NULL;
3075   vtl->current_wp_name = NULL;
3076   vtl->moving_wp = FALSE;
3077
3078   highest_wp_number_reset(vtl);
3079
3080   g_hash_table_foreach(vtl->waypoints_iters, (GHFunc) remove_item_from_treeview, VIK_LAYER(vtl)->vt);
3081   g_hash_table_remove_all(vtl->waypoints_iters);
3082   g_hash_table_remove_all(vtl->waypoints);
3083
3084   vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
3085 }
3086
3087 static void trw_layer_delete_all_tracks ( gpointer lav[2] )
3088 {
3089   VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
3090   // Get confirmation from the user
3091   if ( a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
3092                             _("Are you sure you want to delete all tracks in %s?"),
3093                             vik_layer_get_name ( VIK_LAYER(vtl) ) ) )
3094     vik_trw_layer_delete_all_tracks (vtl);
3095 }
3096
3097 static void trw_layer_delete_all_waypoints ( gpointer lav[2] )
3098 {
3099   VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
3100   // Get confirmation from the user
3101   if ( a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
3102                             _("Are you sure you want to delete all waypoints in %s?"),
3103                             vik_layer_get_name ( VIK_LAYER(vtl) ) ) )
3104     vik_trw_layer_delete_all_waypoints (vtl);
3105 }
3106
3107 static void trw_layer_delete_item ( gpointer pass_along[6] )
3108 {
3109   VikTrwLayer *vtl = VIK_TRW_LAYER(pass_along[0]);
3110   gboolean was_visible = FALSE;
3111   if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_WAYPOINT )
3112   {
3113     if ( GPOINTER_TO_INT ( pass_along[4]) )
3114       // Get confirmation from the user
3115       // Maybe this Waypoint Delete should be optional as is it could get annoying...
3116       if ( ! a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
3117                                   _("Are you sure you want to delete the waypoint \"%s\""),
3118                                   pass_along[3] ) )
3119         return;
3120     was_visible = vik_trw_layer_delete_waypoint ( vtl, (gchar *) pass_along[3] );
3121   }
3122   else
3123   {
3124     if ( GPOINTER_TO_INT ( pass_along[4]) )
3125       // Get confirmation from the user
3126       if ( ! a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
3127                                   _("Are you sure you want to delete the track \"%s\""),
3128                                   pass_along[3] ) )
3129         return;
3130     was_visible = vik_trw_layer_delete_track ( vtl, (gchar *) pass_along[3] );
3131   }
3132   if ( was_visible )
3133     vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
3134 }
3135
3136
3137 static void trw_layer_properties_item ( gpointer pass_along[6] )
3138 {
3139   VikTrwLayer *vtl = VIK_TRW_LAYER(pass_along[0]);
3140   if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_WAYPOINT )
3141   {
3142     VikWaypoint *wp = g_hash_table_lookup ( vtl->waypoints, pass_along[3] );
3143     if ( wp )
3144     {
3145       gboolean updated = FALSE;
3146       a_dialog_waypoint ( VIK_GTK_WINDOW_FROM_LAYER(vtl), pass_along[3], wp, NULL, vtl->coord_mode, FALSE, &updated );
3147
3148       if ( updated && VIK_LAYER(vtl)->visible )
3149         vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
3150     }
3151   }
3152   else
3153   {
3154     VikTrack *tr = g_hash_table_lookup ( vtl->tracks, pass_along[3] );
3155     if ( tr )
3156     {
3157       vik_trw_layer_propwin_run ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
3158                                   vtl, tr,
3159                                   pass_along[1], /* vlp */
3160                                   pass_along[3],  /* track name */
3161                                   pass_along[5] );  /* vvp */
3162     }
3163   }
3164 }
3165
3166 /*
3167    Parameter 1 -> VikLayersPanel
3168    Parameter 2 -> VikLayer
3169    Parameter 3 -> VikViewport
3170 */
3171 static void goto_coord ( gpointer *vlp, gpointer vl, gpointer vvp, const VikCoord *coord )
3172 {
3173   if ( vlp ) {
3174     vik_viewport_set_center_coord ( vik_layers_panel_get_viewport (VIK_LAYERS_PANEL(vlp)), coord );
3175     vik_layers_panel_emit_update ( VIK_LAYERS_PANEL(vlp) );
3176   }
3177   else {
3178     /* since vlp not set, vl & vvp should be valid instead! */
3179     if ( vl && vvp ) {
3180       vik_viewport_set_center_coord ( VIK_VIEWPORT(vvp), coord );
3181       vik_layer_emit_update ( VIK_LAYER(vl), FALSE );
3182     }
3183   }
3184 }
3185
3186 static void trw_layer_goto_track_startpoint ( gpointer pass_along[6] )
3187 {
3188   GList *trps = ((VikTrack *) g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, pass_along[3] ))->trackpoints;
3189   if ( trps && trps->data )
3190     goto_coord ( pass_along[1], pass_along[0], pass_along[5], &(((VikTrackpoint *) trps->data)->coord));
3191 }
3192
3193 static void trw_layer_goto_track_center ( gpointer pass_along[6] )
3194 {
3195   /* FIXME: get this into viktrack.c, and should be ->trackpoints right? */
3196   GList **trps = g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, pass_along[3] );
3197   if ( trps && *trps )
3198   {
3199     struct LatLon average, maxmin[2] = { {0,0}, {0,0} };
3200     VikCoord coord;
3201     trw_layer_find_maxmin_tracks ( NULL, trps, maxmin );
3202     average.lat = (maxmin[0].lat+maxmin[1].lat)/2;
3203     average.lon = (maxmin[0].lon+maxmin[1].lon)/2;
3204     vik_coord_load_from_latlon ( &coord, VIK_TRW_LAYER(pass_along[0])->coord_mode, &average );
3205     goto_coord ( pass_along[1], pass_along[0], pass_along[5], &coord);
3206   }
3207 }
3208
3209 static void trw_layer_extend_track_end ( gpointer pass_along[6] )
3210 {
3211   VikTrwLayer *vtl = VIK_TRW_LAYER(pass_along[0]);
3212   VikTrack *track = g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, pass_along[3] );
3213
3214   vtl->current_track = track;
3215   vik_window_enable_layer_tool ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)), VIK_LAYER_TRW, TOOL_CREATE_TRACK);
3216
3217   if ( track->trackpoints )
3218     goto_coord ( pass_along[1], pass_along[0], pass_along[5], &(((VikTrackpoint *)g_list_last(track->trackpoints)->data)->coord) );
3219 }
3220
3221 /**
3222  * extend a track using route finder
3223  */
3224 static void trw_layer_extend_track_end_route_finder ( gpointer pass_along[6] )
3225 {
3226   VikTrwLayer *vtl = VIK_TRW_LAYER(pass_along[0]);
3227   VikTrack *track = g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, pass_along[3] );
3228   VikCoord last_coord = (((VikTrackpoint *)g_list_last(track->trackpoints)->data)->coord);
3229
3230   vik_window_enable_layer_tool ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)), VIK_LAYER_TRW, NUM_TOOLS );
3231   vtl->route_finder_coord =  last_coord;
3232   vtl->route_finder_current_track = track;
3233   vtl->route_finder_started = TRUE;
3234
3235   if ( track->trackpoints )
3236     goto_coord ( pass_along[1], pass_along[0], pass_along[5], &last_coord) ;
3237
3238 }
3239
3240 static void trw_layer_apply_dem_data ( gpointer pass_along[6] )
3241 {
3242   /* TODO: check & warn if no DEM data, or no applicable DEM data. */
3243   /* Also warn if overwrite old elevation data */
3244   VikTrack *track = (VikTrack *) g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, pass_along[3] );
3245
3246   vik_track_apply_dem_data ( track );
3247 }
3248
3249 static void trw_layer_goto_track_endpoint ( gpointer pass_along[6] )
3250 {
3251   GList *trps = ((VikTrack *) g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, pass_along[3] ))->trackpoints;
3252   if ( !trps )
3253     return;
3254   trps = g_list_last(trps);
3255   goto_coord ( pass_along[1], pass_along[0], pass_along[5], &(((VikTrackpoint *) trps->data)->coord));
3256 }
3257
3258 static void trw_layer_goto_track_max_speed ( gpointer pass_along[6] )
3259 {
3260   VikTrackpoint* vtp = vik_track_get_tp_by_max_speed ( g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, pass_along[3] ) );
3261   if ( !vtp )
3262     return;
3263   goto_coord ( pass_along[1], pass_along[0], pass_along[5], &(vtp->coord));
3264 }
3265
3266 static void trw_layer_goto_track_max_alt ( gpointer pass_along[6] )
3267 {
3268   VikTrackpoint* vtp = vik_track_get_tp_by_max_alt ( g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, pass_along[3] ) );
3269   if ( !vtp )
3270     return;
3271   goto_coord ( pass_along[1], pass_along[0], pass_along[5], &(vtp->coord));
3272 }
3273
3274 static void trw_layer_goto_track_min_alt ( gpointer pass_along[6] )
3275 {
3276   VikTrackpoint* vtp = vik_track_get_tp_by_min_alt ( g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, pass_along[3] ) );
3277   if ( !vtp )
3278     return;
3279   goto_coord ( pass_along[1], pass_along[0], pass_along[5], &(vtp->coord));
3280 }
3281
3282 /*
3283  * Automatically change the viewport to center on the track and zoom to see the extent of the track
3284  */
3285 static void trw_layer_auto_track_view ( gpointer pass_along[6] )
3286 {
3287   GList **trps = g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, pass_along[3] );
3288   if ( trps && *trps )
3289   {
3290     struct LatLon maxmin[2] = { {0,0}, {0,0} };
3291     trw_layer_find_maxmin_tracks ( NULL, trps, maxmin );
3292     trw_layer_zoom_to_show_latlons ( VIK_TRW_LAYER(pass_along[0]), pass_along[5], maxmin );
3293     if ( pass_along[1] )
3294       vik_layers_panel_emit_update ( VIK_LAYERS_PANEL(pass_along[1]) );
3295     else
3296       vik_layer_emit_update ( VIK_LAYER(pass_along[0]), FALSE );
3297   }
3298 }
3299
3300 static void trw_layer_edit_trackpoint ( gpointer pass_along[6] )
3301 {
3302   VikTrwLayer *vtl = VIK_TRW_LAYER(pass_along[0]);
3303   trw_layer_tpwin_init ( vtl );
3304 }
3305
3306 /*************************************
3307  * merge/split by time routines 
3308  *************************************/
3309
3310 /* called for each key in track hash table.
3311  * If the current track has the same time stamp type, add it to the result,
3312  * except the one pointed by "exclude".
3313  * set exclude to NULL if there is no exclude to check.
3314  * Note that the result is in reverse (for performance reasons).
3315  */
3316 typedef struct {
3317   GList **result;
3318   GList  *exclude;
3319   gboolean with_timestamps;
3320 } twt_udata;
3321 static void find_tracks_with_timestamp_type(gpointer key, gpointer value, gpointer udata)
3322 {
3323   twt_udata *user_data = udata;
3324   VikTrackpoint *p1, *p2;
3325
3326   if (VIK_TRACK(value)->trackpoints == user_data->exclude) {
3327     return;
3328   }
3329
3330   if (VIK_TRACK(value)->trackpoints) {
3331     p1 = VIK_TRACKPOINT(VIK_TRACK(value)->trackpoints->data);
3332     p2 = VIK_TRACKPOINT(g_list_last(VIK_TRACK(value)->trackpoints)->data);
3333
3334     if ( user_data->with_timestamps ) {
3335       if (!p1->has_timestamp || !p2->has_timestamp) {
3336         return;
3337       }
3338     }
3339     else {
3340       // Don't add tracks with timestamps when getting non timestamp tracks
3341       if (p1->has_timestamp || p2->has_timestamp) {
3342         return;
3343       }
3344     }
3345   }
3346
3347   *(user_data->result) = g_list_prepend(*(user_data->result), key);
3348 }
3349
3350 /* called for each key in track hash table. if original track user_data[1] is close enough
3351  * to the passed one, add it to list in user_data[0] 
3352  */
3353 static void find_nearby_track(gpointer key, gpointer value, gpointer user_data)
3354 {
3355   time_t t1, t2;
3356   VikTrackpoint *p1, *p2;
3357
3358   GList **nearby_tracks = ((gpointer *)user_data)[0];
3359   GList *orig_track = ((gpointer *)user_data)[1];
3360   guint thr = GPOINTER_TO_UINT (((gpointer *)user_data)[2]);
3361
3362   /* outline: 
3363    * detect reasons for not merging, and return
3364    * if no reason is found not to merge, then do it.
3365    */
3366
3367   if (VIK_TRACK(value)->trackpoints == orig_track) {
3368     return;
3369   }
3370
3371   t1 = VIK_TRACKPOINT(orig_track->data)->timestamp;
3372   t2 = VIK_TRACKPOINT(g_list_last(orig_track)->data)->timestamp;
3373
3374   if (VIK_TRACK(value)->trackpoints) {
3375     p1 = VIK_TRACKPOINT(VIK_TRACK(value)->trackpoints->data);
3376     p2 = VIK_TRACKPOINT(g_list_last(VIK_TRACK(value)->trackpoints)->data);
3377
3378     if (!p1->has_timestamp || !p2->has_timestamp) {
3379       g_print("no timestamp\n");
3380       return;
3381     }
3382
3383     /*  g_print("Got track named %s, times %d, %d\n", (gchar *)key, p1->timestamp, p2->timestamp); */
3384     if (! (abs(t1 - p2->timestamp) < thr*60 ||
3385         /*  p1 p2      t1 t2 */
3386            abs(p1->timestamp - t2) < thr*60)
3387         /*  t1 t2      p1 p2 */
3388         ) {
3389       return;
3390     }
3391   }
3392
3393   *nearby_tracks = g_list_prepend(*nearby_tracks, key);
3394 }
3395
3396 /* comparison function used to sort tracks; a and b are hash table keys */
3397 /* Not actively used - can be restored if needed
3398 static gint track_compare(gconstpointer a, gconstpointer b, gpointer user_data)
3399 {
3400   GHashTable *tracks = user_data;
3401   time_t t1, t2;
3402
3403   t1 = VIK_TRACKPOINT(VIK_TRACK(g_hash_table_lookup(tracks, a))->trackpoints->data)->timestamp;
3404   t2 = VIK_TRACKPOINT(VIK_TRACK(g_hash_table_lookup(tracks, b))->trackpoints->data)->timestamp;
3405   
3406   if (t1 < t2) return -1;
3407   if (t1 > t2) return 1;
3408   return 0;
3409 }
3410 */
3411
3412 /* comparison function used to sort trackpoints */
3413 static gint trackpoint_compare(gconstpointer a, gconstpointer b)
3414 {
3415   time_t t1 = VIK_TRACKPOINT(a)->timestamp, t2 = VIK_TRACKPOINT(b)->timestamp;
3416   
3417   if (t1 < t2) return -1;
3418   if (t1 > t2) return 1;
3419   return 0;
3420 }
3421
3422 /**
3423  * comparison function which can be used to sort tracks or waypoints by name
3424  */
3425 static gint sort_alphabetically (gconstpointer a, gconstpointer b, gpointer user_data)
3426 {
3427   const gchar* namea = (const gchar*) a;
3428   const gchar* nameb = (const gchar*) b;
3429   if ( namea == NULL || nameb == NULL)
3430     return 0;
3431   else
3432     // Same sort method as used in the vik_treeview_*_alphabetize functions
3433     return strcmp ( namea, nameb );
3434 }
3435
3436 /**
3437  * Attempt to merge selected track with other tracks specified by the user
3438  * Tracks to merge with must be of the same 'type' as the selected track -
3439  *  either all with timestamps, or all without timestamps
3440  */
3441 static void trw_layer_merge_with_other ( gpointer pass_along[6] )
3442 {
3443   VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
3444   gchar *orig_track_name = pass_along[3];
3445   GList *other_tracks = NULL;
3446   VikTrack *track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, orig_track_name );
3447
3448   if ( !track->trackpoints )
3449     return;
3450
3451   twt_udata udata;
3452   udata.result = &other_tracks;
3453   udata.exclude = track->trackpoints;
3454   // Allow merging with 'similar' time type time tracks
3455   // i.e. either those times, or those without
3456   udata.with_timestamps = (VIK_TRACKPOINT(track->trackpoints->data)->has_timestamp);
3457
3458   g_hash_table_foreach(vtl->tracks, find_tracks_with_timestamp_type, (gpointer)&udata);
3459   other_tracks = g_list_reverse(other_tracks);
3460
3461   if ( !other_tracks ) {
3462     if ( udata.with_timestamps )
3463       a_dialog_error_msg(VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Failed. No other tracks with timestamps in this layer found"));
3464     else
3465       a_dialog_error_msg(VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Failed. No other tracks without timestamps in this layer found"));
3466     return;
3467   }
3468
3469 #ifdef VIK_CONFIG_ALPHABETIZED_TRW
3470   // Sort alphabetically for user presentation
3471   other_tracks = g_list_sort_with_data (other_tracks, sort_alphabetically, NULL);
3472 #endif
3473
3474   GList *merge_list = a_dialog_select_from_list(VIK_GTK_WINDOW_FROM_LAYER(vtl),
3475       other_tracks, TRUE,
3476       _("Merge with..."), _("Select track to merge with"));
3477   g_list_free(other_tracks);
3478
3479   if (merge_list)
3480   {
3481     GList *l;
3482     for (l = merge_list; l != NULL; l = g_list_next(l)) {
3483       VikTrack *merge_track = (VikTrack *) g_hash_table_lookup (vtl->tracks, l->data );
3484       if (merge_track) {
3485         track->trackpoints = g_list_concat(track->trackpoints, merge_track->trackpoints);
3486         merge_track->trackpoints = NULL;
3487         vik_trw_layer_delete_track(vtl, l->data);
3488         track->trackpoints = g_list_sort(track->trackpoints, trackpoint_compare);
3489       }
3490     }
3491     /* TODO: free data before free merge_list */
3492     for (l = merge_list; l != NULL; l = g_list_next(l))
3493       g_free(l->data);
3494     g_list_free(merge_list);
3495     vik_layer_emit_update( VIK_LAYER(vtl), FALSE );
3496   }
3497 }
3498
3499 /* merge by time routine */
3500 static void trw_layer_merge_by_timestamp ( gpointer pass_along[6] )
3501 {
3502   VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
3503   gchar *orig_track_name = strdup(pass_along[3]);
3504
3505   //time_t t1, t2;
3506   GList *nearby_tracks;
3507   VikTrack *track;
3508   GList *trps;
3509   static  guint thr = 1;
3510   guint track_count = 0;
3511
3512   GList *tracks_with_timestamp = NULL;
3513   track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, orig_track_name );
3514   if (track->trackpoints &&
3515       !VIK_TRACKPOINT(track->trackpoints->data)->has_timestamp) {
3516     a_dialog_error_msg(VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Failed. This track does not have timestamp"));
3517     free(orig_track_name);
3518     return;
3519   }
3520
3521   twt_udata udata;
3522   udata.result = &tracks_with_timestamp;
3523   udata.exclude = track->trackpoints;
3524   udata.with_timestamps = TRUE;
3525   g_hash_table_foreach(vtl->tracks, find_tracks_with_timestamp_type, (gpointer)&udata);
3526   tracks_with_timestamp = g_list_reverse(tracks_with_timestamp);
3527
3528   if (!tracks_with_timestamp) {
3529     a_dialog_error_msg(VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Failed. No other track in this layer has timestamp"));
3530     free(orig_track_name);
3531     return;
3532   }
3533   g_list_free(tracks_with_timestamp);
3534
3535   if (!a_dialog_time_threshold(VIK_GTK_WINDOW_FROM_LAYER(vtl), 
3536                                _("Merge Threshold..."), 
3537                                _("Merge when time between tracks less than:"), 
3538                                &thr)) {
3539     free(orig_track_name);
3540     return;
3541   }
3542
3543   /* merge tracks until we can't */
3544   nearby_tracks = NULL;
3545   do {
3546     gpointer params[3];
3547
3548     track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, orig_track_name );
3549     trps = track->trackpoints;
3550     if ( !trps )
3551       return;
3552
3553
3554     if (nearby_tracks) {
3555       g_list_free(nearby_tracks);
3556       nearby_tracks = NULL;
3557     }
3558
3559     //t1 = ((VikTrackpoint *)trps->data)->timestamp;
3560     //t2 = ((VikTrackpoint *)g_list_last(trps)->data)->timestamp;
3561     
3562     /*    g_print("Original track times: %d and %d\n", t1, t2);  */
3563     params[0] = &nearby_tracks;
3564     params[1] = trps;
3565     params[2] = GUINT_TO_POINTER (thr);
3566
3567     /* get a list of adjacent-in-time tracks */
3568     g_hash_table_foreach(vtl->tracks, find_nearby_track, (gpointer)params);
3569
3570     /* add original track */
3571     nearby_tracks = g_list_prepend(nearby_tracks, orig_track_name);
3572
3573     /* merge them */
3574     { 
3575 #define get_track(x) VIK_TRACK(g_hash_table_lookup(vtl->tracks, (gchar *)((x)->data)))
3576 #define get_first_trackpoint(x) VIK_TRACKPOINT(get_track(x)->trackpoints->data)
3577 #define get_last_trackpoint(x) VIK_TRACKPOINT(g_list_last(get_track(x)->trackpoints)->data)
3578       GList *l = nearby_tracks;
3579       VikTrack *tr = vik_track_new();
3580       tr->visible = track->visible;
3581       track_count = 0;
3582       while (l) {
3583         /*
3584         time_t t1, t2;
3585         t1 = get_first_trackpoint(l)->timestamp;
3586         t2 = get_last_trackpoint(l)->timestamp;
3587         g_print("     %20s: track %d - %d\n", (char *)l->data, (int)t1, (int)t2);
3588         */
3589
3590
3591         /* remove trackpoints from merged track, delete track */
3592         tr->trackpoints = g_list_concat(tr->trackpoints, get_track(l)->trackpoints);
3593         get_track(l)->trackpoints = NULL;
3594         vik_trw_layer_delete_track(vtl, l->data);
3595
3596         track_count ++;
3597         l = g_list_next(l);
3598       }
3599       tr->trackpoints = g_list_sort(tr->trackpoints, trackpoint_compare);
3600       vik_trw_layer_add_track(vtl, strdup(orig_track_name), tr);
3601
3602 #undef get_first_trackpoint
3603 #undef get_last_trackpoint
3604 #undef get_track
3605     }
3606   } while (track_count > 1);
3607   g_list_free(nearby_tracks);
3608   free(orig_track_name);
3609   vik_layer_emit_update( VIK_LAYER(vtl), FALSE );
3610 }
3611
3612 /* split by time routine */
3613 static void trw_layer_split_by_timestamp ( gpointer pass_along[6] )
3614 {
3615   VikTrack *track = (VikTrack *) g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, pass_along[3] );
3616   GList *trps = track->trackpoints;
3617   GList *iter;
3618   GList *newlists = NULL;
3619   GList *newtps = NULL;
3620   guint i;
3621   static guint thr = 1;
3622
3623   time_t ts, prev_ts;
3624
3625   if ( !trps )
3626     return;
3627
3628   if (!a_dialog_time_threshold(VIK_GTK_WINDOW_FROM_LAYER(pass_along[0]), 
3629                                _("Split Threshold..."), 
3630                                _("Split when time between trackpoints exceeds:"), 
3631                                &thr)) {
3632     return;
3633   }
3634
3635   /* iterate through trackpoints, and copy them into new lists without touching original list */
3636   prev_ts = VIK_TRACKPOINT(trps->data)->timestamp;
3637   iter = trps;
3638
3639   while (iter) {
3640     ts = VIK_TRACKPOINT(iter->data)->timestamp;
3641     if (ts < prev_ts) {
3642       g_print("panic: ts < prev_ts: this should never happen!\n");
3643       return;
3644     }
3645     if (ts - prev_ts > thr*60) {
3646       /* flush accumulated trackpoints into new list */
3647       newlists = g_list_append(newlists, g_list_reverse(newtps));
3648       newtps = NULL;
3649     }
3650
3651     /* accumulate trackpoint copies in newtps, in reverse order */
3652     newtps = g_list_prepend(newtps, vik_trackpoint_copy(VIK_TRACKPOINT(iter->data)));
3653     prev_ts = ts;
3654     iter = g_list_next(iter);
3655   }
3656   if (newtps) {
3657       newlists = g_list_append(newlists, g_list_reverse(newtps));
3658   }
3659
3660   /* put lists of trackpoints into tracks */
3661   iter = newlists;
3662   i = 1;
3663   // Only bother updating if the split results in new tracks
3664   if (g_list_length (newlists) > 1) {
3665     while (iter) {
3666       gchar *new_tr_name;
3667       VikTrack *tr;
3668
3669       tr = vik_track_new();
3670       tr->visible = track->visible;
3671       tr->trackpoints = (GList *)(iter->data);
3672
3673       new_tr_name = g_strdup_printf("%s #%d", (gchar *) pass_along[3], i++);
3674       vik_trw_layer_add_track(VIK_TRW_LAYER(pass_along[0]), new_tr_name, tr);
3675       /*    g_print("adding track %s, times %d - %d\n", new_tr_name, VIK_TRACKPOINT(tr->trackpoints->data)->timestamp,
3676           VIK_TRACKPOINT(g_list_last(tr->trackpoints)->data)->timestamp);*/
3677
3678       iter = g_list_next(iter);
3679     }
3680     vik_trw_layer_delete_track(VIK_TRW_LAYER(pass_along[0]), (gchar *)pass_along[3]);
3681     vik_layer_emit_update(VIK_LAYER(pass_along[0]), FALSE);
3682   }
3683   g_list_free(newlists);
3684 }
3685
3686 /**
3687  * Split a track by the number of points as specified by the user
3688  */
3689 static void trw_layer_split_by_n_points ( gpointer pass_along[6] )
3690 {
3691   VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
3692   VikTrack *track = (VikTrack *)g_hash_table_lookup ( vtl->tracks, pass_along[3] );
3693
3694   // Check valid track
3695   GList *trps = track->trackpoints;
3696   if ( !trps )
3697     return;
3698
3699   gint points = a_dialog_get_positive_number(VIK_GTK_WINDOW_FROM_LAYER(pass_along[0]),
3700                                              _("Split Every Nth Point"),
3701                                              _("Split on every Nth point:"),
3702                                              250,   // Default value as per typical limited track capacity of various GPS devices
3703                                              2,     // Min
3704                                              65536, // Max
3705                                              5);    // Step
3706   // Was a valid number returned?
3707   if (!points)
3708     return;
3709
3710   // Now split...
3711   GList *iter;
3712   GList *newlists = NULL;
3713   GList *newtps = NULL;
3714   gint count = 0;
3715   iter = trps;
3716
3717   while (iter) {
3718     /* accumulate trackpoint copies in newtps, in reverse order */
3719     newtps = g_list_prepend(newtps, vik_trackpoint_copy(VIK_TRACKPOINT(iter->data)));
3720     count++;
3721     if (count >= points) {
3722       /* flush accumulated trackpoints into new list */
3723       newlists = g_list_append(newlists, g_list_reverse(newtps));
3724       newtps = NULL;
3725       count = 0;
3726     }
3727     iter = g_list_next(iter);
3728   }
3729
3730   // If there is a remaining chunk put that into the new split list
3731   // This may well be the whole track if no split points were encountered
3732   if (newtps) {
3733       newlists = g_list_append(newlists, g_list_reverse(newtps));
3734   }
3735
3736   /* put lists of trackpoints into tracks */
3737   iter = newlists;
3738   guint i = 1;
3739   // Only bother updating if the split results in new tracks
3740   if (g_list_length (newlists) > 1) {
3741     while (iter) {
3742       gchar *new_tr_name;
3743       VikTrack *tr;
3744
3745       tr = vik_track_new();
3746       tr->visible = track->visible;
3747       tr->trackpoints = (GList *)(iter->data);
3748
3749       new_tr_name = g_strdup_printf("%s #%d", (gchar *) pass_along[3], i++);
3750       vik_trw_layer_add_track(VIK_TRW_LAYER(pass_along[0]), new_tr_name, tr);
3751
3752       iter = g_list_next(iter);
3753     }
3754     // Remove original track and then update the display
3755     vik_trw_layer_delete_track(VIK_TRW_LAYER(pass_along[0]), (gchar *)pass_along[3]);
3756     vik_layer_emit_update(VIK_LAYER(pass_along[0]), FALSE);
3757   }
3758   g_list_free(newlists);
3759 }
3760
3761 /* end of split/merge routines */
3762
3763 /**
3764  * Reverse a track
3765  */
3766 static void trw_layer_reverse ( gpointer pass_along[6] )
3767 {
3768   VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
3769   VikTrack *track = (VikTrack *)g_hash_table_lookup ( vtl->tracks, pass_along[3] );
3770
3771   // Check valid track
3772   GList *trps = track->trackpoints;
3773   if ( !trps )
3774     return;
3775
3776   vik_track_reverse ( track );
3777  
3778   vik_layer_emit_update ( VIK_LAYER(pass_along[0]), FALSE );
3779 }
3780
3781 /**
3782  * Similar to trw_layer_enum_item, but this uses a sorted method
3783  */
3784 static void trw_layer_sorted_name_list(gpointer key, gpointer value, gpointer udata)
3785 {
3786   GList **list = (GList**)udata;
3787   //*list = g_list_prepend(*all, key); //unsorted method
3788   // Sort named list alphabetically
3789   *list = g_list_insert_sorted_with_data (*list, key, sort_alphabetically, NULL);
3790 }
3791
3792 /**
3793  *
3794  */
3795 static void trw_layer_delete_tracks_from_selection ( gpointer lav[2] )
3796 {
3797   VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
3798   GList *all = NULL;
3799   // Sort list alphabetically for better presentation
3800   g_hash_table_foreach(vtl->tracks, trw_layer_sorted_name_list, &all);
3801
3802   if ( ! all ) {
3803     a_dialog_error_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), _("No tracks found"));
3804     return;
3805   }
3806
3807   // Get list of items to delete from the user
3808   GList *delete_list = a_dialog_select_from_list(VIK_GTK_WINDOW_FROM_LAYER(vtl),
3809                                                  all,
3810                                                  TRUE,
3811                                                  _("Delete Selection"),
3812                                                  _("Select tracks to delete"));
3813   g_list_free(all);
3814
3815   // Delete requested tracks
3816   // since specificly requested, IMHO no need for extra confirmation
3817   if ( delete_list ) {
3818     GList *l;
3819     for (l = delete_list; l != NULL; l = g_list_next(l)) {
3820       vik_trw_layer_delete_track(vtl, l->data);
3821     }
3822     g_list_free(delete_list);
3823     vik_layer_emit_update( VIK_LAYER(vtl), FALSE );
3824   }
3825 }
3826
3827 /**
3828  *
3829  */
3830 static void trw_layer_delete_waypoints_from_selection ( gpointer lav[2] )
3831 {
3832   VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
3833   GList *all = NULL;
3834
3835   // Sort list alphabetically for better presentation
3836   g_hash_table_foreach ( vtl->waypoints, trw_layer_sorted_name_list, &all);
3837   if ( ! all ) {
3838     a_dialog_error_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), _("No waypoints found"));
3839     return;
3840   }
3841
3842   all = g_list_sort_with_data(all, sort_alphabetically, NULL);
3843
3844   // Get list of items to delete from the user
3845   GList *delete_list = a_dialog_select_from_list(VIK_GTK_WINDOW_FROM_LAYER(vtl),
3846                                                  all,
3847                                                  TRUE,
3848                                                  _("Delete Selection"),
3849                                                  _("Select waypoints to delete"));
3850   g_list_free(all);
3851
3852   // Delete requested waypoints
3853   // since specificly requested, IMHO no need for extra confirmation
3854   if ( delete_list ) {
3855     GList *l;
3856     for (l = delete_list; l != NULL; l = g_list_next(l)) {
3857       vik_trw_layer_delete_waypoint(vtl, l->data);
3858     }
3859     g_list_free(delete_list);
3860     vik_layer_emit_update( VIK_LAYER(vtl), FALSE );
3861   }
3862
3863 }
3864
3865 static void trw_layer_goto_waypoint ( gpointer pass_along[6] )
3866 {
3867   VikWaypoint *wp = g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->waypoints, pass_along[3] );
3868   if ( wp )
3869     goto_coord ( pass_along[1], pass_along[0], pass_along[5], &(wp->coord) );
3870 }
3871
3872 static void trw_layer_waypoint_gc_webpage ( gpointer pass_along[6] )
3873 {
3874   gchar *webpage = g_strdup_printf("http://www.geocaching.com/seek/cache_details.aspx?wp=%s", (gchar *) pass_along[3] );
3875   open_url(VIK_GTK_WINDOW_FROM_LAYER(VIK_LAYER(pass_along[0])), webpage);
3876   g_free ( webpage );
3877 }
3878
3879 static const gchar* trw_layer_sublayer_rename_request ( VikTrwLayer *l, const gchar *newname, gpointer vlp, gint subtype, gpointer sublayer, GtkTreeIter *iter )
3880 {
3881   if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT )
3882   {
3883     gchar *rv;
3884     VikWaypoint *wp;
3885
3886     if (strcmp(newname, sublayer) == 0 )
3887       return NULL;
3888
3889     if (strcasecmp(newname, sublayer)) { /* Not just changing case */
3890       if (g_hash_table_lookup( l->waypoints, newname))
3891       {
3892         a_dialog_error_msg ( VIK_GTK_WINDOW_FROM_LAYER(l), _("Waypoint Already Exists") );
3893         return NULL;
3894       }
3895     }
3896
3897     iter = g_hash_table_lookup ( l->waypoints_iters, sublayer );
3898     g_hash_table_steal ( l->waypoints_iters, sublayer );
3899
3900     wp = vik_waypoint_copy ( VIK_WAYPOINT(g_hash_table_lookup ( l->waypoints, sublayer )) );
3901     highest_wp_number_remove_wp(l, sublayer);
3902     g_hash_table_remove ( l->waypoints, sublayer );
3903
3904     rv = g_strdup(newname);
3905
3906     vik_treeview_item_set_pointer ( VIK_LAYER(l)->vt, iter, rv );
3907
3908     highest_wp_number_add_wp(l, rv);
3909     g_hash_table_insert ( l->waypoints, rv, wp );
3910     g_hash_table_insert ( l->waypoints_iters, rv, iter );
3911
3912     /* it hasn't been updated yet so we pass new name */
3913 #ifdef VIK_CONFIG_ALPHABETIZED_TRW
3914     vik_treeview_sublayer_realphabetize ( VIK_LAYER(l)->vt, iter, rv );
3915 #endif
3916
3917     vik_layers_panel_emit_update ( VIK_LAYERS_PANEL(vlp) );
3918     return rv;
3919   }
3920   if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
3921   {
3922     gchar *rv;
3923     VikTrack *tr;
3924     GtkTreeIter *iter;
3925     gchar *orig_key;
3926
3927     if (strcmp(newname, sublayer) == 0)
3928       return NULL;
3929
3930     if (strcasecmp(newname, sublayer)) { /* Not just changing case */
3931       if (g_hash_table_lookup( l->tracks, newname))
3932       {
3933         a_dialog_error_msg ( VIK_GTK_WINDOW_FROM_LAYER(l), _("Track Already Exists") );
3934         return NULL;
3935       }
3936     }
3937
3938     g_hash_table_lookup_extended ( l->tracks, sublayer, (void *)&orig_key, (void *)&tr );
3939     g_hash_table_steal ( l->tracks, sublayer );
3940
3941     iter = g_hash_table_lookup ( l->tracks_iters, sublayer );
3942     g_hash_table_steal ( l->tracks_iters, sublayer );
3943
3944     rv = g_strdup(newname);
3945
3946     vik_treeview_item_set_pointer ( VIK_LAYER(l)->vt, iter, rv );
3947
3948     g_hash_table_insert ( l->tracks, rv, tr );
3949     g_hash_table_insert ( l->tracks_iters, rv, iter );
3950
3951     /* don't forget about current_tp_track_name, update that too */
3952     if ( l->current_tp_track_name && g_strcasecmp(orig_key,l->current_tp_track_name) == 0 )
3953     {
3954       l->current_tp_track_name = rv;
3955       if ( l->tpwin )
3956         vik_trw_layer_tpwin_set_track_name ( l->tpwin, rv );
3957     }
3958     else if ( l->last_tp_track_name && g_strcasecmp(orig_key,l->last_tp_track_name) == 0 )
3959       l->last_tp_track_name = rv;
3960
3961     g_free ( orig_key );
3962
3963 #ifdef VIK_CONFIG_ALPHABETIZED_TRW
3964     vik_treeview_sublayer_realphabetize ( VIK_LAYER(l)->vt, iter, rv );
3965 #endif
3966
3967     vik_layers_panel_emit_update ( VIK_LAYERS_PANEL(vlp) );
3968     return rv;
3969   }
3970   return NULL;
3971 }
3972
3973 static gboolean is_valid_geocache_name ( gchar *str )
3974 {
3975   gint len = strlen ( str );
3976   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]));
3977 }
3978
3979 static void trw_layer_track_use_with_filter ( gpointer pass_along[6] )
3980 {
3981   gchar *track_name = (gchar *) pass_along[3];
3982   VikTrack *tr = g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, track_name );
3983   a_acquire_set_filter_track ( tr, track_name );
3984 }
3985
3986 static gboolean is_valid_google_route ( VikTrwLayer *vtl, const gchar *track_name )
3987 {
3988   VikTrack *tr = g_hash_table_lookup ( vtl->tracks, track_name );
3989   return ( tr && tr->comment && strlen(tr->comment) > 7 && !strncmp(tr->comment, "from:", 5) );
3990 }
3991
3992 static void trw_layer_track_google_route_webpage ( gpointer pass_along[6] )
3993 {
3994   gchar *track_name = (gchar *) pass_along[3];
3995   VikTrack *tr = g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, track_name );
3996   if ( tr ) {
3997     gchar *escaped = uri_escape ( tr->comment );
3998     gchar *webpage = g_strdup_printf("http://maps.google.com/maps?f=q&hl=en&q=%s", escaped );
3999     open_url(VIK_GTK_WINDOW_FROM_LAYER(VIK_LAYER(pass_along[0])), webpage);
4000     g_free ( escaped );
4001     g_free ( webpage );
4002   }
4003 }
4004
4005 /* vlp can be NULL if necessary - i.e. right-click from a tool */
4006 /* viewpoint is now available instead */
4007 static gboolean trw_layer_sublayer_add_menu_items ( VikTrwLayer *l, GtkMenu *menu, gpointer vlp, gint subtype, gpointer sublayer, GtkTreeIter *iter, VikViewport *vvp )
4008 {
4009   static gpointer pass_along[6];
4010   GtkWidget *item;
4011   gboolean rv = FALSE;
4012
4013   pass_along[0] = l;
4014   pass_along[1] = vlp;
4015   pass_along[2] = GINT_TO_POINTER (subtype);
4016   pass_along[3] = sublayer;
4017   pass_along[4] = GINT_TO_POINTER (1); // Confirm delete request
4018   pass_along[5] = vvp;
4019
4020   if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT || subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
4021   {
4022     rv = TRUE;
4023
4024     item = gtk_image_menu_item_new_from_stock ( GTK_STOCK_PROPERTIES, NULL );
4025     g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_properties_item), pass_along );
4026     gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
4027     gtk_widget_show ( item );
4028
4029     if (subtype == VIK_TRW_LAYER_SUBLAYER_TRACK) {
4030       VikTrwLayer *vtl = l;
4031       VikTrack *tr = g_hash_table_lookup ( vtl->tracks, sublayer );
4032       if (tr && tr->property_dialog)
4033         gtk_widget_set_sensitive(GTK_WIDGET(item), FALSE );
4034     }
4035
4036     item = gtk_image_menu_item_new_from_stock ( GTK_STOCK_CUT, NULL );
4037     g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_cut_item_cb), pass_along );
4038     gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
4039     gtk_widget_show ( item );
4040
4041     item = gtk_image_menu_item_new_from_stock ( GTK_STOCK_COPY, NULL );
4042     g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_copy_item_cb), pass_along );
4043     gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
4044     gtk_widget_show ( item );
4045
4046     item = gtk_image_menu_item_new_from_stock ( GTK_STOCK_DELETE, NULL );
4047     g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_item), pass_along );
4048     gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
4049     gtk_widget_show ( item );
4050
4051     if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT )
4052     {
4053       gboolean separator_created = FALSE;
4054
4055       /* could be a right-click using the tool */
4056       if ( vlp != NULL ) {
4057         item = gtk_menu_item_new ();
4058         gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
4059         gtk_widget_show ( item );
4060
4061         separator_created = TRUE;
4062
4063         item = gtk_image_menu_item_new_with_mnemonic ( _("_Goto") );
4064         gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_JUMP_TO, GTK_ICON_SIZE_MENU) );
4065         g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_waypoint), pass_along );
4066         gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
4067         gtk_widget_show ( item );
4068       }
4069
4070       if ( is_valid_geocache_name ( (gchar *) sublayer ) )
4071       {
4072         if ( !separator_created ) {
4073           item = gtk_menu_item_new ();
4074           gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
4075           gtk_widget_show ( item );
4076           separator_created = TRUE;
4077         }
4078
4079         item = gtk_menu_item_new_with_mnemonic ( _("_Visit Geocache Webpage") );
4080         g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_waypoint_gc_webpage), pass_along );
4081         gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
4082         gtk_widget_show ( item );
4083       }
4084
4085       VikWaypoint *wp = g_hash_table_lookup ( VIK_TRW_LAYER(l)->waypoints, sublayer );
4086
4087       if ( wp && wp->image )
4088       {
4089         if ( !separator_created ) {
4090           item = gtk_menu_item_new ();
4091           gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
4092           gtk_widget_show ( item );
4093           separator_created = TRUE;
4094         }
4095
4096         // Set up image paramater
4097         pass_along[5] = wp->image;
4098
4099         item = gtk_image_menu_item_new_with_mnemonic ( _("_Show Picture...") );
4100         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
4101         g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_show_picture), pass_along );
4102         gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
4103         gtk_widget_show ( item );
4104
4105 #ifdef VIK_CONFIG_GEOTAG
4106         GtkWidget *geotag_submenu = gtk_menu_new ();
4107         item = gtk_image_menu_item_new_with_mnemonic ( _("Update Geotag on _Image") );
4108         gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REFRESH, GTK_ICON_SIZE_MENU) );
4109         gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
4110         gtk_widget_show ( item );
4111         gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), geotag_submenu );
4112   
4113         item = gtk_menu_item_new_with_mnemonic ( _("_Update") );
4114         g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_geotagging_waypoint_mtime_update), pass_along );
4115         gtk_menu_shell_append (GTK_MENU_SHELL (geotag_submenu), item);
4116         gtk_widget_show ( item );
4117
4118         item = gtk_menu_item_new_with_mnemonic ( _("Update and _Keep File Timestamp") );
4119         g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_geotagging_waypoint_mtime_keep), pass_along );
4120         gtk_menu_shell_append (GTK_MENU_SHELL (geotag_submenu), item);
4121         gtk_widget_show ( item );
4122 #endif
4123       }
4124
4125     }
4126   }
4127
4128   if ( vlp && (subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINTS || subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT) )
4129   {
4130     rv = TRUE;
4131     item = gtk_image_menu_item_new_with_mnemonic ( _("_New Waypoint...") );
4132     gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NEW, GTK_ICON_SIZE_MENU) );
4133     g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_wp), pass_along );
4134     gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
4135     gtk_widget_show ( item );
4136   }
4137
4138   if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINTS )
4139   {
4140     item = gtk_image_menu_item_new_with_mnemonic ( _("_View All Waypoints") );
4141     gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ZOOM_FIT, GTK_ICON_SIZE_MENU) );
4142     g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_waypoints_view), pass_along );
4143     gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
4144     gtk_widget_show ( item );
4145
4146     item = gtk_image_menu_item_new_with_mnemonic ( _("Goto _Waypoint...") );
4147     gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_JUMP_TO, GTK_ICON_SIZE_MENU) );
4148     g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_wp), pass_along );
4149     gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
4150     gtk_widget_show ( item );
4151
4152     item = gtk_image_menu_item_new_with_mnemonic ( _("Delete _All Waypoints") );
4153     gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU) );
4154     g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_all_waypoints), pass_along );
4155     gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
4156     gtk_widget_show ( item );
4157
4158     item = gtk_image_menu_item_new_with_mnemonic ( _("_Delete Waypoints From Selection...") );
4159     gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
4160     g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_waypoints_from_selection), pass_along );
4161     gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
4162     gtk_widget_show ( item );
4163   }
4164
4165   if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACKS )
4166   {
4167     rv = TRUE;
4168
4169     item = gtk_image_menu_item_new_with_mnemonic ( _("_View All Tracks") );
4170     gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ZOOM_FIT, GTK_ICON_SIZE_MENU) );
4171     g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_tracks_view), pass_along );
4172     gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
4173     gtk_widget_show ( item );
4174
4175     item = gtk_image_menu_item_new_with_mnemonic ( _("Delete _All Tracks") );
4176     gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU) );
4177     g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_all_tracks), pass_along );
4178     gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
4179     gtk_widget_show ( item );
4180
4181     item = gtk_image_menu_item_new_with_mnemonic ( _("_Delete Tracks From Selection...") );
4182     gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
4183     g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_tracks_from_selection), pass_along );
4184     gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
4185     gtk_widget_show ( item );
4186   }
4187
4188   if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
4189   {
4190     GtkWidget *goto_submenu;
4191     item = gtk_menu_item_new ();
4192     gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
4193     gtk_widget_show ( item );
4194
4195     goto_submenu = gtk_menu_new ();
4196     item = gtk_image_menu_item_new_with_mnemonic ( _("_Goto") );
4197     gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_JUMP_TO, GTK_ICON_SIZE_MENU) );
4198     gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
4199     gtk_widget_show ( item );
4200     gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), goto_submenu );
4201
4202     item = gtk_image_menu_item_new_with_mnemonic ( _("_Startpoint") );
4203     gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GOTO_FIRST, GTK_ICON_SIZE_MENU) );
4204     g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_startpoint), pass_along );
4205     gtk_menu_shell_append ( GTK_MENU_SHELL(goto_submenu), item );
4206     gtk_widget_show ( item );
4207
4208     item = gtk_image_menu_item_new_with_mnemonic ( _("\"_Center\"") );
4209     gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_JUMP_TO, GTK_ICON_SIZE_MENU) );
4210     g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_center), pass_along );
4211     gtk_menu_shell_append ( GTK_MENU_SHELL(goto_submenu), item );
4212     gtk_widget_show ( item );
4213
4214     item = gtk_image_menu_item_new_with_mnemonic ( _("_Endpoint") );
4215     gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GOTO_LAST, GTK_ICON_SIZE_MENU) );
4216     g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_endpoint), pass_along );
4217     gtk_menu_shell_append ( GTK_MENU_SHELL(goto_submenu), item );
4218     gtk_widget_show ( item );
4219
4220     item = gtk_image_menu_item_new_with_mnemonic ( _("_Highest Altitude") );
4221     gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GOTO_TOP, GTK_ICON_SIZE_MENU) );
4222     g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_max_alt), pass_along );
4223     gtk_menu_shell_append ( GTK_MENU_SHELL(goto_submenu), item );
4224     gtk_widget_show ( item );
4225
4226     item = gtk_image_menu_item_new_with_mnemonic ( _("_Lowest Altitude") );
4227     gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GOTO_BOTTOM, GTK_ICON_SIZE_MENU) );
4228     g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_min_alt), pass_along );
4229     gtk_menu_shell_append ( GTK_MENU_SHELL(goto_submenu), item );
4230     gtk_widget_show ( item );
4231
4232     item = gtk_image_menu_item_new_with_mnemonic ( _("_Maximum Speed") );
4233     gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_MEDIA_FORWARD, GTK_ICON_SIZE_MENU) );
4234     g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_max_speed), pass_along );
4235     gtk_menu_shell_append ( GTK_MENU_SHELL(goto_submenu), item );
4236     gtk_widget_show ( item );
4237
4238     item = gtk_image_menu_item_new_with_mnemonic ( _("_View Track") );
4239     gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ZOOM_FIT, GTK_ICON_SIZE_MENU) );
4240     g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_track_view), pass_along );
4241     gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
4242     gtk_widget_show ( item );
4243
4244     item = gtk_menu_item_new_with_mnemonic ( _("_Merge By Time...") );
4245     g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_merge_by_timestamp), pass_along );
4246     gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
4247     gtk_widget_show ( item );
4248
4249     item = gtk_menu_item_new_with_mnemonic ( _("Merge _With Other Tracks...") );
4250     g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_merge_with_other), pass_along );
4251     gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
4252     gtk_widget_show ( item );
4253
4254     item = gtk_menu_item_new_with_mnemonic ( _("_Split By Time...") );
4255     g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_split_by_timestamp), pass_along );
4256     gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
4257     gtk_widget_show ( item );
4258
4259     item = gtk_menu_item_new_with_mnemonic ( _("Split By _Number of Points...") );
4260     g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_split_by_n_points), pass_along );
4261     gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
4262     gtk_widget_show ( item );
4263
4264     item = gtk_image_menu_item_new_with_mnemonic ( _("_Reverse Track") );
4265     gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_BACK, GTK_ICON_SIZE_MENU) );
4266     g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_reverse), pass_along );
4267     gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
4268     gtk_widget_show ( item );
4269
4270     /* ATM This function is only available via the layers panel, due to the method in finding out the maps in use */
4271     if ( vlp ) {
4272       item = gtk_image_menu_item_new_with_mnemonic ( _("Down_load Maps Along Track...") );
4273       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
4274       g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_download_map_along_track_cb), pass_along );
4275       gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
4276       gtk_widget_show ( item );
4277     }
4278
4279     item = gtk_image_menu_item_new_with_mnemonic ( _("_Apply DEM Data") );
4280     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
4281     g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_apply_dem_data), pass_along );
4282     gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
4283     gtk_widget_show ( item );
4284
4285     item = gtk_image_menu_item_new_with_mnemonic ( _("Export Trac_k as GPX...") );
4286     gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_HARDDISK, GTK_ICON_SIZE_MENU) );
4287     g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_gpx_track), pass_along );
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 ( _("E_xtend Track End") );
4292     gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ADD, GTK_ICON_SIZE_MENU) );
4293     g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_extend_track_end), pass_along );
4294     gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
4295     gtk_widget_show ( item );
4296
4297     item = gtk_image_menu_item_new_with_mnemonic ( _("Extend _Using Route Finder") );
4298     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
4299     g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_extend_track_end_route_finder), pass_along );
4300     gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
4301     gtk_widget_show ( item );
4302
4303 #ifdef VIK_CONFIG_OPENSTREETMAP
4304     item = gtk_image_menu_item_new_with_mnemonic ( _("Upload to _OSM...") );
4305     g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(osm_traces_upload_track_cb), pass_along );
4306     gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_UP, GTK_ICON_SIZE_MENU) );
4307     gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
4308     gtk_widget_show ( item );
4309 #endif
4310
4311     if ( is_valid_google_route ( l, (gchar *) sublayer ) )
4312     {
4313       item = gtk_image_menu_item_new_with_mnemonic ( _("_View Google Directions") );
4314       gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NETWORK, GTK_ICON_SIZE_MENU) );
4315       g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_track_google_route_webpage), pass_along );
4316       gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
4317       gtk_widget_show ( item );
4318     }
4319
4320     item = gtk_image_menu_item_new_with_mnemonic ( _("Use with _Filter") );
4321     gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
4322     g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_track_use_with_filter), pass_along );
4323     gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
4324     gtk_widget_show ( item );
4325
4326     /* ATM This function is only available via the layers panel, due to needing a vlp */
4327     if ( vlp ) {
4328       item = a_acquire_track_menu ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(l)), vlp,
4329                                     vik_layers_panel_get_viewport(VIK_LAYERS_PANEL(vlp)),
4330                                     g_hash_table_lookup ( l->tracks, (gchar *) sublayer ) );
4331       if ( item ) {
4332         gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
4333         gtk_widget_show ( item );
4334       }
4335     }
4336
4337 #ifdef VIK_CONFIG_GEOTAG
4338   item = gtk_menu_item_new_with_mnemonic ( _("Geotag _Images...") );
4339   g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_geotagging_track), pass_along );
4340   gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
4341   gtk_widget_show ( item );
4342 #endif
4343
4344     // Only show on viewport popmenu when a trackpoint is selected
4345     if ( ! vlp && l->current_tpl ) {
4346       // Add separator
4347       item = gtk_menu_item_new ();
4348       gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
4349       gtk_widget_show ( item );
4350
4351       item = gtk_image_menu_item_new_with_mnemonic ( _("_Edit Trackpoint") );
4352       gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_PROPERTIES, GTK_ICON_SIZE_MENU) );
4353       g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_edit_trackpoint), pass_along );
4354       gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
4355       gtk_widget_show ( item );
4356     }
4357
4358   }
4359
4360   return rv;
4361 }
4362
4363 static void trw_layer_insert_tp_after_current_tp ( VikTrwLayer *vtl )
4364 {
4365   /* sanity checks */
4366   if (!vtl->current_tpl)
4367     return;
4368   if (!vtl->current_tpl->next)
4369     return;
4370
4371   VikTrackpoint *tp_current = VIK_TRACKPOINT(vtl->current_tpl->data);
4372   VikTrackpoint *tp_next = VIK_TRACKPOINT(vtl->current_tpl->next->data);
4373
4374   /* Use current and next trackpoints to form a new track point which is inserted into the tracklist */
4375   if ( tp_next ) {
4376
4377     VikTrackpoint *tp_new = vik_trackpoint_new();
4378     struct LatLon ll_current, ll_next;
4379     vik_coord_to_latlon ( &tp_current->coord, &ll_current );
4380     vik_coord_to_latlon ( &tp_next->coord, &ll_next );
4381
4382     /* main positional interpolation */
4383     struct LatLon ll_new = { (ll_current.lat+ll_next.lat)/2, (ll_current.lon+ll_next.lon)/2 };
4384     vik_coord_load_from_latlon ( &(tp_new->coord), vtl->coord_mode, &ll_new );
4385
4386     /* Now other properties that can be interpolated */
4387     tp_new->altitude = (tp_current->altitude + tp_next->altitude) / 2;
4388
4389     if (tp_current->has_timestamp && tp_next->has_timestamp) {
4390       /* Note here the division is applied to each part, then added
4391          This is to avoid potential overflow issues with a 32 time_t for dates after midpoint of this Unix time on 2004/01/04 */
4392       tp_new->timestamp = (tp_current->timestamp/2) + (tp_next->timestamp/2);
4393       tp_new->has_timestamp = TRUE;
4394     }
4395
4396     if (tp_current->speed != NAN && tp_next->speed != NAN)
4397       tp_new->speed = (tp_current->speed + tp_next->speed)/2;
4398
4399     /* TODO - improve interpolation of course, as it may not be correct.
4400        if courses in degrees are 350 + 020, the mid course more likely to be 005 (not 185)
4401        [similar applies if value is in radians] */
4402     if (tp_current->course != NAN && tp_next->course != NAN)
4403       tp_new->speed = (tp_current->course + tp_next->course)/2;
4404
4405     /* DOP / sat values remain at defaults as not they do not seem applicable to a dreamt up point */
4406
4407     /* Insert new point into the trackpoints list after the current TP */
4408     VikTrack *tr = g_hash_table_lookup ( vtl->tracks, vtl->current_tp_track_name );
4409     gint index =  g_list_index ( tr->trackpoints, tp_current );
4410     if ( index > -1 ) {
4411       tr->trackpoints = g_list_insert (tr->trackpoints, tp_new, index+1 );
4412     }
4413   }
4414 }
4415
4416 /* to be called when last_tpl no long exists. */
4417 static void trw_layer_cancel_last_tp ( VikTrwLayer *vtl )
4418 {
4419   if ( vtl->tpwin ) /* can't join with a non-existant TP. */
4420     vik_trw_layer_tpwin_disable_join ( vtl->tpwin );
4421   vtl->last_tpl = NULL;
4422   vtl->last_tp_track_name = NULL;
4423 }
4424
4425 static void trw_layer_cancel_current_tp ( VikTrwLayer *vtl, gboolean destroy )
4426 {
4427   if ( vtl->tpwin )
4428   {
4429     if ( destroy)
4430     {
4431       gtk_widget_destroy ( GTK_WIDGET(vtl->tpwin) );
4432       vtl->tpwin = NULL;
4433     }
4434     else
4435       vik_trw_layer_tpwin_set_empty ( vtl->tpwin );
4436   }
4437   if ( vtl->current_tpl )
4438   {
4439     vtl->current_tpl = NULL;
4440     vtl->current_tp_track_name = NULL;
4441     vik_layer_emit_update(VIK_LAYER(vtl), FALSE);
4442   }
4443 }
4444
4445 static void trw_layer_tpwin_response ( VikTrwLayer *vtl, gint response )
4446 {
4447   g_assert ( vtl->tpwin != NULL );
4448   if ( response == VIK_TRW_LAYER_TPWIN_CLOSE )
4449     trw_layer_cancel_current_tp ( vtl, TRUE );
4450
4451   if ( vtl->current_tpl == NULL )
4452     return;
4453
4454   if ( response == VIK_TRW_LAYER_TPWIN_SPLIT && vtl->current_tpl->next && vtl->current_tpl->prev )
4455   {
4456     gchar *name = get_new_unique_sublayer_name(vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, vtl->current_tp_track_name);
4457     if ( ( name = a_dialog_new_track ( GTK_WINDOW(vtl->tpwin), vtl->tracks, name ) ) )
4458     {
4459       VikTrack *tr = vik_track_new ();
4460       GList *newglist = g_list_alloc ();
4461       newglist->prev = NULL;
4462       newglist->next = vtl->current_tpl->next;
4463       newglist->data = vik_trackpoint_copy(VIK_TRACKPOINT(vtl->current_tpl->data));
4464       tr->trackpoints = newglist;
4465
4466       vtl->current_tpl->next->prev = newglist; /* end old track here */
4467       vtl->current_tpl->next = NULL;
4468
4469       vtl->current_tpl = newglist; /* change tp to first of new track. */
4470       vtl->current_tp_track_name = name;
4471
4472       vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, vtl->current_tp_track_name );
4473
4474       tr->visible = TRUE;
4475
4476       vik_trw_layer_add_track ( vtl, name, tr );
4477       vik_layer_emit_update(VIK_LAYER(vtl), FALSE);
4478     }
4479   }
4480   else if ( response == VIK_TRW_LAYER_TPWIN_DELETE )
4481   {
4482     VikTrack *tr = g_hash_table_lookup ( vtl->tracks, vtl->current_tp_track_name );
4483     GList *new_tpl;
4484     g_assert(tr != NULL);
4485
4486     /* can't join with a non-existent trackpoint */
4487     vtl->last_tpl = NULL;
4488     vtl->last_tp_track_name = NULL;
4489
4490     if ( (new_tpl = vtl->current_tpl->next) || (new_tpl = vtl->current_tpl->prev) )
4491     {
4492       if ( VIK_TRACKPOINT(vtl->current_tpl->data)->newsegment && vtl->current_tpl->next )
4493         VIK_TRACKPOINT(vtl->current_tpl->next->data)->newsegment = TRUE; /* don't concat segments on del */
4494
4495       tr->trackpoints = g_list_remove_link ( tr->trackpoints, vtl->current_tpl ); /* this nulls current_tpl->prev and next */
4496
4497       /* at this point the old trackpoint exists, but the list links are correct (new), so it is safe to do this. */
4498       vik_trw_layer_tpwin_set_tp ( vtl->tpwin, new_tpl, vtl->current_tp_track_name );
4499
4500       trw_layer_cancel_last_tp ( vtl );
4501
4502       g_free ( vtl->current_tpl->data ); /* TODO: vik_trackpoint_free() */
4503       g_list_free_1 ( vtl->current_tpl );
4504       vtl->current_tpl = new_tpl;
4505       vik_layer_emit_update(VIK_LAYER(vtl), FALSE);
4506     }
4507     else
4508     {
4509       tr->trackpoints = g_list_remove_link ( tr->trackpoints, vtl->current_tpl );
4510       g_free ( vtl->current_tpl->data ); /* TODO longone: vik_trackpoint_new() and vik_trackpoint_free() */
4511       g_list_free_1 ( vtl->current_tpl );
4512       trw_layer_cancel_current_tp ( vtl, FALSE );
4513     }
4514   }
4515   else if ( response == VIK_TRW_LAYER_TPWIN_FORWARD && vtl->current_tpl->next )
4516   {
4517     vtl->last_tpl = vtl->current_tpl;
4518     vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl = vtl->current_tpl->next, vtl->current_tp_track_name );
4519     vik_layer_emit_update(VIK_LAYER(vtl), FALSE); /* TODO longone: either move or only update if tp is inside drawing window */
4520   }
4521   else if ( response == VIK_TRW_LAYER_TPWIN_BACK && vtl->current_tpl->prev )
4522   {
4523     vtl->last_tpl = vtl->current_tpl;
4524     vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl = vtl->current_tpl->prev, vtl->current_tp_track_name );
4525     vik_layer_emit_update(VIK_LAYER(vtl), FALSE);
4526   }
4527   else if ( response == VIK_TRW_LAYER_TPWIN_JOIN )
4528   {
4529     // Check tracks exist and are different before joining
4530     if ( ! vtl->last_tp_track_name || ! vtl->current_tp_track_name || vtl->last_tp_track_name == vtl->current_tp_track_name )
4531       return;
4532
4533     VikTrack *tr1 = g_hash_table_lookup ( vtl->tracks, vtl->last_tp_track_name );
4534     VikTrack *tr2 = g_hash_table_lookup ( vtl->tracks, vtl->current_tp_track_name );
4535
4536     VikTrack *tr_first = tr1, *tr_last = tr2;
4537
4538     gchar *tmp;
4539
4540     if ( (!vtl->last_tpl->next) && (!vtl->current_tpl->next) ) /* both endpoints */
4541       vik_track_reverse ( tr2 ); /* reverse the second, that way second track clicked will be later. */
4542     else if ( (!vtl->last_tpl->prev) && (!vtl->current_tpl->prev) )
4543       vik_track_reverse ( tr1 );
4544     else if ( (!vtl->last_tpl->prev) && (!vtl->current_tpl->next) ) /* clicked startpoint, then endpoint -- concat end to start */
4545     {
4546       tr_first = tr2;
4547       tr_last = tr1;
4548     }
4549     /* default -- clicked endpoint then startpoint -- connect endpoint to startpoint */
4550
4551     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. */
4552       VIK_TRACKPOINT(tr_last->trackpoints->data)->newsegment = FALSE;
4553     tr1->trackpoints = g_list_concat ( tr_first->trackpoints, tr_last->trackpoints );
4554     tr2->trackpoints = NULL;
4555
4556     tmp = vtl->current_tp_track_name;
4557
4558     vtl->current_tp_track_name = vtl->last_tp_track_name; /* current_tp stays the same (believe it or not!) */
4559     vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, vtl->current_tp_track_name );
4560
4561     /* if we did this before, trw_layer_delete_track would have canceled the current tp because
4562      * it was the current track. canceling the current tp would have set vtl->current_tpl to NULL */
4563     vik_trw_layer_delete_track ( vtl, tmp );
4564
4565     trw_layer_cancel_last_tp ( vtl ); /* same TP, can't join. */
4566     vik_layer_emit_update(VIK_LAYER(vtl), FALSE);
4567   }
4568   else if ( response == VIK_TRW_LAYER_TPWIN_INSERT && vtl->current_tpl->next )
4569   {
4570     trw_layer_insert_tp_after_current_tp ( vtl );
4571     vik_layer_emit_update(VIK_LAYER(vtl), FALSE);
4572   }
4573   else if ( response == VIK_TRW_LAYER_TPWIN_DATA_CHANGED )
4574     vik_layer_emit_update(VIK_LAYER(vtl), FALSE);
4575 }
4576
4577 static void trw_layer_tpwin_init ( VikTrwLayer *vtl )
4578 {
4579   if ( ! vtl->tpwin )
4580   {
4581     vtl->tpwin = vik_trw_layer_tpwin_new ( VIK_GTK_WINDOW_FROM_LAYER(vtl) );
4582     g_signal_connect_swapped ( GTK_DIALOG(vtl->tpwin), "response", G_CALLBACK(trw_layer_tpwin_response), vtl );
4583     /* connect signals -- DELETE SIGNAL VERY IMPORTANT TO SET TO NULL */
4584     g_signal_connect_swapped ( vtl->tpwin, "delete-event", G_CALLBACK(trw_layer_cancel_current_tp), vtl );
4585     gtk_widget_show_all ( GTK_WIDGET(vtl->tpwin) );
4586   }
4587   if ( vtl->current_tpl )
4588     vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, vtl->current_tp_track_name );
4589   /* set layer name and TP data */
4590 }
4591
4592 /***************************************************************************
4593  ** Tool code
4594  ***************************************************************************/
4595
4596 /*** Utility data structures and functions ****/
4597
4598 typedef struct {
4599   gint x, y;
4600   gint closest_x, closest_y;
4601   gchar *closest_wp_name;
4602   VikWaypoint *closest_wp;
4603   VikViewport *vvp;
4604 } WPSearchParams;
4605
4606 typedef struct {
4607   gint x, y;
4608   gint closest_x, closest_y;
4609   gchar *closest_track_name;
4610   VikTrackpoint *closest_tp;
4611   VikViewport *vvp;
4612   GList *closest_tpl;
4613 } TPSearchParams;
4614
4615 static void waypoint_search_closest_tp ( gchar *name, VikWaypoint *wp, WPSearchParams *params )
4616 {
4617   gint x, y;
4618   if ( !wp->visible )
4619     return;
4620
4621   vik_viewport_coord_to_screen ( params->vvp, &(wp->coord), &x, &y );
4622
4623   // If waypoint has an image then use the image size to select
4624   if ( wp->image ) {
4625     gint slackx, slacky;
4626     slackx = wp->image_width / 2;
4627     slacky = wp->image_height / 2;
4628
4629     if (    x <= params->x + slackx && x >= params->x - slackx
4630          && y <= params->y + slacky && y >= params->y - slacky ) {
4631       params->closest_wp_name = name;
4632       params->closest_wp = wp;
4633       params->closest_x = x;
4634       params->closest_y = y;
4635     }
4636   }
4637   else if ( abs (x - params->x) <= WAYPOINT_SIZE_APPROX && abs (y - params->y) <= WAYPOINT_SIZE_APPROX &&
4638             ((!params->closest_wp) ||        /* was the old waypoint we already found closer than this one? */
4639              abs(x - params->x)+abs(y - params->y) < abs(x - params->closest_x)+abs(y - params->closest_y)))
4640     {
4641       params->closest_wp_name = name;
4642       params->closest_wp = wp;
4643       params->closest_x = x;
4644       params->closest_y = y;
4645     }
4646 }
4647
4648 static void track_search_closest_tp ( gchar *name, VikTrack *t, TPSearchParams *params )
4649 {
4650   GList *tpl = t->trackpoints;
4651   VikTrackpoint *tp;
4652
4653   if ( !t->visible )
4654     return;
4655
4656   while (tpl)
4657   {
4658     gint x, y;
4659     tp = VIK_TRACKPOINT(tpl->data);
4660
4661     vik_viewport_coord_to_screen ( params->vvp, &(tp->coord), &x, &y );
4662  
4663     if ( abs (x - params->x) <= TRACKPOINT_SIZE_APPROX && abs (y - params->y) <= TRACKPOINT_SIZE_APPROX &&
4664         ((!params->closest_tp) ||        /* was the old trackpoint we already found closer than this one? */
4665           abs(x - params->x)+abs(y - params->y) < abs(x - params->closest_x)+abs(y - params->closest_y)))
4666     {
4667       params->closest_track_name = name;
4668       params->closest_tp = tp;
4669       params->closest_tpl = tpl;
4670       params->closest_x = x;
4671       params->closest_y = y;
4672     }
4673     tpl = tpl->next;
4674   }
4675 }
4676
4677 static VikTrackpoint *closest_tp_in_five_pixel_interval ( VikTrwLayer *vtl, VikViewport *vvp, gint x, gint y )
4678 {
4679   TPSearchParams params;
4680   params.x = x;
4681   params.y = y;
4682   params.vvp = vvp;
4683   params.closest_track_name = NULL;
4684   params.closest_tp = NULL;
4685   g_hash_table_foreach ( vtl->tracks, (GHFunc) track_search_closest_tp, &params);
4686   return params.closest_tp;
4687 }
4688
4689 static VikWaypoint *closest_wp_in_five_pixel_interval ( VikTrwLayer *vtl, VikViewport *vvp, gint x, gint y )
4690 {
4691   WPSearchParams params;
4692   params.x = x;
4693   params.y = y;
4694   params.vvp = vvp;
4695   params.closest_wp = NULL;
4696   params.closest_wp_name = NULL;
4697   g_hash_table_foreach ( vtl->waypoints, (GHFunc) waypoint_search_closest_tp, &params);
4698   return params.closest_wp;
4699 }
4700
4701
4702 // Some forward declarations
4703 static void marker_begin_move ( tool_ed_t *t, gint x, gint y );
4704 static void marker_moveto ( tool_ed_t *t, gint x, gint y );
4705 static void marker_end_move ( tool_ed_t *t );
4706 //
4707
4708 static gboolean trw_layer_select_move ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp, tool_ed_t* t )
4709 {
4710   if ( t->holding ) {
4711     VikCoord new_coord;
4712     vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
4713
4714     // Here always allow snapping back to the original location
4715     //  this is useful when one decides not to move the thing afterall
4716     // If one wants to move the item only a little bit then don't hold down the 'snap' key!
4717  
4718     // snap to TP
4719     if ( event->state & GDK_CONTROL_MASK )
4720     {
4721       VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
4722       if ( tp )
4723         new_coord = tp->coord;
4724     }
4725
4726     // snap to WP
4727     if ( event->state & GDK_SHIFT_MASK )
4728     {
4729       VikWaypoint *wp = closest_wp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
4730       if ( wp )
4731         new_coord = wp->coord;
4732     }
4733     
4734     gint x, y;
4735     vik_viewport_coord_to_screen ( vvp, &new_coord, &x, &y );
4736
4737     marker_moveto ( t, x, y );
4738
4739     return TRUE;
4740   }
4741   return FALSE;
4742 }
4743
4744 static gboolean trw_layer_select_release ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp, tool_ed_t* t )
4745 {
4746   if ( t->holding && event->button == 1 )
4747   {
4748     VikCoord new_coord;
4749     vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
4750
4751     // snap to TP
4752     if ( event->state & GDK_CONTROL_MASK )
4753     {
4754       VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
4755       if ( tp )
4756         new_coord = tp->coord;
4757     }
4758
4759     // snap to WP
4760     if ( event->state & GDK_SHIFT_MASK )
4761     {
4762       VikWaypoint *wp = closest_wp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
4763       if ( wp )
4764         new_coord = wp->coord;
4765     }
4766
4767     marker_end_move ( t );
4768
4769     // Determine if working on a waypoint or a trackpoint
4770     if ( t->is_waypoint )
4771       vtl->current_wp->coord = new_coord;
4772     else {
4773       if ( vtl->current_tpl ) {
4774         VIK_TRACKPOINT(vtl->current_tpl->data)->coord = new_coord;
4775       
4776         if ( vtl->tpwin )
4777           vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, vtl->current_tp_track_name );
4778
4779         // Don't really know what this is for but seems like it might be handy...
4780         /* can't join with itself! */
4781         trw_layer_cancel_last_tp ( vtl );
4782       }
4783     }
4784
4785     // Reset
4786     vtl->current_wp      = NULL;
4787     vtl->current_wp_name = NULL;
4788     trw_layer_cancel_current_tp ( vtl, FALSE );
4789
4790     vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
4791     return TRUE;
4792   }
4793   return FALSE;
4794 }
4795
4796 /*
4797   Returns true if a waypoint or track is found near the requested event position for this particular layer
4798   The item found is automatically selected
4799   This is a tool like feature but routed via the layer interface, since it's instigated by a 'global' layer tool in vikwindow.c
4800  */
4801 static gboolean trw_layer_select_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp, tool_ed_t* tet )
4802 {
4803   if ( event->button != 1 )
4804     return FALSE;
4805
4806   if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
4807     return FALSE;
4808
4809   if ( !vtl->tracks_visible && !vtl->waypoints_visible )
4810     return FALSE;
4811
4812   // Go for waypoints first as these often will be near a track, but it's likely the wp is wanted rather then the track
4813
4814   if (vtl->waypoints_visible) {
4815     WPSearchParams wp_params;
4816     wp_params.vvp = vvp;
4817     wp_params.x = event->x;
4818     wp_params.y = event->y;
4819     wp_params.closest_wp_name = NULL;
4820     wp_params.closest_wp = NULL;
4821
4822     g_hash_table_foreach ( vtl->waypoints, (GHFunc) waypoint_search_closest_tp, &wp_params);
4823
4824     if ( wp_params.closest_wp )  {
4825
4826       // Select
4827       vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, g_hash_table_lookup ( vtl->waypoints_iters, wp_params.closest_wp_name ), TRUE );
4828
4829       // Too easy to move it so must be holding shift to start immediately moving it
4830       //   or otherwise be previously selected but not have an image (otherwise clicking within image bounds (again) moves it)
4831       if ( event->state & GDK_SHIFT_MASK ||
4832            ( vtl->current_wp == wp_params.closest_wp && !vtl->current_wp->image ) ) {
4833         // Put into 'move buffer'
4834         // NB vvp & vw already set in tet
4835         tet->vtl = (gpointer)vtl;
4836         tet->is_waypoint = TRUE;
4837       
4838         marker_begin_move (tet, event->x, event->y);
4839       }
4840
4841       vtl->current_wp =      wp_params.closest_wp;
4842       vtl->current_wp_name = wp_params.closest_wp_name;
4843
4844       vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
4845
4846       return TRUE;
4847     }
4848   }
4849
4850   if (vtl->tracks_visible) {
4851     TPSearchParams tp_params;
4852     tp_params.vvp = vvp;
4853     tp_params.x = event->x;
4854     tp_params.y = event->y;
4855     tp_params.closest_track_name = NULL;
4856     tp_params.closest_tp = NULL;
4857
4858     g_hash_table_foreach ( vtl->tracks, (GHFunc) track_search_closest_tp, &tp_params);
4859
4860     if ( tp_params.closest_tp )  {
4861
4862       // Always select + highlight the track
4863       vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, g_hash_table_lookup ( vtl->tracks_iters, tp_params.closest_track_name ), TRUE );
4864
4865       tet->is_waypoint = FALSE;
4866
4867       // Select the Trackpoint
4868       // Can move it immediately when control held or it's the previously selected tp
4869       if ( event->state & GDK_CONTROL_MASK ||
4870            vtl->current_tpl == tp_params.closest_tpl ) {
4871         // Put into 'move buffer'
4872         // NB vvp & vw already set in tet
4873         tet->vtl = (gpointer)vtl;
4874         marker_begin_move (tet, event->x, event->y);
4875       }
4876
4877       vtl->current_tpl = tp_params.closest_tpl;
4878       vtl->current_tp_track_name = tp_params.closest_track_name;
4879
4880       set_statusbar_msg_info_trkpt ( vtl, tp_params.closest_tp );
4881
4882       if ( vtl->tpwin )
4883         vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, vtl->current_tp_track_name );
4884
4885       vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
4886       return TRUE;
4887     }
4888   }
4889
4890   /* these aren't the droids you're looking for */
4891   vtl->current_wp      = NULL;
4892   vtl->current_wp_name = NULL;
4893   trw_layer_cancel_current_tp ( vtl, FALSE );
4894
4895   // Blank info
4896   vik_statusbar_set_message ( vik_window_get_statusbar (VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl))), VIK_STATUSBAR_INFO, "" );
4897
4898   return FALSE;
4899 }
4900
4901 static gboolean trw_layer_show_selected_viewport_menu ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
4902 {
4903   if ( event->button != 3 )
4904     return FALSE;
4905
4906   if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
4907     return FALSE;
4908
4909   if ( !vtl->tracks_visible && !vtl->waypoints_visible )
4910     return FALSE;
4911
4912   /* Post menu for the currently selected item */
4913
4914   /* See if a track is selected */
4915   VikTrack *track = (VikTrack*)vik_window_get_selected_track ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(vtl) );
4916   if ( track && track->visible ) {
4917
4918     if ( vik_window_get_selected_name ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(vtl) ) ) {
4919
4920       if ( vtl->track_right_click_menu )
4921         gtk_object_sink ( GTK_OBJECT(vtl->track_right_click_menu) );
4922
4923       vtl->track_right_click_menu = GTK_MENU ( gtk_menu_new () );
4924       
4925       trw_layer_sublayer_add_menu_items ( vtl,
4926                                           vtl->track_right_click_menu,
4927                                           NULL,
4928                                           VIK_TRW_LAYER_SUBLAYER_TRACK,
4929                                           vik_window_get_selected_name ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(vtl) ),
4930                                           g_hash_table_lookup ( vtl->tracks_iters, vik_window_get_selected_name ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(vtl) ) ),
4931                                           vvp);
4932
4933       gtk_menu_popup ( vtl->track_right_click_menu, NULL, NULL, NULL, NULL, event->button, gtk_get_current_event_time() );
4934         
4935       return TRUE;
4936     }
4937   }
4938
4939   /* See if a waypoint is selected */
4940   VikWaypoint *waypoint = (VikWaypoint*)vik_window_get_selected_waypoint ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(vtl) );
4941   if ( waypoint && waypoint->visible ) {
4942     if ( vik_window_get_selected_name ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(vtl) ) ) {
4943
4944       if ( vtl->wp_right_click_menu )
4945         gtk_object_sink ( GTK_OBJECT(vtl->wp_right_click_menu) );
4946
4947       vtl->wp_right_click_menu = GTK_MENU ( gtk_menu_new () );
4948       trw_layer_sublayer_add_menu_items ( vtl,
4949                                           vtl->wp_right_click_menu,
4950                                           NULL,
4951                                           VIK_TRW_LAYER_SUBLAYER_WAYPOINT,
4952                                           vik_window_get_selected_name ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(vtl) ),
4953                                           g_hash_table_lookup ( vtl->waypoints_iters, vik_window_get_selected_name ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(vtl) ) ),
4954                                           vvp);
4955       gtk_menu_popup ( vtl->wp_right_click_menu, NULL, NULL, NULL, NULL, event->button, gtk_get_current_event_time() );
4956
4957       return TRUE;
4958     }
4959   }
4960
4961   return FALSE;
4962 }
4963
4964 /* background drawing hook, to be passed the viewport */
4965 static gboolean tool_sync_done = TRUE;
4966
4967 static gboolean tool_sync(gpointer data)
4968 {
4969   VikViewport *vvp = data;
4970   gdk_threads_enter();
4971   vik_viewport_sync(vvp);
4972   tool_sync_done = TRUE;
4973   gdk_threads_leave();
4974   return FALSE;
4975 }
4976
4977 static void marker_begin_move ( tool_ed_t *t, gint x, gint y )
4978 {
4979   t->holding = TRUE;
4980   t->gc = vik_viewport_new_gc (t->vvp, "black", 2);
4981   gdk_gc_set_function ( t->gc, GDK_INVERT );
4982   vik_viewport_draw_rectangle ( t->vvp, t->gc, FALSE, x-3, y-3, 6, 6 );
4983   vik_viewport_sync(t->vvp);
4984   t->oldx = x;
4985   t->oldy = y;
4986 }
4987
4988 static void marker_moveto ( tool_ed_t *t, gint x, gint y )
4989 {
4990   VikViewport *vvp =  t->vvp;
4991   vik_viewport_draw_rectangle ( vvp, t->gc, FALSE, t->oldx-3, t->oldy-3, 6, 6 );
4992   vik_viewport_draw_rectangle ( vvp, t->gc, FALSE, x-3, y-3, 6, 6 );
4993   t->oldx = x;
4994   t->oldy = y;
4995
4996   if (tool_sync_done) {
4997     g_idle_add_full (G_PRIORITY_HIGH_IDLE + 10, tool_sync, vvp, NULL);
4998     tool_sync_done = FALSE;
4999   }
5000 }
5001
5002 static void marker_end_move ( tool_ed_t *t )
5003 {
5004   vik_viewport_draw_rectangle ( t->vvp, t->gc, FALSE, t->oldx-3, t->oldy-3, 6, 6 );
5005   g_object_unref ( t->gc );
5006   t->holding = FALSE;
5007 }
5008
5009 /*** Edit waypoint ****/
5010
5011 static gpointer tool_edit_waypoint_create ( VikWindow *vw, VikViewport *vvp)
5012 {
5013   tool_ed_t *t = g_new(tool_ed_t, 1);
5014   t->vvp = vvp;
5015   t->holding = FALSE;
5016   return t;
5017 }
5018
5019 static gboolean tool_edit_waypoint_click ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data )
5020 {
5021   WPSearchParams params;
5022   tool_ed_t *t = data;
5023   VikViewport *vvp = t->vvp;
5024
5025   if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
5026     return FALSE;
5027
5028   if ( t->holding ) {
5029     return TRUE;
5030   }
5031
5032   if ( !vtl->vl.visible || !vtl->waypoints_visible )
5033     return FALSE;
5034
5035   if ( vtl->current_wp && vtl->current_wp->visible )
5036   {
5037     /* first check if current WP is within area (other may be 'closer', but we want to move the current) */
5038     gint x, y;
5039     vik_viewport_coord_to_screen ( vvp, &(vtl->current_wp->coord), &x, &y );
5040
5041     if ( abs(x - event->x) <= WAYPOINT_SIZE_APPROX &&
5042          abs(y - event->y) <= WAYPOINT_SIZE_APPROX )
5043     {
5044       if ( event->button == 3 )
5045         vtl->waypoint_rightclick = TRUE; /* remember that we're clicking; other layers will ignore release signal */
5046       else {
5047         marker_begin_move(t, event->x, event->y);
5048       }
5049       return TRUE;
5050     }
5051   }
5052
5053   params.vvp = vvp;
5054   params.x = event->x;
5055   params.y = event->y;
5056   params.closest_wp_name = NULL;
5057   /* TODO: should get track listitem so we can break it up, make a new track, mess it up, all that. */
5058   params.closest_wp = NULL;
5059   g_hash_table_foreach ( vtl->waypoints, (GHFunc) waypoint_search_closest_tp, &params);
5060   if ( vtl->current_wp == params.closest_wp && vtl->current_wp != NULL )
5061   {
5062     /* how do we get here? I'm putting in the abort until we can figure it out. -alex */
5063     marker_begin_move(t, event->x, event->y);
5064     g_critical("shouldn't be here");
5065     exit(1);
5066   }
5067   else if ( params.closest_wp )
5068   {
5069     if ( event->button == 3 )
5070       vtl->waypoint_rightclick = TRUE; /* remember that we're clicking; other layers will ignore release signal */
5071     else
5072       vtl->waypoint_rightclick = FALSE;
5073
5074     vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, g_hash_table_lookup ( vtl->waypoints_iters, params.closest_wp_name ), TRUE );
5075
5076     vtl->current_wp = params.closest_wp;
5077     vtl->current_wp_name = params.closest_wp_name;
5078
5079     /* could make it so don't update if old WP is off screen and new is null but oh well */
5080     vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
5081     return TRUE;
5082   }
5083
5084   vtl->current_wp = NULL;
5085   vtl->current_wp_name = NULL;
5086   vtl->waypoint_rightclick = FALSE;
5087   vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
5088   return FALSE;
5089 }
5090
5091 static gboolean tool_edit_waypoint_move ( VikTrwLayer *vtl, GdkEventMotion *event, gpointer data )
5092 {
5093   tool_ed_t *t = data;
5094   VikViewport *vvp = t->vvp;
5095
5096   if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
5097     return FALSE;
5098
5099   if ( t->holding ) {
5100     VikCoord new_coord;
5101     vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
5102
5103     /* snap to TP */
5104     if ( event->state & GDK_CONTROL_MASK )
5105     {
5106       VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
5107       if ( tp )
5108         new_coord = tp->coord;
5109     }
5110
5111     /* snap to WP */
5112     if ( event->state & GDK_SHIFT_MASK )
5113     {
5114       VikWaypoint *wp = closest_wp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
5115       if ( wp && wp != vtl->current_wp )
5116         new_coord = wp->coord;
5117     }
5118     
5119     { 
5120       gint x, y;
5121       vik_viewport_coord_to_screen ( vvp, &new_coord, &x, &y );
5122
5123       marker_moveto ( t, x, y );
5124     } 
5125     return TRUE;
5126   }
5127   return FALSE;
5128 }
5129
5130 static gboolean tool_edit_waypoint_release ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data )
5131 {
5132   tool_ed_t *t = data;
5133   VikViewport *vvp = t->vvp;
5134
5135   if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
5136     return FALSE;
5137   
5138   if ( t->holding && event->button == 1 )
5139   {
5140     VikCoord new_coord;
5141     vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
5142
5143     /* snap to TP */
5144     if ( event->state & GDK_CONTROL_MASK )
5145     {
5146       VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
5147       if ( tp )
5148         new_coord = tp->coord;
5149     }
5150
5151     /* snap to WP */
5152     if ( event->state & GDK_SHIFT_MASK )
5153     {
5154       VikWaypoint *wp = closest_wp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
5155       if ( wp && wp != vtl->current_wp )
5156         new_coord = wp->coord;
5157     }
5158
5159     marker_end_move ( t );
5160
5161     vtl->current_wp->coord = new_coord;
5162     vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
5163     return TRUE;
5164   }
5165   /* PUT IN RIGHT PLACE!!! */
5166   if ( event->button == 3 && vtl->waypoint_rightclick )
5167   {
5168     if ( vtl->wp_right_click_menu )
5169       g_object_ref_sink ( G_OBJECT(vtl->wp_right_click_menu) );
5170     if ( vtl->current_wp ) {
5171       vtl->wp_right_click_menu = GTK_MENU ( gtk_menu_new () );
5172       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 );
5173       gtk_menu_popup ( vtl->wp_right_click_menu, NULL, NULL, NULL, NULL, event->button, gtk_get_current_event_time() );
5174     }
5175     vtl->waypoint_rightclick = FALSE;
5176   }
5177   return FALSE;
5178 }
5179
5180 /**** Begin track ***/
5181 static gpointer tool_begin_track_create ( VikWindow *vw, VikViewport *vvp)
5182 {
5183   return vvp;
5184 }
5185
5186 static gboolean tool_begin_track_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
5187 {
5188   vtl->current_track = NULL;
5189   return tool_new_track_click ( vtl, event, vvp );
5190 }
5191
5192 /*** New track ****/
5193
5194 static gpointer tool_new_track_create ( VikWindow *vw, VikViewport *vvp)
5195 {
5196   return vvp;
5197 }
5198
5199 typedef struct {
5200   VikTrwLayer *vtl;
5201   VikViewport *vvp;
5202   gint x1,y1,x2,y2,x3,y3;
5203   const gchar* str;
5204 } new_track_move_passalong_t;
5205
5206 /* sync and undraw, but only when we have time */
5207 static gboolean ct_sync ( gpointer passalong )
5208 {
5209   new_track_move_passalong_t *p = (new_track_move_passalong_t *) passalong;
5210
5211   vik_viewport_sync ( p->vvp );
5212   gdk_gc_set_function ( p->vtl->current_track_gc, GDK_INVERT );
5213   vik_viewport_draw_line ( p->vvp, p->vtl->current_track_gc, p->x1, p->y1, p->x2, p->y2 );
5214   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);
5215   gdk_gc_set_function ( p->vtl->current_track_gc, GDK_COPY );
5216
5217   g_free ( (gpointer) p->str ) ;
5218   p->vtl->ct_sync_done = TRUE;
5219   g_free ( p );
5220   return FALSE;
5221 }
5222
5223 static const gchar* distance_string (gdouble distance)
5224 {
5225   gchar str[128];
5226
5227   /* draw label with distance */
5228   vik_units_distance_t dist_units = a_vik_get_units_distance ();
5229   switch (dist_units) {
5230   case VIK_UNITS_DISTANCE_MILES:
5231     if (distance >= VIK_MILES_TO_METERS(1) && distance < VIK_MILES_TO_METERS(100)) {
5232       g_sprintf(str, "%3.2f miles", VIK_METERS_TO_MILES(distance));
5233     } else if (distance < 1609.4) {
5234       g_sprintf(str, "%d yards", (int)(distance*1.0936133));
5235     } else {
5236       g_sprintf(str, "%d miles", (int)VIK_METERS_TO_MILES(distance));
5237     }
5238     break;
5239   default:
5240     // VIK_UNITS_DISTANCE_KILOMETRES
5241     if (distance >= 1000 && distance < 100000) {
5242       g_sprintf(str, "%3.2f km", distance/1000.0);
5243     } else if (distance < 1000) {
5244       g_sprintf(str, "%d m", (int)distance);
5245     } else {
5246       g_sprintf(str, "%d km", (int)distance/1000);
5247     }
5248     break;
5249   }
5250   return g_strdup (str);
5251 }
5252
5253 /*
5254  * Actually set the message in statusbar
5255  */
5256 static void statusbar_write (const gchar *distance_string, gdouble elev_gain, gdouble elev_loss, VikTrwLayer *vtl )
5257 {
5258   // Only show elevation data when track has some elevation properties
5259   gchar str_gain_loss[64];
5260   str_gain_loss[0] = '\0';
5261   
5262   if ( (elev_gain > 0.1) || (elev_loss > 0.1) ) {
5263     if ( a_vik_get_units_height () == VIK_UNITS_HEIGHT_METRES )
5264       g_sprintf(str_gain_loss, _(" - Gain %dm:Loss %dm"), (int)elev_gain, (int)elev_loss);
5265     else
5266       g_sprintf(str_gain_loss, _(" - Gain %dft:Loss %dft"), (int)VIK_METERS_TO_FEET(elev_gain), (int)VIK_METERS_TO_FEET(elev_loss));
5267   }
5268
5269   // Write with full gain/loss information
5270   gchar *msg = g_strdup_printf ( "%s%s", distance_string, str_gain_loss);
5271   vik_statusbar_set_message ( vik_window_get_statusbar (VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl))), VIK_STATUSBAR_INFO, msg );
5272   g_free ( msg );
5273 }
5274
5275 /*
5276  * Figure out what information should be set in the statusbar and then write it
5277  */
5278 static void update_statusbar ( VikTrwLayer *vtl )
5279 {
5280   // Get elevation data
5281   gdouble elev_gain, elev_loss;
5282   vik_track_get_total_elevation_gain ( vtl->current_track, &elev_gain, &elev_loss);
5283
5284   /* Find out actual distance of current track */
5285   gdouble distance = vik_track_get_length (vtl->current_track);
5286   const gchar *str = distance_string (distance);
5287
5288   statusbar_write (str, elev_gain, elev_loss, vtl);
5289
5290   g_free ((gpointer)str);
5291 }
5292
5293
5294 static VikLayerToolFuncStatus tool_new_track_move ( VikTrwLayer *vtl, GdkEventMotion *event, VikViewport *vvp )
5295 {
5296   /* if we haven't sync'ed yet, we don't have time to do more. */
5297   if ( vtl->ct_sync_done && vtl->current_track && vtl->current_track->trackpoints ) {
5298     GList *iter = vtl->current_track->trackpoints;
5299     new_track_move_passalong_t *passalong;
5300     gint x1, y1;
5301
5302     while ( iter->next )
5303       iter = iter->next;
5304     gdk_gc_set_function ( vtl->current_track_gc, GDK_INVERT );
5305     vik_viewport_coord_to_screen ( vvp, &(VIK_TRACKPOINT(iter->data)->coord), &x1, &y1 );
5306     vik_viewport_draw_line ( vvp, vtl->current_track_gc, x1, y1, event->x, event->y );
5307
5308     /* Find out actual distance of current track */
5309     gdouble distance = vik_track_get_length (vtl->current_track);
5310
5311     // Now add distance to where the pointer is //
5312     VikCoord coord;
5313     struct LatLon ll;
5314     vik_viewport_screen_to_coord ( vvp, (gint) event->x, (gint) event->y, &coord );
5315     vik_coord_to_latlon ( &coord, &ll );
5316     distance = distance + vik_coord_diff( &coord, &(VIK_TRACKPOINT(iter->data)->coord));
5317
5318     // Get elevation data
5319     gdouble elev_gain, elev_loss;
5320     vik_track_get_total_elevation_gain ( vtl->current_track, &elev_gain, &elev_loss);
5321
5322     // Adjust elevation data (if available) for the current pointer position
5323     gdouble elev_new;
5324     elev_new = (gdouble) a_dems_get_elev_by_coord ( &coord, VIK_DEM_INTERPOL_BEST );
5325     if ( elev_new != VIK_DEM_INVALID_ELEVATION ) {
5326       if ( VIK_TRACKPOINT(iter->data)->altitude != VIK_DEFAULT_ALTITUDE ) {
5327         // Adjust elevation of last track point
5328         if ( elev_new > VIK_TRACKPOINT(iter->data)->altitude )
5329           // Going up
5330           elev_gain += elev_new - VIK_TRACKPOINT(iter->data)->altitude;
5331         else
5332           // Going down
5333           elev_loss += VIK_TRACKPOINT(iter->data)->altitude - elev_new;
5334       }
5335     }
5336       
5337     const gchar *str = distance_string (distance);
5338     gint xd,yd;
5339     /* offset from cursor a bit */
5340     xd = event->x + 10;
5341     yd = event->y - 10;
5342     /* note attempted setting text using pango layouts - but the 'undraw' technique didn't work */
5343     vik_viewport_draw_string (vvp, gdk_font_from_description (pango_font_description_from_string ("Sans 8")), vtl->current_track_gc, xd, yd, str);
5344
5345     gdk_gc_set_function ( vtl->current_track_gc, GDK_COPY );
5346
5347     passalong = g_new(new_track_move_passalong_t,1); /* freed by sync */
5348     passalong->vtl = vtl;
5349     passalong->vvp = vvp;
5350     passalong->x1 = x1;
5351     passalong->y1 = y1;
5352     passalong->x2 = event->x;
5353     passalong->y2 = event->y;
5354     passalong->x3 = xd;
5355     passalong->y3 = yd;
5356     passalong->str = str;
5357
5358     // Update statusbar with full gain/loss information
5359     statusbar_write (str, elev_gain, elev_loss, vtl);
5360
5361     /* this will sync and undraw when we have time to */
5362     g_idle_add_full (G_PRIORITY_HIGH_IDLE + 10, ct_sync, passalong, NULL);
5363     vtl->ct_sync_done = FALSE;
5364     return VIK_LAYER_TOOL_ACK_GRAB_FOCUS;
5365   }
5366   return VIK_LAYER_TOOL_ACK;
5367 }
5368
5369 static gboolean tool_new_track_key_press ( VikTrwLayer *vtl, GdkEventKey *event, VikViewport *vvp )
5370 {
5371   if ( vtl->current_track && event->keyval == GDK_Escape ) {
5372     vtl->current_track = NULL;
5373     vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
5374     return TRUE;
5375   } else if ( vtl->current_track && event->keyval == GDK_BackSpace ) {
5376     /* undo */
5377     if ( vtl->current_track->trackpoints )
5378     {
5379       GList *last = g_list_last(vtl->current_track->trackpoints);
5380       g_free ( last->data );
5381       vtl->current_track->trackpoints = g_list_remove_link ( vtl->current_track->trackpoints, last );
5382     }
5383     
5384     update_statusbar ( vtl );
5385
5386     vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
5387     return TRUE;
5388   }
5389   return FALSE;
5390 }
5391
5392 static gboolean tool_new_track_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
5393 {
5394   VikTrackpoint *tp;
5395
5396   if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
5397     return FALSE;
5398
5399   if ( event->button == 3 && vtl->current_track )
5400   {
5401     /* undo */
5402     if ( vtl->current_track->trackpoints )
5403     {
5404       GList *last = g_list_last(vtl->current_track->trackpoints);
5405       g_free ( last->data );
5406       vtl->current_track->trackpoints = g_list_remove_link ( vtl->current_track->trackpoints, last );
5407     }
5408     update_statusbar ( vtl );
5409
5410     vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
5411     return TRUE;
5412   }
5413
5414   if ( event->type == GDK_2BUTTON_PRESS )
5415   {
5416     /* subtract last (duplicate from double click) tp then end */
5417     if ( vtl->current_track && vtl->current_track->trackpoints && vtl->ct_x1 == vtl->ct_x2 && vtl->ct_y1 == vtl->ct_y2 )
5418     {
5419       GList *last = g_list_last(vtl->current_track->trackpoints);
5420       g_free ( last->data );
5421       vtl->current_track->trackpoints = g_list_remove_link ( vtl->current_track->trackpoints, last );
5422       /* undo last, then end */
5423       vtl->current_track = NULL;
5424     }
5425     vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
5426     return TRUE;
5427   }
5428
5429   if ( ! vtl->current_track )
5430   {
5431     gchar *name = get_new_unique_sublayer_name(vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, _("Track"));
5432     if ( ( name = a_dialog_new_track ( VIK_GTK_WINDOW_FROM_LAYER(vtl), vtl->tracks, name ) ) )
5433     {
5434       vtl->current_track = vik_track_new();
5435       vtl->current_track->visible = TRUE;
5436       vik_trw_layer_add_track ( vtl, name, vtl->current_track );
5437
5438       /* incase it was created by begin track */
5439       vik_window_enable_layer_tool ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)), VIK_LAYER_TRW, TOOL_CREATE_TRACK );
5440     }
5441     else
5442       return TRUE;
5443   }
5444   tp = vik_trackpoint_new();
5445   vik_viewport_screen_to_coord ( vvp, event->x, event->y, &(tp->coord) );
5446
5447   /* snap to other TP */
5448   if ( event->state & GDK_CONTROL_MASK )
5449   {
5450     VikTrackpoint *other_tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
5451     if ( other_tp )
5452       tp->coord = other_tp->coord;
5453   }
5454
5455   tp->newsegment = FALSE;
5456   tp->has_timestamp = FALSE;
5457   tp->timestamp = 0;
5458   vtl->current_track->trackpoints = g_list_append ( vtl->current_track->trackpoints, tp );
5459   /* Auto attempt to get elevation from DEM data (if it's available) */
5460   vik_track_apply_dem_data_last_trackpoint ( vtl->current_track );
5461
5462   vtl->ct_x1 = vtl->ct_x2;
5463   vtl->ct_y1 = vtl->ct_y2;
5464   vtl->ct_x2 = event->x;
5465   vtl->ct_y2 = event->y;
5466
5467   vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
5468   return TRUE;
5469 }
5470
5471 /*** New waypoint ****/
5472
5473 static gpointer tool_new_waypoint_create ( VikWindow *vw, VikViewport *vvp)
5474 {
5475   return vvp;
5476 }
5477
5478 static gboolean tool_new_waypoint_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
5479 {
5480   VikCoord coord;
5481   if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
5482     return FALSE;
5483   vik_viewport_screen_to_coord ( vvp, event->x, event->y, &coord );
5484   if (vik_trw_layer_new_waypoint ( vtl, VIK_GTK_WINDOW_FROM_LAYER(vtl), &coord ) && VIK_LAYER(vtl)->visible)
5485     vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
5486   return TRUE;
5487 }
5488
5489
5490 /*** Edit trackpoint ****/
5491
5492 static gpointer tool_edit_trackpoint_create ( VikWindow *vw, VikViewport *vvp)
5493 {
5494   tool_ed_t *t = g_new(tool_ed_t, 1);
5495   t->vvp = vvp;
5496   t->holding = FALSE;
5497   return t;
5498 }
5499
5500 static gboolean tool_edit_trackpoint_click ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data )
5501 {
5502   tool_ed_t *t = data;
5503   VikViewport *vvp = t->vvp;
5504   TPSearchParams params;
5505   /* OUTDATED DOCUMENTATION:
5506    find 5 pixel range on each side. then put these UTM, and a pointer
5507    to the winning track name (and maybe the winning track itself), and a
5508    pointer to the winning trackpoint, inside an array or struct. pass 
5509    this along, do a foreach on the tracks which will do a foreach on the 
5510    trackpoints. */
5511   params.vvp = vvp;
5512   params.x = event->x;
5513   params.y = event->y;
5514   params.closest_track_name = NULL;
5515   /* TODO: should get track listitem so we can break it up, make a new track, mess it up, all that. */
5516   params.closest_tp = NULL;
5517
5518   if ( event->button != 1 ) 
5519     return FALSE;
5520
5521   if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
5522     return FALSE;
5523
5524   if ( !vtl->vl.visible || !vtl->tracks_visible )
5525     return FALSE;
5526
5527   if ( vtl->current_tpl )
5528   {
5529     /* 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.) */
5530     VikTrackpoint *tp = VIK_TRACKPOINT(vtl->current_tpl->data);
5531     VikTrack *current_tr = VIK_TRACK(g_hash_table_lookup(vtl->tracks, vtl->current_tp_track_name));
5532     gint x, y;
5533     g_assert ( current_tr );
5534
5535     vik_viewport_coord_to_screen ( vvp, &(tp->coord), &x, &y );
5536
5537     if ( current_tr->visible && 
5538          abs(x - event->x) < TRACKPOINT_SIZE_APPROX &&
5539          abs(y - event->y) < TRACKPOINT_SIZE_APPROX ) {
5540       marker_begin_move ( t, event->x, event->y );
5541       return TRUE;
5542     }
5543
5544     vtl->last_tpl = vtl->current_tpl;
5545     vtl->last_tp_track_name = vtl->current_tp_track_name;
5546   }
5547
5548   g_hash_table_foreach ( vtl->tracks, (GHFunc) track_search_closest_tp, &params);
5549
5550   if ( params.closest_tp )
5551   {
5552     vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, g_hash_table_lookup ( vtl->tracks_iters, params.closest_track_name ), TRUE );
5553     vtl->current_tpl = params.closest_tpl;
5554     vtl->current_tp_track_name = params.closest_track_name;
5555     trw_layer_tpwin_init ( vtl );
5556     set_statusbar_msg_info_trkpt ( vtl, params.closest_tp );
5557     vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
5558     return TRUE;
5559   }
5560
5561   /* these aren't the droids you're looking for */
5562   return FALSE;
5563 }
5564
5565 static gboolean tool_edit_trackpoint_move ( VikTrwLayer *vtl, GdkEventMotion *event, gpointer data )
5566 {
5567   tool_ed_t *t = data;
5568   VikViewport *vvp = t->vvp;
5569
5570   if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
5571     return FALSE;
5572
5573   if ( t->holding )
5574   {
5575     VikCoord new_coord;
5576     vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
5577
5578     /* snap to TP */
5579     if ( event->state & GDK_CONTROL_MASK )
5580     {
5581       VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
5582       if ( tp && tp != vtl->current_tpl->data )
5583         new_coord = tp->coord;
5584     }
5585     //    VIK_TRACKPOINT(vtl->current_tpl->data)->coord = new_coord;
5586     { 
5587       gint x, y;
5588       vik_viewport_coord_to_screen ( vvp, &new_coord, &x, &y );
5589       marker_moveto ( t, x, y );
5590     } 
5591
5592     return TRUE;
5593   }
5594   return FALSE;
5595 }
5596
5597 static gboolean tool_edit_trackpoint_release ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data )
5598 {
5599   tool_ed_t *t = data;
5600   VikViewport *vvp = t->vvp;
5601
5602   if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
5603     return FALSE;
5604   if ( event->button != 1) 
5605     return FALSE;
5606
5607   if ( t->holding ) {
5608     VikCoord new_coord;
5609     vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
5610
5611     /* snap to TP */
5612     if ( event->state & GDK_CONTROL_MASK )
5613     {
5614       VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
5615       if ( tp && tp != vtl->current_tpl->data )
5616         new_coord = tp->coord;
5617     }
5618
5619     VIK_TRACKPOINT(vtl->current_tpl->data)->coord = new_coord;
5620
5621     marker_end_move ( t );
5622
5623     /* diff dist is diff from orig */
5624     if ( vtl->tpwin )
5625       vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, vtl->current_tp_track_name );
5626     /* can't join with itself! */
5627     trw_layer_cancel_last_tp ( vtl );
5628
5629     vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
5630     return TRUE;
5631   }
5632   return FALSE;
5633 }
5634
5635
5636 /*** Route Finder ***/
5637 static gpointer tool_route_finder_create ( VikWindow *vw, VikViewport *vvp)
5638 {
5639   return vvp;
5640 }
5641
5642 static gboolean tool_route_finder_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
5643 {
5644   VikCoord tmp;
5645   if ( !vtl ) return FALSE;
5646   vik_viewport_screen_to_coord ( vvp, event->x, event->y, &tmp );
5647   if ( event->button == 3 && vtl->route_finder_current_track ) {
5648     VikCoord *new_end;
5649     new_end = vik_track_cut_back_to_double_point ( vtl->route_finder_current_track );
5650     if ( new_end ) {
5651       vtl->route_finder_coord = *new_end;
5652       g_free ( new_end );
5653       vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
5654       /* remove last ' to:...' */
5655       if ( vtl->route_finder_current_track->comment ) {
5656         gchar *last_to = strrchr ( vtl->route_finder_current_track->comment, 't' );
5657         if ( last_to && (last_to - vtl->route_finder_current_track->comment > 1) ) {
5658           gchar *new_comment = g_strndup ( vtl->route_finder_current_track->comment,
5659                                            last_to - vtl->route_finder_current_track->comment - 1);
5660           vik_track_set_comment_no_copy ( vtl->route_finder_current_track, new_comment );
5661         }
5662       }
5663     }
5664   }
5665   else if ( vtl->route_finder_started || (event->state & GDK_CONTROL_MASK && vtl->route_finder_current_track) ) {
5666     struct LatLon start, end;
5667     gchar startlat[G_ASCII_DTOSTR_BUF_SIZE], startlon[G_ASCII_DTOSTR_BUF_SIZE];
5668     gchar endlat[G_ASCII_DTOSTR_BUF_SIZE], endlon[G_ASCII_DTOSTR_BUF_SIZE];
5669     gchar *url;
5670
5671     vik_coord_to_latlon ( &(vtl->route_finder_coord), &start );
5672     vik_coord_to_latlon ( &(tmp), &end );
5673     vtl->route_finder_coord = tmp; /* for continuations */
5674
5675     /* these are checked when adding a track from a file (vik_trw_layer_filein_add_track) */
5676     if ( event->state & GDK_CONTROL_MASK && vtl->route_finder_current_track ) {
5677       vtl->route_finder_append = TRUE;  // merge tracks. keep started true.
5678     } else {
5679       vtl->route_finder_check_added_track = TRUE;
5680       vtl->route_finder_started = FALSE;
5681     }
5682
5683     url = g_strdup_printf(GOOGLE_DIRECTIONS_STRING,
5684                           g_ascii_dtostr (startlat, G_ASCII_DTOSTR_BUF_SIZE, (gdouble) start.lat),
5685                           g_ascii_dtostr (startlon, G_ASCII_DTOSTR_BUF_SIZE, (gdouble) start.lon),
5686                           g_ascii_dtostr (endlat, G_ASCII_DTOSTR_BUF_SIZE, (gdouble) end.lat),
5687                           g_ascii_dtostr (endlon, G_ASCII_DTOSTR_BUF_SIZE, (gdouble) end.lon));
5688     a_babel_convert_from_url ( vtl, url, "kml", NULL, NULL );
5689     g_free ( url );
5690
5691     /* see if anything was done -- a track was added or appended to */
5692     if ( vtl->route_finder_check_added_track && vtl->route_finder_added_track_name ) {
5693       VikTrack *tr;
5694
5695       tr = g_hash_table_lookup ( vtl->tracks, vtl->route_finder_added_track_name );
5696
5697       if ( tr )
5698         vik_track_set_comment_no_copy ( tr, g_strdup_printf("from: %f,%f to: %f,%f", start.lat, start.lon, end.lat, end.lon ) );
5699  
5700       vtl->route_finder_current_track = tr;
5701
5702       g_free ( vtl->route_finder_added_track_name );
5703       vtl->route_finder_added_track_name = NULL;
5704     } else if ( vtl->route_finder_append == FALSE && vtl->route_finder_current_track ) {
5705       /* route_finder_append was originally TRUE but set to FALSE by filein_add_track */
5706       gchar *new_comment = g_strdup_printf("%s to: %f,%f", vtl->route_finder_current_track->comment, end.lat, end.lon );
5707       vik_track_set_comment_no_copy ( vtl->route_finder_current_track, new_comment );
5708     }
5709     vtl->route_finder_check_added_track = FALSE;
5710     vtl->route_finder_append = FALSE;
5711
5712     vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
5713   } else {
5714     vtl->route_finder_started = TRUE;
5715     vtl->route_finder_coord = tmp;
5716     vtl->route_finder_current_track = NULL;
5717   }
5718   return TRUE;
5719 }
5720
5721 /*** Show picture ****/
5722
5723 static gpointer tool_show_picture_create ( VikWindow *vw, VikViewport *vvp)
5724 {
5725   return vvp;
5726 }
5727
5728 /* Params are: vvp, event, last match found or NULL */
5729 static void tool_show_picture_wp ( char *name, VikWaypoint *wp, gpointer params[3] )
5730 {
5731   if ( wp->image && wp->visible )
5732   {
5733     gint x, y, slackx, slacky;
5734     GdkEventButton *event = (GdkEventButton *) params[1];
5735
5736     vik_viewport_coord_to_screen ( VIK_VIEWPORT(params[0]), &(wp->coord), &x, &y );
5737     slackx = wp->image_width / 2;
5738     slacky = wp->image_height / 2;
5739     if (    x <= event->x + slackx && x >= event->x - slackx
5740          && y <= event->y + slacky && y >= event->y - slacky )
5741     {
5742       params[2] = wp->image; /* we've found a match. however continue searching
5743                               * since we want to find the last match -- that
5744                               * is, the match that was drawn last. */
5745     }
5746   }
5747 }
5748
5749 static void trw_layer_show_picture ( gpointer pass_along[6] )
5750 {
5751   /* thanks to the Gaim people for showing me ShellExecute and g_spawn_command_line_async */
5752 #ifdef WINDOWS
5753   ShellExecute(NULL, NULL, (char *) pass_along[2], NULL, ".\\", 0);
5754 #else /* WINDOWS */
5755   GError *err = NULL;
5756   gchar *quoted_file = g_shell_quote ( (gchar *) pass_along[5] );
5757   gchar *cmd = g_strdup_printf ( "%s %s", a_vik_get_image_viewer(), quoted_file );
5758   g_free ( quoted_file );
5759   if ( ! g_spawn_command_line_async ( cmd, &err ) )
5760     {
5761       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() );
5762       g_error_free ( err );
5763     }
5764   g_free ( cmd );
5765 #endif /* WINDOWS */
5766 }
5767
5768 static gboolean tool_show_picture_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
5769 {
5770   gpointer params[3] = { vvp, event, NULL };
5771   if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
5772     return FALSE;
5773   g_hash_table_foreach ( vtl->waypoints, (GHFunc) tool_show_picture_wp, params );
5774   if ( params[2] )
5775   {
5776     static gpointer pass_along[6];
5777     pass_along[0] = vtl;
5778     pass_along[5] = params[2];
5779     trw_layer_show_picture ( pass_along );
5780     return TRUE; /* found a match */
5781   }
5782   else
5783     return FALSE; /* go through other layers, searching for a match */
5784 }
5785
5786 /***************************************************************************
5787  ** End tool code 
5788  ***************************************************************************/
5789
5790
5791
5792
5793
5794 static void image_wp_make_list ( char *name, VikWaypoint *wp, GSList **pics )
5795 {
5796   if ( wp->image && ( ! a_thumbnails_exists ( wp->image ) ) )
5797     *pics = g_slist_append ( *pics, (gpointer) g_strdup ( wp->image ) );
5798 }
5799
5800 /* Structure for thumbnail creating data used in the background thread */
5801 typedef struct {
5802   VikTrwLayer *vtl; // Layer needed for redrawing
5803   GSList *pics;     // Image list
5804 } thumbnail_create_thread_data;
5805
5806 static int create_thumbnails_thread ( thumbnail_create_thread_data *tctd, gpointer threaddata )
5807 {
5808   guint total = g_slist_length(tctd->pics), done = 0;
5809   while ( tctd->pics )
5810   {
5811     a_thumbnails_create ( (gchar *) tctd->pics->data );
5812     int result = a_background_thread_progress ( threaddata, ((gdouble) ++done) / total );
5813     if ( result != 0 )
5814       return -1; /* Abort thread */
5815
5816     tctd->pics = tctd->pics->next;
5817   }
5818
5819   // Redraw to show the thumbnails as they are now created
5820   if ( IS_VIK_LAYER(tctd->vtl) )
5821     vik_layer_emit_update ( VIK_LAYER(tctd->vtl), TRUE ); // Yes update from background thread
5822
5823   return 0;
5824 }
5825
5826 static void thumbnail_create_thread_free ( thumbnail_create_thread_data *tctd )
5827 {
5828   while ( tctd->pics )
5829   {
5830     g_free ( tctd->pics->data );
5831     tctd->pics = g_slist_delete_link ( tctd->pics, tctd->pics );
5832   }
5833   g_free ( tctd );
5834 }
5835
5836 void trw_layer_verify_thumbnails ( VikTrwLayer *vtl, GtkWidget *vp )
5837 {
5838   if ( ! vtl->has_verified_thumbnails )
5839   {
5840     GSList *pics = NULL;
5841     g_hash_table_foreach ( vtl->waypoints, (GHFunc) image_wp_make_list, &pics );
5842     if ( pics )
5843     {
5844       gint len = g_slist_length ( pics );
5845       gchar *tmp = g_strdup_printf ( _("Creating %d Image Thumbnails..."), len );
5846       thumbnail_create_thread_data *tctd = g_malloc ( sizeof(thumbnail_create_thread_data) );
5847       tctd->vtl = vtl;
5848       tctd->pics = pics;
5849       a_background_thread ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
5850                             tmp,
5851                             (vik_thr_func) create_thumbnails_thread,
5852                             tctd,
5853                             (vik_thr_free_func) thumbnail_create_thread_free,
5854                             NULL,
5855                             len );
5856       g_free ( tmp );
5857     }
5858   }
5859 }
5860
5861 VikCoordMode vik_trw_layer_get_coord_mode ( VikTrwLayer *vtl )
5862 {
5863   return vtl->coord_mode;
5864 }
5865
5866
5867
5868 static void waypoint_convert ( const gchar *name, VikWaypoint *wp, VikCoordMode *dest_mode )
5869 {
5870   vik_coord_convert ( &(wp->coord), *dest_mode );
5871 }
5872
5873 static void track_convert ( const gchar *name, VikTrack *tr, VikCoordMode *dest_mode )
5874 {
5875   vik_track_convert ( tr, *dest_mode );
5876 }
5877
5878 static void trw_layer_change_coord_mode ( VikTrwLayer *vtl, VikCoordMode dest_mode )
5879 {
5880   if ( vtl->coord_mode != dest_mode )
5881   {
5882     vtl->coord_mode = dest_mode;
5883     g_hash_table_foreach ( vtl->waypoints, (GHFunc) waypoint_convert, &dest_mode );
5884     g_hash_table_foreach ( vtl->tracks, (GHFunc) track_convert, &dest_mode );
5885   }
5886 }
5887
5888 VikWaypoint *vik_trw_layer_get_waypoint ( VikTrwLayer *vtl, gchar *name )
5889 {
5890   return g_hash_table_lookup ( vtl->waypoints, name );
5891 }
5892
5893 VikTrack *vik_trw_layer_get_track ( VikTrwLayer *vtl, const gchar *name )
5894 {
5895   return g_hash_table_lookup ( vtl->tracks, name );
5896 }
5897
5898 static void trw_layer_set_menu_selection ( VikTrwLayer *vtl, guint16 selection )
5899 {
5900   vtl->menu_selection = selection;
5901 }
5902
5903 static guint16 trw_layer_get_menu_selection ( VikTrwLayer *vtl )
5904 {
5905   return (vtl->menu_selection);
5906 }
5907
5908 /* ----------- Downloading maps along tracks --------------- */
5909
5910 static int get_download_area_width(VikViewport *vvp, gdouble zoom_level, struct LatLon *wh)
5911 {
5912   /* TODO: calculating based on current size of viewport */
5913   const gdouble w_at_zoom_0_125 = 0.0013;
5914   const gdouble h_at_zoom_0_125 = 0.0011;
5915   gdouble zoom_factor = zoom_level/0.125;
5916
5917   wh->lat = h_at_zoom_0_125 * zoom_factor;
5918   wh->lon = w_at_zoom_0_125 * zoom_factor;
5919
5920   return 0;   /* all OK */
5921 }
5922
5923 static VikCoord *get_next_coord(VikCoord *from, VikCoord *to, struct LatLon *dist, gdouble gradient)
5924 {
5925   if ((dist->lon >= ABS(to->east_west - from->east_west)) &&
5926       (dist->lat >= ABS(to->north_south - from->north_south)))
5927     return NULL;
5928
5929   VikCoord *coord = g_malloc(sizeof(VikCoord));
5930   coord->mode = VIK_COORD_LATLON;
5931
5932   if (ABS(gradient) < 1) {
5933     if (from->east_west > to->east_west)
5934       coord->east_west = from->east_west - dist->lon;
5935     else
5936       coord->east_west = from->east_west + dist->lon;
5937     coord->north_south = gradient * (coord->east_west - from->east_west) + from->north_south;
5938   } else {
5939     if (from->north_south > to->north_south)
5940       coord->north_south = from->north_south - dist->lat;
5941     else
5942       coord->north_south = from->north_south + dist->lat;
5943     coord->east_west = (1/gradient) * (coord->north_south - from->north_south) + from->north_south;
5944   }
5945
5946   return coord;
5947 }
5948
5949 static GList *add_fillins(GList *list, VikCoord *from, VikCoord *to, struct LatLon *dist)
5950 {
5951   /* TODO: handle virtical track (to->east_west - from->east_west == 0) */
5952   gdouble gradient = (to->north_south - from->north_south)/(to->east_west - from->east_west);
5953
5954   VikCoord *next = from;
5955   while (TRUE) {
5956     if ((next = get_next_coord(next, to, dist, gradient)) == NULL)
5957         break;
5958     list = g_list_prepend(list, next);
5959   }
5960
5961   return list;
5962 }
5963
5964 void vik_track_download_map(VikTrack *tr, VikMapsLayer *vml, VikViewport *vvp, gdouble zoom_level)
5965 {
5966   typedef struct _Rect {
5967     VikCoord tl;
5968     VikCoord br;
5969     VikCoord center;
5970   } Rect;
5971 #define GLRECT(iter) ((Rect *)((iter)->data))
5972
5973   struct LatLon wh;
5974   GList *rects_to_download = NULL;
5975   GList *rect_iter;
5976
5977   if (get_download_area_width(vvp, zoom_level, &wh))
5978     return;
5979
5980   GList *iter = tr->trackpoints;
5981   if (!iter)
5982     return;
5983
5984   gboolean new_map = TRUE;
5985   VikCoord *cur_coord, tl, br;
5986   Rect *rect;
5987   while (iter) {
5988     cur_coord = &(VIK_TRACKPOINT(iter->data))->coord;
5989     if (new_map) {
5990       vik_coord_set_area(cur_coord, &wh, &tl, &br);
5991       rect = g_malloc(sizeof(Rect));
5992       rect->tl = tl;
5993       rect->br = br;
5994       rect->center = *cur_coord;
5995       rects_to_download = g_list_prepend(rects_to_download, rect);
5996       new_map = FALSE;
5997       iter = iter->next;
5998       continue;
5999     }
6000     gboolean found = FALSE;
6001     for (rect_iter = rects_to_download; rect_iter; rect_iter = rect_iter->next) {
6002       if (vik_coord_inside(cur_coord, &GLRECT(rect_iter)->tl, &GLRECT(rect_iter)->br)) {
6003         found = TRUE;
6004         break;
6005       }
6006     }
6007     if (found)
6008         iter = iter->next;
6009     else
6010       new_map = TRUE;
6011   }
6012
6013   GList *fillins = NULL;
6014   /* 'fillin' doesn't work in UTM mode - potentially ending up in massive loop continually allocating memory - hence don't do it */
6015   /* seems that ATM the function get_next_coord works only for LATLON */
6016   if ( cur_coord->mode == VIK_COORD_LATLON ) {
6017     /* fill-ins for far apart points */
6018     GList *cur_rect, *next_rect;
6019     for (cur_rect = rects_to_download;
6020          (next_rect = cur_rect->next) != NULL;
6021          cur_rect = cur_rect->next) {
6022       if ((wh.lon < ABS(GLRECT(cur_rect)->center.east_west - GLRECT(next_rect)->center.east_west)) ||
6023           (wh.lat < ABS(GLRECT(cur_rect)->center.north_south - GLRECT(next_rect)->center.north_south))) {
6024         fillins = add_fillins(fillins, &GLRECT(cur_rect)->center, &GLRECT(next_rect)->center, &wh);
6025       }
6026     }
6027   } else
6028     g_message("%s: this feature works only in Mercator mode", __FUNCTION__);
6029
6030   if (fillins) {
6031     GList *iter = fillins;
6032     while (iter) {
6033       cur_coord = (VikCoord *)(iter->data);
6034       vik_coord_set_area(cur_coord, &wh, &tl, &br);
6035       rect = g_malloc(sizeof(Rect));
6036       rect->tl = tl;
6037       rect->br = br;
6038       rect->center = *cur_coord;
6039       rects_to_download = g_list_prepend(rects_to_download, rect);
6040       iter = iter->next;
6041     }
6042   }
6043
6044   for (rect_iter = rects_to_download; rect_iter; rect_iter = rect_iter->next) {
6045     maps_layer_download_section_without_redraw(vml, vvp, &(((Rect *)(rect_iter->data))->tl), &(((Rect *)(rect_iter->data))->br), zoom_level);
6046   }
6047
6048   if (fillins) {
6049     for (iter = fillins; iter; iter = iter->next)
6050       g_free(iter->data);
6051     g_list_free(fillins);
6052   }
6053   if (rects_to_download) {
6054     for (rect_iter = rects_to_download; rect_iter; rect_iter = rect_iter->next)
6055       g_free(rect_iter->data);
6056     g_list_free(rects_to_download);
6057   }
6058 }
6059
6060 static void trw_layer_download_map_along_track_cb ( gpointer pass_along[6] )
6061 {
6062   VikMapsLayer *vml;
6063   gint selected_map, default_map;
6064   gchar *zoomlist[] = {"0.125", "0.25", "0.5", "1", "2", "4", "8", "16", "32", "64", "128", "256", "512", "1024", NULL };
6065   gdouble zoom_vals[] = {0.125, 0.25, 0.5, 1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024};
6066   gint selected_zoom, default_zoom;
6067   int i,j;
6068
6069
6070   VikTrwLayer *vtl = pass_along[0];
6071   VikLayersPanel *vlp = pass_along[1];
6072   VikTrack *tr = (VikTrack *) g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, pass_along[3] );
6073   VikViewport *vvp = vik_window_viewport((VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl)));
6074
6075   GList *vmls = vik_layers_panel_get_all_layers_of_type(vlp, VIK_LAYER_MAPS, TRUE); // Includes hidden map layer types
6076   int num_maps = g_list_length(vmls);
6077
6078   if (!num_maps) {
6079     a_dialog_msg(VIK_GTK_WINDOW_FROM_LAYER(vtl), GTK_MESSAGE_ERROR, _("No map layer in use. Create one first"), NULL);
6080     return;
6081   }
6082
6083   gchar **map_names = g_malloc(1 + num_maps * sizeof(gpointer));
6084   VikMapsLayer **map_layers = g_malloc(1 + num_maps * sizeof(gpointer));
6085
6086   gchar **np = map_names;
6087   VikMapsLayer **lp = map_layers;
6088   for (i = 0; i < num_maps; i++) {
6089     gboolean dup = FALSE;
6090     vml = (VikMapsLayer *)(vmls->data);
6091     for (j = 0; j < i; j++) { /* no duplicate allowed */
6092       if (vik_maps_layer_get_map_type(vml) == vik_maps_layer_get_map_type(map_layers[j])) {
6093         dup = TRUE;
6094         break;
6095       }
6096     }
6097     if (!dup) {
6098       *lp++ = vml;
6099       *np++ = vik_maps_layer_get_map_label(vml);
6100     }
6101     vmls = vmls->next;
6102   }
6103   *lp = NULL;
6104   *np = NULL;
6105   num_maps = lp - map_layers;
6106
6107   for (default_map = 0; default_map < num_maps; default_map++) {
6108     /* TODO: check for parent layer's visibility */
6109     if (VIK_LAYER(map_layers[default_map])->visible)
6110       break;
6111   }
6112   default_map = (default_map == num_maps) ? 0 : default_map;
6113
6114   gdouble cur_zoom = vik_viewport_get_zoom(vvp);
6115   for (default_zoom = 0; default_zoom < sizeof(zoom_vals)/sizeof(gdouble); default_zoom++) {
6116     if (cur_zoom == zoom_vals[default_zoom])
6117       break;
6118   }
6119   default_zoom = (default_zoom == sizeof(zoom_vals)/sizeof(gdouble)) ? sizeof(zoom_vals)/sizeof(gdouble) - 1 : default_zoom;
6120
6121   if (!a_dialog_map_n_zoom(VIK_GTK_WINDOW_FROM_LAYER(vtl), map_names, default_map, zoomlist, default_zoom, &selected_map, &selected_zoom))
6122     goto done;
6123
6124   vik_track_download_map(tr, map_layers[selected_map], vvp, zoom_vals[selected_zoom]);
6125
6126 done:
6127   for (i = 0; i < num_maps; i++)
6128     g_free(map_names[i]);
6129   g_free(map_names);
6130   g_free(map_layers);
6131
6132   g_list_free(vmls);
6133
6134 }
6135
6136 /**** lowest waypoint number calculation ***/
6137 static gint highest_wp_number_name_to_number(const gchar *name) {
6138   if ( strlen(name) == 3 ) {
6139     int n = atoi(name);
6140     if ( n < 100 && name[0] != '0' )
6141       return -1;
6142     if ( n < 10 && name[0] != '0' )
6143       return -1;
6144     return n;
6145   }
6146   return -1;
6147 }
6148
6149
6150 static void highest_wp_number_reset(VikTrwLayer *vtl)
6151 {
6152   vtl->highest_wp_number = -1;
6153 }
6154
6155 static void highest_wp_number_add_wp(VikTrwLayer *vtl, const gchar *new_wp_name)
6156 {
6157   /* if is bigger that top, add it */
6158   gint new_wp_num = highest_wp_number_name_to_number(new_wp_name);
6159   if ( new_wp_num > vtl->highest_wp_number )
6160     vtl->highest_wp_number = new_wp_num;
6161 }
6162
6163 static void highest_wp_number_remove_wp(VikTrwLayer *vtl, const gchar *old_wp_name)
6164 {
6165   /* if wasn't top, do nothing. if was top, count backwards until we find one used */
6166   gint old_wp_num = highest_wp_number_name_to_number(old_wp_name);
6167   if ( vtl->highest_wp_number == old_wp_num ) {
6168     gchar buf[4];
6169     vtl->highest_wp_number --;
6170
6171     g_snprintf(buf,4,"%03d", vtl->highest_wp_number );
6172     /* search down until we find something that *does* exist */
6173
6174     while ( vtl->highest_wp_number > 0 && ! g_hash_table_lookup ( vtl->waypoints, buf ) ) {
6175       vtl->highest_wp_number --;
6176       g_snprintf(buf,4,"%03d", vtl->highest_wp_number );
6177     }
6178   }
6179 }
6180
6181 /* get lowest unused number */
6182 static gchar *highest_wp_number_get(VikTrwLayer *vtl)
6183 {
6184   gchar buf[4];
6185   if ( vtl->highest_wp_number < 0 || vtl->highest_wp_number >= 999 )
6186     return NULL;
6187   g_snprintf(buf,4,"%03d", vtl->highest_wp_number+1 );
6188   return g_strdup(buf);
6189 }