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