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