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