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