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