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