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