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