]> git.street.me.uk Git - andy/viking.git/blob - src/viktrwlayer.c
Add function to return an average moving speed for a track.
[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 #ifdef VIK_CONFIG_ALPHABETIZED_TRW
3423 /**
3424  * comparison function which can be used to sort tracks or waypoints by name
3425  */
3426 static gint sort_alphabetically (gconstpointer a, gconstpointer b, gpointer user_data)
3427 {
3428   const gchar* namea = (const gchar*) a;
3429   const gchar* nameb = (const gchar*) b;
3430   if ( namea == NULL || nameb == NULL)
3431     return 0;
3432   else
3433     // Same sort method as used in the vik_treeview_*_alphabetize functions
3434     return strcmp ( namea, nameb );
3435 }
3436 #endif
3437
3438 /**
3439  * Attempt to merge selected track with other tracks specified by the user
3440  * Tracks to merge with must be of the same 'type' as the selected track -
3441  *  either all with timestamps, or all without timestamps
3442  */
3443 static void trw_layer_merge_with_other ( gpointer pass_along[6] )
3444 {
3445   VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
3446   gchar *orig_track_name = pass_along[3];
3447   GList *other_tracks = NULL;
3448   VikTrack *track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, orig_track_name );
3449
3450   if ( !track->trackpoints )
3451     return;
3452
3453   twt_udata udata;
3454   udata.result = &other_tracks;
3455   udata.exclude = track->trackpoints;
3456   // Allow merging with 'similar' time type time tracks
3457   // i.e. either those times, or those without
3458   udata.with_timestamps = (VIK_TRACKPOINT(track->trackpoints->data)->has_timestamp);
3459
3460   g_hash_table_foreach(vtl->tracks, find_tracks_with_timestamp_type, (gpointer)&udata);
3461   other_tracks = g_list_reverse(other_tracks);
3462
3463   if ( !other_tracks ) {
3464     if ( udata.with_timestamps )
3465       a_dialog_error_msg(VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Failed. No other tracks with timestamps in this layer found"));
3466     else
3467       a_dialog_error_msg(VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Failed. No other tracks without timestamps in this layer found"));
3468     return;
3469   }
3470
3471 #ifdef VIK_CONFIG_ALPHABETIZED_TRW
3472   // Sort alphabetically for user presentation
3473   other_tracks = g_list_sort_with_data (other_tracks, sort_alphabetically, NULL);
3474 #endif
3475
3476   GList *merge_list = a_dialog_select_from_list(VIK_GTK_WINDOW_FROM_LAYER(vtl),
3477       other_tracks, TRUE,
3478       _("Merge with..."), _("Select track to merge with"));
3479   g_list_free(other_tracks);
3480
3481   if (merge_list)
3482   {
3483     GList *l;
3484     for (l = merge_list; l != NULL; l = g_list_next(l)) {
3485       VikTrack *merge_track = (VikTrack *) g_hash_table_lookup (vtl->tracks, l->data );
3486       if (merge_track) {
3487         track->trackpoints = g_list_concat(track->trackpoints, merge_track->trackpoints);
3488         merge_track->trackpoints = NULL;
3489         vik_trw_layer_delete_track(vtl, l->data);
3490         track->trackpoints = g_list_sort(track->trackpoints, trackpoint_compare);
3491       }
3492     }
3493     /* TODO: free data before free merge_list */
3494     for (l = merge_list; l != NULL; l = g_list_next(l))
3495       g_free(l->data);
3496     g_list_free(merge_list);
3497     vik_layer_emit_update( VIK_LAYER(vtl), FALSE );
3498   }
3499 }
3500
3501 /* merge by time routine */
3502 static void trw_layer_merge_by_timestamp ( gpointer pass_along[6] )
3503 {
3504   VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
3505   gchar *orig_track_name = strdup(pass_along[3]);
3506
3507   //time_t t1, t2;
3508   GList *nearby_tracks;
3509   VikTrack *track;
3510   GList *trps;
3511   static  guint thr = 1;
3512   guint track_count = 0;
3513
3514   GList *tracks_with_timestamp = NULL;
3515   track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, orig_track_name );
3516   if (track->trackpoints &&
3517       !VIK_TRACKPOINT(track->trackpoints->data)->has_timestamp) {
3518     a_dialog_error_msg(VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Failed. This track does not have timestamp"));
3519     free(orig_track_name);
3520     return;
3521   }
3522
3523   twt_udata udata;
3524   udata.result = &tracks_with_timestamp;
3525   udata.exclude = track->trackpoints;
3526   udata.with_timestamps = TRUE;
3527   g_hash_table_foreach(vtl->tracks, find_tracks_with_timestamp_type, (gpointer)&udata);
3528   tracks_with_timestamp = g_list_reverse(tracks_with_timestamp);
3529
3530   if (!tracks_with_timestamp) {
3531     a_dialog_error_msg(VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Failed. No other track in this layer has timestamp"));
3532     free(orig_track_name);
3533     return;
3534   }
3535   g_list_free(tracks_with_timestamp);
3536
3537   if (!a_dialog_time_threshold(VIK_GTK_WINDOW_FROM_LAYER(vtl), 
3538                                _("Merge Threshold..."), 
3539                                _("Merge when time between tracks less than:"), 
3540                                &thr)) {
3541     free(orig_track_name);
3542     return;
3543   }
3544
3545   /* merge tracks until we can't */
3546   nearby_tracks = NULL;
3547   do {
3548     gpointer params[3];
3549
3550     track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, orig_track_name );
3551     trps = track->trackpoints;
3552     if ( !trps )
3553       return;
3554
3555
3556     if (nearby_tracks) {
3557       g_list_free(nearby_tracks);
3558       nearby_tracks = NULL;
3559     }
3560
3561     //t1 = ((VikTrackpoint *)trps->data)->timestamp;
3562     //t2 = ((VikTrackpoint *)g_list_last(trps)->data)->timestamp;
3563     
3564     /*    g_print("Original track times: %d and %d\n", t1, t2);  */
3565     params[0] = &nearby_tracks;
3566     params[1] = trps;
3567     params[2] = GUINT_TO_POINTER (thr);
3568
3569     /* get a list of adjacent-in-time tracks */
3570     g_hash_table_foreach(vtl->tracks, find_nearby_track, (gpointer)params);
3571
3572     /* add original track */
3573     nearby_tracks = g_list_prepend(nearby_tracks, orig_track_name);
3574
3575     /* merge them */
3576     { 
3577 #define get_track(x) VIK_TRACK(g_hash_table_lookup(vtl->tracks, (gchar *)((x)->data)))
3578 #define get_first_trackpoint(x) VIK_TRACKPOINT(get_track(x)->trackpoints->data)
3579 #define get_last_trackpoint(x) VIK_TRACKPOINT(g_list_last(get_track(x)->trackpoints)->data)
3580       GList *l = nearby_tracks;
3581       VikTrack *tr = vik_track_new();
3582       tr->visible = track->visible;
3583       track_count = 0;
3584       while (l) {
3585         /*
3586         time_t t1, t2;
3587         t1 = get_first_trackpoint(l)->timestamp;
3588         t2 = get_last_trackpoint(l)->timestamp;
3589         g_print("     %20s: track %d - %d\n", (char *)l->data, (int)t1, (int)t2);
3590         */
3591
3592
3593         /* remove trackpoints from merged track, delete track */
3594         tr->trackpoints = g_list_concat(tr->trackpoints, get_track(l)->trackpoints);
3595         get_track(l)->trackpoints = NULL;
3596         vik_trw_layer_delete_track(vtl, l->data);
3597
3598         track_count ++;
3599         l = g_list_next(l);
3600       }
3601       tr->trackpoints = g_list_sort(tr->trackpoints, trackpoint_compare);
3602       vik_trw_layer_add_track(vtl, strdup(orig_track_name), tr);
3603
3604 #undef get_first_trackpoint
3605 #undef get_last_trackpoint
3606 #undef get_track
3607     }
3608   } while (track_count > 1);
3609   g_list_free(nearby_tracks);
3610   free(orig_track_name);
3611   vik_layer_emit_update( VIK_LAYER(vtl), FALSE );
3612 }
3613
3614 /* split by time routine */
3615 static void trw_layer_split_by_timestamp ( gpointer pass_along[6] )
3616 {
3617   VikTrack *track = (VikTrack *) g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, pass_along[3] );
3618   GList *trps = track->trackpoints;
3619   GList *iter;
3620   GList *newlists = NULL;
3621   GList *newtps = NULL;
3622   guint i;
3623   static guint thr = 1;
3624
3625   time_t ts, prev_ts;
3626
3627   if ( !trps )
3628     return;
3629
3630   if (!a_dialog_time_threshold(VIK_GTK_WINDOW_FROM_LAYER(pass_along[0]), 
3631                                _("Split Threshold..."), 
3632                                _("Split when time between trackpoints exceeds:"), 
3633                                &thr)) {
3634     return;
3635   }
3636
3637   /* iterate through trackpoints, and copy them into new lists without touching original list */
3638   prev_ts = VIK_TRACKPOINT(trps->data)->timestamp;
3639   iter = trps;
3640
3641   while (iter) {
3642     ts = VIK_TRACKPOINT(iter->data)->timestamp;
3643     if (ts < prev_ts) {
3644       g_print("panic: ts < prev_ts: this should never happen!\n");
3645       return;
3646     }
3647     if (ts - prev_ts > thr*60) {
3648       /* flush accumulated trackpoints into new list */
3649       newlists = g_list_append(newlists, g_list_reverse(newtps));
3650       newtps = NULL;
3651     }
3652
3653     /* accumulate trackpoint copies in newtps, in reverse order */
3654     newtps = g_list_prepend(newtps, vik_trackpoint_copy(VIK_TRACKPOINT(iter->data)));
3655     prev_ts = ts;
3656     iter = g_list_next(iter);
3657   }
3658   if (newtps) {
3659       newlists = g_list_append(newlists, g_list_reverse(newtps));
3660   }
3661
3662   /* put lists of trackpoints into tracks */
3663   iter = newlists;
3664   i = 1;
3665   // Only bother updating if the split results in new tracks
3666   if (g_list_length (newlists) > 1) {
3667     while (iter) {
3668       gchar *new_tr_name;
3669       VikTrack *tr;
3670
3671       tr = vik_track_new();
3672       tr->visible = track->visible;
3673       tr->trackpoints = (GList *)(iter->data);
3674
3675       new_tr_name = g_strdup_printf("%s #%d", (gchar *) pass_along[3], i++);
3676       vik_trw_layer_add_track(VIK_TRW_LAYER(pass_along[0]), new_tr_name, tr);
3677       /*    g_print("adding track %s, times %d - %d\n", new_tr_name, VIK_TRACKPOINT(tr->trackpoints->data)->timestamp,
3678           VIK_TRACKPOINT(g_list_last(tr->trackpoints)->data)->timestamp);*/
3679
3680       iter = g_list_next(iter);
3681     }
3682     vik_trw_layer_delete_track(VIK_TRW_LAYER(pass_along[0]), (gchar *)pass_along[3]);
3683     vik_layer_emit_update(VIK_LAYER(pass_along[0]), FALSE);
3684   }
3685   g_list_free(newlists);
3686 }
3687
3688 /**
3689  * Split a track by the number of points as specified by the user
3690  */
3691 static void trw_layer_split_by_n_points ( gpointer pass_along[6] )
3692 {
3693   VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
3694   VikTrack *track = (VikTrack *)g_hash_table_lookup ( vtl->tracks, pass_along[3] );
3695
3696   // Check valid track
3697   GList *trps = track->trackpoints;
3698   if ( !trps )
3699     return;
3700
3701   gint points = a_dialog_get_positive_number(VIK_GTK_WINDOW_FROM_LAYER(pass_along[0]),
3702                                              _("Split Every Nth Point"),
3703                                              _("Split on every Nth point:"),
3704                                              250,   // Default value as per typical limited track capacity of various GPS devices
3705                                              2,     // Min
3706                                              65536, // Max
3707                                              5);    // Step
3708   // Was a valid number returned?
3709   if (!points)
3710     return;
3711
3712   // Now split...
3713   GList *iter;
3714   GList *newlists = NULL;
3715   GList *newtps = NULL;
3716   gint count = 0;
3717   iter = trps;
3718
3719   while (iter) {
3720     /* accumulate trackpoint copies in newtps, in reverse order */
3721     newtps = g_list_prepend(newtps, vik_trackpoint_copy(VIK_TRACKPOINT(iter->data)));
3722     count++;
3723     if (count >= points) {
3724       /* flush accumulated trackpoints into new list */
3725       newlists = g_list_append(newlists, g_list_reverse(newtps));
3726       newtps = NULL;
3727       count = 0;
3728     }
3729     iter = g_list_next(iter);
3730   }
3731
3732   // If there is a remaining chunk put that into the new split list
3733   // This may well be the whole track if no split points were encountered
3734   if (newtps) {
3735       newlists = g_list_append(newlists, g_list_reverse(newtps));
3736   }
3737
3738   /* put lists of trackpoints into tracks */
3739   iter = newlists;
3740   guint i = 1;
3741   // Only bother updating if the split results in new tracks
3742   if (g_list_length (newlists) > 1) {
3743     while (iter) {
3744       gchar *new_tr_name;
3745       VikTrack *tr;
3746
3747       tr = vik_track_new();
3748       tr->visible = track->visible;
3749       tr->trackpoints = (GList *)(iter->data);
3750
3751       new_tr_name = g_strdup_printf("%s #%d", (gchar *) pass_along[3], i++);
3752       vik_trw_layer_add_track(VIK_TRW_LAYER(pass_along[0]), new_tr_name, tr);
3753
3754       iter = g_list_next(iter);
3755     }
3756     // Remove original track and then update the display
3757     vik_trw_layer_delete_track(VIK_TRW_LAYER(pass_along[0]), (gchar *)pass_along[3]);
3758     vik_layer_emit_update(VIK_LAYER(pass_along[0]), FALSE);
3759   }
3760   g_list_free(newlists);
3761 }
3762
3763 /* end of split/merge routines */
3764
3765 /**
3766  * Reverse a track
3767  */
3768 static void trw_layer_reverse ( gpointer pass_along[6] )
3769 {
3770   VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
3771   VikTrack *track = (VikTrack *)g_hash_table_lookup ( vtl->tracks, pass_along[3] );
3772
3773   // Check valid track
3774   GList *trps = track->trackpoints;
3775   if ( !trps )
3776     return;
3777
3778   vik_track_reverse ( track );
3779  
3780   vik_layer_emit_update ( VIK_LAYER(pass_along[0]), FALSE );
3781 }
3782
3783 /**
3784  * Similar to trw_layer_enum_item, but this uses a sorted method
3785  */
3786 static void trw_layer_sorted_name_list(gpointer key, gpointer value, gpointer udata)
3787 {
3788   GList **list = (GList**)udata;
3789   //*list = g_list_prepend(*all, key); //unsorted method
3790   // Sort named list alphabetically
3791   *list = g_list_insert_sorted_with_data (*list, key, sort_alphabetically, NULL);
3792 }
3793
3794 /**
3795  *
3796  */
3797 static void trw_layer_delete_tracks_from_selection ( gpointer lav[2] )
3798 {
3799   VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
3800   GList *all = NULL;
3801   // Sort list alphabetically for better presentation
3802   g_hash_table_foreach(vtl->tracks, trw_layer_sorted_name_list, &all);
3803
3804   if ( ! all ) {
3805     a_dialog_error_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), _("No tracks found"));
3806     return;
3807   }
3808
3809   // Get list of items to delete from the user
3810   GList *delete_list = a_dialog_select_from_list(VIK_GTK_WINDOW_FROM_LAYER(vtl),
3811                                                  all,
3812                                                  TRUE,
3813                                                  _("Delete Selection"),
3814                                                  _("Select tracks to delete"));
3815   g_list_free(all);
3816
3817   // Delete requested tracks
3818   // since specificly requested, IMHO no need for extra confirmation
3819   if ( delete_list ) {
3820     GList *l;
3821     for (l = delete_list; l != NULL; l = g_list_next(l)) {
3822       vik_trw_layer_delete_track(vtl, l->data);
3823     }
3824     g_list_free(delete_list);
3825     vik_layer_emit_update( VIK_LAYER(vtl), FALSE );
3826   }
3827 }
3828
3829 /**
3830  *
3831  */
3832 static void trw_layer_delete_waypoints_from_selection ( gpointer lav[2] )
3833 {
3834   VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
3835   GList *all = NULL;
3836
3837   // Sort list alphabetically for better presentation
3838   g_hash_table_foreach ( vtl->waypoints, trw_layer_sorted_name_list, &all);
3839   if ( ! all ) {
3840     a_dialog_error_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), _("No waypoints found"));
3841     return;
3842   }
3843
3844   all = g_list_sort_with_data(all, sort_alphabetically, NULL);
3845
3846   // Get list of items to delete from the user
3847   GList *delete_list = a_dialog_select_from_list(VIK_GTK_WINDOW_FROM_LAYER(vtl),
3848                                                  all,
3849                                                  TRUE,
3850                                                  _("Delete Selection"),
3851                                                  _("Select waypoints to delete"));
3852   g_list_free(all);
3853
3854   // Delete requested waypoints
3855   // since specificly requested, IMHO no need for extra confirmation
3856   if ( delete_list ) {
3857     GList *l;
3858     for (l = delete_list; l != NULL; l = g_list_next(l)) {
3859       vik_trw_layer_delete_waypoint(vtl, l->data);
3860     }
3861     g_list_free(delete_list);
3862     vik_layer_emit_update( VIK_LAYER(vtl), FALSE );
3863   }
3864
3865 }
3866
3867 static void trw_layer_goto_waypoint ( gpointer pass_along[6] )
3868 {
3869   VikWaypoint *wp = g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->waypoints, pass_along[3] );
3870   if ( wp )
3871     goto_coord ( pass_along[1], pass_along[0], pass_along[5], &(wp->coord) );
3872 }
3873
3874 static void trw_layer_waypoint_gc_webpage ( gpointer pass_along[6] )
3875 {
3876   gchar *webpage = g_strdup_printf("http://www.geocaching.com/seek/cache_details.aspx?wp=%s", (gchar *) pass_along[3] );
3877   open_url(VIK_GTK_WINDOW_FROM_LAYER(VIK_LAYER(pass_along[0])), webpage);
3878   g_free ( webpage );
3879 }
3880
3881 static const gchar* trw_layer_sublayer_rename_request ( VikTrwLayer *l, const gchar *newname, gpointer vlp, gint subtype, gpointer sublayer, GtkTreeIter *iter )
3882 {
3883   if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT )
3884   {
3885     gchar *rv;
3886     VikWaypoint *wp;
3887
3888     if (strcmp(newname, sublayer) == 0 )
3889       return NULL;
3890
3891     if (strcasecmp(newname, sublayer)) { /* Not just changing case */
3892       if (g_hash_table_lookup( l->waypoints, newname))
3893       {
3894         a_dialog_error_msg ( VIK_GTK_WINDOW_FROM_LAYER(l), _("Waypoint Already Exists") );
3895         return NULL;
3896       }
3897     }
3898
3899     iter = g_hash_table_lookup ( l->waypoints_iters, sublayer );
3900     g_hash_table_steal ( l->waypoints_iters, sublayer );
3901
3902     wp = vik_waypoint_copy ( VIK_WAYPOINT(g_hash_table_lookup ( l->waypoints, sublayer )) );
3903     highest_wp_number_remove_wp(l, sublayer);
3904     g_hash_table_remove ( l->waypoints, sublayer );
3905
3906     rv = g_strdup(newname);
3907
3908     vik_treeview_item_set_pointer ( VIK_LAYER(l)->vt, iter, rv );
3909
3910     highest_wp_number_add_wp(l, rv);
3911     g_hash_table_insert ( l->waypoints, rv, wp );
3912     g_hash_table_insert ( l->waypoints_iters, rv, iter );
3913
3914     /* it hasn't been updated yet so we pass new name */
3915 #ifdef VIK_CONFIG_ALPHABETIZED_TRW
3916     vik_treeview_sublayer_realphabetize ( VIK_LAYER(l)->vt, iter, rv );
3917 #endif
3918
3919     vik_layers_panel_emit_update ( VIK_LAYERS_PANEL(vlp) );
3920     return rv;
3921   }
3922   if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
3923   {
3924     gchar *rv;
3925     VikTrack *tr;
3926     GtkTreeIter *iter;
3927     gchar *orig_key;
3928
3929     if (strcmp(newname, sublayer) == 0)
3930       return NULL;
3931
3932     if (strcasecmp(newname, sublayer)) { /* Not just changing case */
3933       if (g_hash_table_lookup( l->tracks, newname))
3934       {
3935         a_dialog_error_msg ( VIK_GTK_WINDOW_FROM_LAYER(l), _("Track Already Exists") );
3936         return NULL;
3937       }
3938     }
3939
3940     g_hash_table_lookup_extended ( l->tracks, sublayer, (void *)&orig_key, (void *)&tr );
3941     g_hash_table_steal ( l->tracks, sublayer );
3942
3943     iter = g_hash_table_lookup ( l->tracks_iters, sublayer );
3944     g_hash_table_steal ( l->tracks_iters, sublayer );
3945
3946     rv = g_strdup(newname);
3947
3948     vik_treeview_item_set_pointer ( VIK_LAYER(l)->vt, iter, rv );
3949
3950     g_hash_table_insert ( l->tracks, rv, tr );
3951     g_hash_table_insert ( l->tracks_iters, rv, iter );
3952
3953     /* don't forget about current_tp_track_name, update that too */
3954     if ( l->current_tp_track_name && g_strcasecmp(orig_key,l->current_tp_track_name) == 0 )
3955     {
3956       l->current_tp_track_name = rv;
3957       if ( l->tpwin )
3958         vik_trw_layer_tpwin_set_track_name ( l->tpwin, rv );
3959     }
3960     else if ( l->last_tp_track_name && g_strcasecmp(orig_key,l->last_tp_track_name) == 0 )
3961       l->last_tp_track_name = rv;
3962
3963     g_free ( orig_key );
3964
3965 #ifdef VIK_CONFIG_ALPHABETIZED_TRW
3966     vik_treeview_sublayer_realphabetize ( VIK_LAYER(l)->vt, iter, rv );
3967 #endif
3968
3969     vik_layers_panel_emit_update ( VIK_LAYERS_PANEL(vlp) );
3970     return rv;
3971   }
3972   return NULL;
3973 }
3974
3975 static gboolean is_valid_geocache_name ( gchar *str )
3976 {
3977   gint len = strlen ( str );
3978   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]));
3979 }
3980
3981 static void trw_layer_track_use_with_filter ( gpointer pass_along[6] )
3982 {
3983   gchar *track_name = (gchar *) pass_along[3];
3984   VikTrack *tr = g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, track_name );
3985   a_acquire_set_filter_track ( tr, track_name );
3986 }
3987
3988 static gboolean is_valid_google_route ( VikTrwLayer *vtl, const gchar *track_name )
3989 {
3990   VikTrack *tr = g_hash_table_lookup ( vtl->tracks, track_name );
3991   return ( tr && tr->comment && strlen(tr->comment) > 7 && !strncmp(tr->comment, "from:", 5) );
3992 }
3993
3994 static void trw_layer_track_google_route_webpage ( gpointer pass_along[6] )
3995 {
3996   gchar *track_name = (gchar *) pass_along[3];
3997   VikTrack *tr = g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, track_name );
3998   if ( tr ) {
3999     gchar *escaped = uri_escape ( tr->comment );
4000     gchar *webpage = g_strdup_printf("http://maps.google.com/maps?f=q&hl=en&q=%s", escaped );
4001     open_url(VIK_GTK_WINDOW_FROM_LAYER(VIK_LAYER(pass_along[0])), webpage);
4002     g_free ( escaped );
4003     g_free ( webpage );
4004   }
4005 }
4006
4007 /* vlp can be NULL if necessary - i.e. right-click from a tool */
4008 /* viewpoint is now available instead */
4009 static gboolean trw_layer_sublayer_add_menu_items ( VikTrwLayer *l, GtkMenu *menu, gpointer vlp, gint subtype, gpointer sublayer, GtkTreeIter *iter, VikViewport *vvp )
4010 {
4011   static gpointer pass_along[6];
4012   GtkWidget *item;
4013   gboolean rv = FALSE;
4014
4015   pass_along[0] = l;
4016   pass_along[1] = vlp;
4017   pass_along[2] = GINT_TO_POINTER (subtype);
4018   pass_along[3] = sublayer;
4019   pass_along[4] = GINT_TO_POINTER (1); // Confirm delete request
4020   pass_along[5] = vvp;
4021
4022   if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT || subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
4023   {
4024     rv = TRUE;
4025
4026     item = gtk_image_menu_item_new_from_stock ( GTK_STOCK_PROPERTIES, NULL );
4027     g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_properties_item), pass_along );
4028     gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
4029     gtk_widget_show ( item );
4030
4031     if (subtype == VIK_TRW_LAYER_SUBLAYER_TRACK) {
4032       VikTrwLayer *vtl = l;
4033       VikTrack *tr = g_hash_table_lookup ( vtl->tracks, sublayer );
4034       if (tr && tr->property_dialog)
4035         gtk_widget_set_sensitive(GTK_WIDGET(item), FALSE );
4036     }
4037
4038     item = gtk_image_menu_item_new_from_stock ( GTK_STOCK_CUT, NULL );
4039     g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_cut_item_cb), pass_along );
4040     gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
4041     gtk_widget_show ( item );
4042
4043     item = gtk_image_menu_item_new_from_stock ( GTK_STOCK_COPY, NULL );
4044     g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_copy_item_cb), pass_along );
4045     gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
4046     gtk_widget_show ( item );
4047
4048     item = gtk_image_menu_item_new_from_stock ( GTK_STOCK_DELETE, NULL );
4049     g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_item), pass_along );
4050     gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
4051     gtk_widget_show ( item );
4052
4053     if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT )
4054     {
4055       gboolean separator_created = FALSE;
4056
4057       /* could be a right-click using the tool */
4058       if ( vlp != NULL ) {
4059         item = gtk_menu_item_new ();
4060         gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
4061         gtk_widget_show ( item );
4062
4063         separator_created = TRUE;
4064
4065         item = gtk_image_menu_item_new_with_mnemonic ( _("_Goto") );
4066         gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_JUMP_TO, GTK_ICON_SIZE_MENU) );
4067         g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_waypoint), pass_along );
4068         gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
4069         gtk_widget_show ( item );
4070       }
4071
4072       if ( is_valid_geocache_name ( (gchar *) sublayer ) )
4073       {
4074         if ( !separator_created ) {
4075           item = gtk_menu_item_new ();
4076           gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
4077           gtk_widget_show ( item );
4078           separator_created = TRUE;
4079         }
4080
4081         item = gtk_menu_item_new_with_mnemonic ( _("_Visit Geocache Webpage") );
4082         g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_waypoint_gc_webpage), pass_along );
4083         gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
4084         gtk_widget_show ( item );
4085       }
4086
4087       VikWaypoint *wp = g_hash_table_lookup ( VIK_TRW_LAYER(l)->waypoints, sublayer );
4088
4089       if ( wp && wp->image )
4090       {
4091         if ( !separator_created ) {
4092           item = gtk_menu_item_new ();
4093           gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
4094           gtk_widget_show ( item );
4095           separator_created = TRUE;
4096         }
4097
4098         // Set up image paramater
4099         pass_along[5] = wp->image;
4100
4101         item = gtk_image_menu_item_new_with_mnemonic ( _("_Show Picture...") );
4102         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
4103         g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_show_picture), pass_along );
4104         gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
4105         gtk_widget_show ( item );
4106
4107 #ifdef VIK_CONFIG_GEOTAG
4108         GtkWidget *geotag_submenu = gtk_menu_new ();
4109         item = gtk_image_menu_item_new_with_mnemonic ( _("Update Geotag on _Image") );
4110         gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REFRESH, GTK_ICON_SIZE_MENU) );
4111         gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
4112         gtk_widget_show ( item );
4113         gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), geotag_submenu );
4114   
4115         item = gtk_menu_item_new_with_mnemonic ( _("_Update") );
4116         g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_geotagging_waypoint_mtime_update), pass_along );
4117         gtk_menu_shell_append (GTK_MENU_SHELL (geotag_submenu), item);
4118         gtk_widget_show ( item );
4119
4120         item = gtk_menu_item_new_with_mnemonic ( _("Update and _Keep File Timestamp") );
4121         g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_geotagging_waypoint_mtime_keep), pass_along );
4122         gtk_menu_shell_append (GTK_MENU_SHELL (geotag_submenu), item);
4123         gtk_widget_show ( item );
4124 #endif
4125       }
4126
4127     }
4128   }
4129
4130   if ( vlp && (subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINTS || subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT) )
4131   {
4132     rv = TRUE;
4133     item = gtk_image_menu_item_new_with_mnemonic ( _("_New Waypoint...") );
4134     gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NEW, GTK_ICON_SIZE_MENU) );
4135     g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_wp), pass_along );
4136     gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
4137     gtk_widget_show ( item );
4138   }
4139
4140   if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINTS )
4141   {
4142     item = gtk_image_menu_item_new_with_mnemonic ( _("_View All Waypoints") );
4143     gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ZOOM_FIT, GTK_ICON_SIZE_MENU) );
4144     g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_waypoints_view), pass_along );
4145     gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
4146     gtk_widget_show ( item );
4147
4148     item = gtk_image_menu_item_new_with_mnemonic ( _("Goto _Waypoint...") );
4149     gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_JUMP_TO, GTK_ICON_SIZE_MENU) );
4150     g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_wp), pass_along );
4151     gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
4152     gtk_widget_show ( item );
4153
4154     item = gtk_image_menu_item_new_with_mnemonic ( _("Delete _All Waypoints") );
4155     gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU) );
4156     g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_all_waypoints), pass_along );
4157     gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
4158     gtk_widget_show ( item );
4159
4160     item = gtk_image_menu_item_new_with_mnemonic ( _("_Delete Waypoints From Selection...") );
4161     gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
4162     g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_waypoints_from_selection), pass_along );
4163     gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
4164     gtk_widget_show ( item );
4165   }
4166
4167   if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACKS )
4168   {
4169     rv = TRUE;
4170
4171     item = gtk_image_menu_item_new_with_mnemonic ( _("_View All Tracks") );
4172     gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ZOOM_FIT, GTK_ICON_SIZE_MENU) );
4173     g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_tracks_view), pass_along );
4174     gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
4175     gtk_widget_show ( item );
4176
4177     item = gtk_image_menu_item_new_with_mnemonic ( _("Delete _All Tracks") );
4178     gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU) );
4179     g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_all_tracks), pass_along );
4180     gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
4181     gtk_widget_show ( item );
4182
4183     item = gtk_image_menu_item_new_with_mnemonic ( _("_Delete Tracks From Selection...") );
4184     gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
4185     g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_tracks_from_selection), pass_along );
4186     gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
4187     gtk_widget_show ( item );
4188   }
4189
4190   if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
4191   {
4192     GtkWidget *goto_submenu;
4193     item = gtk_menu_item_new ();
4194     gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
4195     gtk_widget_show ( item );
4196
4197     goto_submenu = gtk_menu_new ();
4198     item = gtk_image_menu_item_new_with_mnemonic ( _("_Goto") );
4199     gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_JUMP_TO, GTK_ICON_SIZE_MENU) );
4200     gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
4201     gtk_widget_show ( item );
4202     gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), goto_submenu );
4203
4204     item = gtk_image_menu_item_new_with_mnemonic ( _("_Startpoint") );
4205     gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GOTO_FIRST, GTK_ICON_SIZE_MENU) );
4206     g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_startpoint), pass_along );
4207     gtk_menu_shell_append ( GTK_MENU_SHELL(goto_submenu), item );
4208     gtk_widget_show ( item );
4209
4210     item = gtk_image_menu_item_new_with_mnemonic ( _("\"_Center\"") );
4211     gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_JUMP_TO, GTK_ICON_SIZE_MENU) );
4212     g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_center), pass_along );
4213     gtk_menu_shell_append ( GTK_MENU_SHELL(goto_submenu), item );
4214     gtk_widget_show ( item );
4215
4216     item = gtk_image_menu_item_new_with_mnemonic ( _("_Endpoint") );
4217     gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GOTO_LAST, GTK_ICON_SIZE_MENU) );
4218     g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_endpoint), pass_along );
4219     gtk_menu_shell_append ( GTK_MENU_SHELL(goto_submenu), item );
4220     gtk_widget_show ( item );
4221
4222     item = gtk_image_menu_item_new_with_mnemonic ( _("_Highest Altitude") );
4223     gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GOTO_TOP, GTK_ICON_SIZE_MENU) );
4224     g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_max_alt), pass_along );
4225     gtk_menu_shell_append ( GTK_MENU_SHELL(goto_submenu), item );
4226     gtk_widget_show ( item );
4227
4228     item = gtk_image_menu_item_new_with_mnemonic ( _("_Lowest Altitude") );
4229     gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GOTO_BOTTOM, GTK_ICON_SIZE_MENU) );
4230     g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_min_alt), pass_along );
4231     gtk_menu_shell_append ( GTK_MENU_SHELL(goto_submenu), item );
4232     gtk_widget_show ( item );
4233
4234     item = gtk_image_menu_item_new_with_mnemonic ( _("_Maximum Speed") );
4235     gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_MEDIA_FORWARD, GTK_ICON_SIZE_MENU) );
4236     g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_max_speed), pass_along );
4237     gtk_menu_shell_append ( GTK_MENU_SHELL(goto_submenu), item );
4238     gtk_widget_show ( item );
4239
4240     item = gtk_image_menu_item_new_with_mnemonic ( _("_View Track") );
4241     gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ZOOM_FIT, GTK_ICON_SIZE_MENU) );
4242     g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_track_view), pass_along );
4243     gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
4244     gtk_widget_show ( item );
4245
4246     item = gtk_menu_item_new_with_mnemonic ( _("_Merge By Time...") );
4247     g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_merge_by_timestamp), pass_along );
4248     gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
4249     gtk_widget_show ( item );
4250
4251     item = gtk_menu_item_new_with_mnemonic ( _("Merge _With Other Tracks...") );
4252     g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_merge_with_other), pass_along );
4253     gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
4254     gtk_widget_show ( item );
4255
4256     item = gtk_menu_item_new_with_mnemonic ( _("_Split By Time...") );
4257     g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_split_by_timestamp), pass_along );
4258     gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
4259     gtk_widget_show ( item );
4260
4261     item = gtk_menu_item_new_with_mnemonic ( _("Split By _Number of Points...") );
4262     g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_split_by_n_points), pass_along );
4263     gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
4264     gtk_widget_show ( item );
4265
4266     item = gtk_image_menu_item_new_with_mnemonic ( _("_Reverse Track") );
4267     gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_BACK, GTK_ICON_SIZE_MENU) );
4268     g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_reverse), pass_along );
4269     gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
4270     gtk_widget_show ( item );
4271
4272     /* ATM This function is only available via the layers panel, due to the method in finding out the maps in use */
4273     if ( vlp ) {
4274       item = gtk_image_menu_item_new_with_mnemonic ( _("Down_load Maps Along Track...") );
4275       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
4276       g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_download_map_along_track_cb), pass_along );
4277       gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
4278       gtk_widget_show ( item );
4279     }
4280
4281     item = gtk_image_menu_item_new_with_mnemonic ( _("_Apply DEM Data") );
4282     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
4283     g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_apply_dem_data), pass_along );
4284     gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
4285     gtk_widget_show ( item );
4286
4287     item = gtk_image_menu_item_new_with_mnemonic ( _("Export Trac_k as GPX...") );
4288     gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_HARDDISK, GTK_ICON_SIZE_MENU) );
4289     g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_gpx_track), pass_along );
4290     gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
4291     gtk_widget_show ( item );
4292
4293     item = gtk_image_menu_item_new_with_mnemonic ( _("E_xtend Track End") );
4294     gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ADD, GTK_ICON_SIZE_MENU) );
4295     g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_extend_track_end), pass_along );
4296     gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
4297     gtk_widget_show ( item );
4298
4299     item = gtk_image_menu_item_new_with_mnemonic ( _("Extend _Using Route Finder") );
4300     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
4301     g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_extend_track_end_route_finder), pass_along );
4302     gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
4303     gtk_widget_show ( item );
4304
4305 #ifdef VIK_CONFIG_OPENSTREETMAP
4306     item = gtk_image_menu_item_new_with_mnemonic ( _("Upload to _OSM...") );
4307     g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(osm_traces_upload_track_cb), pass_along );
4308     gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_UP, GTK_ICON_SIZE_MENU) );
4309     gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
4310     gtk_widget_show ( item );
4311 #endif
4312
4313     if ( is_valid_google_route ( l, (gchar *) sublayer ) )
4314     {
4315       item = gtk_image_menu_item_new_with_mnemonic ( _("_View Google Directions") );
4316       gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NETWORK, GTK_ICON_SIZE_MENU) );
4317       g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_track_google_route_webpage), pass_along );
4318       gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
4319       gtk_widget_show ( item );
4320     }
4321
4322     item = gtk_image_menu_item_new_with_mnemonic ( _("Use with _Filter") );
4323     gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
4324     g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_track_use_with_filter), pass_along );
4325     gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
4326     gtk_widget_show ( item );
4327
4328     /* ATM This function is only available via the layers panel, due to needing a vlp */
4329     if ( vlp ) {
4330       item = a_acquire_track_menu ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(l)), vlp,
4331                                     vik_layers_panel_get_viewport(VIK_LAYERS_PANEL(vlp)),
4332                                     g_hash_table_lookup ( l->tracks, (gchar *) sublayer ) );
4333       if ( item ) {
4334         gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
4335         gtk_widget_show ( item );
4336       }
4337     }
4338
4339 #ifdef VIK_CONFIG_GEOTAG
4340   item = gtk_menu_item_new_with_mnemonic ( _("Geotag _Images...") );
4341   g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_geotagging_track), pass_along );
4342   gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
4343   gtk_widget_show ( item );
4344 #endif
4345
4346     // Only show on viewport popmenu when a trackpoint is selected
4347     if ( ! vlp && l->current_tpl ) {
4348       // Add separator
4349       item = gtk_menu_item_new ();
4350       gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
4351       gtk_widget_show ( item );
4352
4353       item = gtk_image_menu_item_new_with_mnemonic ( _("_Edit Trackpoint") );
4354       gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_PROPERTIES, GTK_ICON_SIZE_MENU) );
4355       g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_edit_trackpoint), pass_along );
4356       gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
4357       gtk_widget_show ( item );
4358     }
4359
4360   }
4361
4362   return rv;
4363 }
4364
4365 static void trw_layer_insert_tp_after_current_tp ( VikTrwLayer *vtl )
4366 {
4367   /* sanity checks */
4368   if (!vtl->current_tpl)
4369     return;
4370   if (!vtl->current_tpl->next)
4371     return;
4372
4373   VikTrackpoint *tp_current = VIK_TRACKPOINT(vtl->current_tpl->data);
4374   VikTrackpoint *tp_next = VIK_TRACKPOINT(vtl->current_tpl->next->data);
4375
4376   /* Use current and next trackpoints to form a new track point which is inserted into the tracklist */
4377   if ( tp_next ) {
4378
4379     VikTrackpoint *tp_new = vik_trackpoint_new();
4380     struct LatLon ll_current, ll_next;
4381     vik_coord_to_latlon ( &tp_current->coord, &ll_current );
4382     vik_coord_to_latlon ( &tp_next->coord, &ll_next );
4383
4384     /* main positional interpolation */
4385     struct LatLon ll_new = { (ll_current.lat+ll_next.lat)/2, (ll_current.lon+ll_next.lon)/2 };
4386     vik_coord_load_from_latlon ( &(tp_new->coord), vtl->coord_mode, &ll_new );
4387
4388     /* Now other properties that can be interpolated */
4389     tp_new->altitude = (tp_current->altitude + tp_next->altitude) / 2;
4390
4391     if (tp_current->has_timestamp && tp_next->has_timestamp) {
4392       /* Note here the division is applied to each part, then added
4393          This is to avoid potential overflow issues with a 32 time_t for dates after midpoint of this Unix time on 2004/01/04 */
4394       tp_new->timestamp = (tp_current->timestamp/2) + (tp_next->timestamp/2);
4395       tp_new->has_timestamp = TRUE;
4396     }
4397
4398     if (tp_current->speed != NAN && tp_next->speed != NAN)
4399       tp_new->speed = (tp_current->speed + tp_next->speed)/2;
4400
4401     /* TODO - improve interpolation of course, as it may not be correct.
4402        if courses in degrees are 350 + 020, the mid course more likely to be 005 (not 185)
4403        [similar applies if value is in radians] */
4404     if (tp_current->course != NAN && tp_next->course != NAN)
4405       tp_new->speed = (tp_current->course + tp_next->course)/2;
4406
4407     /* DOP / sat values remain at defaults as not they do not seem applicable to a dreamt up point */
4408
4409     /* Insert new point into the trackpoints list after the current TP */
4410     VikTrack *tr = g_hash_table_lookup ( vtl->tracks, vtl->current_tp_track_name );
4411     gint index =  g_list_index ( tr->trackpoints, tp_current );
4412     if ( index > -1 ) {
4413       tr->trackpoints = g_list_insert (tr->trackpoints, tp_new, index+1 );
4414     }
4415   }
4416 }
4417
4418 /* to be called when last_tpl no long exists. */
4419 static void trw_layer_cancel_last_tp ( VikTrwLayer *vtl )
4420 {
4421   if ( vtl->tpwin ) /* can't join with a non-existant TP. */
4422     vik_trw_layer_tpwin_disable_join ( vtl->tpwin );
4423   vtl->last_tpl = NULL;
4424   vtl->last_tp_track_name = NULL;
4425 }
4426
4427 static void trw_layer_cancel_current_tp ( VikTrwLayer *vtl, gboolean destroy )
4428 {
4429   if ( vtl->tpwin )
4430   {
4431     if ( destroy)
4432     {
4433       gtk_widget_destroy ( GTK_WIDGET(vtl->tpwin) );
4434       vtl->tpwin = NULL;
4435     }
4436     else
4437       vik_trw_layer_tpwin_set_empty ( vtl->tpwin );
4438   }
4439   if ( vtl->current_tpl )
4440   {
4441     vtl->current_tpl = NULL;
4442     vtl->current_tp_track_name = NULL;
4443     vik_layer_emit_update(VIK_LAYER(vtl), FALSE);
4444   }
4445 }
4446
4447 static void trw_layer_tpwin_response ( VikTrwLayer *vtl, gint response )
4448 {
4449   g_assert ( vtl->tpwin != NULL );
4450   if ( response == VIK_TRW_LAYER_TPWIN_CLOSE )
4451     trw_layer_cancel_current_tp ( vtl, TRUE );
4452
4453   if ( vtl->current_tpl == NULL )
4454     return;
4455
4456   if ( response == VIK_TRW_LAYER_TPWIN_SPLIT && vtl->current_tpl->next && vtl->current_tpl->prev )
4457   {
4458     gchar *name = get_new_unique_sublayer_name(vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, vtl->current_tp_track_name);
4459     if ( ( name = a_dialog_new_track ( GTK_WINDOW(vtl->tpwin), vtl->tracks, name ) ) )
4460     {
4461       VikTrack *tr = vik_track_new ();
4462       GList *newglist = g_list_alloc ();
4463       newglist->prev = NULL;
4464       newglist->next = vtl->current_tpl->next;
4465       newglist->data = vik_trackpoint_copy(VIK_TRACKPOINT(vtl->current_tpl->data));
4466       tr->trackpoints = newglist;
4467
4468       vtl->current_tpl->next->prev = newglist; /* end old track here */
4469       vtl->current_tpl->next = NULL;
4470
4471       vtl->current_tpl = newglist; /* change tp to first of new track. */
4472       vtl->current_tp_track_name = name;
4473
4474       vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, vtl->current_tp_track_name );
4475
4476       tr->visible = TRUE;
4477
4478       vik_trw_layer_add_track ( vtl, name, tr );
4479       vik_layer_emit_update(VIK_LAYER(vtl), FALSE);
4480     }
4481   }
4482   else if ( response == VIK_TRW_LAYER_TPWIN_DELETE )
4483   {
4484     VikTrack *tr = g_hash_table_lookup ( vtl->tracks, vtl->current_tp_track_name );
4485     GList *new_tpl;
4486     g_assert(tr != NULL);
4487
4488     /* can't join with a non-existent trackpoint */
4489     vtl->last_tpl = NULL;
4490     vtl->last_tp_track_name = NULL;
4491
4492     if ( (new_tpl = vtl->current_tpl->next) || (new_tpl = vtl->current_tpl->prev) )
4493     {
4494       if ( VIK_TRACKPOINT(vtl->current_tpl->data)->newsegment && vtl->current_tpl->next )
4495         VIK_TRACKPOINT(vtl->current_tpl->next->data)->newsegment = TRUE; /* don't concat segments on del */
4496
4497       tr->trackpoints = g_list_remove_link ( tr->trackpoints, vtl->current_tpl ); /* this nulls current_tpl->prev and next */
4498
4499       /* at this point the old trackpoint exists, but the list links are correct (new), so it is safe to do this. */
4500       vik_trw_layer_tpwin_set_tp ( vtl->tpwin, new_tpl, vtl->current_tp_track_name );
4501
4502       trw_layer_cancel_last_tp ( vtl );
4503
4504       g_free ( vtl->current_tpl->data ); /* TODO: vik_trackpoint_free() */
4505       g_list_free_1 ( vtl->current_tpl );
4506       vtl->current_tpl = new_tpl;
4507       vik_layer_emit_update(VIK_LAYER(vtl), FALSE);
4508     }
4509     else
4510     {
4511       tr->trackpoints = g_list_remove_link ( tr->trackpoints, vtl->current_tpl );
4512       g_free ( vtl->current_tpl->data ); /* TODO longone: vik_trackpoint_new() and vik_trackpoint_free() */
4513       g_list_free_1 ( vtl->current_tpl );
4514       trw_layer_cancel_current_tp ( vtl, FALSE );
4515     }
4516   }
4517   else if ( response == VIK_TRW_LAYER_TPWIN_FORWARD && vtl->current_tpl->next )
4518   {
4519     vtl->last_tpl = vtl->current_tpl;
4520     vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl = vtl->current_tpl->next, vtl->current_tp_track_name );
4521     vik_layer_emit_update(VIK_LAYER(vtl), FALSE); /* TODO longone: either move or only update if tp is inside drawing window */
4522   }
4523   else if ( response == VIK_TRW_LAYER_TPWIN_BACK && vtl->current_tpl->prev )
4524   {
4525     vtl->last_tpl = vtl->current_tpl;
4526     vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl = vtl->current_tpl->prev, vtl->current_tp_track_name );
4527     vik_layer_emit_update(VIK_LAYER(vtl), FALSE);
4528   }
4529   else if ( response == VIK_TRW_LAYER_TPWIN_JOIN )
4530   {
4531     // Check tracks exist and are different before joining
4532     if ( ! vtl->last_tp_track_name || ! vtl->current_tp_track_name || vtl->last_tp_track_name == vtl->current_tp_track_name )
4533       return;
4534
4535     VikTrack *tr1 = g_hash_table_lookup ( vtl->tracks, vtl->last_tp_track_name );
4536     VikTrack *tr2 = g_hash_table_lookup ( vtl->tracks, vtl->current_tp_track_name );
4537
4538     VikTrack *tr_first = tr1, *tr_last = tr2;
4539
4540     gchar *tmp;
4541
4542     if ( (!vtl->last_tpl->next) && (!vtl->current_tpl->next) ) /* both endpoints */
4543       vik_track_reverse ( tr2 ); /* reverse the second, that way second track clicked will be later. */
4544     else if ( (!vtl->last_tpl->prev) && (!vtl->current_tpl->prev) )
4545       vik_track_reverse ( tr1 );
4546     else if ( (!vtl->last_tpl->prev) && (!vtl->current_tpl->next) ) /* clicked startpoint, then endpoint -- concat end to start */
4547     {
4548       tr_first = tr2;
4549       tr_last = tr1;
4550     }
4551     /* default -- clicked endpoint then startpoint -- connect endpoint to startpoint */
4552
4553     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. */
4554       VIK_TRACKPOINT(tr_last->trackpoints->data)->newsegment = FALSE;
4555     tr1->trackpoints = g_list_concat ( tr_first->trackpoints, tr_last->trackpoints );
4556     tr2->trackpoints = NULL;
4557
4558     tmp = vtl->current_tp_track_name;
4559
4560     vtl->current_tp_track_name = vtl->last_tp_track_name; /* current_tp stays the same (believe it or not!) */
4561     vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, vtl->current_tp_track_name );
4562
4563     /* if we did this before, trw_layer_delete_track would have canceled the current tp because
4564      * it was the current track. canceling the current tp would have set vtl->current_tpl to NULL */
4565     vik_trw_layer_delete_track ( vtl, tmp );
4566
4567     trw_layer_cancel_last_tp ( vtl ); /* same TP, can't join. */
4568     vik_layer_emit_update(VIK_LAYER(vtl), FALSE);
4569   }
4570   else if ( response == VIK_TRW_LAYER_TPWIN_INSERT && vtl->current_tpl->next )
4571   {
4572     trw_layer_insert_tp_after_current_tp ( vtl );
4573     vik_layer_emit_update(VIK_LAYER(vtl), FALSE);
4574   }
4575   else if ( response == VIK_TRW_LAYER_TPWIN_DATA_CHANGED )
4576     vik_layer_emit_update(VIK_LAYER(vtl), FALSE);
4577 }
4578
4579 static void trw_layer_tpwin_init ( VikTrwLayer *vtl )
4580 {
4581   if ( ! vtl->tpwin )
4582   {
4583     vtl->tpwin = vik_trw_layer_tpwin_new ( VIK_GTK_WINDOW_FROM_LAYER(vtl) );
4584     g_signal_connect_swapped ( GTK_DIALOG(vtl->tpwin), "response", G_CALLBACK(trw_layer_tpwin_response), vtl );
4585     /* connect signals -- DELETE SIGNAL VERY IMPORTANT TO SET TO NULL */
4586     g_signal_connect_swapped ( vtl->tpwin, "delete-event", G_CALLBACK(trw_layer_cancel_current_tp), vtl );
4587     gtk_widget_show_all ( GTK_WIDGET(vtl->tpwin) );
4588   }
4589   if ( vtl->current_tpl )
4590     vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, vtl->current_tp_track_name );
4591   /* set layer name and TP data */
4592 }
4593
4594 /***************************************************************************
4595  ** Tool code
4596  ***************************************************************************/
4597
4598 /*** Utility data structures and functions ****/
4599
4600 typedef struct {
4601   gint x, y;
4602   gint closest_x, closest_y;
4603   gchar *closest_wp_name;
4604   VikWaypoint *closest_wp;
4605   VikViewport *vvp;
4606 } WPSearchParams;
4607
4608 typedef struct {
4609   gint x, y;
4610   gint closest_x, closest_y;
4611   gchar *closest_track_name;
4612   VikTrackpoint *closest_tp;
4613   VikViewport *vvp;
4614   GList *closest_tpl;
4615 } TPSearchParams;
4616
4617 static void waypoint_search_closest_tp ( gchar *name, VikWaypoint *wp, WPSearchParams *params )
4618 {
4619   gint x, y;
4620   if ( !wp->visible )
4621     return;
4622
4623   vik_viewport_coord_to_screen ( params->vvp, &(wp->coord), &x, &y );
4624
4625   // If waypoint has an image then use the image size to select
4626   if ( wp->image ) {
4627     gint slackx, slacky;
4628     slackx = wp->image_width / 2;
4629     slacky = wp->image_height / 2;
4630
4631     if (    x <= params->x + slackx && x >= params->x - slackx
4632          && y <= params->y + slacky && y >= params->y - slacky ) {
4633       params->closest_wp_name = name;
4634       params->closest_wp = wp;
4635       params->closest_x = x;
4636       params->closest_y = y;
4637     }
4638   }
4639   else if ( abs (x - params->x) <= WAYPOINT_SIZE_APPROX && abs (y - params->y) <= WAYPOINT_SIZE_APPROX &&
4640             ((!params->closest_wp) ||        /* was the old waypoint we already found closer than this one? */
4641              abs(x - params->x)+abs(y - params->y) < abs(x - params->closest_x)+abs(y - params->closest_y)))
4642     {
4643       params->closest_wp_name = name;
4644       params->closest_wp = wp;
4645       params->closest_x = x;
4646       params->closest_y = y;
4647     }
4648 }
4649
4650 static void track_search_closest_tp ( gchar *name, VikTrack *t, TPSearchParams *params )
4651 {
4652   GList *tpl = t->trackpoints;
4653   VikTrackpoint *tp;
4654
4655   if ( !t->visible )
4656     return;
4657
4658   while (tpl)
4659   {
4660     gint x, y;
4661     tp = VIK_TRACKPOINT(tpl->data);
4662
4663     vik_viewport_coord_to_screen ( params->vvp, &(tp->coord), &x, &y );
4664  
4665     if ( abs (x - params->x) <= TRACKPOINT_SIZE_APPROX && abs (y - params->y) <= TRACKPOINT_SIZE_APPROX &&
4666         ((!params->closest_tp) ||        /* was the old trackpoint we already found closer than this one? */
4667           abs(x - params->x)+abs(y - params->y) < abs(x - params->closest_x)+abs(y - params->closest_y)))
4668     {
4669       params->closest_track_name = name;
4670       params->closest_tp = tp;
4671       params->closest_tpl = tpl;
4672       params->closest_x = x;
4673       params->closest_y = y;
4674     }
4675     tpl = tpl->next;
4676   }
4677 }
4678
4679 static VikTrackpoint *closest_tp_in_five_pixel_interval ( VikTrwLayer *vtl, VikViewport *vvp, gint x, gint y )
4680 {
4681   TPSearchParams params;
4682   params.x = x;
4683   params.y = y;
4684   params.vvp = vvp;
4685   params.closest_track_name = NULL;
4686   params.closest_tp = NULL;
4687   g_hash_table_foreach ( vtl->tracks, (GHFunc) track_search_closest_tp, &params);
4688   return params.closest_tp;
4689 }
4690
4691 static VikWaypoint *closest_wp_in_five_pixel_interval ( VikTrwLayer *vtl, VikViewport *vvp, gint x, gint y )
4692 {
4693   WPSearchParams params;
4694   params.x = x;
4695   params.y = y;
4696   params.vvp = vvp;
4697   params.closest_wp = NULL;
4698   params.closest_wp_name = NULL;
4699   g_hash_table_foreach ( vtl->waypoints, (GHFunc) waypoint_search_closest_tp, &params);
4700   return params.closest_wp;
4701 }
4702
4703
4704 // Some forward declarations
4705 static void marker_begin_move ( tool_ed_t *t, gint x, gint y );
4706 static void marker_moveto ( tool_ed_t *t, gint x, gint y );
4707 static void marker_end_move ( tool_ed_t *t );
4708 //
4709
4710 static gboolean trw_layer_select_move ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp, tool_ed_t* t )
4711 {
4712   if ( t->holding ) {
4713     VikCoord new_coord;
4714     vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
4715
4716     // Here always allow snapping back to the original location
4717     //  this is useful when one decides not to move the thing afterall
4718     // If one wants to move the item only a little bit then don't hold down the 'snap' key!
4719  
4720     // snap to TP
4721     if ( event->state & GDK_CONTROL_MASK )
4722     {
4723       VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
4724       if ( tp )
4725         new_coord = tp->coord;
4726     }
4727
4728     // snap to WP
4729     if ( event->state & GDK_SHIFT_MASK )
4730     {
4731       VikWaypoint *wp = closest_wp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
4732       if ( wp )
4733         new_coord = wp->coord;
4734     }
4735     
4736     gint x, y;
4737     vik_viewport_coord_to_screen ( vvp, &new_coord, &x, &y );
4738
4739     marker_moveto ( t, x, y );
4740
4741     return TRUE;
4742   }
4743   return FALSE;
4744 }
4745
4746 static gboolean trw_layer_select_release ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp, tool_ed_t* t )
4747 {
4748   if ( t->holding && event->button == 1 )
4749   {
4750     VikCoord new_coord;
4751     vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
4752
4753     // snap to TP
4754     if ( event->state & GDK_CONTROL_MASK )
4755     {
4756       VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
4757       if ( tp )
4758         new_coord = tp->coord;
4759     }
4760
4761     // snap to WP
4762     if ( event->state & GDK_SHIFT_MASK )
4763     {
4764       VikWaypoint *wp = closest_wp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
4765       if ( wp )
4766         new_coord = wp->coord;
4767     }
4768
4769     marker_end_move ( t );
4770
4771     // Determine if working on a waypoint or a trackpoint
4772     if ( t->is_waypoint )
4773       vtl->current_wp->coord = new_coord;
4774     else {
4775       if ( vtl->current_tpl ) {
4776         VIK_TRACKPOINT(vtl->current_tpl->data)->coord = new_coord;
4777       
4778         if ( vtl->tpwin )
4779           vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, vtl->current_tp_track_name );
4780
4781         // Don't really know what this is for but seems like it might be handy...
4782         /* can't join with itself! */
4783         trw_layer_cancel_last_tp ( vtl );
4784       }
4785     }
4786
4787     // Reset
4788     vtl->current_wp      = NULL;
4789     vtl->current_wp_name = NULL;
4790     trw_layer_cancel_current_tp ( vtl, FALSE );
4791
4792     vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
4793     return TRUE;
4794   }
4795   return FALSE;
4796 }
4797
4798 /*
4799   Returns true if a waypoint or track is found near the requested event position for this particular layer
4800   The item found is automatically selected
4801   This is a tool like feature but routed via the layer interface, since it's instigated by a 'global' layer tool in vikwindow.c
4802  */
4803 static gboolean trw_layer_select_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp, tool_ed_t* tet )
4804 {
4805   if ( event->button != 1 )
4806     return FALSE;
4807
4808   if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
4809     return FALSE;
4810
4811   if ( !vtl->tracks_visible && !vtl->waypoints_visible )
4812     return FALSE;
4813
4814   // Go for waypoints first as these often will be near a track, but it's likely the wp is wanted rather then the track
4815
4816   if (vtl->waypoints_visible) {
4817     WPSearchParams wp_params;
4818     wp_params.vvp = vvp;
4819     wp_params.x = event->x;
4820     wp_params.y = event->y;
4821     wp_params.closest_wp_name = NULL;
4822     wp_params.closest_wp = NULL;
4823
4824     g_hash_table_foreach ( vtl->waypoints, (GHFunc) waypoint_search_closest_tp, &wp_params);
4825
4826     if ( wp_params.closest_wp )  {
4827
4828       // Select
4829       vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, g_hash_table_lookup ( vtl->waypoints_iters, wp_params.closest_wp_name ), TRUE );
4830
4831       // Too easy to move it so must be holding shift to start immediately moving it
4832       //   or otherwise be previously selected but not have an image (otherwise clicking within image bounds (again) moves it)
4833       if ( event->state & GDK_SHIFT_MASK ||
4834            ( vtl->current_wp == wp_params.closest_wp && !vtl->current_wp->image ) ) {
4835         // Put into 'move buffer'
4836         // NB vvp & vw already set in tet
4837         tet->vtl = (gpointer)vtl;
4838         tet->is_waypoint = TRUE;
4839       
4840         marker_begin_move (tet, event->x, event->y);
4841       }
4842
4843       vtl->current_wp =      wp_params.closest_wp;
4844       vtl->current_wp_name = wp_params.closest_wp_name;
4845
4846       vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
4847
4848       return TRUE;
4849     }
4850   }
4851
4852   if (vtl->tracks_visible) {
4853     TPSearchParams tp_params;
4854     tp_params.vvp = vvp;
4855     tp_params.x = event->x;
4856     tp_params.y = event->y;
4857     tp_params.closest_track_name = NULL;
4858     tp_params.closest_tp = NULL;
4859
4860     g_hash_table_foreach ( vtl->tracks, (GHFunc) track_search_closest_tp, &tp_params);
4861
4862     if ( tp_params.closest_tp )  {
4863
4864       // Always select + highlight the track
4865       vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, g_hash_table_lookup ( vtl->tracks_iters, tp_params.closest_track_name ), TRUE );
4866
4867       tet->is_waypoint = FALSE;
4868
4869       // Select the Trackpoint
4870       // Can move it immediately when control held or it's the previously selected tp
4871       if ( event->state & GDK_CONTROL_MASK ||
4872            vtl->current_tpl == tp_params.closest_tpl ) {
4873         // Put into 'move buffer'
4874         // NB vvp & vw already set in tet
4875         tet->vtl = (gpointer)vtl;
4876         marker_begin_move (tet, event->x, event->y);
4877       }
4878
4879       vtl->current_tpl = tp_params.closest_tpl;
4880       vtl->current_tp_track_name = tp_params.closest_track_name;
4881
4882       set_statusbar_msg_info_trkpt ( vtl, tp_params.closest_tp );
4883
4884       if ( vtl->tpwin )
4885         vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, vtl->current_tp_track_name );
4886
4887       vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
4888       return TRUE;
4889     }
4890   }
4891
4892   /* these aren't the droids you're looking for */
4893   vtl->current_wp      = NULL;
4894   vtl->current_wp_name = NULL;
4895   trw_layer_cancel_current_tp ( vtl, FALSE );
4896
4897   // Blank info
4898   vik_statusbar_set_message ( vik_window_get_statusbar (VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl))), VIK_STATUSBAR_INFO, "" );
4899
4900   return FALSE;
4901 }
4902
4903 static gboolean trw_layer_show_selected_viewport_menu ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
4904 {
4905   if ( event->button != 3 )
4906     return FALSE;
4907
4908   if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
4909     return FALSE;
4910
4911   if ( !vtl->tracks_visible && !vtl->waypoints_visible )
4912     return FALSE;
4913
4914   /* Post menu for the currently selected item */
4915
4916   /* See if a track is selected */
4917   VikTrack *track = (VikTrack*)vik_window_get_selected_track ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(vtl) );
4918   if ( track && track->visible ) {
4919
4920     if ( vik_window_get_selected_name ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(vtl) ) ) {
4921
4922       if ( vtl->track_right_click_menu )
4923         gtk_object_sink ( GTK_OBJECT(vtl->track_right_click_menu) );
4924
4925       vtl->track_right_click_menu = GTK_MENU ( gtk_menu_new () );
4926       
4927       trw_layer_sublayer_add_menu_items ( vtl,
4928                                           vtl->track_right_click_menu,
4929                                           NULL,
4930                                           VIK_TRW_LAYER_SUBLAYER_TRACK,
4931                                           vik_window_get_selected_name ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(vtl) ),
4932                                           g_hash_table_lookup ( vtl->tracks_iters, vik_window_get_selected_name ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(vtl) ) ),
4933                                           vvp);
4934
4935       gtk_menu_popup ( vtl->track_right_click_menu, NULL, NULL, NULL, NULL, event->button, gtk_get_current_event_time() );
4936         
4937       return TRUE;
4938     }
4939   }
4940
4941   /* See if a waypoint is selected */
4942   VikWaypoint *waypoint = (VikWaypoint*)vik_window_get_selected_waypoint ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(vtl) );
4943   if ( waypoint && waypoint->visible ) {
4944     if ( vik_window_get_selected_name ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(vtl) ) ) {
4945
4946       if ( vtl->wp_right_click_menu )
4947         gtk_object_sink ( GTK_OBJECT(vtl->wp_right_click_menu) );
4948
4949       vtl->wp_right_click_menu = GTK_MENU ( gtk_menu_new () );
4950       trw_layer_sublayer_add_menu_items ( vtl,
4951                                           vtl->wp_right_click_menu,
4952                                           NULL,
4953                                           VIK_TRW_LAYER_SUBLAYER_WAYPOINT,
4954                                           vik_window_get_selected_name ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(vtl) ),
4955                                           g_hash_table_lookup ( vtl->waypoints_iters, vik_window_get_selected_name ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(vtl) ) ),
4956                                           vvp);
4957       gtk_menu_popup ( vtl->wp_right_click_menu, NULL, NULL, NULL, NULL, event->button, gtk_get_current_event_time() );
4958
4959       return TRUE;
4960     }
4961   }
4962
4963   return FALSE;
4964 }
4965
4966 /* background drawing hook, to be passed the viewport */
4967 static gboolean tool_sync_done = TRUE;
4968
4969 static gboolean tool_sync(gpointer data)
4970 {
4971   VikViewport *vvp = data;
4972   gdk_threads_enter();
4973   vik_viewport_sync(vvp);
4974   tool_sync_done = TRUE;
4975   gdk_threads_leave();
4976   return FALSE;
4977 }
4978
4979 static void marker_begin_move ( tool_ed_t *t, gint x, gint y )
4980 {
4981   t->holding = TRUE;
4982   t->gc = vik_viewport_new_gc (t->vvp, "black", 2);
4983   gdk_gc_set_function ( t->gc, GDK_INVERT );
4984   vik_viewport_draw_rectangle ( t->vvp, t->gc, FALSE, x-3, y-3, 6, 6 );
4985   vik_viewport_sync(t->vvp);
4986   t->oldx = x;
4987   t->oldy = y;
4988 }
4989
4990 static void marker_moveto ( tool_ed_t *t, gint x, gint y )
4991 {
4992   VikViewport *vvp =  t->vvp;
4993   vik_viewport_draw_rectangle ( vvp, t->gc, FALSE, t->oldx-3, t->oldy-3, 6, 6 );
4994   vik_viewport_draw_rectangle ( vvp, t->gc, FALSE, x-3, y-3, 6, 6 );
4995   t->oldx = x;
4996   t->oldy = y;
4997
4998   if (tool_sync_done) {
4999     g_idle_add_full (G_PRIORITY_HIGH_IDLE + 10, tool_sync, vvp, NULL);
5000     tool_sync_done = FALSE;
5001   }
5002 }
5003
5004 static void marker_end_move ( tool_ed_t *t )
5005 {
5006   vik_viewport_draw_rectangle ( t->vvp, t->gc, FALSE, t->oldx-3, t->oldy-3, 6, 6 );
5007   g_object_unref ( t->gc );
5008   t->holding = FALSE;
5009 }
5010
5011 /*** Edit waypoint ****/
5012
5013 static gpointer tool_edit_waypoint_create ( VikWindow *vw, VikViewport *vvp)
5014 {
5015   tool_ed_t *t = g_new(tool_ed_t, 1);
5016   t->vvp = vvp;
5017   t->holding = FALSE;
5018   return t;
5019 }
5020
5021 static gboolean tool_edit_waypoint_click ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data )
5022 {
5023   WPSearchParams params;
5024   tool_ed_t *t = data;
5025   VikViewport *vvp = t->vvp;
5026
5027   if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
5028     return FALSE;
5029
5030   if ( t->holding ) {
5031     return TRUE;
5032   }
5033
5034   if ( !vtl->vl.visible || !vtl->waypoints_visible )
5035     return FALSE;
5036
5037   if ( vtl->current_wp && vtl->current_wp->visible )
5038   {
5039     /* first check if current WP is within area (other may be 'closer', but we want to move the current) */
5040     gint x, y;
5041     vik_viewport_coord_to_screen ( vvp, &(vtl->current_wp->coord), &x, &y );
5042
5043     if ( abs(x - event->x) <= WAYPOINT_SIZE_APPROX &&
5044          abs(y - event->y) <= WAYPOINT_SIZE_APPROX )
5045     {
5046       if ( event->button == 3 )
5047         vtl->waypoint_rightclick = TRUE; /* remember that we're clicking; other layers will ignore release signal */
5048       else {
5049         marker_begin_move(t, event->x, event->y);
5050       }
5051       return TRUE;
5052     }
5053   }
5054
5055   params.vvp = vvp;
5056   params.x = event->x;
5057   params.y = event->y;
5058   params.closest_wp_name = NULL;
5059   /* TODO: should get track listitem so we can break it up, make a new track, mess it up, all that. */
5060   params.closest_wp = NULL;
5061   g_hash_table_foreach ( vtl->waypoints, (GHFunc) waypoint_search_closest_tp, &params);
5062   if ( vtl->current_wp == params.closest_wp && vtl->current_wp != NULL )
5063   {
5064     /* how do we get here? I'm putting in the abort until we can figure it out. -alex */
5065     marker_begin_move(t, event->x, event->y);
5066     g_critical("shouldn't be here");
5067     exit(1);
5068   }
5069   else if ( params.closest_wp )
5070   {
5071     if ( event->button == 3 )
5072       vtl->waypoint_rightclick = TRUE; /* remember that we're clicking; other layers will ignore release signal */
5073     else
5074       vtl->waypoint_rightclick = FALSE;
5075
5076     vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, g_hash_table_lookup ( vtl->waypoints_iters, params.closest_wp_name ), TRUE );
5077
5078     vtl->current_wp = params.closest_wp;
5079     vtl->current_wp_name = params.closest_wp_name;
5080
5081     /* could make it so don't update if old WP is off screen and new is null but oh well */
5082     vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
5083     return TRUE;
5084   }
5085
5086   vtl->current_wp = NULL;
5087   vtl->current_wp_name = NULL;
5088   vtl->waypoint_rightclick = FALSE;
5089   vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
5090   return FALSE;
5091 }
5092
5093 static gboolean tool_edit_waypoint_move ( VikTrwLayer *vtl, GdkEventMotion *event, gpointer data )
5094 {
5095   tool_ed_t *t = data;
5096   VikViewport *vvp = t->vvp;
5097
5098   if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
5099     return FALSE;
5100
5101   if ( t->holding ) {
5102     VikCoord new_coord;
5103     vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
5104
5105     /* snap to TP */
5106     if ( event->state & GDK_CONTROL_MASK )
5107     {
5108       VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
5109       if ( tp )
5110         new_coord = tp->coord;
5111     }
5112
5113     /* snap to WP */
5114     if ( event->state & GDK_SHIFT_MASK )
5115     {
5116       VikWaypoint *wp = closest_wp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
5117       if ( wp && wp != vtl->current_wp )
5118         new_coord = wp->coord;
5119     }
5120     
5121     { 
5122       gint x, y;
5123       vik_viewport_coord_to_screen ( vvp, &new_coord, &x, &y );
5124
5125       marker_moveto ( t, x, y );
5126     } 
5127     return TRUE;
5128   }
5129   return FALSE;
5130 }
5131
5132 static gboolean tool_edit_waypoint_release ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data )
5133 {
5134   tool_ed_t *t = data;
5135   VikViewport *vvp = t->vvp;
5136
5137   if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
5138     return FALSE;
5139   
5140   if ( t->holding && event->button == 1 )
5141   {
5142     VikCoord new_coord;
5143     vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
5144
5145     /* snap to TP */
5146     if ( event->state & GDK_CONTROL_MASK )
5147     {
5148       VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
5149       if ( tp )
5150         new_coord = tp->coord;
5151     }
5152
5153     /* snap to WP */
5154     if ( event->state & GDK_SHIFT_MASK )
5155     {
5156       VikWaypoint *wp = closest_wp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
5157       if ( wp && wp != vtl->current_wp )
5158         new_coord = wp->coord;
5159     }
5160
5161     marker_end_move ( t );
5162
5163     vtl->current_wp->coord = new_coord;
5164     vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
5165     return TRUE;
5166   }
5167   /* PUT IN RIGHT PLACE!!! */
5168   if ( event->button == 3 && vtl->waypoint_rightclick )
5169   {
5170     if ( vtl->wp_right_click_menu )
5171       g_object_ref_sink ( G_OBJECT(vtl->wp_right_click_menu) );
5172     if ( vtl->current_wp ) {
5173       vtl->wp_right_click_menu = GTK_MENU ( gtk_menu_new () );
5174       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 );
5175       gtk_menu_popup ( vtl->wp_right_click_menu, NULL, NULL, NULL, NULL, event->button, gtk_get_current_event_time() );
5176     }
5177     vtl->waypoint_rightclick = FALSE;
5178   }
5179   return FALSE;
5180 }
5181
5182 /**** Begin track ***/
5183 static gpointer tool_begin_track_create ( VikWindow *vw, VikViewport *vvp)
5184 {
5185   return vvp;
5186 }
5187
5188 static gboolean tool_begin_track_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
5189 {
5190   vtl->current_track = NULL;
5191   return tool_new_track_click ( vtl, event, vvp );
5192 }
5193
5194 /*** New track ****/
5195
5196 static gpointer tool_new_track_create ( VikWindow *vw, VikViewport *vvp)
5197 {
5198   return vvp;
5199 }
5200
5201 typedef struct {
5202   VikTrwLayer *vtl;
5203   VikViewport *vvp;
5204   gint x1,y1,x2,y2,x3,y3;
5205   const gchar* str;
5206 } new_track_move_passalong_t;
5207
5208 /* sync and undraw, but only when we have time */
5209 static gboolean ct_sync ( gpointer passalong )
5210 {
5211   new_track_move_passalong_t *p = (new_track_move_passalong_t *) passalong;
5212
5213   vik_viewport_sync ( p->vvp );
5214   gdk_gc_set_function ( p->vtl->current_track_gc, GDK_INVERT );
5215   vik_viewport_draw_line ( p->vvp, p->vtl->current_track_gc, p->x1, p->y1, p->x2, p->y2 );
5216   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);
5217   gdk_gc_set_function ( p->vtl->current_track_gc, GDK_COPY );
5218
5219   g_free ( (gpointer) p->str ) ;
5220   p->vtl->ct_sync_done = TRUE;
5221   g_free ( p );
5222   return FALSE;
5223 }
5224
5225 static const gchar* distance_string (gdouble distance)
5226 {
5227   gchar str[128];
5228
5229   /* draw label with distance */
5230   vik_units_distance_t dist_units = a_vik_get_units_distance ();
5231   switch (dist_units) {
5232   case VIK_UNITS_DISTANCE_MILES:
5233     if (distance >= VIK_MILES_TO_METERS(1) && distance < VIK_MILES_TO_METERS(100)) {
5234       g_sprintf(str, "%3.2f miles", VIK_METERS_TO_MILES(distance));
5235     } else if (distance < 1609.4) {
5236       g_sprintf(str, "%d yards", (int)(distance*1.0936133));
5237     } else {
5238       g_sprintf(str, "%d miles", (int)VIK_METERS_TO_MILES(distance));
5239     }
5240     break;
5241   default:
5242     // VIK_UNITS_DISTANCE_KILOMETRES
5243     if (distance >= 1000 && distance < 100000) {
5244       g_sprintf(str, "%3.2f km", distance/1000.0);
5245     } else if (distance < 1000) {
5246       g_sprintf(str, "%d m", (int)distance);
5247     } else {
5248       g_sprintf(str, "%d km", (int)distance/1000);
5249     }
5250     break;
5251   }
5252   return g_strdup (str);
5253 }
5254
5255 /*
5256  * Actually set the message in statusbar
5257  */
5258 static void statusbar_write (const gchar *distance_string, gdouble elev_gain, gdouble elev_loss, VikTrwLayer *vtl )
5259 {
5260   // Only show elevation data when track has some elevation properties
5261   gchar str_gain_loss[64];
5262   str_gain_loss[0] = '\0';
5263   
5264   if ( (elev_gain > 0.1) || (elev_loss > 0.1) ) {
5265     if ( a_vik_get_units_height () == VIK_UNITS_HEIGHT_METRES )
5266       g_sprintf(str_gain_loss, _(" - Gain %dm:Loss %dm"), (int)elev_gain, (int)elev_loss);
5267     else
5268       g_sprintf(str_gain_loss, _(" - Gain %dft:Loss %dft"), (int)VIK_METERS_TO_FEET(elev_gain), (int)VIK_METERS_TO_FEET(elev_loss));
5269   }
5270
5271   // Write with full gain/loss information
5272   gchar *msg = g_strdup_printf ( "%s%s", distance_string, str_gain_loss);
5273   vik_statusbar_set_message ( vik_window_get_statusbar (VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl))), VIK_STATUSBAR_INFO, msg );
5274   g_free ( msg );
5275 }
5276
5277 /*
5278  * Figure out what information should be set in the statusbar and then write it
5279  */
5280 static void update_statusbar ( VikTrwLayer *vtl )
5281 {
5282   // Get elevation data
5283   gdouble elev_gain, elev_loss;
5284   vik_track_get_total_elevation_gain ( vtl->current_track, &elev_gain, &elev_loss);
5285
5286   /* Find out actual distance of current track */
5287   gdouble distance = vik_track_get_length (vtl->current_track);
5288   const gchar *str = distance_string (distance);
5289
5290   statusbar_write (str, elev_gain, elev_loss, vtl);
5291
5292   g_free ((gpointer)str);
5293 }
5294
5295
5296 static VikLayerToolFuncStatus tool_new_track_move ( VikTrwLayer *vtl, GdkEventMotion *event, VikViewport *vvp )
5297 {
5298   /* if we haven't sync'ed yet, we don't have time to do more. */
5299   if ( vtl->ct_sync_done && vtl->current_track && vtl->current_track->trackpoints ) {
5300     GList *iter = vtl->current_track->trackpoints;
5301     new_track_move_passalong_t *passalong;
5302     gint x1, y1;
5303
5304     while ( iter->next )
5305       iter = iter->next;
5306     gdk_gc_set_function ( vtl->current_track_gc, GDK_INVERT );
5307     vik_viewport_coord_to_screen ( vvp, &(VIK_TRACKPOINT(iter->data)->coord), &x1, &y1 );
5308     vik_viewport_draw_line ( vvp, vtl->current_track_gc, x1, y1, event->x, event->y );
5309
5310     /* Find out actual distance of current track */
5311     gdouble distance = vik_track_get_length (vtl->current_track);
5312
5313     // Now add distance to where the pointer is //
5314     VikCoord coord;
5315     struct LatLon ll;
5316     vik_viewport_screen_to_coord ( vvp, (gint) event->x, (gint) event->y, &coord );
5317     vik_coord_to_latlon ( &coord, &ll );
5318     distance = distance + vik_coord_diff( &coord, &(VIK_TRACKPOINT(iter->data)->coord));
5319
5320     // Get elevation data
5321     gdouble elev_gain, elev_loss;
5322     vik_track_get_total_elevation_gain ( vtl->current_track, &elev_gain, &elev_loss);
5323
5324     // Adjust elevation data (if available) for the current pointer position
5325     gdouble elev_new;
5326     elev_new = (gdouble) a_dems_get_elev_by_coord ( &coord, VIK_DEM_INTERPOL_BEST );
5327     if ( elev_new != VIK_DEM_INVALID_ELEVATION ) {
5328       if ( VIK_TRACKPOINT(iter->data)->altitude != VIK_DEFAULT_ALTITUDE ) {
5329         // Adjust elevation of last track point
5330         if ( elev_new > VIK_TRACKPOINT(iter->data)->altitude )
5331           // Going up
5332           elev_gain += elev_new - VIK_TRACKPOINT(iter->data)->altitude;
5333         else
5334           // Going down
5335           elev_loss += VIK_TRACKPOINT(iter->data)->altitude - elev_new;
5336       }
5337     }
5338       
5339     const gchar *str = distance_string (distance);
5340     gint xd,yd;
5341     /* offset from cursor a bit */
5342     xd = event->x + 10;
5343     yd = event->y - 10;
5344     /* note attempted setting text using pango layouts - but the 'undraw' technique didn't work */
5345     vik_viewport_draw_string (vvp, gdk_font_from_description (pango_font_description_from_string ("Sans 8")), vtl->current_track_gc, xd, yd, str);
5346
5347     gdk_gc_set_function ( vtl->current_track_gc, GDK_COPY );
5348
5349     passalong = g_new(new_track_move_passalong_t,1); /* freed by sync */
5350     passalong->vtl = vtl;
5351     passalong->vvp = vvp;
5352     passalong->x1 = x1;
5353     passalong->y1 = y1;
5354     passalong->x2 = event->x;
5355     passalong->y2 = event->y;
5356     passalong->x3 = xd;
5357     passalong->y3 = yd;
5358     passalong->str = str;
5359
5360     // Update statusbar with full gain/loss information
5361     statusbar_write (str, elev_gain, elev_loss, vtl);
5362
5363     /* this will sync and undraw when we have time to */
5364     g_idle_add_full (G_PRIORITY_HIGH_IDLE + 10, ct_sync, passalong, NULL);
5365     vtl->ct_sync_done = FALSE;
5366     return VIK_LAYER_TOOL_ACK_GRAB_FOCUS;
5367   }
5368   return VIK_LAYER_TOOL_ACK;
5369 }
5370
5371 static gboolean tool_new_track_key_press ( VikTrwLayer *vtl, GdkEventKey *event, VikViewport *vvp )
5372 {
5373   if ( vtl->current_track && event->keyval == GDK_Escape ) {
5374     vtl->current_track = NULL;
5375     vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
5376     return TRUE;
5377   } else if ( vtl->current_track && event->keyval == GDK_BackSpace ) {
5378     /* undo */
5379     if ( vtl->current_track->trackpoints )
5380     {
5381       GList *last = g_list_last(vtl->current_track->trackpoints);
5382       g_free ( last->data );
5383       vtl->current_track->trackpoints = g_list_remove_link ( vtl->current_track->trackpoints, last );
5384     }
5385     
5386     update_statusbar ( vtl );
5387
5388     vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
5389     return TRUE;
5390   }
5391   return FALSE;
5392 }
5393
5394 static gboolean tool_new_track_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
5395 {
5396   VikTrackpoint *tp;
5397
5398   if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
5399     return FALSE;
5400
5401   if ( event->button == 3 && vtl->current_track )
5402   {
5403     /* undo */
5404     if ( vtl->current_track->trackpoints )
5405     {
5406       GList *last = g_list_last(vtl->current_track->trackpoints);
5407       g_free ( last->data );
5408       vtl->current_track->trackpoints = g_list_remove_link ( vtl->current_track->trackpoints, last );
5409     }
5410     update_statusbar ( vtl );
5411
5412     vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
5413     return TRUE;
5414   }
5415
5416   if ( event->type == GDK_2BUTTON_PRESS )
5417   {
5418     /* subtract last (duplicate from double click) tp then end */
5419     if ( vtl->current_track && vtl->current_track->trackpoints && vtl->ct_x1 == vtl->ct_x2 && vtl->ct_y1 == vtl->ct_y2 )
5420     {
5421       GList *last = g_list_last(vtl->current_track->trackpoints);
5422       g_free ( last->data );
5423       vtl->current_track->trackpoints = g_list_remove_link ( vtl->current_track->trackpoints, last );
5424       /* undo last, then end */
5425       vtl->current_track = NULL;
5426     }
5427     vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
5428     return TRUE;
5429   }
5430
5431   if ( ! vtl->current_track )
5432   {
5433     gchar *name = get_new_unique_sublayer_name(vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, _("Track"));
5434     if ( ( name = a_dialog_new_track ( VIK_GTK_WINDOW_FROM_LAYER(vtl), vtl->tracks, name ) ) )
5435     {
5436       vtl->current_track = vik_track_new();
5437       vtl->current_track->visible = TRUE;
5438       vik_trw_layer_add_track ( vtl, name, vtl->current_track );
5439
5440       /* incase it was created by begin track */
5441       vik_window_enable_layer_tool ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)), VIK_LAYER_TRW, TOOL_CREATE_TRACK );
5442     }
5443     else
5444       return TRUE;
5445   }
5446   tp = vik_trackpoint_new();
5447   vik_viewport_screen_to_coord ( vvp, event->x, event->y, &(tp->coord) );
5448
5449   /* snap to other TP */
5450   if ( event->state & GDK_CONTROL_MASK )
5451   {
5452     VikTrackpoint *other_tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
5453     if ( other_tp )
5454       tp->coord = other_tp->coord;
5455   }
5456
5457   tp->newsegment = FALSE;
5458   tp->has_timestamp = FALSE;
5459   tp->timestamp = 0;
5460   vtl->current_track->trackpoints = g_list_append ( vtl->current_track->trackpoints, tp );
5461   /* Auto attempt to get elevation from DEM data (if it's available) */
5462   vik_track_apply_dem_data_last_trackpoint ( vtl->current_track );
5463
5464   vtl->ct_x1 = vtl->ct_x2;
5465   vtl->ct_y1 = vtl->ct_y2;
5466   vtl->ct_x2 = event->x;
5467   vtl->ct_y2 = event->y;
5468
5469   vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
5470   return TRUE;
5471 }
5472
5473 /*** New waypoint ****/
5474
5475 static gpointer tool_new_waypoint_create ( VikWindow *vw, VikViewport *vvp)
5476 {
5477   return vvp;
5478 }
5479
5480 static gboolean tool_new_waypoint_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
5481 {
5482   VikCoord coord;
5483   if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
5484     return FALSE;
5485   vik_viewport_screen_to_coord ( vvp, event->x, event->y, &coord );
5486   if (vik_trw_layer_new_waypoint ( vtl, VIK_GTK_WINDOW_FROM_LAYER(vtl), &coord ) && VIK_LAYER(vtl)->visible)
5487     vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
5488   return TRUE;
5489 }
5490
5491
5492 /*** Edit trackpoint ****/
5493
5494 static gpointer tool_edit_trackpoint_create ( VikWindow *vw, VikViewport *vvp)
5495 {
5496   tool_ed_t *t = g_new(tool_ed_t, 1);
5497   t->vvp = vvp;
5498   t->holding = FALSE;
5499   return t;
5500 }
5501
5502 static gboolean tool_edit_trackpoint_click ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data )
5503 {
5504   tool_ed_t *t = data;
5505   VikViewport *vvp = t->vvp;
5506   TPSearchParams params;
5507   /* OUTDATED DOCUMENTATION:
5508    find 5 pixel range on each side. then put these UTM, and a pointer
5509    to the winning track name (and maybe the winning track itself), and a
5510    pointer to the winning trackpoint, inside an array or struct. pass 
5511    this along, do a foreach on the tracks which will do a foreach on the 
5512    trackpoints. */
5513   params.vvp = vvp;
5514   params.x = event->x;
5515   params.y = event->y;
5516   params.closest_track_name = NULL;
5517   /* TODO: should get track listitem so we can break it up, make a new track, mess it up, all that. */
5518   params.closest_tp = NULL;
5519
5520   if ( event->button != 1 ) 
5521     return FALSE;
5522
5523   if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
5524     return FALSE;
5525
5526   if ( !vtl->vl.visible || !vtl->tracks_visible )
5527     return FALSE;
5528
5529   if ( vtl->current_tpl )
5530   {
5531     /* 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.) */
5532     VikTrackpoint *tp = VIK_TRACKPOINT(vtl->current_tpl->data);
5533     VikTrack *current_tr = VIK_TRACK(g_hash_table_lookup(vtl->tracks, vtl->current_tp_track_name));
5534     gint x, y;
5535     g_assert ( current_tr );
5536
5537     vik_viewport_coord_to_screen ( vvp, &(tp->coord), &x, &y );
5538
5539     if ( current_tr->visible && 
5540          abs(x - event->x) < TRACKPOINT_SIZE_APPROX &&
5541          abs(y - event->y) < TRACKPOINT_SIZE_APPROX ) {
5542       marker_begin_move ( t, event->x, event->y );
5543       return TRUE;
5544     }
5545
5546     vtl->last_tpl = vtl->current_tpl;
5547     vtl->last_tp_track_name = vtl->current_tp_track_name;
5548   }
5549
5550   g_hash_table_foreach ( vtl->tracks, (GHFunc) track_search_closest_tp, &params);
5551
5552   if ( params.closest_tp )
5553   {
5554     vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, g_hash_table_lookup ( vtl->tracks_iters, params.closest_track_name ), TRUE );
5555     vtl->current_tpl = params.closest_tpl;
5556     vtl->current_tp_track_name = params.closest_track_name;
5557     trw_layer_tpwin_init ( vtl );
5558     set_statusbar_msg_info_trkpt ( vtl, params.closest_tp );
5559     vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
5560     return TRUE;
5561   }
5562
5563   /* these aren't the droids you're looking for */
5564   return FALSE;
5565 }
5566
5567 static gboolean tool_edit_trackpoint_move ( VikTrwLayer *vtl, GdkEventMotion *event, gpointer data )
5568 {
5569   tool_ed_t *t = data;
5570   VikViewport *vvp = t->vvp;
5571
5572   if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
5573     return FALSE;
5574
5575   if ( t->holding )
5576   {
5577     VikCoord new_coord;
5578     vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
5579
5580     /* snap to TP */
5581     if ( event->state & GDK_CONTROL_MASK )
5582     {
5583       VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
5584       if ( tp && tp != vtl->current_tpl->data )
5585         new_coord = tp->coord;
5586     }
5587     //    VIK_TRACKPOINT(vtl->current_tpl->data)->coord = new_coord;
5588     { 
5589       gint x, y;
5590       vik_viewport_coord_to_screen ( vvp, &new_coord, &x, &y );
5591       marker_moveto ( t, x, y );
5592     } 
5593
5594     return TRUE;
5595   }
5596   return FALSE;
5597 }
5598
5599 static gboolean tool_edit_trackpoint_release ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data )
5600 {
5601   tool_ed_t *t = data;
5602   VikViewport *vvp = t->vvp;
5603
5604   if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
5605     return FALSE;
5606   if ( event->button != 1) 
5607     return FALSE;
5608
5609   if ( t->holding ) {
5610     VikCoord new_coord;
5611     vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
5612
5613     /* snap to TP */
5614     if ( event->state & GDK_CONTROL_MASK )
5615     {
5616       VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
5617       if ( tp && tp != vtl->current_tpl->data )
5618         new_coord = tp->coord;
5619     }
5620
5621     VIK_TRACKPOINT(vtl->current_tpl->data)->coord = new_coord;
5622
5623     marker_end_move ( t );
5624
5625     /* diff dist is diff from orig */
5626     if ( vtl->tpwin )
5627       vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, vtl->current_tp_track_name );
5628     /* can't join with itself! */
5629     trw_layer_cancel_last_tp ( vtl );
5630
5631     vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
5632     return TRUE;
5633   }
5634   return FALSE;
5635 }
5636
5637
5638 /*** Route Finder ***/
5639 static gpointer tool_route_finder_create ( VikWindow *vw, VikViewport *vvp)
5640 {
5641   return vvp;
5642 }
5643
5644 static gboolean tool_route_finder_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
5645 {
5646   VikCoord tmp;
5647   if ( !vtl ) return FALSE;
5648   vik_viewport_screen_to_coord ( vvp, event->x, event->y, &tmp );
5649   if ( event->button == 3 && vtl->route_finder_current_track ) {
5650     VikCoord *new_end;
5651     new_end = vik_track_cut_back_to_double_point ( vtl->route_finder_current_track );
5652     if ( new_end ) {
5653       vtl->route_finder_coord = *new_end;
5654       g_free ( new_end );
5655       vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
5656       /* remove last ' to:...' */
5657       if ( vtl->route_finder_current_track->comment ) {
5658         gchar *last_to = strrchr ( vtl->route_finder_current_track->comment, 't' );
5659         if ( last_to && (last_to - vtl->route_finder_current_track->comment > 1) ) {
5660           gchar *new_comment = g_strndup ( vtl->route_finder_current_track->comment,
5661                                            last_to - vtl->route_finder_current_track->comment - 1);
5662           vik_track_set_comment_no_copy ( vtl->route_finder_current_track, new_comment );
5663         }
5664       }
5665     }
5666   }
5667   else if ( vtl->route_finder_started || (event->state & GDK_CONTROL_MASK && vtl->route_finder_current_track) ) {
5668     struct LatLon start, end;
5669     gchar startlat[G_ASCII_DTOSTR_BUF_SIZE], startlon[G_ASCII_DTOSTR_BUF_SIZE];
5670     gchar endlat[G_ASCII_DTOSTR_BUF_SIZE], endlon[G_ASCII_DTOSTR_BUF_SIZE];
5671     gchar *url;
5672
5673     vik_coord_to_latlon ( &(vtl->route_finder_coord), &start );
5674     vik_coord_to_latlon ( &(tmp), &end );
5675     vtl->route_finder_coord = tmp; /* for continuations */
5676
5677     /* these are checked when adding a track from a file (vik_trw_layer_filein_add_track) */
5678     if ( event->state & GDK_CONTROL_MASK && vtl->route_finder_current_track ) {
5679       vtl->route_finder_append = TRUE;  // merge tracks. keep started true.
5680     } else {
5681       vtl->route_finder_check_added_track = TRUE;
5682       vtl->route_finder_started = FALSE;
5683     }
5684
5685     url = g_strdup_printf(GOOGLE_DIRECTIONS_STRING,
5686                           g_ascii_dtostr (startlat, G_ASCII_DTOSTR_BUF_SIZE, (gdouble) start.lat),
5687                           g_ascii_dtostr (startlon, G_ASCII_DTOSTR_BUF_SIZE, (gdouble) start.lon),
5688                           g_ascii_dtostr (endlat, G_ASCII_DTOSTR_BUF_SIZE, (gdouble) end.lat),
5689                           g_ascii_dtostr (endlon, G_ASCII_DTOSTR_BUF_SIZE, (gdouble) end.lon));
5690     a_babel_convert_from_url ( vtl, url, "kml", NULL, NULL );
5691     g_free ( url );
5692
5693     /* see if anything was done -- a track was added or appended to */
5694     if ( vtl->route_finder_check_added_track && vtl->route_finder_added_track_name ) {
5695       VikTrack *tr;
5696
5697       tr = g_hash_table_lookup ( vtl->tracks, vtl->route_finder_added_track_name );
5698
5699       if ( tr )
5700         vik_track_set_comment_no_copy ( tr, g_strdup_printf("from: %f,%f to: %f,%f", start.lat, start.lon, end.lat, end.lon ) );
5701  
5702       vtl->route_finder_current_track = tr;
5703
5704       g_free ( vtl->route_finder_added_track_name );
5705       vtl->route_finder_added_track_name = NULL;
5706     } else if ( vtl->route_finder_append == FALSE && vtl->route_finder_current_track ) {
5707       /* route_finder_append was originally TRUE but set to FALSE by filein_add_track */
5708       gchar *new_comment = g_strdup_printf("%s to: %f,%f", vtl->route_finder_current_track->comment, end.lat, end.lon );
5709       vik_track_set_comment_no_copy ( vtl->route_finder_current_track, new_comment );
5710     }
5711     vtl->route_finder_check_added_track = FALSE;
5712     vtl->route_finder_append = FALSE;
5713
5714     vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
5715   } else {
5716     vtl->route_finder_started = TRUE;
5717     vtl->route_finder_coord = tmp;
5718     vtl->route_finder_current_track = NULL;
5719   }
5720   return TRUE;
5721 }
5722
5723 /*** Show picture ****/
5724
5725 static gpointer tool_show_picture_create ( VikWindow *vw, VikViewport *vvp)
5726 {
5727   return vvp;
5728 }
5729
5730 /* Params are: vvp, event, last match found or NULL */
5731 static void tool_show_picture_wp ( char *name, VikWaypoint *wp, gpointer params[3] )
5732 {
5733   if ( wp->image && wp->visible )
5734   {
5735     gint x, y, slackx, slacky;
5736     GdkEventButton *event = (GdkEventButton *) params[1];
5737
5738     vik_viewport_coord_to_screen ( VIK_VIEWPORT(params[0]), &(wp->coord), &x, &y );
5739     slackx = wp->image_width / 2;
5740     slacky = wp->image_height / 2;
5741     if (    x <= event->x + slackx && x >= event->x - slackx
5742          && y <= event->y + slacky && y >= event->y - slacky )
5743     {
5744       params[2] = wp->image; /* we've found a match. however continue searching
5745                               * since we want to find the last match -- that
5746                               * is, the match that was drawn last. */
5747     }
5748   }
5749 }
5750
5751 static void trw_layer_show_picture ( gpointer pass_along[6] )
5752 {
5753   /* thanks to the Gaim people for showing me ShellExecute and g_spawn_command_line_async */
5754 #ifdef WINDOWS
5755   ShellExecute(NULL, NULL, (char *) pass_along[2], NULL, ".\\", 0);
5756 #else /* WINDOWS */
5757   GError *err = NULL;
5758   gchar *quoted_file = g_shell_quote ( (gchar *) pass_along[5] );
5759   gchar *cmd = g_strdup_printf ( "%s %s", a_vik_get_image_viewer(), quoted_file );
5760   g_free ( quoted_file );
5761   if ( ! g_spawn_command_line_async ( cmd, &err ) )
5762     {
5763       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() );
5764       g_error_free ( err );
5765     }
5766   g_free ( cmd );
5767 #endif /* WINDOWS */
5768 }
5769
5770 static gboolean tool_show_picture_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
5771 {
5772   gpointer params[3] = { vvp, event, NULL };
5773   if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
5774     return FALSE;
5775   g_hash_table_foreach ( vtl->waypoints, (GHFunc) tool_show_picture_wp, params );
5776   if ( params[2] )
5777   {
5778     static gpointer pass_along[6];
5779     pass_along[0] = vtl;
5780     pass_along[5] = params[2];
5781     trw_layer_show_picture ( pass_along );
5782     return TRUE; /* found a match */
5783   }
5784   else
5785     return FALSE; /* go through other layers, searching for a match */
5786 }
5787
5788 /***************************************************************************
5789  ** End tool code 
5790  ***************************************************************************/
5791
5792
5793
5794
5795
5796 static void image_wp_make_list ( char *name, VikWaypoint *wp, GSList **pics )
5797 {
5798   if ( wp->image && ( ! a_thumbnails_exists ( wp->image ) ) )
5799     *pics = g_slist_append ( *pics, (gpointer) g_strdup ( wp->image ) );
5800 }
5801
5802 /* Structure for thumbnail creating data used in the background thread */
5803 typedef struct {
5804   VikTrwLayer *vtl; // Layer needed for redrawing
5805   GSList *pics;     // Image list
5806 } thumbnail_create_thread_data;
5807
5808 static int create_thumbnails_thread ( thumbnail_create_thread_data *tctd, gpointer threaddata )
5809 {
5810   guint total = g_slist_length(tctd->pics), done = 0;
5811   while ( tctd->pics )
5812   {
5813     a_thumbnails_create ( (gchar *) tctd->pics->data );
5814     int result = a_background_thread_progress ( threaddata, ((gdouble) ++done) / total );
5815     if ( result != 0 )
5816       return -1; /* Abort thread */
5817
5818     tctd->pics = tctd->pics->next;
5819   }
5820
5821   // Redraw to show the thumbnails as they are now created
5822   if ( IS_VIK_LAYER(tctd->vtl) )
5823     vik_layer_emit_update ( VIK_LAYER(tctd->vtl), TRUE ); // Yes update from background thread
5824
5825   return 0;
5826 }
5827
5828 static void thumbnail_create_thread_free ( thumbnail_create_thread_data *tctd )
5829 {
5830   while ( tctd->pics )
5831   {
5832     g_free ( tctd->pics->data );
5833     tctd->pics = g_slist_delete_link ( tctd->pics, tctd->pics );
5834   }
5835   g_free ( tctd );
5836 }
5837
5838 void trw_layer_verify_thumbnails ( VikTrwLayer *vtl, GtkWidget *vp )
5839 {
5840   if ( ! vtl->has_verified_thumbnails )
5841   {
5842     GSList *pics = NULL;
5843     g_hash_table_foreach ( vtl->waypoints, (GHFunc) image_wp_make_list, &pics );
5844     if ( pics )
5845     {
5846       gint len = g_slist_length ( pics );
5847       gchar *tmp = g_strdup_printf ( _("Creating %d Image Thumbnails..."), len );
5848       thumbnail_create_thread_data *tctd = g_malloc ( sizeof(thumbnail_create_thread_data) );
5849       tctd->vtl = vtl;
5850       tctd->pics = pics;
5851       a_background_thread ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
5852                             tmp,
5853                             (vik_thr_func) create_thumbnails_thread,
5854                             tctd,
5855                             (vik_thr_free_func) thumbnail_create_thread_free,
5856                             NULL,
5857                             len );
5858       g_free ( tmp );
5859     }
5860   }
5861 }
5862
5863 VikCoordMode vik_trw_layer_get_coord_mode ( VikTrwLayer *vtl )
5864 {
5865   return vtl->coord_mode;
5866 }
5867
5868
5869
5870 static void waypoint_convert ( const gchar *name, VikWaypoint *wp, VikCoordMode *dest_mode )
5871 {
5872   vik_coord_convert ( &(wp->coord), *dest_mode );
5873 }
5874
5875 static void track_convert ( const gchar *name, VikTrack *tr, VikCoordMode *dest_mode )
5876 {
5877   vik_track_convert ( tr, *dest_mode );
5878 }
5879
5880 static void trw_layer_change_coord_mode ( VikTrwLayer *vtl, VikCoordMode dest_mode )
5881 {
5882   if ( vtl->coord_mode != dest_mode )
5883   {
5884     vtl->coord_mode = dest_mode;
5885     g_hash_table_foreach ( vtl->waypoints, (GHFunc) waypoint_convert, &dest_mode );
5886     g_hash_table_foreach ( vtl->tracks, (GHFunc) track_convert, &dest_mode );
5887   }
5888 }
5889
5890 VikWaypoint *vik_trw_layer_get_waypoint ( VikTrwLayer *vtl, gchar *name )
5891 {
5892   return g_hash_table_lookup ( vtl->waypoints, name );
5893 }
5894
5895 VikTrack *vik_trw_layer_get_track ( VikTrwLayer *vtl, const gchar *name )
5896 {
5897   return g_hash_table_lookup ( vtl->tracks, name );
5898 }
5899
5900 static void trw_layer_set_menu_selection ( VikTrwLayer *vtl, guint16 selection )
5901 {
5902   vtl->menu_selection = selection;
5903 }
5904
5905 static guint16 trw_layer_get_menu_selection ( VikTrwLayer *vtl )
5906 {
5907   return (vtl->menu_selection);
5908 }
5909
5910 /* ----------- Downloading maps along tracks --------------- */
5911
5912 static int get_download_area_width(VikViewport *vvp, gdouble zoom_level, struct LatLon *wh)
5913 {
5914   /* TODO: calculating based on current size of viewport */
5915   const gdouble w_at_zoom_0_125 = 0.0013;
5916   const gdouble h_at_zoom_0_125 = 0.0011;
5917   gdouble zoom_factor = zoom_level/0.125;
5918
5919   wh->lat = h_at_zoom_0_125 * zoom_factor;
5920   wh->lon = w_at_zoom_0_125 * zoom_factor;
5921
5922   return 0;   /* all OK */
5923 }
5924
5925 static VikCoord *get_next_coord(VikCoord *from, VikCoord *to, struct LatLon *dist, gdouble gradient)
5926 {
5927   if ((dist->lon >= ABS(to->east_west - from->east_west)) &&
5928       (dist->lat >= ABS(to->north_south - from->north_south)))
5929     return NULL;
5930
5931   VikCoord *coord = g_malloc(sizeof(VikCoord));
5932   coord->mode = VIK_COORD_LATLON;
5933
5934   if (ABS(gradient) < 1) {
5935     if (from->east_west > to->east_west)
5936       coord->east_west = from->east_west - dist->lon;
5937     else
5938       coord->east_west = from->east_west + dist->lon;
5939     coord->north_south = gradient * (coord->east_west - from->east_west) + from->north_south;
5940   } else {
5941     if (from->north_south > to->north_south)
5942       coord->north_south = from->north_south - dist->lat;
5943     else
5944       coord->north_south = from->north_south + dist->lat;
5945     coord->east_west = (1/gradient) * (coord->north_south - from->north_south) + from->north_south;
5946   }
5947
5948   return coord;
5949 }
5950
5951 static GList *add_fillins(GList *list, VikCoord *from, VikCoord *to, struct LatLon *dist)
5952 {
5953   /* TODO: handle virtical track (to->east_west - from->east_west == 0) */
5954   gdouble gradient = (to->north_south - from->north_south)/(to->east_west - from->east_west);
5955
5956   VikCoord *next = from;
5957   while (TRUE) {
5958     if ((next = get_next_coord(next, to, dist, gradient)) == NULL)
5959         break;
5960     list = g_list_prepend(list, next);
5961   }
5962
5963   return list;
5964 }
5965
5966 void vik_track_download_map(VikTrack *tr, VikMapsLayer *vml, VikViewport *vvp, gdouble zoom_level)
5967 {
5968   typedef struct _Rect {
5969     VikCoord tl;
5970     VikCoord br;
5971     VikCoord center;
5972   } Rect;
5973 #define GLRECT(iter) ((Rect *)((iter)->data))
5974
5975   struct LatLon wh;
5976   GList *rects_to_download = NULL;
5977   GList *rect_iter;
5978
5979   if (get_download_area_width(vvp, zoom_level, &wh))
5980     return;
5981
5982   GList *iter = tr->trackpoints;
5983   if (!iter)
5984     return;
5985
5986   gboolean new_map = TRUE;
5987   VikCoord *cur_coord, tl, br;
5988   Rect *rect;
5989   while (iter) {
5990     cur_coord = &(VIK_TRACKPOINT(iter->data))->coord;
5991     if (new_map) {
5992       vik_coord_set_area(cur_coord, &wh, &tl, &br);
5993       rect = g_malloc(sizeof(Rect));
5994       rect->tl = tl;
5995       rect->br = br;
5996       rect->center = *cur_coord;
5997       rects_to_download = g_list_prepend(rects_to_download, rect);
5998       new_map = FALSE;
5999       iter = iter->next;
6000       continue;
6001     }
6002     gboolean found = FALSE;
6003     for (rect_iter = rects_to_download; rect_iter; rect_iter = rect_iter->next) {
6004       if (vik_coord_inside(cur_coord, &GLRECT(rect_iter)->tl, &GLRECT(rect_iter)->br)) {
6005         found = TRUE;
6006         break;
6007       }
6008     }
6009     if (found)
6010         iter = iter->next;
6011     else
6012       new_map = TRUE;
6013   }
6014
6015   GList *fillins = NULL;
6016   /* 'fillin' doesn't work in UTM mode - potentially ending up in massive loop continually allocating memory - hence don't do it */
6017   /* seems that ATM the function get_next_coord works only for LATLON */
6018   if ( cur_coord->mode == VIK_COORD_LATLON ) {
6019     /* fill-ins for far apart points */
6020     GList *cur_rect, *next_rect;
6021     for (cur_rect = rects_to_download;
6022          (next_rect = cur_rect->next) != NULL;
6023          cur_rect = cur_rect->next) {
6024       if ((wh.lon < ABS(GLRECT(cur_rect)->center.east_west - GLRECT(next_rect)->center.east_west)) ||
6025           (wh.lat < ABS(GLRECT(cur_rect)->center.north_south - GLRECT(next_rect)->center.north_south))) {
6026         fillins = add_fillins(fillins, &GLRECT(cur_rect)->center, &GLRECT(next_rect)->center, &wh);
6027       }
6028     }
6029   } else
6030     g_message("%s: this feature works only in Mercator mode", __FUNCTION__);
6031
6032   if (fillins) {
6033     GList *iter = fillins;
6034     while (iter) {
6035       cur_coord = (VikCoord *)(iter->data);
6036       vik_coord_set_area(cur_coord, &wh, &tl, &br);
6037       rect = g_malloc(sizeof(Rect));
6038       rect->tl = tl;
6039       rect->br = br;
6040       rect->center = *cur_coord;
6041       rects_to_download = g_list_prepend(rects_to_download, rect);
6042       iter = iter->next;
6043     }
6044   }
6045
6046   for (rect_iter = rects_to_download; rect_iter; rect_iter = rect_iter->next) {
6047     maps_layer_download_section_without_redraw(vml, vvp, &(((Rect *)(rect_iter->data))->tl), &(((Rect *)(rect_iter->data))->br), zoom_level);
6048   }
6049
6050   if (fillins) {
6051     for (iter = fillins; iter; iter = iter->next)
6052       g_free(iter->data);
6053     g_list_free(fillins);
6054   }
6055   if (rects_to_download) {
6056     for (rect_iter = rects_to_download; rect_iter; rect_iter = rect_iter->next)
6057       g_free(rect_iter->data);
6058     g_list_free(rects_to_download);
6059   }
6060 }
6061
6062 static void trw_layer_download_map_along_track_cb ( gpointer pass_along[6] )
6063 {
6064   VikMapsLayer *vml;
6065   gint selected_map, default_map;
6066   gchar *zoomlist[] = {"0.125", "0.25", "0.5", "1", "2", "4", "8", "16", "32", "64", "128", "256", "512", "1024", NULL };
6067   gdouble zoom_vals[] = {0.125, 0.25, 0.5, 1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024};
6068   gint selected_zoom, default_zoom;
6069   int i,j;
6070
6071
6072   VikTrwLayer *vtl = pass_along[0];
6073   VikLayersPanel *vlp = pass_along[1];
6074   VikTrack *tr = (VikTrack *) g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, pass_along[3] );
6075   VikViewport *vvp = vik_window_viewport((VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl)));
6076
6077   GList *vmls = vik_layers_panel_get_all_layers_of_type(vlp, VIK_LAYER_MAPS, TRUE); // Includes hidden map layer types
6078   int num_maps = g_list_length(vmls);
6079
6080   if (!num_maps) {
6081     a_dialog_msg(VIK_GTK_WINDOW_FROM_LAYER(vtl), GTK_MESSAGE_ERROR, _("No map layer in use. Create one first"), NULL);
6082     return;
6083   }
6084
6085   gchar **map_names = g_malloc(1 + num_maps * sizeof(gpointer));
6086   VikMapsLayer **map_layers = g_malloc(1 + num_maps * sizeof(gpointer));
6087
6088   gchar **np = map_names;
6089   VikMapsLayer **lp = map_layers;
6090   for (i = 0; i < num_maps; i++) {
6091     gboolean dup = FALSE;
6092     vml = (VikMapsLayer *)(vmls->data);
6093     for (j = 0; j < i; j++) { /* no duplicate allowed */
6094       if (vik_maps_layer_get_map_type(vml) == vik_maps_layer_get_map_type(map_layers[j])) {
6095         dup = TRUE;
6096         break;
6097       }
6098     }
6099     if (!dup) {
6100       *lp++ = vml;
6101       *np++ = vik_maps_layer_get_map_label(vml);
6102     }
6103     vmls = vmls->next;
6104   }
6105   *lp = NULL;
6106   *np = NULL;
6107   num_maps = lp - map_layers;
6108
6109   for (default_map = 0; default_map < num_maps; default_map++) {
6110     /* TODO: check for parent layer's visibility */
6111     if (VIK_LAYER(map_layers[default_map])->visible)
6112       break;
6113   }
6114   default_map = (default_map == num_maps) ? 0 : default_map;
6115
6116   gdouble cur_zoom = vik_viewport_get_zoom(vvp);
6117   for (default_zoom = 0; default_zoom < sizeof(zoom_vals)/sizeof(gdouble); default_zoom++) {
6118     if (cur_zoom == zoom_vals[default_zoom])
6119       break;
6120   }
6121   default_zoom = (default_zoom == sizeof(zoom_vals)/sizeof(gdouble)) ? sizeof(zoom_vals)/sizeof(gdouble) - 1 : default_zoom;
6122
6123   if (!a_dialog_map_n_zoom(VIK_GTK_WINDOW_FROM_LAYER(vtl), map_names, default_map, zoomlist, default_zoom, &selected_map, &selected_zoom))
6124     goto done;
6125
6126   vik_track_download_map(tr, map_layers[selected_map], vvp, zoom_vals[selected_zoom]);
6127
6128 done:
6129   for (i = 0; i < num_maps; i++)
6130     g_free(map_names[i]);
6131   g_free(map_names);
6132   g_free(map_layers);
6133
6134   g_list_free(vmls);
6135
6136 }
6137
6138 /**** lowest waypoint number calculation ***/
6139 static gint highest_wp_number_name_to_number(const gchar *name) {
6140   if ( strlen(name) == 3 ) {
6141     int n = atoi(name);
6142     if ( n < 100 && name[0] != '0' )
6143       return -1;
6144     if ( n < 10 && name[0] != '0' )
6145       return -1;
6146     return n;
6147   }
6148   return -1;
6149 }
6150
6151
6152 static void highest_wp_number_reset(VikTrwLayer *vtl)
6153 {
6154   vtl->highest_wp_number = -1;
6155 }
6156
6157 static void highest_wp_number_add_wp(VikTrwLayer *vtl, const gchar *new_wp_name)
6158 {
6159   /* if is bigger that top, add it */
6160   gint new_wp_num = highest_wp_number_name_to_number(new_wp_name);
6161   if ( new_wp_num > vtl->highest_wp_number )
6162     vtl->highest_wp_number = new_wp_num;
6163 }
6164
6165 static void highest_wp_number_remove_wp(VikTrwLayer *vtl, const gchar *old_wp_name)
6166 {
6167   /* if wasn't top, do nothing. if was top, count backwards until we find one used */
6168   gint old_wp_num = highest_wp_number_name_to_number(old_wp_name);
6169   if ( vtl->highest_wp_number == old_wp_num ) {
6170     gchar buf[4];
6171     vtl->highest_wp_number --;
6172
6173     g_snprintf(buf,4,"%03d", vtl->highest_wp_number );
6174     /* search down until we find something that *does* exist */
6175
6176     while ( vtl->highest_wp_number > 0 && ! g_hash_table_lookup ( vtl->waypoints, buf ) ) {
6177       vtl->highest_wp_number --;
6178       g_snprintf(buf,4,"%03d", vtl->highest_wp_number );
6179     }
6180   }
6181 }
6182
6183 /* get lowest unused number */
6184 static gchar *highest_wp_number_get(VikTrwLayer *vtl)
6185 {
6186   gchar buf[4];
6187   if ( vtl->highest_wp_number < 0 || vtl->highest_wp_number >= 999 )
6188     return NULL;
6189   g_snprintf(buf,4,"%03d", vtl->highest_wp_number+1 );
6190   return g_strdup(buf);
6191 }