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