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