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