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