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