]> git.street.me.uk Git - andy/viking.git/blob - src/viktrwlayer.c
1598836e09d508641f724e341baa754cbb8aec7e
[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  *
9  * This program is free software; you can redistribute it and/or modify
10  * it under the terms of the GNU General Public License as published by
11  * the Free Software Foundation; either version 2 of the License, or
12  * (at your option) any later version.
13  *
14  * This program is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17  * GNU General Public License for more details.
18  *
19  * You should have received a copy of the GNU General Public License
20  * along with this program; if not, write to the Free Software
21  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
22  *
23  */
24
25 #define WAYPOINT_FONT "Sans 8"
26
27 /* WARNING: If you go beyond this point, we are NOT responsible for any ill effects on your sanity */
28 /* viktrwlayer.c -- 2200 lines can make a difference in the state of things */
29
30 #ifdef HAVE_CONFIG_H
31 #include "config.h"
32 #endif
33
34 #include "viking.h"
35 #include "vikmapslayer.h"
36 #include "viktrwlayer_tpwin.h"
37 #include "viktrwlayer_propwin.h"
38 #include "garminsymbols.h"
39 #include "thumbnails.h"
40 #include "background.h"
41 #include "gpx.h"
42 #include "babel.h"
43 #include "dem.h"
44 #include "dems.h"
45 #include "geonamessearch.h"
46 #ifdef VIK_CONFIG_OPENSTREETMAP
47 #include "osm-traces.h"
48 #endif
49 #include "acquire.h"
50 #include "util.h"
51
52 #include "icons/icons.h"
53
54 #ifdef HAVE_MATH_H
55 #include <math.h>
56 #endif
57 #ifdef HAVE_STRING_H
58 #include <string.h>
59 #endif
60 #ifdef HAVE_STDLIB_H
61 #include <stdlib.h>
62 #endif
63 #include <stdio.h>
64 #include <ctype.h>
65
66 #include <gdk/gdkkeysyms.h>
67 #include <glib.h>
68 #include <glib/gstdio.h>
69 #include <glib/gi18n.h>
70
71 /* Relax some dependencies */
72 #if ! GLIB_CHECK_VERSION(2,12,0)
73 static gboolean return_true (gpointer a, gpointer b, gpointer c) { return TRUE; }
74 static g_hash_table_remove_all (GHashTable *ght) { g_hash_table_foreach_remove ( ght, (GHRFunc) return_true, FALSE ); }
75 #endif
76
77 #define GOOGLE_DIRECTIONS_STRING "maps.google.com/maps?q=from:%s,%s+to:%s,%s&output=kml"
78 #define VIK_TRW_LAYER_TRACK_GC 13
79 #define VIK_TRW_LAYER_TRACK_GC_RATES 10
80 #define VIK_TRW_LAYER_TRACK_GC_MIN 0
81 #define VIK_TRW_LAYER_TRACK_GC_MAX 11
82 #define VIK_TRW_LAYER_TRACK_GC_BLACK 12
83
84 #define DRAWMODE_BY_TRACK 0
85 #define DRAWMODE_BY_VELOCITY 1
86 #define DRAWMODE_ALL_BLACK 2
87
88 #define POINTS 1
89 #define LINES 2
90
91 /* this is how it knows when you click if you are clicking close to a trackpoint. */
92 #define TRACKPOINT_SIZE_APPROX 5
93 #define WAYPOINT_SIZE_APPROX 5
94
95 #define MIN_STOP_LENGTH 15
96 #define MAX_STOP_LENGTH 86400
97 #define DRAW_ELEVATION_FACTOR 30 /* height of elevation plotting, sort of relative to zoom level ("mpp" that isn't mpp necessarily) */
98                                  /* this is multiplied by user-inputted value from 1-100. */
99 enum {
100 VIK_TRW_LAYER_SUBLAYER_TRACKS,
101 VIK_TRW_LAYER_SUBLAYER_WAYPOINTS,
102 VIK_TRW_LAYER_SUBLAYER_TRACK,
103 VIK_TRW_LAYER_SUBLAYER_WAYPOINT
104 };
105
106 enum { WP_SYMBOL_FILLED_SQUARE, WP_SYMBOL_SQUARE, WP_SYMBOL_CIRCLE, WP_SYMBOL_X, WP_NUM_SYMBOLS };
107
108 struct _VikTrwLayer {
109   VikLayer vl;
110   GHashTable *tracks;
111   GHashTable *tracks_iters;
112   GHashTable *waypoints_iters;
113   GHashTable *waypoints;
114   GtkTreeIter waypoints_iter, tracks_iter;
115   gboolean tracks_visible, waypoints_visible;
116   guint8 drawmode;
117   guint8 drawpoints;
118   guint8 drawelevation;
119   guint8 elevation_factor;
120   guint8 drawstops;
121   guint32 stop_length;
122   guint8 drawlines;
123   guint8 line_thickness;
124   guint8 bg_line_thickness;
125
126   guint8 wp_symbol;
127   guint8 wp_size;
128   gboolean wp_draw_symbols;
129
130   gdouble velocity_min, velocity_max;
131   GArray *track_gc;
132   guint16 track_gc_iter;
133   GdkGC *current_track_gc;
134   GdkGC *track_bg_gc;
135   GdkGC *waypoint_gc;
136   GdkGC *waypoint_text_gc;
137   GdkGC *waypoint_bg_gc;
138   GdkFont *waypoint_font;
139   VikTrack *current_track;
140   guint16 ct_x1, ct_y1, ct_x2, ct_y2;
141   gboolean ct_sync_done;
142
143
144   VikCoordMode coord_mode;
145
146   /* wp editing tool */
147   VikWaypoint *current_wp;
148   gchar *current_wp_name;
149   gboolean moving_wp;
150   gboolean waypoint_rightclick;
151
152   /* track editing tool */
153   GList *current_tpl;
154   gchar *current_tp_track_name;
155   VikTrwLayerTpwin *tpwin;
156
157   /* weird hack for joining tracks */
158   GList *last_tpl;
159   gchar *last_tp_track_name;
160
161   /* track editing tool -- more specifically, moving tps */
162   gboolean moving_tp;
163
164   /* magic scissors tool */
165   gboolean magic_scissors_started;
166   VikCoord magic_scissors_coord;
167   gboolean magic_scissors_check_added_track;
168   gchar *magic_scissors_added_track_name;
169   VikTrack *magic_scissors_current_track;
170   gboolean magic_scissors_append;
171
172   gboolean drawlabels;
173   gboolean drawimages;
174   guint8 image_alpha;
175   GQueue *image_cache;
176   guint8 image_size;
177   guint16 image_cache_size;
178
179   /* for waypoint text */
180   PangoLayout *wplabellayout;
181
182   gboolean has_verified_thumbnails;
183
184   GtkMenu *wp_right_click_menu;
185
186   /* menu */
187   VikStdLayerMenuItem menu_selection;
188
189   gint highest_wp_number;
190 };
191
192 /* A caached waypoint image. */
193 typedef struct {
194   GdkPixbuf *pixbuf;
195   gchar *image; /* filename */
196 } CachedPixbuf;
197
198 struct DrawingParams {
199   VikViewport *vp;
200   VikTrwLayer *vtl;
201   gdouble xmpp, ympp;
202   guint16 width, height;
203   const VikCoord *center;
204   gint track_gc_iter;
205   gboolean one_zone, lat_lon;
206   gdouble ce1, ce2, cn1, cn2;
207 };
208
209 static void vik_trw_layer_set_menu_selection(VikTrwLayer *vtl, guint16);
210 static guint16 vik_trw_layer_get_menu_selection(VikTrwLayer *vtl);
211
212 static void trw_layer_delete_item ( gpointer pass_along[6] );
213 static void trw_layer_copy_item_cb ( gpointer pass_along[6] );
214 static void trw_layer_cut_item_cb ( gpointer pass_along[6] );
215
216 static void trw_layer_find_maxmin_waypoints ( const gchar *name, const VikWaypoint *w, struct LatLon maxmin[2] );
217 static void trw_layer_find_maxmin_tracks ( const gchar *name, GList **t, struct LatLon maxmin[2] );     
218 static void trw_layer_find_maxmin (VikTrwLayer *vtl, struct LatLon maxmin[2]);
219
220 static void trw_layer_new_track_gcs ( VikTrwLayer *vtl, VikViewport *vp );
221 static void trw_layer_free_track_gcs ( VikTrwLayer *vtl );
222
223 static gint calculate_velocity ( VikTrwLayer *vtl, VikTrackpoint *tp1, VikTrackpoint *tp2 );
224 static void trw_layer_draw_track_cb ( const gchar *name, VikTrack *track, struct DrawingParams *dp );
225 static void trw_layer_draw_waypoint ( const gchar *name, VikWaypoint *wp, struct DrawingParams *dp );
226
227 static const gchar* trw_layer_layer_tooltip ( VikTrwLayer *vtl );
228 static const gchar* trw_layer_sublayer_tooltip ( VikTrwLayer *l, gint subtype, gpointer sublayer );
229
230 static void goto_coord ( gpointer *vlp, gpointer vvp, gpointer vl, const VikCoord *coord );
231 static void trw_layer_goto_track_startpoint ( gpointer pass_along[6] );
232 static void trw_layer_goto_track_endpoint ( gpointer pass_along[6] );
233 static void trw_layer_goto_track_max_speed ( gpointer pass_along[6] );
234 static void trw_layer_goto_track_max_alt ( gpointer pass_along[6] );
235 static void trw_layer_goto_track_min_alt ( gpointer pass_along[6] );
236 static void trw_layer_goto_track_center ( gpointer pass_along[6] );
237 static void trw_layer_merge_by_timestamp ( gpointer pass_along[6] );
238 static void trw_layer_merge_with_other ( gpointer pass_along[6] );
239 static void trw_layer_split_by_timestamp ( gpointer pass_along[6] );
240 static void trw_layer_split_by_n_points ( gpointer pass_along[6] );
241 static void trw_layer_download_map_along_track_cb (gpointer pass_along[6]);
242 static void trw_layer_centerize ( gpointer layer_and_vlp[2] );
243 static void trw_layer_auto_view ( gpointer layer_and_vlp[2] );
244 static void trw_layer_export ( gpointer layer_and_vlp[2], const gchar* title, const gchar* default_name, const gchar* trackname, guint file_type );
245 static void trw_layer_goto_wp ( gpointer layer_and_vlp[2] );
246 static void trw_layer_new_wp ( gpointer lav[2] );
247 static void trw_layer_auto_waypoints_view ( gpointer lav[2] );
248 static void trw_layer_auto_tracks_view ( gpointer lav[2] );
249 static void trw_layer_new_wikipedia_wp_viewport ( gpointer lav[2] );
250 static void trw_layer_new_wikipedia_wp_layer ( gpointer lav[2] );
251
252 /* pop-up items */
253 static void trw_layer_properties_item ( gpointer pass_along[6] );
254 static void trw_layer_goto_waypoint ( gpointer pass_along[6] );
255 static void trw_layer_waypoint_gc_webpage ( gpointer pass_along[6] );
256
257 static void trw_layer_realize_waypoint ( gchar *name, VikWaypoint *wp, gpointer pass_along[5] );
258 static void trw_layer_realize_track ( gchar *name, VikTrack *track, gpointer pass_along[5] );
259 static void init_drawing_params ( struct DrawingParams *dp, VikViewport *vp );
260
261
262 static void trw_layer_marshall( VikTrwLayer *vtl, guint8 **data, gint *len );
263 static VikTrwLayer *trw_layer_unmarshall( guint8 *data, gint len, VikViewport *vvp );
264
265 static gboolean trw_layer_set_param ( VikTrwLayer *vtl, guint16 id, VikLayerParamData data, VikViewport *vp, gboolean is_file_operation );
266 static VikLayerParamData trw_layer_get_param ( VikTrwLayer *vtl, guint16 id, gboolean is_file_operation );
267
268 static void trw_layer_del_item ( VikTrwLayer *vtl, gint subtype, gpointer sublayer );
269 static void trw_layer_cut_item ( VikTrwLayer *vtl, gint subtype, gpointer sublayer );
270
271 static void trw_layer_copy_item ( VikTrwLayer *vtl, gint subtype, gpointer sublayer, guint8 **item, guint *len );
272 static gboolean trw_layer_paste_item ( VikTrwLayer *vtl, gint subtype, guint8 *item, guint len );
273 static void trw_layer_free_copied_item ( gint subtype, gpointer item );
274 static void trw_layer_drag_drop_request ( VikTrwLayer *vtl_src, VikTrwLayer *vtl_dest, GtkTreeIter *src_item_iter, GtkTreePath *dest_path );
275
276 static void trw_layer_insert_tp_after_current_tp ( VikTrwLayer *vtl );
277 static void trw_layer_cancel_last_tp ( VikTrwLayer *vtl );
278 static void trw_layer_cancel_current_tp ( VikTrwLayer *vtl, gboolean destroy );
279 static void trw_layer_tpwin_response ( VikTrwLayer *vtl, gint response );
280 static void trw_layer_tpwin_init ( VikTrwLayer *vtl );
281
282 static gpointer tool_edit_trackpoint_create ( VikWindow *vw, VikViewport *vvp);
283 static gboolean tool_edit_trackpoint_click ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data );
284 static gboolean tool_edit_trackpoint_move ( VikTrwLayer *vtl, GdkEventMotion *event, gpointer data );
285 static gboolean tool_edit_trackpoint_release ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data );
286 static gpointer tool_show_picture_create ( VikWindow *vw, VikViewport *vvp);
287 static gboolean tool_show_picture_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp ); 
288 static gpointer tool_edit_waypoint_create ( VikWindow *vw, VikViewport *vvp);
289 static gboolean tool_edit_waypoint_click ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data );
290 static gboolean tool_edit_waypoint_move ( VikTrwLayer *vtl, GdkEventMotion *event, gpointer data );
291 static gboolean tool_edit_waypoint_release ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data );
292 static gpointer tool_begin_track_create ( VikWindow *vw, VikViewport *vvp);
293 static gboolean tool_begin_track_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp ); 
294 static gpointer tool_new_track_create ( VikWindow *vw, VikViewport *vvp);
295 static gboolean tool_new_track_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp ); 
296 static VikLayerToolFuncStatus tool_new_track_move ( VikTrwLayer *vtl, GdkEventMotion *event, VikViewport *vvp ); 
297 static gboolean tool_new_track_key_press ( VikTrwLayer *vtl, GdkEventKey *event, VikViewport *vvp ); 
298 static gpointer tool_new_waypoint_create ( VikWindow *vw, VikViewport *vvp);
299 static gboolean tool_new_waypoint_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp );
300 static gpointer tool_magic_scissors_create ( VikWindow *vw, VikViewport *vvp);
301 static gboolean tool_magic_scissors_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp );
302
303
304 static void cached_pixbuf_free ( CachedPixbuf *cp );
305 static gint cached_pixbuf_cmp ( CachedPixbuf *cp, const gchar *name );
306 static void trw_layer_verify_thumbnails ( VikTrwLayer *vtl, GtkWidget *vp );
307
308 static VikTrackpoint *closest_tp_in_five_pixel_interval ( VikTrwLayer *vtl, VikViewport *vvp, gint x, gint y );
309 static VikWaypoint *closest_wp_in_five_pixel_interval ( VikTrwLayer *vtl, VikViewport *vvp, gint x, gint y );
310
311 static void trw_layer_change_coord_mode ( VikTrwLayer *vtl, VikCoordMode dest_mode );
312
313 static gchar *get_new_unique_sublayer_name (VikTrwLayer *vtl, gint sublayer_type, const gchar *name);
314 static void waypoint_convert ( const gchar *name, VikWaypoint *wp, VikCoordMode *dest_mode );
315 static void track_convert ( const gchar *name, VikTrack *tr, VikCoordMode *dest_mode );
316
317 static gchar *highest_wp_number_get(VikTrwLayer *vtl);
318 static void highest_wp_number_reset(VikTrwLayer *vtl);
319 static void highest_wp_number_add_wp(VikTrwLayer *vtl, const gchar *new_wp_name);
320 static void highest_wp_number_remove_wp(VikTrwLayer *vtl, const gchar *old_wp_name);
321
322
323 static VikToolInterface trw_layer_tools[] = {
324   { N_("Create Waypoint"), (VikToolConstructorFunc) tool_new_waypoint_create,    NULL, NULL, NULL, 
325     (VikToolMouseFunc) tool_new_waypoint_click,    NULL, NULL, (VikToolKeyFunc) NULL, GDK_CURSOR_IS_PIXMAP, &cursor_addwp_pixbuf },
326
327   { N_("Create Track"),    (VikToolConstructorFunc) tool_new_track_create,       NULL, NULL, NULL, 
328     (VikToolMouseFunc) tool_new_track_click, (VikToolMouseMoveFunc) tool_new_track_move, NULL,
329     (VikToolKeyFunc) tool_new_track_key_press, GDK_CURSOR_IS_PIXMAP, &cursor_addtr_pixbuf },
330
331   { N_("Begin Track"),    (VikToolConstructorFunc) tool_begin_track_create,       NULL, NULL, NULL, 
332     (VikToolMouseFunc) tool_begin_track_click,       NULL, NULL, (VikToolKeyFunc) NULL, GDK_CURSOR_IS_PIXMAP, &cursor_begintr_pixbuf },
333
334   { N_("Edit Waypoint"),   (VikToolConstructorFunc) tool_edit_waypoint_create,   NULL, NULL, NULL, 
335     (VikToolMouseFunc) tool_edit_waypoint_click,   
336     (VikToolMouseMoveFunc) tool_edit_waypoint_move,
337     (VikToolMouseFunc) tool_edit_waypoint_release, (VikToolKeyFunc) NULL, GDK_CURSOR_IS_PIXMAP, &cursor_edwp_pixbuf },
338
339   { N_("Edit Trackpoint"), (VikToolConstructorFunc) tool_edit_trackpoint_create, NULL, NULL, NULL, 
340     (VikToolMouseFunc) tool_edit_trackpoint_click,
341     (VikToolMouseMoveFunc) tool_edit_trackpoint_move,
342     (VikToolMouseFunc) tool_edit_trackpoint_release, (VikToolKeyFunc) NULL, GDK_CURSOR_IS_PIXMAP, &cursor_edtr_pixbuf },
343
344   { N_("Show Picture"),    (VikToolConstructorFunc) tool_show_picture_create,    NULL, NULL, NULL, 
345     (VikToolMouseFunc) tool_show_picture_click,    NULL, NULL, (VikToolKeyFunc) NULL, GDK_CURSOR_IS_PIXMAP, &cursor_showpic_pixbuf },
346
347   { N_("Magic Scissors"),  (VikToolConstructorFunc) tool_magic_scissors_create,  NULL, NULL, NULL,
348     (VikToolMouseFunc) tool_magic_scissors_click, NULL, NULL, (VikToolKeyFunc) NULL, GDK_CURSOR_IS_PIXMAP, &cursor_iscissors_pixbuf },
349 };
350 enum { TOOL_CREATE_WAYPOINT=0, TOOL_CREATE_TRACK, TOOL_BEGIN_TRACK, TOOL_EDIT_WAYPOINT, TOOL_EDIT_TRACKPOINT, TOOL_SHOW_PICTURE, NUM_TOOLS };
351
352 /****** PARAMETERS ******/
353
354 static gchar *params_groups[] = { N_("Waypoints"), N_("Tracks"), N_("Waypoint Images") };
355 enum { GROUP_WAYPOINTS, GROUP_TRACKS, GROUP_IMAGES };
356
357 static gchar *params_drawmodes[] = { N_("Draw by Track"), N_("Draw by Velocity"), N_("All Tracks Black"), 0 };
358 static gchar *params_wpsymbols[] = { N_("Filled Square"), N_("Square"), N_("Circle"), N_("X"), 0 };
359
360
361 static VikLayerParamScale params_scales[] = {
362  /* min  max    step digits */
363  {  1,   10,    1,   0 }, /* line_thickness */
364  {  0.0, 99.0,  1,   2 }, /* velocity_min */
365  {  1.0, 100.0, 1.0, 2 }, /* velocity_max */
366                 /* 5 * step == how much to turn */
367  {  16,   128,  3.2, 0 }, /* image_size */
368  {   0,   255,  5,   0 }, /* image alpha */
369  {   5,   500,  5,   0 }, /* image cache_size */
370  {   0,   8,    1,   0 }, /* image cache_size */
371  {   1,  64,    1,   0 }, /* wpsize */
372  {   MIN_STOP_LENGTH, MAX_STOP_LENGTH, 1,   0 }, /* stop_length */
373  {   1, 100, 1,   0 }, /* stop_length */
374 };
375
376 VikLayerParam trw_layer_params[] = {
377   { "tracks_visible", VIK_LAYER_PARAM_BOOLEAN, VIK_LAYER_NOT_IN_PROPERTIES },
378   { "waypoints_visible", VIK_LAYER_PARAM_BOOLEAN, VIK_LAYER_NOT_IN_PROPERTIES },
379
380   { "drawmode", VIK_LAYER_PARAM_UINT, GROUP_TRACKS, N_("Track Drawing Mode:"), VIK_LAYER_WIDGET_RADIOGROUP, NULL },
381   { "drawlines", VIK_LAYER_PARAM_BOOLEAN, GROUP_TRACKS, N_("Draw Track Lines"), VIK_LAYER_WIDGET_CHECKBUTTON },
382   { "drawpoints", VIK_LAYER_PARAM_BOOLEAN, GROUP_TRACKS, N_("Draw Trackpoints"), VIK_LAYER_WIDGET_CHECKBUTTON },
383   { "drawelevation", VIK_LAYER_PARAM_BOOLEAN, GROUP_TRACKS, N_("Draw Elevation"), VIK_LAYER_WIDGET_CHECKBUTTON },
384   { "elevation_factor", VIK_LAYER_PARAM_UINT, GROUP_TRACKS, N_("Draw Elevation Height %:"), VIK_LAYER_WIDGET_HSCALE, params_scales + 9 },
385
386   { "drawstops", VIK_LAYER_PARAM_BOOLEAN, GROUP_TRACKS, N_("Draw Stops"), VIK_LAYER_WIDGET_CHECKBUTTON },
387   { "stop_length", VIK_LAYER_PARAM_UINT, GROUP_TRACKS, N_("Min Stop Length (seconds):"), VIK_LAYER_WIDGET_SPINBUTTON, params_scales + 8 },
388
389   { "line_thickness", VIK_LAYER_PARAM_UINT, GROUP_TRACKS, N_("Track Thickness:"), VIK_LAYER_WIDGET_SPINBUTTON, params_scales + 0 },
390   { "bg_line_thickness", VIK_LAYER_PARAM_UINT, GROUP_TRACKS, N_("Track BG Thickness:"), VIK_LAYER_WIDGET_SPINBUTTON, params_scales + 6 },
391   { "trackbgcolor", VIK_LAYER_PARAM_COLOR, GROUP_TRACKS, N_("Track Background Color"), VIK_LAYER_WIDGET_COLOR, 0 },
392   { "velocity_min", VIK_LAYER_PARAM_DOUBLE, GROUP_TRACKS, N_("Min Track Velocity:"), VIK_LAYER_WIDGET_SPINBUTTON, params_scales + 1 },
393   { "velocity_max", VIK_LAYER_PARAM_DOUBLE, GROUP_TRACKS, N_("Max Track Velocity:"), VIK_LAYER_WIDGET_SPINBUTTON, params_scales + 2 },
394
395   { "drawlabels", VIK_LAYER_PARAM_BOOLEAN, GROUP_WAYPOINTS, N_("Draw Labels"), VIK_LAYER_WIDGET_CHECKBUTTON },
396   { "wpcolor", VIK_LAYER_PARAM_COLOR, GROUP_WAYPOINTS, N_("Waypoint Color:"), VIK_LAYER_WIDGET_COLOR, 0 },
397   { "wptextcolor", VIK_LAYER_PARAM_COLOR, GROUP_WAYPOINTS, N_("Waypoint Text:"), VIK_LAYER_WIDGET_COLOR, 0 },
398   { "wpbgcolor", VIK_LAYER_PARAM_COLOR, GROUP_WAYPOINTS, N_("Background:"), VIK_LAYER_WIDGET_COLOR, 0 },
399   { "wpbgand", VIK_LAYER_PARAM_BOOLEAN, GROUP_WAYPOINTS, N_("Fake BG Color Translucency:"), VIK_LAYER_WIDGET_CHECKBUTTON, 0 },
400   { "wpsymbol", VIK_LAYER_PARAM_UINT, GROUP_WAYPOINTS, N_("Waypoint marker:"), VIK_LAYER_WIDGET_RADIOGROUP, NULL },
401   { "wpsize", VIK_LAYER_PARAM_UINT, GROUP_WAYPOINTS, N_("Waypoint size:"), VIK_LAYER_WIDGET_SPINBUTTON, params_scales + 7 },
402   { "wpsyms", VIK_LAYER_PARAM_BOOLEAN, GROUP_WAYPOINTS, N_("Draw Waypoint Symbols:"), VIK_LAYER_WIDGET_CHECKBUTTON },
403
404   { "drawimages", VIK_LAYER_PARAM_BOOLEAN, GROUP_IMAGES, N_("Draw Waypoint Images"), VIK_LAYER_WIDGET_CHECKBUTTON },
405   { "image_size", VIK_LAYER_PARAM_UINT, GROUP_IMAGES, N_("Image Size (pixels):"), VIK_LAYER_WIDGET_HSCALE, params_scales + 3 },
406   { "image_alpha", VIK_LAYER_PARAM_UINT, GROUP_IMAGES, N_("Image Alpha:"), VIK_LAYER_WIDGET_HSCALE, params_scales + 4 },
407   { "image_cache_size", VIK_LAYER_PARAM_UINT, GROUP_IMAGES, N_("Image Memory Cache Size:"), VIK_LAYER_WIDGET_HSCALE, params_scales + 5 },
408 };
409
410 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 };
411
412 /*** TO ADD A PARAM:
413  *** 1) Add to trw_layer_params and enumeration
414  *** 2) Handle in get_param & set_param (presumably adding on to VikTrwLayer struct)
415  ***/
416
417 /****** END PARAMETERS ******/
418
419 VikLayerInterface vik_trw_layer_interface = {
420   "TrackWaypoint",
421   &viktrwlayer_pixbuf,
422
423   trw_layer_tools,
424   sizeof(trw_layer_tools) / sizeof(VikToolInterface),
425
426   trw_layer_params,
427   NUM_PARAMS,
428   params_groups, /* params_groups */
429   sizeof(params_groups)/sizeof(params_groups[0]),    /* number of groups */
430
431   VIK_MENU_ITEM_ALL,
432
433   (VikLayerFuncCreate)                  vik_trw_layer_create,
434   (VikLayerFuncRealize)                 vik_trw_layer_realize,
435   (VikLayerFuncPostRead)                trw_layer_verify_thumbnails,
436   (VikLayerFuncFree)                    vik_trw_layer_free,
437
438   (VikLayerFuncProperties)              NULL,
439   (VikLayerFuncDraw)                    vik_trw_layer_draw,
440   (VikLayerFuncChangeCoordMode)         trw_layer_change_coord_mode,
441
442   (VikLayerFuncSetMenuItemsSelection)   vik_trw_layer_set_menu_selection,
443   (VikLayerFuncGetMenuItemsSelection)   vik_trw_layer_get_menu_selection,
444
445   (VikLayerFuncAddMenuItems)            vik_trw_layer_add_menu_items,
446   (VikLayerFuncSublayerAddMenuItems)    vik_trw_layer_sublayer_add_menu_items,
447
448   (VikLayerFuncSublayerRenameRequest)   vik_trw_layer_sublayer_rename_request,
449   (VikLayerFuncSublayerToggleVisible)   vik_trw_layer_sublayer_toggle_visible,
450   (VikLayerFuncSublayerTooltip)         trw_layer_sublayer_tooltip,
451   (VikLayerFuncLayerTooltip)            trw_layer_layer_tooltip,
452   (VikLayerFuncLayerSelected)           vik_trw_layer_selected,
453
454   (VikLayerFuncMarshall)                trw_layer_marshall,
455   (VikLayerFuncUnmarshall)              trw_layer_unmarshall,
456
457   (VikLayerFuncSetParam)                trw_layer_set_param,
458   (VikLayerFuncGetParam)                trw_layer_get_param,
459
460   (VikLayerFuncReadFileData)            a_gpspoint_read_file,
461   (VikLayerFuncWriteFileData)           a_gpspoint_write_file,
462
463   (VikLayerFuncDeleteItem)              trw_layer_del_item,
464   (VikLayerFuncCutItem)                 trw_layer_cut_item,
465   (VikLayerFuncCopyItem)                trw_layer_copy_item,
466   (VikLayerFuncPasteItem)               trw_layer_paste_item,
467   (VikLayerFuncFreeCopiedItem)          trw_layer_free_copied_item,
468   
469   (VikLayerFuncDragDropRequest)         trw_layer_drag_drop_request,
470 };
471
472 /* for copy & paste (I think?) */
473 typedef struct {
474   guint len;
475   guint8 data[0];
476   //  gchar *name;
477   //  VikWaypoint *wp;
478 } FlatItem;
479
480 GType vik_trw_layer_get_type ()
481 {
482   static GType vtl_type = 0;
483
484   if (!vtl_type)
485   {
486     static const GTypeInfo vtl_info =
487     {
488       sizeof (VikTrwLayerClass),
489       NULL, /* base_init */
490       NULL, /* base_finalize */
491       NULL, /* class init */
492       NULL, /* class_finalize */
493       NULL, /* class_data */
494       sizeof (VikTrwLayer),
495       0,
496       NULL /* instance init */
497     };
498     vtl_type = g_type_register_static ( VIK_LAYER_TYPE, "VikTrwLayer", &vtl_info, 0 );
499   }
500
501   return vtl_type;
502 }
503
504 static void trw_layer_del_item ( VikTrwLayer *vtl, gint subtype, gpointer sublayer )
505 {
506   static gpointer pass_along[6];
507   if (!sublayer) {
508     return;
509   }
510   
511   pass_along[0] = vtl;
512   pass_along[1] = NULL;
513   pass_along[2] = GINT_TO_POINTER (subtype);
514   pass_along[3] = sublayer;
515   pass_along[4] = NULL;
516   pass_along[5] = NULL;
517
518   trw_layer_delete_item ( pass_along );
519 }
520
521 static void trw_layer_cut_item ( VikTrwLayer *vtl, gint subtype, gpointer sublayer )
522 {
523   static gpointer pass_along[6];
524   if (!sublayer) {
525     return;
526   }
527
528   pass_along[0] = vtl;
529   pass_along[1] = NULL;
530   pass_along[2] = GINT_TO_POINTER (subtype);
531   pass_along[3] = sublayer;
532   pass_along[4] = NULL;
533   pass_along[5] = NULL;
534
535   trw_layer_copy_item_cb(pass_along);
536   trw_layer_cut_item_cb(pass_along);
537 }
538
539 static void trw_layer_copy_item_cb ( gpointer pass_along[6])
540 {
541   VikTrwLayer *vtl = VIK_TRW_LAYER(pass_along[0]);
542   gint subtype = GPOINTER_TO_INT (pass_along[2]);
543   gpointer * sublayer = pass_along[3];
544   guint8 *data = NULL;
545   guint len;
546
547   trw_layer_copy_item( vtl, subtype, sublayer, &data, &len);
548
549   if (data) {
550     a_clipboard_copy( VIK_CLIPBOARD_DATA_SUBLAYER, VIK_LAYER_TRW,
551         subtype, len, data);
552   }
553 }
554
555 static void trw_layer_cut_item_cb ( gpointer pass_along[6])
556 {
557   trw_layer_copy_item_cb(pass_along);
558   trw_layer_delete_item(pass_along);
559 }
560
561 static void trw_layer_copy_item ( VikTrwLayer *vtl, gint subtype, gpointer sublayer, guint8 **item, guint *len )
562 {
563   FlatItem *fi;
564   guint8 *id;
565   guint il;
566
567   if (!sublayer) {
568     *item = NULL;
569     return;
570   }
571
572   if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT )
573   {
574     vik_waypoint_marshall ( g_hash_table_lookup ( vtl->waypoints, sublayer ), &id, &il );
575   } else {
576     vik_track_marshall ( g_hash_table_lookup ( vtl->tracks, sublayer ), &id, &il );
577   }
578
579   *len = sizeof(FlatItem) + strlen(sublayer) + 1 + il;
580   fi = g_malloc ( *len );
581   fi->len = strlen(sublayer) + 1;
582   memcpy(fi->data, sublayer, fi->len);
583   memcpy(fi->data + fi->len, id, il);
584   g_free(id);
585   *item = (guint8 *)fi;
586 }
587
588 static gboolean trw_layer_paste_item ( VikTrwLayer *vtl, gint subtype, guint8 *item, guint len )
589 {
590   FlatItem *fi = (FlatItem *) item;
591
592   if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT && fi )
593   {
594     VikWaypoint *w;
595     gchar *name;
596
597     name = get_new_unique_sublayer_name(vtl, VIK_TRW_LAYER_SUBLAYER_WAYPOINT, (gchar *)fi->data);
598     w = vik_waypoint_unmarshall(fi->data + fi->len, len - sizeof(*fi) - fi->len);
599     vik_trw_layer_add_waypoint ( vtl, name, w );
600     waypoint_convert(name, w, &vtl->coord_mode);
601     // Consider if redraw necessary for the new item
602     if ( vtl->vl.visible && vtl->waypoints_visible && w->visible )
603       vik_layer_emit_update ( VIK_LAYER(vtl) );
604     return TRUE;
605   }
606   if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK && fi )
607   {
608     VikTrack *t;
609     gchar *name;
610     name = get_new_unique_sublayer_name(vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, (gchar *)fi->data);
611     t = vik_track_unmarshall(fi->data + fi->len, len - sizeof(*fi) - fi->len);
612     vik_trw_layer_add_track ( vtl, name, t );
613     track_convert(name, t, &vtl->coord_mode);
614     // Consider if redraw necessary for the new item
615     if ( vtl->vl.visible && vtl->tracks_visible && t->visible )
616       vik_layer_emit_update ( VIK_LAYER(vtl) );
617     return TRUE;
618   }
619   return FALSE;
620 }
621
622 static void trw_layer_free_copied_item ( gint subtype, gpointer item )
623 {
624   if (item) {
625     g_free(item);
626   }
627 }
628
629 static gboolean trw_layer_set_param ( VikTrwLayer *vtl, guint16 id, VikLayerParamData data, VikViewport *vp, gboolean is_file_operation )
630 {
631   switch ( id )
632   {
633     case PARAM_TV: vtl->tracks_visible = data.b; break;
634     case PARAM_WV: vtl->waypoints_visible = data.b; break;
635     case PARAM_DM: vtl->drawmode = data.u; break;
636     case PARAM_DP: vtl->drawpoints = data.b; break;
637     case PARAM_DE: vtl->drawelevation = data.b; break;
638     case PARAM_DS: vtl->drawstops = data.b; break;
639     case PARAM_DL: vtl->drawlines = data.b; break;
640     case PARAM_SL: if ( data.u >= MIN_STOP_LENGTH && data.u <= MAX_STOP_LENGTH )
641                      vtl->stop_length = data.u;
642                    break;
643     case PARAM_EF: if ( data.u >= 1 && data.u <= 100 )
644                      vtl->elevation_factor = data.u;
645                    break;
646     case PARAM_LT: if ( data.u > 0 && data.u < 15 && data.u != vtl->line_thickness )
647                    {
648                      vtl->line_thickness = data.u;
649                      trw_layer_new_track_gcs ( vtl, vp );
650                    }
651                    break;
652     case PARAM_BLT: if ( data.u >= 0 && data.u <= 8 && data.u != vtl->bg_line_thickness )
653                    {
654                      vtl->bg_line_thickness = data.u;
655                      trw_layer_new_track_gcs ( vtl, vp );
656                    }
657                    break;
658     case PARAM_VMIN:
659     {
660       /* Convert to store internally
661          NB file operation always in internal units (metres per second) */
662       vik_units_speed_t speed_units = a_vik_get_units_speed ();
663       if ( is_file_operation || speed_units == VIK_UNITS_SPEED_METRES_PER_SECOND )
664         vtl->velocity_min = data.d;
665       else if ( speed_units == VIK_UNITS_SPEED_KILOMETRES_PER_HOUR )
666         vtl->velocity_min = VIK_KPH_TO_MPS(data.d);
667       else if ( speed_units == VIK_UNITS_SPEED_MILES_PER_HOUR )
668         vtl->velocity_min = VIK_MPH_TO_MPS(data.d);
669       else
670         /* Knots */
671         vtl->velocity_min = VIK_KNOTS_TO_MPS(data.d);
672       break;
673     }
674     case PARAM_VMAX:
675     {
676       /* Convert to store internally
677          NB file operation always in internal units (metres per second) */
678       vik_units_speed_t speed_units = a_vik_get_units_speed ();
679       if ( is_file_operation || speed_units == VIK_UNITS_SPEED_METRES_PER_SECOND )
680         vtl->velocity_max = data.d;
681       else if ( speed_units == VIK_UNITS_SPEED_KILOMETRES_PER_HOUR )
682         vtl->velocity_max = VIK_KPH_TO_MPS(data.d);
683       else if ( speed_units == VIK_UNITS_SPEED_MILES_PER_HOUR )
684         vtl->velocity_max = VIK_MPH_TO_MPS(data.d);
685       else
686         /* Knots */
687         vtl->velocity_max = VIK_KNOTS_TO_MPS(data.d);
688       break;
689     }
690     case PARAM_TBGC: gdk_gc_set_rgb_fg_color(vtl->track_bg_gc, &(data.c)); break;
691     case PARAM_DLA: vtl->drawlabels = data.b; break;
692     case PARAM_DI: vtl->drawimages = data.b; break;
693     case PARAM_IS: if ( data.u != vtl->image_size )
694       {
695         vtl->image_size = data.u;
696         g_list_foreach ( vtl->image_cache->head, (GFunc) cached_pixbuf_free, NULL );
697         g_queue_free ( vtl->image_cache );
698         vtl->image_cache = g_queue_new ();
699       }
700       break;
701     case PARAM_IA: vtl->image_alpha = data.u; break;
702     case PARAM_ICS: vtl->image_cache_size = data.u;
703       while ( vtl->image_cache->length > vtl->image_cache_size ) /* if shrinking cache_size, free pixbuf ASAP */
704           cached_pixbuf_free ( g_queue_pop_tail ( vtl->image_cache ) );
705       break;
706     case PARAM_WPC: gdk_gc_set_rgb_fg_color(vtl->waypoint_gc, &(data.c)); break;
707     case PARAM_WPTC: gdk_gc_set_rgb_fg_color(vtl->waypoint_text_gc, &(data.c)); break;
708     case PARAM_WPBC: gdk_gc_set_rgb_fg_color(vtl->waypoint_bg_gc, &(data.c)); break;
709     case PARAM_WPBA: gdk_gc_set_function(vtl->waypoint_bg_gc, data.b ? GDK_AND : GDK_COPY ); break;
710     case PARAM_WPSYM: if ( data.u < WP_NUM_SYMBOLS ) vtl->wp_symbol = data.u; break;
711     case PARAM_WPSIZE: if ( data.u > 0 && data.u <= 64 ) vtl->wp_size = data.u; break;
712     case PARAM_WPSYMS: vtl->wp_draw_symbols = data.b; break;
713   }
714   return TRUE;
715 }
716
717 static VikLayerParamData trw_layer_get_param ( VikTrwLayer *vtl, guint16 id, gboolean is_file_operation )
718 {
719   VikLayerParamData rv;
720   switch ( id )
721   {
722     case PARAM_TV: rv.b = vtl->tracks_visible; break;
723     case PARAM_WV: rv.b = vtl->waypoints_visible; break;
724     case PARAM_DM: rv.u = vtl->drawmode; break;
725     case PARAM_DP: rv.b = vtl->drawpoints; break;
726     case PARAM_DE: rv.b = vtl->drawelevation; break;
727     case PARAM_EF: rv.u = vtl->elevation_factor; break;
728     case PARAM_DS: rv.b = vtl->drawstops; break;
729     case PARAM_SL: rv.u = vtl->stop_length; break;
730     case PARAM_DL: rv.b = vtl->drawlines; break;
731     case PARAM_LT: rv.u = vtl->line_thickness; break;
732     case PARAM_BLT: rv.u = vtl->bg_line_thickness; break;
733     case PARAM_VMIN:
734     {
735       /* Convert to store internally
736          NB file operation always in internal units (metres per second) */
737       vik_units_speed_t speed_units = a_vik_get_units_speed ();
738       if ( is_file_operation || speed_units == VIK_UNITS_SPEED_METRES_PER_SECOND )
739         rv.d = vtl->velocity_min;
740       else if ( speed_units == VIK_UNITS_SPEED_KILOMETRES_PER_HOUR )
741         rv.d = VIK_MPS_TO_KPH(vtl->velocity_min);
742       else if ( speed_units == VIK_UNITS_SPEED_MILES_PER_HOUR )
743         rv.d = VIK_MPS_TO_MPH(vtl->velocity_min);
744       else
745         /* Knots */
746         rv.d = VIK_MPS_TO_KNOTS(vtl->velocity_min);
747       break;
748     }
749     case PARAM_VMAX:
750     {
751       /* Convert to store internally
752          NB file operation always in internal units (metres per second) */
753       vik_units_speed_t speed_units = a_vik_get_units_speed ();
754       if ( is_file_operation || speed_units == VIK_UNITS_SPEED_METRES_PER_SECOND )
755         rv.d = vtl->velocity_max;
756       else if ( speed_units == VIK_UNITS_SPEED_KILOMETRES_PER_HOUR )
757         rv.d = VIK_MPS_TO_KPH(vtl->velocity_max);
758       else if ( speed_units == VIK_UNITS_SPEED_MILES_PER_HOUR )
759         rv.d = VIK_MPS_TO_MPH(vtl->velocity_max);
760       else
761         /* Knots */
762         rv.d = VIK_MPS_TO_KNOTS(vtl->velocity_max);
763       break;
764     }
765     case PARAM_DLA: rv.b = vtl->drawlabels; break;
766     case PARAM_DI: rv.b = vtl->drawimages; break;
767     case PARAM_TBGC: vik_gc_get_fg_color(vtl->track_bg_gc, &(rv.c)); break;
768     case PARAM_IS: rv.u = vtl->image_size; break;
769     case PARAM_IA: rv.u = vtl->image_alpha; break;
770     case PARAM_ICS: rv.u = vtl->image_cache_size; break;
771     case PARAM_WPC: vik_gc_get_fg_color(vtl->waypoint_gc, &(rv.c)); break;
772     case PARAM_WPTC: vik_gc_get_fg_color(vtl->waypoint_text_gc, &(rv.c)); break;
773     case PARAM_WPBC: vik_gc_get_fg_color(vtl->waypoint_bg_gc, &(rv.c)); break;
774     case PARAM_WPBA: rv.b = (vik_gc_get_function(vtl->waypoint_bg_gc)==GDK_AND); break;
775     case PARAM_WPSYM: rv.u = vtl->wp_symbol; break;
776     case PARAM_WPSIZE: rv.u = vtl->wp_size; break;
777     case PARAM_WPSYMS: rv.b = vtl->wp_draw_symbols; break;
778   }
779   return rv;
780 }
781
782 static void trw_layer_marshall( VikTrwLayer *vtl, guint8 **data, gint *len )
783 {
784   guint8 *pd;
785   gchar *dd;
786   gsize dl;
787   gint pl;
788   gchar *tmpname;
789   FILE *f;
790
791   *data = NULL;
792
793   if ((f = fdopen(g_file_open_tmp (NULL, &tmpname, NULL), "r+"))) {
794     a_gpx_write_file(vtl, f);
795     vik_layer_marshall_params(VIK_LAYER(vtl), &pd, &pl);
796     fclose(f);
797     f = NULL;
798     g_file_get_contents(tmpname, &dd, &dl, NULL);
799     *len = sizeof(pl) + pl + dl;
800     *data = g_malloc(*len);
801     memcpy(*data, &pl, sizeof(pl));
802     memcpy(*data + sizeof(pl), pd, pl);
803     memcpy(*data + sizeof(pl) + pl, dd, dl);
804     
805     g_free(pd);
806     g_free(dd);
807     g_remove(tmpname);
808     g_free(tmpname);
809   }
810 }
811
812 static VikTrwLayer *trw_layer_unmarshall( guint8 *data, gint len, VikViewport *vvp )
813 {
814   VikTrwLayer *rv = VIK_TRW_LAYER(vik_layer_create ( VIK_LAYER_TRW, vvp, NULL, FALSE ));
815   gint pl;
816   gchar *tmpname;
817   FILE *f;
818
819
820   memcpy(&pl, data, sizeof(pl));
821   data += sizeof(pl);
822   vik_layer_unmarshall_params ( VIK_LAYER(rv), data, pl, vvp );
823   data += pl;
824
825   if (!(f = fdopen(g_file_open_tmp (NULL, &tmpname, NULL), "r+"))) {
826     g_critical("couldn't open temp file");
827     exit(1);
828   }
829   fwrite(data, len - pl - sizeof(pl), 1, f);
830   rewind(f);
831   a_gpx_read_file(rv, f);
832   fclose(f);
833   f = NULL;
834   g_remove(tmpname);
835   g_free(tmpname);
836   return rv;
837 }
838
839 static GList * str_array_to_glist(gchar* data[])
840 {
841   GList *gl = NULL;
842   gpointer * p;
843   for (p = (gpointer)data; *p; p++)
844     gl = g_list_prepend(gl, *p);
845   return(g_list_reverse(gl));
846 }
847
848 static gboolean strcase_equal(gconstpointer s1, gconstpointer s2)
849 {
850   return (strcasecmp(s1, s2) == 0);
851 }
852
853 static guint strcase_hash(gconstpointer v)
854 {
855   /* 31 bit hash function */
856   int i;
857   const gchar *t = v;
858   gchar s[128];   /* malloc is too slow for reading big files */
859   gchar *p = s;
860
861   for (i = 0; (i < (sizeof(s)- 1)) && t[i]; i++)
862       p[i] = toupper(t[i]);
863   p[i] = '\0';
864
865   p = s;
866   guint32 h = *p;
867   if (h) {
868     for (p += 1; *p != '\0'; p++)
869       h = (h << 5) - h + *p;
870   }
871
872   return h;  
873 }
874
875 VikTrwLayer *vik_trw_layer_new ( gint drawmode )
876 {
877   if (trw_layer_params[PARAM_DM].widget_data == NULL)
878     trw_layer_params[PARAM_DM].widget_data = str_array_to_glist(params_drawmodes);
879   if (trw_layer_params[PARAM_WPSYM].widget_data == NULL)
880     trw_layer_params[PARAM_WPSYM].widget_data = str_array_to_glist(params_wpsymbols);
881
882   VikTrwLayer *rv = VIK_TRW_LAYER ( g_object_new ( VIK_TRW_LAYER_TYPE, NULL ) );
883   vik_layer_init ( VIK_LAYER(rv), VIK_LAYER_TRW );
884
885   rv->waypoints = g_hash_table_new_full ( strcase_hash, strcase_equal, g_free, (GDestroyNotify) vik_waypoint_free );
886   rv->tracks = g_hash_table_new_full ( g_str_hash, g_str_equal, g_free, (GDestroyNotify) vik_track_free );
887   rv->tracks_iters = g_hash_table_new_full ( g_str_hash, g_str_equal, NULL, g_free );
888   rv->waypoints_iters = g_hash_table_new_full ( strcase_hash, strcase_equal, NULL, g_free );
889
890   /* TODO: constants at top */
891   rv->waypoints_visible = rv->tracks_visible = TRUE;
892   rv->drawmode = drawmode;
893   rv->drawpoints = TRUE;
894   rv->drawstops = FALSE;
895   rv->drawelevation = FALSE;
896   rv->elevation_factor = 30;
897   rv->stop_length = 60;
898   rv->drawlines = TRUE;
899   rv->wplabellayout = NULL;
900   rv->wp_right_click_menu = NULL;
901   rv->waypoint_gc = NULL;
902   rv->waypoint_text_gc = NULL;
903   rv->waypoint_bg_gc = NULL;
904   rv->track_gc = NULL;
905   rv->velocity_max = 5.0;
906   rv->velocity_min = 0.0;
907   rv->line_thickness = 1;
908   rv->bg_line_thickness = 0;
909   rv->current_wp = NULL;
910   rv->current_wp_name = NULL;
911   rv->current_track = NULL;
912   rv->current_tpl = NULL;
913   rv->current_tp_track_name = NULL;
914   rv->moving_tp = FALSE;
915   rv->moving_wp = FALSE;
916
917   rv->ct_sync_done = TRUE;
918
919   rv->magic_scissors_started = FALSE;
920   rv->magic_scissors_check_added_track = FALSE;
921   rv->magic_scissors_added_track_name = NULL;
922   rv->magic_scissors_current_track = NULL;
923   rv->magic_scissors_append = FALSE;
924
925   rv->waypoint_rightclick = FALSE;
926   rv->last_tpl = NULL;
927   rv->last_tp_track_name = NULL;
928   rv->tpwin = NULL;
929   rv->image_cache = g_queue_new();
930   rv->image_size = 64;
931   rv->image_alpha = 255;
932   rv->image_cache_size = 300;
933   rv->drawimages = TRUE;
934   rv->drawlabels = TRUE;
935   return rv;
936 }
937
938
939 void vik_trw_layer_free ( VikTrwLayer *trwlayer )
940 {
941   g_hash_table_destroy(trwlayer->waypoints);
942   g_hash_table_destroy(trwlayer->tracks);
943
944   /* ODC: replace with GArray */
945   trw_layer_free_track_gcs ( trwlayer );
946
947   if ( trwlayer->wp_right_click_menu )
948     g_object_ref_sink ( G_OBJECT(trwlayer->wp_right_click_menu) );
949
950   if ( trwlayer->wplabellayout != NULL)
951     g_object_unref ( G_OBJECT ( trwlayer->wplabellayout ) );
952
953   if ( trwlayer->waypoint_gc != NULL )
954     g_object_unref ( G_OBJECT ( trwlayer->waypoint_gc ) );
955
956   if ( trwlayer->waypoint_text_gc != NULL )
957     g_object_unref ( G_OBJECT ( trwlayer->waypoint_text_gc ) );
958
959   if ( trwlayer->waypoint_bg_gc != NULL )
960     g_object_unref ( G_OBJECT ( trwlayer->waypoint_bg_gc ) );
961
962   if ( trwlayer->waypoint_font != NULL )
963     gdk_font_unref ( trwlayer->waypoint_font );
964
965   if ( trwlayer->tpwin != NULL )
966     gtk_widget_destroy ( GTK_WIDGET(trwlayer->tpwin) );
967
968   g_list_foreach ( trwlayer->image_cache->head, (GFunc) cached_pixbuf_free, NULL );
969   g_queue_free ( trwlayer->image_cache );
970 }
971
972 static void init_drawing_params ( struct DrawingParams *dp, VikViewport *vp )
973 {
974   dp->vp = vp;
975   dp->xmpp = vik_viewport_get_xmpp ( vp );
976   dp->ympp = vik_viewport_get_ympp ( vp );
977   dp->width = vik_viewport_get_width ( vp );
978   dp->height = vik_viewport_get_height ( vp );
979   dp->center = vik_viewport_get_center ( vp );
980   dp->one_zone = vik_viewport_is_one_zone ( vp ); /* false if some other projection besides UTM */
981   dp->lat_lon = vik_viewport_get_coord_mode ( vp ) == VIK_COORD_LATLON;
982
983   if ( dp->one_zone )
984   {
985     gint w2, h2;
986     w2 = dp->xmpp * (dp->width / 2) + 1600 / dp->xmpp; 
987     h2 = dp->ympp * (dp->height / 2) + 1600 / dp->ympp;
988     /* leniency -- for tracks. Obviously for waypoints this SHOULD be a lot smaller */
989  
990     dp->ce1 = dp->center->east_west-w2; 
991     dp->ce2 = dp->center->east_west+w2;
992     dp->cn1 = dp->center->north_south-h2;
993     dp->cn2 = dp->center->north_south+h2;
994   } else if ( dp->lat_lon ) {
995     VikCoord upperleft, bottomright;
996     /* quick & dirty calculation; really want to check all corners due to lat/lon smaller at top in northern hemisphere */
997     /* this also DOESN'T WORK if you are crossing 180/-180 lon. I don't plan to in the near future...  */
998     vik_viewport_screen_to_coord ( vp, -500, -500, &upperleft );
999     vik_viewport_screen_to_coord ( vp, dp->width+500, dp->height+500, &bottomright );
1000     dp->ce1 = upperleft.east_west;
1001     dp->ce2 = bottomright.east_west;
1002     dp->cn1 = bottomright.north_south;
1003     dp->cn2 = upperleft.north_south;
1004   }
1005
1006   dp->track_gc_iter = 0;
1007 }
1008
1009 static gint calculate_velocity ( VikTrwLayer *vtl, VikTrackpoint *tp1, VikTrackpoint *tp2 )
1010 {
1011   static gdouble rv = 0;
1012   if ( tp1->has_timestamp && tp2->has_timestamp )
1013   {
1014     rv = ( vik_coord_diff ( &(tp1->coord), &(tp2->coord) )
1015            / (tp1->timestamp - tp2->timestamp) ) - vtl->velocity_min;
1016
1017     if ( rv < 0 )
1018       return VIK_TRW_LAYER_TRACK_GC_MIN;
1019     else if ( vtl->velocity_min >= vtl->velocity_max )
1020       return VIK_TRW_LAYER_TRACK_GC_MAX;
1021
1022     rv *= (VIK_TRW_LAYER_TRACK_GC_RATES / (vtl->velocity_max - vtl->velocity_min));
1023
1024     if ( rv >= VIK_TRW_LAYER_TRACK_GC_MAX )
1025       return VIK_TRW_LAYER_TRACK_GC_MAX;
1026     return (gint) rv;
1027  }
1028  else
1029    return VIK_TRW_LAYER_TRACK_GC_BLACK;
1030 }
1031
1032 void draw_utm_skip_insignia ( VikViewport *vvp, GdkGC *gc, gint x, gint y )
1033 {
1034   vik_viewport_draw_line ( vvp, gc, x+5, y, x-5, y );
1035   vik_viewport_draw_line ( vvp, gc, x, y+5, x, y-5 );
1036   vik_viewport_draw_line ( vvp, gc, x+5, y+5, x-5, y-5 );
1037   vik_viewport_draw_line ( vvp, gc, x+5, y-5, x-5, y+5 );
1038 }
1039
1040 static void trw_layer_draw_track ( const gchar *name, VikTrack *track, struct DrawingParams *dp, gboolean drawing_white_background )
1041 {
1042   /* TODO: this function is a mess, get rid of any redundancy */
1043   GList *list = track->trackpoints;
1044   GdkGC *main_gc;
1045   gboolean useoldvals = TRUE;
1046
1047   gboolean drawpoints;
1048   gboolean drawstops;
1049   gboolean drawelevation;
1050   gdouble min_alt, max_alt, alt_diff = 0;
1051
1052   const guint8 tp_size_reg = 2;
1053   const guint8 tp_size_cur = 4;
1054   guint8 tp_size;
1055
1056   if ( dp->vtl->drawelevation )
1057   {
1058     /* assume if it has elevation at the beginning, it has it throughout. not ness a true good assumption */
1059     if ( ( drawelevation = vik_track_get_minmax_alt ( track, &min_alt, &max_alt ) ) )
1060       alt_diff = max_alt - min_alt;
1061   }
1062
1063   if ( ! track->visible )
1064     return;
1065
1066   /* admittedly this is not an efficient way to do it because we go through the whole GC thing all over... */
1067   if ( dp->vtl->bg_line_thickness && !drawing_white_background )
1068     trw_layer_draw_track ( name, track, dp, TRUE );
1069
1070   if ( drawing_white_background )
1071     drawpoints = drawstops = FALSE;
1072   else {
1073     drawpoints = dp->vtl->drawpoints;
1074     drawstops = dp->vtl->drawstops;
1075   }
1076
1077   /* Current track - used for creation */
1078   if ( track == dp->vtl->current_track )
1079     main_gc = dp->vtl->current_track_gc;
1080   else {
1081     if ( vik_viewport_get_draw_highlight ( dp->vp ) ) {
1082       /* Draw all tracks of the layer in special colour */
1083       /* if track is member of selected layer or is the current selected track
1084          then draw in the highlight colour.
1085          NB this supercedes the drawmode */
1086       if ( dp->vtl && ( ( dp->vtl == vik_window_get_selected_trw_layer ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(dp->vtl) ) ) ||
1087                         ( dp->vtl->tracks == vik_window_get_selected_tracks ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(dp->vtl) ) ) ||
1088                         track == vik_window_get_selected_track ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(dp->vtl) ) ) ) {
1089         main_gc = vik_viewport_get_gc_highlight (dp->vp);
1090       }
1091       else {
1092         if ( dp->vtl->drawmode == DRAWMODE_ALL_BLACK )
1093           dp->track_gc_iter = VIK_TRW_LAYER_TRACK_GC_BLACK;
1094
1095         main_gc = g_array_index(dp->vtl->track_gc, GdkGC *, dp->track_gc_iter);
1096       }
1097     }
1098     else {
1099       if ( dp->vtl->drawmode == DRAWMODE_ALL_BLACK )
1100         dp->track_gc_iter = VIK_TRW_LAYER_TRACK_GC_BLACK;
1101           
1102       main_gc = g_array_index(dp->vtl->track_gc, GdkGC *, dp->track_gc_iter);
1103     }
1104   }
1105
1106   if (list) {
1107     int x, y, oldx, oldy;
1108     VikTrackpoint *tp = VIK_TRACKPOINT(list->data);
1109   
1110     tp_size = (list == dp->vtl->current_tpl) ? tp_size_cur : tp_size_reg;
1111
1112     vik_viewport_coord_to_screen ( dp->vp, &(tp->coord), &x, &y );
1113
1114     if ( (drawpoints) && dp->track_gc_iter < VIK_TRW_LAYER_TRACK_GC )
1115     {
1116       GdkPoint trian[3] = { { x, y-(3*tp_size) }, { x-(2*tp_size), y+(2*tp_size) }, {x+(2*tp_size), y+(2*tp_size)} };
1117       vik_viewport_draw_polygon ( dp->vp, main_gc, TRUE, trian, 3 );
1118     }
1119
1120     oldx = x;
1121     oldy = y;
1122
1123     while ((list = g_list_next(list)))
1124     {
1125       tp = VIK_TRACKPOINT(list->data);
1126       tp_size = (list == dp->vtl->current_tpl) ? tp_size_cur : tp_size_reg;
1127
1128       /* check some stuff -- but only if we're in UTM and there's only ONE ZONE; or lat lon */
1129       if ( (!dp->one_zone && !dp->lat_lon) ||     /* UTM & zones; do everything */
1130              ( ((!dp->one_zone) || tp->coord.utm_zone == dp->center->utm_zone) &&   /* only check zones if UTM & one_zone */
1131              tp->coord.east_west < dp->ce2 && tp->coord.east_west > dp->ce1 &&  /* both UTM and lat lon */
1132              tp->coord.north_south > dp->cn1 && tp->coord.north_south < dp->cn2 ) )
1133       {
1134         vik_viewport_coord_to_screen ( dp->vp, &(tp->coord), &x, &y );
1135
1136         if ( drawpoints && ! drawing_white_background )
1137         {
1138           if ( list->next ) {
1139             vik_viewport_draw_rectangle ( dp->vp, main_gc, TRUE, x-tp_size, y-tp_size, 2*tp_size, 2*tp_size );
1140
1141             /* stops */
1142             if ( drawstops && VIK_TRACKPOINT(list->next->data)->timestamp - VIK_TRACKPOINT(list->data)->timestamp > dp->vtl->stop_length )
1143               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 );
1144           }
1145           else
1146             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 );
1147         }
1148
1149         if ((!tp->newsegment) && (dp->vtl->drawlines))
1150         {
1151           VikTrackpoint *tp2 = VIK_TRACKPOINT(list->prev->data);
1152
1153           /* UTM only: zone check */
1154           if ( drawpoints && dp->vtl->coord_mode == VIK_COORD_UTM && tp->coord.utm_zone != dp->center->utm_zone )
1155             draw_utm_skip_insignia (  dp->vp, main_gc, x, y);
1156
1157           if ( dp->vtl->drawmode == DRAWMODE_BY_VELOCITY ) {
1158             dp->track_gc_iter = calculate_velocity ( dp->vtl, tp, tp2 );
1159             main_gc = g_array_index(dp->vtl->track_gc, GdkGC *, dp->track_gc_iter);
1160           }
1161
1162           if (!useoldvals)
1163             vik_viewport_coord_to_screen ( dp->vp, &(tp2->coord), &oldx, &oldy );
1164
1165           if ( drawing_white_background ) {
1166             vik_viewport_draw_line ( dp->vp, dp->vtl->track_bg_gc, oldx, oldy, x, y);
1167           }
1168           else {
1169
1170             vik_viewport_draw_line ( dp->vp, main_gc, oldx, oldy, x, y);
1171             if ( dp->vtl->drawelevation && list && list->next && VIK_TRACKPOINT(list->next->data)->altitude != VIK_DEFAULT_ALTITUDE ) {
1172               GdkPoint tmp[4];
1173               #define FIXALTITUDE(what) ((VIK_TRACKPOINT((what))->altitude-min_alt)/alt_diff*DRAW_ELEVATION_FACTOR*dp->vtl->elevation_factor/dp->xmpp)
1174               if ( list && list->next && VIK_TRACKPOINT(list->next->data)->altitude != VIK_DEFAULT_ALTITUDE ) {
1175                 tmp[0].x = oldx;
1176                 tmp[0].y = oldy;
1177                 tmp[1].x = oldx;
1178                 tmp[1].y = oldy-FIXALTITUDE(list->data);
1179                 tmp[2].x = x;
1180                 tmp[2].y = y-FIXALTITUDE(list->next->data);
1181                 tmp[3].x = x;
1182                 tmp[3].y = y;
1183
1184                 GdkGC *tmp_gc;
1185                 if ( ((oldx - x) > 0 && (oldy - y) > 0) || ((oldx - x) < 0 && (oldy - y) < 0))
1186                   tmp_gc = GTK_WIDGET(dp->vp)->style->light_gc[3];
1187                 else
1188                   tmp_gc = GTK_WIDGET(dp->vp)->style->dark_gc[0];
1189                 vik_viewport_draw_polygon ( dp->vp, tmp_gc, TRUE, tmp, 4);
1190               }
1191               vik_viewport_draw_line ( dp->vp, main_gc, oldx, oldy-FIXALTITUDE(list->data), x, y-FIXALTITUDE(list->next->data));
1192             }
1193           }
1194         }
1195
1196         oldx = x;
1197         oldy = y;
1198         useoldvals = TRUE;
1199       }
1200       else {
1201         if (useoldvals && dp->vtl->drawlines && (!tp->newsegment))
1202         {
1203           VikTrackpoint *tp2 = VIK_TRACKPOINT(list->prev->data);
1204           if ( dp->vtl->coord_mode != VIK_COORD_UTM || tp->coord.utm_zone == dp->center->utm_zone )
1205           {
1206             vik_viewport_coord_to_screen ( dp->vp, &(tp->coord), &x, &y );
1207             if ( dp->vtl->drawmode == DRAWMODE_BY_VELOCITY ) {
1208               dp->track_gc_iter = calculate_velocity ( dp->vtl, tp, tp2 );
1209               main_gc = g_array_index(dp->vtl->track_gc, GdkGC *, dp->track_gc_iter);
1210             }
1211
1212             if ( drawing_white_background )
1213               vik_viewport_draw_line ( dp->vp, dp->vtl->track_bg_gc, oldx, oldy, x, y);
1214             else
1215               vik_viewport_draw_line ( dp->vp, main_gc, oldx, oldy, x, y);
1216           }
1217           else 
1218           {
1219             vik_viewport_coord_to_screen ( dp->vp, &(tp2->coord), &x, &y );
1220             draw_utm_skip_insignia ( dp->vp, main_gc, x, y );
1221           }
1222         }
1223         useoldvals = FALSE;
1224       }
1225     }
1226   }
1227   if ( dp->vtl->drawmode == DRAWMODE_BY_TRACK )
1228     if ( ++(dp->track_gc_iter) >= VIK_TRW_LAYER_TRACK_GC )
1229       dp->track_gc_iter = 0;
1230 }
1231
1232 /* the only reason this exists is so that trw_layer_draw_track can first call itself to draw the white track background */
1233 static void trw_layer_draw_track_cb ( const gchar *name, VikTrack *track, struct DrawingParams *dp )
1234 {
1235   trw_layer_draw_track ( name, track, dp, FALSE );
1236 }
1237
1238 static void cached_pixbuf_free ( CachedPixbuf *cp )
1239 {
1240   g_object_unref ( G_OBJECT(cp->pixbuf) );
1241   g_free ( cp->image );
1242 }
1243
1244 static gint cached_pixbuf_cmp ( CachedPixbuf *cp, const gchar *name )
1245 {
1246   return strcmp ( cp->image, name );
1247 }
1248
1249 static void trw_layer_draw_waypoint ( const gchar *name, VikWaypoint *wp, struct DrawingParams *dp )
1250 {
1251   if ( wp->visible )
1252   if ( (!dp->one_zone && !dp->lat_lon) || ( ( dp->lat_lon || wp->coord.utm_zone == dp->center->utm_zone ) && 
1253              wp->coord.east_west < dp->ce2 && wp->coord.east_west > dp->ce1 && 
1254              wp->coord.north_south > dp->cn1 && wp->coord.north_south < dp->cn2 ) )
1255   {
1256     gint x, y;
1257     GdkPixbuf *sym = NULL;
1258     vik_viewport_coord_to_screen ( dp->vp, &(wp->coord), &x, &y );
1259
1260     /* if in shrunken_cache, get that. If not, get and add to shrunken_cache */
1261
1262     if ( wp->image && dp->vtl->drawimages )
1263     {
1264       GdkPixbuf *pixbuf = NULL;
1265       GList *l;
1266
1267       if ( dp->vtl->image_alpha == 0)
1268         return;
1269
1270       l = g_list_find_custom ( dp->vtl->image_cache->head, wp->image, (GCompareFunc) cached_pixbuf_cmp );
1271       if ( l )
1272         pixbuf = ((CachedPixbuf *) l->data)->pixbuf;
1273       else
1274       {
1275         gchar *image = wp->image;
1276         GdkPixbuf *regularthumb = a_thumbnails_get ( wp->image );
1277         if ( ! regularthumb )
1278         {
1279           regularthumb = a_thumbnails_get_default (); /* cache one 'not yet loaded' for all thumbs not loaded */
1280           image = "\x12\x00"; /* this shouldn't occur naturally. */
1281         }
1282         if ( regularthumb )
1283         {
1284           CachedPixbuf *cp = NULL;
1285           cp = g_malloc ( sizeof ( CachedPixbuf ) );
1286           if ( dp->vtl->image_size == 128 )
1287             cp->pixbuf = regularthumb;
1288           else
1289           {
1290             cp->pixbuf = a_thumbnails_scale_pixbuf(regularthumb, dp->vtl->image_size, dp->vtl->image_size);
1291             g_assert ( cp->pixbuf );
1292             g_object_unref ( G_OBJECT(regularthumb) );
1293           }
1294           cp->image = g_strdup ( image );
1295
1296           /* needed so 'click picture' tool knows how big the pic is; we don't
1297            * store it in cp because they may have been freed already. */
1298           wp->image_width = gdk_pixbuf_get_width ( cp->pixbuf );
1299           wp->image_height = gdk_pixbuf_get_height ( cp->pixbuf );
1300
1301           g_queue_push_head ( dp->vtl->image_cache, cp );
1302           if ( dp->vtl->image_cache->length > dp->vtl->image_cache_size )
1303             cached_pixbuf_free ( g_queue_pop_tail ( dp->vtl->image_cache ) );
1304
1305           pixbuf = cp->pixbuf;
1306         }
1307         else
1308         {
1309           pixbuf = a_thumbnails_get_default (); /* thumbnail not yet loaded */
1310         }
1311       }
1312       if ( pixbuf )
1313       {
1314         gint w, h;
1315         w = gdk_pixbuf_get_width ( pixbuf );
1316         h = gdk_pixbuf_get_height ( pixbuf );
1317
1318         if ( x+(w/2) > 0 && y+(h/2) > 0 && x-(w/2) < dp->width && y-(h/2) < dp->height ) /* always draw within boundaries */
1319         {
1320           if ( vik_viewport_get_draw_highlight ( dp->vp ) ) {
1321             if ( dp->vtl == vik_window_get_selected_trw_layer ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(dp->vtl) ) ||
1322                  dp->vtl->waypoints == vik_window_get_selected_waypoints ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(dp->vtl) ) ||
1323                  wp == vik_window_get_selected_waypoint ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(dp->vtl) ) ) {
1324               // Highlighted - so draw a little border around the chosen one
1325               // single line seems a little weak so draw 2 of them
1326               vik_viewport_draw_rectangle (dp->vp, vik_viewport_get_gc_highlight (dp->vp), FALSE,
1327                                            x - (w/2) - 1, y - (h/2) - 1, w + 2, h + 2 );
1328               vik_viewport_draw_rectangle (dp->vp, vik_viewport_get_gc_highlight (dp->vp), FALSE,
1329                                            x - (w/2) - 2, y - (h/2) - 2, w + 4, h + 4 );
1330             }
1331           }
1332           if ( dp->vtl->image_alpha == 255 )
1333             vik_viewport_draw_pixbuf ( dp->vp, pixbuf, 0, 0, x - (w/2), y - (h/2), w, h );
1334           else
1335             vik_viewport_draw_pixbuf_with_alpha ( dp->vp, pixbuf, dp->vtl->image_alpha, 0, 0, x - (w/2), y - (h/2), w, h );
1336         }
1337         return; /* if failed to draw picture, default to drawing regular waypoint (below) */
1338       }
1339     }
1340
1341     /* DRAW ACTUAL DOT */
1342     if ( dp->vtl->wp_draw_symbols && wp->symbol && (sym = a_get_wp_sym(wp->symbol)) ) {
1343       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 );
1344     } 
1345     else if ( wp == dp->vtl->current_wp ) {
1346       switch ( dp->vtl->wp_symbol ) {
1347         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;
1348         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;
1349         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;
1350         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 );
1351                           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 );
1352       }
1353     }
1354     else {
1355       switch ( dp->vtl->wp_symbol ) {
1356         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;
1357         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;
1358         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;
1359         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 );
1360                           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;
1361       }
1362     }
1363
1364     if ( dp->vtl->drawlabels )
1365     {
1366       /* thanks to the GPSDrive people (Fritz Ganter et al.) for hints on this part ... yah, I'm too lazy to study documentation */
1367       gint label_x, label_y;
1368       gint width, height;
1369       pango_layout_set_text ( dp->vtl->wplabellayout, name, -1 );
1370       pango_layout_get_pixel_size ( dp->vtl->wplabellayout, &width, &height );
1371       label_x = x - width/2;
1372       if (sym)
1373         label_y = y - height - 2 - gdk_pixbuf_get_height(sym)/2;
1374       else
1375         label_y = y - dp->vtl->wp_size - height - 2;
1376
1377       /* if highlight mode on, then draw background text in highlight colour */
1378       if ( vik_viewport_get_draw_highlight ( dp->vp ) ) {
1379         if ( dp->vtl == vik_window_get_selected_trw_layer ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(dp->vtl) ) ||
1380              dp->vtl->waypoints == vik_window_get_selected_waypoints ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(dp->vtl) ) ||
1381              wp == vik_window_get_selected_waypoint ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(dp->vtl) ) )
1382           vik_viewport_draw_rectangle ( dp->vp, vik_viewport_get_gc_highlight (dp->vp), TRUE, label_x - 1, label_y-1,width+2,height+2);
1383         else
1384           vik_viewport_draw_rectangle ( dp->vp, dp->vtl->waypoint_bg_gc, TRUE, label_x - 1, label_y-1,width+2,height+2);
1385       }
1386       else {
1387         vik_viewport_draw_rectangle ( dp->vp, dp->vtl->waypoint_bg_gc, TRUE, label_x - 1, label_y-1,width+2,height+2);
1388       }
1389       vik_viewport_draw_layout ( dp->vp, dp->vtl->waypoint_text_gc, label_x, label_y, dp->vtl->wplabellayout );
1390     }
1391   }
1392 }
1393
1394 void vik_trw_layer_draw ( VikTrwLayer *l, gpointer data )
1395 {
1396   static struct DrawingParams dp;
1397   g_assert ( l != NULL );
1398
1399   init_drawing_params ( &dp, VIK_VIEWPORT(data) );
1400   dp.vtl = l;
1401
1402   if ( l->tracks_visible )
1403     g_hash_table_foreach ( l->tracks, (GHFunc) trw_layer_draw_track_cb, &dp );
1404
1405   if (l->waypoints_visible)
1406     g_hash_table_foreach ( l->waypoints, (GHFunc) trw_layer_draw_waypoint, &dp );
1407 }
1408
1409 static void trw_layer_free_track_gcs ( VikTrwLayer *vtl )
1410 {
1411   int i;
1412   if ( vtl->track_bg_gc ) 
1413   {
1414     g_object_unref ( vtl->track_bg_gc );
1415     vtl->track_bg_gc = NULL;
1416   }
1417   if ( vtl->current_track_gc ) 
1418   {
1419     g_object_unref ( vtl->current_track_gc );
1420     vtl->current_track_gc = NULL;
1421   }
1422
1423   if ( ! vtl->track_gc )
1424     return;
1425   for ( i = vtl->track_gc->len - 1; i >= 0; i-- )
1426     g_object_unref ( g_array_index ( vtl->track_gc, GObject *, i ) );
1427   g_array_free ( vtl->track_gc, TRUE );
1428   vtl->track_gc = NULL;
1429 }
1430
1431 static void trw_layer_new_track_gcs ( VikTrwLayer *vtl, VikViewport *vp )
1432 {
1433   GdkGC *gc[ VIK_TRW_LAYER_TRACK_GC ];
1434   gint width = vtl->line_thickness;
1435
1436   if ( vtl->track_gc )
1437     trw_layer_free_track_gcs ( vtl );
1438
1439   if ( vtl->track_bg_gc )
1440     g_object_unref ( vtl->track_bg_gc );
1441   vtl->track_bg_gc = vik_viewport_new_gc ( vp, "#FFFFFF", width + vtl->bg_line_thickness );
1442
1443   if ( vtl->current_track_gc )
1444     g_object_unref ( vtl->current_track_gc );
1445   vtl->current_track_gc = vik_viewport_new_gc ( vp, "#FF0000", 2 );
1446   gdk_gc_set_line_attributes ( vtl->current_track_gc, 2, GDK_LINE_ON_OFF_DASH, GDK_CAP_ROUND, GDK_JOIN_ROUND );
1447
1448   vtl->track_gc = g_array_sized_new ( FALSE, FALSE, sizeof ( GdkGC * ), VIK_TRW_LAYER_TRACK_GC );
1449
1450   gc[0] = vik_viewport_new_gc ( vp, "#2d870a", width ); /* below range */
1451
1452   gc[1] = vik_viewport_new_gc ( vp, "#0a8742", width );
1453   gc[2] = vik_viewport_new_gc ( vp, "#0a8783", width );
1454   gc[3] = vik_viewport_new_gc ( vp, "#0a4d87", width );
1455   gc[4] = vik_viewport_new_gc ( vp, "#05469f", width );
1456   gc[5] = vik_viewport_new_gc ( vp, "#1b059f", width );
1457   gc[6] = vik_viewport_new_gc ( vp, "#2d059f", width );
1458   gc[7] = vik_viewport_new_gc ( vp, "#4a059f", width );
1459   gc[8] = vik_viewport_new_gc ( vp, "#84059f", width );
1460   gc[9] = vik_viewport_new_gc ( vp, "#96059f", width );
1461   gc[10] = vik_viewport_new_gc ( vp, "#f22ef2", width );
1462
1463   gc[11] = vik_viewport_new_gc ( vp, "#874200", width ); /* above range */
1464
1465   gc[12] = vik_viewport_new_gc ( vp, "#000000", width ); /* black / no speed data */
1466
1467   g_array_append_vals ( vtl->track_gc, gc, VIK_TRW_LAYER_TRACK_GC );
1468 }
1469
1470 VikTrwLayer *vik_trw_layer_create ( VikViewport *vp )
1471 {
1472   VikTrwLayer *rv = vik_trw_layer_new ( 0 );
1473   PangoFontDescription *pfd;
1474   rv->wplabellayout = gtk_widget_create_pango_layout (GTK_WIDGET(vp), NULL);
1475   pfd = pango_font_description_from_string (WAYPOINT_FONT);
1476   pango_layout_set_font_description (rv->wplabellayout, pfd);
1477   /* freeing PangoFontDescription, cause it has been copied by prev. call */
1478   pango_font_description_free (pfd);
1479
1480   vik_layer_rename ( VIK_LAYER(rv), vik_trw_layer_interface.name );
1481
1482   trw_layer_new_track_gcs ( rv, vp );
1483
1484   rv->waypoint_gc = vik_viewport_new_gc ( vp, "#000000", 2 );
1485   rv->waypoint_text_gc = vik_viewport_new_gc ( vp, "#FFFFFF", 1 );
1486   rv->waypoint_bg_gc = vik_viewport_new_gc ( vp, "#8383C4", 1 );
1487   gdk_gc_set_function ( rv->waypoint_bg_gc, GDK_AND );
1488
1489   rv->waypoint_font = gdk_font_load ( "-*-helvetica-bold-r-normal-*-*-100-*-*-p-*-iso8859-1" );
1490
1491   rv->has_verified_thumbnails = FALSE;
1492   rv->wp_symbol = WP_SYMBOL_FILLED_SQUARE;
1493   rv->wp_size = 4;
1494   rv->wp_draw_symbols = TRUE;
1495
1496   rv->coord_mode = vik_viewport_get_coord_mode ( vp );
1497
1498   rv->menu_selection = vik_layer_get_interface(VIK_LAYER(rv)->type)->menu_items_selection;
1499
1500   return rv;
1501 }
1502
1503 static void trw_layer_realize_track ( gchar *name, VikTrack *track, gpointer pass_along[5] )
1504 {
1505   GtkTreeIter *new_iter = g_malloc(sizeof(GtkTreeIter));
1506
1507 #ifdef VIK_CONFIG_ALPHABETIZED_TRW
1508   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 );
1509 #else
1510   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 );
1511 #endif
1512
1513   *new_iter = *((GtkTreeIter *) pass_along[1]);
1514   g_hash_table_insert ( VIK_TRW_LAYER(pass_along[2])->tracks_iters, name, new_iter );
1515
1516   if ( ! track->visible )
1517     vik_treeview_item_set_visible ( (VikTreeview *) pass_along[3], (GtkTreeIter *) pass_along[1], FALSE );
1518 }
1519
1520 static void trw_layer_realize_waypoint ( gchar *name, VikWaypoint *wp, gpointer pass_along[5] )
1521 {
1522   GtkTreeIter *new_iter = g_malloc(sizeof(GtkTreeIter));
1523 #ifdef VIK_CONFIG_ALPHABETIZED_TRW
1524   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 );
1525 #else
1526   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 );
1527 #endif
1528
1529   *new_iter = *((GtkTreeIter *) pass_along[1]);
1530   g_hash_table_insert ( VIK_TRW_LAYER(pass_along[2])->waypoints_iters, name, new_iter );
1531
1532   if ( ! wp->visible )
1533     vik_treeview_item_set_visible ( (VikTreeview *) pass_along[3], (GtkTreeIter *) pass_along[1], FALSE );
1534 }
1535
1536
1537 void vik_trw_layer_realize ( VikTrwLayer *vtl, VikTreeview *vt, GtkTreeIter *layer_iter )
1538 {
1539   GtkTreeIter iter2;
1540   gpointer pass_along[5] = { &(vtl->tracks_iter), &iter2, vtl, vt, (gpointer) VIK_TRW_LAYER_SUBLAYER_TRACK };
1541
1542 #ifdef VIK_CONFIG_ALPHABETIZED_TRW
1543   vik_treeview_add_sublayer_alphabetized ( (VikTreeview *) vt, layer_iter, &(vtl->tracks_iter), _("Tracks"), vtl, NULL, VIK_TRW_LAYER_SUBLAYER_TRACKS, NULL, TRUE, FALSE );
1544 #else
1545   vik_treeview_add_sublayer ( (VikTreeview *) vt, layer_iter, &(vtl->tracks_iter), _("Tracks"), vtl, NULL, VIK_TRW_LAYER_SUBLAYER_TRACKS, NULL, TRUE, FALSE );
1546 #endif
1547   if ( ! vtl->tracks_visible )
1548     vik_treeview_item_set_visible ( (VikTreeview *) vt, &(vtl->tracks_iter), FALSE ); 
1549
1550   g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_realize_track, pass_along );
1551
1552 #ifdef VIK_CONFIG_ALPHABETIZED_TRW
1553   vik_treeview_add_sublayer_alphabetized ( (VikTreeview *) vt, layer_iter, &(vtl->waypoints_iter), _("Waypoints"), vtl, NULL, VIK_TRW_LAYER_SUBLAYER_WAYPOINTS, NULL, TRUE, FALSE );
1554 #else
1555   vik_treeview_add_sublayer ( (VikTreeview *) vt, layer_iter, &(vtl->waypoints_iter), _("Waypoints"), vtl, NULL, VIK_TRW_LAYER_SUBLAYER_WAYPOINTS, NULL, TRUE, FALSE );
1556 #endif
1557
1558   if ( ! vtl->waypoints_visible )
1559     vik_treeview_item_set_visible ( (VikTreeview *) vt, &(vtl->waypoints_iter), FALSE ); 
1560
1561   pass_along[0] = &(vtl->waypoints_iter);
1562   pass_along[4] = (gpointer) VIK_TRW_LAYER_SUBLAYER_WAYPOINT;
1563
1564   g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_realize_waypoint, pass_along );
1565
1566 }
1567
1568 gboolean vik_trw_layer_sublayer_toggle_visible ( VikTrwLayer *l, gint subtype, gpointer sublayer )
1569 {
1570   switch ( subtype )
1571   {
1572     case VIK_TRW_LAYER_SUBLAYER_TRACKS: return (l->tracks_visible ^= 1);
1573     case VIK_TRW_LAYER_SUBLAYER_WAYPOINTS: return (l->waypoints_visible ^= 1);
1574     case VIK_TRW_LAYER_SUBLAYER_TRACK:
1575     {
1576       VikTrack *t = g_hash_table_lookup ( l->tracks, sublayer );
1577       if (t)
1578         return (t->visible ^= 1);
1579       else
1580         return TRUE;
1581     }
1582     case VIK_TRW_LAYER_SUBLAYER_WAYPOINT:
1583     {
1584       VikWaypoint *t = g_hash_table_lookup ( l->waypoints, sublayer );
1585       if (t)
1586         return (t->visible ^= 1);
1587       else
1588         return TRUE;
1589     }
1590   }
1591   return TRUE;
1592 }
1593
1594 // Structure to hold multiple track information for a layer
1595 typedef struct {
1596   gdouble length;
1597   time_t  start_time;
1598   time_t  end_time;
1599   gint    duration;
1600 } tooltip_tracks;
1601
1602 /*
1603  * Build up layer multiple track information via updating the tooltip_tracks structure
1604  */
1605 static void trw_layer_tracks_tooltip ( const gchar *name, VikTrack *tr, tooltip_tracks *tt )
1606 {
1607   tt->length = tt->length + vik_track_get_length (tr);
1608
1609   // Ensure times are available
1610   if ( tr->trackpoints &&
1611        VIK_TRACKPOINT(tr->trackpoints->data)->has_timestamp &&
1612        VIK_TRACKPOINT(g_list_last(tr->trackpoints)->data)->has_timestamp ) {
1613
1614     time_t t1, t2;
1615     t1 = VIK_TRACKPOINT(tr->trackpoints->data)->timestamp;
1616     t2 = VIK_TRACKPOINT(g_list_last(tr->trackpoints)->data)->timestamp;
1617
1618     // Assume never actually have a track with a time of 0 (1st Jan 1970)
1619     // Hence initialize to the first 'proper' value
1620     if ( tt->start_time == 0 )
1621         tt->start_time = t1;
1622     if ( tt->end_time == 0 )
1623         tt->end_time = t2;
1624
1625     // Update find the earliest / last times
1626     if ( t1 < tt->start_time )
1627         tt->start_time = t1;
1628     if ( t2 > tt->end_time )
1629         tt->end_time = t2;
1630
1631     // Keep track of total time
1632     //  there maybe gaps within a track (eg segments)
1633     //  but this should be generally good enough for a simple indicator
1634     tt->duration = tt->duration + (int)(t2-t1);
1635   }
1636 }
1637
1638 /*
1639  * Generate tooltip text for the layer.
1640  * This is relatively complicated as it considers information for
1641  *   no tracks, a single track or multiple tracks
1642  *     (which may or may not have timing information)
1643  */
1644 static const gchar* trw_layer_layer_tooltip ( VikTrwLayer *vtl )
1645 {
1646   gchar tbuf1[32];
1647   gchar tbuf2[64];
1648   gchar tbuf3[64];
1649   gchar tbuf4[10];
1650   tbuf1[0] = '\0';
1651   tbuf2[0] = '\0';
1652   tbuf3[0] = '\0';
1653   tbuf4[0] = '\0';
1654
1655   static gchar tmp_buf[128];
1656   tmp_buf[0] = '\0';
1657
1658   // For compact date format I'm using '%x'     [The preferred date representation for the current locale without the time.]
1659
1660   // Safety check - I think these should always be valid
1661   if ( vtl->tracks && vtl->waypoints ) {
1662     tooltip_tracks tt = { 0.0, 0, 0 };
1663     g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_tracks_tooltip, &tt );
1664
1665     GDate* gdate_start = g_date_new ();
1666     g_date_set_time_t (gdate_start, tt.start_time);
1667
1668     GDate* gdate_end = g_date_new ();
1669     g_date_set_time_t (gdate_end, tt.end_time);
1670
1671     if ( g_date_compare (gdate_start, gdate_end) ) {
1672       // Dates differ so print range on separate line
1673       g_date_strftime (tbuf1, sizeof(tbuf1), "%x", gdate_start);
1674       g_date_strftime (tbuf2, sizeof(tbuf2), "%x", gdate_end);
1675       g_snprintf (tbuf3, sizeof(tbuf3), "%s to %s\n", tbuf1, tbuf2);
1676     }
1677     else {
1678       // Same date so just show it and keep rest of text on the same line - provided it's a valid time!
1679       if ( tt.start_time != 0 )
1680         g_date_strftime (tbuf3, sizeof(tbuf3), "%x: ", gdate_start);
1681     }
1682
1683     tbuf2[0] = '\0';
1684     if ( tt.length > 0.0 ) {
1685       gdouble len_in_units;
1686
1687       // Setup info dependent on distance units
1688       if ( a_vik_get_units_distance() == VIK_UNITS_DISTANCE_MILES ) {
1689         g_snprintf (tbuf4, sizeof(tbuf4), "miles");
1690         len_in_units = VIK_METERS_TO_MILES(tt.length);
1691       }
1692       else {
1693         g_snprintf (tbuf4, sizeof(tbuf4), "kms");
1694         len_in_units = tt.length/1000.0;
1695       }
1696
1697       // Timing information if available
1698       tbuf1[0] = '\0';
1699       if ( tt.duration > 0 ) {
1700         g_snprintf (tbuf1, sizeof(tbuf1),
1701                     _(" in %d:%02d hrs:mins"),
1702                     (int)round(tt.duration/3600), (int)round((tt.duration/60)%60));
1703       }
1704       g_snprintf (tbuf2, sizeof(tbuf2),
1705                   _("\n%sTotal Length %.1f %s%s"),
1706                   tbuf3, len_in_units, tbuf4, tbuf1);
1707     }
1708
1709     // Put together all the elements to form compact tooltip text
1710     g_snprintf (tmp_buf, sizeof(tmp_buf),
1711                 _("Tracks: %d - Waypoints: %d%s"),
1712                 g_hash_table_size (vtl->tracks), g_hash_table_size (vtl->waypoints), tbuf2);
1713
1714     g_date_free (gdate_start);
1715     g_date_free (gdate_end);
1716
1717   }
1718
1719   return tmp_buf;
1720 }
1721
1722 static const gchar* trw_layer_sublayer_tooltip ( VikTrwLayer *l, gint subtype, gpointer sublayer )
1723 {
1724   switch ( subtype )
1725   {
1726     case VIK_TRW_LAYER_SUBLAYER_TRACKS: return NULL;
1727     case VIK_TRW_LAYER_SUBLAYER_WAYPOINTS: return NULL;
1728     case VIK_TRW_LAYER_SUBLAYER_TRACK:
1729     {
1730       VikTrack *tr = g_hash_table_lookup ( l->tracks, sublayer );
1731       if ( tr ) {
1732         // Could be a better way of handling strings - but this works...
1733         gchar time_buf1[20];
1734         gchar time_buf2[20];
1735         time_buf1[0] = '\0';
1736         time_buf2[0] = '\0';
1737         static gchar tmp_buf[100];
1738         // Compact info: Short date eg (11/20/99), duration and length
1739         // Hopefully these are the things that are most useful and so promoted into the tooltip
1740         if ( tr->trackpoints && VIK_TRACKPOINT(tr->trackpoints->data)->has_timestamp ) {
1741           // %x     The preferred date representation for the current locale without the time.
1742           strftime (time_buf1, sizeof(time_buf1), "%x: ", gmtime(&(VIK_TRACKPOINT(tr->trackpoints->data)->timestamp)));
1743           if ( VIK_TRACKPOINT(g_list_last(tr->trackpoints)->data)->has_timestamp ) {
1744             gint dur = ( (VIK_TRACKPOINT(g_list_last(tr->trackpoints)->data)->timestamp) - (VIK_TRACKPOINT(tr->trackpoints->data)->timestamp) );
1745             if ( dur > 0 )
1746               g_snprintf ( time_buf2, sizeof(time_buf2), _("- %d:%02d hrs:mins"), (int)round(dur/3600), (int)round((dur/60)%60) );
1747           }
1748         }
1749         // Get length and consider the appropriate distance units
1750         gdouble tr_len = vik_track_get_length(tr);
1751         vik_units_distance_t dist_units = a_vik_get_units_distance ();
1752         switch (dist_units) {
1753         case VIK_UNITS_DISTANCE_KILOMETRES:
1754           g_snprintf (tmp_buf, sizeof(tmp_buf), _("%s%.1f km %s"), time_buf1, tr_len/1000.0, time_buf2);
1755           break;
1756         case VIK_UNITS_DISTANCE_MILES:
1757           g_snprintf (tmp_buf, sizeof(tmp_buf), _("%s%.1f miles %s"), time_buf1, VIK_METERS_TO_MILES(tr_len), time_buf2);
1758           break;
1759         default:
1760           break;
1761         }
1762         return tmp_buf;
1763       }
1764     }
1765     break;
1766     case VIK_TRW_LAYER_SUBLAYER_WAYPOINT:
1767     {
1768       VikWaypoint *w = g_hash_table_lookup ( l->waypoints, sublayer );
1769       // NB It's OK to return NULL
1770       return w->comment;
1771     }
1772     break;
1773     default: break;
1774   }
1775   return NULL;
1776 }
1777
1778 /**
1779  * General layer selection function, find out which bit is selected and take appropriate action
1780  */
1781 gboolean vik_trw_layer_selected ( VikTrwLayer *l, gint subtype, gpointer sublayer, gint type, gpointer vlp )
1782 {
1783   switch ( type )
1784     {
1785     case VIK_TREEVIEW_TYPE_LAYER:
1786       {
1787         vik_window_set_selected_trw_layer ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l), l );
1788         /* Mark for redraw */
1789         return TRUE;
1790       }
1791       break;
1792
1793     case VIK_TREEVIEW_TYPE_SUBLAYER:
1794       {
1795         switch ( subtype )
1796           {
1797           case VIK_TRW_LAYER_SUBLAYER_TRACKS:
1798             {
1799               vik_window_set_selected_tracks ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l), l->tracks );
1800               /* Mark for redraw */
1801               return TRUE;
1802             }
1803             break;
1804           case VIK_TRW_LAYER_SUBLAYER_TRACK:
1805             {
1806               vik_window_set_selected_track ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l), g_hash_table_lookup ( l->tracks, sublayer ) );
1807               /* Mark for redraw */
1808               return TRUE;
1809             }
1810             break;
1811           case VIK_TRW_LAYER_SUBLAYER_WAYPOINTS:
1812             {
1813               vik_window_set_selected_waypoints ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l), l->waypoints );
1814               /* Mark for redraw */
1815               return TRUE;
1816             }
1817             break;
1818           case VIK_TRW_LAYER_SUBLAYER_WAYPOINT:
1819             {
1820               vik_window_set_selected_waypoint ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l), g_hash_table_lookup ( l->waypoints, sublayer ) );
1821               /* Mark for redraw */
1822               return TRUE;
1823             }
1824             break;
1825           default:
1826             {
1827               return vik_window_clear_highlight ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l) );
1828             }
1829             break;
1830           }
1831         return FALSE;
1832       }
1833       break;
1834
1835     default:
1836       return vik_window_clear_highlight ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l) );
1837       break;
1838     }
1839 }
1840
1841 GHashTable *vik_trw_layer_get_tracks ( VikTrwLayer *l )
1842 {
1843   return l->tracks;
1844 }
1845
1846 GHashTable *vik_trw_layer_get_waypoints ( VikTrwLayer *l )
1847 {
1848   return l->waypoints;
1849 }
1850
1851 static void trw_layer_find_maxmin_waypoints ( const gchar *name, const VikWaypoint *w, struct LatLon maxmin[2] )
1852 {
1853   static VikCoord fixme;
1854   vik_coord_copy_convert ( &(w->coord), VIK_COORD_LATLON, &fixme );
1855   if ( VIK_LATLON(&fixme)->lat > maxmin[0].lat || maxmin[0].lat == 0.0 )
1856     maxmin[0].lat = VIK_LATLON(&fixme)->lat;
1857   if ( VIK_LATLON(&fixme)->lat < maxmin[1].lat || maxmin[1].lat == 0.0 )
1858     maxmin[1].lat = VIK_LATLON(&fixme)->lat;
1859   if ( VIK_LATLON(&fixme)->lon > maxmin[0].lon || maxmin[0].lon == 0.0 )
1860     maxmin[0].lon = VIK_LATLON(&fixme)->lon;
1861   if ( VIK_LATLON(&fixme)->lon < maxmin[1].lon || maxmin[1].lon == 0.0 )
1862     maxmin[1].lon = VIK_LATLON(&fixme)->lon;
1863 }
1864
1865 static void trw_layer_find_maxmin_tracks ( const gchar *name, GList **t, struct LatLon maxmin[2] )
1866 {
1867   GList *tr = *t;
1868   static VikCoord fixme;
1869
1870   while ( tr )
1871   {
1872     vik_coord_copy_convert ( &(VIK_TRACKPOINT(tr->data)->coord), VIK_COORD_LATLON, &fixme );
1873     if ( VIK_LATLON(&fixme)->lat > maxmin[0].lat || maxmin[0].lat == 0.0 )
1874       maxmin[0].lat = VIK_LATLON(&fixme)->lat;
1875     if ( VIK_LATLON(&fixme)->lat < maxmin[1].lat || maxmin[1].lat == 0.0 )
1876       maxmin[1].lat = VIK_LATLON(&fixme)->lat;
1877     if ( VIK_LATLON(&fixme)->lon > maxmin[0].lon || maxmin[0].lon == 0.0 )
1878       maxmin[0].lon = VIK_LATLON(&fixme)->lon;
1879     if ( VIK_LATLON(&fixme)->lon < maxmin[1].lon || maxmin[1].lon == 0.0 )
1880       maxmin[1].lon = VIK_LATLON(&fixme)->lon;
1881     tr = tr->next;
1882   }
1883 }
1884
1885 static void trw_layer_find_maxmin (VikTrwLayer *vtl, struct LatLon maxmin[2])
1886 {
1887   struct LatLon wpt_maxmin[2] = { {0.0,0.0}, {0.0,0.0} };
1888   struct LatLon trk_maxmin[2] = { {0.0,0.0}, {0.0,0.0} };
1889   
1890   g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_find_maxmin_waypoints, wpt_maxmin );
1891   g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_find_maxmin_tracks, trk_maxmin );
1892   if ((wpt_maxmin[0].lat != 0.0 && wpt_maxmin[0].lat > trk_maxmin[0].lat) || trk_maxmin[0].lat == 0.0) {
1893     maxmin[0].lat = wpt_maxmin[0].lat;
1894   }
1895   else {
1896     maxmin[0].lat = trk_maxmin[0].lat;
1897   }
1898   if ((wpt_maxmin[0].lon != 0.0 && wpt_maxmin[0].lon > trk_maxmin[0].lon) || trk_maxmin[0].lon == 0.0) {
1899     maxmin[0].lon = wpt_maxmin[0].lon;
1900   }
1901   else {
1902     maxmin[0].lon = trk_maxmin[0].lon;
1903   }
1904   if ((wpt_maxmin[1].lat != 0.0 && wpt_maxmin[1].lat < trk_maxmin[1].lat) || trk_maxmin[1].lat == 0.0) {
1905     maxmin[1].lat = wpt_maxmin[1].lat;
1906   }
1907   else {
1908     maxmin[1].lat = trk_maxmin[1].lat;
1909   }
1910   if ((wpt_maxmin[1].lon != 0.0 && wpt_maxmin[1].lon < trk_maxmin[1].lon) || trk_maxmin[1].lon == 0.0) {
1911     maxmin[1].lon = wpt_maxmin[1].lon;
1912   }
1913   else {
1914     maxmin[1].lon = trk_maxmin[1].lon;
1915   }
1916 }
1917
1918 gboolean vik_trw_layer_find_center ( VikTrwLayer *vtl, VikCoord *dest )
1919 {
1920   /* 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... */
1921   struct LatLon maxmin[2] = { {0.0,0.0}, {0.0,0.0} };
1922   trw_layer_find_maxmin (vtl, maxmin);
1923   if (maxmin[0].lat == 0.0 && maxmin[0].lon == 0.0 && maxmin[1].lat == 0.0 && maxmin[1].lon == 0.0)
1924     return FALSE;
1925   else
1926   {
1927     struct LatLon average = { (maxmin[0].lat+maxmin[1].lat)/2, (maxmin[0].lon+maxmin[1].lon)/2 };
1928     vik_coord_load_from_latlon ( dest, vtl->coord_mode, &average );
1929     return TRUE;
1930   }
1931 }
1932
1933 static void trw_layer_centerize ( gpointer layer_and_vlp[2] )
1934 {
1935   VikCoord coord;
1936   if ( vik_trw_layer_find_center ( VIK_TRW_LAYER(layer_and_vlp[0]), &coord ) )
1937     goto_coord ( layer_and_vlp[1], NULL, NULL, &coord );
1938   else
1939     a_dialog_info_msg ( VIK_GTK_WINDOW_FROM_LAYER(layer_and_vlp[0]), _("This layer has no waypoints or trackpoints.") );
1940 }
1941
1942 static void trw_layer_zoom_to_show_latlons ( VikTrwLayer *vtl, VikViewport *vvp, struct LatLon maxmin[2] )
1943 {
1944   /* First set the center [in case previously viewing from elsewhere] */
1945   /* Then loop through zoom levels until provided positions are in view */
1946   /* This method is not particularly fast - but should work well enough */
1947   struct LatLon average = { (maxmin[0].lat+maxmin[1].lat)/2, (maxmin[0].lon+maxmin[1].lon)/2 };
1948   VikCoord coord;
1949   vik_coord_load_from_latlon ( &coord, vtl->coord_mode, &average );
1950   vik_viewport_set_center_coord ( vvp, &coord );
1951
1952   /* Convert into definite 'smallest' and 'largest' positions */
1953   struct LatLon minmin;
1954   if ( maxmin[0].lat < maxmin[1].lat )
1955     minmin.lat = maxmin[0].lat;
1956   else
1957     minmin.lat = maxmin[1].lat;
1958
1959   struct LatLon maxmax;
1960   if ( maxmin[0].lon > maxmin[1].lon )
1961     maxmax.lon = maxmin[0].lon;
1962   else
1963     maxmax.lon = maxmin[1].lon;
1964
1965   /* Never zoom in too far - generally not that useful, as too close ! */
1966   /* Always recalculate the 'best' zoom level */
1967   gdouble zoom = 1.0;
1968   vik_viewport_set_zoom ( vvp, zoom );
1969
1970   gdouble min_lat, max_lat, min_lon, max_lon;
1971   /* Should only be a maximum of about 18 iterations from min to max zoom levels */
1972   while ( zoom <= VIK_VIEWPORT_MAX_ZOOM ) {
1973     vik_viewport_get_min_max_lat_lon ( vvp, &min_lat, &max_lat, &min_lon, &max_lon );
1974     /* NB I think the logic used in this test to determine if the bounds is within view
1975        fails if track goes across 180 degrees longitude.
1976        Hopefully that situation is not too common...
1977        Mind you viking doesn't really do edge locations to well anyway */
1978     if ( min_lat < minmin.lat &&
1979          max_lat > minmin.lat &&
1980          min_lon < maxmax.lon &&
1981          max_lon > maxmax.lon )
1982       /* Found within zoom level */
1983       break;
1984
1985     /* Try next */
1986     zoom = zoom * 2;
1987     vik_viewport_set_zoom ( vvp, zoom );
1988   }
1989 }
1990
1991 gboolean vik_trw_layer_auto_set_view ( VikTrwLayer *vtl, VikViewport *vvp )
1992 {
1993   /* TODO: what if there's only one waypoint @ 0,0, it will think nothing found. */
1994   struct LatLon maxmin[2] = { {0.0,0.0}, {0.0,0.0} };
1995   trw_layer_find_maxmin (vtl, maxmin);
1996   if (maxmin[0].lat == 0.0 && maxmin[0].lon == 0.0 && maxmin[1].lat == 0.0 && maxmin[1].lon == 0.0)
1997     return FALSE;
1998   else {
1999     trw_layer_zoom_to_show_latlons ( vtl, vvp, maxmin );
2000     return TRUE;
2001   }
2002 }
2003
2004 static void trw_layer_auto_view ( gpointer layer_and_vlp[2] )
2005 {
2006   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])) ) ) {
2007     vik_layers_panel_emit_update ( VIK_LAYERS_PANEL(layer_and_vlp[1]) );
2008   }
2009   else
2010     a_dialog_info_msg ( VIK_GTK_WINDOW_FROM_LAYER(layer_and_vlp[0]), _("This layer has no waypoints or trackpoints.") );
2011 }
2012
2013 static void trw_layer_export ( gpointer layer_and_vlp[2], const gchar *title, const gchar* default_name, const gchar* trackname, guint file_type )
2014 {
2015   GtkWidget *file_selector;
2016   const gchar *fn;
2017   gboolean failed = FALSE;
2018   file_selector = gtk_file_chooser_dialog_new (title,
2019                                                NULL,
2020                                                GTK_FILE_CHOOSER_ACTION_SAVE,
2021                                                GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
2022                                                GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT,
2023                                                NULL);
2024   gtk_file_chooser_set_current_name (GTK_FILE_CHOOSER(file_selector), default_name);
2025
2026   while ( gtk_dialog_run ( GTK_DIALOG(file_selector) ) == GTK_RESPONSE_ACCEPT )
2027   {
2028     fn = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER(file_selector) );
2029     if ( g_file_test ( fn, G_FILE_TEST_EXISTS ) == FALSE )
2030     {
2031       gtk_widget_hide ( file_selector );
2032       failed = ! a_file_export ( VIK_TRW_LAYER(layer_and_vlp[0]), fn, file_type, trackname);
2033       break;
2034     }
2035     else
2036     {
2037       if ( a_dialog_yes_or_no ( GTK_WINDOW(file_selector), _("The file \"%s\" exists, do you wish to overwrite it?"), a_file_basename ( fn ) ) )
2038       {
2039         gtk_widget_hide ( file_selector );
2040         failed = ! a_file_export ( VIK_TRW_LAYER(layer_and_vlp[0]), fn, file_type, trackname);
2041         break;
2042       }
2043     }
2044   }
2045   gtk_widget_destroy ( file_selector );
2046   if ( failed )
2047     a_dialog_error_msg ( VIK_GTK_WINDOW_FROM_LAYER(layer_and_vlp[0]), _("The filename you requested could not be opened for writing.") );
2048 }
2049
2050 static void trw_layer_export_gpspoint ( gpointer layer_and_vlp[2] )
2051 {
2052   trw_layer_export ( layer_and_vlp, _("Export Layer"), vik_layer_get_name(VIK_LAYER(layer_and_vlp[0])), NULL, FILE_TYPE_GPSPOINT );
2053 }
2054
2055 static void trw_layer_export_gpsmapper ( gpointer layer_and_vlp[2] )
2056 {
2057   trw_layer_export ( layer_and_vlp, _("Export Layer"), vik_layer_get_name(VIK_LAYER(layer_and_vlp[0])), NULL, FILE_TYPE_GPSMAPPER );
2058 }
2059
2060 static void trw_layer_export_gpx ( gpointer layer_and_vlp[2] )
2061 {
2062   trw_layer_export ( layer_and_vlp, _("Export Layer"), vik_layer_get_name(VIK_LAYER(layer_and_vlp[0])), NULL, FILE_TYPE_GPX );
2063 }
2064
2065 static void trw_layer_export_gpx_track ( gpointer pass_along[6] )
2066 {
2067   gpointer layer_and_vlp[2];
2068   layer_and_vlp[0] = pass_along[0];
2069   layer_and_vlp[1] = pass_along[1];
2070
2071   /* Auto append '.gpx' to track name (providing it's not already there) for the default filename */
2072   gchar *auto_save_name = g_strdup ( pass_along[3] );
2073   if ( ! check_file_ext ( auto_save_name, ".gpx" ) )
2074     auto_save_name = g_strconcat ( auto_save_name, ".gpx", NULL );
2075
2076   trw_layer_export ( layer_and_vlp, _("Export Track as GPX"), auto_save_name, pass_along[3], FILE_TYPE_GPX );
2077
2078   g_free ( auto_save_name );
2079 }
2080
2081 static void trw_layer_goto_wp ( gpointer layer_and_vlp[2] )
2082 {
2083   GHashTable *wps = vik_trw_layer_get_waypoints ( VIK_TRW_LAYER(layer_and_vlp[0]) );
2084   GtkWidget *dia = gtk_dialog_new_with_buttons (_("Find"),
2085                                                  VIK_GTK_WINDOW_FROM_LAYER(layer_and_vlp[0]),
2086                                                  GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
2087                                                  GTK_STOCK_CANCEL,
2088                                                  GTK_RESPONSE_REJECT,
2089                                                  GTK_STOCK_OK,
2090                                                  GTK_RESPONSE_ACCEPT,
2091                                                  NULL);
2092
2093   GtkWidget *label, *entry;
2094   label = gtk_label_new(_("Waypoint Name:"));
2095   entry = gtk_entry_new();
2096
2097   gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dia)->vbox), label, FALSE, FALSE, 0);
2098   gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dia)->vbox), entry, FALSE, FALSE, 0);
2099   gtk_widget_show_all ( label );
2100   gtk_widget_show_all ( entry );
2101
2102   gtk_dialog_set_default_response ( GTK_DIALOG(dia), GTK_RESPONSE_ACCEPT );
2103
2104   while ( gtk_dialog_run ( GTK_DIALOG(dia) ) == GTK_RESPONSE_ACCEPT )
2105   {
2106     VikWaypoint *wp;
2107     gchar *upname = g_strdup(gtk_entry_get_text(GTK_ENTRY(entry)));
2108     int i;
2109
2110     for ( i = strlen(upname)-1; i >= 0; i-- )
2111       upname[i] = toupper(upname[i]);
2112
2113     wp = g_hash_table_lookup ( wps, upname );
2114
2115     if (!wp)
2116       a_dialog_error_msg ( VIK_GTK_WINDOW_FROM_LAYER(layer_and_vlp[0]), _("Waypoint not found in this layer.") );
2117     else
2118     {
2119       vik_viewport_set_center_coord ( vik_layers_panel_get_viewport(VIK_LAYERS_PANEL(layer_and_vlp[1])), &(wp->coord) );
2120       vik_layers_panel_emit_update ( VIK_LAYERS_PANEL(layer_and_vlp[1]) );
2121       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 ) );
2122       break;
2123     }
2124
2125     g_free ( upname );
2126
2127   }
2128   gtk_widget_destroy ( dia );
2129 }
2130
2131 gboolean vik_trw_layer_new_waypoint ( VikTrwLayer *vtl, GtkWindow *w, const VikCoord *def_coord )
2132 {
2133   gchar *default_name = highest_wp_number_get(vtl);
2134   VikWaypoint *wp = vik_waypoint_new();
2135   gchar *returned_name;
2136   gboolean updated;
2137   wp->coord = *def_coord;
2138
2139   if ( ( returned_name = a_dialog_waypoint ( w, default_name, wp, vik_trw_layer_get_waypoints ( vtl ), vtl->coord_mode, TRUE, &updated ) ) )
2140   {
2141     wp->visible = TRUE;
2142     vik_trw_layer_add_waypoint ( vtl, returned_name, wp );
2143     g_free (default_name);
2144     return TRUE;
2145   }
2146   g_free (default_name);
2147   vik_waypoint_free(wp);
2148   return FALSE;
2149 }
2150
2151 static void trw_layer_new_wikipedia_wp_viewport ( gpointer lav[2] )
2152 {
2153   VikCoord one, two;
2154   struct LatLon one_ll, two_ll;
2155   struct LatLon maxmin[2] = { {0.0,0.0}, {0.0,0.0} };
2156
2157   VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
2158   VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
2159   VikWindow *vw = (VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl));
2160   VikViewport *vvp =  vik_window_viewport(vw);
2161   vik_viewport_screen_to_coord ( vvp, 0, 0, &one);
2162   vik_viewport_screen_to_coord ( vvp, vik_viewport_get_width(vvp), vik_viewport_get_height(vvp), &two);
2163   vik_coord_to_latlon(&one, &one_ll);
2164   vik_coord_to_latlon(&two, &two_ll);
2165   if (one_ll.lat > two_ll.lat) {
2166     maxmin[0].lat = one_ll.lat;
2167     maxmin[1].lat = two_ll.lat;
2168   }
2169   else {
2170     maxmin[0].lat = two_ll.lat;
2171     maxmin[1].lat = one_ll.lat;
2172   }
2173   if (one_ll.lon > two_ll.lon) {
2174     maxmin[0].lon = one_ll.lon;
2175     maxmin[1].lon = two_ll.lon;
2176   }
2177   else {
2178     maxmin[0].lon = two_ll.lon;
2179     maxmin[1].lon = one_ll.lon;
2180   }
2181   a_geonames_wikipedia_box((VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl)), vtl, vlp, maxmin);
2182 }
2183
2184 static void trw_layer_new_wikipedia_wp_layer ( gpointer lav[2] )
2185 {
2186   VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
2187   VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
2188   struct LatLon maxmin[2] = { {0.0,0.0}, {0.0,0.0} };
2189   
2190   trw_layer_find_maxmin (vtl, maxmin);
2191   a_geonames_wikipedia_box((VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl)), vtl, vlp, maxmin);
2192 }
2193
2194 static void trw_layer_new_wp ( gpointer lav[2] )
2195 {
2196   VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
2197   VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
2198   /* TODO longone: okay, if layer above (aggregate) is invisible but vtl->visible is true, this redraws for no reason.
2199      instead return true if you want to update. */
2200   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 )
2201     vik_layers_panel_emit_update ( vlp );
2202 }
2203
2204 static void trw_layer_auto_tracks_view ( gpointer lav[2] )
2205 {
2206   VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
2207   VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
2208
2209   if ( g_hash_table_size (vtl->tracks) > 0 ) {
2210     struct LatLon maxmin[2] = { {0,0}, {0,0} };
2211     g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_find_maxmin_tracks, maxmin );
2212     trw_layer_zoom_to_show_latlons ( vtl, vik_layers_panel_get_viewport (vlp), maxmin );
2213     vik_layers_panel_emit_update ( vlp );
2214   }
2215 }
2216
2217 static void trw_layer_single_waypoint_jump ( const gchar *name, const VikWaypoint *wp, gpointer vvp )
2218 {
2219   /* NB do not care if wp is visible or not */
2220   vik_viewport_set_center_coord ( VIK_VIEWPORT(vvp), &(wp->coord) );
2221 }
2222
2223 static void trw_layer_auto_waypoints_view ( gpointer lav[2] )
2224 {
2225   VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
2226   VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
2227
2228   /* Only 1 waypoint - jump straight to it */
2229   if ( g_hash_table_size (vtl->waypoints) == 1 ) {
2230     VikViewport *vvp = vik_layers_panel_get_viewport (vlp);
2231     g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_single_waypoint_jump, (gpointer) vvp );
2232   }
2233   /* If at least 2 waypoints - find center and then zoom to fit */
2234   else if ( g_hash_table_size (vtl->waypoints) > 1 )
2235   {
2236     struct LatLon maxmin[2] = { {0,0}, {0,0} };
2237     g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_find_maxmin_waypoints, maxmin );
2238     trw_layer_zoom_to_show_latlons ( vtl, vik_layers_panel_get_viewport (vlp), maxmin );
2239   }
2240
2241   vik_layers_panel_emit_update ( vlp );
2242 }
2243
2244 void vik_trw_layer_add_menu_items ( VikTrwLayer *vtl, GtkMenu *menu, gpointer vlp )
2245 {
2246   static gpointer pass_along[2];
2247   GtkWidget *item;
2248   GtkWidget *export_submenu;
2249   GtkWidget *wikipedia_submenu;
2250   pass_along[0] = vtl;
2251   pass_along[1] = vlp;
2252
2253   item = gtk_menu_item_new();
2254   gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
2255   gtk_widget_show ( item );
2256
2257   item = gtk_menu_item_new_with_mnemonic ( _("_View Layer") );
2258   g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_view), pass_along );
2259   gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
2260   gtk_widget_show ( item );
2261
2262   item = gtk_menu_item_new_with_mnemonic ( _("View All Trac_ks") );
2263   g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_tracks_view), pass_along );
2264   gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
2265   gtk_widget_show ( item );
2266
2267   item = gtk_menu_item_new_with_mnemonic ( _("V_iew All Waypoints") );
2268   g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_waypoints_view), pass_along );
2269   gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
2270   gtk_widget_show ( item );
2271
2272   item = gtk_menu_item_new_with_mnemonic ( _("_Goto Center of Layer") );
2273   g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_centerize), pass_along );
2274   gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
2275   gtk_widget_show ( item );
2276
2277   item = gtk_menu_item_new_with_mnemonic ( _("Goto _Waypoint") );
2278   g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_wp), pass_along );
2279   gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
2280   gtk_widget_show ( item );
2281
2282   export_submenu = gtk_menu_new ();
2283   item = gtk_menu_item_new_with_mnemonic ( _("_Export Layer") );
2284   gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
2285   gtk_widget_show ( item );
2286   gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), export_submenu );
2287   
2288   item = gtk_menu_item_new_with_mnemonic ( _("Export as GPS_Point") );
2289   g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_gpspoint), pass_along );
2290   gtk_menu_shell_append (GTK_MENU_SHELL (export_submenu), item);
2291   gtk_widget_show ( item );
2292
2293   item = gtk_menu_item_new_with_mnemonic ( _("Export as GPS_Mapper") );
2294   g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_gpsmapper), pass_along );
2295   gtk_menu_shell_append (GTK_MENU_SHELL (export_submenu), item);
2296   gtk_widget_show ( item );
2297
2298   item = gtk_menu_item_new_with_mnemonic ( _("Export as _GPX") );
2299   g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_gpx), pass_along );
2300   gtk_menu_shell_append (GTK_MENU_SHELL (export_submenu), item);
2301   gtk_widget_show ( item );
2302
2303   item = gtk_menu_item_new_with_mnemonic ( _("_New Waypoint") );
2304   g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_wp), pass_along );
2305   gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
2306   gtk_widget_show ( item );
2307
2308 #ifdef VIK_CONFIG_GEONAMES
2309   wikipedia_submenu = gtk_menu_new();
2310   item = gtk_menu_item_new_with_mnemonic ( _("_Add Wikipedia Waypoints") );
2311   gtk_menu_shell_append(GTK_MENU_SHELL (menu), item);
2312   gtk_widget_show(item);
2313   gtk_menu_item_set_submenu(GTK_MENU_ITEM(item), wikipedia_submenu);
2314
2315   item = gtk_menu_item_new_with_mnemonic ( _("Within _Layer Bounds") );
2316   g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_wikipedia_wp_layer), pass_along );
2317   gtk_menu_shell_append (GTK_MENU_SHELL (wikipedia_submenu), item);
2318   gtk_widget_show ( item );
2319
2320   item = gtk_menu_item_new_with_mnemonic ( _("Within _Current View") );
2321   g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_wikipedia_wp_viewport), pass_along );
2322   gtk_menu_shell_append (GTK_MENU_SHELL (wikipedia_submenu), item);
2323   gtk_widget_show ( item );
2324 #endif
2325
2326 #ifdef VIK_CONFIG_OPENSTREETMAP 
2327   item = gtk_menu_item_new_with_mnemonic ( _("Upload to _OSM") );
2328   g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(osm_traces_upload_cb), pass_along );
2329   gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
2330   gtk_widget_show ( item );
2331 #endif
2332
2333   item = a_acquire_trwlayer_menu ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)), vlp,
2334                                    vik_layers_panel_get_viewport(VIK_LAYERS_PANEL(vlp)), vtl );
2335   if ( item ) {
2336     gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
2337     gtk_widget_show ( item );
2338   }  
2339
2340   item = a_acquire_trwlayer_track_menu ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)), vlp,
2341                                          vik_layers_panel_get_viewport(VIK_LAYERS_PANEL(vlp)), vtl );
2342   if ( item ) {
2343     gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
2344     gtk_widget_show ( item );
2345   }  
2346 }
2347
2348 void vik_trw_layer_add_waypoint ( VikTrwLayer *vtl, gchar *name, VikWaypoint *wp )
2349 {
2350   if ( VIK_LAYER(vtl)->realized )
2351   {
2352     VikWaypoint *oldwp = VIK_WAYPOINT ( g_hash_table_lookup ( vtl->waypoints, name ) );
2353     if ( oldwp )
2354       wp->visible = oldwp->visible; /* same visibility so we don't have to update viktreeview */
2355     else
2356     {
2357       GtkTreeIter *iter = g_malloc(sizeof(GtkTreeIter));
2358       // Visibility column always needed for waypoints
2359 #ifdef VIK_CONFIG_ALPHABETIZED_TRW
2360       vik_treeview_add_sublayer_alphabetized ( VIK_LAYER(vtl)->vt, &(vtl->waypoints_iter), iter, name, vtl, name, VIK_TRW_LAYER_SUBLAYER_WAYPOINT, NULL, TRUE, TRUE );
2361 #else
2362       vik_treeview_add_sublayer ( VIK_LAYER(vtl)->vt, &(vtl->waypoints_iter), iter, name, vtl, name, VIK_TRW_LAYER_SUBLAYER_WAYPOINT, NULL, TRUE, TRUE );
2363 #endif
2364       // Actual setting of visibility dependent on the waypoint
2365       vik_treeview_item_set_visible ( VIK_LAYER(vtl)->vt, iter, wp->visible );
2366       vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, iter );
2367       g_hash_table_insert ( vtl->waypoints_iters, name, iter );
2368     }
2369   }
2370
2371   highest_wp_number_add_wp(vtl, name);
2372   g_hash_table_insert ( vtl->waypoints, name, wp );
2373  
2374 }
2375
2376 void vik_trw_layer_add_track ( VikTrwLayer *vtl, gchar *name, VikTrack *t )
2377 {
2378   if ( VIK_LAYER(vtl)->realized )
2379   {
2380     VikTrack *oldt = VIK_TRACK ( g_hash_table_lookup ( vtl->tracks, name ) );
2381     if ( oldt )
2382       t->visible = oldt->visible; /* same visibility so we don't have to update viktreeview */
2383     else
2384     {
2385       GtkTreeIter *iter = g_malloc(sizeof(GtkTreeIter));
2386       // Visibility column always needed for tracks
2387 #ifdef VIK_CONFIG_ALPHABETIZED_TRW
2388       vik_treeview_add_sublayer_alphabetized ( VIK_LAYER(vtl)->vt, &(vtl->tracks_iter), iter, name, vtl, name, VIK_TRW_LAYER_SUBLAYER_TRACK, NULL, TRUE, TRUE );
2389 #else
2390       vik_treeview_add_sublayer ( VIK_LAYER(vtl)->vt, &(vtl->tracks_iter), iter, name, vtl, name, VIK_TRW_LAYER_SUBLAYER_TRACK, NULL, TRUE, TRUE );
2391 #endif
2392       // Actual setting of visibility dependent on the track
2393       vik_treeview_item_set_visible ( VIK_LAYER(vtl)->vt, iter, t->visible );
2394       vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, iter );
2395       g_hash_table_insert ( vtl->tracks_iters, name, iter );
2396     }
2397   }
2398
2399   g_hash_table_insert ( vtl->tracks, name, t );
2400  
2401 }
2402
2403 /* to be called whenever a track has been deleted or may have been changed. */
2404 void trw_layer_cancel_tps_of_track ( VikTrwLayer *vtl, const gchar *trk_name )
2405 {
2406   if (vtl->current_tp_track_name && g_strcasecmp(trk_name, vtl->current_tp_track_name) == 0)
2407     trw_layer_cancel_current_tp ( vtl, FALSE );
2408   else if (vtl->last_tp_track_name && g_strcasecmp(trk_name, vtl->last_tp_track_name) == 0)
2409     trw_layer_cancel_last_tp ( vtl );
2410 }
2411         
2412 static gchar *get_new_unique_sublayer_name (VikTrwLayer *vtl, gint sublayer_type, const gchar *name)
2413 {
2414  gint i = 2;
2415  gchar *newname = g_strdup(name);
2416  while ((sublayer_type == VIK_TRW_LAYER_SUBLAYER_TRACK) ?
2417          (void *)vik_trw_layer_get_track(vtl, newname) : (void *)vik_trw_layer_get_waypoint(vtl, newname)) {
2418     gchar *new_newname = g_strdup_printf("%s#%d", name, i);
2419     g_free(newname);
2420     newname = new_newname;
2421     i++;
2422   }
2423   return newname;
2424 }
2425
2426 void vik_trw_layer_filein_add_waypoint ( VikTrwLayer *vtl, gchar *name, VikWaypoint *wp )
2427 {
2428   vik_trw_layer_add_waypoint ( vtl,
2429                         get_new_unique_sublayer_name(vtl, VIK_TRW_LAYER_SUBLAYER_WAYPOINT, name),
2430                         wp );
2431 }
2432 void vik_trw_layer_filein_add_track ( VikTrwLayer *vtl, gchar *name, VikTrack *tr )
2433 {
2434   if ( vtl->magic_scissors_append && vtl->magic_scissors_current_track ) {
2435     vik_track_remove_dup_points ( tr ); /* make "double point" track work to undo */
2436     vik_track_steal_and_append_trackpoints ( vtl->magic_scissors_current_track, tr );
2437     vik_track_free ( tr );
2438     vtl->magic_scissors_append = FALSE; /* this means we have added it */
2439   } else {
2440     gchar *new_name = get_new_unique_sublayer_name(vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, name);
2441     vik_trw_layer_add_track ( vtl, new_name, tr );
2442
2443     if ( vtl->magic_scissors_check_added_track ) {
2444       vik_track_remove_dup_points ( tr ); /* make "double point" track work to undo */
2445       if ( vtl->magic_scissors_added_track_name ) /* for google routes */
2446         g_free ( vtl->magic_scissors_added_track_name );
2447       vtl->magic_scissors_added_track_name = g_strdup(new_name);
2448     }
2449   }
2450 }
2451
2452 static void trw_layer_enum_item ( const gchar *name, GList **tr, GList **l )
2453 {
2454   *l = g_list_append(*l, (gpointer)name);
2455 }
2456
2457 static void trw_layer_move_item ( VikTrwLayer *vtl_src, VikTrwLayer *vtl_dest, gchar *name, gint type )
2458 {
2459   gchar *newname = get_new_unique_sublayer_name(vtl_dest, type, name);
2460   if (type == VIK_TRW_LAYER_SUBLAYER_TRACK) {
2461     VikTrack *t;
2462     t = vik_track_copy(vik_trw_layer_get_track(vtl_src, name));
2463     vik_trw_layer_delete_track(vtl_src, name);
2464     vik_trw_layer_add_track(vtl_dest, newname, t);
2465   }
2466   if (type==VIK_TRW_LAYER_SUBLAYER_WAYPOINT) {
2467     VikWaypoint *w;
2468     w = vik_waypoint_copy(vik_trw_layer_get_waypoint(vtl_src, name));
2469     vik_trw_layer_delete_waypoint(vtl_src, name);
2470     vik_trw_layer_add_waypoint(vtl_dest, newname, w);
2471   }
2472 }
2473
2474 static void trw_layer_drag_drop_request ( VikTrwLayer *vtl_src, VikTrwLayer *vtl_dest, GtkTreeIter *src_item_iter, GtkTreePath *dest_path )
2475 {
2476   VikTreeview *vt = VIK_LAYER(vtl_src)->vt;
2477   gint type = vik_treeview_item_get_data(vt, src_item_iter);
2478
2479   if (!vik_treeview_item_get_pointer(vt, src_item_iter)) {
2480     GList *items = NULL;
2481     GList *iter;
2482
2483     if (type==VIK_TRW_LAYER_SUBLAYER_TRACKS) {
2484       g_hash_table_foreach ( vtl_src->tracks, (GHFunc)trw_layer_enum_item, &items);
2485     } 
2486     if (type==VIK_TRW_LAYER_SUBLAYER_WAYPOINTS) {
2487       g_hash_table_foreach ( vtl_src->waypoints, (GHFunc)trw_layer_enum_item, &items);
2488     }    
2489       
2490     iter = items;
2491     while (iter) {
2492       if (type==VIK_TRW_LAYER_SUBLAYER_TRACKS) {
2493         trw_layer_move_item ( vtl_src, vtl_dest, iter->data, VIK_TRW_LAYER_SUBLAYER_TRACK);
2494       } else {
2495         trw_layer_move_item ( vtl_src, vtl_dest, iter->data, VIK_TRW_LAYER_SUBLAYER_WAYPOINT);
2496       }
2497       iter = iter->next;
2498     }
2499     if (items) 
2500       g_list_free(items);
2501   } else {
2502     gchar *name = vik_treeview_item_get_pointer(vt, src_item_iter);
2503     trw_layer_move_item(vtl_src, vtl_dest, name, type);
2504   }
2505 }
2506
2507
2508 gboolean vik_trw_layer_delete_track ( VikTrwLayer *vtl, const gchar *trk_name )
2509 {
2510   VikTrack *t = g_hash_table_lookup ( vtl->tracks, trk_name );
2511   gboolean was_visible = FALSE;
2512   if ( t )
2513   {
2514     GtkTreeIter *it;
2515     was_visible = t->visible;
2516     if ( t == vtl->current_track )
2517       vtl->current_track = NULL;
2518     if ( t == vtl->magic_scissors_current_track )
2519       vtl->magic_scissors_current_track = NULL;
2520
2521     /* could be current_tp, so we have to check */
2522     trw_layer_cancel_tps_of_track ( vtl, trk_name );
2523
2524     g_assert ( ( it = g_hash_table_lookup ( vtl->tracks_iters, trk_name ) ) );
2525     vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, it );
2526     g_hash_table_remove ( vtl->tracks_iters, trk_name );
2527
2528     /* do this last because trk_name may be pointing to actual orig key */
2529     g_hash_table_remove ( vtl->tracks, trk_name );
2530   }
2531   return was_visible;
2532 }
2533
2534 gboolean vik_trw_layer_delete_waypoint ( VikTrwLayer *vtl, const gchar *wp_name )
2535 {
2536   gboolean was_visible = FALSE;
2537   VikWaypoint *wp;
2538
2539   wp = g_hash_table_lookup ( vtl->waypoints, wp_name );
2540   if ( wp ) {
2541     GtkTreeIter *it;
2542
2543     if ( wp == vtl->current_wp ) {
2544       vtl->current_wp = NULL;
2545       vtl->current_wp_name = NULL;
2546       vtl->moving_wp = FALSE;
2547     }
2548
2549     was_visible = wp->visible;
2550     g_assert ( ( it = g_hash_table_lookup ( vtl->waypoints_iters, (gchar *) wp_name ) ) );
2551     vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, it );
2552     g_hash_table_remove ( vtl->waypoints_iters, (gchar *) wp_name );
2553
2554     highest_wp_number_remove_wp(vtl, wp_name);
2555     g_hash_table_remove ( vtl->waypoints, wp_name ); /* last because this frees name */
2556   }
2557
2558   return was_visible;
2559 }
2560
2561 static void remove_item_from_treeview(const gchar *name, GtkTreeIter *it, VikTreeview * vt)
2562 {
2563     vik_treeview_item_delete (vt, it );
2564 }
2565
2566 void vik_trw_layer_delete_all_tracks ( VikTrwLayer *vtl )
2567 {
2568
2569   vtl->current_track = NULL;
2570   vtl->magic_scissors_current_track = NULL;
2571   if (vtl->current_tp_track_name)
2572     trw_layer_cancel_current_tp(vtl, FALSE);
2573   if (vtl->last_tp_track_name)
2574     trw_layer_cancel_last_tp ( vtl );
2575
2576   g_hash_table_foreach(vtl->tracks_iters, (GHFunc) remove_item_from_treeview, VIK_LAYER(vtl)->vt);
2577   g_hash_table_remove_all(vtl->tracks_iters);
2578   g_hash_table_remove_all(vtl->tracks);
2579
2580   /* TODO: only update if the layer is visible (ticked) */
2581   vik_layer_emit_update ( VIK_LAYER(vtl) );
2582 }
2583
2584 void vik_trw_layer_delete_all_waypoints ( VikTrwLayer *vtl )
2585 {
2586   vtl->current_wp = NULL;
2587   vtl->current_wp_name = NULL;
2588   vtl->moving_wp = FALSE;
2589
2590   highest_wp_number_reset(vtl);
2591
2592   g_hash_table_foreach(vtl->waypoints_iters, (GHFunc) remove_item_from_treeview, VIK_LAYER(vtl)->vt);
2593   g_hash_table_remove_all(vtl->waypoints_iters);
2594   g_hash_table_remove_all(vtl->waypoints);
2595
2596   /* TODO: only update if the layer is visible (ticked) */
2597   vik_layer_emit_update ( VIK_LAYER(vtl) );
2598 }
2599
2600 static void trw_layer_delete_item ( gpointer pass_along[6] )
2601 {
2602   VikTrwLayer *vtl = VIK_TRW_LAYER(pass_along[0]);
2603   gboolean was_visible = FALSE;
2604   if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_WAYPOINT )
2605   {
2606     was_visible = vik_trw_layer_delete_waypoint ( vtl, (gchar *) pass_along[3] );
2607   }
2608   else
2609   {
2610     was_visible = vik_trw_layer_delete_track ( vtl, (gchar *) pass_along[3] );
2611   }
2612   if ( was_visible )
2613     vik_layer_emit_update ( VIK_LAYER(vtl) );
2614 }
2615
2616
2617 static void trw_layer_properties_item ( gpointer pass_along[6] )
2618 {
2619   VikTrwLayer *vtl = VIK_TRW_LAYER(pass_along[0]);
2620   if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_WAYPOINT )
2621   {
2622     VikWaypoint *wp = g_hash_table_lookup ( vtl->waypoints, pass_along[3] );
2623     if ( wp )
2624     {
2625       gboolean updated = FALSE;
2626       a_dialog_waypoint ( VIK_GTK_WINDOW_FROM_LAYER(vtl), pass_along[3], wp, NULL, vtl->coord_mode, FALSE, &updated );
2627
2628       if ( updated && VIK_LAYER(vtl)->visible )
2629         vik_layer_emit_update ( VIK_LAYER(vtl) );
2630     }
2631   }
2632   else
2633   {
2634     VikTrack *tr = g_hash_table_lookup ( vtl->tracks, pass_along[3] );
2635     if ( tr )
2636     {
2637       vik_trw_layer_propwin_run ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
2638                                   vtl, tr,
2639                                   pass_along[1], /* vlp */
2640                                   pass_along[3],  /* track name */
2641                                   pass_along[5] );  /* vvp */
2642     }
2643   }
2644 }
2645
2646 /*
2647    Parameter 1 -> VikLayersPanel
2648    Parameter 2 -> VikLayer
2649    Parameter 3 -> VikViewport
2650 */
2651 static void goto_coord ( gpointer *vlp, gpointer vl, gpointer vvp, const VikCoord *coord )
2652 {
2653   if ( vlp ) {
2654     vik_viewport_set_center_coord ( vik_layers_panel_get_viewport (VIK_LAYERS_PANEL(vlp)), coord );
2655     vik_layers_panel_emit_update ( VIK_LAYERS_PANEL(vlp) );
2656   }
2657   else {
2658     /* since vlp not set, vl & vvp should be valid instead! */
2659     if ( vl && vvp ) {
2660       vik_viewport_set_center_coord ( VIK_VIEWPORT(vvp), coord );
2661       vik_layer_emit_update ( VIK_LAYER(vl) );
2662     }
2663   }
2664 }
2665
2666 static void trw_layer_goto_track_startpoint ( gpointer pass_along[6] )
2667 {
2668   GList *trps = ((VikTrack *) g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, pass_along[3] ))->trackpoints;
2669   if ( trps && trps->data )
2670     goto_coord ( pass_along[1], pass_along[0], pass_along[5], &(((VikTrackpoint *) trps->data)->coord));
2671 }
2672
2673 static void trw_layer_goto_track_center ( gpointer pass_along[6] )
2674 {
2675   /* FIXME: get this into viktrack.c, and should be ->trackpoints right? */
2676   GList **trps = g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, pass_along[3] );
2677   if ( trps && *trps )
2678   {
2679     struct LatLon average, maxmin[2] = { {0,0}, {0,0} };
2680     VikCoord coord;
2681     trw_layer_find_maxmin_tracks ( NULL, trps, maxmin );
2682     average.lat = (maxmin[0].lat+maxmin[1].lat)/2;
2683     average.lon = (maxmin[0].lon+maxmin[1].lon)/2;
2684     vik_coord_load_from_latlon ( &coord, VIK_TRW_LAYER(pass_along[0])->coord_mode, &average );
2685     goto_coord ( pass_along[1], pass_along[0], pass_along[5], &coord);
2686   }
2687 }
2688
2689 static void trw_layer_extend_track_end ( gpointer pass_along[6] )
2690 {
2691   VikTrwLayer *vtl = VIK_TRW_LAYER(pass_along[0]);
2692   VikTrack *track = g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, pass_along[3] );
2693
2694   vtl->current_track = track;
2695   vik_window_enable_layer_tool ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)), VIK_LAYER_TRW, TOOL_CREATE_TRACK);
2696
2697   if ( track->trackpoints )
2698     goto_coord ( pass_along[1], pass_along[0], pass_along[5], &(((VikTrackpoint *)g_list_last(track->trackpoints)->data)->coord) );
2699 }
2700
2701 /**
2702  * extend a track using magic scissors
2703  */
2704 static void trw_layer_extend_track_end_ms ( gpointer pass_along[6] )
2705 {
2706   VikTrwLayer *vtl = VIK_TRW_LAYER(pass_along[0]);
2707   VikTrack *track = g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, pass_along[3] );
2708   VikCoord last_coord = (((VikTrackpoint *)g_list_last(track->trackpoints)->data)->coord);
2709
2710   vik_window_enable_layer_tool ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)), VIK_LAYER_TRW, NUM_TOOLS );
2711   vtl->magic_scissors_coord =  last_coord;
2712   vtl->magic_scissors_current_track = track;
2713   vtl->magic_scissors_started = TRUE;
2714
2715   if ( track->trackpoints )
2716     goto_coord ( pass_along[1], pass_along[0], pass_along[5], &last_coord) ;
2717
2718 }
2719
2720 static void trw_layer_apply_dem_data ( gpointer pass_along[6] )
2721 {
2722   /* TODO: check & warn if no DEM data, or no applicable DEM data. */
2723   /* Also warn if overwrite old elevation data */
2724   VikTrack *track = (VikTrack *) g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, pass_along[3] );
2725
2726   vik_track_apply_dem_data ( track );
2727 }
2728
2729 static void trw_layer_goto_track_endpoint ( gpointer pass_along[6] )
2730 {
2731   GList *trps = ((VikTrack *) g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, pass_along[3] ))->trackpoints;
2732   if ( !trps )
2733     return;
2734   trps = g_list_last(trps);
2735   goto_coord ( pass_along[1], pass_along[0], pass_along[5], &(((VikTrackpoint *) trps->data)->coord));
2736 }
2737
2738 static void trw_layer_goto_track_max_speed ( gpointer pass_along[6] )
2739 {
2740   VikTrackpoint* vtp = vik_track_get_tp_by_max_speed ( g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, pass_along[3] ) );
2741   if ( !vtp )
2742     return;
2743   goto_coord ( pass_along[1], pass_along[0], pass_along[5], &(vtp->coord));
2744 }
2745
2746 static void trw_layer_goto_track_max_alt ( gpointer pass_along[6] )
2747 {
2748   VikTrackpoint* vtp = vik_track_get_tp_by_max_alt ( g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, pass_along[3] ) );
2749   if ( !vtp )
2750     return;
2751   goto_coord ( pass_along[1], pass_along[0], pass_along[5], &(vtp->coord));
2752 }
2753
2754 static void trw_layer_goto_track_min_alt ( gpointer pass_along[6] )
2755 {
2756   VikTrackpoint* vtp = vik_track_get_tp_by_min_alt ( g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, pass_along[3] ) );
2757   if ( !vtp )
2758     return;
2759   goto_coord ( pass_along[1], pass_along[0], pass_along[5], &(vtp->coord));
2760 }
2761
2762 /* 
2763  * Automatically change the viewport to center on the track and zoom to see the extent of the track
2764  */ 
2765 static void trw_layer_auto_track_view ( gpointer pass_along[5] )
2766 {
2767   GList **trps = g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, pass_along[3] );
2768   if ( trps && *trps )
2769   {
2770     struct LatLon maxmin[2] = { {0,0}, {0,0} };
2771     trw_layer_find_maxmin_tracks ( NULL, trps, maxmin );
2772     trw_layer_zoom_to_show_latlons ( VIK_TRW_LAYER(pass_along[0]), pass_along[5], maxmin );
2773     if ( pass_along[1] )
2774       vik_layers_panel_emit_update ( VIK_LAYERS_PANEL(pass_along[1]) );
2775     else
2776       vik_layer_emit_update ( VIK_LAYER(pass_along[0]) );
2777   }
2778 }
2779
2780 /*************************************
2781  * merge/split by time routines 
2782  *************************************/
2783
2784 /* called for each key in track hash table.
2785  * If the current track has time stamp, add it to the result,
2786  * except the one pointed by "exclude".
2787  * set exclude to NULL if there is no exclude to check.
2788  * Not that result is in reverse (for performance reason).
2789  */
2790 typedef struct {
2791   GList **result;
2792   GList  *exclude;
2793 } twt_udata;
2794 static void find_tracks_with_timestamp(gpointer key, gpointer value, gpointer udata)
2795 {
2796   twt_udata *user_data = udata;
2797   VikTrackpoint *p1, *p2;
2798
2799   if (VIK_TRACK(value)->trackpoints == user_data->exclude) {
2800     return;
2801   }
2802
2803   if (VIK_TRACK(value)->trackpoints) {
2804     p1 = VIK_TRACKPOINT(VIK_TRACK(value)->trackpoints->data);
2805     p2 = VIK_TRACKPOINT(g_list_last(VIK_TRACK(value)->trackpoints)->data);
2806
2807     if (!p1->has_timestamp || !p2->has_timestamp) {
2808       g_print("no timestamp\n");
2809       return;
2810     }
2811
2812   }
2813
2814   *(user_data->result) = g_list_prepend(*(user_data->result), key);
2815 }
2816
2817 /* called for each key in track hash table. if original track user_data[1] is close enough
2818  * to the passed one, add it to list in user_data[0] 
2819  */
2820 static void find_nearby_track(gpointer key, gpointer value, gpointer user_data)
2821 {
2822   time_t t1, t2;
2823   VikTrackpoint *p1, *p2;
2824
2825   GList **nearby_tracks = ((gpointer *)user_data)[0];
2826   GList *orig_track = ((gpointer *)user_data)[1];
2827   guint thr = GPOINTER_TO_UINT (((gpointer *)user_data)[2]);
2828
2829   /* outline: 
2830    * detect reasons for not merging, and return
2831    * if no reason is found not to merge, then do it.
2832    */
2833
2834   if (VIK_TRACK(value)->trackpoints == orig_track) {
2835     return;
2836   }
2837
2838   t1 = VIK_TRACKPOINT(orig_track->data)->timestamp;
2839   t2 = VIK_TRACKPOINT(g_list_last(orig_track)->data)->timestamp;
2840
2841   if (VIK_TRACK(value)->trackpoints) {
2842     p1 = VIK_TRACKPOINT(VIK_TRACK(value)->trackpoints->data);
2843     p2 = VIK_TRACKPOINT(g_list_last(VIK_TRACK(value)->trackpoints)->data);
2844
2845     if (!p1->has_timestamp || !p2->has_timestamp) {
2846       g_print("no timestamp\n");
2847       return;
2848     }
2849
2850     /*  g_print("Got track named %s, times %d, %d\n", (gchar *)key, p1->timestamp, p2->timestamp); */
2851     if (! (abs(t1 - p2->timestamp) < thr*60 ||
2852         /*  p1 p2      t1 t2 */
2853            abs(p1->timestamp - t2) < thr*60)
2854         /*  t1 t2      p1 p2 */
2855         ) {
2856       return;
2857     }
2858   }
2859
2860   *nearby_tracks = g_list_prepend(*nearby_tracks, key);
2861 }
2862
2863 /* comparison function used to sort tracks; a and b are hash table keys */
2864 static gint track_compare(gconstpointer a, gconstpointer b, gpointer user_data)
2865 {
2866   GHashTable *tracks = user_data;
2867   time_t t1, t2;
2868
2869   t1 = VIK_TRACKPOINT(VIK_TRACK(g_hash_table_lookup(tracks, a))->trackpoints->data)->timestamp;
2870   t2 = VIK_TRACKPOINT(VIK_TRACK(g_hash_table_lookup(tracks, b))->trackpoints->data)->timestamp;
2871   
2872   if (t1 < t2) return -1;
2873   if (t1 > t2) return 1;
2874   return 0;
2875 }
2876
2877 /* comparison function used to sort trackpoints */
2878 static gint trackpoint_compare(gconstpointer a, gconstpointer b)
2879 {
2880   time_t t1 = VIK_TRACKPOINT(a)->timestamp, t2 = VIK_TRACKPOINT(b)->timestamp;
2881   
2882   if (t1 < t2) return -1;
2883   if (t1 > t2) return 1;
2884   return 0;
2885 }
2886
2887 #ifdef VIK_CONFIG_ALPHABETIZED_TRW
2888 /**
2889  * comparison function which can be used to sort tracks or waypoints by name
2890  */
2891 static gint sort_alphabetically (gconstpointer a, gconstpointer b, gpointer user_data)
2892 {
2893   const gchar* namea = (const gchar*) a;
2894   const gchar* nameb = (const gchar*) b;
2895   if ( namea == NULL || nameb == NULL)
2896     return 0;
2897   else
2898     // Same sort method as used in the vik_treeview_*_alphabetize functions
2899     return strcmp ( namea, nameb );
2900 }
2901 #endif
2902
2903 static void trw_layer_merge_with_other ( gpointer pass_along[6] )
2904 {
2905   VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
2906   gchar *orig_track_name = pass_along[3];
2907   GList *tracks_with_timestamp = NULL;
2908   VikTrack *track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, orig_track_name );
2909
2910   if (track->trackpoints &&
2911       !VIK_TRACKPOINT(track->trackpoints->data)->has_timestamp) {
2912     a_dialog_error_msg(VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Failed. This track does not have timestamp"));
2913     return;
2914   }
2915
2916   if (1) {
2917
2918     twt_udata udata;
2919     udata.result = &tracks_with_timestamp;
2920     udata.exclude = track->trackpoints;
2921     g_hash_table_foreach(vtl->tracks, find_tracks_with_timestamp, (gpointer)&udata);
2922     tracks_with_timestamp = g_list_reverse(tracks_with_timestamp);
2923   }
2924
2925   if (!tracks_with_timestamp) {
2926     a_dialog_error_msg(VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Failed. No other track in this layer has timestamp"));
2927     return;
2928   }
2929
2930 #ifdef VIK_CONFIG_ALPHABETIZED_TRW
2931   // Sort alphabetically for user presentation
2932   tracks_with_timestamp = g_list_sort_with_data (tracks_with_timestamp, sort_alphabetically, NULL);
2933 #endif
2934
2935   GList *merge_list = a_dialog_select_from_list(VIK_GTK_WINDOW_FROM_LAYER(vtl),
2936       tracks_with_timestamp, TRUE,
2937       _("Merge with..."), _("Select track to merge with"));
2938   g_list_free(tracks_with_timestamp);
2939
2940   if (merge_list)
2941   {
2942     GList *l;
2943     for (l = merge_list; l != NULL; l = g_list_next(l)) {
2944       VikTrack *merge_track = (VikTrack *) g_hash_table_lookup (vtl->tracks, l->data );
2945       if (merge_track) {
2946         track->trackpoints = g_list_concat(track->trackpoints, merge_track->trackpoints);
2947         merge_track->trackpoints = NULL;
2948         vik_trw_layer_delete_track(vtl, l->data);
2949         track->trackpoints = g_list_sort(track->trackpoints, trackpoint_compare);
2950       }
2951     }
2952     /* TODO: free data before free merge_list */
2953     for (l = merge_list; l != NULL; l = g_list_next(l))
2954       g_free(l->data);
2955     g_list_free(merge_list);
2956     vik_layer_emit_update( VIK_LAYER(vtl) );
2957   }
2958 }
2959
2960 /* merge by time routine */
2961 static void trw_layer_merge_by_timestamp ( gpointer pass_along[6] )
2962 {
2963   VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
2964   gchar *orig_track_name = strdup(pass_along[3]);
2965
2966   time_t t1, t2;
2967   GList *nearby_tracks;
2968   VikTrack *track;
2969   GList *trps;
2970   static  guint thr = 1;
2971   guint track_count = 0;
2972
2973   if (!a_dialog_time_threshold(VIK_GTK_WINDOW_FROM_LAYER(vtl), 
2974                                _("Merge Threshold..."), 
2975                                _("Merge when time between tracks less than:"), 
2976                                &thr)) {
2977     free(orig_track_name);
2978     return;
2979   }
2980
2981   /* merge tracks until we can't */
2982   nearby_tracks = NULL;
2983   do {
2984     gpointer params[3];
2985
2986     track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, orig_track_name );
2987     trps = track->trackpoints;
2988     if ( !trps )
2989       return;
2990
2991
2992     if (nearby_tracks) {
2993       g_list_free(nearby_tracks);
2994       nearby_tracks = NULL;
2995     }
2996
2997     t1 = ((VikTrackpoint *)trps->data)->timestamp;
2998     t2 = ((VikTrackpoint *)g_list_last(trps)->data)->timestamp;
2999     
3000     /*    g_print("Original track times: %d and %d\n", t1, t2);  */
3001     params[0] = &nearby_tracks;
3002     params[1] = trps;
3003     params[2] = GUINT_TO_POINTER (thr);
3004
3005     /* get a list of adjacent-in-time tracks */
3006     g_hash_table_foreach(vtl->tracks, find_nearby_track, (gpointer)params);
3007
3008     /* add original track */
3009     nearby_tracks = g_list_prepend(nearby_tracks, orig_track_name);
3010
3011     /* merge them */
3012     { 
3013 #define get_track(x) VIK_TRACK(g_hash_table_lookup(vtl->tracks, (gchar *)((x)->data)))
3014 #define get_first_trackpoint(x) VIK_TRACKPOINT(get_track(x)->trackpoints->data)
3015 #define get_last_trackpoint(x) VIK_TRACKPOINT(g_list_last(get_track(x)->trackpoints)->data)
3016       GList *l = nearby_tracks;
3017       VikTrack *tr = vik_track_new();
3018       tr->visible = track->visible;
3019       track_count = 0;
3020       while (l) {
3021         /*
3022         time_t t1, t2;
3023         t1 = get_first_trackpoint(l)->timestamp;
3024         t2 = get_last_trackpoint(l)->timestamp;
3025         g_print("     %20s: track %d - %d\n", (char *)l->data, (int)t1, (int)t2);
3026         */
3027
3028
3029         /* remove trackpoints from merged track, delete track */
3030         tr->trackpoints = g_list_concat(tr->trackpoints, get_track(l)->trackpoints);
3031         get_track(l)->trackpoints = NULL;
3032         vik_trw_layer_delete_track(vtl, l->data);
3033
3034         track_count ++;
3035         l = g_list_next(l);
3036       }
3037       tr->trackpoints = g_list_sort(tr->trackpoints, trackpoint_compare);
3038       vik_trw_layer_add_track(vtl, strdup(orig_track_name), tr);
3039
3040 #undef get_first_trackpoint
3041 #undef get_last_trackpoint
3042 #undef get_track
3043     }
3044   } while (track_count > 1);
3045   g_list_free(nearby_tracks);
3046   free(orig_track_name);
3047   vik_layer_emit_update( VIK_LAYER(vtl) );
3048 }
3049
3050 /* split by time routine */
3051 static void trw_layer_split_by_timestamp ( gpointer pass_along[6] )
3052 {
3053   VikTrack *track = (VikTrack *) g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, pass_along[3] );
3054   GList *trps = track->trackpoints;
3055   GList *iter;
3056   GList *newlists = NULL;
3057   GList *newtps = NULL;
3058   guint i;
3059   static guint thr = 1;
3060
3061   time_t ts, prev_ts;
3062
3063   if ( !trps )
3064     return;
3065
3066   if (!a_dialog_time_threshold(VIK_GTK_WINDOW_FROM_LAYER(pass_along[0]), 
3067                                _("Split Threshold..."), 
3068                                _("Split when time between trackpoints exceeds:"), 
3069                                &thr)) {
3070     return;
3071   }
3072
3073   /* iterate through trackpoints, and copy them into new lists without touching original list */
3074   prev_ts = VIK_TRACKPOINT(trps->data)->timestamp;
3075   iter = trps;
3076
3077   while (iter) {
3078     ts = VIK_TRACKPOINT(iter->data)->timestamp;
3079     if (ts < prev_ts) {
3080       g_print("panic: ts < prev_ts: this should never happen!\n");
3081       return;
3082     }
3083     if (ts - prev_ts > thr*60) {
3084       /* flush accumulated trackpoints into new list */
3085       newlists = g_list_prepend(newlists, g_list_reverse(newtps));
3086       newtps = NULL;
3087     }
3088
3089     /* accumulate trackpoint copies in newtps, in reverse order */
3090     newtps = g_list_prepend(newtps, vik_trackpoint_copy(VIK_TRACKPOINT(iter->data)));
3091     prev_ts = ts;
3092     iter = g_list_next(iter);
3093   }
3094   if (newtps) {
3095       newlists = g_list_prepend(newlists, g_list_reverse(newtps));
3096   }
3097
3098   /* put lists of trackpoints into tracks */
3099   iter = newlists;
3100   i = 1;
3101   while (iter) {
3102     gchar *new_tr_name;
3103     VikTrack *tr;
3104
3105     tr = vik_track_new();
3106     tr->visible = track->visible;
3107     tr->trackpoints = (GList *)(iter->data);
3108
3109     new_tr_name = g_strdup_printf("%s #%d", (gchar *) pass_along[3], i++);
3110     vik_trw_layer_add_track(VIK_TRW_LAYER(pass_along[0]), new_tr_name, tr);
3111     /*    g_print("adding track %s, times %d - %d\n", new_tr_name, VIK_TRACKPOINT(tr->trackpoints->data)->timestamp,
3112           VIK_TRACKPOINT(g_list_last(tr->trackpoints)->data)->timestamp);*/
3113
3114     iter = g_list_next(iter);
3115   }
3116   g_list_free(newlists);
3117   vik_trw_layer_delete_track(VIK_TRW_LAYER(pass_along[0]), (gchar *)pass_along[3]);
3118   vik_layer_emit_update(VIK_LAYER(pass_along[0]));
3119 }
3120
3121 /**
3122  * Split a track by the number of points as specified by the user
3123  */
3124 static void trw_layer_split_by_n_points ( gpointer pass_along[6] )
3125 {
3126   VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
3127   VikTrack *track = (VikTrack *)g_hash_table_lookup ( vtl->tracks, pass_along[3] );
3128
3129   // Check valid track
3130   GList *trps = track->trackpoints;
3131   if ( !trps )
3132     return;
3133
3134   gint points = a_dialog_get_positive_number(VIK_GTK_WINDOW_FROM_LAYER(pass_along[0]),
3135                                              _("Split Every Nth Point"),
3136                                              _("Split on every Nth point:"),
3137                                              250,   // Default value as per typical limited track capacity of various GPS devices
3138                                              2,     // Min
3139                                              65536, // Max
3140                                              5);    // Step
3141   // Was a valid number returned?
3142   if (!points)
3143     return;
3144
3145   // Now split...
3146   GList *iter;
3147   GList *newlists = NULL;
3148   GList *newtps = NULL;
3149   gint count = 0;
3150   iter = trps;
3151
3152   while (iter) {
3153     /* accumulate trackpoint copies in newtps, in reverse order */
3154     newtps = g_list_prepend(newtps, vik_trackpoint_copy(VIK_TRACKPOINT(iter->data)));
3155     count++;
3156     if (count >= points) {
3157       /* flush accumulated trackpoints into new list */
3158       newlists = g_list_append(newlists, g_list_reverse(newtps));
3159       newtps = NULL;
3160       count = 0;
3161     }
3162     iter = g_list_next(iter);
3163   }
3164
3165   // If there is a remaining chunk put that into the new split list
3166   // This may well be the whole track if no split points were encountered
3167   if (newtps) {
3168       newlists = g_list_append(newlists, g_list_reverse(newtps));
3169   }
3170
3171   /* put lists of trackpoints into tracks */
3172   iter = newlists;
3173   guint i = 1;
3174   // Only bother updating if the split results in new tracks
3175   if (g_list_length (newlists) > 1) {
3176     while (iter) {
3177       gchar *new_tr_name;
3178       VikTrack *tr;
3179
3180       tr = vik_track_new();
3181       tr->visible = track->visible;
3182       tr->trackpoints = (GList *)(iter->data);
3183
3184       new_tr_name = g_strdup_printf("%s #%d", (gchar *) pass_along[3], i++);
3185       vik_trw_layer_add_track(VIK_TRW_LAYER(pass_along[0]), new_tr_name, tr);
3186
3187       iter = g_list_next(iter);
3188     }
3189     // Remove original track and then update the display
3190     vik_trw_layer_delete_track(VIK_TRW_LAYER(pass_along[0]), (gchar *)pass_along[3]);
3191     vik_layer_emit_update(VIK_LAYER(pass_along[0]));
3192   }
3193   g_list_free(newlists);
3194 }
3195
3196 /* end of split/merge routines */
3197
3198
3199 static void trw_layer_goto_waypoint ( gpointer pass_along[6] )
3200 {
3201   VikWaypoint *wp = g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->waypoints, pass_along[3] );
3202   if ( wp )
3203     goto_coord ( pass_along[1], pass_along[0], pass_along[5], &(wp->coord) );
3204 }
3205
3206 static void trw_layer_waypoint_gc_webpage ( gpointer pass_along[6] )
3207 {
3208   gchar *webpage = g_strdup_printf("http://www.geocaching.com/seek/cache_details.aspx?wp=%s", (gchar *) pass_along[3] );
3209   open_url(VIK_GTK_WINDOW_FROM_LAYER(VIK_LAYER(pass_along[0])), webpage);
3210   g_free ( webpage );
3211 }
3212
3213 const gchar *vik_trw_layer_sublayer_rename_request ( VikTrwLayer *l, const gchar *newname, gpointer vlp, gint subtype, gpointer sublayer, GtkTreeIter *iter )
3214 {
3215   if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT )
3216   {
3217     gchar *rv;
3218     VikWaypoint *wp;
3219
3220     if (strcmp(newname, sublayer) == 0 )
3221       return NULL;
3222
3223     if (strcasecmp(newname, sublayer)) { /* Not just changing case */
3224       if (g_hash_table_lookup( l->waypoints, newname))
3225       {
3226         a_dialog_error_msg ( VIK_GTK_WINDOW_FROM_LAYER(l), _("Waypoint Already Exists") );
3227         return NULL;
3228       }
3229     }
3230
3231     iter = g_hash_table_lookup ( l->waypoints_iters, sublayer );
3232     g_hash_table_steal ( l->waypoints_iters, sublayer );
3233
3234     wp = vik_waypoint_copy ( VIK_WAYPOINT(g_hash_table_lookup ( l->waypoints, sublayer )) );
3235     highest_wp_number_remove_wp(l, sublayer);
3236     g_hash_table_remove ( l->waypoints, sublayer );
3237
3238     rv = g_strdup(newname);
3239
3240     vik_treeview_item_set_pointer ( VIK_LAYER(l)->vt, iter, rv );
3241
3242     highest_wp_number_add_wp(l, rv);
3243     g_hash_table_insert ( l->waypoints, rv, wp );
3244     g_hash_table_insert ( l->waypoints_iters, rv, iter );
3245
3246     /* it hasn't been updated yet so we pass new name */
3247 #ifdef VIK_CONFIG_ALPHABETIZED_TRW
3248     vik_treeview_sublayer_realphabetize ( VIK_LAYER(l)->vt, iter, rv );
3249 #endif
3250
3251     vik_layers_panel_emit_update ( VIK_LAYERS_PANEL(vlp) );
3252     return rv;
3253   }
3254   if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
3255   {
3256     gchar *rv;
3257     VikTrack *tr;
3258     GtkTreeIter *iter;
3259     gchar *orig_key;
3260
3261     if (strcmp(newname, sublayer) == 0)
3262       return NULL;
3263
3264     if (strcasecmp(newname, sublayer)) { /* Not just changing case */
3265       if (g_hash_table_lookup( l->tracks, newname))
3266       {
3267         a_dialog_error_msg ( VIK_GTK_WINDOW_FROM_LAYER(l), _("Track Already Exists") );
3268         return NULL;
3269       }
3270     }
3271
3272     g_hash_table_lookup_extended ( l->tracks, sublayer, (void *)&orig_key, (void *)&tr );
3273     g_hash_table_steal ( l->tracks, sublayer );
3274
3275     iter = g_hash_table_lookup ( l->tracks_iters, sublayer );
3276     g_hash_table_steal ( l->tracks_iters, sublayer );
3277
3278     rv = g_strdup(newname);
3279
3280     vik_treeview_item_set_pointer ( VIK_LAYER(l)->vt, iter, rv );
3281
3282     g_hash_table_insert ( l->tracks, rv, tr );
3283     g_hash_table_insert ( l->tracks_iters, rv, iter );
3284
3285     /* don't forget about current_tp_track_name, update that too */
3286     if ( l->current_tp_track_name && g_strcasecmp(orig_key,l->current_tp_track_name) == 0 )
3287     {
3288       l->current_tp_track_name = rv;
3289       if ( l->tpwin )
3290         vik_trw_layer_tpwin_set_track_name ( l->tpwin, rv );
3291     }
3292     else if ( l->last_tp_track_name && g_strcasecmp(orig_key,l->last_tp_track_name) == 0 )
3293       l->last_tp_track_name = rv;
3294
3295     g_free ( orig_key );
3296
3297 #ifdef VIK_CONFIG_ALPHABETIZED_TRW
3298     vik_treeview_sublayer_realphabetize ( VIK_LAYER(l)->vt, iter, rv );
3299 #endif
3300
3301     vik_layers_panel_emit_update ( VIK_LAYERS_PANEL(vlp) );
3302     return rv;
3303   }
3304   return NULL;
3305 }
3306
3307 static gboolean is_valid_geocache_name ( gchar *str )
3308 {
3309   gint len = strlen ( str );
3310   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]));
3311 }
3312
3313 static void trw_layer_track_use_with_filter ( gpointer pass_along[6] )
3314 {
3315   gchar *track_name = (gchar *) pass_along[3];
3316   VikTrack *tr = g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, track_name );
3317   a_acquire_set_filter_track ( tr, track_name );
3318 }
3319
3320 static gboolean is_valid_google_route ( VikTrwLayer *vtl, const gchar *track_name )
3321 {
3322   VikTrack *tr = g_hash_table_lookup ( vtl->tracks, track_name );
3323   return ( tr && tr->comment && strlen(tr->comment) > 7 && !strncmp(tr->comment, "from:", 5) );
3324 }
3325
3326 static void trw_layer_track_google_route_webpage ( gpointer pass_along[6] )
3327 {
3328   gchar *track_name = (gchar *) pass_along[3];
3329   VikTrack *tr = g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, track_name );
3330   if ( tr ) {
3331     gchar *escaped = uri_escape ( tr->comment );
3332     gchar *webpage = g_strdup_printf("http://maps.google.com/maps?f=q&hl=en&q=%s", escaped );
3333     open_url(VIK_GTK_WINDOW_FROM_LAYER(VIK_LAYER(pass_along[0])), webpage);
3334     g_free ( escaped );
3335     g_free ( webpage );
3336   }
3337 }
3338
3339 /* vlp can be NULL if necessary - i.e. right-click from a tool */
3340 /* viewpoint is now available instead */
3341 gboolean vik_trw_layer_sublayer_add_menu_items ( VikTrwLayer *l, GtkMenu *menu, gpointer vlp, gint subtype, gpointer sublayer, GtkTreeIter *iter, VikViewport *vvp )
3342 {
3343   static GtkTreeIter staticiter;
3344   static gpointer pass_along[6];
3345   GtkWidget *item;
3346   gboolean rv = FALSE;
3347
3348   pass_along[0] = l;
3349   pass_along[1] = vlp;
3350   pass_along[2] = GINT_TO_POINTER (subtype);
3351   pass_along[3] = sublayer;
3352   if ( iter ) {
3353     staticiter = *iter; /* will exist after function has ended */
3354     pass_along[4] = &staticiter;
3355   }
3356   pass_along[5] = vvp;
3357
3358   if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT || subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
3359   {
3360     rv = TRUE;
3361
3362     item = gtk_image_menu_item_new_from_stock ( GTK_STOCK_PROPERTIES, NULL );
3363     g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_properties_item), pass_along );
3364     gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
3365     gtk_widget_show ( item );
3366
3367     if (subtype == VIK_TRW_LAYER_SUBLAYER_TRACK) {
3368       VikTrwLayer *vtl = l;
3369       VikTrack *tr = g_hash_table_lookup ( vtl->tracks, sublayer );
3370       if (tr && tr->property_dialog)
3371         gtk_widget_set_sensitive(GTK_WIDGET(item), FALSE );
3372     }
3373
3374     item = gtk_image_menu_item_new_from_stock ( GTK_STOCK_CUT, NULL );
3375     g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_cut_item_cb), pass_along );
3376     gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
3377     gtk_widget_show ( item );
3378
3379     item = gtk_image_menu_item_new_from_stock ( GTK_STOCK_COPY, NULL );
3380     g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_copy_item_cb), pass_along );
3381     gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
3382     gtk_widget_show ( item );
3383
3384     item = gtk_image_menu_item_new_from_stock ( GTK_STOCK_DELETE, NULL );
3385     g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_item), pass_along );
3386     gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
3387     gtk_widget_show ( item );
3388
3389     if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT )
3390     {
3391       gboolean separator_created = FALSE;
3392
3393       /* could be a right-click using the tool */
3394       if ( vlp != NULL ) {
3395         item = gtk_menu_item_new ();
3396         gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
3397         gtk_widget_show ( item );
3398
3399         separator_created = TRUE;
3400
3401         item = gtk_menu_item_new_with_mnemonic ( _("_Goto") );
3402         g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_waypoint), pass_along );
3403         gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
3404         gtk_widget_show ( item );
3405       }
3406
3407       if ( is_valid_geocache_name ( (gchar *) sublayer ) )
3408       {
3409         if ( !separator_created ) {
3410           item = gtk_menu_item_new ();
3411           gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
3412           gtk_widget_show ( item );
3413         }
3414
3415         item = gtk_menu_item_new_with_mnemonic ( _("_Visit Geocache Webpage") );
3416         g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_waypoint_gc_webpage), pass_along );
3417         gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
3418         gtk_widget_show ( item );
3419       }
3420
3421     }
3422   }
3423
3424   if ( vlp && (subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINTS || subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT) )
3425   {
3426     rv = TRUE;
3427     item = gtk_menu_item_new_with_mnemonic ( _("_New Waypoint") );
3428     g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_wp), pass_along );
3429     gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3430     gtk_widget_show ( item );
3431   }
3432
3433   if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
3434   {
3435     GtkWidget *goto_submenu;
3436     item = gtk_menu_item_new ();
3437     gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
3438     gtk_widget_show ( item );
3439
3440     goto_submenu = gtk_menu_new ();
3441     item = gtk_menu_item_new_with_mnemonic ( _("_Goto") );
3442     gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3443     gtk_widget_show ( item );
3444     gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), goto_submenu );
3445
3446     item = gtk_menu_item_new_with_mnemonic ( _("_Startpoint") );
3447     g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_startpoint), pass_along );
3448     gtk_menu_shell_append ( GTK_MENU_SHELL(goto_submenu), item );
3449     gtk_widget_show ( item );
3450
3451     item = gtk_menu_item_new_with_mnemonic ( _("\"_Center\"") );
3452     g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_center), pass_along );
3453     gtk_menu_shell_append ( GTK_MENU_SHELL(goto_submenu), item );
3454     gtk_widget_show ( item );
3455
3456     item = gtk_menu_item_new_with_mnemonic ( _("_Endpoint") );
3457     g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_endpoint), pass_along );
3458     gtk_menu_shell_append ( GTK_MENU_SHELL(goto_submenu), item );
3459     gtk_widget_show ( item );
3460
3461     item = gtk_menu_item_new_with_mnemonic ( _("_Highest Altitude") );
3462     g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_max_alt), pass_along );
3463     gtk_menu_shell_append ( GTK_MENU_SHELL(goto_submenu), item );
3464     gtk_widget_show ( item );
3465
3466     item = gtk_menu_item_new_with_mnemonic ( _("_Lowest Altitude") );
3467     g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_min_alt), pass_along );
3468     gtk_menu_shell_append ( GTK_MENU_SHELL(goto_submenu), item );
3469     gtk_widget_show ( item );
3470
3471     item = gtk_menu_item_new_with_mnemonic ( _("_Maximum Speed") );
3472     g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_max_speed), pass_along );
3473     gtk_menu_shell_append ( GTK_MENU_SHELL(goto_submenu), item );
3474     gtk_widget_show ( item );
3475
3476     item = gtk_menu_item_new_with_mnemonic ( _("_View Track") );
3477     g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_track_view), pass_along );
3478     gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
3479     gtk_widget_show ( item );
3480
3481     item = gtk_menu_item_new_with_mnemonic ( _("_Merge By Time") );
3482     g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_merge_by_timestamp), pass_along );
3483     gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
3484     gtk_widget_show ( item );
3485
3486     item = gtk_menu_item_new_with_mnemonic ( _("Merge _With Other Tracks...") );
3487     g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_merge_with_other), pass_along );
3488     gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
3489     gtk_widget_show ( item );
3490
3491     item = gtk_menu_item_new_with_mnemonic ( _("_Split By Time") );
3492     g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_split_by_timestamp), pass_along );
3493     gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
3494     gtk_widget_show ( item );
3495
3496     item = gtk_menu_item_new_with_mnemonic ( _("Split By _Number of Points") );
3497     g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_split_by_n_points), pass_along );
3498     gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
3499     gtk_widget_show ( item );
3500
3501     /* ATM This function is only available via the layers panel, due to the method in finding out the maps in use */
3502     if ( vlp ) {
3503       item = gtk_menu_item_new_with_mnemonic ( _("Down_load Maps Along Track...") );
3504       g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_download_map_along_track_cb), pass_along );
3505       gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
3506       gtk_widget_show ( item );
3507     }
3508
3509     item = gtk_menu_item_new_with_mnemonic ( _("_Apply DEM Data") );
3510     g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_apply_dem_data), pass_along );
3511     gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
3512     gtk_widget_show ( item );
3513
3514     item = gtk_menu_item_new_with_mnemonic ( _("Export Trac_k as GPX") );
3515     g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_gpx_track), pass_along );
3516     gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
3517     gtk_widget_show ( item );
3518
3519     item = gtk_menu_item_new_with_mnemonic ( _("E_xtend Track End") );
3520     g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_extend_track_end), pass_along );
3521     gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
3522     gtk_widget_show ( item );
3523
3524     item = gtk_menu_item_new_with_mnemonic ( _("Extend _Using Magic Scissors") );
3525     g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_extend_track_end_ms), pass_along );
3526     gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
3527     gtk_widget_show ( item );
3528
3529 #ifdef VIK_CONFIG_OPENSTREETMAP
3530     item = gtk_menu_item_new_with_mnemonic ( _("Upload to _OSM") );
3531     g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(osm_traces_upload_track_cb), pass_along );
3532     gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
3533     gtk_widget_show ( item );
3534 #endif
3535
3536     if ( is_valid_google_route ( l, (gchar *) sublayer ) )
3537     {
3538       item = gtk_menu_item_new_with_mnemonic ( _("_View Google Directions") );
3539       g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_track_google_route_webpage), pass_along );
3540       gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
3541       gtk_widget_show ( item );
3542     }
3543
3544     item = gtk_menu_item_new_with_mnemonic ( _("Use with _Filter") );
3545     g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_track_use_with_filter), pass_along );
3546     gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
3547     gtk_widget_show ( item );
3548
3549     /* ATM This function is only available via the layers panel, due to needing a vlp */
3550     if ( vlp ) {
3551       item = a_acquire_track_menu ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(l)), vlp,
3552                                     vik_layers_panel_get_viewport(VIK_LAYERS_PANEL(vlp)),
3553                                     g_hash_table_lookup ( l->tracks, (gchar *) sublayer ) );
3554       if ( item ) {
3555         gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3556         gtk_widget_show ( item );
3557       }
3558     }
3559   }
3560
3561   return rv;
3562 }
3563
3564 static void trw_layer_insert_tp_after_current_tp ( VikTrwLayer *vtl )
3565 {
3566   /* sanity checks */
3567   if (!vtl->current_tpl)
3568     return;
3569   if (!vtl->current_tpl->next)
3570     return;
3571
3572   VikTrackpoint *tp_current = VIK_TRACKPOINT(vtl->current_tpl->data);
3573   VikTrackpoint *tp_next = VIK_TRACKPOINT(vtl->current_tpl->next->data);
3574
3575   /* Use current and next trackpoints to form a new track point which is inserted into the tracklist */
3576   if ( tp_next ) {
3577
3578     VikTrackpoint *tp_new = vik_trackpoint_new();
3579     struct LatLon ll_current, ll_next;
3580     vik_coord_to_latlon ( &tp_current->coord, &ll_current );
3581     vik_coord_to_latlon ( &tp_next->coord, &ll_next );
3582
3583     /* main positional interpolation */
3584     struct LatLon ll_new = { (ll_current.lat+ll_next.lat)/2, (ll_current.lon+ll_next.lon)/2 };
3585     vik_coord_load_from_latlon ( &(tp_new->coord), vtl->coord_mode, &ll_new );
3586
3587     /* Now other properties that can be interpolated */
3588     tp_new->altitude = (tp_current->altitude + tp_next->altitude) / 2;
3589
3590     if (tp_current->has_timestamp && tp_next->has_timestamp) {
3591       /* Note here the division is applied to each part, then added
3592          This is to avoid potential overflow issues with a 32 time_t for dates after midpoint of this Unix time on 2004/01/04 */
3593       tp_new->timestamp = (tp_current->timestamp/2) + (tp_next->timestamp/2);
3594       tp_new->has_timestamp = TRUE;
3595     }
3596
3597     if (tp_current->speed != NAN && tp_next->speed != NAN)
3598       tp_new->speed = (tp_current->speed + tp_next->speed)/2;
3599
3600     /* TODO - improve interpolation of course, as it may not be correct.
3601        if courses in degrees are 350 + 020, the mid course more likely to be 005 (not 185)
3602        [similar applies if value is in radians] */
3603     if (tp_current->course != NAN && tp_next->course != NAN)
3604       tp_new->speed = (tp_current->course + tp_next->course)/2;
3605
3606     /* DOP / sat values remain at defaults as not they do not seem applicable to a dreamt up point */
3607
3608     /* Insert new point into the trackpoints list after the current TP */
3609     VikTrack *tr = g_hash_table_lookup ( vtl->tracks, vtl->current_tp_track_name );
3610     gint index =  g_list_index ( tr->trackpoints, tp_current );
3611     if ( index > -1 ) {
3612       tr->trackpoints = g_list_insert (tr->trackpoints, tp_new, index+1 );
3613     }
3614   }
3615 }
3616
3617 /* to be called when last_tpl no long exists. */
3618 static void trw_layer_cancel_last_tp ( VikTrwLayer *vtl )
3619 {
3620   if ( vtl->tpwin ) /* can't join with a non-existant TP. */
3621     vik_trw_layer_tpwin_disable_join ( vtl->tpwin );
3622   vtl->last_tpl = NULL;
3623   vtl->last_tp_track_name = NULL;
3624 }
3625
3626 static void trw_layer_cancel_current_tp ( VikTrwLayer *vtl, gboolean destroy )
3627 {
3628   if ( vtl->tpwin )
3629   {
3630     if ( destroy)
3631     {
3632       gtk_widget_destroy ( GTK_WIDGET(vtl->tpwin) );
3633       vtl->tpwin = NULL;
3634     }
3635     else
3636       vik_trw_layer_tpwin_set_empty ( vtl->tpwin );
3637   }
3638   if ( vtl->current_tpl )
3639   {
3640     vtl->current_tpl = NULL;
3641     vtl->current_tp_track_name = NULL;
3642     vik_layer_emit_update(VIK_LAYER(vtl));
3643   }
3644 }
3645
3646 static void trw_layer_tpwin_response ( VikTrwLayer *vtl, gint response )
3647 {
3648   g_assert ( vtl->tpwin != NULL );
3649   if ( response == VIK_TRW_LAYER_TPWIN_CLOSE )
3650     trw_layer_cancel_current_tp ( vtl, TRUE );
3651   else if ( response == VIK_TRW_LAYER_TPWIN_SPLIT && vtl->current_tpl->next && vtl->current_tpl->prev )
3652   {
3653     gchar *name = get_new_unique_sublayer_name(vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, vtl->current_tp_track_name);
3654     if ( ( name = a_dialog_new_track ( GTK_WINDOW(vtl->tpwin), vtl->tracks, name ) ) )
3655     {
3656       VikTrack *tr = vik_track_new ();
3657       GList *newglist = g_list_alloc ();
3658       newglist->prev = NULL;
3659       newglist->next = vtl->current_tpl->next;
3660       newglist->data = vik_trackpoint_copy(VIK_TRACKPOINT(vtl->current_tpl->data));
3661       tr->trackpoints = newglist;
3662
3663       vtl->current_tpl->next->prev = newglist; /* end old track here */
3664       vtl->current_tpl->next = NULL;
3665
3666       vtl->current_tpl = newglist; /* change tp to first of new track. */
3667       vtl->current_tp_track_name = name;
3668
3669       vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, vtl->current_tp_track_name );
3670
3671       tr->visible = TRUE;
3672
3673       vik_trw_layer_add_track ( vtl, name, tr );
3674       vik_layer_emit_update(VIK_LAYER(vtl));
3675     }
3676   }
3677   else if ( response == VIK_TRW_LAYER_TPWIN_DELETE )
3678   {
3679     VikTrack *tr = g_hash_table_lookup ( vtl->tracks, vtl->current_tp_track_name );
3680     GList *new_tpl;
3681     g_assert(tr != NULL);
3682
3683     /* can't join with a non-existent trackpoint */
3684     vtl->last_tpl = NULL;
3685     vtl->last_tp_track_name = NULL;
3686
3687     if ( (new_tpl = vtl->current_tpl->next) || (new_tpl = vtl->current_tpl->prev) )
3688     {
3689       if ( VIK_TRACKPOINT(vtl->current_tpl->data)->newsegment && vtl->current_tpl->next )
3690         VIK_TRACKPOINT(vtl->current_tpl->next->data)->newsegment = TRUE; /* don't concat segments on del */
3691
3692       tr->trackpoints = g_list_remove_link ( tr->trackpoints, vtl->current_tpl ); /* this nulls current_tpl->prev and next */
3693
3694       /* at this point the old trackpoint exists, but the list links are correct (new), so it is safe to do this. */
3695       vik_trw_layer_tpwin_set_tp ( vtl->tpwin, new_tpl, vtl->current_tp_track_name );
3696
3697       trw_layer_cancel_last_tp ( vtl );
3698
3699       g_free ( vtl->current_tpl->data ); /* TODO: vik_trackpoint_free() */
3700       g_list_free_1 ( vtl->current_tpl );
3701       vtl->current_tpl = new_tpl;
3702       vik_layer_emit_update(VIK_LAYER(vtl));
3703     }
3704     else
3705     {
3706       tr->trackpoints = g_list_remove_link ( tr->trackpoints, vtl->current_tpl );
3707       g_free ( vtl->current_tpl->data ); /* TODO longone: vik_trackpoint_new() and vik_trackpoint_free() */
3708       g_list_free_1 ( vtl->current_tpl );
3709       trw_layer_cancel_current_tp ( vtl, FALSE );
3710     }
3711   }
3712   else if ( response == VIK_TRW_LAYER_TPWIN_FORWARD && vtl->current_tpl->next )
3713   {
3714     vtl->last_tpl = vtl->current_tpl;
3715     vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl = vtl->current_tpl->next, vtl->current_tp_track_name );
3716     vik_layer_emit_update(VIK_LAYER(vtl)); /* TODO longone: either move or only update if tp is inside drawing window */
3717   }
3718   else if ( response == VIK_TRW_LAYER_TPWIN_BACK && vtl->current_tpl->prev )
3719   {
3720     vtl->last_tpl = vtl->current_tpl;
3721     vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl = vtl->current_tpl->prev, vtl->current_tp_track_name );
3722     vik_layer_emit_update(VIK_LAYER(vtl));
3723   }
3724   else if ( response == VIK_TRW_LAYER_TPWIN_JOIN )
3725   {
3726     VikTrack *tr1 = g_hash_table_lookup ( vtl->tracks, vtl->last_tp_track_name );
3727     VikTrack *tr2 = g_hash_table_lookup ( vtl->tracks, vtl->current_tp_track_name );
3728
3729     VikTrack *tr_first = tr1, *tr_last = tr2;
3730
3731     gchar *tmp;
3732
3733     if ( (!vtl->last_tpl->next) && (!vtl->current_tpl->next) ) /* both endpoints */
3734       vik_track_reverse ( tr2 ); /* reverse the second, that way second track clicked will be later. */
3735     else if ( (!vtl->last_tpl->prev) && (!vtl->current_tpl->prev) )
3736       vik_track_reverse ( tr1 );
3737     else if ( (!vtl->last_tpl->prev) && (!vtl->current_tpl->next) ) /* clicked startpoint, then endpoint -- concat end to start */
3738     {
3739       tr_first = tr2;
3740       tr_last = tr1;
3741     }
3742     /* default -- clicked endpoint then startpoint -- connect endpoint to startpoint */
3743
3744     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. */
3745       VIK_TRACKPOINT(tr_last->trackpoints->data)->newsegment = FALSE;
3746     tr1->trackpoints = g_list_concat ( tr_first->trackpoints, tr_last->trackpoints );
3747     tr2->trackpoints = NULL;
3748
3749     tmp = vtl->current_tp_track_name;
3750
3751     vtl->current_tp_track_name = vtl->last_tp_track_name; /* current_tp stays the same (believe it or not!) */
3752     vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, vtl->current_tp_track_name );
3753
3754     /* if we did this before, trw_layer_delete_track would have canceled the current tp because
3755      * it was the current track. canceling the current tp would have set vtl->current_tpl to NULL */
3756     vik_trw_layer_delete_track ( vtl, tmp );
3757
3758     trw_layer_cancel_last_tp ( vtl ); /* same TP, can't join. */
3759     vik_layer_emit_update(VIK_LAYER(vtl));
3760   }
3761   else if ( response == VIK_TRW_LAYER_TPWIN_INSERT && vtl->current_tpl->next )
3762   {
3763     trw_layer_insert_tp_after_current_tp ( vtl );
3764     vik_layer_emit_update(VIK_LAYER(vtl));
3765   }
3766   else if ( response == VIK_TRW_LAYER_TPWIN_DATA_CHANGED )
3767     vik_layer_emit_update (VIK_LAYER(vtl));
3768 }
3769
3770 static void trw_layer_tpwin_init ( VikTrwLayer *vtl )
3771 {
3772   if ( ! vtl->tpwin )
3773   {
3774     vtl->tpwin = vik_trw_layer_tpwin_new ( VIK_GTK_WINDOW_FROM_LAYER(vtl) );
3775     g_signal_connect_swapped ( GTK_DIALOG(vtl->tpwin), "response", G_CALLBACK(trw_layer_tpwin_response), vtl );
3776     /* connect signals -- DELETE SIGNAL VERY IMPORTANT TO SET TO NULL */
3777     g_signal_connect_swapped ( vtl->tpwin, "delete-event", G_CALLBACK(trw_layer_cancel_current_tp), vtl );
3778     gtk_widget_show_all ( GTK_WIDGET(vtl->tpwin) );
3779   }
3780   if ( vtl->current_tpl )
3781     vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, vtl->current_tp_track_name );
3782   /* set layer name and TP data */
3783 }
3784
3785 /***************************************************************************
3786  ** Tool code
3787  ***************************************************************************/
3788
3789 /*** Utility data structures and functions ****/
3790
3791 typedef struct {
3792   gint x, y;
3793   gint closest_x, closest_y;
3794   gchar *closest_wp_name;
3795   VikWaypoint *closest_wp;
3796   VikViewport *vvp;
3797 } WPSearchParams;
3798
3799 typedef struct {
3800   gint x, y;
3801   gint closest_x, closest_y;
3802   gchar *closest_track_name;
3803   VikTrackpoint *closest_tp;
3804   VikViewport *vvp;
3805   GList *closest_tpl;
3806 } TPSearchParams;
3807
3808 static void waypoint_search_closest_tp ( gchar *name, VikWaypoint *wp, WPSearchParams *params )
3809 {
3810   gint x, y;
3811   if ( !wp->visible )
3812     return;
3813
3814   vik_viewport_coord_to_screen ( params->vvp, &(wp->coord), &x, &y );
3815  
3816   if ( abs (x - params->x) <= WAYPOINT_SIZE_APPROX && abs (y - params->y) <= WAYPOINT_SIZE_APPROX &&
3817       ((!params->closest_wp) ||        /* was the old waypoint we already found closer than this one? */
3818         abs(x - params->x)+abs(y - params->y) < abs(x - params->closest_x)+abs(y - params->closest_y)))
3819   {
3820     params->closest_wp_name = name;
3821     params->closest_wp = wp;
3822     params->closest_x = x;
3823     params->closest_y = y;
3824   }
3825 }
3826
3827 static void track_search_closest_tp ( gchar *name, VikTrack *t, TPSearchParams *params )
3828 {
3829   GList *tpl = t->trackpoints;
3830   VikTrackpoint *tp;
3831
3832   if ( !t->visible )
3833     return;
3834
3835   while (tpl)
3836   {
3837     gint x, y;
3838     tp = VIK_TRACKPOINT(tpl->data);
3839
3840     vik_viewport_coord_to_screen ( params->vvp, &(tp->coord), &x, &y );
3841  
3842     if ( abs (x - params->x) <= TRACKPOINT_SIZE_APPROX && abs (y - params->y) <= TRACKPOINT_SIZE_APPROX &&
3843         ((!params->closest_tp) ||        /* was the old trackpoint we already found closer than this one? */
3844           abs(x - params->x)+abs(y - params->y) < abs(x - params->closest_x)+abs(y - params->closest_y)))
3845     {
3846       params->closest_track_name = name;
3847       params->closest_tp = tp;
3848       params->closest_tpl = tpl;
3849       params->closest_x = x;
3850       params->closest_y = y;
3851     }
3852     tpl = tpl->next;
3853   }
3854 }
3855
3856 static VikTrackpoint *closest_tp_in_five_pixel_interval ( VikTrwLayer *vtl, VikViewport *vvp, gint x, gint y )
3857 {
3858   TPSearchParams params;
3859   params.x = x;
3860   params.y = y;
3861   params.vvp = vvp;
3862   params.closest_track_name = NULL;
3863   params.closest_tp = NULL;
3864   g_hash_table_foreach ( vtl->tracks, (GHFunc) track_search_closest_tp, &params);
3865   return params.closest_tp;
3866 }
3867
3868 static VikWaypoint *closest_wp_in_five_pixel_interval ( VikTrwLayer *vtl, VikViewport *vvp, gint x, gint y )
3869 {
3870   WPSearchParams params;
3871   params.x = x;
3872   params.y = y;
3873   params.vvp = vvp;
3874   params.closest_wp = NULL;
3875   params.closest_wp_name = NULL;
3876   g_hash_table_foreach ( vtl->waypoints, (GHFunc) waypoint_search_closest_tp, &params);
3877   return params.closest_wp;
3878 }
3879
3880 /* background drawing hook, to be passed the viewport */
3881 static gboolean tool_sync_done = TRUE;
3882
3883 static gboolean tool_sync(gpointer data)
3884 {
3885   VikViewport *vvp = data;
3886   gdk_threads_enter();
3887   vik_viewport_sync(vvp);
3888   tool_sync_done = TRUE;
3889   gdk_threads_leave();
3890   return FALSE;
3891 }
3892
3893 typedef struct {
3894   VikViewport *vvp;
3895   gboolean holding;
3896   GdkGC *gc;
3897   int oldx, oldy;
3898 } tool_ed_t;
3899
3900 static void marker_begin_move ( tool_ed_t *t, gint x, gint y )
3901 {
3902   t->holding = TRUE;
3903   t->gc = vik_viewport_new_gc (t->vvp, "black", 2);
3904   gdk_gc_set_function ( t->gc, GDK_INVERT );
3905   vik_viewport_draw_rectangle ( t->vvp, t->gc, FALSE, x-3, y-3, 6, 6 );
3906   vik_viewport_sync(t->vvp);
3907   t->oldx = x;
3908   t->oldy = y;
3909 }
3910
3911 static void marker_moveto ( tool_ed_t *t, gint x, gint y )
3912 {
3913   VikViewport *vvp =  t->vvp;
3914   vik_viewport_draw_rectangle ( vvp, t->gc, FALSE, t->oldx-3, t->oldy-3, 6, 6 );
3915   vik_viewport_draw_rectangle ( vvp, t->gc, FALSE, x-3, y-3, 6, 6 );
3916   t->oldx = x;
3917   t->oldy = y;
3918
3919   if (tool_sync_done) {
3920     g_idle_add_full (G_PRIORITY_HIGH_IDLE + 10, tool_sync, vvp, NULL);
3921     tool_sync_done = FALSE;
3922   }
3923 }
3924
3925 static void marker_end_move ( tool_ed_t *t )
3926 {
3927   vik_viewport_draw_rectangle ( t->vvp, t->gc, FALSE, t->oldx-3, t->oldy-3, 6, 6 );
3928   g_object_unref ( t->gc );
3929   t->holding = FALSE;
3930 }
3931
3932 /*** Edit waypoint ****/
3933
3934 static gpointer tool_edit_waypoint_create ( VikWindow *vw, VikViewport *vvp)
3935 {
3936   tool_ed_t *t = g_new(tool_ed_t, 1);
3937   t->vvp = vvp;
3938   t->holding = FALSE;
3939   return t;
3940 }
3941
3942 static gboolean tool_edit_waypoint_click ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data )
3943 {
3944   WPSearchParams params;
3945   tool_ed_t *t = data;
3946   VikViewport *vvp = t->vvp;
3947
3948   if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
3949     return FALSE;
3950
3951   if ( t->holding ) {
3952     return TRUE;
3953   }
3954
3955   if ( !vtl->vl.visible || !vtl->waypoints_visible )
3956     return FALSE;
3957
3958   if ( vtl->current_wp && vtl->current_wp->visible )
3959   {
3960     /* first check if current WP is within area (other may be 'closer', but we want to move the current) */
3961     gint x, y;
3962     vik_viewport_coord_to_screen ( vvp, &(vtl->current_wp->coord), &x, &y );
3963
3964     if ( abs(x - event->x) <= WAYPOINT_SIZE_APPROX &&
3965          abs(y - event->y) <= WAYPOINT_SIZE_APPROX )
3966     {
3967       if ( event->button == 3 )
3968         vtl->waypoint_rightclick = TRUE; /* remember that we're clicking; other layers will ignore release signal */
3969       else {
3970         marker_begin_move(t, event->x, event->y);
3971       }
3972       return TRUE;
3973     }
3974   }
3975
3976   params.vvp = vvp;
3977   params.x = event->x;
3978   params.y = event->y;
3979   params.closest_wp_name = NULL;
3980   /* TODO: should get track listitem so we can break it up, make a new track, mess it up, all that. */
3981   params.closest_wp = NULL;
3982   g_hash_table_foreach ( vtl->waypoints, (GHFunc) waypoint_search_closest_tp, &params);
3983   if ( vtl->current_wp == params.closest_wp && vtl->current_wp != NULL )
3984   {
3985     /* how do we get here? I'm putting in the abort until we can figure it out. -alex */
3986     marker_begin_move(t, event->x, event->y);
3987     g_critical("shouldn't be here");
3988     exit(1);
3989   }
3990   else if ( params.closest_wp )
3991   {
3992     if ( event->button == 3 )
3993       vtl->waypoint_rightclick = TRUE; /* remember that we're clicking; other layers will ignore release signal */
3994     else
3995       vtl->waypoint_rightclick = FALSE;
3996
3997     vtl->current_wp = params.closest_wp;
3998     vtl->current_wp_name = params.closest_wp_name;
3999
4000     if ( params.closest_wp )
4001       vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, g_hash_table_lookup ( vtl->waypoints_iters, vtl->current_wp_name ) );
4002
4003     /* could make it so don't update if old WP is off screen and new is null but oh well */
4004     vik_layer_emit_update ( VIK_LAYER(vtl) );
4005     return TRUE;
4006   }
4007
4008   vtl->current_wp = NULL;
4009   vtl->current_wp_name = NULL;
4010   vtl->waypoint_rightclick = FALSE;
4011   vik_layer_emit_update ( VIK_LAYER(vtl) );
4012   return FALSE;
4013 }
4014
4015 static gboolean tool_edit_waypoint_move ( VikTrwLayer *vtl, GdkEventMotion *event, gpointer data )
4016 {
4017   tool_ed_t *t = data;
4018   VikViewport *vvp = t->vvp;
4019
4020   if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
4021     return FALSE;
4022
4023   if ( t->holding ) {
4024     VikCoord new_coord;
4025     vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
4026
4027     /* snap to TP */
4028     if ( event->state & GDK_CONTROL_MASK )
4029     {
4030       VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
4031       if ( tp )
4032         new_coord = tp->coord;
4033     }
4034
4035     /* snap to WP */
4036     if ( event->state & GDK_SHIFT_MASK )
4037     {
4038       VikWaypoint *wp = closest_wp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
4039       if ( wp && wp != vtl->current_wp )
4040         new_coord = wp->coord;
4041     }
4042     
4043     { 
4044       gint x, y;
4045       vik_viewport_coord_to_screen ( vvp, &new_coord, &x, &y );
4046
4047       marker_moveto ( t, x, y );
4048     } 
4049     return TRUE;
4050   }
4051   return FALSE;
4052 }
4053
4054 static gboolean tool_edit_waypoint_release ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data )
4055 {
4056   tool_ed_t *t = data;
4057   VikViewport *vvp = t->vvp;
4058
4059   if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
4060     return FALSE;
4061   
4062   if ( t->holding && event->button == 1 )
4063   {
4064     VikCoord new_coord;
4065     vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
4066
4067     /* snap to TP */
4068     if ( event->state & GDK_CONTROL_MASK )
4069     {
4070       VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
4071       if ( tp )
4072         new_coord = tp->coord;
4073     }
4074
4075     /* snap to WP */
4076     if ( event->state & GDK_SHIFT_MASK )
4077     {
4078       VikWaypoint *wp = closest_wp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
4079       if ( wp && wp != vtl->current_wp )
4080         new_coord = wp->coord;
4081     }
4082
4083     marker_end_move ( t );
4084
4085     vtl->current_wp->coord = new_coord;
4086     vik_layer_emit_update ( VIK_LAYER(vtl) );
4087     return TRUE;
4088   }
4089   /* PUT IN RIGHT PLACE!!! */
4090   if ( event->button == 3 && vtl->waypoint_rightclick )
4091   {
4092     if ( vtl->wp_right_click_menu )
4093       g_object_ref_sink ( G_OBJECT(vtl->wp_right_click_menu) );
4094     vtl->wp_right_click_menu = GTK_MENU ( gtk_menu_new () );
4095     vik_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 );
4096     gtk_menu_popup ( vtl->wp_right_click_menu, NULL, NULL, NULL, NULL, event->button, gtk_get_current_event_time() );
4097     vtl->waypoint_rightclick = FALSE;
4098   }
4099   return FALSE;
4100 }
4101
4102 /**** Begin track ***/
4103 static gpointer tool_begin_track_create ( VikWindow *vw, VikViewport *vvp)
4104 {
4105   return vvp;
4106 }
4107
4108 static gboolean tool_begin_track_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
4109 {
4110   vtl->current_track = NULL;
4111   return tool_new_track_click ( vtl, event, vvp );
4112 }
4113
4114 /*** New track ****/
4115
4116 static gpointer tool_new_track_create ( VikWindow *vw, VikViewport *vvp)
4117 {
4118   return vvp;
4119 }
4120
4121 typedef struct {
4122   VikTrwLayer *vtl;
4123   VikViewport *vvp;
4124   gint x1,y1,x2,y2;
4125 } new_track_move_passalong_t;
4126
4127 /* sync and undraw, but only when we have time */
4128 static gboolean ct_sync ( gpointer passalong )
4129 {
4130   new_track_move_passalong_t *p = (new_track_move_passalong_t *) passalong;
4131   vik_viewport_sync ( p->vvp );
4132   gdk_gc_set_function ( p->vtl->current_track_gc, GDK_INVERT );
4133   vik_viewport_draw_line ( p->vvp, p->vtl->current_track_gc, p->x1, p->y1, p->x2, p->y2 );
4134   gdk_gc_set_function ( p->vtl->current_track_gc, GDK_COPY );
4135   p->vtl->ct_sync_done = TRUE;
4136   g_free ( p );
4137   return FALSE;
4138 }
4139
4140 static VikLayerToolFuncStatus tool_new_track_move ( VikTrwLayer *vtl, GdkEventMotion *event, VikViewport *vvp )
4141 {
4142   /* if we haven't sync'ed yet, we don't have time to do more. */
4143   if ( vtl->ct_sync_done && vtl->current_track && vtl->current_track->trackpoints ) {
4144     GList *iter = vtl->current_track->trackpoints;
4145     new_track_move_passalong_t *passalong;
4146     gint x1, y1;
4147
4148     while ( iter->next )
4149       iter = iter->next;
4150     gdk_gc_set_function ( vtl->current_track_gc, GDK_INVERT );
4151     vik_viewport_coord_to_screen ( vvp, &(VIK_TRACKPOINT(iter->data)->coord), &x1, &y1 );
4152     vik_viewport_draw_line ( vvp, vtl->current_track_gc, x1, y1, event->x, event->y );
4153     gdk_gc_set_function ( vtl->current_track_gc, GDK_COPY );
4154
4155     passalong = g_new(new_track_move_passalong_t,1); /* freed by sync */
4156     passalong->vtl = vtl;
4157     passalong->vvp = vvp;
4158     passalong->x1 = x1;
4159     passalong->y1 = y1;
4160     passalong->x2 = event->x;
4161     passalong->y2 = event->y;
4162
4163     /* this will sync and undraw when we have time to */
4164     g_idle_add_full (G_PRIORITY_HIGH_IDLE + 10, ct_sync, passalong, NULL);
4165     vtl->ct_sync_done = FALSE;
4166     return VIK_LAYER_TOOL_ACK_GRAB_FOCUS;
4167   }
4168   return VIK_LAYER_TOOL_ACK;
4169 }
4170
4171 static gboolean tool_new_track_key_press ( VikTrwLayer *vtl, GdkEventKey *event, VikViewport *vvp )
4172 {
4173   if ( vtl->current_track && event->keyval == GDK_Escape ) {
4174     vtl->current_track = NULL;
4175     vik_layer_emit_update ( VIK_LAYER(vtl) );
4176     return TRUE;
4177   } else if ( vtl->current_track && event->keyval == GDK_BackSpace ) {
4178     /* undo */
4179     if ( vtl->current_track->trackpoints )
4180     {
4181       GList *last = g_list_last(vtl->current_track->trackpoints);
4182       g_free ( last->data );
4183       vtl->current_track->trackpoints = g_list_remove_link ( vtl->current_track->trackpoints, last );
4184     }
4185     vik_layer_emit_update ( VIK_LAYER(vtl) );
4186     return TRUE;
4187   }
4188   return FALSE;
4189 }
4190
4191 static gboolean tool_new_track_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
4192 {
4193   VikTrackpoint *tp;
4194
4195   if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
4196     return FALSE;
4197
4198   if ( event->button == 3 && vtl->current_track )
4199   {
4200     /* undo */
4201     if ( vtl->current_track->trackpoints )
4202     {
4203       GList *last = g_list_last(vtl->current_track->trackpoints);
4204       g_free ( last->data );
4205       vtl->current_track->trackpoints = g_list_remove_link ( vtl->current_track->trackpoints, last );
4206     }
4207     vik_layer_emit_update ( VIK_LAYER(vtl) );
4208     return TRUE;
4209   }
4210
4211   if ( event->type == GDK_2BUTTON_PRESS )
4212   {
4213     /* subtract last (duplicate from double click) tp then end */
4214     if ( vtl->current_track && vtl->current_track->trackpoints && vtl->ct_x1 == vtl->ct_x2 && vtl->ct_y1 == vtl->ct_y2 )
4215     {
4216       GList *last = g_list_last(vtl->current_track->trackpoints);
4217       g_free ( last->data );
4218       vtl->current_track->trackpoints = g_list_remove_link ( vtl->current_track->trackpoints, last );
4219       /* undo last, then end */
4220       vtl->current_track = NULL;
4221     }
4222     vik_layer_emit_update ( VIK_LAYER(vtl) );
4223     return TRUE;
4224   }
4225
4226   if ( ! vtl->current_track )
4227   {
4228     gchar *name = get_new_unique_sublayer_name(vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, _("Track"));
4229     if ( ( name = a_dialog_new_track ( VIK_GTK_WINDOW_FROM_LAYER(vtl), vtl->tracks, name ) ) )
4230     {
4231       vtl->current_track = vik_track_new();
4232       vtl->current_track->visible = TRUE;
4233       vik_trw_layer_add_track ( vtl, name, vtl->current_track );
4234
4235       /* incase it was created by begin track */
4236       vik_window_enable_layer_tool ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)), VIK_LAYER_TRW, TOOL_CREATE_TRACK );
4237     }
4238     else
4239       return TRUE;
4240   }
4241   tp = vik_trackpoint_new();
4242   vik_viewport_screen_to_coord ( vvp, event->x, event->y, &(tp->coord) );
4243
4244   /* snap to other TP */
4245   if ( event->state & GDK_CONTROL_MASK )
4246   {
4247     VikTrackpoint *other_tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
4248     if ( other_tp )
4249       tp->coord = other_tp->coord;
4250   }
4251
4252   tp->newsegment = FALSE;
4253   tp->has_timestamp = FALSE;
4254   tp->timestamp = 0;
4255   vtl->current_track->trackpoints = g_list_append ( vtl->current_track->trackpoints, tp );
4256
4257   vtl->ct_x1 = vtl->ct_x2;
4258   vtl->ct_y1 = vtl->ct_y2;
4259   vtl->ct_x2 = event->x;
4260   vtl->ct_y2 = event->y;
4261
4262   vik_layer_emit_update ( VIK_LAYER(vtl) );
4263   return TRUE;
4264 }
4265
4266
4267 /*** New waypoint ****/
4268
4269 static gpointer tool_new_waypoint_create ( VikWindow *vw, VikViewport *vvp)
4270 {
4271   return vvp;
4272 }
4273
4274 static gboolean tool_new_waypoint_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
4275 {
4276   VikCoord coord;
4277   if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
4278     return FALSE;
4279   vik_viewport_screen_to_coord ( vvp, event->x, event->y, &coord );
4280   if (vik_trw_layer_new_waypoint ( vtl, VIK_GTK_WINDOW_FROM_LAYER(vtl), &coord ) && VIK_LAYER(vtl)->visible)
4281     vik_layer_emit_update ( VIK_LAYER(vtl) );
4282   return TRUE;
4283 }
4284
4285
4286 /*** Edit trackpoint ****/
4287
4288 static gpointer tool_edit_trackpoint_create ( VikWindow *vw, VikViewport *vvp)
4289 {
4290   tool_ed_t *t = g_new(tool_ed_t, 1);
4291   t->vvp = vvp;
4292   t->holding = FALSE;
4293   return t;
4294 }
4295
4296 static gboolean tool_edit_trackpoint_click ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data )
4297 {
4298   tool_ed_t *t = data;
4299   VikViewport *vvp = t->vvp;
4300   TPSearchParams params;
4301   /* OUTDATED DOCUMENTATION:
4302    find 5 pixel range on each side. then put these UTM, and a pointer
4303    to the winning track name (and maybe the winning track itself), and a
4304    pointer to the winning trackpoint, inside an array or struct. pass 
4305    this along, do a foreach on the tracks which will do a foreach on the 
4306    trackpoints. */
4307   params.vvp = vvp;
4308   params.x = event->x;
4309   params.y = event->y;
4310   params.closest_track_name = NULL;
4311   /* TODO: should get track listitem so we can break it up, make a new track, mess it up, all that. */
4312   params.closest_tp = NULL;
4313
4314   if ( event->button != 1 ) 
4315     return FALSE;
4316
4317   if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
4318     return FALSE;
4319
4320   if ( !vtl->vl.visible || !vtl->tracks_visible )
4321     return FALSE;
4322
4323   if ( vtl->current_tpl )
4324   {
4325     /* 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.) */
4326     VikTrackpoint *tp = VIK_TRACKPOINT(vtl->current_tpl->data);
4327     VikTrack *current_tr = VIK_TRACK(g_hash_table_lookup(vtl->tracks, vtl->current_tp_track_name));
4328     gint x, y;
4329     g_assert ( current_tr );
4330
4331     vik_viewport_coord_to_screen ( vvp, &(tp->coord), &x, &y );
4332
4333     if ( current_tr->visible && 
4334          abs(x - event->x) < TRACKPOINT_SIZE_APPROX &&
4335          abs(y - event->y) < TRACKPOINT_SIZE_APPROX ) {
4336       marker_begin_move ( t, event->x, event->y );
4337       return TRUE;
4338     }
4339
4340     vtl->last_tpl = vtl->current_tpl;
4341     vtl->last_tp_track_name = vtl->current_tp_track_name;
4342   }
4343
4344   g_hash_table_foreach ( vtl->tracks, (GHFunc) track_search_closest_tp, &params);
4345
4346   if ( params.closest_tp )
4347   {
4348     vtl->current_tpl = params.closest_tpl;
4349     vtl->current_tp_track_name = params.closest_track_name;
4350     vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, g_hash_table_lookup ( vtl->tracks_iters, vtl->current_tp_track_name ) );
4351     trw_layer_tpwin_init ( vtl );
4352     vik_layer_emit_update ( VIK_LAYER(vtl) );
4353     return TRUE;
4354   }
4355
4356   /* these aren't the droids you're looking for */
4357   return FALSE;
4358 }
4359
4360 static gboolean tool_edit_trackpoint_move ( VikTrwLayer *vtl, GdkEventMotion *event, gpointer data )
4361 {
4362   tool_ed_t *t = data;
4363   VikViewport *vvp = t->vvp;
4364
4365   if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
4366     return FALSE;
4367
4368   if ( t->holding )
4369   {
4370     VikCoord new_coord;
4371     vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
4372
4373     /* snap to TP */
4374     if ( event->state & GDK_CONTROL_MASK )
4375     {
4376       VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
4377       if ( tp && tp != vtl->current_tpl->data )
4378         new_coord = tp->coord;
4379     }
4380     //    VIK_TRACKPOINT(vtl->current_tpl->data)->coord = new_coord;
4381     { 
4382       gint x, y;
4383       vik_viewport_coord_to_screen ( vvp, &new_coord, &x, &y );
4384       marker_moveto ( t, x, y );
4385     } 
4386
4387     return TRUE;
4388   }
4389   return FALSE;
4390 }
4391
4392 static gboolean tool_edit_trackpoint_release ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data )
4393 {
4394   tool_ed_t *t = data;
4395   VikViewport *vvp = t->vvp;
4396
4397   if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
4398     return FALSE;
4399   if ( event->button != 1) 
4400     return FALSE;
4401
4402   if ( t->holding ) {
4403     VikCoord new_coord;
4404     vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
4405
4406     /* snap to TP */
4407     if ( event->state & GDK_CONTROL_MASK )
4408     {
4409       VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
4410       if ( tp && tp != vtl->current_tpl->data )
4411         new_coord = tp->coord;
4412     }
4413
4414     VIK_TRACKPOINT(vtl->current_tpl->data)->coord = new_coord;
4415
4416     marker_end_move ( t );
4417
4418     /* diff dist is diff from orig */
4419     vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, vtl->current_tp_track_name );
4420     /* can't join with itself! */
4421     trw_layer_cancel_last_tp ( vtl );
4422
4423     vik_layer_emit_update ( VIK_LAYER(vtl) );
4424     return TRUE;
4425   }
4426   return FALSE;
4427 }
4428
4429
4430 /*** Magic Scissors ***/
4431 static gpointer tool_magic_scissors_create ( VikWindow *vw, VikViewport *vvp)
4432 {
4433   return vvp;
4434 }
4435
4436 static gboolean tool_magic_scissors_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
4437 {
4438   VikCoord tmp;
4439   if ( !vtl ) return FALSE;
4440   vik_viewport_screen_to_coord ( vvp, event->x, event->y, &tmp );
4441   if ( event->button == 3 && vtl->magic_scissors_current_track ) {
4442     VikCoord *new_end;
4443     new_end = vik_track_cut_back_to_double_point ( vtl->magic_scissors_current_track );
4444     if ( new_end ) {
4445       vtl->magic_scissors_coord = *new_end;
4446       g_free ( new_end );
4447       vik_layer_emit_update ( VIK_LAYER(vtl) );
4448       /* remove last ' to:...' */
4449       if ( vtl->magic_scissors_current_track->comment ) {
4450         gchar *last_to = strrchr ( vtl->magic_scissors_current_track->comment, 't' );
4451         if ( last_to && (last_to - vtl->magic_scissors_current_track->comment > 1) ) {
4452           gchar *new_comment = g_strndup ( vtl->magic_scissors_current_track->comment,
4453                                            last_to - vtl->magic_scissors_current_track->comment - 1);
4454           vik_track_set_comment_no_copy ( vtl->magic_scissors_current_track, new_comment );
4455         }
4456       }
4457     }
4458   }
4459   else if ( vtl->magic_scissors_started || (event->state & GDK_CONTROL_MASK && vtl->magic_scissors_current_track) ) {
4460     struct LatLon start, end;
4461     gchar startlat[G_ASCII_DTOSTR_BUF_SIZE], startlon[G_ASCII_DTOSTR_BUF_SIZE];
4462     gchar endlat[G_ASCII_DTOSTR_BUF_SIZE], endlon[G_ASCII_DTOSTR_BUF_SIZE];
4463     gchar *url;
4464
4465     vik_coord_to_latlon ( &(vtl->magic_scissors_coord), &start );
4466     vik_coord_to_latlon ( &(tmp), &end );
4467     vtl->magic_scissors_coord = tmp; /* for continuations */
4468
4469     /* these are checked when adding a track from a file (vik_trw_layer_filein_add_track) */
4470     if ( event->state & GDK_CONTROL_MASK && vtl->magic_scissors_current_track ) {
4471       vtl->magic_scissors_append = TRUE;  // merge tracks. keep started true.
4472     } else {
4473       vtl->magic_scissors_check_added_track = TRUE;
4474       vtl->magic_scissors_started = FALSE;
4475     }
4476
4477     url = g_strdup_printf(GOOGLE_DIRECTIONS_STRING,
4478                           g_ascii_dtostr (startlat, G_ASCII_DTOSTR_BUF_SIZE, (gdouble) start.lat),
4479                           g_ascii_dtostr (startlon, G_ASCII_DTOSTR_BUF_SIZE, (gdouble) start.lon),
4480                           g_ascii_dtostr (endlat, G_ASCII_DTOSTR_BUF_SIZE, (gdouble) end.lat),
4481                           g_ascii_dtostr (endlon, G_ASCII_DTOSTR_BUF_SIZE, (gdouble) end.lon));
4482     a_babel_convert_from_url ( vtl, url, "kml", NULL, NULL );
4483     g_free ( url );
4484
4485     /* see if anything was done -- a track was added or appended to */
4486     if ( vtl->magic_scissors_check_added_track && vtl->magic_scissors_added_track_name ) {
4487       VikTrack *tr;
4488
4489       tr = g_hash_table_lookup ( vtl->tracks, vtl->magic_scissors_added_track_name );
4490
4491       if ( tr )
4492         vik_track_set_comment_no_copy ( tr, g_strdup_printf("from: %f,%f to: %f,%f", start.lat, start.lon, end.lat, end.lon ) );
4493  
4494       vtl->magic_scissors_current_track = tr;
4495
4496       g_free ( vtl->magic_scissors_added_track_name );
4497       vtl->magic_scissors_added_track_name = NULL;
4498     } else if ( vtl->magic_scissors_append == FALSE && vtl->magic_scissors_current_track ) {
4499       /* magic_scissors_append was originally TRUE but set to FALSE by filein_add_track */
4500       gchar *new_comment = g_strdup_printf("%s to: %f,%f", vtl->magic_scissors_current_track->comment, end.lat, end.lon );
4501       vik_track_set_comment_no_copy ( vtl->magic_scissors_current_track, new_comment );
4502     }
4503     vtl->magic_scissors_check_added_track = FALSE;
4504     vtl->magic_scissors_append = FALSE;
4505
4506     vik_layer_emit_update ( VIK_LAYER(vtl) );
4507   } else {
4508     vtl->magic_scissors_started = TRUE;
4509     vtl->magic_scissors_coord = tmp;
4510     vtl->magic_scissors_current_track = NULL;
4511   }
4512   return TRUE;
4513 }
4514
4515 /*** Show picture ****/
4516
4517 static gpointer tool_show_picture_create ( VikWindow *vw, VikViewport *vvp)
4518 {
4519   return vvp;
4520 }
4521
4522 /* Params are: vvp, event, last match found or NULL */
4523 static void tool_show_picture_wp ( char *name, VikWaypoint *wp, gpointer params[2] )
4524 {
4525   if ( wp->image && wp->visible )
4526   {
4527     gint x, y, slackx, slacky;
4528     GdkEventButton *event = (GdkEventButton *) params[1];
4529
4530     vik_viewport_coord_to_screen ( VIK_VIEWPORT(params[0]), &(wp->coord), &x, &y );
4531     slackx = wp->image_width / 2;
4532     slacky = wp->image_height / 2;
4533     if (    x <= event->x + slackx && x >= event->x - slackx
4534          && y <= event->y + slacky && y >= event->y - slacky )
4535     {
4536       params[2] = wp->image; /* we've found a match. however continue searching
4537                               * since we want to find the last match -- that
4538                               * is, the match that was drawn last. */
4539     }
4540   }
4541 }
4542
4543 static gboolean tool_show_picture_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
4544 {
4545   gpointer params[3] = { vvp, event, NULL };
4546   if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
4547     return FALSE;
4548   g_hash_table_foreach ( vtl->waypoints, (GHFunc) tool_show_picture_wp, params );
4549   if ( params[2] )
4550   {
4551     /* thanks to the Gaim people for showing me ShellExecute and g_spawn_command_line_async */
4552 #ifdef WINDOWS
4553     ShellExecute(NULL, NULL, (char *) params[2], NULL, ".\\", 0);
4554 #else /* WINDOWS */
4555     GError *err = NULL;
4556     gchar *quoted_file = g_shell_quote ( (gchar *) params[2] );
4557     gchar *cmd = g_strdup_printf ( "eog %s", quoted_file );
4558     g_free ( quoted_file );
4559     if ( ! g_spawn_command_line_async ( cmd, &err ) )
4560     {
4561       a_dialog_error_msg ( VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Could not launch eog to open file.") );
4562       g_error_free ( err );
4563     }
4564     g_free ( cmd );
4565 #endif /* WINDOWS */
4566     return TRUE; /* found a match */
4567   }
4568   else
4569     return FALSE; /* go through other layers, searching for a match */
4570 }
4571
4572 /***************************************************************************
4573  ** End tool code 
4574  ***************************************************************************/
4575
4576
4577
4578
4579
4580 static void image_wp_make_list ( char *name, VikWaypoint *wp, GSList **pics )
4581 {
4582   if ( wp->image && ( ! a_thumbnails_exists ( wp->image ) ) )
4583     *pics = g_slist_append ( *pics, (gpointer) g_strdup ( wp->image ) );
4584 }
4585
4586 /* Structure for thumbnail creating data used in the background thread */
4587 typedef struct {
4588   VikTrwLayer *vtl; // Layer needed for redrawing
4589   GSList *pics;     // Image list
4590 } thumbnail_create_thread_data;
4591
4592 static int create_thumbnails_thread ( thumbnail_create_thread_data *tctd, gpointer threaddata )
4593 {
4594   guint total = g_slist_length(tctd->pics), done = 0;
4595   while ( tctd->pics )
4596   {
4597     a_thumbnails_create ( (gchar *) tctd->pics->data );
4598     int result = a_background_thread_progress ( threaddata, ((gdouble) ++done) / total );
4599     if ( result != 0 )
4600       return -1; /* Abort thread */
4601
4602     tctd->pics = tctd->pics->next;
4603   }
4604
4605   // Redraw to show the thumbnails as they are now created
4606   gdk_threads_enter();
4607   if ( IS_VIK_LAYER(tctd->vtl) )
4608     vik_layer_emit_update ( VIK_LAYER(tctd->vtl) );
4609   gdk_threads_leave();
4610
4611   return 0;
4612 }
4613
4614 static void thumbnail_create_thread_free ( thumbnail_create_thread_data *tctd )
4615 {
4616   while ( tctd->pics )
4617   {
4618     g_free ( tctd->pics->data );
4619     tctd->pics = g_slist_delete_link ( tctd->pics, tctd->pics );
4620   }
4621   g_free ( tctd );
4622 }
4623
4624 static void trw_layer_verify_thumbnails ( VikTrwLayer *vtl, GtkWidget *vp )
4625 {
4626   if ( ! vtl->has_verified_thumbnails )
4627   {
4628     GSList *pics = NULL;
4629     g_hash_table_foreach ( vtl->waypoints, (GHFunc) image_wp_make_list, &pics );
4630     if ( pics )
4631     {
4632       gint len = g_slist_length ( pics );
4633       gchar *tmp = g_strdup_printf ( _("Creating %d Image Thumbnails..."), len );
4634       thumbnail_create_thread_data *tctd = g_malloc ( sizeof(thumbnail_create_thread_data) );
4635       tctd->vtl = vtl;
4636       tctd->pics = pics;
4637       a_background_thread ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
4638                             tmp,
4639                             (vik_thr_func) create_thumbnails_thread,
4640                             tctd,
4641                             (vik_thr_free_func) thumbnail_create_thread_free,
4642                             NULL,
4643                             len );
4644       g_free ( tmp );
4645     }
4646   }
4647 }
4648
4649 VikCoordMode vik_trw_layer_get_coord_mode ( VikTrwLayer *vtl )
4650 {
4651   return vtl->coord_mode;
4652 }
4653
4654
4655
4656 static void waypoint_convert ( const gchar *name, VikWaypoint *wp, VikCoordMode *dest_mode )
4657 {
4658   vik_coord_convert ( &(wp->coord), *dest_mode );
4659 }
4660
4661 static void track_convert ( const gchar *name, VikTrack *tr, VikCoordMode *dest_mode )
4662 {
4663   vik_track_convert ( tr, *dest_mode );
4664 }
4665
4666 static void trw_layer_change_coord_mode ( VikTrwLayer *vtl, VikCoordMode dest_mode )
4667 {
4668   if ( vtl->coord_mode != dest_mode )
4669   {
4670     vtl->coord_mode = dest_mode;
4671     g_hash_table_foreach ( vtl->waypoints, (GHFunc) waypoint_convert, &dest_mode );
4672     g_hash_table_foreach ( vtl->tracks, (GHFunc) track_convert, &dest_mode );
4673   }
4674 }
4675
4676 VikWaypoint *vik_trw_layer_get_waypoint ( VikTrwLayer *vtl, gchar *name )
4677 {
4678   return g_hash_table_lookup ( vtl->waypoints, name );
4679 }
4680
4681 VikTrack *vik_trw_layer_get_track ( VikTrwLayer *vtl, const gchar *name )
4682 {
4683   return g_hash_table_lookup ( vtl->tracks, name );
4684 }
4685
4686 static void vik_trw_layer_set_menu_selection(VikTrwLayer *vtl, guint16 selection)
4687 {
4688   vtl->menu_selection = selection;
4689 }
4690
4691 static guint16 vik_trw_layer_get_menu_selection(VikTrwLayer *vtl)
4692 {
4693   return(vtl->menu_selection);
4694 }
4695
4696 /* ----------- Downloading maps along tracks --------------- */
4697
4698 static int get_download_area_width(VikViewport *vvp, gdouble zoom_level, struct LatLon *wh)
4699 {
4700   /* TODO: calculating based on current size of viewport */
4701   const gdouble w_at_zoom_0_125 = 0.0013;
4702   const gdouble h_at_zoom_0_125 = 0.0011;
4703   gdouble zoom_factor = zoom_level/0.125;
4704
4705   wh->lat = h_at_zoom_0_125 * zoom_factor;
4706   wh->lon = w_at_zoom_0_125 * zoom_factor;
4707
4708   return 0;   /* all OK */
4709 }
4710
4711 static VikCoord *get_next_coord(VikCoord *from, VikCoord *to, struct LatLon *dist, gdouble gradient)
4712 {
4713   if ((dist->lon >= ABS(to->east_west - from->east_west)) &&
4714       (dist->lat >= ABS(to->north_south - from->north_south)))
4715     return NULL;
4716
4717   VikCoord *coord = g_malloc(sizeof(VikCoord));
4718   coord->mode = VIK_COORD_LATLON;
4719
4720   if (ABS(gradient) < 1) {
4721     if (from->east_west > to->east_west)
4722       coord->east_west = from->east_west - dist->lon;
4723     else
4724       coord->east_west = from->east_west + dist->lon;
4725     coord->north_south = gradient * (coord->east_west - from->east_west) + from->north_south;
4726   } else {
4727     if (from->north_south > to->north_south)
4728       coord->north_south = from->north_south - dist->lat;
4729     else
4730       coord->north_south = from->north_south + dist->lat;
4731     coord->east_west = (1/gradient) * (coord->north_south - from->north_south) + from->north_south;
4732   }
4733
4734   return coord;
4735 }
4736
4737 static GList *add_fillins(GList *list, VikCoord *from, VikCoord *to, struct LatLon *dist)
4738 {
4739   /* TODO: handle virtical track (to->east_west - from->east_west == 0) */
4740   gdouble gradient = (to->north_south - from->north_south)/(to->east_west - from->east_west);
4741
4742   VikCoord *next = from;
4743   while (TRUE) {
4744     if ((next = get_next_coord(next, to, dist, gradient)) == NULL)
4745         break;
4746     list = g_list_prepend(list, next);
4747   }
4748
4749   return list;
4750 }
4751
4752 void vik_track_download_map(VikTrack *tr, VikMapsLayer *vml, VikViewport *vvp, gdouble zoom_level)
4753 {
4754   typedef struct _Rect {
4755     VikCoord tl;
4756     VikCoord br;
4757     VikCoord center;
4758   } Rect;
4759 #define GLRECT(iter) ((Rect *)((iter)->data))
4760
4761   struct LatLon wh;
4762   GList *rects_to_download = NULL;
4763   GList *rect_iter;
4764
4765   if (get_download_area_width(vvp, zoom_level, &wh))
4766     return;
4767
4768   GList *iter = tr->trackpoints;
4769   if (!iter)
4770     return;
4771
4772   gboolean new_map = TRUE;
4773   VikCoord *cur_coord, tl, br;
4774   Rect *rect;
4775   while (iter) {
4776     cur_coord = &(VIK_TRACKPOINT(iter->data))->coord;
4777     if (new_map) {
4778       vik_coord_set_area(cur_coord, &wh, &tl, &br);
4779       rect = g_malloc(sizeof(Rect));
4780       rect->tl = tl;
4781       rect->br = br;
4782       rect->center = *cur_coord;
4783       rects_to_download = g_list_prepend(rects_to_download, rect);
4784       new_map = FALSE;
4785       iter = iter->next;
4786       continue;
4787     }
4788     gboolean found = FALSE;
4789     for (rect_iter = rects_to_download; rect_iter; rect_iter = rect_iter->next) {
4790       if (vik_coord_inside(cur_coord, &GLRECT(rect_iter)->tl, &GLRECT(rect_iter)->br)) {
4791         found = TRUE;
4792         break;
4793       }
4794     }
4795     if (found)
4796         iter = iter->next;
4797     else
4798       new_map = TRUE;
4799   }
4800
4801   GList *fillins = NULL;
4802   /* 'fillin' doesn't work in UTM mode - potentially ending up in massive loop continually allocating memory - hence don't do it */
4803   /* seems that ATM the function get_next_coord works only for LATLON */
4804   if ( cur_coord->mode == VIK_COORD_LATLON ) {
4805     /* fill-ins for far apart points */
4806     GList *cur_rect, *next_rect;
4807     for (cur_rect = rects_to_download;
4808          (next_rect = cur_rect->next) != NULL;
4809          cur_rect = cur_rect->next) {
4810       if ((wh.lon < ABS(GLRECT(cur_rect)->center.east_west - GLRECT(next_rect)->center.east_west)) ||
4811           (wh.lat < ABS(GLRECT(cur_rect)->center.north_south - GLRECT(next_rect)->center.north_south))) {
4812         fillins = add_fillins(fillins, &GLRECT(cur_rect)->center, &GLRECT(next_rect)->center, &wh);
4813       }
4814     }
4815   } else
4816     g_message("%s: this feature works only in Mercator mode", __FUNCTION__);
4817
4818   if (fillins) {
4819     GList *iter = fillins;
4820     while (iter) {
4821       cur_coord = (VikCoord *)(iter->data);
4822       vik_coord_set_area(cur_coord, &wh, &tl, &br);
4823       rect = g_malloc(sizeof(Rect));
4824       rect->tl = tl;
4825       rect->br = br;
4826       rect->center = *cur_coord;
4827       rects_to_download = g_list_prepend(rects_to_download, rect);
4828       iter = iter->next;
4829     }
4830   }
4831
4832   for (rect_iter = rects_to_download; rect_iter; rect_iter = rect_iter->next) {
4833     maps_layer_download_section_without_redraw(vml, vvp, &(((Rect *)(rect_iter->data))->tl), &(((Rect *)(rect_iter->data))->br), zoom_level);
4834   }
4835
4836   if (fillins) {
4837     for (iter = fillins; iter; iter = iter->next)
4838       g_free(iter->data);
4839     g_list_free(fillins);
4840   }
4841   if (rects_to_download) {
4842     for (rect_iter = rects_to_download; rect_iter; rect_iter = rect_iter->next)
4843       g_free(rect_iter->data);
4844     g_list_free(rects_to_download);
4845   }
4846 }
4847
4848 static void trw_layer_download_map_along_track_cb ( gpointer pass_along[6] )
4849 {
4850   VikMapsLayer *vml;
4851   gint selected_map, default_map;
4852   gchar *zoomlist[] = {"0.125", "0.25", "0.5", "1", "2", "4", "8", "16", "32", "64", "128", "256", "512", "1024", NULL };
4853   gdouble zoom_vals[] = {0.125, 0.25, 0.5, 1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024};
4854   gint selected_zoom, default_zoom;
4855   int i,j;
4856
4857
4858   VikTrwLayer *vtl = pass_along[0];
4859   VikLayersPanel *vlp = pass_along[1];
4860   VikTrack *tr = (VikTrack *) g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, pass_along[3] );
4861   VikViewport *vvp = vik_window_viewport((VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl)));
4862
4863   GList *vmls = vik_layers_panel_get_all_layers_of_type(vlp, VIK_LAYER_MAPS);
4864   int num_maps = g_list_length(vmls);
4865
4866   if (!num_maps) {
4867     a_dialog_msg(VIK_GTK_WINDOW_FROM_LAYER(vtl), GTK_MESSAGE_ERROR, _("No map layer in use. Create one first"), NULL);
4868     return;
4869   }
4870
4871   gchar **map_names = g_malloc(1 + num_maps * sizeof(gpointer));
4872   VikMapsLayer **map_layers = g_malloc(1 + num_maps * sizeof(gpointer));
4873
4874   gchar **np = map_names;
4875   VikMapsLayer **lp = map_layers;
4876   for (i = 0; i < num_maps; i++) {
4877     gboolean dup = FALSE;
4878     vml = (VikMapsLayer *)(vmls->data);
4879     for (j = 0; j < i; j++) { /* no duplicate allowed */
4880       if (vik_maps_layer_get_map_type(vml) == vik_maps_layer_get_map_type(map_layers[j])) {
4881         dup = TRUE;
4882         break;
4883       }
4884     }
4885     if (!dup) {
4886       *lp++ = vml;
4887       *np++ = vik_maps_layer_get_map_label(vml);
4888     }
4889     vmls = vmls->next;
4890   }
4891   *lp = NULL;
4892   *np = NULL;
4893   num_maps = lp - map_layers;
4894
4895   for (default_map = 0; default_map < num_maps; default_map++) {
4896     /* TODO: check for parent layer's visibility */
4897     if (VIK_LAYER(map_layers[default_map])->visible)
4898       break;
4899   }
4900   default_map = (default_map == num_maps) ? 0 : default_map;
4901
4902   gdouble cur_zoom = vik_viewport_get_zoom(vvp);
4903   for (default_zoom = 0; default_zoom < sizeof(zoom_vals)/sizeof(gdouble); default_zoom++) {
4904     if (cur_zoom == zoom_vals[default_zoom])
4905       break;
4906   }
4907   default_zoom = (default_zoom == sizeof(zoom_vals)/sizeof(gdouble)) ? sizeof(zoom_vals)/sizeof(gdouble) - 1 : default_zoom;
4908
4909   if (!a_dialog_map_n_zoom(VIK_GTK_WINDOW_FROM_LAYER(vtl), map_names, default_map, zoomlist, default_zoom, &selected_map, &selected_zoom))
4910     goto done;
4911
4912   vik_track_download_map(tr, map_layers[selected_map], vvp, zoom_vals[selected_zoom]);
4913
4914 done:
4915   for (i = 0; i < num_maps; i++)
4916     g_free(map_names[i]);
4917   g_free(map_names);
4918   g_free(map_layers);
4919
4920   g_list_free(vmls);
4921
4922 }
4923
4924 /**** lowest waypoint number calculation ***/
4925 static gint highest_wp_number_name_to_number(const gchar *name) {
4926   if ( strlen(name) == 3 ) {
4927     int n = atoi(name);
4928     if ( n < 100 && name[0] != '0' )
4929       return -1;
4930     if ( n < 10 && name[0] != '0' )
4931       return -1;
4932     return n;
4933   }
4934   return -1;
4935 }
4936
4937
4938 static void highest_wp_number_reset(VikTrwLayer *vtl)
4939 {
4940   vtl->highest_wp_number = -1;
4941 }
4942
4943 static void highest_wp_number_add_wp(VikTrwLayer *vtl, const gchar *new_wp_name)
4944 {
4945   /* if is bigger that top, add it */
4946   gint new_wp_num = highest_wp_number_name_to_number(new_wp_name);
4947   if ( new_wp_num > vtl->highest_wp_number )
4948     vtl->highest_wp_number = new_wp_num;
4949 }
4950
4951 static void highest_wp_number_remove_wp(VikTrwLayer *vtl, const gchar *old_wp_name)
4952 {
4953   /* if wasn't top, do nothing. if was top, count backwards until we find one used */
4954   gint old_wp_num = highest_wp_number_name_to_number(old_wp_name);
4955   if ( vtl->highest_wp_number == old_wp_num ) {
4956     gchar buf[4];
4957     vtl->highest_wp_number --;
4958
4959     g_snprintf(buf,4,"%03d", vtl->highest_wp_number );
4960     /* search down until we find something that *does* exist */
4961
4962     while ( vtl->highest_wp_number > 0 && ! g_hash_table_lookup ( vtl->waypoints, buf ) ) {
4963       vtl->highest_wp_number --;
4964       g_snprintf(buf,4,"%03d", vtl->highest_wp_number );
4965     }
4966   }
4967 }
4968
4969 /* get lowest unused number */
4970 static gchar *highest_wp_number_get(VikTrwLayer *vtl)
4971 {
4972   gchar buf[4];
4973   if ( vtl->highest_wp_number < 0 || vtl->highest_wp_number >= 999 )
4974     return NULL;
4975   g_snprintf(buf,4,"%03d", vtl->highest_wp_number+1 );
4976   return g_strdup(buf);
4977 }