]> git.street.me.uk Git - andy/viking.git/blame - src/viktrwlayer.c
Enable vik_layers_panel_get_all_layers_of_type to optionally return only visible...
[andy/viking.git] / src / viktrwlayer.c
CommitLineData
50a14534
EB
1/*
2 * viking -- GPS Data and Topo Analyzer, Explorer, and Manager
3 *
a482007a
GB
4 * Copyright (C) 2003-2007, Evan Battaglia <gtoevan@gmx.net>
5 * Copyright (C) 2005-2008, Alex Foobarian <foobarian@gmail.com>
6 * Copyright (C) 2007, Quy Tonthat <qtonthat@gmail.com>
7 * Copyright (C) 2009, Hein Ragas <viking@ragas.nl>
50a14534
EB
8 *
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2 of the License, or
12 * (at your option) any later version.
13 *
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
18 *
19 * You should have received a copy of the GNU General Public License
20 * along with this program; if not, write to the Free Software
21 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
22 *
23 */
24
25#define WAYPOINT_FONT "Sans 8"
26
27/* WARNING: If you go beyond this point, we are NOT responsible for any ill effects on your sanity */
28/* viktrwlayer.c -- 2200 lines can make a difference in the state of things */
29
3e7553ae
GB
30#ifdef HAVE_CONFIG_H
31#include "config.h"
32#endif
33
50a14534 34#include "viking.h"
7114e879 35#include "vikmapslayer.h"
50a14534
EB
36#include "viktrwlayer_tpwin.h"
37#include "viktrwlayer_propwin.h"
acaf7113 38#include "garminsymbols.h"
50a14534
EB
39#include "thumbnails.h"
40#include "background.h"
911400b5 41#include "gpx.h"
e0b0b9c1 42#include "babel.h"
ad0a8c2d
EB
43#include "dem.h"
44#include "dems.h"
165a4fa9 45#include "geonamessearch.h"
3e7553ae
GB
46#ifdef VIK_CONFIG_OPENSTREETMAP
47#include "osm-traces.h"
48#endif
28c82d8b 49#include "acquire.h"
7d02a0b0 50#include "util.h"
50a14534 51
bce3a7b0
EB
52#include "icons/icons.h"
53
8c00358d 54#ifdef HAVE_MATH_H
50a14534 55#include <math.h>
8c00358d
GB
56#endif
57#ifdef HAVE_STRING_H
50a14534 58#include <string.h>
8c00358d
GB
59#endif
60#ifdef HAVE_STDLIB_H
50a14534 61#include <stdlib.h>
8c00358d 62#endif
8c060406 63#include <stdio.h>
50a14534
EB
64#include <ctype.h>
65
777e2d4d 66#include <gdk/gdkkeysyms.h>
8c060406
MA
67#include <glib.h>
68#include <glib/gstdio.h>
4c77d5e0 69#include <glib/gi18n.h>
777e2d4d 70
090cae39
GB
71/* Relax some dependencies */
72#if ! GLIB_CHECK_VERSION(2,12,0)
73static gboolean return_true (gpointer a, gpointer b, gpointer c) { return TRUE; }
74static g_hash_table_remove_all (GHashTable *ght) { g_hash_table_foreach_remove ( ght, (GHRFunc) return_true, FALSE ); }
75#endif
76
ae941b4c 77#define GOOGLE_DIRECTIONS_STRING "maps.google.com/maps?q=from:%s,%s+to:%s,%s&output=kml"
480fb7e1 78#define VIK_TRW_LAYER_TRACK_GC 13
50a14534
EB
79#define VIK_TRW_LAYER_TRACK_GC_RATES 10
80#define VIK_TRW_LAYER_TRACK_GC_MIN 0
81#define VIK_TRW_LAYER_TRACK_GC_MAX 11
82#define VIK_TRW_LAYER_TRACK_GC_BLACK 12
83
84#define DRAWMODE_BY_TRACK 0
85#define DRAWMODE_BY_VELOCITY 1
86#define DRAWMODE_ALL_BLACK 2
87
88#define POINTS 1
89#define LINES 2
90
91/* this is how it knows when you click if you are clicking close to a trackpoint. */
92#define TRACKPOINT_SIZE_APPROX 5
93#define WAYPOINT_SIZE_APPROX 5
94
b42a25ba
EB
95#define MIN_STOP_LENGTH 15
96#define MAX_STOP_LENGTH 86400
97#define DRAW_ELEVATION_FACTOR 30 /* height of elevation plotting, sort of relative to zoom level ("mpp" that isn't mpp necessarily) */
98 /* this is multiplied by user-inputted value from 1-100. */
50a14534
EB
99enum {
100VIK_TRW_LAYER_SUBLAYER_TRACKS,
101VIK_TRW_LAYER_SUBLAYER_WAYPOINTS,
102VIK_TRW_LAYER_SUBLAYER_TRACK,
103VIK_TRW_LAYER_SUBLAYER_WAYPOINT
104};
105
106enum { WP_SYMBOL_FILLED_SQUARE, WP_SYMBOL_SQUARE, WP_SYMBOL_CIRCLE, WP_SYMBOL_X, WP_NUM_SYMBOLS };
107
108struct _VikTrwLayer {
109 VikLayer vl;
110 GHashTable *tracks;
111 GHashTable *tracks_iters;
112 GHashTable *waypoints_iters;
113 GHashTable *waypoints;
114 GtkTreeIter waypoints_iter, tracks_iter;
115 gboolean tracks_visible, waypoints_visible;
116 guint8 drawmode;
117 guint8 drawpoints;
b42a25ba
EB
118 guint8 drawelevation;
119 guint8 elevation_factor;
120 guint8 drawstops;
121 guint32 stop_length;
50a14534
EB
122 guint8 drawlines;
123 guint8 line_thickness;
124 guint8 bg_line_thickness;
125
126 guint8 wp_symbol;
127 guint8 wp_size;
ea3933fc 128 gboolean wp_draw_symbols;
50a14534
EB
129
130 gdouble velocity_min, velocity_max;
131 GArray *track_gc;
132 guint16 track_gc_iter;
8e9c992d 133 GdkGC *current_track_gc;
50a14534
EB
134 GdkGC *track_bg_gc;
135 GdkGC *waypoint_gc;
136 GdkGC *waypoint_text_gc;
137 GdkGC *waypoint_bg_gc;
138 GdkFont *waypoint_font;
139 VikTrack *current_track;
140 guint16 ct_x1, ct_y1, ct_x2, ct_y2;
7b203521
EB
141 gboolean ct_sync_done;
142
50a14534
EB
143
144 VikCoordMode coord_mode;
145
146 /* wp editing tool */
147 VikWaypoint *current_wp;
148 gchar *current_wp_name;
149 gboolean moving_wp;
150 gboolean waypoint_rightclick;
151
152 /* track editing tool */
153 GList *current_tpl;
154 gchar *current_tp_track_name;
155 VikTrwLayerTpwin *tpwin;
156
157 /* weird hack for joining tracks */
158 GList *last_tpl;
159 gchar *last_tp_track_name;
160
161 /* track editing tool -- more specifically, moving tps */
162 gboolean moving_tp;
163
1eef1bde
QT
164 /* magic scissors tool */
165 gboolean magic_scissors_started;
166 VikCoord magic_scissors_coord;
bddd2056
EB
167 gboolean magic_scissors_check_added_track;
168 gchar *magic_scissors_added_track_name;
169 VikTrack *magic_scissors_current_track;
170 gboolean magic_scissors_append;
1eef1bde 171
50a14534
EB
172 gboolean drawlabels;
173 gboolean drawimages;
174 guint8 image_alpha;
175 GQueue *image_cache;
176 guint8 image_size;
177 guint16 image_cache_size;
178
179 /* for waypoint text */
180 PangoLayout *wplabellayout;
181
182 gboolean has_verified_thumbnails;
183
184 GtkMenu *wp_right_click_menu;
8bd81489 185 GtkMenu *track_right_click_menu;
50a14534 186
20c7a3a0
QT
187 /* menu */
188 VikStdLayerMenuItem menu_selection;
189
a8fe53f8 190 gint highest_wp_number;
50a14534
EB
191};
192
193/* A caached waypoint image. */
194typedef struct {
195 GdkPixbuf *pixbuf;
196 gchar *image; /* filename */
197} CachedPixbuf;
198
199struct DrawingParams {
200 VikViewport *vp;
201 VikTrwLayer *vtl;
202 gdouble xmpp, ympp;
203 guint16 width, height;
204 const VikCoord *center;
205 gint track_gc_iter;
206 gboolean one_zone, lat_lon;
207 gdouble ce1, ce2, cn1, cn2;
208};
209
2cebc318 210static void vik_trw_layer_set_menu_selection(VikTrwLayer *vtl, guint16);
20c7a3a0
QT
211static guint16 vik_trw_layer_get_menu_selection(VikTrwLayer *vtl);
212
6bb72350
RN
213static void trw_layer_delete_item ( gpointer pass_along[6] );
214static void trw_layer_copy_item_cb ( gpointer pass_along[6] );
215static void trw_layer_cut_item_cb ( gpointer pass_along[6] );
33534cd8 216
50a14534
EB
217static void trw_layer_find_maxmin_waypoints ( const gchar *name, const VikWaypoint *w, struct LatLon maxmin[2] );
218static void trw_layer_find_maxmin_tracks ( const gchar *name, GList **t, struct LatLon maxmin[2] );
165a4fa9 219static void trw_layer_find_maxmin (VikTrwLayer *vtl, struct LatLon maxmin[2]);
50a14534
EB
220
221static void trw_layer_new_track_gcs ( VikTrwLayer *vtl, VikViewport *vp );
222static void trw_layer_free_track_gcs ( VikTrwLayer *vtl );
223
224static gint calculate_velocity ( VikTrwLayer *vtl, VikTrackpoint *tp1, VikTrackpoint *tp2 );
225static void trw_layer_draw_track_cb ( const gchar *name, VikTrack *track, struct DrawingParams *dp );
226static void trw_layer_draw_waypoint ( const gchar *name, VikWaypoint *wp, struct DrawingParams *dp );
227
cb89c5a5 228static const gchar* trw_layer_layer_tooltip ( VikTrwLayer *vtl );
c7060c4e
RN
229static const gchar* trw_layer_sublayer_tooltip ( VikTrwLayer *l, gint subtype, gpointer sublayer );
230
6bb72350
RN
231static void goto_coord ( gpointer *vlp, gpointer vvp, gpointer vl, const VikCoord *coord );
232static void trw_layer_goto_track_startpoint ( gpointer pass_along[6] );
50a14534 233static void trw_layer_goto_track_endpoint ( gpointer pass_along[6] );
6bb72350
RN
234static void trw_layer_goto_track_max_speed ( gpointer pass_along[6] );
235static void trw_layer_goto_track_max_alt ( gpointer pass_along[6] );
236static void trw_layer_goto_track_min_alt ( gpointer pass_along[6] );
237static void trw_layer_goto_track_center ( gpointer pass_along[6] );
111fa174 238static void trw_layer_merge_by_timestamp ( gpointer pass_along[6] );
6bb72350 239static void trw_layer_merge_with_other ( gpointer pass_along[6] );
111fa174 240static void trw_layer_split_by_timestamp ( gpointer pass_along[6] );
af2341f3 241static void trw_layer_split_by_n_points ( gpointer pass_along[6] );
c95d6b00
RN
242static void trw_layer_download_map_along_track_cb ( gpointer pass_along[6] );
243static void trw_layer_edit_trackpoint ( gpointer pass_along[6] );
a412f3f5 244static void trw_layer_show_picture ( gpointer pass_along[6] );
c95d6b00 245
50a14534 246static void trw_layer_centerize ( gpointer layer_and_vlp[2] );
5a10c240 247static void trw_layer_auto_view ( gpointer layer_and_vlp[2] );
f7f8a0a6 248static void trw_layer_export ( gpointer layer_and_vlp[2], const gchar* title, const gchar* default_name, const gchar* trackname, guint file_type );
50a14534
EB
249static void trw_layer_goto_wp ( gpointer layer_and_vlp[2] );
250static void trw_layer_new_wp ( gpointer lav[2] );
fc59e8c7 251static void trw_layer_auto_waypoints_view ( gpointer lav[2] );
535ed1ae 252static void trw_layer_auto_tracks_view ( gpointer lav[2] );
165a4fa9
HR
253static void trw_layer_new_wikipedia_wp_viewport ( gpointer lav[2] );
254static void trw_layer_new_wikipedia_wp_layer ( gpointer lav[2] );
50a14534
EB
255
256/* pop-up items */
6bb72350
RN
257static void trw_layer_properties_item ( gpointer pass_along[6] );
258static void trw_layer_goto_waypoint ( gpointer pass_along[6] );
259static void trw_layer_waypoint_gc_webpage ( gpointer pass_along[6] );
50a14534 260
6bb72350
RN
261static void trw_layer_realize_waypoint ( gchar *name, VikWaypoint *wp, gpointer pass_along[5] );
262static void trw_layer_realize_track ( gchar *name, VikTrack *track, gpointer pass_along[5] );
50a14534
EB
263static void init_drawing_params ( struct DrawingParams *dp, VikViewport *vp );
264
50a14534 265
911400b5 266static void trw_layer_marshall( VikTrwLayer *vtl, guint8 **data, gint *len );
28612684 267static VikTrwLayer *trw_layer_unmarshall( guint8 *data, gint len, VikViewport *vvp );
911400b5 268
158b3642
RN
269static gboolean trw_layer_set_param ( VikTrwLayer *vtl, guint16 id, VikLayerParamData data, VikViewport *vp, gboolean is_file_operation );
270static VikLayerParamData trw_layer_get_param ( VikTrwLayer *vtl, guint16 id, gboolean is_file_operation );
50a14534 271
33534cd8 272static void trw_layer_del_item ( VikTrwLayer *vtl, gint subtype, gpointer sublayer );
d5874ef9
RN
273static void trw_layer_cut_item ( VikTrwLayer *vtl, gint subtype, gpointer sublayer );
274
ddc47a46
AF
275static void trw_layer_copy_item ( VikTrwLayer *vtl, gint subtype, gpointer sublayer, guint8 **item, guint *len );
276static gboolean trw_layer_paste_item ( VikTrwLayer *vtl, gint subtype, guint8 *item, guint len );
50a14534 277static void trw_layer_free_copied_item ( gint subtype, gpointer item );
70a23263 278static void trw_layer_drag_drop_request ( VikTrwLayer *vtl_src, VikTrwLayer *vtl_dest, GtkTreeIter *src_item_iter, GtkTreePath *dest_path );
50a14534 279
08f14055
RN
280static gboolean trw_layer_select_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp, tool_ed_t *t );
281static gboolean trw_layer_select_move ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp, tool_ed_t *t );
282static gboolean trw_layer_select_release ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp, tool_ed_t *t );
e46f259a 283static gboolean trw_layer_show_selected_viewport_menu ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp );
77ad64fa 284
db79f75f 285static void trw_layer_insert_tp_after_current_tp ( VikTrwLayer *vtl );
50a14534
EB
286static void trw_layer_cancel_last_tp ( VikTrwLayer *vtl );
287static void trw_layer_cancel_current_tp ( VikTrwLayer *vtl, gboolean destroy );
288static void trw_layer_tpwin_response ( VikTrwLayer *vtl, gint response );
289static void trw_layer_tpwin_init ( VikTrwLayer *vtl );
50a14534 290
941aa6e9 291static gpointer tool_edit_trackpoint_create ( VikWindow *vw, VikViewport *vvp);
33534cd8 292static gboolean tool_edit_trackpoint_click ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data );
dc2c040e 293static gboolean tool_edit_trackpoint_move ( VikTrwLayer *vtl, GdkEventMotion *event, gpointer data );
33534cd8 294static gboolean tool_edit_trackpoint_release ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data );
941aa6e9
AF
295static gpointer tool_show_picture_create ( VikWindow *vw, VikViewport *vvp);
296static gboolean tool_show_picture_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp );
297static gpointer tool_edit_waypoint_create ( VikWindow *vw, VikViewport *vvp);
7432fddf 298static gboolean tool_edit_waypoint_click ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data );
dc2c040e 299static gboolean tool_edit_waypoint_move ( VikTrwLayer *vtl, GdkEventMotion *event, gpointer data );
7432fddf 300static gboolean tool_edit_waypoint_release ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data );
98f5364d
EB
301static gpointer tool_begin_track_create ( VikWindow *vw, VikViewport *vvp);
302static gboolean tool_begin_track_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp );
941aa6e9
AF
303static gpointer tool_new_track_create ( VikWindow *vw, VikViewport *vvp);
304static gboolean tool_new_track_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp );
dc2c040e 305static VikLayerToolFuncStatus tool_new_track_move ( VikTrwLayer *vtl, GdkEventMotion *event, VikViewport *vvp );
777e2d4d 306static gboolean tool_new_track_key_press ( VikTrwLayer *vtl, GdkEventKey *event, VikViewport *vvp );
941aa6e9
AF
307static gpointer tool_new_waypoint_create ( VikWindow *vw, VikViewport *vvp);
308static gboolean tool_new_waypoint_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp );
1eef1bde
QT
309static gpointer tool_magic_scissors_create ( VikWindow *vw, VikViewport *vvp);
310static gboolean tool_magic_scissors_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp );
311
50a14534 312
50a14534
EB
313static void cached_pixbuf_free ( CachedPixbuf *cp );
314static gint cached_pixbuf_cmp ( CachedPixbuf *cp, const gchar *name );
315static void trw_layer_verify_thumbnails ( VikTrwLayer *vtl, GtkWidget *vp );
316
317static VikTrackpoint *closest_tp_in_five_pixel_interval ( VikTrwLayer *vtl, VikViewport *vvp, gint x, gint y );
318static VikWaypoint *closest_wp_in_five_pixel_interval ( VikTrwLayer *vtl, VikViewport *vvp, gint x, gint y );
319
320static void trw_layer_change_coord_mode ( VikTrwLayer *vtl, VikCoordMode dest_mode );
321
e890a6e6 322static gchar *get_new_unique_sublayer_name (VikTrwLayer *vtl, gint sublayer_type, const gchar *name);
ddc47a46
AF
323static void waypoint_convert ( const gchar *name, VikWaypoint *wp, VikCoordMode *dest_mode );
324static void track_convert ( const gchar *name, VikTrack *tr, VikCoordMode *dest_mode );
e890a6e6 325
a8fe53f8
EB
326static gchar *highest_wp_number_get(VikTrwLayer *vtl);
327static void highest_wp_number_reset(VikTrwLayer *vtl);
328static void highest_wp_number_add_wp(VikTrwLayer *vtl, const gchar *new_wp_name);
329static void highest_wp_number_remove_wp(VikTrwLayer *vtl, const gchar *old_wp_name);
330
331
50a14534 332static VikToolInterface trw_layer_tools[] = {
4c77d5e0 333 { N_("Create Waypoint"), (VikToolConstructorFunc) tool_new_waypoint_create, NULL, NULL, NULL,
5bfafde9 334 (VikToolMouseFunc) tool_new_waypoint_click, NULL, NULL, (VikToolKeyFunc) NULL, GDK_CURSOR_IS_PIXMAP, &cursor_addwp_pixbuf },
941aa6e9 335
4c77d5e0 336 { N_("Create Track"), (VikToolConstructorFunc) tool_new_track_create, NULL, NULL, NULL,
dc2c040e 337 (VikToolMouseFunc) tool_new_track_click, (VikToolMouseMoveFunc) tool_new_track_move, NULL,
5bfafde9 338 (VikToolKeyFunc) tool_new_track_key_press, GDK_CURSOR_IS_PIXMAP, &cursor_addtr_pixbuf },
50a14534 339
4c77d5e0 340 { N_("Begin Track"), (VikToolConstructorFunc) tool_begin_track_create, NULL, NULL, NULL,
5bfafde9 341 (VikToolMouseFunc) tool_begin_track_click, NULL, NULL, (VikToolKeyFunc) NULL, GDK_CURSOR_IS_PIXMAP, &cursor_begintr_pixbuf },
98f5364d 342
4c77d5e0 343 { N_("Edit Waypoint"), (VikToolConstructorFunc) tool_edit_waypoint_create, NULL, NULL, NULL,
7432fddf 344 (VikToolMouseFunc) tool_edit_waypoint_click,
dc2c040e 345 (VikToolMouseMoveFunc) tool_edit_waypoint_move,
5bfafde9 346 (VikToolMouseFunc) tool_edit_waypoint_release, (VikToolKeyFunc) NULL, GDK_CURSOR_IS_PIXMAP, &cursor_edwp_pixbuf },
941aa6e9 347
4c77d5e0 348 { N_("Edit Trackpoint"), (VikToolConstructorFunc) tool_edit_trackpoint_create, NULL, NULL, NULL,
33534cd8 349 (VikToolMouseFunc) tool_edit_trackpoint_click,
dc2c040e 350 (VikToolMouseMoveFunc) tool_edit_trackpoint_move,
5bfafde9 351 (VikToolMouseFunc) tool_edit_trackpoint_release, (VikToolKeyFunc) NULL, GDK_CURSOR_IS_PIXMAP, &cursor_edtr_pixbuf },
941aa6e9 352
4c77d5e0 353 { N_("Show Picture"), (VikToolConstructorFunc) tool_show_picture_create, NULL, NULL, NULL,
5bfafde9 354 (VikToolMouseFunc) tool_show_picture_click, NULL, NULL, (VikToolKeyFunc) NULL, GDK_CURSOR_IS_PIXMAP, &cursor_showpic_pixbuf },
1eef1bde 355
4c77d5e0 356 { N_("Magic Scissors"), (VikToolConstructorFunc) tool_magic_scissors_create, NULL, NULL, NULL,
5bfafde9 357 (VikToolMouseFunc) tool_magic_scissors_click, NULL, NULL, (VikToolKeyFunc) NULL, GDK_CURSOR_IS_PIXMAP, &cursor_iscissors_pixbuf },
941aa6e9 358};
8e9c992d 359enum { TOOL_CREATE_WAYPOINT=0, TOOL_CREATE_TRACK, TOOL_BEGIN_TRACK, TOOL_EDIT_WAYPOINT, TOOL_EDIT_TRACKPOINT, TOOL_SHOW_PICTURE, NUM_TOOLS };
50a14534
EB
360
361/****** PARAMETERS ******/
362
4c77d5e0 363static gchar *params_groups[] = { N_("Waypoints"), N_("Tracks"), N_("Waypoint Images") };
55906514 364enum { GROUP_WAYPOINTS, GROUP_TRACKS, GROUP_IMAGES };
50a14534 365
4c77d5e0
GB
366static gchar *params_drawmodes[] = { N_("Draw by Track"), N_("Draw by Velocity"), N_("All Tracks Black"), 0 };
367static gchar *params_wpsymbols[] = { N_("Filled Square"), N_("Square"), N_("Circle"), N_("X"), 0 };
50a14534 368
b42a25ba 369
50a14534
EB
370static VikLayerParamScale params_scales[] = {
371 /* min max step digits */
372 { 1, 10, 1, 0 }, /* line_thickness */
373 { 0.0, 99.0, 1, 2 }, /* velocity_min */
374 { 1.0, 100.0, 1.0, 2 }, /* velocity_max */
375 /* 5 * step == how much to turn */
376 { 16, 128, 3.2, 0 }, /* image_size */
377 { 0, 255, 5, 0 }, /* image alpha */
378 { 5, 500, 5, 0 }, /* image cache_size */
379 { 0, 8, 1, 0 }, /* image cache_size */
380 { 1, 64, 1, 0 }, /* wpsize */
b42a25ba
EB
381 { MIN_STOP_LENGTH, MAX_STOP_LENGTH, 1, 0 }, /* stop_length */
382 { 1, 100, 1, 0 }, /* stop_length */
50a14534
EB
383};
384
385VikLayerParam trw_layer_params[] = {
386 { "tracks_visible", VIK_LAYER_PARAM_BOOLEAN, VIK_LAYER_NOT_IN_PROPERTIES },
387 { "waypoints_visible", VIK_LAYER_PARAM_BOOLEAN, VIK_LAYER_NOT_IN_PROPERTIES },
388
4c77d5e0
GB
389 { "drawmode", VIK_LAYER_PARAM_UINT, GROUP_TRACKS, N_("Track Drawing Mode:"), VIK_LAYER_WIDGET_RADIOGROUP, NULL },
390 { "drawlines", VIK_LAYER_PARAM_BOOLEAN, GROUP_TRACKS, N_("Draw Track Lines"), VIK_LAYER_WIDGET_CHECKBUTTON },
391 { "drawpoints", VIK_LAYER_PARAM_BOOLEAN, GROUP_TRACKS, N_("Draw Trackpoints"), VIK_LAYER_WIDGET_CHECKBUTTON },
392 { "drawelevation", VIK_LAYER_PARAM_BOOLEAN, GROUP_TRACKS, N_("Draw Elevation"), VIK_LAYER_WIDGET_CHECKBUTTON },
393 { "elevation_factor", VIK_LAYER_PARAM_UINT, GROUP_TRACKS, N_("Draw Elevation Height %:"), VIK_LAYER_WIDGET_HSCALE, params_scales + 9 },
394
395 { "drawstops", VIK_LAYER_PARAM_BOOLEAN, GROUP_TRACKS, N_("Draw Stops"), VIK_LAYER_WIDGET_CHECKBUTTON },
396 { "stop_length", VIK_LAYER_PARAM_UINT, GROUP_TRACKS, N_("Min Stop Length (seconds):"), VIK_LAYER_WIDGET_SPINBUTTON, params_scales + 8 },
397
398 { "line_thickness", VIK_LAYER_PARAM_UINT, GROUP_TRACKS, N_("Track Thickness:"), VIK_LAYER_WIDGET_SPINBUTTON, params_scales + 0 },
399 { "bg_line_thickness", VIK_LAYER_PARAM_UINT, GROUP_TRACKS, N_("Track BG Thickness:"), VIK_LAYER_WIDGET_SPINBUTTON, params_scales + 6 },
400 { "trackbgcolor", VIK_LAYER_PARAM_COLOR, GROUP_TRACKS, N_("Track Background Color"), VIK_LAYER_WIDGET_COLOR, 0 },
401 { "velocity_min", VIK_LAYER_PARAM_DOUBLE, GROUP_TRACKS, N_("Min Track Velocity:"), VIK_LAYER_WIDGET_SPINBUTTON, params_scales + 1 },
402 { "velocity_max", VIK_LAYER_PARAM_DOUBLE, GROUP_TRACKS, N_("Max Track Velocity:"), VIK_LAYER_WIDGET_SPINBUTTON, params_scales + 2 },
403
404 { "drawlabels", VIK_LAYER_PARAM_BOOLEAN, GROUP_WAYPOINTS, N_("Draw Labels"), VIK_LAYER_WIDGET_CHECKBUTTON },
405 { "wpcolor", VIK_LAYER_PARAM_COLOR, GROUP_WAYPOINTS, N_("Waypoint Color:"), VIK_LAYER_WIDGET_COLOR, 0 },
406 { "wptextcolor", VIK_LAYER_PARAM_COLOR, GROUP_WAYPOINTS, N_("Waypoint Text:"), VIK_LAYER_WIDGET_COLOR, 0 },
407 { "wpbgcolor", VIK_LAYER_PARAM_COLOR, GROUP_WAYPOINTS, N_("Background:"), VIK_LAYER_WIDGET_COLOR, 0 },
408 { "wpbgand", VIK_LAYER_PARAM_BOOLEAN, GROUP_WAYPOINTS, N_("Fake BG Color Translucency:"), VIK_LAYER_WIDGET_CHECKBUTTON, 0 },
409 { "wpsymbol", VIK_LAYER_PARAM_UINT, GROUP_WAYPOINTS, N_("Waypoint marker:"), VIK_LAYER_WIDGET_RADIOGROUP, NULL },
410 { "wpsize", VIK_LAYER_PARAM_UINT, GROUP_WAYPOINTS, N_("Waypoint size:"), VIK_LAYER_WIDGET_SPINBUTTON, params_scales + 7 },
411 { "wpsyms", VIK_LAYER_PARAM_BOOLEAN, GROUP_WAYPOINTS, N_("Draw Waypoint Symbols:"), VIK_LAYER_WIDGET_CHECKBUTTON },
412
413 { "drawimages", VIK_LAYER_PARAM_BOOLEAN, GROUP_IMAGES, N_("Draw Waypoint Images"), VIK_LAYER_WIDGET_CHECKBUTTON },
414 { "image_size", VIK_LAYER_PARAM_UINT, GROUP_IMAGES, N_("Image Size (pixels):"), VIK_LAYER_WIDGET_HSCALE, params_scales + 3 },
415 { "image_alpha", VIK_LAYER_PARAM_UINT, GROUP_IMAGES, N_("Image Alpha:"), VIK_LAYER_WIDGET_HSCALE, params_scales + 4 },
416 { "image_cache_size", VIK_LAYER_PARAM_UINT, GROUP_IMAGES, N_("Image Memory Cache Size:"), VIK_LAYER_WIDGET_HSCALE, params_scales + 5 },
50a14534
EB
417};
418
55906514 419enum { 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 };
ad0a8c2d
EB
420
421/*** TO ADD A PARAM:
422 *** 1) Add to trw_layer_params and enumeration
423 *** 2) Handle in get_param & set_param (presumably adding on to VikTrwLayer struct)
ad0a8c2d 424 ***/
50a14534
EB
425
426/****** END PARAMETERS ******/
427
428VikLayerInterface vik_trw_layer_interface = {
429 "TrackWaypoint",
5bfafde9 430 &viktrwlayer_pixbuf,
50a14534
EB
431
432 trw_layer_tools,
433 sizeof(trw_layer_tools) / sizeof(VikToolInterface),
434
435 trw_layer_params,
436 NUM_PARAMS,
437 params_groups, /* params_groups */
438 sizeof(params_groups)/sizeof(params_groups[0]), /* number of groups */
439
5a4a28bf
QT
440 VIK_MENU_ITEM_ALL,
441
50a14534
EB
442 (VikLayerFuncCreate) vik_trw_layer_create,
443 (VikLayerFuncRealize) vik_trw_layer_realize,
444 (VikLayerFuncPostRead) trw_layer_verify_thumbnails,
445 (VikLayerFuncFree) vik_trw_layer_free,
446
447 (VikLayerFuncProperties) NULL,
448 (VikLayerFuncDraw) vik_trw_layer_draw,
449 (VikLayerFuncChangeCoordMode) trw_layer_change_coord_mode,
450
20c7a3a0
QT
451 (VikLayerFuncSetMenuItemsSelection) vik_trw_layer_set_menu_selection,
452 (VikLayerFuncGetMenuItemsSelection) vik_trw_layer_get_menu_selection,
453
50a14534
EB
454 (VikLayerFuncAddMenuItems) vik_trw_layer_add_menu_items,
455 (VikLayerFuncSublayerAddMenuItems) vik_trw_layer_sublayer_add_menu_items,
456
457 (VikLayerFuncSublayerRenameRequest) vik_trw_layer_sublayer_rename_request,
458 (VikLayerFuncSublayerToggleVisible) vik_trw_layer_sublayer_toggle_visible,
c7060c4e 459 (VikLayerFuncSublayerTooltip) trw_layer_sublayer_tooltip,
cb89c5a5 460 (VikLayerFuncLayerTooltip) trw_layer_layer_tooltip,
a5dcfdb7 461 (VikLayerFuncLayerSelected) vik_trw_layer_selected,
50a14534 462
911400b5
AF
463 (VikLayerFuncMarshall) trw_layer_marshall,
464 (VikLayerFuncUnmarshall) trw_layer_unmarshall,
50a14534
EB
465
466 (VikLayerFuncSetParam) trw_layer_set_param,
467 (VikLayerFuncGetParam) trw_layer_get_param,
468
469 (VikLayerFuncReadFileData) a_gpspoint_read_file,
470 (VikLayerFuncWriteFileData) a_gpspoint_write_file,
471
33534cd8 472 (VikLayerFuncDeleteItem) trw_layer_del_item,
d5874ef9 473 (VikLayerFuncCutItem) trw_layer_cut_item,
50a14534
EB
474 (VikLayerFuncCopyItem) trw_layer_copy_item,
475 (VikLayerFuncPasteItem) trw_layer_paste_item,
476 (VikLayerFuncFreeCopiedItem) trw_layer_free_copied_item,
70a23263
AF
477
478 (VikLayerFuncDragDropRequest) trw_layer_drag_drop_request,
77ad64fa
RN
479
480 (VikLayerFuncSelectClick) trw_layer_select_click,
08f14055
RN
481 (VikLayerFuncSelectMove) trw_layer_select_move,
482 (VikLayerFuncSelectRelease) trw_layer_select_release,
e46f259a 483 (VikLayerFuncSelectedViewportMenu) trw_layer_show_selected_viewport_menu,
50a14534
EB
484};
485
486/* for copy & paste (I think?) */
487typedef struct {
ddc47a46
AF
488 guint len;
489 guint8 data[0];
490 // gchar *name;
491 // VikWaypoint *wp;
492} FlatItem;
50a14534
EB
493
494GType vik_trw_layer_get_type ()
495{
496 static GType vtl_type = 0;
497
498 if (!vtl_type)
499 {
500 static const GTypeInfo vtl_info =
501 {
502 sizeof (VikTrwLayerClass),
503 NULL, /* base_init */
504 NULL, /* base_finalize */
505 NULL, /* class init */
506 NULL, /* class_finalize */
507 NULL, /* class_data */
508 sizeof (VikTrwLayer),
509 0,
510 NULL /* instance init */
511 };
512 vtl_type = g_type_register_static ( VIK_LAYER_TYPE, "VikTrwLayer", &vtl_info, 0 );
513 }
514
515 return vtl_type;
516}
517
33534cd8
AF
518static void trw_layer_del_item ( VikTrwLayer *vtl, gint subtype, gpointer sublayer )
519{
6bb72350 520 static gpointer pass_along[6];
33534cd8
AF
521 if (!sublayer) {
522 return;
523 }
524
525 pass_along[0] = vtl;
526 pass_along[1] = NULL;
dc2c040e 527 pass_along[2] = GINT_TO_POINTER (subtype);
33534cd8
AF
528 pass_along[3] = sublayer;
529 pass_along[4] = NULL;
6bb72350 530 pass_along[5] = NULL;
33534cd8
AF
531
532 trw_layer_delete_item ( pass_along );
533}
50a14534 534
d5874ef9
RN
535static void trw_layer_cut_item ( VikTrwLayer *vtl, gint subtype, gpointer sublayer )
536{
6bb72350 537 static gpointer pass_along[6];
d5874ef9
RN
538 if (!sublayer) {
539 return;
540 }
541
542 pass_along[0] = vtl;
543 pass_along[1] = NULL;
544 pass_along[2] = GINT_TO_POINTER (subtype);
545 pass_along[3] = sublayer;
546 pass_along[4] = NULL;
6bb72350 547 pass_along[5] = NULL;
d5874ef9
RN
548
549 trw_layer_copy_item_cb(pass_along);
550 trw_layer_cut_item_cb(pass_along);
551}
552
6bb72350 553static void trw_layer_copy_item_cb ( gpointer pass_along[6])
2cebc318
QT
554{
555 VikTrwLayer *vtl = VIK_TRW_LAYER(pass_along[0]);
dc2c040e 556 gint subtype = GPOINTER_TO_INT (pass_along[2]);
2cebc318
QT
557 gpointer * sublayer = pass_along[3];
558 guint8 *data = NULL;
559 guint len;
560
561 trw_layer_copy_item( vtl, subtype, sublayer, &data, &len);
562
563 if (data) {
564 a_clipboard_copy( VIK_CLIPBOARD_DATA_SUBLAYER, VIK_LAYER_TRW,
565 subtype, len, data);
566 }
567}
568
6bb72350 569static void trw_layer_cut_item_cb ( gpointer pass_along[6])
2cebc318
QT
570{
571 trw_layer_copy_item_cb(pass_along);
572 trw_layer_delete_item(pass_along);
573}
574
ddc47a46 575static void trw_layer_copy_item ( VikTrwLayer *vtl, gint subtype, gpointer sublayer, guint8 **item, guint *len )
50a14534 576{
ddc47a46
AF
577 FlatItem *fi;
578 guint8 *id;
579 guint il;
580
581 if (!sublayer) {
582 *item = NULL;
583 return;
50a14534 584 }
ddc47a46
AF
585
586 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT )
50a14534 587 {
ddc47a46
AF
588 vik_waypoint_marshall ( g_hash_table_lookup ( vtl->waypoints, sublayer ), &id, &il );
589 } else {
590 vik_track_marshall ( g_hash_table_lookup ( vtl->tracks, sublayer ), &id, &il );
50a14534 591 }
ddc47a46
AF
592
593 *len = sizeof(FlatItem) + strlen(sublayer) + 1 + il;
594 fi = g_malloc ( *len );
595 fi->len = strlen(sublayer) + 1;
596 memcpy(fi->data, sublayer, fi->len);
597 memcpy(fi->data + fi->len, id, il);
598 g_free(id);
599 *item = (guint8 *)fi;
50a14534
EB
600}
601
ddc47a46 602static gboolean trw_layer_paste_item ( VikTrwLayer *vtl, gint subtype, guint8 *item, guint len )
50a14534 603{
ddc47a46
AF
604 FlatItem *fi = (FlatItem *) item;
605
606 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT && fi )
50a14534 607 {
ddc47a46
AF
608 VikWaypoint *w;
609 gchar *name;
610
611 name = get_new_unique_sublayer_name(vtl, VIK_TRW_LAYER_SUBLAYER_WAYPOINT, (gchar *)fi->data);
612 w = vik_waypoint_unmarshall(fi->data + fi->len, len - sizeof(*fi) - fi->len);
613 vik_trw_layer_add_waypoint ( vtl, name, w );
614 waypoint_convert(name, w, &vtl->coord_mode);
58cd1c43
RN
615 // Consider if redraw necessary for the new item
616 if ( vtl->vl.visible && vtl->waypoints_visible && w->visible )
617 vik_layer_emit_update ( VIK_LAYER(vtl) );
50a14534
EB
618 return TRUE;
619 }
ddc47a46 620 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK && fi )
50a14534 621 {
ddc47a46
AF
622 VikTrack *t;
623 gchar *name;
624 name = get_new_unique_sublayer_name(vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, (gchar *)fi->data);
625 t = vik_track_unmarshall(fi->data + fi->len, len - sizeof(*fi) - fi->len);
626 vik_trw_layer_add_track ( vtl, name, t );
627 track_convert(name, t, &vtl->coord_mode);
58cd1c43
RN
628 // Consider if redraw necessary for the new item
629 if ( vtl->vl.visible && vtl->tracks_visible && t->visible )
630 vik_layer_emit_update ( VIK_LAYER(vtl) );
50a14534
EB
631 return TRUE;
632 }
633 return FALSE;
634}
635
636static void trw_layer_free_copied_item ( gint subtype, gpointer item )
637{
ddc47a46
AF
638 if (item) {
639 g_free(item);
50a14534
EB
640 }
641}
642
158b3642 643static gboolean trw_layer_set_param ( VikTrwLayer *vtl, guint16 id, VikLayerParamData data, VikViewport *vp, gboolean is_file_operation )
50a14534
EB
644{
645 switch ( id )
646 {
647 case PARAM_TV: vtl->tracks_visible = data.b; break;
648 case PARAM_WV: vtl->waypoints_visible = data.b; break;
649 case PARAM_DM: vtl->drawmode = data.u; break;
650 case PARAM_DP: vtl->drawpoints = data.b; break;
b42a25ba
EB
651 case PARAM_DE: vtl->drawelevation = data.b; break;
652 case PARAM_DS: vtl->drawstops = data.b; break;
50a14534 653 case PARAM_DL: vtl->drawlines = data.b; break;
b42a25ba
EB
654 case PARAM_SL: if ( data.u >= MIN_STOP_LENGTH && data.u <= MAX_STOP_LENGTH )
655 vtl->stop_length = data.u;
656 break;
657 case PARAM_EF: if ( data.u >= 1 && data.u <= 100 )
658 vtl->elevation_factor = data.u;
659 break;
50a14534
EB
660 case PARAM_LT: if ( data.u > 0 && data.u < 15 && data.u != vtl->line_thickness )
661 {
662 vtl->line_thickness = data.u;
663 trw_layer_new_track_gcs ( vtl, vp );
664 }
665 break;
f53c5b13 666 case PARAM_BLT: if ( data.u >= 0 && data.u <= 8 && data.u != vtl->bg_line_thickness )
50a14534
EB
667 {
668 vtl->bg_line_thickness = data.u;
669 trw_layer_new_track_gcs ( vtl, vp );
670 }
671 break;
72ff842f
RN
672 case PARAM_VMIN:
673 {
674 /* Convert to store internally
675 NB file operation always in internal units (metres per second) */
676 vik_units_speed_t speed_units = a_vik_get_units_speed ();
677 if ( is_file_operation || speed_units == VIK_UNITS_SPEED_METRES_PER_SECOND )
678 vtl->velocity_min = data.d;
679 else if ( speed_units == VIK_UNITS_SPEED_KILOMETRES_PER_HOUR )
680 vtl->velocity_min = VIK_KPH_TO_MPS(data.d);
681 else if ( speed_units == VIK_UNITS_SPEED_MILES_PER_HOUR )
682 vtl->velocity_min = VIK_MPH_TO_MPS(data.d);
683 else
684 /* Knots */
685 vtl->velocity_min = VIK_KNOTS_TO_MPS(data.d);
686 break;
687 }
688 case PARAM_VMAX:
689 {
690 /* Convert to store internally
691 NB file operation always in internal units (metres per second) */
692 vik_units_speed_t speed_units = a_vik_get_units_speed ();
693 if ( is_file_operation || speed_units == VIK_UNITS_SPEED_METRES_PER_SECOND )
694 vtl->velocity_max = data.d;
695 else if ( speed_units == VIK_UNITS_SPEED_KILOMETRES_PER_HOUR )
696 vtl->velocity_max = VIK_KPH_TO_MPS(data.d);
697 else if ( speed_units == VIK_UNITS_SPEED_MILES_PER_HOUR )
698 vtl->velocity_max = VIK_MPH_TO_MPS(data.d);
699 else
700 /* Knots */
701 vtl->velocity_max = VIK_KNOTS_TO_MPS(data.d);
702 break;
703 }
50a14534
EB
704 case PARAM_TBGC: gdk_gc_set_rgb_fg_color(vtl->track_bg_gc, &(data.c)); break;
705 case PARAM_DLA: vtl->drawlabels = data.b; break;
706 case PARAM_DI: vtl->drawimages = data.b; break;
707 case PARAM_IS: if ( data.u != vtl->image_size )
708 {
709 vtl->image_size = data.u;
710 g_list_foreach ( vtl->image_cache->head, (GFunc) cached_pixbuf_free, NULL );
711 g_queue_free ( vtl->image_cache );
712 vtl->image_cache = g_queue_new ();
713 }
714 break;
715 case PARAM_IA: vtl->image_alpha = data.u; break;
716 case PARAM_ICS: vtl->image_cache_size = data.u;
717 while ( vtl->image_cache->length > vtl->image_cache_size ) /* if shrinking cache_size, free pixbuf ASAP */
718 cached_pixbuf_free ( g_queue_pop_tail ( vtl->image_cache ) );
719 break;
720 case PARAM_WPC: gdk_gc_set_rgb_fg_color(vtl->waypoint_gc, &(data.c)); break;
721 case PARAM_WPTC: gdk_gc_set_rgb_fg_color(vtl->waypoint_text_gc, &(data.c)); break;
722 case PARAM_WPBC: gdk_gc_set_rgb_fg_color(vtl->waypoint_bg_gc, &(data.c)); break;
723 case PARAM_WPBA: gdk_gc_set_function(vtl->waypoint_bg_gc, data.b ? GDK_AND : GDK_COPY ); break;
724 case PARAM_WPSYM: if ( data.u < WP_NUM_SYMBOLS ) vtl->wp_symbol = data.u; break;
725 case PARAM_WPSIZE: if ( data.u > 0 && data.u <= 64 ) vtl->wp_size = data.u; break;
ea3933fc 726 case PARAM_WPSYMS: vtl->wp_draw_symbols = data.b; break;
50a14534
EB
727 }
728 return TRUE;
729}
730
158b3642 731static VikLayerParamData trw_layer_get_param ( VikTrwLayer *vtl, guint16 id, gboolean is_file_operation )
50a14534
EB
732{
733 VikLayerParamData rv;
734 switch ( id )
735 {
736 case PARAM_TV: rv.b = vtl->tracks_visible; break;
737 case PARAM_WV: rv.b = vtl->waypoints_visible; break;
738 case PARAM_DM: rv.u = vtl->drawmode; break;
739 case PARAM_DP: rv.b = vtl->drawpoints; break;
b42a25ba
EB
740 case PARAM_DE: rv.b = vtl->drawelevation; break;
741 case PARAM_EF: rv.u = vtl->elevation_factor; break;
742 case PARAM_DS: rv.b = vtl->drawstops; break;
743 case PARAM_SL: rv.u = vtl->stop_length; break;
50a14534
EB
744 case PARAM_DL: rv.b = vtl->drawlines; break;
745 case PARAM_LT: rv.u = vtl->line_thickness; break;
746 case PARAM_BLT: rv.u = vtl->bg_line_thickness; break;
72ff842f
RN
747 case PARAM_VMIN:
748 {
749 /* Convert to store internally
750 NB file operation always in internal units (metres per second) */
751 vik_units_speed_t speed_units = a_vik_get_units_speed ();
752 if ( is_file_operation || speed_units == VIK_UNITS_SPEED_METRES_PER_SECOND )
753 rv.d = vtl->velocity_min;
754 else if ( speed_units == VIK_UNITS_SPEED_KILOMETRES_PER_HOUR )
755 rv.d = VIK_MPS_TO_KPH(vtl->velocity_min);
756 else if ( speed_units == VIK_UNITS_SPEED_MILES_PER_HOUR )
757 rv.d = VIK_MPS_TO_MPH(vtl->velocity_min);
758 else
759 /* Knots */
760 rv.d = VIK_MPS_TO_KNOTS(vtl->velocity_min);
761 break;
762 }
763 case PARAM_VMAX:
764 {
765 /* Convert to store internally
766 NB file operation always in internal units (metres per second) */
767 vik_units_speed_t speed_units = a_vik_get_units_speed ();
768 if ( is_file_operation || speed_units == VIK_UNITS_SPEED_METRES_PER_SECOND )
769 rv.d = vtl->velocity_max;
770 else if ( speed_units == VIK_UNITS_SPEED_KILOMETRES_PER_HOUR )
771 rv.d = VIK_MPS_TO_KPH(vtl->velocity_max);
772 else if ( speed_units == VIK_UNITS_SPEED_MILES_PER_HOUR )
773 rv.d = VIK_MPS_TO_MPH(vtl->velocity_max);
774 else
775 /* Knots */
776 rv.d = VIK_MPS_TO_KNOTS(vtl->velocity_max);
777 break;
778 }
50a14534
EB
779 case PARAM_DLA: rv.b = vtl->drawlabels; break;
780 case PARAM_DI: rv.b = vtl->drawimages; break;
781 case PARAM_TBGC: vik_gc_get_fg_color(vtl->track_bg_gc, &(rv.c)); break;
782 case PARAM_IS: rv.u = vtl->image_size; break;
783 case PARAM_IA: rv.u = vtl->image_alpha; break;
784 case PARAM_ICS: rv.u = vtl->image_cache_size; break;
785 case PARAM_WPC: vik_gc_get_fg_color(vtl->waypoint_gc, &(rv.c)); break;
786 case PARAM_WPTC: vik_gc_get_fg_color(vtl->waypoint_text_gc, &(rv.c)); break;
787 case PARAM_WPBC: vik_gc_get_fg_color(vtl->waypoint_bg_gc, &(rv.c)); break;
788 case PARAM_WPBA: rv.b = (vik_gc_get_function(vtl->waypoint_bg_gc)==GDK_AND); break;
789 case PARAM_WPSYM: rv.u = vtl->wp_symbol; break;
790 case PARAM_WPSIZE: rv.u = vtl->wp_size; break;
ea3933fc 791 case PARAM_WPSYMS: rv.b = vtl->wp_draw_symbols; break;
50a14534
EB
792 }
793 return rv;
794}
795
911400b5
AF
796static void trw_layer_marshall( VikTrwLayer *vtl, guint8 **data, gint *len )
797{
4efcd55c
RN
798 guint8 *pd;
799 gchar *dd;
800 gsize dl;
801 gint pl;
911400b5
AF
802 gchar *tmpname;
803 FILE *f;
804
805 *data = NULL;
806
807 if ((f = fdopen(g_file_open_tmp (NULL, &tmpname, NULL), "r+"))) {
808 a_gpx_write_file(vtl, f);
809 vik_layer_marshall_params(VIK_LAYER(vtl), &pd, &pl);
810 fclose(f);
8c060406 811 f = NULL;
4efcd55c 812 g_file_get_contents(tmpname, &dd, &dl, NULL);
911400b5
AF
813 *len = sizeof(pl) + pl + dl;
814 *data = g_malloc(*len);
815 memcpy(*data, &pl, sizeof(pl));
816 memcpy(*data + sizeof(pl), pd, pl);
817 memcpy(*data + sizeof(pl) + pl, dd, dl);
818
819 g_free(pd);
820 g_free(dd);
8c060406 821 g_remove(tmpname);
911400b5
AF
822 g_free(tmpname);
823 }
824}
825
28612684 826static VikTrwLayer *trw_layer_unmarshall( guint8 *data, gint len, VikViewport *vvp )
911400b5
AF
827{
828 VikTrwLayer *rv = VIK_TRW_LAYER(vik_layer_create ( VIK_LAYER_TRW, vvp, NULL, FALSE ));
28612684 829 gint pl;
911400b5
AF
830 gchar *tmpname;
831 FILE *f;
832
833
834 memcpy(&pl, data, sizeof(pl));
835 data += sizeof(pl);
836 vik_layer_unmarshall_params ( VIK_LAYER(rv), data, pl, vvp );
837 data += pl;
838
839 if (!(f = fdopen(g_file_open_tmp (NULL, &tmpname, NULL), "r+"))) {
4258f4e2 840 g_critical("couldn't open temp file");
911400b5
AF
841 exit(1);
842 }
843 fwrite(data, len - pl - sizeof(pl), 1, f);
844 rewind(f);
845 a_gpx_read_file(rv, f);
846 fclose(f);
8c060406
MA
847 f = NULL;
848 g_remove(tmpname);
911400b5
AF
849 g_free(tmpname);
850 return rv;
851}
852
753ec753 853static GList * str_array_to_glist(gchar* data[])
267b6db5
QT
854{
855 GList *gl = NULL;
856 gpointer * p;
0654760a 857 for (p = (gpointer)data; *p; p++)
267b6db5
QT
858 gl = g_list_prepend(gl, *p);
859 return(g_list_reverse(gl));
860}
861
8499a412
QT
862static gboolean strcase_equal(gconstpointer s1, gconstpointer s2)
863{
864 return (strcasecmp(s1, s2) == 0);
865}
866
867static guint strcase_hash(gconstpointer v)
868{
869 /* 31 bit hash function */
870 int i;
871 const gchar *t = v;
872 gchar s[128]; /* malloc is too slow for reading big files */
873 gchar *p = s;
874
875 for (i = 0; (i < (sizeof(s)- 1)) && t[i]; i++)
876 p[i] = toupper(t[i]);
877 p[i] = '\0';
878
879 p = s;
880 guint32 h = *p;
881 if (h) {
882 for (p += 1; *p != '\0'; p++)
883 h = (h << 5) - h + *p;
884 }
885
886 return h;
887}
888
50a14534
EB
889VikTrwLayer *vik_trw_layer_new ( gint drawmode )
890{
267b6db5 891 if (trw_layer_params[PARAM_DM].widget_data == NULL)
753ec753 892 trw_layer_params[PARAM_DM].widget_data = str_array_to_glist(params_drawmodes);
267b6db5 893 if (trw_layer_params[PARAM_WPSYM].widget_data == NULL)
753ec753 894 trw_layer_params[PARAM_WPSYM].widget_data = str_array_to_glist(params_wpsymbols);
267b6db5 895
50a14534
EB
896 VikTrwLayer *rv = VIK_TRW_LAYER ( g_object_new ( VIK_TRW_LAYER_TYPE, NULL ) );
897 vik_layer_init ( VIK_LAYER(rv), VIK_LAYER_TRW );
898
8499a412 899 rv->waypoints = g_hash_table_new_full ( strcase_hash, strcase_equal, g_free, (GDestroyNotify) vik_waypoint_free );
50a14534
EB
900 rv->tracks = g_hash_table_new_full ( g_str_hash, g_str_equal, g_free, (GDestroyNotify) vik_track_free );
901 rv->tracks_iters = g_hash_table_new_full ( g_str_hash, g_str_equal, NULL, g_free );
8499a412 902 rv->waypoints_iters = g_hash_table_new_full ( strcase_hash, strcase_equal, NULL, g_free );
50a14534
EB
903
904 /* TODO: constants at top */
905 rv->waypoints_visible = rv->tracks_visible = TRUE;
906 rv->drawmode = drawmode;
907 rv->drawpoints = TRUE;
b42a25ba
EB
908 rv->drawstops = FALSE;
909 rv->drawelevation = FALSE;
910 rv->elevation_factor = 30;
911 rv->stop_length = 60;
50a14534
EB
912 rv->drawlines = TRUE;
913 rv->wplabellayout = NULL;
914 rv->wp_right_click_menu = NULL;
8bd81489 915 rv->track_right_click_menu = NULL;
50a14534
EB
916 rv->waypoint_gc = NULL;
917 rv->waypoint_text_gc = NULL;
918 rv->waypoint_bg_gc = NULL;
919 rv->track_gc = NULL;
920 rv->velocity_max = 5.0;
921 rv->velocity_min = 0.0;
922 rv->line_thickness = 1;
b42a25ba 923 rv->bg_line_thickness = 0;
50a14534
EB
924 rv->current_wp = NULL;
925 rv->current_wp_name = NULL;
926 rv->current_track = NULL;
927 rv->current_tpl = NULL;
928 rv->current_tp_track_name = NULL;
929 rv->moving_tp = FALSE;
930 rv->moving_wp = FALSE;
1eef1bde 931
7b203521
EB
932 rv->ct_sync_done = TRUE;
933
1eef1bde 934 rv->magic_scissors_started = FALSE;
bddd2056
EB
935 rv->magic_scissors_check_added_track = FALSE;
936 rv->magic_scissors_added_track_name = NULL;
937 rv->magic_scissors_current_track = NULL;
938 rv->magic_scissors_append = FALSE;
1eef1bde 939
50a14534
EB
940 rv->waypoint_rightclick = FALSE;
941 rv->last_tpl = NULL;
942 rv->last_tp_track_name = NULL;
943 rv->tpwin = NULL;
944 rv->image_cache = g_queue_new();
945 rv->image_size = 64;
946 rv->image_alpha = 255;
947 rv->image_cache_size = 300;
948 rv->drawimages = TRUE;
949 rv->drawlabels = TRUE;
950 return rv;
951}
952
953
954void vik_trw_layer_free ( VikTrwLayer *trwlayer )
955{
956 g_hash_table_destroy(trwlayer->waypoints);
957 g_hash_table_destroy(trwlayer->tracks);
958
959 /* ODC: replace with GArray */
960 trw_layer_free_track_gcs ( trwlayer );
961
962 if ( trwlayer->wp_right_click_menu )
4f14a010 963 g_object_ref_sink ( G_OBJECT(trwlayer->wp_right_click_menu) );
50a14534 964
8bd81489
RN
965 if ( trwlayer->track_right_click_menu )
966 gtk_object_sink ( GTK_OBJECT(trwlayer->track_right_click_menu) );
967
50a14534
EB
968 if ( trwlayer->wplabellayout != NULL)
969 g_object_unref ( G_OBJECT ( trwlayer->wplabellayout ) );
970
971 if ( trwlayer->waypoint_gc != NULL )
972 g_object_unref ( G_OBJECT ( trwlayer->waypoint_gc ) );
973
974 if ( trwlayer->waypoint_text_gc != NULL )
975 g_object_unref ( G_OBJECT ( trwlayer->waypoint_text_gc ) );
976
977 if ( trwlayer->waypoint_bg_gc != NULL )
978 g_object_unref ( G_OBJECT ( trwlayer->waypoint_bg_gc ) );
979
980 if ( trwlayer->waypoint_font != NULL )
981 gdk_font_unref ( trwlayer->waypoint_font );
982
983 if ( trwlayer->tpwin != NULL )
984 gtk_widget_destroy ( GTK_WIDGET(trwlayer->tpwin) );
985
986 g_list_foreach ( trwlayer->image_cache->head, (GFunc) cached_pixbuf_free, NULL );
987 g_queue_free ( trwlayer->image_cache );
988}
989
990static void init_drawing_params ( struct DrawingParams *dp, VikViewport *vp )
991{
992 dp->vp = vp;
993 dp->xmpp = vik_viewport_get_xmpp ( vp );
994 dp->ympp = vik_viewport_get_ympp ( vp );
995 dp->width = vik_viewport_get_width ( vp );
996 dp->height = vik_viewport_get_height ( vp );
997 dp->center = vik_viewport_get_center ( vp );
998 dp->one_zone = vik_viewport_is_one_zone ( vp ); /* false if some other projection besides UTM */
999 dp->lat_lon = vik_viewport_get_coord_mode ( vp ) == VIK_COORD_LATLON;
1000
1001 if ( dp->one_zone )
1002 {
1003 gint w2, h2;
1004 w2 = dp->xmpp * (dp->width / 2) + 1600 / dp->xmpp;
1005 h2 = dp->ympp * (dp->height / 2) + 1600 / dp->ympp;
1006 /* leniency -- for tracks. Obviously for waypoints this SHOULD be a lot smaller */
1007
1008 dp->ce1 = dp->center->east_west-w2;
1009 dp->ce2 = dp->center->east_west+w2;
1010 dp->cn1 = dp->center->north_south-h2;
1011 dp->cn2 = dp->center->north_south+h2;
1012 } else if ( dp->lat_lon ) {
1013 VikCoord upperleft, bottomright;
1014 /* quick & dirty calculation; really want to check all corners due to lat/lon smaller at top in northern hemisphere */
1015 /* this also DOESN'T WORK if you are crossing 180/-180 lon. I don't plan to in the near future... */
1016 vik_viewport_screen_to_coord ( vp, -500, -500, &upperleft );
1017 vik_viewport_screen_to_coord ( vp, dp->width+500, dp->height+500, &bottomright );
1018 dp->ce1 = upperleft.east_west;
1019 dp->ce2 = bottomright.east_west;
1020 dp->cn1 = bottomright.north_south;
1021 dp->cn2 = upperleft.north_south;
1022 }
1023
1024 dp->track_gc_iter = 0;
1025}
1026
1027static gint calculate_velocity ( VikTrwLayer *vtl, VikTrackpoint *tp1, VikTrackpoint *tp2 )
1028{
1029 static gdouble rv = 0;
1030 if ( tp1->has_timestamp && tp2->has_timestamp )
1031 {
1032 rv = ( vik_coord_diff ( &(tp1->coord), &(tp2->coord) )
1033 / (tp1->timestamp - tp2->timestamp) ) - vtl->velocity_min;
1034
1035 if ( rv < 0 )
1036 return VIK_TRW_LAYER_TRACK_GC_MIN;
1037 else if ( vtl->velocity_min >= vtl->velocity_max )
1038 return VIK_TRW_LAYER_TRACK_GC_MAX;
1039
1040 rv *= (VIK_TRW_LAYER_TRACK_GC_RATES / (vtl->velocity_max - vtl->velocity_min));
1041
1042 if ( rv >= VIK_TRW_LAYER_TRACK_GC_MAX )
1043 return VIK_TRW_LAYER_TRACK_GC_MAX;
1044 return (gint) rv;
1045 }
1046 else
1047 return VIK_TRW_LAYER_TRACK_GC_BLACK;
1048}
1049
1050void draw_utm_skip_insignia ( VikViewport *vvp, GdkGC *gc, gint x, gint y )
1051{
1052 vik_viewport_draw_line ( vvp, gc, x+5, y, x-5, y );
1053 vik_viewport_draw_line ( vvp, gc, x, y+5, x, y-5 );
1054 vik_viewport_draw_line ( vvp, gc, x+5, y+5, x-5, y-5 );
1055 vik_viewport_draw_line ( vvp, gc, x+5, y-5, x-5, y+5 );
1056}
1057
1058static void trw_layer_draw_track ( const gchar *name, VikTrack *track, struct DrawingParams *dp, gboolean drawing_white_background )
1059{
1060 /* TODO: this function is a mess, get rid of any redundancy */
1061 GList *list = track->trackpoints;
8e9c992d 1062 GdkGC *main_gc;
50a14534
EB
1063 gboolean useoldvals = TRUE;
1064
1065 gboolean drawpoints;
b42a25ba
EB
1066 gboolean drawstops;
1067 gboolean drawelevation;
941aa6e9 1068 gdouble min_alt, max_alt, alt_diff = 0;
50a14534
EB
1069
1070 const guint8 tp_size_reg = 2;
1071 const guint8 tp_size_cur = 4;
1072 guint8 tp_size;
1073
b42a25ba
EB
1074 if ( dp->vtl->drawelevation )
1075 {
1076 /* assume if it has elevation at the beginning, it has it throughout. not ness a true good assumption */
1077 if ( ( drawelevation = vik_track_get_minmax_alt ( track, &min_alt, &max_alt ) ) )
1078 alt_diff = max_alt - min_alt;
1079 }
1080
50a14534
EB
1081 if ( ! track->visible )
1082 return;
1083
1084 /* admittedly this is not an efficient way to do it because we go through the whole GC thing all over... */
1085 if ( dp->vtl->bg_line_thickness && !drawing_white_background )
1086 trw_layer_draw_track ( name, track, dp, TRUE );
1087
1088 if ( drawing_white_background )
b42a25ba
EB
1089 drawpoints = drawstops = FALSE;
1090 else {
50a14534 1091 drawpoints = dp->vtl->drawpoints;
b42a25ba
EB
1092 drawstops = dp->vtl->drawstops;
1093 }
50a14534 1094
a5dcfdb7 1095 /* Current track - used for creation */
8e9c992d
EB
1096 if ( track == dp->vtl->current_track )
1097 main_gc = dp->vtl->current_track_gc;
a5dcfdb7
RN
1098 else {
1099 if ( vik_viewport_get_draw_highlight ( dp->vp ) ) {
1100 /* Draw all tracks of the layer in special colour */
1101 /* if track is member of selected layer or is the current selected track
1102 then draw in the highlight colour.
1103 NB this supercedes the drawmode */
1104 if ( dp->vtl && ( ( dp->vtl == vik_window_get_selected_trw_layer ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(dp->vtl) ) ) ||
1105 ( dp->vtl->tracks == vik_window_get_selected_tracks ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(dp->vtl) ) ) ||
1106 track == vik_window_get_selected_track ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(dp->vtl) ) ) ) {
480fb7e1 1107 main_gc = vik_viewport_get_gc_highlight (dp->vp);
a5dcfdb7
RN
1108 }
1109 else {
1110 if ( dp->vtl->drawmode == DRAWMODE_ALL_BLACK )
1111 dp->track_gc_iter = VIK_TRW_LAYER_TRACK_GC_BLACK;
1112
1113 main_gc = g_array_index(dp->vtl->track_gc, GdkGC *, dp->track_gc_iter);
1114 }
1115 }
1116 else {
1117 if ( dp->vtl->drawmode == DRAWMODE_ALL_BLACK )
1118 dp->track_gc_iter = VIK_TRW_LAYER_TRACK_GC_BLACK;
1119
1120 main_gc = g_array_index(dp->vtl->track_gc, GdkGC *, dp->track_gc_iter);
1121 }
1122 }
8e9c992d 1123
50a14534
EB
1124 if (list) {
1125 int x, y, oldx, oldy;
1126 VikTrackpoint *tp = VIK_TRACKPOINT(list->data);
1127
1128 tp_size = (list == dp->vtl->current_tpl) ? tp_size_cur : tp_size_reg;
1129
1130 vik_viewport_coord_to_screen ( dp->vp, &(tp->coord), &x, &y );
1131
1132 if ( (drawpoints) && dp->track_gc_iter < VIK_TRW_LAYER_TRACK_GC )
1133 {
1134 GdkPoint trian[3] = { { x, y-(3*tp_size) }, { x-(2*tp_size), y+(2*tp_size) }, {x+(2*tp_size), y+(2*tp_size)} };
8e9c992d 1135 vik_viewport_draw_polygon ( dp->vp, main_gc, TRUE, trian, 3 );
50a14534
EB
1136 }
1137
1138 oldx = x;
1139 oldy = y;
1140
50a14534
EB
1141 while ((list = g_list_next(list)))
1142 {
1143 tp = VIK_TRACKPOINT(list->data);
1144 tp_size = (list == dp->vtl->current_tpl) ? tp_size_cur : tp_size_reg;
1145
1146 /* check some stuff -- but only if we're in UTM and there's only ONE ZONE; or lat lon */
1147 if ( (!dp->one_zone && !dp->lat_lon) || /* UTM & zones; do everything */
1148 ( ((!dp->one_zone) || tp->coord.utm_zone == dp->center->utm_zone) && /* only check zones if UTM & one_zone */
1149 tp->coord.east_west < dp->ce2 && tp->coord.east_west > dp->ce1 && /* both UTM and lat lon */
1150 tp->coord.north_south > dp->cn1 && tp->coord.north_south < dp->cn2 ) )
1151 {
1152 vik_viewport_coord_to_screen ( dp->vp, &(tp->coord), &x, &y );
1153
1154 if ( drawpoints && ! drawing_white_background )
1155 {
1156 if ( list->next ) {
8e9c992d 1157 vik_viewport_draw_rectangle ( dp->vp, main_gc, TRUE, x-tp_size, y-tp_size, 2*tp_size, 2*tp_size );
50a14534
EB
1158
1159 /* stops */
b42a25ba 1160 if ( drawstops && VIK_TRACKPOINT(list->next->data)->timestamp - VIK_TRACKPOINT(list->data)->timestamp > dp->vtl->stop_length )
50a14534
EB
1161 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 );
1162 }
1163 else
8e9c992d 1164 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 );
50a14534
EB
1165 }
1166
1167 if ((!tp->newsegment) && (dp->vtl->drawlines))
1168 {
1169 VikTrackpoint *tp2 = VIK_TRACKPOINT(list->prev->data);
1170
1171 /* UTM only: zone check */
1172 if ( drawpoints && dp->vtl->coord_mode == VIK_COORD_UTM && tp->coord.utm_zone != dp->center->utm_zone )
8e9c992d 1173 draw_utm_skip_insignia ( dp->vp, main_gc, x, y);
50a14534 1174
f66c1d0c 1175 if ( dp->vtl->drawmode == DRAWMODE_BY_VELOCITY ) {
50a14534 1176 dp->track_gc_iter = calculate_velocity ( dp->vtl, tp, tp2 );
f66c1d0c
RN
1177 main_gc = g_array_index(dp->vtl->track_gc, GdkGC *, dp->track_gc_iter);
1178 }
50a14534
EB
1179
1180 if (!useoldvals)
1181 vik_viewport_coord_to_screen ( dp->vp, &(tp2->coord), &oldx, &oldy );
1182
8c4f1350 1183 if ( drawing_white_background ) {
50a14534 1184 vik_viewport_draw_line ( dp->vp, dp->vtl->track_bg_gc, oldx, oldy, x, y);
8c4f1350
EB
1185 }
1186 else {
8c4f1350 1187
8e9c992d 1188 vik_viewport_draw_line ( dp->vp, main_gc, oldx, oldy, x, y);
b42a25ba
EB
1189 if ( dp->vtl->drawelevation && list && list->next && VIK_TRACKPOINT(list->next->data)->altitude != VIK_DEFAULT_ALTITUDE ) {
1190 GdkPoint tmp[4];
1191 #define FIXALTITUDE(what) ((VIK_TRACKPOINT((what))->altitude-min_alt)/alt_diff*DRAW_ELEVATION_FACTOR*dp->vtl->elevation_factor/dp->xmpp)
1192 if ( list && list->next && VIK_TRACKPOINT(list->next->data)->altitude != VIK_DEFAULT_ALTITUDE ) {
1193 tmp[0].x = oldx;
1194 tmp[0].y = oldy;
1195 tmp[1].x = oldx;
1196 tmp[1].y = oldy-FIXALTITUDE(list->data);
1197 tmp[2].x = x;
1198 tmp[2].y = y-FIXALTITUDE(list->next->data);
1199 tmp[3].x = x;
1200 tmp[3].y = y;
1201
1202 GdkGC *tmp_gc;
1203 if ( ((oldx - x) > 0 && (oldy - y) > 0) || ((oldx - x) < 0 && (oldy - y) < 0))
1204 tmp_gc = GTK_WIDGET(dp->vp)->style->light_gc[3];
1205 else
1206 tmp_gc = GTK_WIDGET(dp->vp)->style->dark_gc[0];
1207 vik_viewport_draw_polygon ( dp->vp, tmp_gc, TRUE, tmp, 4);
1208 }
8e9c992d 1209 vik_viewport_draw_line ( dp->vp, main_gc, oldx, oldy-FIXALTITUDE(list->data), x, y-FIXALTITUDE(list->next->data));
8c4f1350
EB
1210 }
1211 }
50a14534
EB
1212 }
1213
1214 oldx = x;
1215 oldy = y;
1216 useoldvals = TRUE;
1217 }
1218 else {
1219 if (useoldvals && dp->vtl->drawlines && (!tp->newsegment))
1220 {
1221 VikTrackpoint *tp2 = VIK_TRACKPOINT(list->prev->data);
1222 if ( dp->vtl->coord_mode != VIK_COORD_UTM || tp->coord.utm_zone == dp->center->utm_zone )
1223 {
1224 vik_viewport_coord_to_screen ( dp->vp, &(tp->coord), &x, &y );
f66c1d0c 1225 if ( dp->vtl->drawmode == DRAWMODE_BY_VELOCITY ) {
50a14534 1226 dp->track_gc_iter = calculate_velocity ( dp->vtl, tp, tp2 );
f66c1d0c
RN
1227 main_gc = g_array_index(dp->vtl->track_gc, GdkGC *, dp->track_gc_iter);
1228 }
50a14534
EB
1229
1230 if ( drawing_white_background )
1231 vik_viewport_draw_line ( dp->vp, dp->vtl->track_bg_gc, oldx, oldy, x, y);
1232 else
8e9c992d 1233 vik_viewport_draw_line ( dp->vp, main_gc, oldx, oldy, x, y);
50a14534
EB
1234 }
1235 else
1236 {
1237 vik_viewport_coord_to_screen ( dp->vp, &(tp2->coord), &x, &y );
8e9c992d 1238 draw_utm_skip_insignia ( dp->vp, main_gc, x, y );
50a14534
EB
1239 }
1240 }
1241 useoldvals = FALSE;
1242 }
1243 }
1244 }
1245 if ( dp->vtl->drawmode == DRAWMODE_BY_TRACK )
480fb7e1 1246 if ( ++(dp->track_gc_iter) >= VIK_TRW_LAYER_TRACK_GC )
50a14534
EB
1247 dp->track_gc_iter = 0;
1248}
1249
1250/* the only reason this exists is so that trw_layer_draw_track can first call itself to draw the white track background */
1251static void trw_layer_draw_track_cb ( const gchar *name, VikTrack *track, struct DrawingParams *dp )
1252{
1253 trw_layer_draw_track ( name, track, dp, FALSE );
1254}
1255
1256static void cached_pixbuf_free ( CachedPixbuf *cp )
1257{
1258 g_object_unref ( G_OBJECT(cp->pixbuf) );
1259 g_free ( cp->image );
1260}
1261
1262static gint cached_pixbuf_cmp ( CachedPixbuf *cp, const gchar *name )
1263{
1264 return strcmp ( cp->image, name );
1265}
1266
1267static void trw_layer_draw_waypoint ( const gchar *name, VikWaypoint *wp, struct DrawingParams *dp )
1268{
1269 if ( wp->visible )
51f0884d 1270 if ( (!dp->one_zone && !dp->lat_lon) || ( ( dp->lat_lon || wp->coord.utm_zone == dp->center->utm_zone ) &&
50a14534
EB
1271 wp->coord.east_west < dp->ce2 && wp->coord.east_west > dp->ce1 &&
1272 wp->coord.north_south > dp->cn1 && wp->coord.north_south < dp->cn2 ) )
1273 {
1274 gint x, y;
a7f9c01e 1275 GdkPixbuf *sym = NULL;
50a14534
EB
1276 vik_viewport_coord_to_screen ( dp->vp, &(wp->coord), &x, &y );
1277
1278 /* if in shrunken_cache, get that. If not, get and add to shrunken_cache */
1279
1280 if ( wp->image && dp->vtl->drawimages )
1281 {
1282 GdkPixbuf *pixbuf = NULL;
1283 GList *l;
1284
1285 if ( dp->vtl->image_alpha == 0)
1286 return;
1287
1288 l = g_list_find_custom ( dp->vtl->image_cache->head, wp->image, (GCompareFunc) cached_pixbuf_cmp );
1289 if ( l )
1290 pixbuf = ((CachedPixbuf *) l->data)->pixbuf;
1291 else
1292 {
1293 gchar *image = wp->image;
1294 GdkPixbuf *regularthumb = a_thumbnails_get ( wp->image );
1295 if ( ! regularthumb )
1296 {
1297 regularthumb = a_thumbnails_get_default (); /* cache one 'not yet loaded' for all thumbs not loaded */
1298 image = "\x12\x00"; /* this shouldn't occur naturally. */
1299 }
1300 if ( regularthumb )
1301 {
1302 CachedPixbuf *cp = NULL;
1303 cp = g_malloc ( sizeof ( CachedPixbuf ) );
1304 if ( dp->vtl->image_size == 128 )
1305 cp->pixbuf = regularthumb;
1306 else
1307 {
1308 cp->pixbuf = a_thumbnails_scale_pixbuf(regularthumb, dp->vtl->image_size, dp->vtl->image_size);
1309 g_assert ( cp->pixbuf );
1310 g_object_unref ( G_OBJECT(regularthumb) );
1311 }
1312 cp->image = g_strdup ( image );
1313
1314 /* needed so 'click picture' tool knows how big the pic is; we don't
1315 * store it in cp because they may have been freed already. */
1316 wp->image_width = gdk_pixbuf_get_width ( cp->pixbuf );
1317 wp->image_height = gdk_pixbuf_get_height ( cp->pixbuf );
1318
1319 g_queue_push_head ( dp->vtl->image_cache, cp );
1320 if ( dp->vtl->image_cache->length > dp->vtl->image_cache_size )
1321 cached_pixbuf_free ( g_queue_pop_tail ( dp->vtl->image_cache ) );
1322
1323 pixbuf = cp->pixbuf;
1324 }
1325 else
1326 {
1327 pixbuf = a_thumbnails_get_default (); /* thumbnail not yet loaded */
1328 }
1329 }
1330 if ( pixbuf )
1331 {
1332 gint w, h;
1333 w = gdk_pixbuf_get_width ( pixbuf );
1334 h = gdk_pixbuf_get_height ( pixbuf );
1335
1336 if ( x+(w/2) > 0 && y+(h/2) > 0 && x-(w/2) < dp->width && y-(h/2) < dp->height ) /* always draw within boundaries */
1337 {
a5dcfdb7
RN
1338 if ( vik_viewport_get_draw_highlight ( dp->vp ) ) {
1339 if ( dp->vtl == vik_window_get_selected_trw_layer ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(dp->vtl) ) ||
1340 dp->vtl->waypoints == vik_window_get_selected_waypoints ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(dp->vtl) ) ||
1341 wp == vik_window_get_selected_waypoint ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(dp->vtl) ) ) {
1342 // Highlighted - so draw a little border around the chosen one
1343 // single line seems a little weak so draw 2 of them
480fb7e1 1344 vik_viewport_draw_rectangle (dp->vp, vik_viewport_get_gc_highlight (dp->vp), FALSE,
a5dcfdb7 1345 x - (w/2) - 1, y - (h/2) - 1, w + 2, h + 2 );
480fb7e1 1346 vik_viewport_draw_rectangle (dp->vp, vik_viewport_get_gc_highlight (dp->vp), FALSE,
a5dcfdb7
RN
1347 x - (w/2) - 2, y - (h/2) - 2, w + 4, h + 4 );
1348 }
1349 }
50a14534
EB
1350 if ( dp->vtl->image_alpha == 255 )
1351 vik_viewport_draw_pixbuf ( dp->vp, pixbuf, 0, 0, x - (w/2), y - (h/2), w, h );
1352 else
1353 vik_viewport_draw_pixbuf_with_alpha ( dp->vp, pixbuf, dp->vtl->image_alpha, 0, 0, x - (w/2), y - (h/2), w, h );
1354 }
1355 return; /* if failed to draw picture, default to drawing regular waypoint (below) */
1356 }
1357 }
1358
1359 /* DRAW ACTUAL DOT */
ea3933fc 1360 if ( dp->vtl->wp_draw_symbols && wp->symbol && (sym = a_get_wp_sym(wp->symbol)) ) {
acaf7113
AF
1361 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 );
1362 }
1363 else if ( wp == dp->vtl->current_wp ) {
50a14534
EB
1364 switch ( dp->vtl->wp_symbol ) {
1365 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;
1366 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;
1367 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;
1368 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 );
1369 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 );
1370 }
1371 }
1372 else {
1373 switch ( dp->vtl->wp_symbol ) {
1374 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;
1375 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;
1376 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;
1377 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 );
1378 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;
1379 }
1380 }
1381
1382 if ( dp->vtl->drawlabels )
1383 {
1384 /* thanks to the GPSDrive people (Fritz Ganter et al.) for hints on this part ... yah, I'm too lazy to study documentation */
a7f9c01e 1385 gint label_x, label_y;
50a14534
EB
1386 gint width, height;
1387 pango_layout_set_text ( dp->vtl->wplabellayout, name, -1 );
1388 pango_layout_get_pixel_size ( dp->vtl->wplabellayout, &width, &height );
a7f9c01e
QT
1389 label_x = x - width/2;
1390 if (sym)
1391 label_y = y - height - 2 - gdk_pixbuf_get_height(sym)/2;
1392 else
1393 label_y = y - dp->vtl->wp_size - height - 2;
1394
480fb7e1 1395 /* if highlight mode on, then draw background text in highlight colour */
a5dcfdb7
RN
1396 if ( vik_viewport_get_draw_highlight ( dp->vp ) ) {
1397 if ( dp->vtl == vik_window_get_selected_trw_layer ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(dp->vtl) ) ||
1398 dp->vtl->waypoints == vik_window_get_selected_waypoints ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(dp->vtl) ) ||
1399 wp == vik_window_get_selected_waypoint ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(dp->vtl) ) )
480fb7e1 1400 vik_viewport_draw_rectangle ( dp->vp, vik_viewport_get_gc_highlight (dp->vp), TRUE, label_x - 1, label_y-1,width+2,height+2);
a5dcfdb7
RN
1401 else
1402 vik_viewport_draw_rectangle ( dp->vp, dp->vtl->waypoint_bg_gc, TRUE, label_x - 1, label_y-1,width+2,height+2);
1403 }
1404 else {
1405 vik_viewport_draw_rectangle ( dp->vp, dp->vtl->waypoint_bg_gc, TRUE, label_x - 1, label_y-1,width+2,height+2);
1406 }
a7f9c01e 1407 vik_viewport_draw_layout ( dp->vp, dp->vtl->waypoint_text_gc, label_x, label_y, dp->vtl->wplabellayout );
50a14534
EB
1408 }
1409 }
1410}
1411
1412void vik_trw_layer_draw ( VikTrwLayer *l, gpointer data )
1413{
1414 static struct DrawingParams dp;
1415 g_assert ( l != NULL );
1416
1417 init_drawing_params ( &dp, VIK_VIEWPORT(data) );
1418 dp.vtl = l;
1419
1420 if ( l->tracks_visible )
1421 g_hash_table_foreach ( l->tracks, (GHFunc) trw_layer_draw_track_cb, &dp );
1422
1423 if (l->waypoints_visible)
1424 g_hash_table_foreach ( l->waypoints, (GHFunc) trw_layer_draw_waypoint, &dp );
1425}
1426
1427static void trw_layer_free_track_gcs ( VikTrwLayer *vtl )
1428{
1429 int i;
1430 if ( vtl->track_bg_gc )
1431 {
1432 g_object_unref ( vtl->track_bg_gc );
1433 vtl->track_bg_gc = NULL;
1434 }
8e9c992d
EB
1435 if ( vtl->current_track_gc )
1436 {
1437 g_object_unref ( vtl->current_track_gc );
1438 vtl->current_track_gc = NULL;
1439 }
50a14534
EB
1440
1441 if ( ! vtl->track_gc )
1442 return;
1443 for ( i = vtl->track_gc->len - 1; i >= 0; i-- )
1444 g_object_unref ( g_array_index ( vtl->track_gc, GObject *, i ) );
1445 g_array_free ( vtl->track_gc, TRUE );
1446 vtl->track_gc = NULL;
1447}
1448
1449static void trw_layer_new_track_gcs ( VikTrwLayer *vtl, VikViewport *vp )
1450{
1451 GdkGC *gc[ VIK_TRW_LAYER_TRACK_GC ];
1452 gint width = vtl->line_thickness;
1453
1454 if ( vtl->track_gc )
1455 trw_layer_free_track_gcs ( vtl );
1456
1457 if ( vtl->track_bg_gc )
1458 g_object_unref ( vtl->track_bg_gc );
1459 vtl->track_bg_gc = vik_viewport_new_gc ( vp, "#FFFFFF", width + vtl->bg_line_thickness );
1460
8e9c992d
EB
1461 if ( vtl->current_track_gc )
1462 g_object_unref ( vtl->current_track_gc );
1463 vtl->current_track_gc = vik_viewport_new_gc ( vp, "#FF0000", 2 );
1464 gdk_gc_set_line_attributes ( vtl->current_track_gc, 2, GDK_LINE_ON_OFF_DASH, GDK_CAP_ROUND, GDK_JOIN_ROUND );
1465
50a14534
EB
1466 vtl->track_gc = g_array_sized_new ( FALSE, FALSE, sizeof ( GdkGC * ), VIK_TRW_LAYER_TRACK_GC );
1467
1468 gc[0] = vik_viewport_new_gc ( vp, "#2d870a", width ); /* below range */
1469
1470 gc[1] = vik_viewport_new_gc ( vp, "#0a8742", width );
1471 gc[2] = vik_viewport_new_gc ( vp, "#0a8783", width );
1472 gc[3] = vik_viewport_new_gc ( vp, "#0a4d87", width );
1473 gc[4] = vik_viewport_new_gc ( vp, "#05469f", width );
1474 gc[5] = vik_viewport_new_gc ( vp, "#1b059f", width );
1475 gc[6] = vik_viewport_new_gc ( vp, "#2d059f", width );
1476 gc[7] = vik_viewport_new_gc ( vp, "#4a059f", width );
1477 gc[8] = vik_viewport_new_gc ( vp, "#84059f", width );
1478 gc[9] = vik_viewport_new_gc ( vp, "#96059f", width );
1479 gc[10] = vik_viewport_new_gc ( vp, "#f22ef2", width );
1480
16b01243 1481 gc[11] = vik_viewport_new_gc ( vp, "#874200", width ); /* above range */
50a14534
EB
1482
1483 gc[12] = vik_viewport_new_gc ( vp, "#000000", width ); /* black / no speed data */
1484
1485 g_array_append_vals ( vtl->track_gc, gc, VIK_TRW_LAYER_TRACK_GC );
1486}
1487
1488VikTrwLayer *vik_trw_layer_create ( VikViewport *vp )
1489{
1490 VikTrwLayer *rv = vik_trw_layer_new ( 0 );
1491 PangoFontDescription *pfd;
1492 rv->wplabellayout = gtk_widget_create_pango_layout (GTK_WIDGET(vp), NULL);
1493 pfd = pango_font_description_from_string (WAYPOINT_FONT);
1494 pango_layout_set_font_description (rv->wplabellayout, pfd);
1495 /* freeing PangoFontDescription, cause it has been copied by prev. call */
1496 pango_font_description_free (pfd);
1497
1498 vik_layer_rename ( VIK_LAYER(rv), vik_trw_layer_interface.name );
1499
1500 trw_layer_new_track_gcs ( rv, vp );
1501
1502 rv->waypoint_gc = vik_viewport_new_gc ( vp, "#000000", 2 );
1503 rv->waypoint_text_gc = vik_viewport_new_gc ( vp, "#FFFFFF", 1 );
1504 rv->waypoint_bg_gc = vik_viewport_new_gc ( vp, "#8383C4", 1 );
1505 gdk_gc_set_function ( rv->waypoint_bg_gc, GDK_AND );
1506
1507 rv->waypoint_font = gdk_font_load ( "-*-helvetica-bold-r-normal-*-*-100-*-*-p-*-iso8859-1" );
1508
1509 rv->has_verified_thumbnails = FALSE;
1510 rv->wp_symbol = WP_SYMBOL_FILLED_SQUARE;
1511 rv->wp_size = 4;
ea3933fc 1512 rv->wp_draw_symbols = TRUE;
50a14534
EB
1513
1514 rv->coord_mode = vik_viewport_get_coord_mode ( vp );
1515
20c7a3a0
QT
1516 rv->menu_selection = vik_layer_get_interface(VIK_LAYER(rv)->type)->menu_items_selection;
1517
50a14534
EB
1518 return rv;
1519}
1520
6bb72350 1521static void trw_layer_realize_track ( gchar *name, VikTrack *track, gpointer pass_along[5] )
50a14534
EB
1522{
1523 GtkTreeIter *new_iter = g_malloc(sizeof(GtkTreeIter));
1524
1525#ifdef VIK_CONFIG_ALPHABETIZED_TRW
dc2c040e 1526 vik_treeview_add_sublayer_alphabetized ( (VikTreeview *) pass_along[3], (GtkTreeIter *) pass_along[0], (GtkTreeIter *) pass_along[1], name, pass_along[2], name, GPOINTER_TO_INT (pass_along[4]), NULL, TRUE, TRUE );
50a14534 1527#else
1d45914c 1528 vik_treeview_add_sublayer ( (VikTreeview *) pass_along[3], (GtkTreeIter *) pass_along[0], (GtkTreeIter *) pass_along[1], name, pass_along[2], name, GPOINTER_TO_INT (pass_along[4]), NULL, TRUE, TRUE );
50a14534
EB
1529#endif
1530
1531 *new_iter = *((GtkTreeIter *) pass_along[1]);
1532 g_hash_table_insert ( VIK_TRW_LAYER(pass_along[2])->tracks_iters, name, new_iter );
1533
1534 if ( ! track->visible )
1535 vik_treeview_item_set_visible ( (VikTreeview *) pass_along[3], (GtkTreeIter *) pass_along[1], FALSE );
1536}
1537
6bb72350 1538static void trw_layer_realize_waypoint ( gchar *name, VikWaypoint *wp, gpointer pass_along[5] )
50a14534
EB
1539{
1540 GtkTreeIter *new_iter = g_malloc(sizeof(GtkTreeIter));
1541#ifdef VIK_CONFIG_ALPHABETIZED_TRW
dc2c040e 1542 vik_treeview_add_sublayer_alphabetized ( (VikTreeview *) pass_along[3], (GtkTreeIter *) pass_along[0], (GtkTreeIter *) pass_along[1], name, pass_along[2], name, GPOINTER_TO_INT (pass_along[4]), NULL, TRUE, TRUE );
50a14534 1543#else
1d45914c 1544 vik_treeview_add_sublayer ( (VikTreeview *) pass_along[3], (GtkTreeIter *) pass_along[0], (GtkTreeIter *) pass_along[1], name, pass_along[2], name, GPOINTER_TO_INT (pass_along[4]), NULL, TRUE, TRUE );
50a14534
EB
1545#endif
1546
1547 *new_iter = *((GtkTreeIter *) pass_along[1]);
1548 g_hash_table_insert ( VIK_TRW_LAYER(pass_along[2])->waypoints_iters, name, new_iter );
1549
1550 if ( ! wp->visible )
1551 vik_treeview_item_set_visible ( (VikTreeview *) pass_along[3], (GtkTreeIter *) pass_along[1], FALSE );
1552}
1553
1554
1555void vik_trw_layer_realize ( VikTrwLayer *vtl, VikTreeview *vt, GtkTreeIter *layer_iter )
1556{
1557 GtkTreeIter iter2;
1558 gpointer pass_along[5] = { &(vtl->tracks_iter), &iter2, vtl, vt, (gpointer) VIK_TRW_LAYER_SUBLAYER_TRACK };
1559
1560#ifdef VIK_CONFIG_ALPHABETIZED_TRW
4c77d5e0 1561 vik_treeview_add_sublayer_alphabetized ( (VikTreeview *) vt, layer_iter, &(vtl->tracks_iter), _("Tracks"), vtl, NULL, VIK_TRW_LAYER_SUBLAYER_TRACKS, NULL, TRUE, FALSE );
50a14534 1562#else
4c77d5e0 1563 vik_treeview_add_sublayer ( (VikTreeview *) vt, layer_iter, &(vtl->tracks_iter), _("Tracks"), vtl, NULL, VIK_TRW_LAYER_SUBLAYER_TRACKS, NULL, TRUE, FALSE );
50a14534
EB
1564#endif
1565 if ( ! vtl->tracks_visible )
1566 vik_treeview_item_set_visible ( (VikTreeview *) vt, &(vtl->tracks_iter), FALSE );
1567
1568 g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_realize_track, pass_along );
1569
1570#ifdef VIK_CONFIG_ALPHABETIZED_TRW
4c77d5e0 1571 vik_treeview_add_sublayer_alphabetized ( (VikTreeview *) vt, layer_iter, &(vtl->waypoints_iter), _("Waypoints"), vtl, NULL, VIK_TRW_LAYER_SUBLAYER_WAYPOINTS, NULL, TRUE, FALSE );
50a14534 1572#else
4c77d5e0 1573 vik_treeview_add_sublayer ( (VikTreeview *) vt, layer_iter, &(vtl->waypoints_iter), _("Waypoints"), vtl, NULL, VIK_TRW_LAYER_SUBLAYER_WAYPOINTS, NULL, TRUE, FALSE );
50a14534
EB
1574#endif
1575
1576 if ( ! vtl->waypoints_visible )
1577 vik_treeview_item_set_visible ( (VikTreeview *) vt, &(vtl->waypoints_iter), FALSE );
1578
1579 pass_along[0] = &(vtl->waypoints_iter);
1580 pass_along[4] = (gpointer) VIK_TRW_LAYER_SUBLAYER_WAYPOINT;
1581
1582 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_realize_waypoint, pass_along );
1583
1584}
1585
1586gboolean vik_trw_layer_sublayer_toggle_visible ( VikTrwLayer *l, gint subtype, gpointer sublayer )
1587{
1588 switch ( subtype )
1589 {
1590 case VIK_TRW_LAYER_SUBLAYER_TRACKS: return (l->tracks_visible ^= 1);
1591 case VIK_TRW_LAYER_SUBLAYER_WAYPOINTS: return (l->waypoints_visible ^= 1);
1592 case VIK_TRW_LAYER_SUBLAYER_TRACK:
1593 {
1594 VikTrack *t = g_hash_table_lookup ( l->tracks, sublayer );
1595 if (t)
1596 return (t->visible ^= 1);
1597 else
1598 return TRUE;
1599 }
1600 case VIK_TRW_LAYER_SUBLAYER_WAYPOINT:
1601 {
1602 VikWaypoint *t = g_hash_table_lookup ( l->waypoints, sublayer );
1603 if (t)
1604 return (t->visible ^= 1);
1605 else
1606 return TRUE;
1607 }
1608 }
1609 return TRUE;
1610}
1611
cb89c5a5
RN
1612// Structure to hold multiple track information for a layer
1613typedef struct {
1614 gdouble length;
1615 time_t start_time;
1616 time_t end_time;
1617 gint duration;
1618} tooltip_tracks;
1619
1620/*
1621 * Build up layer multiple track information via updating the tooltip_tracks structure
1622 */
1623static void trw_layer_tracks_tooltip ( const gchar *name, VikTrack *tr, tooltip_tracks *tt )
1624{
1625 tt->length = tt->length + vik_track_get_length (tr);
1626
1627 // Ensure times are available
1628 if ( tr->trackpoints &&
1629 VIK_TRACKPOINT(tr->trackpoints->data)->has_timestamp &&
1630 VIK_TRACKPOINT(g_list_last(tr->trackpoints)->data)->has_timestamp ) {
1631
1632 time_t t1, t2;
1633 t1 = VIK_TRACKPOINT(tr->trackpoints->data)->timestamp;
1634 t2 = VIK_TRACKPOINT(g_list_last(tr->trackpoints)->data)->timestamp;
1635
1636 // Assume never actually have a track with a time of 0 (1st Jan 1970)
1637 // Hence initialize to the first 'proper' value
1638 if ( tt->start_time == 0 )
1639 tt->start_time = t1;
1640 if ( tt->end_time == 0 )
1641 tt->end_time = t2;
1642
1643 // Update find the earliest / last times
1644 if ( t1 < tt->start_time )
1645 tt->start_time = t1;
1646 if ( t2 > tt->end_time )
1647 tt->end_time = t2;
1648
1649 // Keep track of total time
1650 // there maybe gaps within a track (eg segments)
1651 // but this should be generally good enough for a simple indicator
1652 tt->duration = tt->duration + (int)(t2-t1);
1653 }
1654}
1655
1656/*
1657 * Generate tooltip text for the layer.
1658 * This is relatively complicated as it considers information for
1659 * no tracks, a single track or multiple tracks
1660 * (which may or may not have timing information)
1661 */
1662static const gchar* trw_layer_layer_tooltip ( VikTrwLayer *vtl )
1663{
1664 gchar tbuf1[32];
1665 gchar tbuf2[64];
1666 gchar tbuf3[64];
1667 gchar tbuf4[10];
1668 tbuf1[0] = '\0';
1669 tbuf2[0] = '\0';
1670 tbuf3[0] = '\0';
1671 tbuf4[0] = '\0';
1672
1673 static gchar tmp_buf[128];
1674 tmp_buf[0] = '\0';
1675
1676 // For compact date format I'm using '%x' [The preferred date representation for the current locale without the time.]
1677
1678 // Safety check - I think these should always be valid
1679 if ( vtl->tracks && vtl->waypoints ) {
1680 tooltip_tracks tt = { 0.0, 0, 0 };
1681 g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_tracks_tooltip, &tt );
1682
1683 GDate* gdate_start = g_date_new ();
1684 g_date_set_time_t (gdate_start, tt.start_time);
1685
1686 GDate* gdate_end = g_date_new ();
1687 g_date_set_time_t (gdate_end, tt.end_time);
1688
1689 if ( g_date_compare (gdate_start, gdate_end) ) {
1690 // Dates differ so print range on separate line
1691 g_date_strftime (tbuf1, sizeof(tbuf1), "%x", gdate_start);
1692 g_date_strftime (tbuf2, sizeof(tbuf2), "%x", gdate_end);
1693 g_snprintf (tbuf3, sizeof(tbuf3), "%s to %s\n", tbuf1, tbuf2);
1694 }
1695 else {
1696 // Same date so just show it and keep rest of text on the same line - provided it's a valid time!
1697 if ( tt.start_time != 0 )
1698 g_date_strftime (tbuf3, sizeof(tbuf3), "%x: ", gdate_start);
1699 }
1700
1701 tbuf2[0] = '\0';
1702 if ( tt.length > 0.0 ) {
1703 gdouble len_in_units;
1704
1705 // Setup info dependent on distance units
1706 if ( a_vik_get_units_distance() == VIK_UNITS_DISTANCE_MILES ) {
1707 g_snprintf (tbuf4, sizeof(tbuf4), "miles");
a492ff13 1708 len_in_units = VIK_METERS_TO_MILES(tt.length);
cb89c5a5
RN
1709 }
1710 else {
1711 g_snprintf (tbuf4, sizeof(tbuf4), "kms");
1712 len_in_units = tt.length/1000.0;
1713 }
1714
1715 // Timing information if available
1716 tbuf1[0] = '\0';
1717 if ( tt.duration > 0 ) {
1718 g_snprintf (tbuf1, sizeof(tbuf1),
1719 _(" in %d:%02d hrs:mins"),
1720 (int)round(tt.duration/3600), (int)round((tt.duration/60)%60));
1721 }
1722 g_snprintf (tbuf2, sizeof(tbuf2),
1723 _("\n%sTotal Length %.1f %s%s"),
1724 tbuf3, len_in_units, tbuf4, tbuf1);
1725 }
1726
1727 // Put together all the elements to form compact tooltip text
1728 g_snprintf (tmp_buf, sizeof(tmp_buf),
1729 _("Tracks: %d - Waypoints: %d%s"),
1730 g_hash_table_size (vtl->tracks), g_hash_table_size (vtl->waypoints), tbuf2);
1731
1732 g_date_free (gdate_start);
1733 g_date_free (gdate_end);
1734
1735 }
1736
1737 return tmp_buf;
1738}
1739
c7060c4e
RN
1740static const gchar* trw_layer_sublayer_tooltip ( VikTrwLayer *l, gint subtype, gpointer sublayer )
1741{
1742 switch ( subtype )
1743 {
1744 case VIK_TRW_LAYER_SUBLAYER_TRACKS: return NULL;
1745 case VIK_TRW_LAYER_SUBLAYER_WAYPOINTS: return NULL;
1746 case VIK_TRW_LAYER_SUBLAYER_TRACK:
1747 {
1748 VikTrack *tr = g_hash_table_lookup ( l->tracks, sublayer );
1749 if ( tr ) {
1750 // Could be a better way of handling strings - but this works...
1751 gchar time_buf1[20];
1752 gchar time_buf2[20];
1753 time_buf1[0] = '\0';
1754 time_buf2[0] = '\0';
1755 static gchar tmp_buf[100];
1756 // Compact info: Short date eg (11/20/99), duration and length
1757 // Hopefully these are the things that are most useful and so promoted into the tooltip
1758 if ( tr->trackpoints && VIK_TRACKPOINT(tr->trackpoints->data)->has_timestamp ) {
1759 // %x The preferred date representation for the current locale without the time.
1760 strftime (time_buf1, sizeof(time_buf1), "%x: ", gmtime(&(VIK_TRACKPOINT(tr->trackpoints->data)->timestamp)));
1761 if ( VIK_TRACKPOINT(g_list_last(tr->trackpoints)->data)->has_timestamp ) {
1762 gint dur = ( (VIK_TRACKPOINT(g_list_last(tr->trackpoints)->data)->timestamp) - (VIK_TRACKPOINT(tr->trackpoints->data)->timestamp) );
1763 if ( dur > 0 )
cb89c5a5 1764 g_snprintf ( time_buf2, sizeof(time_buf2), _("- %d:%02d hrs:mins"), (int)round(dur/3600), (int)round((dur/60)%60) );
c7060c4e
RN
1765 }
1766 }
1767 // Get length and consider the appropriate distance units
1768 gdouble tr_len = vik_track_get_length(tr);
1769 vik_units_distance_t dist_units = a_vik_get_units_distance ();
1770 switch (dist_units) {
1771 case VIK_UNITS_DISTANCE_KILOMETRES:
1772 g_snprintf (tmp_buf, sizeof(tmp_buf), _("%s%.1f km %s"), time_buf1, tr_len/1000.0, time_buf2);
1773 break;
1774 case VIK_UNITS_DISTANCE_MILES:
a492ff13 1775 g_snprintf (tmp_buf, sizeof(tmp_buf), _("%s%.1f miles %s"), time_buf1, VIK_METERS_TO_MILES(tr_len), time_buf2);
c7060c4e
RN
1776 break;
1777 default:
1778 break;
1779 }
1780 return tmp_buf;
1781 }
1782 }
1783 break;
1784 case VIK_TRW_LAYER_SUBLAYER_WAYPOINT:
1785 {
1786 VikWaypoint *w = g_hash_table_lookup ( l->waypoints, sublayer );
1787 // NB It's OK to return NULL
1788 return w->comment;
1789 }
1790 break;
1791 default: break;
1792 }
1793 return NULL;
1794}
1795
a5dcfdb7
RN
1796/**
1797 * General layer selection function, find out which bit is selected and take appropriate action
1798 */
1799gboolean vik_trw_layer_selected ( VikTrwLayer *l, gint subtype, gpointer sublayer, gint type, gpointer vlp )
1800{
c95d6b00
RN
1801 // Reset
1802 l->current_wp = NULL;
1803 l->current_wp_name = NULL;
1804 trw_layer_cancel_current_tp ( l, FALSE );
1805
a5dcfdb7
RN
1806 switch ( type )
1807 {
1808 case VIK_TREEVIEW_TYPE_LAYER:
1809 {
1810 vik_window_set_selected_trw_layer ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l), l );
1811 /* Mark for redraw */
1812 return TRUE;
1813 }
1814 break;
1815
1816 case VIK_TREEVIEW_TYPE_SUBLAYER:
1817 {
1818 switch ( subtype )
1819 {
1820 case VIK_TRW_LAYER_SUBLAYER_TRACKS:
1821 {
113c74f6 1822 vik_window_set_selected_tracks ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l), l->tracks, l );
a5dcfdb7
RN
1823 /* Mark for redraw */
1824 return TRUE;
1825 }
1826 break;
1827 case VIK_TRW_LAYER_SUBLAYER_TRACK:
1828 {
43f2e1da 1829 vik_window_set_selected_track ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l), g_hash_table_lookup ( l->tracks, sublayer ), l, sublayer );
a5dcfdb7
RN
1830 /* Mark for redraw */
1831 return TRUE;
1832 }
1833 break;
1834 case VIK_TRW_LAYER_SUBLAYER_WAYPOINTS:
1835 {
113c74f6 1836 vik_window_set_selected_waypoints ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l), l->waypoints, l );
a5dcfdb7
RN
1837 /* Mark for redraw */
1838 return TRUE;
1839 }
1840 break;
1841 case VIK_TRW_LAYER_SUBLAYER_WAYPOINT:
1842 {
43f2e1da 1843 vik_window_set_selected_waypoint ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l), g_hash_table_lookup ( l->waypoints, sublayer ), l, sublayer );
a5dcfdb7
RN
1844 /* Mark for redraw */
1845 return TRUE;
1846 }
1847 break;
1848 default:
1849 {
1850 return vik_window_clear_highlight ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l) );
1851 }
1852 break;
1853 }
1854 return FALSE;
1855 }
1856 break;
1857
1858 default:
1859 return vik_window_clear_highlight ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l) );
1860 break;
1861 }
1862}
c7060c4e 1863
50a14534
EB
1864GHashTable *vik_trw_layer_get_tracks ( VikTrwLayer *l )
1865{
1866 return l->tracks;
1867}
1868
1869GHashTable *vik_trw_layer_get_waypoints ( VikTrwLayer *l )
1870{
1871 return l->waypoints;
1872}
1873
1874static void trw_layer_find_maxmin_waypoints ( const gchar *name, const VikWaypoint *w, struct LatLon maxmin[2] )
1875{
1876 static VikCoord fixme;
1877 vik_coord_copy_convert ( &(w->coord), VIK_COORD_LATLON, &fixme );
1878 if ( VIK_LATLON(&fixme)->lat > maxmin[0].lat || maxmin[0].lat == 0.0 )
1879 maxmin[0].lat = VIK_LATLON(&fixme)->lat;
1880 if ( VIK_LATLON(&fixme)->lat < maxmin[1].lat || maxmin[1].lat == 0.0 )
1881 maxmin[1].lat = VIK_LATLON(&fixme)->lat;
1882 if ( VIK_LATLON(&fixme)->lon > maxmin[0].lon || maxmin[0].lon == 0.0 )
1883 maxmin[0].lon = VIK_LATLON(&fixme)->lon;
1884 if ( VIK_LATLON(&fixme)->lon < maxmin[1].lon || maxmin[1].lon == 0.0 )
1885 maxmin[1].lon = VIK_LATLON(&fixme)->lon;
1886}
1887
1888static void trw_layer_find_maxmin_tracks ( const gchar *name, GList **t, struct LatLon maxmin[2] )
1889{
1890 GList *tr = *t;
1891 static VikCoord fixme;
1892
1893 while ( tr )
1894 {
1895 vik_coord_copy_convert ( &(VIK_TRACKPOINT(tr->data)->coord), VIK_COORD_LATLON, &fixme );
1896 if ( VIK_LATLON(&fixme)->lat > maxmin[0].lat || maxmin[0].lat == 0.0 )
1897 maxmin[0].lat = VIK_LATLON(&fixme)->lat;
1898 if ( VIK_LATLON(&fixme)->lat < maxmin[1].lat || maxmin[1].lat == 0.0 )
1899 maxmin[1].lat = VIK_LATLON(&fixme)->lat;
1900 if ( VIK_LATLON(&fixme)->lon > maxmin[0].lon || maxmin[0].lon == 0.0 )
1901 maxmin[0].lon = VIK_LATLON(&fixme)->lon;
1902 if ( VIK_LATLON(&fixme)->lon < maxmin[1].lon || maxmin[1].lon == 0.0 )
1903 maxmin[1].lon = VIK_LATLON(&fixme)->lon;
1904 tr = tr->next;
1905 }
1906}
1907
165a4fa9
HR
1908static void trw_layer_find_maxmin (VikTrwLayer *vtl, struct LatLon maxmin[2])
1909{
1910 struct LatLon wpt_maxmin[2] = { {0.0,0.0}, {0.0,0.0} };
1911 struct LatLon trk_maxmin[2] = { {0.0,0.0}, {0.0,0.0} };
1912
1913 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_find_maxmin_waypoints, wpt_maxmin );
1914 g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_find_maxmin_tracks, trk_maxmin );
1915 if ((wpt_maxmin[0].lat != 0.0 && wpt_maxmin[0].lat > trk_maxmin[0].lat) || trk_maxmin[0].lat == 0.0) {
1916 maxmin[0].lat = wpt_maxmin[0].lat;
1917 }
1918 else {
1919 maxmin[0].lat = trk_maxmin[0].lat;
1920 }
1921 if ((wpt_maxmin[0].lon != 0.0 && wpt_maxmin[0].lon > trk_maxmin[0].lon) || trk_maxmin[0].lon == 0.0) {
1922 maxmin[0].lon = wpt_maxmin[0].lon;
1923 }
1924 else {
1925 maxmin[0].lon = trk_maxmin[0].lon;
1926 }
1927 if ((wpt_maxmin[1].lat != 0.0 && wpt_maxmin[1].lat < trk_maxmin[1].lat) || trk_maxmin[1].lat == 0.0) {
1928 maxmin[1].lat = wpt_maxmin[1].lat;
1929 }
1930 else {
1931 maxmin[1].lat = trk_maxmin[1].lat;
1932 }
1933 if ((wpt_maxmin[1].lon != 0.0 && wpt_maxmin[1].lon < trk_maxmin[1].lon) || trk_maxmin[1].lon == 0.0) {
1934 maxmin[1].lon = wpt_maxmin[1].lon;
1935 }
1936 else {
1937 maxmin[1].lon = trk_maxmin[1].lon;
1938 }
1939}
50a14534
EB
1940
1941gboolean vik_trw_layer_find_center ( VikTrwLayer *vtl, VikCoord *dest )
1942{
1943 /* 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... */
1944 struct LatLon maxmin[2] = { {0.0,0.0}, {0.0,0.0} };
165a4fa9 1945 trw_layer_find_maxmin (vtl, maxmin);
50a14534
EB
1946 if (maxmin[0].lat == 0.0 && maxmin[0].lon == 0.0 && maxmin[1].lat == 0.0 && maxmin[1].lon == 0.0)
1947 return FALSE;
1948 else
1949 {
1950 struct LatLon average = { (maxmin[0].lat+maxmin[1].lat)/2, (maxmin[0].lon+maxmin[1].lon)/2 };
1951 vik_coord_load_from_latlon ( dest, vtl->coord_mode, &average );
1952 return TRUE;
1953 }
1954}
1955
1956static void trw_layer_centerize ( gpointer layer_and_vlp[2] )
1957{
1958 VikCoord coord;
1959 if ( vik_trw_layer_find_center ( VIK_TRW_LAYER(layer_and_vlp[0]), &coord ) )
6bb72350 1960 goto_coord ( layer_and_vlp[1], NULL, NULL, &coord );
50a14534 1961 else
4c77d5e0 1962 a_dialog_info_msg ( VIK_GTK_WINDOW_FROM_LAYER(layer_and_vlp[0]), _("This layer has no waypoints or trackpoints.") );
50a14534
EB
1963}
1964
c5638216
RN
1965static void trw_layer_zoom_to_show_latlons ( VikTrwLayer *vtl, VikViewport *vvp, struct LatLon maxmin[2] )
1966{
1967 /* First set the center [in case previously viewing from elsewhere] */
1968 /* Then loop through zoom levels until provided positions are in view */
1969 /* This method is not particularly fast - but should work well enough */
1970 struct LatLon average = { (maxmin[0].lat+maxmin[1].lat)/2, (maxmin[0].lon+maxmin[1].lon)/2 };
1971 VikCoord coord;
1972 vik_coord_load_from_latlon ( &coord, vtl->coord_mode, &average );
1973 vik_viewport_set_center_coord ( vvp, &coord );
1974
1975 /* Convert into definite 'smallest' and 'largest' positions */
1976 struct LatLon minmin;
1977 if ( maxmin[0].lat < maxmin[1].lat )
1978 minmin.lat = maxmin[0].lat;
1979 else
1980 minmin.lat = maxmin[1].lat;
1981
1982 struct LatLon maxmax;
1983 if ( maxmin[0].lon > maxmin[1].lon )
1984 maxmax.lon = maxmin[0].lon;
1985 else
1986 maxmax.lon = maxmin[1].lon;
1987
1988 /* Never zoom in too far - generally not that useful, as too close ! */
1989 /* Always recalculate the 'best' zoom level */
1990 gdouble zoom = 1.0;
1991 vik_viewport_set_zoom ( vvp, zoom );
1992
1993 gdouble min_lat, max_lat, min_lon, max_lon;
1994 /* Should only be a maximum of about 18 iterations from min to max zoom levels */
1995 while ( zoom <= VIK_VIEWPORT_MAX_ZOOM ) {
1996 vik_viewport_get_min_max_lat_lon ( vvp, &min_lat, &max_lat, &min_lon, &max_lon );
1997 /* NB I think the logic used in this test to determine if the bounds is within view
1998 fails if track goes across 180 degrees longitude.
1999 Hopefully that situation is not too common...
2000 Mind you viking doesn't really do edge locations to well anyway */
2001 if ( min_lat < minmin.lat &&
2002 max_lat > minmin.lat &&
2003 min_lon < maxmax.lon &&
2004 max_lon > maxmax.lon )
2005 /* Found within zoom level */
2006 break;
2007
2008 /* Try next */
2009 zoom = zoom * 2;
2010 vik_viewport_set_zoom ( vvp, zoom );
2011 }
2012}
2013
2014gboolean vik_trw_layer_auto_set_view ( VikTrwLayer *vtl, VikViewport *vvp )
2015{
2016 /* TODO: what if there's only one waypoint @ 0,0, it will think nothing found. */
2017 struct LatLon maxmin[2] = { {0.0,0.0}, {0.0,0.0} };
2018 trw_layer_find_maxmin (vtl, maxmin);
2019 if (maxmin[0].lat == 0.0 && maxmin[0].lon == 0.0 && maxmin[1].lat == 0.0 && maxmin[1].lon == 0.0)
2020 return FALSE;
2021 else {
2022 trw_layer_zoom_to_show_latlons ( vtl, vvp, maxmin );
2023 return TRUE;
2024 }
2025}
2026
5a10c240
RN
2027static void trw_layer_auto_view ( gpointer layer_and_vlp[2] )
2028{
2029 if ( vik_trw_layer_auto_set_view ( VIK_TRW_LAYER(layer_and_vlp[0]), vik_layers_panel_get_viewport (VIK_LAYERS_PANEL(layer_and_vlp[1])) ) ) {
2030 vik_layers_panel_emit_update ( VIK_LAYERS_PANEL(layer_and_vlp[1]) );
2031 }
2032 else
2033 a_dialog_info_msg ( VIK_GTK_WINDOW_FROM_LAYER(layer_and_vlp[0]), _("This layer has no waypoints or trackpoints.") );
2034}
2035
f7f8a0a6 2036static void trw_layer_export ( gpointer layer_and_vlp[2], const gchar *title, const gchar* default_name, const gchar* trackname, guint file_type )
50a14534
EB
2037{
2038 GtkWidget *file_selector;
2039 const gchar *fn;
2040 gboolean failed = FALSE;
7f6757c4
RN
2041 file_selector = gtk_file_chooser_dialog_new (title,
2042 NULL,
2043 GTK_FILE_CHOOSER_ACTION_SAVE,
2044 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
2045 GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT,
2046 NULL);
2047 gtk_file_chooser_set_current_name (GTK_FILE_CHOOSER(file_selector), default_name);
6e4a49aa
MA
2048
2049 while ( gtk_dialog_run ( GTK_DIALOG(file_selector) ) == GTK_RESPONSE_ACCEPT )
50a14534 2050 {
6e4a49aa 2051 fn = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER(file_selector) );
45acf79e 2052 if ( g_file_test ( fn, G_FILE_TEST_EXISTS ) == FALSE )
50a14534
EB
2053 {
2054 gtk_widget_hide ( file_selector );
f7f8a0a6 2055 failed = ! a_file_export ( VIK_TRW_LAYER(layer_and_vlp[0]), fn, file_type, trackname);
50a14534
EB
2056 break;
2057 }
2058 else
2059 {
d91e5f2b 2060 if ( a_dialog_yes_or_no ( GTK_WINDOW(file_selector), _("The file \"%s\" exists, do you wish to overwrite it?"), a_file_basename ( fn ) ) )
50a14534
EB
2061 {
2062 gtk_widget_hide ( file_selector );
f7f8a0a6 2063 failed = ! a_file_export ( VIK_TRW_LAYER(layer_and_vlp[0]), fn, file_type, trackname);
50a14534
EB
2064 break;
2065 }
2066 }
2067 }
2068 gtk_widget_destroy ( file_selector );
2069 if ( failed )
4c77d5e0 2070 a_dialog_error_msg ( VIK_GTK_WINDOW_FROM_LAYER(layer_and_vlp[0]), _("The filename you requested could not be opened for writing.") );
50a14534
EB
2071}
2072
2073static void trw_layer_export_gpspoint ( gpointer layer_and_vlp[2] )
2074{
f7f8a0a6 2075 trw_layer_export ( layer_and_vlp, _("Export Layer"), vik_layer_get_name(VIK_LAYER(layer_and_vlp[0])), NULL, FILE_TYPE_GPSPOINT );
50a14534
EB
2076}
2077
2078static void trw_layer_export_gpsmapper ( gpointer layer_and_vlp[2] )
2079{
f7f8a0a6 2080 trw_layer_export ( layer_and_vlp, _("Export Layer"), vik_layer_get_name(VIK_LAYER(layer_and_vlp[0])), NULL, FILE_TYPE_GPSMAPPER );
50a14534
EB
2081}
2082
561e6ad0
EB
2083static void trw_layer_export_gpx ( gpointer layer_and_vlp[2] )
2084{
f7f8a0a6 2085 trw_layer_export ( layer_and_vlp, _("Export Layer"), vik_layer_get_name(VIK_LAYER(layer_and_vlp[0])), NULL, FILE_TYPE_GPX );
7f6757c4
RN
2086}
2087
2088static void trw_layer_export_gpx_track ( gpointer pass_along[6] )
2089{
7f6757c4
RN
2090 gpointer layer_and_vlp[2];
2091 layer_and_vlp[0] = pass_along[0];
2092 layer_and_vlp[1] = pass_along[1];
2093
2094 /* Auto append '.gpx' to track name (providing it's not already there) for the default filename */
2095 gchar *auto_save_name = g_strdup ( pass_along[3] );
2096 if ( ! check_file_ext ( auto_save_name, ".gpx" ) )
2097 auto_save_name = g_strconcat ( auto_save_name, ".gpx", NULL );
2098
f7f8a0a6 2099 trw_layer_export ( layer_and_vlp, _("Export Track as GPX"), auto_save_name, pass_along[3], FILE_TYPE_GPX );
7f6757c4
RN
2100
2101 g_free ( auto_save_name );
561e6ad0
EB
2102}
2103
50a14534
EB
2104static void trw_layer_goto_wp ( gpointer layer_and_vlp[2] )
2105{
2106 GHashTable *wps = vik_trw_layer_get_waypoints ( VIK_TRW_LAYER(layer_and_vlp[0]) );
a77d62d8 2107 GtkWidget *dia = gtk_dialog_new_with_buttons (_("Find"),
50a14534
EB
2108 VIK_GTK_WINDOW_FROM_LAYER(layer_and_vlp[0]),
2109 GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
2110 GTK_STOCK_CANCEL,
2111 GTK_RESPONSE_REJECT,
2112 GTK_STOCK_OK,
2113 GTK_RESPONSE_ACCEPT,
2114 NULL);
2115
2116 GtkWidget *label, *entry;
4c77d5e0 2117 label = gtk_label_new(_("Waypoint Name:"));
50a14534
EB
2118 entry = gtk_entry_new();
2119
2120 gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dia)->vbox), label, FALSE, FALSE, 0);
2121 gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dia)->vbox), entry, FALSE, FALSE, 0);
2122 gtk_widget_show_all ( label );
2123 gtk_widget_show_all ( entry );
2124
7ea3cb11
RN
2125 gtk_dialog_set_default_response ( GTK_DIALOG(dia), GTK_RESPONSE_ACCEPT );
2126
50a14534
EB
2127 while ( gtk_dialog_run ( GTK_DIALOG(dia) ) == GTK_RESPONSE_ACCEPT )
2128 {
2129 VikWaypoint *wp;
2130 gchar *upname = g_strdup(gtk_entry_get_text(GTK_ENTRY(entry)));
2131 int i;
2132
2133 for ( i = strlen(upname)-1; i >= 0; i-- )
2134 upname[i] = toupper(upname[i]);
2135
2136 wp = g_hash_table_lookup ( wps, upname );
2137
2138 if (!wp)
4c77d5e0 2139 a_dialog_error_msg ( VIK_GTK_WINDOW_FROM_LAYER(layer_and_vlp[0]), _("Waypoint not found in this layer.") );
50a14534
EB
2140 else
2141 {
2142 vik_viewport_set_center_coord ( vik_layers_panel_get_viewport(VIK_LAYERS_PANEL(layer_and_vlp[1])), &(wp->coord) );
2143 vik_layers_panel_emit_update ( VIK_LAYERS_PANEL(layer_and_vlp[1]) );
bdd1c412 2144 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 ), TRUE );
50a14534
EB
2145 break;
2146 }
2147
2148 g_free ( upname );
2149
2150 }
2151 gtk_widget_destroy ( dia );
2152}
2153
2154gboolean vik_trw_layer_new_waypoint ( VikTrwLayer *vtl, GtkWindow *w, const VikCoord *def_coord )
2155{
ac1bde8b 2156 gchar *default_name = highest_wp_number_get(vtl);
acaf7113 2157 VikWaypoint *wp = vik_waypoint_new();
ac1bde8b
RN
2158 gchar *returned_name;
2159 gboolean updated;
acaf7113 2160 wp->coord = *def_coord;
50a14534 2161
ac1bde8b 2162 if ( ( returned_name = a_dialog_waypoint ( w, default_name, wp, vik_trw_layer_get_waypoints ( vtl ), vtl->coord_mode, TRUE, &updated ) ) )
50a14534 2163 {
805d282e 2164 wp->visible = TRUE;
ac1bde8b
RN
2165 vik_trw_layer_add_waypoint ( vtl, returned_name, wp );
2166 g_free (default_name);
50a14534
EB
2167 return TRUE;
2168 }
ac1bde8b 2169 g_free (default_name);
acaf7113 2170 vik_waypoint_free(wp);
50a14534
EB
2171 return FALSE;
2172}
2173
165a4fa9
HR
2174static void trw_layer_new_wikipedia_wp_viewport ( gpointer lav[2] )
2175{
2176 VikCoord one, two;
2177 struct LatLon one_ll, two_ll;
2178 struct LatLon maxmin[2] = { {0.0,0.0}, {0.0,0.0} };
2179
2180 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
2181 VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
2182 VikWindow *vw = (VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl));
2183 VikViewport *vvp = vik_window_viewport(vw);
2184 vik_viewport_screen_to_coord ( vvp, 0, 0, &one);
2185 vik_viewport_screen_to_coord ( vvp, vik_viewport_get_width(vvp), vik_viewport_get_height(vvp), &two);
2186 vik_coord_to_latlon(&one, &one_ll);
2187 vik_coord_to_latlon(&two, &two_ll);
2188 if (one_ll.lat > two_ll.lat) {
2189 maxmin[0].lat = one_ll.lat;
2190 maxmin[1].lat = two_ll.lat;
2191 }
2192 else {
2193 maxmin[0].lat = two_ll.lat;
2194 maxmin[1].lat = one_ll.lat;
2195 }
2196 if (one_ll.lon > two_ll.lon) {
2197 maxmin[0].lon = one_ll.lon;
2198 maxmin[1].lon = two_ll.lon;
2199 }
2200 else {
2201 maxmin[0].lon = two_ll.lon;
2202 maxmin[1].lon = one_ll.lon;
2203 }
2204 a_geonames_wikipedia_box((VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl)), vtl, vlp, maxmin);
2205}
2206
2207static void trw_layer_new_wikipedia_wp_layer ( gpointer lav[2] )
2208{
2209 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
2210 VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
2211 struct LatLon maxmin[2] = { {0.0,0.0}, {0.0,0.0} };
2212
2213 trw_layer_find_maxmin (vtl, maxmin);
2214 a_geonames_wikipedia_box((VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl)), vtl, vlp, maxmin);
2215}
2216
50a14534
EB
2217static void trw_layer_new_wp ( gpointer lav[2] )
2218{
2219 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
2220 VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
2221 /* TODO longone: okay, if layer above (aggregate) is invisible but vtl->visible is true, this redraws for no reason.
2222 instead return true if you want to update. */
2223 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 )
2224 vik_layers_panel_emit_update ( vlp );
2225}
2226
535ed1ae
RN
2227static void trw_layer_auto_tracks_view ( gpointer lav[2] )
2228{
2229 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
2230 VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
2231
2232 if ( g_hash_table_size (vtl->tracks) > 0 ) {
2233 struct LatLon maxmin[2] = { {0,0}, {0,0} };
2234 g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_find_maxmin_tracks, maxmin );
2235 trw_layer_zoom_to_show_latlons ( vtl, vik_layers_panel_get_viewport (vlp), maxmin );
2236 vik_layers_panel_emit_update ( vlp );
2237 }
2238}
2239
fc59e8c7
RN
2240static void trw_layer_single_waypoint_jump ( const gchar *name, const VikWaypoint *wp, gpointer vvp )
2241{
2242 /* NB do not care if wp is visible or not */
2243 vik_viewport_set_center_coord ( VIK_VIEWPORT(vvp), &(wp->coord) );
2244}
2245
2246static void trw_layer_auto_waypoints_view ( gpointer lav[2] )
2247{
2248 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
2249 VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
2250
2251 /* Only 1 waypoint - jump straight to it */
2252 if ( g_hash_table_size (vtl->waypoints) == 1 ) {
2253 VikViewport *vvp = vik_layers_panel_get_viewport (vlp);
2254 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_single_waypoint_jump, (gpointer) vvp );
2255 }
2256 /* If at least 2 waypoints - find center and then zoom to fit */
2257 else if ( g_hash_table_size (vtl->waypoints) > 1 )
2258 {
2259 struct LatLon maxmin[2] = { {0,0}, {0,0} };
2260 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_find_maxmin_waypoints, maxmin );
2261 trw_layer_zoom_to_show_latlons ( vtl, vik_layers_panel_get_viewport (vlp), maxmin );
2262 }
2263
2264 vik_layers_panel_emit_update ( vlp );
2265}
2266
50a14534
EB
2267void vik_trw_layer_add_menu_items ( VikTrwLayer *vtl, GtkMenu *menu, gpointer vlp )
2268{
2269 static gpointer pass_along[2];
2270 GtkWidget *item;
98fcbbdb 2271 GtkWidget *export_submenu;
165a4fa9 2272 GtkWidget *wikipedia_submenu;
50a14534
EB
2273 pass_along[0] = vtl;
2274 pass_along[1] = vlp;
2275
2276 item = gtk_menu_item_new();
2277 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
2278 gtk_widget_show ( item );
2279
5a10c240
RN
2280 item = gtk_menu_item_new_with_mnemonic ( _("_View Layer") );
2281 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_view), pass_along );
2282 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
2283 gtk_widget_show ( item );
2284
535ed1ae
RN
2285 item = gtk_menu_item_new_with_mnemonic ( _("View All Trac_ks") );
2286 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_tracks_view), pass_along );
2287 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
2288 gtk_widget_show ( item );
2289
fc59e8c7
RN
2290 item = gtk_menu_item_new_with_mnemonic ( _("V_iew All Waypoints") );
2291 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_waypoints_view), pass_along );
2292 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
2293 gtk_widget_show ( item );
2294
d65659e7 2295 item = gtk_menu_item_new_with_mnemonic ( _("_Goto Center of Layer") );
50a14534
EB
2296 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_centerize), pass_along );
2297 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
2298 gtk_widget_show ( item );
2299
d65659e7 2300 item = gtk_menu_item_new_with_mnemonic ( _("Goto _Waypoint") );
50a14534
EB
2301 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_wp), pass_along );
2302 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
2303 gtk_widget_show ( item );
2304
1bd88e66 2305 export_submenu = gtk_menu_new ();
d65659e7 2306 item = gtk_menu_item_new_with_mnemonic ( _("_Export Layer") );
50a14534
EB
2307 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
2308 gtk_widget_show ( item );
98fcbbdb 2309 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), export_submenu );
1bd88e66 2310
d65659e7 2311 item = gtk_menu_item_new_with_mnemonic ( _("Export as GPS_Point") );
1bd88e66
GB
2312 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_gpspoint), pass_along );
2313 gtk_menu_shell_append (GTK_MENU_SHELL (export_submenu), item);
2314 gtk_widget_show ( item );
50a14534 2315
d65659e7 2316 item = gtk_menu_item_new_with_mnemonic ( _("Export as GPS_Mapper") );
50a14534 2317 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_gpsmapper), pass_along );
1bd88e66 2318 gtk_menu_shell_append (GTK_MENU_SHELL (export_submenu), item);
50a14534
EB
2319 gtk_widget_show ( item );
2320
d65659e7 2321 item = gtk_menu_item_new_with_mnemonic ( _("Export as _GPX") );
561e6ad0 2322 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_gpx), pass_along );
1bd88e66 2323 gtk_menu_shell_append (GTK_MENU_SHELL (export_submenu), item);
561e6ad0
EB
2324 gtk_widget_show ( item );
2325
d65659e7 2326 item = gtk_menu_item_new_with_mnemonic ( _("_New Waypoint") );
50a14534
EB
2327 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_wp), pass_along );
2328 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
2329 gtk_widget_show ( item );
3e7553ae 2330
da03b29d 2331#ifdef VIK_CONFIG_GEONAMES
165a4fa9 2332 wikipedia_submenu = gtk_menu_new();
d65659e7 2333 item = gtk_menu_item_new_with_mnemonic ( _("_Add Wikipedia Waypoints") );
165a4fa9
HR
2334 gtk_menu_shell_append(GTK_MENU_SHELL (menu), item);
2335 gtk_widget_show(item);
2336 gtk_menu_item_set_submenu(GTK_MENU_ITEM(item), wikipedia_submenu);
2337
d65659e7 2338 item = gtk_menu_item_new_with_mnemonic ( _("Within _Layer Bounds") );
165a4fa9
HR
2339 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_wikipedia_wp_layer), pass_along );
2340 gtk_menu_shell_append (GTK_MENU_SHELL (wikipedia_submenu), item);
2341 gtk_widget_show ( item );
2342
d65659e7 2343 item = gtk_menu_item_new_with_mnemonic ( _("Within _Current View") );
165a4fa9
HR
2344 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_wikipedia_wp_viewport), pass_along );
2345 gtk_menu_shell_append (GTK_MENU_SHELL (wikipedia_submenu), item);
2346 gtk_widget_show ( item );
da03b29d 2347#endif
165a4fa9 2348
3e7553ae 2349#ifdef VIK_CONFIG_OPENSTREETMAP
d65659e7 2350 item = gtk_menu_item_new_with_mnemonic ( _("Upload to _OSM") );
3e7553ae
GB
2351 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(osm_traces_upload_cb), pass_along );
2352 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
2353 gtk_widget_show ( item );
2354#endif
28c82d8b
EB
2355
2356 item = a_acquire_trwlayer_menu ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)), vlp,
d65659e7 2357 vik_layers_panel_get_viewport(VIK_LAYERS_PANEL(vlp)), vtl );
28c82d8b
EB
2358 if ( item ) {
2359 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
2360 gtk_widget_show ( item );
2361 }
2362
2363 item = a_acquire_trwlayer_track_menu ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)), vlp,
d65659e7 2364 vik_layers_panel_get_viewport(VIK_LAYERS_PANEL(vlp)), vtl );
28c82d8b
EB
2365 if ( item ) {
2366 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
2367 gtk_widget_show ( item );
2368 }
50a14534
EB
2369}
2370
2371void vik_trw_layer_add_waypoint ( VikTrwLayer *vtl, gchar *name, VikWaypoint *wp )
2372{
2373 if ( VIK_LAYER(vtl)->realized )
2374 {
2375 VikWaypoint *oldwp = VIK_WAYPOINT ( g_hash_table_lookup ( vtl->waypoints, name ) );
2376 if ( oldwp )
2377 wp->visible = oldwp->visible; /* same visibility so we don't have to update viktreeview */
2378 else
2379 {
2380 GtkTreeIter *iter = g_malloc(sizeof(GtkTreeIter));
58cd1c43 2381 // Visibility column always needed for waypoints
50a14534
EB
2382#ifdef VIK_CONFIG_ALPHABETIZED_TRW
2383 vik_treeview_add_sublayer_alphabetized ( VIK_LAYER(vtl)->vt, &(vtl->waypoints_iter), iter, name, vtl, name, VIK_TRW_LAYER_SUBLAYER_WAYPOINT, NULL, TRUE, TRUE );
2384#else
2385 vik_treeview_add_sublayer ( VIK_LAYER(vtl)->vt, &(vtl->waypoints_iter), iter, name, vtl, name, VIK_TRW_LAYER_SUBLAYER_WAYPOINT, NULL, TRUE, TRUE );
2386#endif
58cd1c43
RN
2387 // Actual setting of visibility dependent on the waypoint
2388 vik_treeview_item_set_visible ( VIK_LAYER(vtl)->vt, iter, wp->visible );
bdd1c412 2389 vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, iter, TRUE );
50a14534 2390 g_hash_table_insert ( vtl->waypoints_iters, name, iter );
50a14534
EB
2391 }
2392 }
50a14534 2393
a8fe53f8 2394 highest_wp_number_add_wp(vtl, name);
50a14534
EB
2395 g_hash_table_insert ( vtl->waypoints, name, wp );
2396
2397}
2398
2399void vik_trw_layer_add_track ( VikTrwLayer *vtl, gchar *name, VikTrack *t )
2400{
2401 if ( VIK_LAYER(vtl)->realized )
2402 {
2403 VikTrack *oldt = VIK_TRACK ( g_hash_table_lookup ( vtl->tracks, name ) );
2404 if ( oldt )
2405 t->visible = oldt->visible; /* same visibility so we don't have to update viktreeview */
2406 else
2407 {
2408 GtkTreeIter *iter = g_malloc(sizeof(GtkTreeIter));
58cd1c43 2409 // Visibility column always needed for tracks
50a14534 2410#ifdef VIK_CONFIG_ALPHABETIZED_TRW
58cd1c43 2411 vik_treeview_add_sublayer_alphabetized ( VIK_LAYER(vtl)->vt, &(vtl->tracks_iter), iter, name, vtl, name, VIK_TRW_LAYER_SUBLAYER_TRACK, NULL, TRUE, TRUE );
50a14534 2412#else
58cd1c43 2413 vik_treeview_add_sublayer ( VIK_LAYER(vtl)->vt, &(vtl->tracks_iter), iter, name, vtl, name, VIK_TRW_LAYER_SUBLAYER_TRACK, NULL, TRUE, TRUE );
50a14534 2414#endif
58cd1c43
RN
2415 // Actual setting of visibility dependent on the track
2416 vik_treeview_item_set_visible ( VIK_LAYER(vtl)->vt, iter, t->visible );
bdd1c412 2417 vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, iter, TRUE );
50a14534 2418 g_hash_table_insert ( vtl->tracks_iters, name, iter );
50a14534
EB
2419 }
2420 }
50a14534
EB
2421
2422 g_hash_table_insert ( vtl->tracks, name, t );
2423
2424}
2425
50a14534 2426/* to be called whenever a track has been deleted or may have been changed. */
21700912 2427void trw_layer_cancel_tps_of_track ( VikTrwLayer *vtl, const gchar *trk_name )
50a14534
EB
2428{
2429 if (vtl->current_tp_track_name && g_strcasecmp(trk_name, vtl->current_tp_track_name) == 0)
2430 trw_layer_cancel_current_tp ( vtl, FALSE );
2431 else if (vtl->last_tp_track_name && g_strcasecmp(trk_name, vtl->last_tp_track_name) == 0)
2432 trw_layer_cancel_last_tp ( vtl );
2433}
e890a6e6
EB
2434
2435static gchar *get_new_unique_sublayer_name (VikTrwLayer *vtl, gint sublayer_type, const gchar *name)
2436{
2437 gint i = 2;
2438 gchar *newname = g_strdup(name);
2439 while ((sublayer_type == VIK_TRW_LAYER_SUBLAYER_TRACK) ?
941aa6e9 2440 (void *)vik_trw_layer_get_track(vtl, newname) : (void *)vik_trw_layer_get_waypoint(vtl, newname)) {
e890a6e6
EB
2441 gchar *new_newname = g_strdup_printf("%s#%d", name, i);
2442 g_free(newname);
2443 newname = new_newname;
2444 i++;
2445 }
2446 return newname;
2447}
50a14534 2448
805d282e
EB
2449void vik_trw_layer_filein_add_waypoint ( VikTrwLayer *vtl, gchar *name, VikWaypoint *wp )
2450{
2451 vik_trw_layer_add_waypoint ( vtl,
2452 get_new_unique_sublayer_name(vtl, VIK_TRW_LAYER_SUBLAYER_WAYPOINT, name),
2453 wp );
2454}
2455void vik_trw_layer_filein_add_track ( VikTrwLayer *vtl, gchar *name, VikTrack *tr )
2456{
bddd2056 2457 if ( vtl->magic_scissors_append && vtl->magic_scissors_current_track ) {
c3deba01 2458 vik_track_remove_dup_points ( tr ); /* make "double point" track work to undo */
bddd2056
EB
2459 vik_track_steal_and_append_trackpoints ( vtl->magic_scissors_current_track, tr );
2460 vik_track_free ( tr );
2461 vtl->magic_scissors_append = FALSE; /* this means we have added it */
2462 } else {
2463 gchar *new_name = get_new_unique_sublayer_name(vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, name);
2464 vik_trw_layer_add_track ( vtl, new_name, tr );
2465
2466 if ( vtl->magic_scissors_check_added_track ) {
c3deba01 2467 vik_track_remove_dup_points ( tr ); /* make "double point" track work to undo */
bddd2056
EB
2468 if ( vtl->magic_scissors_added_track_name ) /* for google routes */
2469 g_free ( vtl->magic_scissors_added_track_name );
2470 vtl->magic_scissors_added_track_name = g_strdup(new_name);
2471 }
2472 }
805d282e
EB
2473}
2474
c48517ad
AF
2475static void trw_layer_enum_item ( const gchar *name, GList **tr, GList **l )
2476{
2477 *l = g_list_append(*l, (gpointer)name);
2478}
2479
2480static void trw_layer_move_item ( VikTrwLayer *vtl_src, VikTrwLayer *vtl_dest, gchar *name, gint type )
2481{
2482 gchar *newname = get_new_unique_sublayer_name(vtl_dest, type, name);
2483 if (type == VIK_TRW_LAYER_SUBLAYER_TRACK) {
2484 VikTrack *t;
2485 t = vik_track_copy(vik_trw_layer_get_track(vtl_src, name));
2486 vik_trw_layer_delete_track(vtl_src, name);
2487 vik_trw_layer_add_track(vtl_dest, newname, t);
2488 }
2489 if (type==VIK_TRW_LAYER_SUBLAYER_WAYPOINT) {
2490 VikWaypoint *w;
2491 w = vik_waypoint_copy(vik_trw_layer_get_waypoint(vtl_src, name));
2492 vik_trw_layer_delete_waypoint(vtl_src, name);
2493 vik_trw_layer_add_waypoint(vtl_dest, newname, w);
2494 }
2495}
2496
70a23263 2497static void trw_layer_drag_drop_request ( VikTrwLayer *vtl_src, VikTrwLayer *vtl_dest, GtkTreeIter *src_item_iter, GtkTreePath *dest_path )
e4afc73a
EB
2498{
2499 VikTreeview *vt = VIK_LAYER(vtl_src)->vt;
c48517ad
AF
2500 gint type = vik_treeview_item_get_data(vt, src_item_iter);
2501
e4afc73a 2502 if (!vik_treeview_item_get_pointer(vt, src_item_iter)) {
b637058e
EB
2503 GList *items = NULL;
2504 GList *iter;
e4afc73a 2505
c48517ad
AF
2506 if (type==VIK_TRW_LAYER_SUBLAYER_TRACKS) {
2507 g_hash_table_foreach ( vtl_src->tracks, (GHFunc)trw_layer_enum_item, &items);
2508 }
2509 if (type==VIK_TRW_LAYER_SUBLAYER_WAYPOINTS) {
2510 g_hash_table_foreach ( vtl_src->waypoints, (GHFunc)trw_layer_enum_item, &items);
2511 }
2512
2513 iter = items;
2514 while (iter) {
2515 if (type==VIK_TRW_LAYER_SUBLAYER_TRACKS) {
2516 trw_layer_move_item ( vtl_src, vtl_dest, iter->data, VIK_TRW_LAYER_SUBLAYER_TRACK);
2517 } else {
2518 trw_layer_move_item ( vtl_src, vtl_dest, iter->data, VIK_TRW_LAYER_SUBLAYER_WAYPOINT);
2519 }
2520 iter = iter->next;
e4afc73a 2521 }
c48517ad 2522 if (items)
b637058e 2523 g_list_free(items);
c48517ad
AF
2524 } else {
2525 gchar *name = vik_treeview_item_get_pointer(vt, src_item_iter);
2526 trw_layer_move_item(vtl_src, vtl_dest, name, type);
e4afc73a
EB
2527 }
2528}
2529
e4afc73a 2530gboolean vik_trw_layer_delete_track ( VikTrwLayer *vtl, const gchar *trk_name )
50a14534
EB
2531{
2532 VikTrack *t = g_hash_table_lookup ( vtl->tracks, trk_name );
2533 gboolean was_visible = FALSE;
2534 if ( t )
2535 {
2536 GtkTreeIter *it;
2537 was_visible = t->visible;
77ad64fa 2538 if ( t == vtl->current_track ) {
50a14534 2539 vtl->current_track = NULL;
77ad64fa 2540 }
bddd2056
EB
2541 if ( t == vtl->magic_scissors_current_track )
2542 vtl->magic_scissors_current_track = NULL;
50a14534
EB
2543 /* could be current_tp, so we have to check */
2544 trw_layer_cancel_tps_of_track ( vtl, trk_name );
2545
2546 g_assert ( ( it = g_hash_table_lookup ( vtl->tracks_iters, trk_name ) ) );
2547 vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, it );
2548 g_hash_table_remove ( vtl->tracks_iters, trk_name );
2549
2550 /* do this last because trk_name may be pointing to actual orig key */
2551 g_hash_table_remove ( vtl->tracks, trk_name );
2552 }
2553 return was_visible;
2554}
2555
e4afc73a
EB
2556gboolean vik_trw_layer_delete_waypoint ( VikTrwLayer *vtl, const gchar *wp_name )
2557{
2558 gboolean was_visible = FALSE;
2559 VikWaypoint *wp;
2560
2561 wp = g_hash_table_lookup ( vtl->waypoints, wp_name );
2562 if ( wp ) {
2563 GtkTreeIter *it;
2564
2565 if ( wp == vtl->current_wp ) {
2566 vtl->current_wp = NULL;
2567 vtl->current_wp_name = NULL;
2568 vtl->moving_wp = FALSE;
2569 }
2570
2571 was_visible = wp->visible;
2572 g_assert ( ( it = g_hash_table_lookup ( vtl->waypoints_iters, (gchar *) wp_name ) ) );
2573 vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, it );
2574 g_hash_table_remove ( vtl->waypoints_iters, (gchar *) wp_name );
a8fe53f8
EB
2575
2576 highest_wp_number_remove_wp(vtl, wp_name);
e4afc73a
EB
2577 g_hash_table_remove ( vtl->waypoints, wp_name ); /* last because this frees name */
2578 }
2579
2580 return was_visible;
2581}
2582
700b0908
QT
2583static void remove_item_from_treeview(const gchar *name, GtkTreeIter *it, VikTreeview * vt)
2584{
2585 vik_treeview_item_delete (vt, it );
2586}
2587
2588void vik_trw_layer_delete_all_tracks ( VikTrwLayer *vtl )
2589{
2590
2591 vtl->current_track = NULL;
bddd2056 2592 vtl->magic_scissors_current_track = NULL;
700b0908
QT
2593 if (vtl->current_tp_track_name)
2594 trw_layer_cancel_current_tp(vtl, FALSE);
2595 if (vtl->last_tp_track_name)
2596 trw_layer_cancel_last_tp ( vtl );
2597
2598 g_hash_table_foreach(vtl->tracks_iters, (GHFunc) remove_item_from_treeview, VIK_LAYER(vtl)->vt);
2599 g_hash_table_remove_all(vtl->tracks_iters);
2600 g_hash_table_remove_all(vtl->tracks);
2601
2602 /* TODO: only update if the layer is visible (ticked) */
2603 vik_layer_emit_update ( VIK_LAYER(vtl) );
2604}
2605
2606void vik_trw_layer_delete_all_waypoints ( VikTrwLayer *vtl )
2607{
2608 vtl->current_wp = NULL;
2609 vtl->current_wp_name = NULL;
2610 vtl->moving_wp = FALSE;
2611
a8fe53f8
EB
2612 highest_wp_number_reset(vtl);
2613
700b0908
QT
2614 g_hash_table_foreach(vtl->waypoints_iters, (GHFunc) remove_item_from_treeview, VIK_LAYER(vtl)->vt);
2615 g_hash_table_remove_all(vtl->waypoints_iters);
2616 g_hash_table_remove_all(vtl->waypoints);
2617
2618 /* TODO: only update if the layer is visible (ticked) */
2619 vik_layer_emit_update ( VIK_LAYER(vtl) );
2620}
2621
6bb72350 2622static void trw_layer_delete_item ( gpointer pass_along[6] )
50a14534
EB
2623{
2624 VikTrwLayer *vtl = VIK_TRW_LAYER(pass_along[0]);
2625 gboolean was_visible = FALSE;
dc2c040e 2626 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_WAYPOINT )
50a14534 2627 {
e4afc73a 2628 was_visible = vik_trw_layer_delete_waypoint ( vtl, (gchar *) pass_along[3] );
50a14534
EB
2629 }
2630 else
2631 {
e4afc73a 2632 was_visible = vik_trw_layer_delete_track ( vtl, (gchar *) pass_along[3] );
50a14534 2633 }
50a14534
EB
2634 if ( was_visible )
2635 vik_layer_emit_update ( VIK_LAYER(vtl) );
2636}
2637
2638
6bb72350 2639static void trw_layer_properties_item ( gpointer pass_along[6] )
50a14534
EB
2640{
2641 VikTrwLayer *vtl = VIK_TRW_LAYER(pass_along[0]);
dc2c040e 2642 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_WAYPOINT )
50a14534
EB
2643 {
2644 VikWaypoint *wp = g_hash_table_lookup ( vtl->waypoints, pass_along[3] );
2645 if ( wp )
2646 {
ac1bde8b
RN
2647 gboolean updated = FALSE;
2648 a_dialog_waypoint ( VIK_GTK_WINDOW_FROM_LAYER(vtl), pass_along[3], wp, NULL, vtl->coord_mode, FALSE, &updated );
50a14534 2649
ac1bde8b
RN
2650 if ( updated && VIK_LAYER(vtl)->visible )
2651 vik_layer_emit_update ( VIK_LAYER(vtl) );
50a14534
EB
2652 }
2653 }
2654 else
2655 {
2656 VikTrack *tr = g_hash_table_lookup ( vtl->tracks, pass_along[3] );
2657 if ( tr )
2658 {
21700912 2659 vik_trw_layer_propwin_run ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
6bb72350
RN
2660 vtl, tr,
2661 pass_along[1], /* vlp */
2662 pass_along[3], /* track name */
2663 pass_along[5] ); /* vvp */
50a14534
EB
2664 }
2665 }
2666}
2667
6bb72350
RN
2668/*
2669 Parameter 1 -> VikLayersPanel
2670 Parameter 2 -> VikLayer
2671 Parameter 3 -> VikViewport
2672*/
2673static void goto_coord ( gpointer *vlp, gpointer vl, gpointer vvp, const VikCoord *coord )
2674{
2675 if ( vlp ) {
2676 vik_viewport_set_center_coord ( vik_layers_panel_get_viewport (VIK_LAYERS_PANEL(vlp)), coord );
2677 vik_layers_panel_emit_update ( VIK_LAYERS_PANEL(vlp) );
2678 }
2679 else {
2680 /* since vlp not set, vl & vvp should be valid instead! */
2681 if ( vl && vvp ) {
2682 vik_viewport_set_center_coord ( VIK_VIEWPORT(vvp), coord );
2683 vik_layer_emit_update ( VIK_LAYER(vl) );
2684 }
2685 }
50a14534
EB
2686}
2687
6bb72350 2688static void trw_layer_goto_track_startpoint ( gpointer pass_along[6] )
50a14534
EB
2689{
2690 GList *trps = ((VikTrack *) g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, pass_along[3] ))->trackpoints;
2691 if ( trps && trps->data )
6bb72350 2692 goto_coord ( pass_along[1], pass_along[0], pass_along[5], &(((VikTrackpoint *) trps->data)->coord));
50a14534
EB
2693}
2694
6bb72350 2695static void trw_layer_goto_track_center ( gpointer pass_along[6] )
50a14534 2696{
8fb71d6c 2697 /* FIXME: get this into viktrack.c, and should be ->trackpoints right? */
50a14534
EB
2698 GList **trps = g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, pass_along[3] );
2699 if ( trps && *trps )
2700 {
2701 struct LatLon average, maxmin[2] = { {0,0}, {0,0} };
2702 VikCoord coord;
2703 trw_layer_find_maxmin_tracks ( NULL, trps, maxmin );
2704 average.lat = (maxmin[0].lat+maxmin[1].lat)/2;
2705 average.lon = (maxmin[0].lon+maxmin[1].lon)/2;
2706 vik_coord_load_from_latlon ( &coord, VIK_TRW_LAYER(pass_along[0])->coord_mode, &average );
6bb72350 2707 goto_coord ( pass_along[1], pass_along[0], pass_along[5], &coord);
50a14534
EB
2708 }
2709}
2710
8fb71d6c
EB
2711static void trw_layer_extend_track_end ( gpointer pass_along[6] )
2712{
2713 VikTrwLayer *vtl = VIK_TRW_LAYER(pass_along[0]);
2714 VikTrack *track = g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, pass_along[3] );
2715
2716 vtl->current_track = track;
a7955c1d 2717 vik_window_enable_layer_tool ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)), VIK_LAYER_TRW, TOOL_CREATE_TRACK);
8fb71d6c
EB
2718
2719 if ( track->trackpoints )
6bb72350 2720 goto_coord ( pass_along[1], pass_along[0], pass_along[5], &(((VikTrackpoint *)g_list_last(track->trackpoints)->data)->coord) );
8fb71d6c
EB
2721}
2722
e3154bef
GB
2723/**
2724 * extend a track using magic scissors
2725 */
a7955c1d
AM
2726static void trw_layer_extend_track_end_ms ( gpointer pass_along[6] )
2727{
2728 VikTrwLayer *vtl = VIK_TRW_LAYER(pass_along[0]);
2729 VikTrack *track = g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, pass_along[3] );
2730 VikCoord last_coord = (((VikTrackpoint *)g_list_last(track->trackpoints)->data)->coord);
2731
2732 vik_window_enable_layer_tool ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)), VIK_LAYER_TRW, NUM_TOOLS );
2733 vtl->magic_scissors_coord = last_coord;
2734 vtl->magic_scissors_current_track = track;
f751ecd9 2735 vtl->magic_scissors_started = TRUE;
a7955c1d
AM
2736
2737 if ( track->trackpoints )
6bb72350 2738 goto_coord ( pass_along[1], pass_along[0], pass_along[5], &last_coord) ;
a7955c1d
AM
2739
2740}
a7955c1d 2741
ad0a8c2d
EB
2742static void trw_layer_apply_dem_data ( gpointer pass_along[6] )
2743{
2744 /* TODO: check & warn if no DEM data, or no applicable DEM data. */
2745 /* Also warn if overwrite old elevation data */
2746 VikTrack *track = (VikTrack *) g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, pass_along[3] );
2747
55906514 2748 vik_track_apply_dem_data ( track );
ad0a8c2d
EB
2749}
2750
50a14534
EB
2751static void trw_layer_goto_track_endpoint ( gpointer pass_along[6] )
2752{
2753 GList *trps = ((VikTrack *) g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, pass_along[3] ))->trackpoints;
2754 if ( !trps )
2755 return;
111fa174 2756 trps = g_list_last(trps);
6bb72350 2757 goto_coord ( pass_along[1], pass_along[0], pass_along[5], &(((VikTrackpoint *) trps->data)->coord));
50a14534
EB
2758}
2759
6bb72350 2760static void trw_layer_goto_track_max_speed ( gpointer pass_along[6] )
03e7da75
RN
2761{
2762 VikTrackpoint* vtp = vik_track_get_tp_by_max_speed ( g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, pass_along[3] ) );
2763 if ( !vtp )
2764 return;
6bb72350 2765 goto_coord ( pass_along[1], pass_along[0], pass_along[5], &(vtp->coord));
03e7da75
RN
2766}
2767
6bb72350 2768static void trw_layer_goto_track_max_alt ( gpointer pass_along[6] )
c28faca8
RN
2769{
2770 VikTrackpoint* vtp = vik_track_get_tp_by_max_alt ( g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, pass_along[3] ) );
2771 if ( !vtp )
2772 return;
6bb72350 2773 goto_coord ( pass_along[1], pass_along[0], pass_along[5], &(vtp->coord));
c28faca8
RN
2774}
2775
6bb72350 2776static void trw_layer_goto_track_min_alt ( gpointer pass_along[6] )
c28faca8
RN
2777{
2778 VikTrackpoint* vtp = vik_track_get_tp_by_min_alt ( g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, pass_along[3] ) );
2779 if ( !vtp )
2780 return;
6bb72350 2781 goto_coord ( pass_along[1], pass_along[0], pass_along[5], &(vtp->coord));
c28faca8 2782}
111fa174 2783
469113fb
RN
2784/*
2785 * Automatically change the viewport to center on the track and zoom to see the extent of the track
2786 */
2787static void trw_layer_auto_track_view ( gpointer pass_along[5] )
2788{
2789 GList **trps = g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, pass_along[3] );
2790 if ( trps && *trps )
2791 {
2792 struct LatLon maxmin[2] = { {0,0}, {0,0} };
2793 trw_layer_find_maxmin_tracks ( NULL, trps, maxmin );
6bb72350
RN
2794 trw_layer_zoom_to_show_latlons ( VIK_TRW_LAYER(pass_along[0]), pass_along[5], maxmin );
2795 if ( pass_along[1] )
2796 vik_layers_panel_emit_update ( VIK_LAYERS_PANEL(pass_along[1]) );
2797 else
2798 vik_layer_emit_update ( VIK_LAYER(pass_along[0]) );
469113fb
RN
2799 }
2800}
2801
c95d6b00
RN
2802static void trw_layer_edit_trackpoint ( gpointer pass_along[6] )
2803{
2804 VikTrwLayer *vtl = VIK_TRW_LAYER(pass_along[0]);
2805 trw_layer_tpwin_init ( vtl );
2806}
2807
111fa174
AF
2808/*************************************
2809 * merge/split by time routines
2810 *************************************/
2811
15f45edc
QT
2812/* called for each key in track hash table.
2813 * If the current track has time stamp, add it to the result,
2814 * except the one pointed by "exclude".
2815 * set exclude to NULL if there is no exclude to check.
2816 * Not that result is in reverse (for performance reason).
2817 */
2818typedef struct {
2819 GList **result;
2820 GList *exclude;
2821} twt_udata;
2822static void find_tracks_with_timestamp(gpointer key, gpointer value, gpointer udata)
2823{
2824 twt_udata *user_data = udata;
2825 VikTrackpoint *p1, *p2;
2826
2827 if (VIK_TRACK(value)->trackpoints == user_data->exclude) {
2828 return;
2829 }
2830
2831 if (VIK_TRACK(value)->trackpoints) {
2832 p1 = VIK_TRACKPOINT(VIK_TRACK(value)->trackpoints->data);
2833 p2 = VIK_TRACKPOINT(g_list_last(VIK_TRACK(value)->trackpoints)->data);
2834
2835 if (!p1->has_timestamp || !p2->has_timestamp) {
2836 g_print("no timestamp\n");
2837 return;
2838 }
2839
2840 }
2841
2842 *(user_data->result) = g_list_prepend(*(user_data->result), key);
2843}
2844
111fa174
AF
2845/* called for each key in track hash table. if original track user_data[1] is close enough
2846 * to the passed one, add it to list in user_data[0]
2847 */
2848static void find_nearby_track(gpointer key, gpointer value, gpointer user_data)
2849{
2850 time_t t1, t2;
2851 VikTrackpoint *p1, *p2;
2852
2853 GList **nearby_tracks = ((gpointer *)user_data)[0];
2854 GList *orig_track = ((gpointer *)user_data)[1];
dc2c040e 2855 guint thr = GPOINTER_TO_UINT (((gpointer *)user_data)[2]);
111fa174 2856
a61b2619
AF
2857 /* outline:
2858 * detect reasons for not merging, and return
2859 * if no reason is found not to merge, then do it.
2860 */
111fa174
AF
2861
2862 if (VIK_TRACK(value)->trackpoints == orig_track) {
2863 return;
2864 }
2865
a61b2619
AF
2866 t1 = VIK_TRACKPOINT(orig_track->data)->timestamp;
2867 t2 = VIK_TRACKPOINT(g_list_last(orig_track)->data)->timestamp;
111fa174 2868
a61b2619
AF
2869 if (VIK_TRACK(value)->trackpoints) {
2870 p1 = VIK_TRACKPOINT(VIK_TRACK(value)->trackpoints->data);
2871 p2 = VIK_TRACKPOINT(g_list_last(VIK_TRACK(value)->trackpoints)->data);
2872
2873 if (!p1->has_timestamp || !p2->has_timestamp) {
2874 g_print("no timestamp\n");
2875 return;
2876 }
111fa174 2877
a61b2619
AF
2878 /* g_print("Got track named %s, times %d, %d\n", (gchar *)key, p1->timestamp, p2->timestamp); */
2879 if (! (abs(t1 - p2->timestamp) < thr*60 ||
2880 /* p1 p2 t1 t2 */
2881 abs(p1->timestamp - t2) < thr*60)
2882 /* t1 t2 p1 p2 */
2883 ) {
2884 return;
2885 }
111fa174 2886 }
a61b2619
AF
2887
2888 *nearby_tracks = g_list_prepend(*nearby_tracks, key);
111fa174
AF
2889}
2890
2891/* comparison function used to sort tracks; a and b are hash table keys */
2892static gint track_compare(gconstpointer a, gconstpointer b, gpointer user_data)
2893{
2894 GHashTable *tracks = user_data;
2895 time_t t1, t2;
2896
2897 t1 = VIK_TRACKPOINT(VIK_TRACK(g_hash_table_lookup(tracks, a))->trackpoints->data)->timestamp;
2898 t2 = VIK_TRACKPOINT(VIK_TRACK(g_hash_table_lookup(tracks, b))->trackpoints->data)->timestamp;
2899
2900 if (t1 < t2) return -1;
2901 if (t1 > t2) return 1;
2902 return 0;
2903}
2904
2905/* comparison function used to sort trackpoints */
2906static gint trackpoint_compare(gconstpointer a, gconstpointer b)
2907{
2908 time_t t1 = VIK_TRACKPOINT(a)->timestamp, t2 = VIK_TRACKPOINT(b)->timestamp;
2909
2910 if (t1 < t2) return -1;
2911 if (t1 > t2) return 1;
2912 return 0;
2913}
2914
fb2306f7
RN
2915#ifdef VIK_CONFIG_ALPHABETIZED_TRW
2916/**
2917 * comparison function which can be used to sort tracks or waypoints by name
2918 */
2919static gint sort_alphabetically (gconstpointer a, gconstpointer b, gpointer user_data)
2920{
2921 const gchar* namea = (const gchar*) a;
2922 const gchar* nameb = (const gchar*) b;
2923 if ( namea == NULL || nameb == NULL)
2924 return 0;
2925 else
2926 // Same sort method as used in the vik_treeview_*_alphabetize functions
2927 return strcmp ( namea, nameb );
2928}
2929#endif
2930
291edcab
HR
2931static void trw_layer_merge_with_other ( gpointer pass_along[6] )
2932{
2933 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
15f45edc 2934 gchar *orig_track_name = pass_along[3];
15f45edc
QT
2935 GList *tracks_with_timestamp = NULL;
2936 VikTrack *track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, orig_track_name );
2937
2938 if (track->trackpoints &&
2939 !VIK_TRACKPOINT(track->trackpoints->data)->has_timestamp) {
2940 a_dialog_error_msg(VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Failed. This track does not have timestamp"));
2941 return;
2942 }
2943
2944 if (1) {
2945
2946 twt_udata udata;
2947 udata.result = &tracks_with_timestamp;
2948 udata.exclude = track->trackpoints;
2949 g_hash_table_foreach(vtl->tracks, find_tracks_with_timestamp, (gpointer)&udata);
2950 tracks_with_timestamp = g_list_reverse(tracks_with_timestamp);
2951 }
291edcab 2952
15f45edc
QT
2953 if (!tracks_with_timestamp) {
2954 a_dialog_error_msg(VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Failed. No other track in this layer has timestamp"));
2955 return;
2956 }
2957
8352326e
RN
2958#ifdef VIK_CONFIG_ALPHABETIZED_TRW
2959 // Sort alphabetically for user presentation
2960 tracks_with_timestamp = g_list_sort_with_data (tracks_with_timestamp, sort_alphabetically, NULL);
2961#endif
2962
7767aa02 2963 GList *merge_list = a_dialog_select_from_list(VIK_GTK_WINDOW_FROM_LAYER(vtl),
02bba540 2964 tracks_with_timestamp, TRUE,
7767aa02 2965 _("Merge with..."), _("Select track to merge with"));
15f45edc 2966 g_list_free(tracks_with_timestamp);
291edcab 2967
7767aa02 2968 if (merge_list)
291edcab 2969 {
7767aa02
QT
2970 GList *l;
2971 for (l = merge_list; l != NULL; l = g_list_next(l)) {
2972 VikTrack *merge_track = (VikTrack *) g_hash_table_lookup (vtl->tracks, l->data );
2973 if (merge_track) {
2974 track->trackpoints = g_list_concat(track->trackpoints, merge_track->trackpoints);
2975 merge_track->trackpoints = NULL;
2976 vik_trw_layer_delete_track(vtl, l->data);
2977 track->trackpoints = g_list_sort(track->trackpoints, trackpoint_compare);
2978 }
291edcab 2979 }
7767aa02
QT
2980 /* TODO: free data before free merge_list */
2981 for (l = merge_list; l != NULL; l = g_list_next(l))
2982 g_free(l->data);
2983 g_list_free(merge_list);
15f45edc 2984 vik_layer_emit_update( VIK_LAYER(vtl) );
291edcab 2985 }
291edcab
HR
2986}
2987
111fa174
AF
2988/* merge by time routine */
2989static void trw_layer_merge_by_timestamp ( gpointer pass_along[6] )
2990{
a61b2619
AF
2991 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
2992 gchar *orig_track_name = strdup(pass_along[3]);
2993
111fa174 2994 time_t t1, t2;
a61b2619 2995 GList *nearby_tracks;
111fa174
AF
2996 VikTrack *track;
2997 GList *trps;
2998 static guint thr = 1;
2999 guint track_count = 0;
111fa174 3000
a61b2619 3001 if (!a_dialog_time_threshold(VIK_GTK_WINDOW_FROM_LAYER(vtl),
4c77d5e0 3002 _("Merge Threshold..."),
a61b2619 3003 _("Merge when time between tracks less than:"),
111fa174 3004 &thr)) {
3b36279c 3005 free(orig_track_name);
111fa174
AF
3006 return;
3007 }
3008
3009 /* merge tracks until we can't */
a61b2619 3010 nearby_tracks = NULL;
111fa174
AF
3011 do {
3012 gpointer params[3];
3013
a61b2619 3014 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, orig_track_name );
111fa174
AF
3015 trps = track->trackpoints;
3016 if ( !trps )
3017 return;
3018
3019
3020 if (nearby_tracks) {
3021 g_list_free(nearby_tracks);
3022 nearby_tracks = NULL;
3023 }
3024
3025 t1 = ((VikTrackpoint *)trps->data)->timestamp;
3026 t2 = ((VikTrackpoint *)g_list_last(trps)->data)->timestamp;
3027
70a23263 3028 /* g_print("Original track times: %d and %d\n", t1, t2); */
111fa174
AF
3029 params[0] = &nearby_tracks;
3030 params[1] = trps;
dc2c040e 3031 params[2] = GUINT_TO_POINTER (thr);
111fa174
AF
3032
3033 /* get a list of adjacent-in-time tracks */
a61b2619 3034 g_hash_table_foreach(vtl->tracks, find_nearby_track, (gpointer)params);
111fa174
AF
3035
3036 /* add original track */
3037 nearby_tracks = g_list_prepend(nearby_tracks, orig_track_name);
3038
111fa174
AF
3039 /* merge them */
3040 {
a61b2619 3041#define get_track(x) VIK_TRACK(g_hash_table_lookup(vtl->tracks, (gchar *)((x)->data)))
111fa174
AF
3042#define get_first_trackpoint(x) VIK_TRACKPOINT(get_track(x)->trackpoints->data)
3043#define get_last_trackpoint(x) VIK_TRACKPOINT(g_list_last(get_track(x)->trackpoints)->data)
3044 GList *l = nearby_tracks;
3045 VikTrack *tr = vik_track_new();
3046 tr->visible = track->visible;
3047 track_count = 0;
3048 while (l) {
3049 /*
3050 time_t t1, t2;
3051 t1 = get_first_trackpoint(l)->timestamp;
3052 t2 = get_last_trackpoint(l)->timestamp;
70a23263 3053 g_print(" %20s: track %d - %d\n", (char *)l->data, (int)t1, (int)t2);
111fa174
AF
3054 */
3055
3056
3057 /* remove trackpoints from merged track, delete track */
3058 tr->trackpoints = g_list_concat(tr->trackpoints, get_track(l)->trackpoints);
3059 get_track(l)->trackpoints = NULL;
a61b2619 3060 vik_trw_layer_delete_track(vtl, l->data);
111fa174
AF
3061
3062 track_count ++;
3063 l = g_list_next(l);
3064 }
3065 tr->trackpoints = g_list_sort(tr->trackpoints, trackpoint_compare);
a61b2619 3066 vik_trw_layer_add_track(vtl, strdup(orig_track_name), tr);
111fa174
AF
3067
3068#undef get_first_trackpoint
3069#undef get_last_trackpoint
3070#undef get_track
3071 }
3072 } while (track_count > 1);
3073 g_list_free(nearby_tracks);
3074 free(orig_track_name);
0654760a 3075 vik_layer_emit_update( VIK_LAYER(vtl) );
111fa174
AF
3076}
3077
3078/* split by time routine */
3079static void trw_layer_split_by_timestamp ( gpointer pass_along[6] )
3080{
3081 VikTrack *track = (VikTrack *) g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, pass_along[3] );
3082 GList *trps = track->trackpoints;
3083 GList *iter;
3084 GList *newlists = NULL;
3085 GList *newtps = NULL;
3086 guint i;
3087 static guint thr = 1;
3088
3089 time_t ts, prev_ts;
3090
3091 if ( !trps )
3092 return;
3093
3094 if (!a_dialog_time_threshold(VIK_GTK_WINDOW_FROM_LAYER(pass_along[0]),
4c77d5e0
GB
3095 _("Split Threshold..."),
3096 _("Split when time between trackpoints exceeds:"),
111fa174
AF
3097 &thr)) {
3098 return;
3099 }
3100
3101 /* iterate through trackpoints, and copy them into new lists without touching original list */
3102 prev_ts = VIK_TRACKPOINT(trps->data)->timestamp;
3103 iter = trps;
3104
3105 while (iter) {
3106 ts = VIK_TRACKPOINT(iter->data)->timestamp;
3107 if (ts < prev_ts) {
70a23263 3108 g_print("panic: ts < prev_ts: this should never happen!\n");
111fa174
AF
3109 return;
3110 }
3111 if (ts - prev_ts > thr*60) {
3112 /* flush accumulated trackpoints into new list */
3113 newlists = g_list_prepend(newlists, g_list_reverse(newtps));
3114 newtps = NULL;
3115 }
3116
3117 /* accumulate trackpoint copies in newtps, in reverse order */
3118 newtps = g_list_prepend(newtps, vik_trackpoint_copy(VIK_TRACKPOINT(iter->data)));
3119 prev_ts = ts;
3120 iter = g_list_next(iter);
3121 }
3122 if (newtps) {
3123 newlists = g_list_prepend(newlists, g_list_reverse(newtps));
3124 }
3125
3126 /* put lists of trackpoints into tracks */
3127 iter = newlists;
3128 i = 1;
3129 while (iter) {
3130 gchar *new_tr_name;
3131 VikTrack *tr;
3132
3133 tr = vik_track_new();
3134 tr->visible = track->visible;
3135 tr->trackpoints = (GList *)(iter->data);
3136
3137 new_tr_name = g_strdup_printf("%s #%d", (gchar *) pass_along[3], i++);
3138 vik_trw_layer_add_track(VIK_TRW_LAYER(pass_along[0]), new_tr_name, tr);
70a23263 3139 /* g_print("adding track %s, times %d - %d\n", new_tr_name, VIK_TRACKPOINT(tr->trackpoints->data)->timestamp,
111fa174
AF
3140 VIK_TRACKPOINT(g_list_last(tr->trackpoints)->data)->timestamp);*/
3141
3142 iter = g_list_next(iter);
3143 }
3144 g_list_free(newlists);
e4afc73a 3145 vik_trw_layer_delete_track(VIK_TRW_LAYER(pass_along[0]), (gchar *)pass_along[3]);
111fa174
AF
3146 vik_layer_emit_update(VIK_LAYER(pass_along[0]));
3147}
3148
af2341f3
RN
3149/**
3150 * Split a track by the number of points as specified by the user
3151 */
3152static void trw_layer_split_by_n_points ( gpointer pass_along[6] )
3153{
3154 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
3155 VikTrack *track = (VikTrack *)g_hash_table_lookup ( vtl->tracks, pass_along[3] );
3156
3157 // Check valid track
3158 GList *trps = track->trackpoints;
3159 if ( !trps )
3160 return;
3161
3162 gint points = a_dialog_get_positive_number(VIK_GTK_WINDOW_FROM_LAYER(pass_along[0]),
3163 _("Split Every Nth Point"),
3164 _("Split on every Nth point:"),
3165 250, // Default value as per typical limited track capacity of various GPS devices
3166 2, // Min
3167 65536, // Max
3168 5); // Step
3169 // Was a valid number returned?
3170 if (!points)
3171 return;
3172
3173 // Now split...
3174 GList *iter;
3175 GList *newlists = NULL;
3176 GList *newtps = NULL;
3177 gint count = 0;
3178 iter = trps;
3179
3180 while (iter) {
3181 /* accumulate trackpoint copies in newtps, in reverse order */
3182 newtps = g_list_prepend(newtps, vik_trackpoint_copy(VIK_TRACKPOINT(iter->data)));
3183 count++;
3184 if (count >= points) {
3185 /* flush accumulated trackpoints into new list */
3186 newlists = g_list_append(newlists, g_list_reverse(newtps));
3187 newtps = NULL;
3188 count = 0;
3189 }
3190 iter = g_list_next(iter);
3191 }
3192
3193 // If there is a remaining chunk put that into the new split list
3194 // This may well be the whole track if no split points were encountered
3195 if (newtps) {
3196 newlists = g_list_append(newlists, g_list_reverse(newtps));
3197 }
3198
3199 /* put lists of trackpoints into tracks */
3200 iter = newlists;
3201 guint i = 1;
3202 // Only bother updating if the split results in new tracks
3203 if (g_list_length (newlists) > 1) {
3204 while (iter) {
3205 gchar *new_tr_name;
3206 VikTrack *tr;
3207
3208 tr = vik_track_new();
3209 tr->visible = track->visible;
3210 tr->trackpoints = (GList *)(iter->data);
3211
3212 new_tr_name = g_strdup_printf("%s #%d", (gchar *) pass_along[3], i++);
3213 vik_trw_layer_add_track(VIK_TRW_LAYER(pass_along[0]), new_tr_name, tr);
3214
3215 iter = g_list_next(iter);
3216 }
3217 // Remove original track and then update the display
3218 vik_trw_layer_delete_track(VIK_TRW_LAYER(pass_along[0]), (gchar *)pass_along[3]);
3219 vik_layer_emit_update(VIK_LAYER(pass_along[0]));
3220 }
3221 g_list_free(newlists);
3222}
3223
111fa174
AF
3224/* end of split/merge routines */
3225
3226
6bb72350 3227static void trw_layer_goto_waypoint ( gpointer pass_along[6] )
50a14534
EB
3228{
3229 VikWaypoint *wp = g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->waypoints, pass_along[3] );
3230 if ( wp )
6bb72350 3231 goto_coord ( pass_along[1], pass_along[0], pass_along[5], &(wp->coord) );
50a14534
EB
3232}
3233
6bb72350 3234static void trw_layer_waypoint_gc_webpage ( gpointer pass_along[6] )
50a14534
EB
3235{
3236 gchar *webpage = g_strdup_printf("http://www.geocaching.com/seek/cache_details.aspx?wp=%s", (gchar *) pass_along[3] );
7d02a0b0 3237 open_url(VIK_GTK_WINDOW_FROM_LAYER(VIK_LAYER(pass_along[0])), webpage);
50a14534
EB
3238 g_free ( webpage );
3239}
3240
3241const gchar *vik_trw_layer_sublayer_rename_request ( VikTrwLayer *l, const gchar *newname, gpointer vlp, gint subtype, gpointer sublayer, GtkTreeIter *iter )
3242{
3243 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT )
3244 {
50a14534
EB
3245 gchar *rv;
3246 VikWaypoint *wp;
3247
8499a412 3248 if (strcmp(newname, sublayer) == 0 )
50a14534
EB
3249 return NULL;
3250
8499a412
QT
3251 if (strcasecmp(newname, sublayer)) { /* Not just changing case */
3252 if (g_hash_table_lookup( l->waypoints, newname))
3253 {
3254 a_dialog_error_msg ( VIK_GTK_WINDOW_FROM_LAYER(l), _("Waypoint Already Exists") );
3255 return NULL;
3256 }
50a14534
EB
3257 }
3258
50a14534
EB
3259 iter = g_hash_table_lookup ( l->waypoints_iters, sublayer );
3260 g_hash_table_steal ( l->waypoints_iters, sublayer );
3261
e4afc73a 3262 wp = vik_waypoint_copy ( VIK_WAYPOINT(g_hash_table_lookup ( l->waypoints, sublayer )) );
a8fe53f8 3263 highest_wp_number_remove_wp(l, sublayer);
e4afc73a
EB
3264 g_hash_table_remove ( l->waypoints, sublayer );
3265
50a14534 3266 rv = g_strdup(newname);
50a14534
EB
3267
3268 vik_treeview_item_set_pointer ( VIK_LAYER(l)->vt, iter, rv );
3269
a8fe53f8 3270 highest_wp_number_add_wp(l, rv);
50a14534
EB
3271 g_hash_table_insert ( l->waypoints, rv, wp );
3272 g_hash_table_insert ( l->waypoints_iters, rv, iter );
3273
3274 /* it hasn't been updated yet so we pass new name */
3275#ifdef VIK_CONFIG_ALPHABETIZED_TRW
3276 vik_treeview_sublayer_realphabetize ( VIK_LAYER(l)->vt, iter, rv );
3277#endif
3278
3279 vik_layers_panel_emit_update ( VIK_LAYERS_PANEL(vlp) );
3280 return rv;
3281 }
3282 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
3283 {
50a14534
EB
3284 gchar *rv;
3285 VikTrack *tr;
3286 GtkTreeIter *iter;
3287 gchar *orig_key;
3288
8499a412 3289 if (strcmp(newname, sublayer) == 0)
50a14534
EB
3290 return NULL;
3291
8499a412 3292 if (strcasecmp(newname, sublayer)) { /* Not just changing case */
c8de7dd7 3293 if (g_hash_table_lookup( l->tracks, newname))
8499a412
QT
3294 {
3295 a_dialog_error_msg ( VIK_GTK_WINDOW_FROM_LAYER(l), _("Track Already Exists") );
3296 return NULL;
3297 }
50a14534
EB
3298 }
3299
886031df 3300 g_hash_table_lookup_extended ( l->tracks, sublayer, (void *)&orig_key, (void *)&tr );
50a14534
EB
3301 g_hash_table_steal ( l->tracks, sublayer );
3302
3303 iter = g_hash_table_lookup ( l->tracks_iters, sublayer );
3304 g_hash_table_steal ( l->tracks_iters, sublayer );
3305
3306 rv = g_strdup(newname);
50a14534
EB
3307
3308 vik_treeview_item_set_pointer ( VIK_LAYER(l)->vt, iter, rv );
3309
3310 g_hash_table_insert ( l->tracks, rv, tr );
3311 g_hash_table_insert ( l->tracks_iters, rv, iter );
3312
3313 /* don't forget about current_tp_track_name, update that too */
3314 if ( l->current_tp_track_name && g_strcasecmp(orig_key,l->current_tp_track_name) == 0 )
3315 {
3316 l->current_tp_track_name = rv;
3317 if ( l->tpwin )
3318 vik_trw_layer_tpwin_set_track_name ( l->tpwin, rv );
3319 }
3320 else if ( l->last_tp_track_name && g_strcasecmp(orig_key,l->last_tp_track_name) == 0 )
3321 l->last_tp_track_name = rv;
3322
3323 g_free ( orig_key );
3324
3325#ifdef VIK_CONFIG_ALPHABETIZED_TRW
3326 vik_treeview_sublayer_realphabetize ( VIK_LAYER(l)->vt, iter, rv );
3327#endif
3328
3329 vik_layers_panel_emit_update ( VIK_LAYERS_PANEL(vlp) );
3330 return rv;
3331 }
3332 return NULL;
3333}
3334
3335static gboolean is_valid_geocache_name ( gchar *str )
3336{
3337 gint len = strlen ( str );
0c1044e9 3338 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]));
50a14534
EB
3339}
3340
6bb72350 3341static void trw_layer_track_use_with_filter ( gpointer pass_along[6] )
28c82d8b
EB
3342{
3343 gchar *track_name = (gchar *) pass_along[3];
3344 VikTrack *tr = g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, track_name );
3345 a_acquire_set_filter_track ( tr, track_name );
3346}
3347
bddd2056
EB
3348static gboolean is_valid_google_route ( VikTrwLayer *vtl, const gchar *track_name )
3349{
3350 VikTrack *tr = g_hash_table_lookup ( vtl->tracks, track_name );
3351 return ( tr && tr->comment && strlen(tr->comment) > 7 && !strncmp(tr->comment, "from:", 5) );
3352}
3353
6bb72350 3354static void trw_layer_track_google_route_webpage ( gpointer pass_along[6] )
bddd2056
EB
3355{
3356 gchar *track_name = (gchar *) pass_along[3];
3357 VikTrack *tr = g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, track_name );
3358 if ( tr ) {
3359 gchar *escaped = uri_escape ( tr->comment );
3360 gchar *webpage = g_strdup_printf("http://maps.google.com/maps?f=q&hl=en&q=%s", escaped );
7d02a0b0 3361 open_url(VIK_GTK_WINDOW_FROM_LAYER(VIK_LAYER(pass_along[0])), webpage);
bddd2056 3362 g_free ( escaped );
bddd2056
EB
3363 g_free ( webpage );
3364 }
3365}
3366
50eadc64
RN
3367/* vlp can be NULL if necessary - i.e. right-click from a tool */
3368/* viewpoint is now available instead */
3369gboolean vik_trw_layer_sublayer_add_menu_items ( VikTrwLayer *l, GtkMenu *menu, gpointer vlp, gint subtype, gpointer sublayer, GtkTreeIter *iter, VikViewport *vvp )
50a14534
EB
3370{
3371 static GtkTreeIter staticiter;
6bb72350 3372 static gpointer pass_along[6];
50a14534
EB
3373 GtkWidget *item;
3374 gboolean rv = FALSE;
3375
3376 pass_along[0] = l;
3377 pass_along[1] = vlp;
dc2c040e 3378 pass_along[2] = GINT_TO_POINTER (subtype);
50a14534 3379 pass_along[3] = sublayer;
6bb72350
RN
3380 if ( iter ) {
3381 staticiter = *iter; /* will exist after function has ended */
3382 pass_along[4] = &staticiter;
3383 }
3384 pass_along[5] = vvp;
50a14534
EB
3385
3386 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT || subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
3387 {
3388 rv = TRUE;
3389
3390 item = gtk_image_menu_item_new_from_stock ( GTK_STOCK_PROPERTIES, NULL );
3391 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_properties_item), pass_along );
3392 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
3393 gtk_widget_show ( item );
3394
21700912
QT
3395 if (subtype == VIK_TRW_LAYER_SUBLAYER_TRACK) {
3396 VikTrwLayer *vtl = l;
3397 VikTrack *tr = g_hash_table_lookup ( vtl->tracks, sublayer );
3398 if (tr && tr->property_dialog)
3399 gtk_widget_set_sensitive(GTK_WIDGET(item), FALSE );
3400 }
3401
2cebc318
QT
3402 item = gtk_image_menu_item_new_from_stock ( GTK_STOCK_CUT, NULL );
3403 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_cut_item_cb), pass_along );
3404 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
3405 gtk_widget_show ( item );
3406
3407 item = gtk_image_menu_item_new_from_stock ( GTK_STOCK_COPY, NULL );
3408 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_copy_item_cb), pass_along );
3409 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
3410 gtk_widget_show ( item );
3411
50a14534
EB
3412 item = gtk_image_menu_item_new_from_stock ( GTK_STOCK_DELETE, NULL );
3413 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_item), pass_along );
3414 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
3415 gtk_widget_show ( item );
3416
3417 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT )
3418 {
5ede6aa6
RN
3419 gboolean separator_created = FALSE;
3420
50a14534
EB
3421 /* could be a right-click using the tool */
3422 if ( vlp != NULL ) {
5ede6aa6
RN
3423 item = gtk_menu_item_new ();
3424 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
3425 gtk_widget_show ( item );
3426
3427 separator_created = TRUE;
3428
92dce2ef 3429 item = gtk_menu_item_new_with_mnemonic ( _("_Goto") );
50a14534
EB
3430 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_waypoint), pass_along );
3431 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
3432 gtk_widget_show ( item );
3433 }
3434
3435 if ( is_valid_geocache_name ( (gchar *) sublayer ) )
3436 {
5ede6aa6
RN
3437 if ( !separator_created ) {
3438 item = gtk_menu_item_new ();
3439 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
3440 gtk_widget_show ( item );
a412f3f5 3441 separator_created = TRUE;
5ede6aa6
RN
3442 }
3443
92dce2ef 3444 item = gtk_menu_item_new_with_mnemonic ( _("_Visit Geocache Webpage") );
50a14534
EB
3445 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_waypoint_gc_webpage), pass_along );
3446 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
3447 gtk_widget_show ( item );
3448 }
3449
a412f3f5
RN
3450 VikWaypoint *wp = g_hash_table_lookup ( VIK_TRW_LAYER(l)->waypoints, sublayer );
3451
3452 if ( wp && wp->image )
3453 {
3454 if ( !separator_created ) {
3455 item = gtk_menu_item_new ();
3456 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
3457 gtk_widget_show ( item );
3458 separator_created = TRUE;
3459 }
3460
3461 // Set up image paramater
3462 pass_along[5] = wp->image;
3463
3464 item = gtk_menu_item_new_with_mnemonic ( _("_Show Picture...") );
3465 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_show_picture), pass_along );
3466 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
3467 gtk_widget_show ( item );
3468 }
3469
50a14534
EB
3470 }
3471 }
3472
5ede6aa6
RN
3473 if ( vlp && (subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINTS || subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT) )
3474 {
b66bb4ab 3475 rv = TRUE;
5ede6aa6
RN
3476 item = gtk_menu_item_new_with_mnemonic ( _("_New Waypoint") );
3477 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_wp), pass_along );
3478 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3479 gtk_widget_show ( item );
3480 }
3481
50a14534
EB
3482 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
3483 {
937b36ed 3484 GtkWidget *goto_submenu;
50a14534
EB
3485 item = gtk_menu_item_new ();
3486 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
3487 gtk_widget_show ( item );
3488
937b36ed
RN
3489 goto_submenu = gtk_menu_new ();
3490 item = gtk_menu_item_new_with_mnemonic ( _("_Goto") );
3491 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3492 gtk_widget_show ( item );
3493 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), goto_submenu );
3494
3495 item = gtk_menu_item_new_with_mnemonic ( _("_Startpoint") );
50a14534 3496 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_startpoint), pass_along );
937b36ed 3497 gtk_menu_shell_append ( GTK_MENU_SHELL(goto_submenu), item );
50a14534
EB
3498 gtk_widget_show ( item );
3499
937b36ed 3500 item = gtk_menu_item_new_with_mnemonic ( _("\"_Center\"") );
50a14534 3501 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_center), pass_along );
937b36ed 3502 gtk_menu_shell_append ( GTK_MENU_SHELL(goto_submenu), item );
50a14534
EB
3503 gtk_widget_show ( item );
3504
937b36ed 3505 item = gtk_menu_item_new_with_mnemonic ( _("_Endpoint") );
50a14534 3506 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_endpoint), pass_along );
937b36ed 3507 gtk_menu_shell_append ( GTK_MENU_SHELL(goto_submenu), item );
50a14534 3508 gtk_widget_show ( item );
111fa174 3509
c28faca8
RN
3510 item = gtk_menu_item_new_with_mnemonic ( _("_Highest Altitude") );
3511 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_max_alt), pass_along );
3512 gtk_menu_shell_append ( GTK_MENU_SHELL(goto_submenu), item );
3513 gtk_widget_show ( item );
3514
3515 item = gtk_menu_item_new_with_mnemonic ( _("_Lowest Altitude") );
3516 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_min_alt), pass_along );
3517 gtk_menu_shell_append ( GTK_MENU_SHELL(goto_submenu), item );
3518 gtk_widget_show ( item );
3519
03e7da75
RN
3520 item = gtk_menu_item_new_with_mnemonic ( _("_Maximum Speed") );
3521 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_max_speed), pass_along );
3522 gtk_menu_shell_append ( GTK_MENU_SHELL(goto_submenu), item );
3523 gtk_widget_show ( item );
3524
469113fb
RN
3525 item = gtk_menu_item_new_with_mnemonic ( _("_View Track") );
3526 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_track_view), pass_along );
3527 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
3528 gtk_widget_show ( item );
3529
40a68e7c 3530 item = gtk_menu_item_new_with_mnemonic ( _("_Merge By Time") );
111fa174
AF
3531 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_merge_by_timestamp), pass_along );
3532 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
3533 gtk_widget_show ( item );
3534
40a68e7c 3535 item = gtk_menu_item_new_with_mnemonic ( _("Merge _With Other Tracks...") );
291edcab
HR
3536 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_merge_with_other), pass_along );
3537 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
3538 gtk_widget_show ( item );
3539
40a68e7c 3540 item = gtk_menu_item_new_with_mnemonic ( _("_Split By Time") );
111fa174
AF
3541 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_split_by_timestamp), pass_along );
3542 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
3543 gtk_widget_show ( item );
7114e879 3544
af2341f3
RN
3545 item = gtk_menu_item_new_with_mnemonic ( _("Split By _Number of Points") );
3546 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_split_by_n_points), pass_along );
3547 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
3548 gtk_widget_show ( item );
3549
6bb72350
RN
3550 /* ATM This function is only available via the layers panel, due to the method in finding out the maps in use */
3551 if ( vlp ) {
3552 item = gtk_menu_item_new_with_mnemonic ( _("Down_load Maps Along Track...") );
3553 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_download_map_along_track_cb), pass_along );
3554 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
3555 gtk_widget_show ( item );
3556 }
ad0a8c2d 3557
40a68e7c 3558 item = gtk_menu_item_new_with_mnemonic ( _("_Apply DEM Data") );
ad0a8c2d
EB
3559 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_apply_dem_data), pass_along );
3560 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
8fb71d6c
EB
3561 gtk_widget_show ( item );
3562
7f6757c4
RN
3563 item = gtk_menu_item_new_with_mnemonic ( _("Export Trac_k as GPX") );
3564 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_gpx_track), pass_along );
3565 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
3566 gtk_widget_show ( item );
3567
40a68e7c 3568 item = gtk_menu_item_new_with_mnemonic ( _("E_xtend Track End") );
8fb71d6c
EB
3569 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_extend_track_end), pass_along );
3570 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
ad0a8c2d 3571 gtk_widget_show ( item );
5092de80 3572
40a68e7c 3573 item = gtk_menu_item_new_with_mnemonic ( _("Extend _Using Magic Scissors") );
a7955c1d
AM
3574 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_extend_track_end_ms), pass_along );
3575 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
3576 gtk_widget_show ( item );
3577
5092de80 3578#ifdef VIK_CONFIG_OPENSTREETMAP
40a68e7c 3579 item = gtk_menu_item_new_with_mnemonic ( _("Upload to _OSM") );
5092de80
GB
3580 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(osm_traces_upload_track_cb), pass_along );
3581 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
3582 gtk_widget_show ( item );
3583#endif
bddd2056
EB
3584
3585 if ( is_valid_google_route ( l, (gchar *) sublayer ) )
3586 {
40a68e7c 3587 item = gtk_menu_item_new_with_mnemonic ( _("_View Google Directions") );
bddd2056
EB
3588 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_track_google_route_webpage), pass_along );
3589 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
3590 gtk_widget_show ( item );
3591 }
3592
40a68e7c 3593 item = gtk_menu_item_new_with_mnemonic ( _("Use with _Filter") );
28c82d8b
EB
3594 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_track_use_with_filter), pass_along );
3595 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
3596 gtk_widget_show ( item );
3597
6bb72350
RN
3598 /* ATM This function is only available via the layers panel, due to needing a vlp */
3599 if ( vlp ) {
3600 item = a_acquire_track_menu ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(l)), vlp,
3601 vik_layers_panel_get_viewport(VIK_LAYERS_PANEL(vlp)),
3602 g_hash_table_lookup ( l->tracks, (gchar *) sublayer ) );
3603 if ( item ) {
3604 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3605 gtk_widget_show ( item );
3606 }
3607 }
c95d6b00
RN
3608
3609 // Only show on viewport popmenu when a trackpoint is selected
3610 if ( ! vlp && l->current_tpl ) {
3611 // Add separator
3612 item = gtk_menu_item_new ();
3613 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
3614 gtk_widget_show ( item );
3615
3616 item = gtk_image_menu_item_new_with_mnemonic ( _("_Edit Trackpoint") );
3617 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_PROPERTIES, GTK_ICON_SIZE_MENU) );
3618 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_edit_trackpoint), pass_along );
3619 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
3620 gtk_widget_show ( item );
3621 }
3622
50a14534
EB
3623 }
3624
50a14534
EB
3625 return rv;
3626}
3627
db79f75f
RN
3628static void trw_layer_insert_tp_after_current_tp ( VikTrwLayer *vtl )
3629{
3630 /* sanity checks */
3631 if (!vtl->current_tpl)
3632 return;
3633 if (!vtl->current_tpl->next)
3634 return;
3635
3636 VikTrackpoint *tp_current = VIK_TRACKPOINT(vtl->current_tpl->data);
3637 VikTrackpoint *tp_next = VIK_TRACKPOINT(vtl->current_tpl->next->data);
3638
3639 /* Use current and next trackpoints to form a new track point which is inserted into the tracklist */
3640 if ( tp_next ) {
3641
3642 VikTrackpoint *tp_new = vik_trackpoint_new();
3643 struct LatLon ll_current, ll_next;
3644 vik_coord_to_latlon ( &tp_current->coord, &ll_current );
3645 vik_coord_to_latlon ( &tp_next->coord, &ll_next );
3646
3647 /* main positional interpolation */
3648 struct LatLon ll_new = { (ll_current.lat+ll_next.lat)/2, (ll_current.lon+ll_next.lon)/2 };
3649 vik_coord_load_from_latlon ( &(tp_new->coord), vtl->coord_mode, &ll_new );
3650
3651 /* Now other properties that can be interpolated */
3652 tp_new->altitude = (tp_current->altitude + tp_next->altitude) / 2;
3653
3654 if (tp_current->has_timestamp && tp_next->has_timestamp) {
3655 /* Note here the division is applied to each part, then added
3656 This is to avoid potential overflow issues with a 32 time_t for dates after midpoint of this Unix time on 2004/01/04 */
3657 tp_new->timestamp = (tp_current->timestamp/2) + (tp_next->timestamp/2);
3658 tp_new->has_timestamp = TRUE;
3659 }
3660
3661 if (tp_current->speed != NAN && tp_next->speed != NAN)
3662 tp_new->speed = (tp_current->speed + tp_next->speed)/2;
3663
3664 /* TODO - improve interpolation of course, as it may not be correct.
3665 if courses in degrees are 350 + 020, the mid course more likely to be 005 (not 185)
3666 [similar applies if value is in radians] */
3667 if (tp_current->course != NAN && tp_next->course != NAN)
3668 tp_new->speed = (tp_current->course + tp_next->course)/2;
3669
3670 /* DOP / sat values remain at defaults as not they do not seem applicable to a dreamt up point */
3671
3672 /* Insert new point into the trackpoints list after the current TP */
3673 VikTrack *tr = g_hash_table_lookup ( vtl->tracks, vtl->current_tp_track_name );
3674 gint index = g_list_index ( tr->trackpoints, tp_current );
3675 if ( index > -1 ) {
3676 tr->trackpoints = g_list_insert (tr->trackpoints, tp_new, index+1 );
3677 }
3678 }
3679}
50a14534
EB
3680
3681/* to be called when last_tpl no long exists. */
3682static void trw_layer_cancel_last_tp ( VikTrwLayer *vtl )
3683{
3684 if ( vtl->tpwin ) /* can't join with a non-existant TP. */
3685 vik_trw_layer_tpwin_disable_join ( vtl->tpwin );
3686 vtl->last_tpl = NULL;
3687 vtl->last_tp_track_name = NULL;
3688}
3689
3690static void trw_layer_cancel_current_tp ( VikTrwLayer *vtl, gboolean destroy )
3691{
3692 if ( vtl->tpwin )
3693 {
3694 if ( destroy)
3695 {
3696 gtk_widget_destroy ( GTK_WIDGET(vtl->tpwin) );
3697 vtl->tpwin = NULL;
3698 }
3699 else
3700 vik_trw_layer_tpwin_set_empty ( vtl->tpwin );
3701 }
3702 if ( vtl->current_tpl )
3703 {
3704 vtl->current_tpl = NULL;
3705 vtl->current_tp_track_name = NULL;
3706 vik_layer_emit_update(VIK_LAYER(vtl));
3707 }
3708}
3709
3710static void trw_layer_tpwin_response ( VikTrwLayer *vtl, gint response )
3711{
3712 g_assert ( vtl->tpwin != NULL );
3713 if ( response == VIK_TRW_LAYER_TPWIN_CLOSE )
3714 trw_layer_cancel_current_tp ( vtl, TRUE );
3715 else if ( response == VIK_TRW_LAYER_TPWIN_SPLIT && vtl->current_tpl->next && vtl->current_tpl->prev )
3716 {
b3538521
RN
3717 gchar *name = get_new_unique_sublayer_name(vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, vtl->current_tp_track_name);
3718 if ( ( name = a_dialog_new_track ( GTK_WINDOW(vtl->tpwin), vtl->tracks, name ) ) )
50a14534
EB
3719 {
3720 VikTrack *tr = vik_track_new ();
3721 GList *newglist = g_list_alloc ();
3722 newglist->prev = NULL;
3723 newglist->next = vtl->current_tpl->next;
3724 newglist->data = vik_trackpoint_copy(VIK_TRACKPOINT(vtl->current_tpl->data));
3725 tr->trackpoints = newglist;
3726
3727 vtl->current_tpl->next->prev = newglist; /* end old track here */
3728 vtl->current_tpl->next = NULL;
3729
3730 vtl->current_tpl = newglist; /* change tp to first of new track. */
3731 vtl->current_tp_track_name = name;
3732
3733 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, vtl->current_tp_track_name );
3734
e632ab42
GB
3735 tr->visible = TRUE;
3736
50a14534
EB
3737 vik_trw_layer_add_track ( vtl, name, tr );
3738 vik_layer_emit_update(VIK_LAYER(vtl));
3739 }
3740 }
3741 else if ( response == VIK_TRW_LAYER_TPWIN_DELETE )
3742 {
3743 VikTrack *tr = g_hash_table_lookup ( vtl->tracks, vtl->current_tp_track_name );
3744 GList *new_tpl;
3745 g_assert(tr != NULL);
3746
3747 /* can't join with a non-existent trackpoint */
3748 vtl->last_tpl = NULL;
3749 vtl->last_tp_track_name = NULL;
3750
3751 if ( (new_tpl = vtl->current_tpl->next) || (new_tpl = vtl->current_tpl->prev) )
3752 {
3753 if ( VIK_TRACKPOINT(vtl->current_tpl->data)->newsegment && vtl->current_tpl->next )
3754 VIK_TRACKPOINT(vtl->current_tpl->next->data)->newsegment = TRUE; /* don't concat segments on del */
3755
3756 tr->trackpoints = g_list_remove_link ( tr->trackpoints, vtl->current_tpl ); /* this nulls current_tpl->prev and next */
3757
3758 /* at this point the old trackpoint exists, but the list links are correct (new), so it is safe to do this. */
3759 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, new_tpl, vtl->current_tp_track_name );
3760
3761 trw_layer_cancel_last_tp ( vtl );
3762
3763 g_free ( vtl->current_tpl->data ); /* TODO: vik_trackpoint_free() */
3764 g_list_free_1 ( vtl->current_tpl );
3765 vtl->current_tpl = new_tpl;
3766 vik_layer_emit_update(VIK_LAYER(vtl));
3767 }
3768 else
3769 {
3770 tr->trackpoints = g_list_remove_link ( tr->trackpoints, vtl->current_tpl );
3771 g_free ( vtl->current_tpl->data ); /* TODO longone: vik_trackpoint_new() and vik_trackpoint_free() */
3772 g_list_free_1 ( vtl->current_tpl );
3773 trw_layer_cancel_current_tp ( vtl, FALSE );
3774 }
3775 }
3776 else if ( response == VIK_TRW_LAYER_TPWIN_FORWARD && vtl->current_tpl->next )
3777 {
3778 vtl->last_tpl = vtl->current_tpl;
3779 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl = vtl->current_tpl->next, vtl->current_tp_track_name );
3780 vik_layer_emit_update(VIK_LAYER(vtl)); /* TODO longone: either move or only update if tp is inside drawing window */
3781 }
3782 else if ( response == VIK_TRW_LAYER_TPWIN_BACK && vtl->current_tpl->prev )
3783 {
3784 vtl->last_tpl = vtl->current_tpl;
3785 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl = vtl->current_tpl->prev, vtl->current_tp_track_name );
3786 vik_layer_emit_update(VIK_LAYER(vtl));
3787 }
3788 else if ( response == VIK_TRW_LAYER_TPWIN_JOIN )
3789 {
3790 VikTrack *tr1 = g_hash_table_lookup ( vtl->tracks, vtl->last_tp_track_name );
3791 VikTrack *tr2 = g_hash_table_lookup ( vtl->tracks, vtl->current_tp_track_name );
3792
3793 VikTrack *tr_first = tr1, *tr_last = tr2;
3794
3795 gchar *tmp;
3796
3797 if ( (!vtl->last_tpl->next) && (!vtl->current_tpl->next) ) /* both endpoints */
3798 vik_track_reverse ( tr2 ); /* reverse the second, that way second track clicked will be later. */
3799 else if ( (!vtl->last_tpl->prev) && (!vtl->current_tpl->prev) )
3800 vik_track_reverse ( tr1 );
3801 else if ( (!vtl->last_tpl->prev) && (!vtl->current_tpl->next) ) /* clicked startpoint, then endpoint -- concat end to start */
3802 {
3803 tr_first = tr2;
3804 tr_last = tr1;
3805 }
3806 /* default -- clicked endpoint then startpoint -- connect endpoint to startpoint */
3807
3808 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. */
3809 VIK_TRACKPOINT(tr_last->trackpoints->data)->newsegment = FALSE;
3810 tr1->trackpoints = g_list_concat ( tr_first->trackpoints, tr_last->trackpoints );
3811 tr2->trackpoints = NULL;
3812
3813 tmp = vtl->current_tp_track_name;
3814
3815 vtl->current_tp_track_name = vtl->last_tp_track_name; /* current_tp stays the same (believe it or not!) */
3816 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, vtl->current_tp_track_name );
3817
3818 /* if we did this before, trw_layer_delete_track would have canceled the current tp because
3819 * it was the current track. canceling the current tp would have set vtl->current_tpl to NULL */
e4afc73a 3820 vik_trw_layer_delete_track ( vtl, tmp );
50a14534
EB
3821
3822 trw_layer_cancel_last_tp ( vtl ); /* same TP, can't join. */
3823 vik_layer_emit_update(VIK_LAYER(vtl));
3824 }
2880a1de
RN
3825 else if ( response == VIK_TRW_LAYER_TPWIN_INSERT && vtl->current_tpl->next )
3826 {
3827 trw_layer_insert_tp_after_current_tp ( vtl );
3828 vik_layer_emit_update(VIK_LAYER(vtl));
3829 }
50a14534
EB
3830 else if ( response == VIK_TRW_LAYER_TPWIN_DATA_CHANGED )
3831 vik_layer_emit_update (VIK_LAYER(vtl));
3832}
3833
3834static void trw_layer_tpwin_init ( VikTrwLayer *vtl )
3835{
3836 if ( ! vtl->tpwin )
3837 {
3838 vtl->tpwin = vik_trw_layer_tpwin_new ( VIK_GTK_WINDOW_FROM_LAYER(vtl) );
3839 g_signal_connect_swapped ( GTK_DIALOG(vtl->tpwin), "response", G_CALLBACK(trw_layer_tpwin_response), vtl );
3840 /* connect signals -- DELETE SIGNAL VERY IMPORTANT TO SET TO NULL */
3841 g_signal_connect_swapped ( vtl->tpwin, "delete-event", G_CALLBACK(trw_layer_cancel_current_tp), vtl );
3842 gtk_widget_show_all ( GTK_WIDGET(vtl->tpwin) );
3843 }
3844 if ( vtl->current_tpl )
3845 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, vtl->current_tp_track_name );
3846 /* set layer name and TP data */
3847}
3848
941aa6e9
AF
3849/***************************************************************************
3850 ** Tool code
3851 ***************************************************************************/
50a14534 3852
941aa6e9 3853/*** Utility data structures and functions ****/
50a14534
EB
3854
3855typedef struct {
3856 gint x, y;
3857 gint closest_x, closest_y;
3858 gchar *closest_wp_name;
3859 VikWaypoint *closest_wp;
3860 VikViewport *vvp;
3861} WPSearchParams;
3862
941aa6e9
AF
3863typedef struct {
3864 gint x, y;
3865 gint closest_x, closest_y;
3866 gchar *closest_track_name;
3867 VikTrackpoint *closest_tp;
3868 VikViewport *vvp;
3869 GList *closest_tpl;
3870} TPSearchParams;
3871
50a14534
EB
3872static void waypoint_search_closest_tp ( gchar *name, VikWaypoint *wp, WPSearchParams *params )
3873{
3874 gint x, y;
3875 if ( !wp->visible )
3876 return;
3877
3878 vik_viewport_coord_to_screen ( params->vvp, &(wp->coord), &x, &y );
59b0a97a
RN
3879
3880 // If waypoint has an image then use the image size to select
3881 if ( wp->image ) {
3882 gint slackx, slacky;
3883 slackx = wp->image_width / 2;
3884 slacky = wp->image_height / 2;
3885
3886 if ( x <= params->x + slackx && x >= params->x - slackx
3887 && y <= params->y + slacky && y >= params->y - slacky ) {
3888 params->closest_wp_name = name;
3889 params->closest_wp = wp;
3890 params->closest_x = x;
3891 params->closest_y = y;
3892 }
50a14534 3893 }
59b0a97a
RN
3894 else if ( abs (x - params->x) <= WAYPOINT_SIZE_APPROX && abs (y - params->y) <= WAYPOINT_SIZE_APPROX &&
3895 ((!params->closest_wp) || /* was the old waypoint we already found closer than this one? */
3896 abs(x - params->x)+abs(y - params->y) < abs(x - params->closest_x)+abs(y - params->closest_y)))
3897 {
3898 params->closest_wp_name = name;
3899 params->closest_wp = wp;
3900 params->closest_x = x;
3901 params->closest_y = y;
3902 }
50a14534
EB
3903}
3904
941aa6e9 3905static void track_search_closest_tp ( gchar *name, VikTrack *t, TPSearchParams *params )
50a14534 3906{
941aa6e9
AF
3907 GList *tpl = t->trackpoints;
3908 VikTrackpoint *tp;
50a14534 3909
941aa6e9
AF
3910 if ( !t->visible )
3911 return;
50a14534 3912
941aa6e9
AF
3913 while (tpl)
3914 {
3915 gint x, y;
3916 tp = VIK_TRACKPOINT(tpl->data);
3917
3918 vik_viewport_coord_to_screen ( params->vvp, &(tp->coord), &x, &y );
3919
3920 if ( abs (x - params->x) <= TRACKPOINT_SIZE_APPROX && abs (y - params->y) <= TRACKPOINT_SIZE_APPROX &&
3921 ((!params->closest_tp) || /* was the old trackpoint we already found closer than this one? */
3922 abs(x - params->x)+abs(y - params->y) < abs(x - params->closest_x)+abs(y - params->closest_y)))
50a14534 3923 {
941aa6e9
AF
3924 params->closest_track_name = name;
3925 params->closest_tp = tp;
3926 params->closest_tpl = tpl;
3927 params->closest_x = x;
3928 params->closest_y = y;
50a14534 3929 }
941aa6e9 3930 tpl = tpl->next;
50a14534 3931 }
941aa6e9
AF
3932}
3933
3934static VikTrackpoint *closest_tp_in_five_pixel_interval ( VikTrwLayer *vtl, VikViewport *vvp, gint x, gint y )
3935{
3936 TPSearchParams params;
3937 params.x = x;
3938 params.y = y;
3939 params.vvp = vvp;
3940 params.closest_track_name = NULL;
3941 params.closest_tp = NULL;
3942 g_hash_table_foreach ( vtl->tracks, (GHFunc) track_search_closest_tp, &params);
3943 return params.closest_tp;
50a14534
EB
3944}
3945
3946static VikWaypoint *closest_wp_in_five_pixel_interval ( VikTrwLayer *vtl, VikViewport *vvp, gint x, gint y )
3947{
3948 WPSearchParams params;
3949 params.x = x;
3950 params.y = y;
3951 params.vvp = vvp;
3952 params.closest_wp = NULL;
3953 params.closest_wp_name = NULL;
3954 g_hash_table_foreach ( vtl->waypoints, (GHFunc) waypoint_search_closest_tp, &params);
3955 return params.closest_wp;
3956}
3957
08f14055
RN
3958// Some forward declarations
3959static void marker_begin_move ( tool_ed_t *t, gint x, gint y );
3960static void marker_moveto ( tool_ed_t *t, gint x, gint y );
3961static void marker_end_move ( tool_ed_t *t );
3962//
3963
3964static gboolean trw_layer_select_move ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp, tool_ed_t* t )
3965{
3966 if ( t->holding ) {
3967 VikCoord new_coord;
3968 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
3969
3970 // Here always allow snapping back to the original location
3971 // this is useful when one decides not to move the thing afterall
3972 // If one wants to move the item only a little bit then don't hold down the 'snap' key!
3973
3974 // snap to TP
3975 if ( event->state & GDK_CONTROL_MASK )
3976 {
3977 VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
3978 if ( tp )
3979 new_coord = tp->coord;
3980 }
3981
3982 // snap to WP
3983 if ( event->state & GDK_SHIFT_MASK )
3984 {
3985 VikWaypoint *wp = closest_wp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
3986 if ( wp )
3987 new_coord = wp->coord;
3988 }
3989
3990 gint x, y;
3991 vik_viewport_coord_to_screen ( vvp, &new_coord, &x, &y );
3992
3993 marker_moveto ( t, x, y );
3994
3995 return TRUE;
3996 }
3997 return FALSE;
3998}
3999
4000static gboolean trw_layer_select_release ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp, tool_ed_t* t )
4001{
4002 if ( t->holding && event->button == 1 )
4003 {
4004 VikCoord new_coord;
4005 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
4006
4007 // snap to TP
4008 if ( event->state & GDK_CONTROL_MASK )
4009 {
4010 VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
4011 if ( tp )
4012 new_coord = tp->coord;
4013 }
4014
4015 // snap to WP
4016 if ( event->state & GDK_SHIFT_MASK )
4017 {
4018 VikWaypoint *wp = closest_wp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
4019 if ( wp )
4020 new_coord = wp->coord;
4021 }
4022
4023 marker_end_move ( t );
4024
4025 // Determine if working on a waypoint or a trackpoint
4026 if ( t->is_waypoint )
4027 vtl->current_wp->coord = new_coord;
4028 else {
4029 if ( vtl->current_tpl ) {
4030 VIK_TRACKPOINT(vtl->current_tpl->data)->coord = new_coord;
4031
4032 if ( vtl->tpwin )
4033 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, vtl->current_tp_track_name );
4034
4035 // Don't really know what this is for but seems like it might be handy...
4036 /* can't join with itself! */
4037 trw_layer_cancel_last_tp ( vtl );
4038 }
4039 }
4040
4041 // Reset
4042 vtl->current_wp = NULL;
4043 vtl->current_wp_name = NULL;
4044 trw_layer_cancel_current_tp ( vtl, FALSE );
4045
4046 vik_layer_emit_update ( VIK_LAYER(vtl) );
4047 return TRUE;
4048 }
4049 return FALSE;
4050}
4051
77ad64fa
RN
4052/*
4053 Returns true if a waypoint or track is found near the requested event position for this particular layer
4054 The item found is automatically selected
4055 This is a tool like feature but routed via the layer interface, since it's instigated by a 'global' layer tool in vikwindow.c
4056 */
08f14055 4057static gboolean trw_layer_select_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp, tool_ed_t* tet )
77ad64fa
RN
4058{
4059 if ( event->button != 1 )
4060 return FALSE;
4061
4062 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
4063 return FALSE;
4064
4065 if ( !vtl->tracks_visible && !vtl->waypoints_visible )
4066 return FALSE;
4067
08f14055 4068 // Go for waypoints first as these often will be near a track, but it's likely the wp is wanted rather then the track
77ad64fa
RN
4069
4070 if (vtl->waypoints_visible) {
4071 WPSearchParams wp_params;
4072 wp_params.vvp = vvp;
4073 wp_params.x = event->x;
4074 wp_params.y = event->y;
4075 wp_params.closest_wp_name = NULL;
4076 wp_params.closest_wp = NULL;
4077
4078 g_hash_table_foreach ( vtl->waypoints, (GHFunc) waypoint_search_closest_tp, &wp_params);
4079
4080 if ( wp_params.closest_wp ) {
08f14055
RN
4081
4082 // Select
77ad64fa 4083 vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, g_hash_table_lookup ( vtl->waypoints_iters, wp_params.closest_wp_name ), TRUE );
08f14055
RN
4084
4085 // Too easy to move it so must be holding shift to start immediately moving it
4086 // or otherwise be previously selected
4087 if ( event->state & GDK_SHIFT_MASK ||
4088 vtl->current_wp == wp_params.closest_wp ) {
4089 // Put into 'move buffer'
4090 // NB vvp & vw already set in tet
4091 tet->vtl = (gpointer)vtl;
4092 tet->is_waypoint = TRUE;
4093
4094 marker_begin_move (tet, event->x, event->y);
4095 }
4096
4097 vtl->current_wp = wp_params.closest_wp;
4098 vtl->current_wp_name = wp_params.closest_wp_name;
4099
77ad64fa 4100 vik_layer_emit_update ( VIK_LAYER(vtl) );
08f14055 4101
77ad64fa
RN
4102 return TRUE;
4103 }
4104 }
4105
4106 if (vtl->tracks_visible) {
4107 TPSearchParams tp_params;
4108 tp_params.vvp = vvp;
4109 tp_params.x = event->x;
4110 tp_params.y = event->y;
4111 tp_params.closest_track_name = NULL;
4112 tp_params.closest_tp = NULL;
4113
4114 g_hash_table_foreach ( vtl->tracks, (GHFunc) track_search_closest_tp, &tp_params);
4115
4116 if ( tp_params.closest_tp ) {
08f14055
RN
4117
4118 // Always select + highlight the track
77ad64fa 4119 vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, g_hash_table_lookup ( vtl->tracks_iters, tp_params.closest_track_name ), TRUE );
08f14055
RN
4120
4121 tet->is_waypoint = FALSE;
4122
4123 // Select the Trackpoint
4124 // Can move it immediately when control held or it's the previously selected tp
4125 if ( event->state & GDK_CONTROL_MASK ||
4126 vtl->current_tpl == tp_params.closest_tpl ) {
4127 // Put into 'move buffer'
4128 // NB vvp & vw already set in tet
4129 tet->vtl = (gpointer)vtl;
4130 marker_begin_move (tet, event->x, event->y);
4131 }
4132
4133 vtl->current_tpl = tp_params.closest_tpl;
4134 vtl->current_tp_track_name = tp_params.closest_track_name;
4135
4136 if ( vtl->tpwin )
4137 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, vtl->current_tp_track_name );
4138
77ad64fa
RN
4139 vik_layer_emit_update ( VIK_LAYER(vtl) );
4140 return TRUE;
4141 }
4142 }
4143
4144 /* these aren't the droids you're looking for */
08f14055
RN
4145 vtl->current_wp = NULL;
4146 vtl->current_wp_name = NULL;
4147 trw_layer_cancel_current_tp ( vtl, FALSE );
4148
77ad64fa
RN
4149 return FALSE;
4150}
4151
e46f259a
RN
4152static gboolean trw_layer_show_selected_viewport_menu ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
4153{
4154 if ( event->button != 3 )
4155 return FALSE;
4156
4157 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
4158 return FALSE;
4159
4160 if ( !vtl->tracks_visible && !vtl->waypoints_visible )
4161 return FALSE;
4162
4163 /* Post menu for the currently selected item */
4164
4165 /* See if a track is selected */
4166 VikTrack *track = (VikTrack*)vik_window_get_selected_track ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(vtl) );
4167 if ( track && track->visible ) {
4168
4169 if ( vik_window_get_selected_name ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(vtl) ) ) {
4170
4171 if ( vtl->track_right_click_menu )
4172 gtk_object_sink ( GTK_OBJECT(vtl->track_right_click_menu) );
4173
4174 vtl->track_right_click_menu = GTK_MENU ( gtk_menu_new () );
4175
4176 vik_trw_layer_sublayer_add_menu_items ( vtl,
4177 vtl->track_right_click_menu,
4178 NULL,
4179 VIK_TRW_LAYER_SUBLAYER_TRACK,
4180 vik_window_get_selected_name ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(vtl) ),
4181 g_hash_table_lookup ( vtl->tracks_iters, vik_window_get_selected_name ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(vtl) ) ),
4182 vvp);
4183
4184 gtk_menu_popup ( vtl->track_right_click_menu, NULL, NULL, NULL, NULL, event->button, gtk_get_current_event_time() );
4185
4186 return TRUE;
4187 }
4188 }
4189
4190 /* See if a waypoint is selected */
4191 VikWaypoint *waypoint = (VikWaypoint*)vik_window_get_selected_waypoint ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(vtl) );
4192 if ( waypoint && waypoint->visible ) {
4193 if ( vik_window_get_selected_name ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(vtl) ) ) {
4194
4195 if ( vtl->wp_right_click_menu )
4196 gtk_object_sink ( GTK_OBJECT(vtl->wp_right_click_menu) );
4197
4198 vtl->wp_right_click_menu = GTK_MENU ( gtk_menu_new () );
4199 vik_trw_layer_sublayer_add_menu_items ( vtl,
4200 vtl->wp_right_click_menu,
4201 NULL,
4202 VIK_TRW_LAYER_SUBLAYER_WAYPOINT,
4203 vik_window_get_selected_name ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(vtl) ),
4204 g_hash_table_lookup ( vtl->waypoints_iters, vik_window_get_selected_name ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(vtl) ) ),
4205 vvp);
4206 gtk_menu_popup ( vtl->wp_right_click_menu, NULL, NULL, NULL, NULL, event->button, gtk_get_current_event_time() );
4207
4208 return TRUE;
4209 }
4210 }
4211
4212 return FALSE;
4213}
4214
7432fddf
AF
4215/* background drawing hook, to be passed the viewport */
4216static gboolean tool_sync_done = TRUE;
4217
4218static gboolean tool_sync(gpointer data)
4219{
4220 VikViewport *vvp = data;
4221 gdk_threads_enter();
4222 vik_viewport_sync(vvp);
4223 tool_sync_done = TRUE;
4224 gdk_threads_leave();
4225 return FALSE;
4226}
4227
7432fddf
AF
4228static void marker_begin_move ( tool_ed_t *t, gint x, gint y )
4229{
4230 t->holding = TRUE;
4231 t->gc = vik_viewport_new_gc (t->vvp, "black", 2);
4232 gdk_gc_set_function ( t->gc, GDK_INVERT );
4233 vik_viewport_draw_rectangle ( t->vvp, t->gc, FALSE, x-3, y-3, 6, 6 );
4234 vik_viewport_sync(t->vvp);
4235 t->oldx = x;
4236 t->oldy = y;
4237}
4238
4239static void marker_moveto ( tool_ed_t *t, gint x, gint y )
4240{
4241 VikViewport *vvp = t->vvp;
4242 vik_viewport_draw_rectangle ( vvp, t->gc, FALSE, t->oldx-3, t->oldy-3, 6, 6 );
4243 vik_viewport_draw_rectangle ( vvp, t->gc, FALSE, x-3, y-3, 6, 6 );
4244 t->oldx = x;
4245 t->oldy = y;
7b203521 4246
7432fddf
AF
4247 if (tool_sync_done) {
4248 g_idle_add_full (G_PRIORITY_HIGH_IDLE + 10, tool_sync, vvp, NULL);
4249 tool_sync_done = FALSE;
4250 }
4251}
4252
4253static void marker_end_move ( tool_ed_t *t )
4254{
4255 vik_viewport_draw_rectangle ( t->vvp, t->gc, FALSE, t->oldx-3, t->oldy-3, 6, 6 );
4256 g_object_unref ( t->gc );
4257 t->holding = FALSE;
4258}
4259
941aa6e9
AF
4260/*** Edit waypoint ****/
4261
4262static gpointer tool_edit_waypoint_create ( VikWindow *vw, VikViewport *vvp)
4263{
7432fddf
AF
4264 tool_ed_t *t = g_new(tool_ed_t, 1);
4265 t->vvp = vvp;
4266 t->holding = FALSE;
4267 return t;
941aa6e9
AF
4268}
4269
7432fddf 4270static gboolean tool_edit_waypoint_click ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data )
50a14534
EB
4271{
4272 WPSearchParams params;
7432fddf
AF
4273 tool_ed_t *t = data;
4274 VikViewport *vvp = t->vvp;
50a14534 4275
941aa6e9
AF
4276 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
4277 return FALSE;
7432fddf
AF
4278
4279 if ( t->holding ) {
4280 return TRUE;
4281 }
4282
87741170
RN
4283 if ( !vtl->vl.visible || !vtl->waypoints_visible )
4284 return FALSE;
4285
50a14534
EB
4286 if ( vtl->current_wp && vtl->current_wp->visible )
4287 {
4288 /* first check if current WP is within area (other may be 'closer', but we want to move the current) */
4289 gint x, y;
4290 vik_viewport_coord_to_screen ( vvp, &(vtl->current_wp->coord), &x, &y );
4291
c75d78d7
AF
4292 if ( abs(x - event->x) <= WAYPOINT_SIZE_APPROX &&
4293 abs(y - event->y) <= WAYPOINT_SIZE_APPROX )
50a14534
EB
4294 {
4295 if ( event->button == 3 )
4296 vtl->waypoint_rightclick = TRUE; /* remember that we're clicking; other layers will ignore release signal */
7432fddf
AF
4297 else {
4298 marker_begin_move(t, event->x, event->y);
4299 }
50a14534
EB
4300 return TRUE;
4301 }
4302 }
4303
4304 params.vvp = vvp;
4305 params.x = event->x;
4306 params.y = event->y;
4307 params.closest_wp_name = NULL;
4308 /* TODO: should get track listitem so we can break it up, make a new track, mess it up, all that. */
4309 params.closest_wp = NULL;
4310 g_hash_table_foreach ( vtl->waypoints, (GHFunc) waypoint_search_closest_tp, &params);
4311 if ( vtl->current_wp == params.closest_wp && vtl->current_wp != NULL )
4312 {
7432fddf
AF
4313 /* how do we get here? I'm putting in the abort until we can figure it out. -alex */
4314 marker_begin_move(t, event->x, event->y);
7742da66 4315 g_critical("shouldn't be here");
7432fddf 4316 exit(1);
50a14534
EB
4317 }
4318 else if ( params.closest_wp )
4319 {
4320 if ( event->button == 3 )
4321 vtl->waypoint_rightclick = TRUE; /* remember that we're clicking; other layers will ignore release signal */
4322 else
4323 vtl->waypoint_rightclick = FALSE;
4324
4325 vtl->current_wp = params.closest_wp;
4326 vtl->current_wp_name = params.closest_wp_name;
50a14534
EB
4327
4328 if ( params.closest_wp )
bdd1c412 4329 vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, g_hash_table_lookup ( vtl->waypoints_iters, vtl->current_wp_name ), TRUE );
50a14534
EB
4330
4331 /* could make it so don't update if old WP is off screen and new is null but oh well */
4332 vik_layer_emit_update ( VIK_LAYER(vtl) );
4333 return TRUE;
4334 }
4335
4336 vtl->current_wp = NULL;
4337 vtl->current_wp_name = NULL;
50a14534 4338 vtl->waypoint_rightclick = FALSE;
7432fddf
AF
4339 vik_layer_emit_update ( VIK_LAYER(vtl) );
4340 return FALSE;
4341}
4342
dc2c040e 4343static gboolean tool_edit_waypoint_move ( VikTrwLayer *vtl, GdkEventMotion *event, gpointer data )
7432fddf
AF
4344{
4345 tool_ed_t *t = data;
4346 VikViewport *vvp = t->vvp;
4347
4348 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
4349 return FALSE;
4350
4351 if ( t->holding ) {
4352 VikCoord new_coord;
4353 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
4354
4355 /* snap to TP */
4356 if ( event->state & GDK_CONTROL_MASK )
4357 {
4358 VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
4359 if ( tp )
4360 new_coord = tp->coord;
4361 }
4362
4363 /* snap to WP */
4364 if ( event->state & GDK_SHIFT_MASK )
4365 {
4366 VikWaypoint *wp = closest_wp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
4367 if ( wp && wp != vtl->current_wp )
4368 new_coord = wp->coord;
4369 }
4370
4371 {
4372 gint x, y;
4373 vik_viewport_coord_to_screen ( vvp, &new_coord, &x, &y );
7b203521 4374
7432fddf
AF
4375 marker_moveto ( t, x, y );
4376 }
4377 return TRUE;
4378 }
50a14534
EB
4379 return FALSE;
4380}
4381
7432fddf 4382static gboolean tool_edit_waypoint_release ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data )
941aa6e9 4383{
7432fddf
AF
4384 tool_ed_t *t = data;
4385 VikViewport *vvp = t->vvp;
4386
941aa6e9
AF
4387 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
4388 return FALSE;
7432fddf
AF
4389
4390 if ( t->holding && event->button == 1 )
941aa6e9
AF
4391 {
4392 VikCoord new_coord;
941aa6e9
AF
4393 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
4394
4395 /* snap to TP */
4396 if ( event->state & GDK_CONTROL_MASK )
4397 {
4398 VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
4399 if ( tp )
4400 new_coord = tp->coord;
4401 }
4402
4403 /* snap to WP */
4404 if ( event->state & GDK_SHIFT_MASK )
4405 {
4406 VikWaypoint *wp = closest_wp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
4407 if ( wp && wp != vtl->current_wp )
4408 new_coord = wp->coord;
4409 }
4410
7432fddf
AF
4411 marker_end_move ( t );
4412
941aa6e9
AF
4413 vtl->current_wp->coord = new_coord;
4414 vik_layer_emit_update ( VIK_LAYER(vtl) );
4415 return TRUE;
4416 }
4417 /* PUT IN RIGHT PLACE!!! */
7432fddf 4418 if ( event->button == 3 && vtl->waypoint_rightclick )
941aa6e9
AF
4419 {
4420 if ( vtl->wp_right_click_menu )
4f14a010 4421 g_object_ref_sink ( G_OBJECT(vtl->wp_right_click_menu) );
941aa6e9 4422 vtl->wp_right_click_menu = GTK_MENU ( gtk_menu_new () );
50eadc64 4423 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 ), vvp );
941aa6e9
AF
4424 gtk_menu_popup ( vtl->wp_right_click_menu, NULL, NULL, NULL, NULL, event->button, gtk_get_current_event_time() );
4425 vtl->waypoint_rightclick = FALSE;
4426 }
4427 return FALSE;
4428}
4429
98f5364d
EB
4430/**** Begin track ***/
4431static gpointer tool_begin_track_create ( VikWindow *vw, VikViewport *vvp)
4432{
4433 return vvp;
4434}
4435
4436static gboolean tool_begin_track_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
4437{
4438 vtl->current_track = NULL;
4439 return tool_new_track_click ( vtl, event, vvp );
4440}
4441
941aa6e9
AF
4442/*** New track ****/
4443
4444static gpointer tool_new_track_create ( VikWindow *vw, VikViewport *vvp)
4445{
4446 return vvp;
4447}
4448
7b203521
EB
4449typedef struct {
4450 VikTrwLayer *vtl;
4451 VikViewport *vvp;
4452 gint x1,y1,x2,y2;
4453} new_track_move_passalong_t;
4454
4455/* sync and undraw, but only when we have time */
4456static gboolean ct_sync ( gpointer passalong )
4457{
4458 new_track_move_passalong_t *p = (new_track_move_passalong_t *) passalong;
4459 vik_viewport_sync ( p->vvp );
4460 gdk_gc_set_function ( p->vtl->current_track_gc, GDK_INVERT );
4461 vik_viewport_draw_line ( p->vvp, p->vtl->current_track_gc, p->x1, p->y1, p->x2, p->y2 );
4462 gdk_gc_set_function ( p->vtl->current_track_gc, GDK_COPY );
4463 p->vtl->ct_sync_done = TRUE;
4464 g_free ( p );
4465 return FALSE;
4466}
4467
dc2c040e 4468static VikLayerToolFuncStatus tool_new_track_move ( VikTrwLayer *vtl, GdkEventMotion *event, VikViewport *vvp )
7b203521
EB
4469{
4470 /* if we haven't sync'ed yet, we don't have time to do more. */
4471 if ( vtl->ct_sync_done && vtl->current_track && vtl->current_track->trackpoints ) {
4472 GList *iter = vtl->current_track->trackpoints;
4473 new_track_move_passalong_t *passalong;
4474 gint x1, y1;
4475
4476 while ( iter->next )
4477 iter = iter->next;
4478 gdk_gc_set_function ( vtl->current_track_gc, GDK_INVERT );
4479 vik_viewport_coord_to_screen ( vvp, &(VIK_TRACKPOINT(iter->data)->coord), &x1, &y1 );
4480 vik_viewport_draw_line ( vvp, vtl->current_track_gc, x1, y1, event->x, event->y );
4481 gdk_gc_set_function ( vtl->current_track_gc, GDK_COPY );
4482
4483 passalong = g_new(new_track_move_passalong_t,1); /* freed by sync */
4484 passalong->vtl = vtl;
4485 passalong->vvp = vvp;
4486 passalong->x1 = x1;
4487 passalong->y1 = y1;
4488 passalong->x2 = event->x;
4489 passalong->y2 = event->y;
4490
4491 /* this will sync and undraw when we have time to */
4492 g_idle_add_full (G_PRIORITY_HIGH_IDLE + 10, ct_sync, passalong, NULL);
4493 vtl->ct_sync_done = FALSE;
165d30aa 4494 return VIK_LAYER_TOOL_ACK_GRAB_FOCUS;
7b203521 4495 }
165d30aa 4496 return VIK_LAYER_TOOL_ACK;
7b203521
EB
4497}
4498
777e2d4d
EB
4499static gboolean tool_new_track_key_press ( VikTrwLayer *vtl, GdkEventKey *event, VikViewport *vvp )
4500{
4501 if ( vtl->current_track && event->keyval == GDK_Escape ) {
4502 vtl->current_track = NULL;
4503 vik_layer_emit_update ( VIK_LAYER(vtl) );
4504 return TRUE;
4505 } else if ( vtl->current_track && event->keyval == GDK_BackSpace ) {
4506 /* undo */
4507 if ( vtl->current_track->trackpoints )
4508 {
4509 GList *last = g_list_last(vtl->current_track->trackpoints);
4510 g_free ( last->data );
4511 vtl->current_track->trackpoints = g_list_remove_link ( vtl->current_track->trackpoints, last );
4512 }
4513 vik_layer_emit_update ( VIK_LAYER(vtl) );
4514 return TRUE;
4515 }
4516 return FALSE;
4517}
4518
941aa6e9 4519static gboolean tool_new_track_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
50a14534
EB
4520{
4521 VikTrackpoint *tp;
4522
941aa6e9
AF
4523 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
4524 return FALSE;
4525
50a14534
EB
4526 if ( event->button == 3 && vtl->current_track )
4527 {
4528 /* undo */
4529 if ( vtl->current_track->trackpoints )
4530 {
4531 GList *last = g_list_last(vtl->current_track->trackpoints);
4532 g_free ( last->data );
4533 vtl->current_track->trackpoints = g_list_remove_link ( vtl->current_track->trackpoints, last );
4534 }
4535 vik_layer_emit_update ( VIK_LAYER(vtl) );
4536 return TRUE;
4537 }
4538
4539 if ( event->type == GDK_2BUTTON_PRESS )
4540 {
4541 /* subtract last (duplicate from double click) tp then end */
4542 if ( vtl->current_track && vtl->current_track->trackpoints && vtl->ct_x1 == vtl->ct_x2 && vtl->ct_y1 == vtl->ct_y2 )
4543 {
4544 GList *last = g_list_last(vtl->current_track->trackpoints);
4545 g_free ( last->data );
4546 vtl->current_track->trackpoints = g_list_remove_link ( vtl->current_track->trackpoints, last );
4547 /* undo last, then end */
4548 vtl->current_track = NULL;
4549 }
8e9c992d 4550 vik_layer_emit_update ( VIK_LAYER(vtl) );
50a14534
EB
4551 return TRUE;
4552 }
4553
4554 if ( ! vtl->current_track )
4555 {
e13ab673
RN
4556 gchar *name = get_new_unique_sublayer_name(vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, _("Track"));
4557 if ( ( name = a_dialog_new_track ( VIK_GTK_WINDOW_FROM_LAYER(vtl), vtl->tracks, name ) ) )
50a14534
EB
4558 {
4559 vtl->current_track = vik_track_new();
4560 vtl->current_track->visible = TRUE;
4561 vik_trw_layer_add_track ( vtl, name, vtl->current_track );
98f5364d
EB
4562
4563 /* incase it was created by begin track */
4564 vik_window_enable_layer_tool ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)), VIK_LAYER_TRW, TOOL_CREATE_TRACK );
50a14534
EB
4565 }
4566 else
4567 return TRUE;
4568 }
4569 tp = vik_trackpoint_new();
4570 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &(tp->coord) );
4571
4572 /* snap to other TP */
4573 if ( event->state & GDK_CONTROL_MASK )
4574 {
4575 VikTrackpoint *other_tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
4576 if ( other_tp )
4577 tp->coord = other_tp->coord;
4578 }
4579
4580 tp->newsegment = FALSE;
4581 tp->has_timestamp = FALSE;
4582 tp->timestamp = 0;
50a14534
EB
4583 vtl->current_track->trackpoints = g_list_append ( vtl->current_track->trackpoints, tp );
4584
4585 vtl->ct_x1 = vtl->ct_x2;
4586 vtl->ct_y1 = vtl->ct_y2;
4587 vtl->ct_x2 = event->x;
4588 vtl->ct_y2 = event->y;
4589
4590 vik_layer_emit_update ( VIK_LAYER(vtl) );
4591 return TRUE;
4592}
4593
941aa6e9
AF
4594/*** New waypoint ****/
4595
4596static gpointer tool_new_waypoint_create ( VikWindow *vw, VikViewport *vvp)
4597{
4598 return vvp;
4599}
4600
4601static gboolean tool_new_waypoint_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
4602{
4603 VikCoord coord;
4604 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
4605 return FALSE;
4606 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &coord );
4607 if (vik_trw_layer_new_waypoint ( vtl, VIK_GTK_WINDOW_FROM_LAYER(vtl), &coord ) && VIK_LAYER(vtl)->visible)
4608 vik_layer_emit_update ( VIK_LAYER(vtl) );
4609 return TRUE;
4610}
4611
4612
4613/*** Edit trackpoint ****/
4614
4615static gpointer tool_edit_trackpoint_create ( VikWindow *vw, VikViewport *vvp)
4616{
7432fddf 4617 tool_ed_t *t = g_new(tool_ed_t, 1);
33534cd8
AF
4618 t->vvp = vvp;
4619 t->holding = FALSE;
4620 return t;
941aa6e9
AF
4621}
4622
33534cd8 4623static gboolean tool_edit_trackpoint_click ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data )
941aa6e9 4624{
7432fddf 4625 tool_ed_t *t = data;
33534cd8
AF
4626 VikViewport *vvp = t->vvp;
4627 TPSearchParams params;
941aa6e9
AF
4628 /* OUTDATED DOCUMENTATION:
4629 find 5 pixel range on each side. then put these UTM, and a pointer
4630 to the winning track name (and maybe the winning track itself), and a
4631 pointer to the winning trackpoint, inside an array or struct. pass
4632 this along, do a foreach on the tracks which will do a foreach on the
4633 trackpoints. */
4634 params.vvp = vvp;
4635 params.x = event->x;
4636 params.y = event->y;
4637 params.closest_track_name = NULL;
4638 /* TODO: should get track listitem so we can break it up, make a new track, mess it up, all that. */
4639 params.closest_tp = NULL;
4640
7432fddf
AF
4641 if ( event->button != 1 )
4642 return FALSE;
4643
941aa6e9
AF
4644 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
4645 return FALSE;
4646
87741170
RN
4647 if ( !vtl->vl.visible || !vtl->tracks_visible )
4648 return FALSE;
4649
941aa6e9
AF
4650 if ( vtl->current_tpl )
4651 {
4652 /* 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.) */
4653 VikTrackpoint *tp = VIK_TRACKPOINT(vtl->current_tpl->data);
4654 VikTrack *current_tr = VIK_TRACK(g_hash_table_lookup(vtl->tracks, vtl->current_tp_track_name));
4655 gint x, y;
4656 g_assert ( current_tr );
4657
4658 vik_viewport_coord_to_screen ( vvp, &(tp->coord), &x, &y );
4659
4660 if ( current_tr->visible &&
4661 abs(x - event->x) < TRACKPOINT_SIZE_APPROX &&
7432fddf
AF
4662 abs(y - event->y) < TRACKPOINT_SIZE_APPROX ) {
4663 marker_begin_move ( t, event->x, event->y );
941aa6e9
AF
4664 return TRUE;
4665 }
4666
4667 vtl->last_tpl = vtl->current_tpl;
4668 vtl->last_tp_track_name = vtl->current_tp_track_name;
4669 }
4670
4671 g_hash_table_foreach ( vtl->tracks, (GHFunc) track_search_closest_tp, &params);
4672
4673 if ( params.closest_tp )
4674 {
4675 vtl->current_tpl = params.closest_tpl;
4676 vtl->current_tp_track_name = params.closest_track_name;
bdd1c412 4677 vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, g_hash_table_lookup ( vtl->tracks_iters, vtl->current_tp_track_name ), TRUE );
941aa6e9
AF
4678 trw_layer_tpwin_init ( vtl );
4679 vik_layer_emit_update ( VIK_LAYER(vtl) );
4680 return TRUE;
4681 }
4682
4683 /* these aren't the droids you're looking for */
4684 return FALSE;
4685}
4686
dc2c040e 4687static gboolean tool_edit_trackpoint_move ( VikTrwLayer *vtl, GdkEventMotion *event, gpointer data )
33534cd8 4688{
7432fddf 4689 tool_ed_t *t = data;
33534cd8
AF
4690 VikViewport *vvp = t->vvp;
4691
4692 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
4693 return FALSE;
4694
4695 if ( t->holding )
4696 {
4697 VikCoord new_coord;
33534cd8
AF
4698 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
4699
4700 /* snap to TP */
4701 if ( event->state & GDK_CONTROL_MASK )
4702 {
4703 VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
4704 if ( tp && tp != vtl->current_tpl->data )
4705 new_coord = tp->coord;
4706 }
4707 // VIK_TRACKPOINT(vtl->current_tpl->data)->coord = new_coord;
7432fddf
AF
4708 {
4709 gint x, y;
4710 vik_viewport_coord_to_screen ( vvp, &new_coord, &x, &y );
4711 marker_moveto ( t, x, y );
4712 }
33534cd8
AF
4713
4714 return TRUE;
4715 }
4716 return FALSE;
4717}
4718
33534cd8 4719static gboolean tool_edit_trackpoint_release ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data )
941aa6e9 4720{
7432fddf 4721 tool_ed_t *t = data;
33534cd8
AF
4722 VikViewport *vvp = t->vvp;
4723
941aa6e9
AF
4724 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
4725 return FALSE;
7432fddf
AF
4726 if ( event->button != 1)
4727 return FALSE;
33534cd8 4728
7432fddf 4729 if ( t->holding ) {
941aa6e9 4730 VikCoord new_coord;
941aa6e9
AF
4731 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
4732
4733 /* snap to TP */
4734 if ( event->state & GDK_CONTROL_MASK )
4735 {
4736 VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
4737 if ( tp && tp != vtl->current_tpl->data )
4738 new_coord = tp->coord;
4739 }
4740
4741 VIK_TRACKPOINT(vtl->current_tpl->data)->coord = new_coord;
4742
7432fddf 4743 marker_end_move ( t );
33534cd8 4744
941aa6e9
AF
4745 /* diff dist is diff from orig */
4746 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, vtl->current_tp_track_name );
4747 /* can't join with itself! */
4748 trw_layer_cancel_last_tp ( vtl );
4749
4750 vik_layer_emit_update ( VIK_LAYER(vtl) );
4751 return TRUE;
4752 }
4753 return FALSE;
4754}
4755
4756
1eef1bde
QT
4757/*** Magic Scissors ***/
4758static gpointer tool_magic_scissors_create ( VikWindow *vw, VikViewport *vvp)
4759{
4760 return vvp;
4761}
4762
4763static gboolean tool_magic_scissors_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
4764{
4765 VikCoord tmp;
0c1044e9 4766 if ( !vtl ) return FALSE;
1eef1bde 4767 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &tmp );
c3deba01
EB
4768 if ( event->button == 3 && vtl->magic_scissors_current_track ) {
4769 VikCoord *new_end;
4770 new_end = vik_track_cut_back_to_double_point ( vtl->magic_scissors_current_track );
4771 if ( new_end ) {
4772 vtl->magic_scissors_coord = *new_end;
4773 g_free ( new_end );
4774 vik_layer_emit_update ( VIK_LAYER(vtl) );
4775 /* remove last ' to:...' */
4776 if ( vtl->magic_scissors_current_track->comment ) {
4777 gchar *last_to = strrchr ( vtl->magic_scissors_current_track->comment, 't' );
4778 if ( last_to && (last_to - vtl->magic_scissors_current_track->comment > 1) ) {
4779 gchar *new_comment = g_strndup ( vtl->magic_scissors_current_track->comment,
4780 last_to - vtl->magic_scissors_current_track->comment - 1);
4781 vik_track_set_comment_no_copy ( vtl->magic_scissors_current_track, new_comment );
4782 }
4783 }
4784 }
4785 }
4786 else if ( vtl->magic_scissors_started || (event->state & GDK_CONTROL_MASK && vtl->magic_scissors_current_track) ) {
1eef1bde 4787 struct LatLon start, end;
533bbf34
MA
4788 gchar startlat[G_ASCII_DTOSTR_BUF_SIZE], startlon[G_ASCII_DTOSTR_BUF_SIZE];
4789 gchar endlat[G_ASCII_DTOSTR_BUF_SIZE], endlon[G_ASCII_DTOSTR_BUF_SIZE];
4790 gchar *url;
bddd2056 4791
1eef1bde
QT
4792 vik_coord_to_latlon ( &(vtl->magic_scissors_coord), &start );
4793 vik_coord_to_latlon ( &(tmp), &end );
bddd2056
EB
4794 vtl->magic_scissors_coord = tmp; /* for continuations */
4795
4796 /* these are checked when adding a track from a file (vik_trw_layer_filein_add_track) */
4797 if ( event->state & GDK_CONTROL_MASK && vtl->magic_scissors_current_track ) {
4798 vtl->magic_scissors_append = TRUE; // merge tracks. keep started true.
4799 } else {
4800 vtl->magic_scissors_check_added_track = TRUE;
4801 vtl->magic_scissors_started = FALSE;
4802 }
4803
533bbf34
MA
4804 url = g_strdup_printf(GOOGLE_DIRECTIONS_STRING,
4805 g_ascii_dtostr (startlat, G_ASCII_DTOSTR_BUF_SIZE, (gdouble) start.lat),
4806 g_ascii_dtostr (startlon, G_ASCII_DTOSTR_BUF_SIZE, (gdouble) start.lon),
4807 g_ascii_dtostr (endlat, G_ASCII_DTOSTR_BUF_SIZE, (gdouble) end.lat),
4808 g_ascii_dtostr (endlon, G_ASCII_DTOSTR_BUF_SIZE, (gdouble) end.lon));
ae941b4c 4809 a_babel_convert_from_url ( vtl, url, "kml", NULL, NULL );
533bbf34 4810 g_free ( url );
bddd2056
EB
4811
4812 /* see if anything was done -- a track was added or appended to */
4813 if ( vtl->magic_scissors_check_added_track && vtl->magic_scissors_added_track_name ) {
4814 VikTrack *tr;
4815
4816 tr = g_hash_table_lookup ( vtl->tracks, vtl->magic_scissors_added_track_name );
4817
4818 if ( tr )
265bffa9 4819 vik_track_set_comment_no_copy ( tr, g_strdup_printf("from: %f,%f to: %f,%f", start.lat, start.lon, end.lat, end.lon ) );
bddd2056
EB
4820
4821 vtl->magic_scissors_current_track = tr;
4822
4823 g_free ( vtl->magic_scissors_added_track_name );
4824 vtl->magic_scissors_added_track_name = NULL;
4825 } else if ( vtl->magic_scissors_append == FALSE && vtl->magic_scissors_current_track ) {
4826 /* magic_scissors_append was originally TRUE but set to FALSE by filein_add_track */
4827 gchar *new_comment = g_strdup_printf("%s to: %f,%f", vtl->magic_scissors_current_track->comment, end.lat, end.lon );
4828 vik_track_set_comment_no_copy ( vtl->magic_scissors_current_track, new_comment );
4829 }
4830 vtl->magic_scissors_check_added_track = FALSE;
4831 vtl->magic_scissors_append = FALSE;
4832
1eef1bde
QT
4833 vik_layer_emit_update ( VIK_LAYER(vtl) );
4834 } else {
bddd2056 4835 vtl->magic_scissors_started = TRUE;
1eef1bde 4836 vtl->magic_scissors_coord = tmp;
bddd2056 4837 vtl->magic_scissors_current_track = NULL;
1eef1bde 4838 }
1eef1bde
QT
4839 return TRUE;
4840}
4841
941aa6e9
AF
4842/*** Show picture ****/
4843
4844static gpointer tool_show_picture_create ( VikWindow *vw, VikViewport *vvp)
4845{
4846 return vvp;
4847}
4848
4849/* Params are: vvp, event, last match found or NULL */
4850static void tool_show_picture_wp ( char *name, VikWaypoint *wp, gpointer params[2] )
4851{
4852 if ( wp->image && wp->visible )
4853 {
4854 gint x, y, slackx, slacky;
4855 GdkEventButton *event = (GdkEventButton *) params[1];
4856
4857 vik_viewport_coord_to_screen ( VIK_VIEWPORT(params[0]), &(wp->coord), &x, &y );
4858 slackx = wp->image_width / 2;
4859 slacky = wp->image_height / 2;
4860 if ( x <= event->x + slackx && x >= event->x - slackx
4861 && y <= event->y + slacky && y >= event->y - slacky )
4862 {
4863 params[2] = wp->image; /* we've found a match. however continue searching
4864 * since we want to find the last match -- that
4865 * is, the match that was drawn last. */
4866 }
4867 }
4868}
4869
a412f3f5
RN
4870static void trw_layer_show_picture ( gpointer pass_along[6] )
4871{
4872 /* thanks to the Gaim people for showing me ShellExecute and g_spawn_command_line_async */
4873#ifdef WINDOWS
4874 ShellExecute(NULL, NULL, (char *) pass_along[2], NULL, ".\\", 0);
4875#else /* WINDOWS */
4876 GError *err = NULL;
4877 gchar *quoted_file = g_shell_quote ( (gchar *) pass_along[5] );
4878 gchar *cmd = g_strdup_printf ( "eog %s", quoted_file );
4879 g_free ( quoted_file );
4880 if ( ! g_spawn_command_line_async ( cmd, &err ) )
4881 {
4882 a_dialog_error_msg ( VIK_GTK_WINDOW_FROM_LAYER( pass_along[0]), _("Could not launch eog to open file.") );
4883 g_error_free ( err );
4884 }
4885 g_free ( cmd );
4886#endif /* WINDOWS */
4887}
4888
941aa6e9
AF
4889static gboolean tool_show_picture_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
4890{
4891 gpointer params[3] = { vvp, event, NULL };
4892 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
4893 return FALSE;
4894 g_hash_table_foreach ( vtl->waypoints, (GHFunc) tool_show_picture_wp, params );
4895 if ( params[2] )
4896 {
a412f3f5
RN
4897 static gpointer pass_along[6];
4898 pass_along[0] = vtl;
4899 pass_along[5] = params[2];
4900 trw_layer_show_picture ( pass_along );
941aa6e9
AF
4901 return TRUE; /* found a match */
4902 }
4903 else
4904 return FALSE; /* go through other layers, searching for a match */
4905}
4906
4907/***************************************************************************
4908 ** End tool code
4909 ***************************************************************************/
4910
4911
4912
4913
4914
50a14534
EB
4915static void image_wp_make_list ( char *name, VikWaypoint *wp, GSList **pics )
4916{
4917 if ( wp->image && ( ! a_thumbnails_exists ( wp->image ) ) )
4918 *pics = g_slist_append ( *pics, (gpointer) g_strdup ( wp->image ) );
4919}
4920
91822ddd
RN
4921/* Structure for thumbnail creating data used in the background thread */
4922typedef struct {
4923 VikTrwLayer *vtl; // Layer needed for redrawing
4924 GSList *pics; // Image list
4925} thumbnail_create_thread_data;
4926
4927static int create_thumbnails_thread ( thumbnail_create_thread_data *tctd, gpointer threaddata )
50a14534 4928{
91822ddd
RN
4929 guint total = g_slist_length(tctd->pics), done = 0;
4930 while ( tctd->pics )
50a14534 4931 {
91822ddd 4932 a_thumbnails_create ( (gchar *) tctd->pics->data );
54861848
GB
4933 int result = a_background_thread_progress ( threaddata, ((gdouble) ++done) / total );
4934 if ( result != 0 )
4935 return -1; /* Abort thread */
4936
91822ddd 4937 tctd->pics = tctd->pics->next;
50a14534 4938 }
91822ddd
RN
4939
4940 // Redraw to show the thumbnails as they are now created
4941 gdk_threads_enter();
4942 if ( IS_VIK_LAYER(tctd->vtl) )
4943 vik_layer_emit_update ( VIK_LAYER(tctd->vtl) );
4944 gdk_threads_leave();
4945
17c8aefa 4946 return 0;
50a14534
EB
4947}
4948
91822ddd 4949static void thumbnail_create_thread_free ( thumbnail_create_thread_data *tctd )
50a14534 4950{
91822ddd 4951 while ( tctd->pics )
50a14534 4952 {
91822ddd
RN
4953 g_free ( tctd->pics->data );
4954 tctd->pics = g_slist_delete_link ( tctd->pics, tctd->pics );
50a14534 4955 }
91822ddd 4956 g_free ( tctd );
50a14534
EB
4957}
4958
4959static void trw_layer_verify_thumbnails ( VikTrwLayer *vtl, GtkWidget *vp )
4960{
4961 if ( ! vtl->has_verified_thumbnails )
4962 {
4963 GSList *pics = NULL;
4964 g_hash_table_foreach ( vtl->waypoints, (GHFunc) image_wp_make_list, &pics );
4965 if ( pics )
4966 {
4967 gint len = g_slist_length ( pics );
4c77d5e0 4968 gchar *tmp = g_strdup_printf ( _("Creating %d Image Thumbnails..."), len );
91822ddd
RN
4969 thumbnail_create_thread_data *tctd = g_malloc ( sizeof(thumbnail_create_thread_data) );
4970 tctd->vtl = vtl;
4971 tctd->pics = pics;
4972 a_background_thread ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
4973 tmp,
4974 (vik_thr_func) create_thumbnails_thread,
4975 tctd,
4976 (vik_thr_free_func) thumbnail_create_thread_free,
4977 NULL,
4978 len );
50a14534
EB
4979 g_free ( tmp );
4980 }
4981 }
4982}
4983
4984VikCoordMode vik_trw_layer_get_coord_mode ( VikTrwLayer *vtl )
4985{
4986 return vtl->coord_mode;
4987}
4988
4989
4990
4991static void waypoint_convert ( const gchar *name, VikWaypoint *wp, VikCoordMode *dest_mode )
4992{
4993 vik_coord_convert ( &(wp->coord), *dest_mode );
4994}
4995
4996static void track_convert ( const gchar *name, VikTrack *tr, VikCoordMode *dest_mode )
4997{
4998 vik_track_convert ( tr, *dest_mode );
4999}
5000
5001static void trw_layer_change_coord_mode ( VikTrwLayer *vtl, VikCoordMode dest_mode )
5002{
5003 if ( vtl->coord_mode != dest_mode )
5004 {
5005 vtl->coord_mode = dest_mode;
5006 g_hash_table_foreach ( vtl->waypoints, (GHFunc) waypoint_convert, &dest_mode );
5007 g_hash_table_foreach ( vtl->tracks, (GHFunc) track_convert, &dest_mode );
5008 }
5009}
e4afc73a
EB
5010
5011VikWaypoint *vik_trw_layer_get_waypoint ( VikTrwLayer *vtl, gchar *name )
5012{
5013 return g_hash_table_lookup ( vtl->waypoints, name );
5014}
5015
f7f8a0a6 5016VikTrack *vik_trw_layer_get_track ( VikTrwLayer *vtl, const gchar *name )
e4afc73a
EB
5017{
5018 return g_hash_table_lookup ( vtl->tracks, name );
5019}
20c7a3a0 5020
2cebc318 5021static void vik_trw_layer_set_menu_selection(VikTrwLayer *vtl, guint16 selection)
20c7a3a0
QT
5022{
5023 vtl->menu_selection = selection;
5024}
5025
5026static guint16 vik_trw_layer_get_menu_selection(VikTrwLayer *vtl)
5027{
5028 return(vtl->menu_selection);
5029}
5030
7114e879
QT
5031/* ----------- Downloading maps along tracks --------------- */
5032
5033static int get_download_area_width(VikViewport *vvp, gdouble zoom_level, struct LatLon *wh)
5034{
5035 /* TODO: calculating based on current size of viewport */
5036 const gdouble w_at_zoom_0_125 = 0.0013;
5037 const gdouble h_at_zoom_0_125 = 0.0011;
5038 gdouble zoom_factor = zoom_level/0.125;
5039
5040 wh->lat = h_at_zoom_0_125 * zoom_factor;
5041 wh->lon = w_at_zoom_0_125 * zoom_factor;
5042
5043 return 0; /* all OK */
5044}
5045
35e22ed8
QT
5046static VikCoord *get_next_coord(VikCoord *from, VikCoord *to, struct LatLon *dist, gdouble gradient)
5047{
5048 if ((dist->lon >= ABS(to->east_west - from->east_west)) &&
5049 (dist->lat >= ABS(to->north_south - from->north_south)))
5050 return NULL;
5051
5052 VikCoord *coord = g_malloc(sizeof(VikCoord));
5053 coord->mode = VIK_COORD_LATLON;
5054
5055 if (ABS(gradient) < 1) {
5056 if (from->east_west > to->east_west)
5057 coord->east_west = from->east_west - dist->lon;
5058 else
5059 coord->east_west = from->east_west + dist->lon;
5060 coord->north_south = gradient * (coord->east_west - from->east_west) + from->north_south;
5061 } else {
5062 if (from->north_south > to->north_south)
5063 coord->north_south = from->north_south - dist->lat;
5064 else
5065 coord->north_south = from->north_south + dist->lat;
5066 coord->east_west = (1/gradient) * (coord->north_south - from->north_south) + from->north_south;
5067 }
5068
5069 return coord;
5070}
5071
5072static GList *add_fillins(GList *list, VikCoord *from, VikCoord *to, struct LatLon *dist)
5073{
5074 /* TODO: handle virtical track (to->east_west - from->east_west == 0) */
5075 gdouble gradient = (to->north_south - from->north_south)/(to->east_west - from->east_west);
5076
5077 VikCoord *next = from;
5078 while (TRUE) {
5079 if ((next = get_next_coord(next, to, dist, gradient)) == NULL)
5080 break;
5081 list = g_list_prepend(list, next);
5082 }
5083
5084 return list;
5085}
5086
7114e879
QT
5087void vik_track_download_map(VikTrack *tr, VikMapsLayer *vml, VikViewport *vvp, gdouble zoom_level)
5088{
5089 typedef struct _Rect {
5090 VikCoord tl;
5091 VikCoord br;
35e22ed8 5092 VikCoord center;
7114e879 5093 } Rect;
35e22ed8 5094#define GLRECT(iter) ((Rect *)((iter)->data))
7114e879
QT
5095
5096 struct LatLon wh;
35e22ed8 5097 GList *rects_to_download = NULL;
7114e879
QT
5098 GList *rect_iter;
5099
5100 if (get_download_area_width(vvp, zoom_level, &wh))
5101 return;
5102
5103 GList *iter = tr->trackpoints;
35e22ed8
QT
5104 if (!iter)
5105 return;
7114e879
QT
5106
5107 gboolean new_map = TRUE;
5108 VikCoord *cur_coord, tl, br;
5109 Rect *rect;
7114e879
QT
5110 while (iter) {
5111 cur_coord = &(VIK_TRACKPOINT(iter->data))->coord;
5112 if (new_map) {
5113 vik_coord_set_area(cur_coord, &wh, &tl, &br);
5114 rect = g_malloc(sizeof(Rect));
5115 rect->tl = tl;
5116 rect->br = br;
35e22ed8
QT
5117 rect->center = *cur_coord;
5118 rects_to_download = g_list_prepend(rects_to_download, rect);
7114e879
QT
5119 new_map = FALSE;
5120 iter = iter->next;
5121 continue;
5122 }
5123 gboolean found = FALSE;
35e22ed8
QT
5124 for (rect_iter = rects_to_download; rect_iter; rect_iter = rect_iter->next) {
5125 if (vik_coord_inside(cur_coord, &GLRECT(rect_iter)->tl, &GLRECT(rect_iter)->br)) {
7114e879
QT
5126 found = TRUE;
5127 break;
5128 }
5129 }
5130 if (found)
5131 iter = iter->next;
5132 else
5133 new_map = TRUE;
5134 }
35e22ed8 5135
35e22ed8 5136 GList *fillins = NULL;
b1e57d16
RN
5137 /* 'fillin' doesn't work in UTM mode - potentially ending up in massive loop continually allocating memory - hence don't do it */
5138 /* seems that ATM the function get_next_coord works only for LATLON */
5139 if ( cur_coord->mode == VIK_COORD_LATLON ) {
5140 /* fill-ins for far apart points */
5141 GList *cur_rect, *next_rect;
5142 for (cur_rect = rects_to_download;
5143 (next_rect = cur_rect->next) != NULL;
5144 cur_rect = cur_rect->next) {
5145 if ((wh.lon < ABS(GLRECT(cur_rect)->center.east_west - GLRECT(next_rect)->center.east_west)) ||
5146 (wh.lat < ABS(GLRECT(cur_rect)->center.north_south - GLRECT(next_rect)->center.north_south))) {
5147 fillins = add_fillins(fillins, &GLRECT(cur_rect)->center, &GLRECT(next_rect)->center, &wh);
5148 }
35e22ed8 5149 }
3cbbb49e
GB
5150 } else
5151 g_message("%s: this feature works only in Mercator mode", __FUNCTION__);
35e22ed8
QT
5152
5153 if (fillins) {
5154 GList *iter = fillins;
5155 while (iter) {
5156 cur_coord = (VikCoord *)(iter->data);
5157 vik_coord_set_area(cur_coord, &wh, &tl, &br);
5158 rect = g_malloc(sizeof(Rect));
5159 rect->tl = tl;
5160 rect->br = br;
5161 rect->center = *cur_coord;
5162 rects_to_download = g_list_prepend(rects_to_download, rect);
5163 iter = iter->next;
5164 }
5165 }
5166
5167 for (rect_iter = rects_to_download; rect_iter; rect_iter = rect_iter->next) {
7114e879
QT
5168 maps_layer_download_section_without_redraw(vml, vvp, &(((Rect *)(rect_iter->data))->tl), &(((Rect *)(rect_iter->data))->br), zoom_level);
5169 }
5170
35e22ed8
QT
5171 if (fillins) {
5172 for (iter = fillins; iter; iter = iter->next)
5173 g_free(iter->data);
5174 g_list_free(fillins);
5175 }
5176 if (rects_to_download) {
5177 for (rect_iter = rects_to_download; rect_iter; rect_iter = rect_iter->next)
5178 g_free(rect_iter->data);
5179 g_list_free(rects_to_download);
5180 }
7114e879
QT
5181}
5182
6bb72350 5183static void trw_layer_download_map_along_track_cb ( gpointer pass_along[6] )
7114e879
QT
5184{
5185 VikMapsLayer *vml;
5186 gint selected_map, default_map;
5187 gchar *zoomlist[] = {"0.125", "0.25", "0.5", "1", "2", "4", "8", "16", "32", "64", "128", "256", "512", "1024", NULL };
5188 gdouble zoom_vals[] = {0.125, 0.25, 0.5, 1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024};
5189 gint selected_zoom, default_zoom;
5190 int i,j;
5191
5192
5193 VikTrwLayer *vtl = pass_along[0];
5194 VikLayersPanel *vlp = pass_along[1];
5195 VikTrack *tr = (VikTrack *) g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, pass_along[3] );
5196 VikViewport *vvp = vik_window_viewport((VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl)));
5197
aa7ed888 5198 GList *vmls = vik_layers_panel_get_all_layers_of_type(vlp, VIK_LAYER_MAPS, TRUE); // Includes hidden map layer types
7114e879
QT
5199 int num_maps = g_list_length(vmls);
5200
5201 if (!num_maps) {
4c77d5e0 5202 a_dialog_msg(VIK_GTK_WINDOW_FROM_LAYER(vtl), GTK_MESSAGE_ERROR, _("No map layer in use. Create one first"), NULL);
7114e879
QT
5203 return;
5204 }
5205
5206 gchar **map_names = g_malloc(1 + num_maps * sizeof(gpointer));
5207 VikMapsLayer **map_layers = g_malloc(1 + num_maps * sizeof(gpointer));
5208
5209 gchar **np = map_names;
5210 VikMapsLayer **lp = map_layers;
5211 for (i = 0; i < num_maps; i++) {
5212 gboolean dup = FALSE;
5213 vml = (VikMapsLayer *)(vmls->data);
5214 for (j = 0; j < i; j++) { /* no duplicate allowed */
5215 if (vik_maps_layer_get_map_type(vml) == vik_maps_layer_get_map_type(map_layers[j])) {
5216 dup = TRUE;
5217 break;
5218 }
5219 }
5220 if (!dup) {
5221 *lp++ = vml;
5222 *np++ = vik_maps_layer_get_map_label(vml);
5223 }
5224 vmls = vmls->next;
5225 }
5226 *lp = NULL;
5227 *np = NULL;
5228 num_maps = lp - map_layers;
5229
5230 for (default_map = 0; default_map < num_maps; default_map++) {
5231 /* TODO: check for parent layer's visibility */
5232 if (VIK_LAYER(map_layers[default_map])->visible)
5233 break;
5234 }
5235 default_map = (default_map == num_maps) ? 0 : default_map;
5236
5237 gdouble cur_zoom = vik_viewport_get_zoom(vvp);
5238 for (default_zoom = 0; default_zoom < sizeof(zoom_vals)/sizeof(gdouble); default_zoom++) {
5239 if (cur_zoom == zoom_vals[default_zoom])
5240 break;
5241 }
5242 default_zoom = (default_zoom == sizeof(zoom_vals)/sizeof(gdouble)) ? sizeof(zoom_vals)/sizeof(gdouble) - 1 : default_zoom;
5243
5244 if (!a_dialog_map_n_zoom(VIK_GTK_WINDOW_FROM_LAYER(vtl), map_names, default_map, zoomlist, default_zoom, &selected_map, &selected_zoom))
5245 goto done;
5246
5247 vik_track_download_map(tr, map_layers[selected_map], vvp, zoom_vals[selected_zoom]);
5248
5249done:
5250 for (i = 0; i < num_maps; i++)
5251 g_free(map_names[i]);
5252 g_free(map_names);
5253 g_free(map_layers);
5254
5255 g_list_free(vmls);
5256
5257}
0c1044e9 5258
a8fe53f8
EB
5259/**** lowest waypoint number calculation ***/
5260static gint highest_wp_number_name_to_number(const gchar *name) {
5261 if ( strlen(name) == 3 ) {
5262 int n = atoi(name);
5263 if ( n < 100 && name[0] != '0' )
5264 return -1;
5265 if ( n < 10 && name[0] != '0' )
5266 return -1;
5267 return n;
5268 }
5269 return -1;
5270}
5271
5272
5273static void highest_wp_number_reset(VikTrwLayer *vtl)
5274{
5275 vtl->highest_wp_number = -1;
5276}
5277
5278static void highest_wp_number_add_wp(VikTrwLayer *vtl, const gchar *new_wp_name)
5279{
5280 /* if is bigger that top, add it */
5281 gint new_wp_num = highest_wp_number_name_to_number(new_wp_name);
5282 if ( new_wp_num > vtl->highest_wp_number )
5283 vtl->highest_wp_number = new_wp_num;
5284}
5285
5286static void highest_wp_number_remove_wp(VikTrwLayer *vtl, const gchar *old_wp_name)
5287{
5288 /* if wasn't top, do nothing. if was top, count backwards until we find one used */
5289 gint old_wp_num = highest_wp_number_name_to_number(old_wp_name);
5290 if ( vtl->highest_wp_number == old_wp_num ) {
5291 gchar buf[4];
5292 vtl->highest_wp_number --;
5293
5294 g_snprintf(buf,4,"%03d", vtl->highest_wp_number );
5295 /* search down until we find something that *does* exist */
5296
3ce17b61 5297 while ( vtl->highest_wp_number > 0 && ! g_hash_table_lookup ( vtl->waypoints, buf ) ) {
a8fe53f8
EB
5298 vtl->highest_wp_number --;
5299 g_snprintf(buf,4,"%03d", vtl->highest_wp_number );
5300 }
5301 }
5302}
5303
5304/* get lowest unused number */
5305static gchar *highest_wp_number_get(VikTrwLayer *vtl)
5306{
5307 gchar buf[4];
5308 if ( vtl->highest_wp_number < 0 || vtl->highest_wp_number >= 999 )
3ce17b61 5309 return NULL;
a8fe53f8
EB
5310 g_snprintf(buf,4,"%03d", vtl->highest_wp_number+1 );
5311 return g_strdup(buf);
5312}