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