]> git.street.me.uk Git - andy/viking.git/blame - src/viktrwlayer.c
[QA] Better internal variable name in track drawing
[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>
c9570f86 8 * Copyright (c) 2012, Rob Norris <rw_norris@hotmail.com>
50a14534
EB
9 *
10 * This program is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License as published by
12 * the Free Software Foundation; either version 2 of the License, or
13 * (at your option) any later version.
14 *
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU General Public License for more details.
19 *
20 * You should have received a copy of the GNU General Public License
21 * along with this program; if not, write to the Free Software
22 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
23 *
24 */
50a14534 25/* WARNING: If you go beyond this point, we are NOT responsible for any ill effects on your sanity */
a7cd93ac 26/* viktrwlayer.c -- 5000+ lines can make a difference in the state of things */
50a14534 27
3e7553ae
GB
28#ifdef HAVE_CONFIG_H
29#include "config.h"
30#endif
31
50a14534 32#include "viking.h"
7114e879 33#include "vikmapslayer.h"
e50758c7 34#include "vikgpslayer.h"
50a14534
EB
35#include "viktrwlayer_tpwin.h"
36#include "viktrwlayer_propwin.h"
b3eb3b98
RN
37#ifdef VIK_CONFIG_GEOTAG
38#include "viktrwlayer_geotag.h"
39#include "geotag_exif.h"
40#endif
acaf7113 41#include "garminsymbols.h"
50a14534
EB
42#include "thumbnails.h"
43#include "background.h"
911400b5 44#include "gpx.h"
e0b0b9c1 45#include "babel.h"
ad0a8c2d
EB
46#include "dem.h"
47#include "dems.h"
165a4fa9 48#include "geonamessearch.h"
3e7553ae
GB
49#ifdef VIK_CONFIG_OPENSTREETMAP
50#include "osm-traces.h"
51#endif
28c82d8b 52#include "acquire.h"
16fc32f6 53#include "datasources.h"
e50758c7 54#include "datasource_gps.h"
7d02a0b0 55#include "util.h"
50a14534 56
bce3a7b0
EB
57#include "icons/icons.h"
58
8c00358d 59#ifdef HAVE_MATH_H
50a14534 60#include <math.h>
8c00358d
GB
61#endif
62#ifdef HAVE_STRING_H
50a14534 63#include <string.h>
8c00358d
GB
64#endif
65#ifdef HAVE_STDLIB_H
50a14534 66#include <stdlib.h>
8c00358d 67#endif
8c060406 68#include <stdio.h>
50a14534
EB
69#include <ctype.h>
70
777e2d4d 71#include <gdk/gdkkeysyms.h>
8c060406
MA
72#include <glib.h>
73#include <glib/gstdio.h>
4c77d5e0 74#include <glib/gi18n.h>
777e2d4d 75
55340efa
GB
76#ifdef VIK_CONFIG_GOOGLE
77#define GOOGLE_DIRECTIONS_STRING "maps.google.com/maps?q=from:%s,%s+to:%s,%s&output=js"
ebf1bd39
RN
78#endif
79
18df6d42 80#define VIK_TRW_LAYER_TRACK_GC 16
50a14534
EB
81#define VIK_TRW_LAYER_TRACK_GC_RATES 10
82#define VIK_TRW_LAYER_TRACK_GC_MIN 0
83#define VIK_TRW_LAYER_TRACK_GC_MAX 11
84#define VIK_TRW_LAYER_TRACK_GC_BLACK 12
18df6d42
RN
85#define VIK_TRW_LAYER_TRACK_GC_SLOW 13
86#define VIK_TRW_LAYER_TRACK_GC_AVER 14
87#define VIK_TRW_LAYER_TRACK_GC_FAST 15
50a14534
EB
88
89#define DRAWMODE_BY_TRACK 0
18df6d42 90#define DRAWMODE_BY_SPEED 1
50a14534 91#define DRAWMODE_ALL_BLACK 2
18df6d42
RN
92// Note using DRAWMODE_BY_SPEED may be slow especially for vast numbers of trackpoints
93// as we are (re)calculating the colour for every point
50a14534
EB
94
95#define POINTS 1
96#define LINES 2
97
98/* this is how it knows when you click if you are clicking close to a trackpoint. */
99#define TRACKPOINT_SIZE_APPROX 5
100#define WAYPOINT_SIZE_APPROX 5
101
b42a25ba
EB
102#define MIN_STOP_LENGTH 15
103#define MAX_STOP_LENGTH 86400
104#define DRAW_ELEVATION_FACTOR 30 /* height of elevation plotting, sort of relative to zoom level ("mpp" that isn't mpp necessarily) */
105 /* this is multiplied by user-inputted value from 1-100. */
50a14534
EB
106
107enum { WP_SYMBOL_FILLED_SQUARE, WP_SYMBOL_SQUARE, WP_SYMBOL_CIRCLE, WP_SYMBOL_X, WP_NUM_SYMBOLS };
108
5a77ae71
RN
109// See http://developer.gnome.org/pango/stable/PangoMarkupFormat.html
110typedef enum {
111 FS_XX_SMALL = 0, // 'xx-small'
112 FS_X_SMALL,
113 FS_SMALL,
114 FS_MEDIUM, // DEFAULT
115 FS_LARGE,
116 FS_X_LARGE,
117 FS_XX_LARGE,
118 FS_NUM_SIZES
119} font_size_t;
120
50a14534
EB
121struct _VikTrwLayer {
122 VikLayer vl;
123 GHashTable *tracks;
124 GHashTable *tracks_iters;
125 GHashTable *waypoints_iters;
126 GHashTable *waypoints;
127 GtkTreeIter waypoints_iter, tracks_iter;
128 gboolean tracks_visible, waypoints_visible;
129 guint8 drawmode;
130 guint8 drawpoints;
3a1c8e42 131 guint8 drawpoints_size;
b42a25ba
EB
132 guint8 drawelevation;
133 guint8 elevation_factor;
134 guint8 drawstops;
135 guint32 stop_length;
50a14534 136 guint8 drawlines;
08084371 137 guint8 drawdirections;
3a1c8e42 138 guint8 drawdirections_size;
50a14534
EB
139 guint8 line_thickness;
140 guint8 bg_line_thickness;
141
142 guint8 wp_symbol;
143 guint8 wp_size;
ea3933fc 144 gboolean wp_draw_symbols;
5a77ae71 145 font_size_t wp_font_size;
50a14534 146
18df6d42 147 gdouble track_draw_speed_factor;
50a14534 148 GArray *track_gc;
8e9c992d 149 GdkGC *current_track_gc;
745fda83
RN
150 // Separate GC for a track's potential new point as drawn via separate method
151 // (compared to the actual track points drawn in the main trw_layer_draw_track function)
152 GdkGC *current_track_newpoint_gc;
50a14534
EB
153 GdkGC *track_bg_gc;
154 GdkGC *waypoint_gc;
155 GdkGC *waypoint_text_gc;
156 GdkGC *waypoint_bg_gc;
50a14534
EB
157 VikTrack *current_track;
158 guint16 ct_x1, ct_y1, ct_x2, ct_y2;
745fda83 159 gboolean draw_sync_done;
ef5e8132 160 gboolean draw_sync_do;
50a14534
EB
161
162 VikCoordMode coord_mode;
163
164 /* wp editing tool */
165 VikWaypoint *current_wp;
c9570f86 166 gpointer current_wp_id;
50a14534
EB
167 gboolean moving_wp;
168 gboolean waypoint_rightclick;
169
170 /* track editing tool */
171 GList *current_tpl;
ce4bd1cf
RN
172 VikTrack *current_tp_track;
173 gpointer current_tp_id;
50a14534
EB
174 VikTrwLayerTpwin *tpwin;
175
50a14534
EB
176 /* track editing tool -- more specifically, moving tps */
177 gboolean moving_tp;
178
7ff7d728
RN
179 /* route finder tool */
180 gboolean route_finder_started;
181 VikCoord route_finder_coord;
182 gboolean route_finder_check_added_track;
ce4bd1cf 183 VikTrack *route_finder_added_track;
7ff7d728
RN
184 VikTrack *route_finder_current_track;
185 gboolean route_finder_append;
1eef1bde 186
50a14534
EB
187 gboolean drawlabels;
188 gboolean drawimages;
189 guint8 image_alpha;
190 GQueue *image_cache;
191 guint8 image_size;
192 guint16 image_cache_size;
193
194 /* for waypoint text */
195 PangoLayout *wplabellayout;
196
197 gboolean has_verified_thumbnails;
198
199 GtkMenu *wp_right_click_menu;
8bd81489 200 GtkMenu *track_right_click_menu;
50a14534 201
20c7a3a0
QT
202 /* menu */
203 VikStdLayerMenuItem menu_selection;
204
a8fe53f8 205 gint highest_wp_number;
50a14534
EB
206};
207
208/* A caached waypoint image. */
209typedef struct {
210 GdkPixbuf *pixbuf;
211 gchar *image; /* filename */
212} CachedPixbuf;
213
214struct DrawingParams {
215 VikViewport *vp;
216 VikTrwLayer *vtl;
217 gdouble xmpp, ympp;
218 guint16 width, height;
08084371
RN
219 gdouble cc; // Cosine factor in track directions
220 gdouble ss; // Sine factor in track directions
50a14534
EB
221 const VikCoord *center;
222 gint track_gc_iter;
223 gboolean one_zone, lat_lon;
224 gdouble ce1, ce2, cn1, cn2;
225};
226
c9570f86
RN
227static gboolean trw_layer_delete_waypoint ( VikTrwLayer *vtl, VikWaypoint *wp );
228
6bb72350
RN
229static void trw_layer_delete_item ( gpointer pass_along[6] );
230static void trw_layer_copy_item_cb ( gpointer pass_along[6] );
231static void trw_layer_cut_item_cb ( gpointer pass_along[6] );
33534cd8 232
50a14534 233static void trw_layer_find_maxmin_waypoints ( const gchar *name, const VikWaypoint *w, struct LatLon maxmin[2] );
867f4673 234static void trw_layer_find_maxmin_tracks ( const gchar *name, const VikTrack *trk, struct LatLon maxmin[2] );
165a4fa9 235static void trw_layer_find_maxmin (VikTrwLayer *vtl, struct LatLon maxmin[2]);
50a14534
EB
236
237static void trw_layer_new_track_gcs ( VikTrwLayer *vtl, VikViewport *vp );
238static void trw_layer_free_track_gcs ( VikTrwLayer *vtl );
239
50a14534
EB
240static void trw_layer_draw_track_cb ( const gchar *name, VikTrack *track, struct DrawingParams *dp );
241static void trw_layer_draw_waypoint ( const gchar *name, VikWaypoint *wp, struct DrawingParams *dp );
242
6bb72350
RN
243static void goto_coord ( gpointer *vlp, gpointer vvp, gpointer vl, const VikCoord *coord );
244static void trw_layer_goto_track_startpoint ( gpointer pass_along[6] );
50a14534 245static void trw_layer_goto_track_endpoint ( gpointer pass_along[6] );
6bb72350
RN
246static void trw_layer_goto_track_max_speed ( gpointer pass_along[6] );
247static void trw_layer_goto_track_max_alt ( gpointer pass_along[6] );
248static void trw_layer_goto_track_min_alt ( gpointer pass_along[6] );
249static void trw_layer_goto_track_center ( gpointer pass_along[6] );
24774c43 250static void trw_layer_merge_by_segment ( gpointer pass_along[6] );
111fa174 251static void trw_layer_merge_by_timestamp ( gpointer pass_along[6] );
6bb72350 252static void trw_layer_merge_with_other ( gpointer pass_along[6] );
9c34f614 253static void trw_layer_append_track ( gpointer pass_along[6] );
111fa174 254static void trw_layer_split_by_timestamp ( gpointer pass_along[6] );
af2341f3 255static void trw_layer_split_by_n_points ( gpointer pass_along[6] );
a4aefe69 256static void trw_layer_split_at_trackpoint ( gpointer pass_along[6] );
98e87078 257static void trw_layer_split_segments ( gpointer pass_along[6] );
b6eda120 258static void trw_layer_delete_points_same_position ( gpointer pass_along[6] );
6579ca1f 259static void trw_layer_delete_points_same_time ( gpointer pass_along[6] );
eb9fd106 260static void trw_layer_reverse ( gpointer pass_along[6] );
c95d6b00
RN
261static void trw_layer_download_map_along_track_cb ( gpointer pass_along[6] );
262static void trw_layer_edit_trackpoint ( gpointer pass_along[6] );
a412f3f5 263static void trw_layer_show_picture ( gpointer pass_along[6] );
e50758c7 264static void trw_layer_gps_upload_any ( gpointer pass_along[6] );
c95d6b00 265
50a14534 266static void trw_layer_centerize ( gpointer layer_and_vlp[2] );
5a10c240 267static void trw_layer_auto_view ( gpointer layer_and_vlp[2] );
ce4bd1cf 268static void trw_layer_export ( gpointer layer_and_vlp[2], const gchar* title, const gchar* default_name, VikTrack* trk, guint file_type );
50a14534
EB
269static void trw_layer_goto_wp ( gpointer layer_and_vlp[2] );
270static void trw_layer_new_wp ( gpointer lav[2] );
fc59e8c7 271static void trw_layer_auto_waypoints_view ( gpointer lav[2] );
535ed1ae 272static void trw_layer_auto_tracks_view ( gpointer lav[2] );
c9a5cbf9 273static void trw_layer_delete_all_tracks ( gpointer lav[2] );
20b671c3 274static void trw_layer_delete_tracks_from_selection ( gpointer lav[2] );
c9a5cbf9 275static void trw_layer_delete_all_waypoints ( gpointer lav[2] );
20b671c3 276static void trw_layer_delete_waypoints_from_selection ( gpointer lav[2] );
165a4fa9
HR
277static void trw_layer_new_wikipedia_wp_viewport ( gpointer lav[2] );
278static void trw_layer_new_wikipedia_wp_layer ( gpointer lav[2] );
b3eb3b98
RN
279#ifdef VIK_CONFIG_GEOTAG
280static void trw_layer_geotagging_waypoint_mtime_keep ( gpointer pass_along[6] );
281static void trw_layer_geotagging_waypoint_mtime_update ( gpointer pass_along[6] );
282static void trw_layer_geotagging_track ( gpointer pass_along[6] );
283static void trw_layer_geotagging ( gpointer lav[2] );
284#endif
16fc32f6 285static void trw_layer_acquire_gps_cb ( gpointer lav[2] );
55340efa 286#ifdef VIK_CONFIG_GOOGLE
16fc32f6 287static void trw_layer_acquire_google_cb ( gpointer lav[2] );
29332250 288#endif
40f5740b
RN
289#ifdef VIK_CONFIG_OPENSTREETMAP
290static void trw_layer_acquire_osm_cb ( gpointer lav[2] );
291#endif
16fc32f6
RN
292#ifdef VIK_CONFIG_GEOCACHES
293static void trw_layer_acquire_geocache_cb ( gpointer lav[2] );
294#endif
68bab1bd
RN
295#ifdef VIK_CONFIG_GEOTAG
296static void trw_layer_acquire_geotagged_cb ( gpointer lav[2] );
297#endif
d7ac7564 298static void trw_layer_acquire_file_cb ( gpointer lav[2] );
e50758c7 299static void trw_layer_gps_upload ( gpointer lav[2] );
50a14534
EB
300
301/* pop-up items */
c9570f86 302static void trw_layer_properties_item ( gpointer pass_along[7] );
6bb72350
RN
303static void trw_layer_goto_waypoint ( gpointer pass_along[6] );
304static void trw_layer_waypoint_gc_webpage ( gpointer pass_along[6] );
50a14534 305
c9570f86 306static void trw_layer_realize_waypoint ( gpointer id, VikWaypoint *wp, gpointer pass_along[5] );
ce4bd1cf 307static void trw_layer_realize_track ( gpointer id, VikTrack *track, gpointer pass_along[5] );
08084371 308static void init_drawing_params ( struct DrawingParams *dp, VikTrwLayer *vtl, VikViewport *vp );
50a14534 309
db79f75f 310static void trw_layer_insert_tp_after_current_tp ( VikTrwLayer *vtl );
50a14534
EB
311static void trw_layer_cancel_current_tp ( VikTrwLayer *vtl, gboolean destroy );
312static void trw_layer_tpwin_response ( VikTrwLayer *vtl, gint response );
313static void trw_layer_tpwin_init ( VikTrwLayer *vtl );
50a14534 314
941aa6e9 315static gpointer tool_edit_trackpoint_create ( VikWindow *vw, VikViewport *vvp);
33534cd8 316static gboolean tool_edit_trackpoint_click ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data );
dc2c040e 317static gboolean tool_edit_trackpoint_move ( VikTrwLayer *vtl, GdkEventMotion *event, gpointer data );
33534cd8 318static gboolean tool_edit_trackpoint_release ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data );
941aa6e9
AF
319static gpointer tool_show_picture_create ( VikWindow *vw, VikViewport *vvp);
320static gboolean tool_show_picture_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp );
321static gpointer tool_edit_waypoint_create ( VikWindow *vw, VikViewport *vvp);
7432fddf 322static gboolean tool_edit_waypoint_click ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data );
dc2c040e 323static gboolean tool_edit_waypoint_move ( VikTrwLayer *vtl, GdkEventMotion *event, gpointer data );
7432fddf 324static gboolean tool_edit_waypoint_release ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data );
98f5364d
EB
325static gpointer tool_begin_track_create ( VikWindow *vw, VikViewport *vvp);
326static gboolean tool_begin_track_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp );
941aa6e9
AF
327static gpointer tool_new_track_create ( VikWindow *vw, VikViewport *vvp);
328static gboolean tool_new_track_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp );
dc2c040e 329static VikLayerToolFuncStatus tool_new_track_move ( VikTrwLayer *vtl, GdkEventMotion *event, VikViewport *vvp );
ef5e8132 330static void tool_new_track_release ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp );
777e2d4d 331static gboolean tool_new_track_key_press ( VikTrwLayer *vtl, GdkEventKey *event, VikViewport *vvp );
941aa6e9
AF
332static gpointer tool_new_waypoint_create ( VikWindow *vw, VikViewport *vvp);
333static gboolean tool_new_waypoint_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp );
55340efa 334#ifdef VIK_CONFIG_GOOGLE
7ff7d728
RN
335static gpointer tool_route_finder_create ( VikWindow *vw, VikViewport *vvp);
336static gboolean tool_route_finder_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp );
ebf1bd39 337#endif
50a14534 338
50a14534
EB
339static void cached_pixbuf_free ( CachedPixbuf *cp );
340static gint cached_pixbuf_cmp ( CachedPixbuf *cp, const gchar *name );
50a14534
EB
341
342static VikTrackpoint *closest_tp_in_five_pixel_interval ( VikTrwLayer *vtl, VikViewport *vvp, gint x, gint y );
343static VikWaypoint *closest_wp_in_five_pixel_interval ( VikTrwLayer *vtl, VikViewport *vvp, gint x, gint y );
344
c9570f86 345static void waypoint_convert ( const gpointer id, VikWaypoint *wp, VikCoordMode *dest_mode );
ddc47a46 346static void track_convert ( const gchar *name, VikTrack *tr, VikCoordMode *dest_mode );
e890a6e6 347
a8fe53f8
EB
348static gchar *highest_wp_number_get(VikTrwLayer *vtl);
349static void highest_wp_number_reset(VikTrwLayer *vtl);
350static void highest_wp_number_add_wp(VikTrwLayer *vtl, const gchar *new_wp_name);
351static void highest_wp_number_remove_wp(VikTrwLayer *vtl, const gchar *old_wp_name);
352
79dce0cb
RN
353// Note for the following tool GtkRadioActionEntry texts:
354// the very first text value is an internal name not displayed anywhere
355// the first N_ text value is the name used for menu entries - hence has an underscore for the keyboard accelerator
356// * remember not to clash with the values used for VikWindow level tools (Pan, Zoom, Ruler + Select)
357// the second N_ text value is used for the button tooltip (i.e. generally don't want an underscore here)
358// the value is always set to 0 and the tool loader in VikWindow will set the actual appropriate value used
50a14534 359static VikToolInterface trw_layer_tools[] = {
79dce0cb
RN
360 { { "CreateWaypoint", "vik-icon-Create Waypoint", N_("Create _Waypoint"), "<control><shift>W", N_("Create Waypoint"), 0 },
361 (VikToolConstructorFunc) tool_new_waypoint_create, NULL, NULL, NULL,
ef5e8132
RN
362 (VikToolMouseFunc) tool_new_waypoint_click, NULL, NULL, (VikToolKeyFunc) NULL,
363 FALSE,
364 GDK_CURSOR_IS_PIXMAP, &cursor_addwp_pixbuf },
941aa6e9 365
79dce0cb
RN
366 { { "CreateTrack", "vik-icon-Create Track", N_("Create _Track"), "<control><shift>T", N_("Create Track"), 0 },
367 (VikToolConstructorFunc) tool_new_track_create, NULL, NULL, NULL,
ef5e8132
RN
368 (VikToolMouseFunc) tool_new_track_click,
369 (VikToolMouseMoveFunc) tool_new_track_move,
370 (VikToolMouseFunc) tool_new_track_release,
371 (VikToolKeyFunc) tool_new_track_key_press,
372 TRUE, // Still need to handle clicks when in PAN mode to disable the potential trackpoint drawing
373 GDK_CURSOR_IS_PIXMAP, &cursor_addtr_pixbuf },
50a14534 374
79dce0cb
RN
375 { { "BeginTrack", "vik-icon-Begin Track", N_("_Begin Track"), "<control><shift>B", N_("Begin Track"), 0 },
376 (VikToolConstructorFunc) tool_begin_track_create, NULL, NULL, NULL,
ef5e8132
RN
377 (VikToolMouseFunc) tool_begin_track_click, NULL, NULL, (VikToolKeyFunc) NULL,
378 FALSE,
379 GDK_CURSOR_IS_PIXMAP, &cursor_begintr_pixbuf },
98f5364d 380
79dce0cb
RN
381 { { "EditWaypoint", "vik-icon-Edit Waypoint", N_("_Edit Waypoint"), "<control><shift>E", N_("Edit Waypoint"), 0 },
382 (VikToolConstructorFunc) tool_edit_waypoint_create, NULL, NULL, NULL,
7432fddf 383 (VikToolMouseFunc) tool_edit_waypoint_click,
dc2c040e 384 (VikToolMouseMoveFunc) tool_edit_waypoint_move,
ef5e8132
RN
385 (VikToolMouseFunc) tool_edit_waypoint_release, (VikToolKeyFunc) NULL,
386 FALSE,
387 GDK_CURSOR_IS_PIXMAP, &cursor_edwp_pixbuf },
941aa6e9 388
79dce0cb
RN
389 { { "EditTrackpoint", "vik-icon-Edit Trackpoint", N_("Edit Trac_kpoint"), "<control><shift>K", N_("Edit Trackpoint"), 0 },
390 (VikToolConstructorFunc) tool_edit_trackpoint_create, NULL, NULL, NULL,
33534cd8 391 (VikToolMouseFunc) tool_edit_trackpoint_click,
dc2c040e 392 (VikToolMouseMoveFunc) tool_edit_trackpoint_move,
ef5e8132
RN
393 (VikToolMouseFunc) tool_edit_trackpoint_release, (VikToolKeyFunc) NULL,
394 FALSE,
395 GDK_CURSOR_IS_PIXMAP, &cursor_edtr_pixbuf },
941aa6e9 396
79dce0cb
RN
397 { { "ShowPicture", "vik-icon-Show Picture", N_("Show P_icture"), "<control><shift>I", N_("Show Picture"), 0 },
398 (VikToolConstructorFunc) tool_show_picture_create, NULL, NULL, NULL,
ef5e8132
RN
399 (VikToolMouseFunc) tool_show_picture_click, NULL, NULL, (VikToolKeyFunc) NULL,
400 FALSE,
401 GDK_CURSOR_IS_PIXMAP, &cursor_showpic_pixbuf },
1eef1bde 402
55340efa 403#ifdef VIK_CONFIG_GOOGLE
79dce0cb
RN
404 { { "RouteFinder", "vik-icon-Route Finder", N_("Route _Finder"), "<control><shift>F", N_("Route Finder"), 0 },
405 (VikToolConstructorFunc) tool_route_finder_create, NULL, NULL, NULL,
ef5e8132
RN
406 (VikToolMouseFunc) tool_route_finder_click, NULL, NULL, (VikToolKeyFunc) NULL,
407 FALSE,
408 GDK_CURSOR_IS_PIXMAP, &cursor_route_finder_pixbuf },
ebf1bd39 409#endif
941aa6e9 410};
8e9c992d 411enum { TOOL_CREATE_WAYPOINT=0, TOOL_CREATE_TRACK, TOOL_BEGIN_TRACK, TOOL_EDIT_WAYPOINT, TOOL_EDIT_TRACKPOINT, TOOL_SHOW_PICTURE, NUM_TOOLS };
50a14534
EB
412
413/****** PARAMETERS ******/
414
4c77d5e0 415static gchar *params_groups[] = { N_("Waypoints"), N_("Tracks"), N_("Waypoint Images") };
55906514 416enum { GROUP_WAYPOINTS, GROUP_TRACKS, GROUP_IMAGES };
50a14534 417
18df6d42 418static gchar *params_drawmodes[] = { N_("Draw by Track"), N_("Draw by Speed"), N_("All Tracks Black"), 0 };
4c77d5e0 419static gchar *params_wpsymbols[] = { N_("Filled Square"), N_("Square"), N_("Circle"), N_("X"), 0 };
50a14534 420
3a1c8e42
RN
421#define MIN_POINT_SIZE 2
422#define MAX_POINT_SIZE 10
423
424#define MIN_ARROW_SIZE 3
425#define MAX_ARROW_SIZE 20
b42a25ba 426
50a14534
EB
427static VikLayerParamScale params_scales[] = {
428 /* min max step digits */
429 { 1, 10, 1, 0 }, /* line_thickness */
18df6d42
RN
430 { 0, 100, 1, 0 }, /* track draw speed factor */
431 { 1.0, 100.0, 1.0, 2 }, /* UNUSED */
50a14534 432 /* 5 * step == how much to turn */
3a1c8e42
RN
433 { 16, 128, 4, 0 }, // 3: image_size - NB step size ignored when an HSCALE used
434 { 0, 255, 5, 0 }, // 4: image alpha - " " " "
435 { 5, 500, 5, 0 }, // 5: image cache_size - " "
436 { 0, 8, 1, 0 }, // 6: Background line thickness
50a14534 437 { 1, 64, 1, 0 }, /* wpsize */
b42a25ba 438 { MIN_STOP_LENGTH, MAX_STOP_LENGTH, 1, 0 }, /* stop_length */
3a1c8e42
RN
439 { 1, 100, 1, 0 }, // 9: elevation factor
440 { MIN_POINT_SIZE, MAX_POINT_SIZE, 1, 0 }, // 10: track point size
441 { MIN_ARROW_SIZE, MAX_ARROW_SIZE, 1, 0 }, // 11: direction arrow size
50a14534
EB
442};
443
5a77ae71
RN
444static gchar* params_font_sizes[] = {
445 N_("Extra Extra Small"),
446 N_("Extra Small"),
447 N_("Small"),
448 N_("Medium"),
449 N_("Large"),
450 N_("Extra Large"),
451 N_("Extra Extra Large"),
452 NULL };
453
50a14534
EB
454VikLayerParam trw_layer_params[] = {
455 { "tracks_visible", VIK_LAYER_PARAM_BOOLEAN, VIK_LAYER_NOT_IN_PROPERTIES },
456 { "waypoints_visible", VIK_LAYER_PARAM_BOOLEAN, VIK_LAYER_NOT_IN_PROPERTIES },
457
4c77d5e0
GB
458 { "drawmode", VIK_LAYER_PARAM_UINT, GROUP_TRACKS, N_("Track Drawing Mode:"), VIK_LAYER_WIDGET_RADIOGROUP, NULL },
459 { "drawlines", VIK_LAYER_PARAM_BOOLEAN, GROUP_TRACKS, N_("Draw Track Lines"), VIK_LAYER_WIDGET_CHECKBUTTON },
3a1c8e42 460 { "line_thickness", VIK_LAYER_PARAM_UINT, GROUP_TRACKS, N_("Track Thickness:"), VIK_LAYER_WIDGET_SPINBUTTON, params_scales + 0 },
08084371 461 { "drawdirections", VIK_LAYER_PARAM_BOOLEAN, GROUP_TRACKS, N_("Draw Track Direction"), VIK_LAYER_WIDGET_CHECKBUTTON },
3a1c8e42 462 { "trkdirectionsize", VIK_LAYER_PARAM_UINT, GROUP_TRACKS, N_("Direction Size:"), VIK_LAYER_WIDGET_SPINBUTTON, params_scales + 11 },
4c77d5e0 463 { "drawpoints", VIK_LAYER_PARAM_BOOLEAN, GROUP_TRACKS, N_("Draw Trackpoints"), VIK_LAYER_WIDGET_CHECKBUTTON },
3a1c8e42 464 { "trkpointsize", VIK_LAYER_PARAM_UINT, GROUP_TRACKS, N_("Trackpoint Size:"), VIK_LAYER_WIDGET_SPINBUTTON, params_scales + 10 },
4c77d5e0
GB
465 { "drawelevation", VIK_LAYER_PARAM_BOOLEAN, GROUP_TRACKS, N_("Draw Elevation"), VIK_LAYER_WIDGET_CHECKBUTTON },
466 { "elevation_factor", VIK_LAYER_PARAM_UINT, GROUP_TRACKS, N_("Draw Elevation Height %:"), VIK_LAYER_WIDGET_HSCALE, params_scales + 9 },
467
468 { "drawstops", VIK_LAYER_PARAM_BOOLEAN, GROUP_TRACKS, N_("Draw Stops"), VIK_LAYER_WIDGET_CHECKBUTTON },
469 { "stop_length", VIK_LAYER_PARAM_UINT, GROUP_TRACKS, N_("Min Stop Length (seconds):"), VIK_LAYER_WIDGET_SPINBUTTON, params_scales + 8 },
470
4c77d5e0
GB
471 { "bg_line_thickness", VIK_LAYER_PARAM_UINT, GROUP_TRACKS, N_("Track BG Thickness:"), VIK_LAYER_WIDGET_SPINBUTTON, params_scales + 6 },
472 { "trackbgcolor", VIK_LAYER_PARAM_COLOR, GROUP_TRACKS, N_("Track Background Color"), VIK_LAYER_WIDGET_COLOR, 0 },
3a1c8e42 473 { "speed_factor", VIK_LAYER_PARAM_DOUBLE, GROUP_TRACKS, N_("Draw by Speed Factor (%):"), VIK_LAYER_WIDGET_HSCALE, params_scales + 1 },
4c77d5e0
GB
474
475 { "drawlabels", VIK_LAYER_PARAM_BOOLEAN, GROUP_WAYPOINTS, N_("Draw Labels"), VIK_LAYER_WIDGET_CHECKBUTTON },
5a77ae71 476 { "wpfontsize", VIK_LAYER_PARAM_UINT, GROUP_WAYPOINTS, N_("Waypoint Font Size:"), VIK_LAYER_WIDGET_COMBOBOX, params_font_sizes, NULL },
4c77d5e0
GB
477 { "wpcolor", VIK_LAYER_PARAM_COLOR, GROUP_WAYPOINTS, N_("Waypoint Color:"), VIK_LAYER_WIDGET_COLOR, 0 },
478 { "wptextcolor", VIK_LAYER_PARAM_COLOR, GROUP_WAYPOINTS, N_("Waypoint Text:"), VIK_LAYER_WIDGET_COLOR, 0 },
479 { "wpbgcolor", VIK_LAYER_PARAM_COLOR, GROUP_WAYPOINTS, N_("Background:"), VIK_LAYER_WIDGET_COLOR, 0 },
480 { "wpbgand", VIK_LAYER_PARAM_BOOLEAN, GROUP_WAYPOINTS, N_("Fake BG Color Translucency:"), VIK_LAYER_WIDGET_CHECKBUTTON, 0 },
481 { "wpsymbol", VIK_LAYER_PARAM_UINT, GROUP_WAYPOINTS, N_("Waypoint marker:"), VIK_LAYER_WIDGET_RADIOGROUP, NULL },
482 { "wpsize", VIK_LAYER_PARAM_UINT, GROUP_WAYPOINTS, N_("Waypoint size:"), VIK_LAYER_WIDGET_SPINBUTTON, params_scales + 7 },
483 { "wpsyms", VIK_LAYER_PARAM_BOOLEAN, GROUP_WAYPOINTS, N_("Draw Waypoint Symbols:"), VIK_LAYER_WIDGET_CHECKBUTTON },
484
485 { "drawimages", VIK_LAYER_PARAM_BOOLEAN, GROUP_IMAGES, N_("Draw Waypoint Images"), VIK_LAYER_WIDGET_CHECKBUTTON },
486 { "image_size", VIK_LAYER_PARAM_UINT, GROUP_IMAGES, N_("Image Size (pixels):"), VIK_LAYER_WIDGET_HSCALE, params_scales + 3 },
487 { "image_alpha", VIK_LAYER_PARAM_UINT, GROUP_IMAGES, N_("Image Alpha:"), VIK_LAYER_WIDGET_HSCALE, params_scales + 4 },
488 { "image_cache_size", VIK_LAYER_PARAM_UINT, GROUP_IMAGES, N_("Image Memory Cache Size:"), VIK_LAYER_WIDGET_HSCALE, params_scales + 5 },
50a14534
EB
489};
490
3a1c8e42 491// ENUMERATION MUST BE IN THE SAME ORDER AS THE NAMED PARAMS ABOVE
08084371
RN
492enum {
493 // Sublayer visibilities
494 PARAM_TV,
495 PARAM_WV,
496 // Tracks
497 PARAM_DM,
498 PARAM_DL,
3a1c8e42 499 PARAM_LT,
08084371 500 PARAM_DD,
3a1c8e42 501 PARAM_DDS,
08084371 502 PARAM_DP,
3a1c8e42 503 PARAM_DPS,
08084371
RN
504 PARAM_DE,
505 PARAM_EF,
506 PARAM_DS,
507 PARAM_SL,
08084371
RN
508 PARAM_BLT,
509 PARAM_TBGC,
510 PARAM_TDSF,
08084371 511 // Waypoints
3a1c8e42 512 PARAM_DLA,
08084371
RN
513 PARAM_WPFONTSIZE,
514 PARAM_WPC,
515 PARAM_WPTC,
516 PARAM_WPBC,
517 PARAM_WPBA,
518 PARAM_WPSYM,
519 PARAM_WPSIZE,
520 PARAM_WPSYMS,
521 // WP images
522 PARAM_DI,
523 PARAM_IS,
524 PARAM_IA,
525 PARAM_ICS,
526 NUM_PARAMS
527};
ad0a8c2d
EB
528
529/*** TO ADD A PARAM:
530 *** 1) Add to trw_layer_params and enumeration
531 *** 2) Handle in get_param & set_param (presumably adding on to VikTrwLayer struct)
ad0a8c2d 532 ***/
50a14534
EB
533
534/****** END PARAMETERS ******/
535
a7cd93ac
RN
536static VikTrwLayer* trw_layer_new ( gint drawmode );
537/* Layer Interface function definitions */
538static VikTrwLayer* trw_layer_create ( VikViewport *vp );
539static void trw_layer_realize ( VikTrwLayer *vtl, VikTreeview *vt, GtkTreeIter *layer_iter );
a7cd93ac
RN
540static void trw_layer_free ( VikTrwLayer *trwlayer );
541static void trw_layer_draw ( VikTrwLayer *l, gpointer data );
542static void trw_layer_change_coord_mode ( VikTrwLayer *vtl, VikCoordMode dest_mode );
543static void trw_layer_set_menu_selection ( VikTrwLayer *vtl, guint16 );
544static guint16 trw_layer_get_menu_selection ( VikTrwLayer *vtl );
545static void trw_layer_add_menu_items ( VikTrwLayer *vtl, GtkMenu *menu, gpointer vlp );
546static gboolean trw_layer_sublayer_add_menu_items ( VikTrwLayer *l, GtkMenu *menu, gpointer vlp, gint subtype, gpointer sublayer, GtkTreeIter *iter, VikViewport *vvp );
547static const gchar* trw_layer_sublayer_rename_request ( VikTrwLayer *l, const gchar *newname, gpointer vlp, gint subtype, gpointer sublayer, GtkTreeIter *iter );
548static gboolean trw_layer_sublayer_toggle_visible ( VikTrwLayer *l, gint subtype, gpointer sublayer );
549static const gchar* trw_layer_layer_tooltip ( VikTrwLayer *vtl );
550static const gchar* trw_layer_sublayer_tooltip ( VikTrwLayer *l, gint subtype, gpointer sublayer );
551static gboolean trw_layer_selected ( VikTrwLayer *l, gint subtype, gpointer sublayer, gint type, gpointer vlp );
552static void trw_layer_marshall ( VikTrwLayer *vtl, guint8 **data, gint *len );
553static VikTrwLayer *trw_layer_unmarshall ( guint8 *data, gint len, VikViewport *vvp );
554static gboolean trw_layer_set_param ( VikTrwLayer *vtl, guint16 id, VikLayerParamData data, VikViewport *vp, gboolean is_file_operation );
555static VikLayerParamData trw_layer_get_param ( VikTrwLayer *vtl, guint16 id, gboolean is_file_operation );
556static void trw_layer_del_item ( VikTrwLayer *vtl, gint subtype, gpointer sublayer );
557static void trw_layer_cut_item ( VikTrwLayer *vtl, gint subtype, gpointer sublayer );
558static void trw_layer_copy_item ( VikTrwLayer *vtl, gint subtype, gpointer sublayer, guint8 **item, guint *len );
559static gboolean trw_layer_paste_item ( VikTrwLayer *vtl, gint subtype, guint8 *item, guint len );
560static void trw_layer_free_copied_item ( gint subtype, gpointer item );
561static void trw_layer_drag_drop_request ( VikTrwLayer *vtl_src, VikTrwLayer *vtl_dest, GtkTreeIter *src_item_iter, GtkTreePath *dest_path );
562static gboolean trw_layer_select_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp, tool_ed_t *t );
563static gboolean trw_layer_select_move ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp, tool_ed_t *t );
564static gboolean trw_layer_select_release ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp, tool_ed_t *t );
565static gboolean trw_layer_show_selected_viewport_menu ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp );
566/* End Layer Interface function definitions */
567
50a14534 568VikLayerInterface vik_trw_layer_interface = {
db386630 569 "TrackWaypoint",
affcc0f2 570 N_("TrackWaypoint"),
75078768 571 "<control><shift>Y",
5bfafde9 572 &viktrwlayer_pixbuf,
50a14534
EB
573
574 trw_layer_tools,
575 sizeof(trw_layer_tools) / sizeof(VikToolInterface),
576
577 trw_layer_params,
578 NUM_PARAMS,
579 params_groups, /* params_groups */
580 sizeof(params_groups)/sizeof(params_groups[0]), /* number of groups */
581
5a4a28bf
QT
582 VIK_MENU_ITEM_ALL,
583
a7cd93ac
RN
584 (VikLayerFuncCreate) trw_layer_create,
585 (VikLayerFuncRealize) trw_layer_realize,
50a14534 586 (VikLayerFuncPostRead) trw_layer_verify_thumbnails,
a7cd93ac 587 (VikLayerFuncFree) trw_layer_free,
50a14534
EB
588
589 (VikLayerFuncProperties) NULL,
a7cd93ac 590 (VikLayerFuncDraw) trw_layer_draw,
50a14534
EB
591 (VikLayerFuncChangeCoordMode) trw_layer_change_coord_mode,
592
a7cd93ac
RN
593 (VikLayerFuncSetMenuItemsSelection) trw_layer_set_menu_selection,
594 (VikLayerFuncGetMenuItemsSelection) trw_layer_get_menu_selection,
20c7a3a0 595
a7cd93ac
RN
596 (VikLayerFuncAddMenuItems) trw_layer_add_menu_items,
597 (VikLayerFuncSublayerAddMenuItems) trw_layer_sublayer_add_menu_items,
50a14534 598
a7cd93ac
RN
599 (VikLayerFuncSublayerRenameRequest) trw_layer_sublayer_rename_request,
600 (VikLayerFuncSublayerToggleVisible) trw_layer_sublayer_toggle_visible,
c7060c4e 601 (VikLayerFuncSublayerTooltip) trw_layer_sublayer_tooltip,
cb89c5a5 602 (VikLayerFuncLayerTooltip) trw_layer_layer_tooltip,
a7cd93ac 603 (VikLayerFuncLayerSelected) trw_layer_selected,
50a14534 604
911400b5
AF
605 (VikLayerFuncMarshall) trw_layer_marshall,
606 (VikLayerFuncUnmarshall) trw_layer_unmarshall,
50a14534
EB
607
608 (VikLayerFuncSetParam) trw_layer_set_param,
609 (VikLayerFuncGetParam) trw_layer_get_param,
610
611 (VikLayerFuncReadFileData) a_gpspoint_read_file,
612 (VikLayerFuncWriteFileData) a_gpspoint_write_file,
613
33534cd8 614 (VikLayerFuncDeleteItem) trw_layer_del_item,
d5874ef9 615 (VikLayerFuncCutItem) trw_layer_cut_item,
50a14534
EB
616 (VikLayerFuncCopyItem) trw_layer_copy_item,
617 (VikLayerFuncPasteItem) trw_layer_paste_item,
618 (VikLayerFuncFreeCopiedItem) trw_layer_free_copied_item,
70a23263
AF
619
620 (VikLayerFuncDragDropRequest) trw_layer_drag_drop_request,
77ad64fa
RN
621
622 (VikLayerFuncSelectClick) trw_layer_select_click,
08f14055
RN
623 (VikLayerFuncSelectMove) trw_layer_select_move,
624 (VikLayerFuncSelectRelease) trw_layer_select_release,
e46f259a 625 (VikLayerFuncSelectedViewportMenu) trw_layer_show_selected_viewport_menu,
50a14534
EB
626};
627
c9570f86 628// for copy & paste
50a14534 629typedef struct {
ddc47a46
AF
630 guint len;
631 guint8 data[0];
ddc47a46 632} FlatItem;
50a14534
EB
633
634GType vik_trw_layer_get_type ()
635{
636 static GType vtl_type = 0;
637
638 if (!vtl_type)
639 {
640 static const GTypeInfo vtl_info =
641 {
642 sizeof (VikTrwLayerClass),
643 NULL, /* base_init */
644 NULL, /* base_finalize */
645 NULL, /* class init */
646 NULL, /* class_finalize */
647 NULL, /* class_data */
648 sizeof (VikTrwLayer),
649 0,
650 NULL /* instance init */
651 };
652 vtl_type = g_type_register_static ( VIK_LAYER_TYPE, "VikTrwLayer", &vtl_info, 0 );
653 }
654
655 return vtl_type;
656}
657
33534cd8
AF
658static void trw_layer_del_item ( VikTrwLayer *vtl, gint subtype, gpointer sublayer )
659{
6bb72350 660 static gpointer pass_along[6];
33534cd8
AF
661 if (!sublayer) {
662 return;
663 }
664
665 pass_along[0] = vtl;
666 pass_along[1] = NULL;
dc2c040e 667 pass_along[2] = GINT_TO_POINTER (subtype);
33534cd8 668 pass_along[3] = sublayer;
169acf64 669 pass_along[4] = GINT_TO_POINTER (1); // Confirm delete request
6bb72350 670 pass_along[5] = NULL;
33534cd8
AF
671
672 trw_layer_delete_item ( pass_along );
673}
50a14534 674
d5874ef9
RN
675static void trw_layer_cut_item ( VikTrwLayer *vtl, gint subtype, gpointer sublayer )
676{
6bb72350 677 static gpointer pass_along[6];
d5874ef9
RN
678 if (!sublayer) {
679 return;
680 }
681
682 pass_along[0] = vtl;
683 pass_along[1] = NULL;
684 pass_along[2] = GINT_TO_POINTER (subtype);
685 pass_along[3] = sublayer;
169acf64 686 pass_along[4] = GINT_TO_POINTER (0); // No delete confirmation needed for auto delete
6bb72350 687 pass_along[5] = NULL;
d5874ef9
RN
688
689 trw_layer_copy_item_cb(pass_along);
690 trw_layer_cut_item_cb(pass_along);
691}
692
6bb72350 693static void trw_layer_copy_item_cb ( gpointer pass_along[6])
2cebc318
QT
694{
695 VikTrwLayer *vtl = VIK_TRW_LAYER(pass_along[0]);
dc2c040e 696 gint subtype = GPOINTER_TO_INT (pass_along[2]);
2cebc318
QT
697 gpointer * sublayer = pass_along[3];
698 guint8 *data = NULL;
699 guint len;
700
701 trw_layer_copy_item( vtl, subtype, sublayer, &data, &len);
702
703 if (data) {
c9570f86
RN
704 const gchar* name;
705 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT ) {
706 VikWaypoint *wp = g_hash_table_lookup ( vtl->waypoints, sublayer);
707 if ( wp && wp->name )
708 name = wp->name;
709 else
710 name = NULL; // Broken :(
711 }
ce4bd1cf
RN
712 else {
713 VikTrack *trk = g_hash_table_lookup ( vtl->tracks, sublayer);
714 if ( trk && trk->name )
715 name = trk->name;
716 else
717 name = NULL; // Broken :(
718 }
c9570f86 719
2cebc318 720 a_clipboard_copy( VIK_CLIPBOARD_DATA_SUBLAYER, VIK_LAYER_TRW,
c9570f86 721 subtype, len, name, data);
2cebc318
QT
722 }
723}
724
6bb72350 725static void trw_layer_cut_item_cb ( gpointer pass_along[6])
2cebc318
QT
726{
727 trw_layer_copy_item_cb(pass_along);
169acf64 728 pass_along[4] = GINT_TO_POINTER (0); // Never need to confirm automatic delete
2cebc318
QT
729 trw_layer_delete_item(pass_along);
730}
731
ddc47a46 732static void trw_layer_copy_item ( VikTrwLayer *vtl, gint subtype, gpointer sublayer, guint8 **item, guint *len )
50a14534 733{
ddc47a46
AF
734 FlatItem *fi;
735 guint8 *id;
736 guint il;
737
738 if (!sublayer) {
739 *item = NULL;
740 return;
50a14534 741 }
ddc47a46
AF
742
743 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT )
50a14534 744 {
ddc47a46 745 vik_waypoint_marshall ( g_hash_table_lookup ( vtl->waypoints, sublayer ), &id, &il );
c9570f86
RN
746 // 'Simple' memory copy of byte array from the marshalling above
747 *len = sizeof(FlatItem) + 1 + il; // not sure what the 1 is for yet...
748 fi = g_malloc ( *len );
749 fi->len = *len;
750 memcpy(fi->data, id, il);
ddc47a46 751 } else {
c9570f86 752
ddc47a46 753 vik_track_marshall ( g_hash_table_lookup ( vtl->tracks, sublayer ), &id, &il );
ce4bd1cf
RN
754 // less magic than before...
755 *len = sizeof(FlatItem) + 1 + il;
c9570f86 756 fi = g_malloc ( *len );
ce4bd1cf
RN
757 fi->len = *len;
758 memcpy(fi->data, id, il);
50a14534 759 }
ddc47a46 760
ddc47a46
AF
761 g_free(id);
762 *item = (guint8 *)fi;
50a14534
EB
763}
764
ddc47a46 765static gboolean trw_layer_paste_item ( VikTrwLayer *vtl, gint subtype, guint8 *item, guint len )
50a14534 766{
ddc47a46
AF
767 FlatItem *fi = (FlatItem *) item;
768
769 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT && fi )
50a14534 770 {
ddc47a46
AF
771 VikWaypoint *w;
772 gchar *name;
773
c9570f86
RN
774 w = vik_waypoint_unmarshall(fi->data, fi->len);
775 // When copying - we'll create a new name based on the original
9748531a 776 name = trw_layer_new_unique_sublayer_name(vtl, VIK_TRW_LAYER_SUBLAYER_WAYPOINT, w->name);
ddc47a46 777 vik_trw_layer_add_waypoint ( vtl, name, w );
c9570f86
RN
778 waypoint_convert (NULL, w, &vtl->coord_mode);
779
58cd1c43
RN
780 // Consider if redraw necessary for the new item
781 if ( vtl->vl.visible && vtl->waypoints_visible && w->visible )
f01eebd4 782 vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
50a14534
EB
783 return TRUE;
784 }
ddc47a46 785 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK && fi )
50a14534 786 {
ddc47a46
AF
787 VikTrack *t;
788 gchar *name;
ce4bd1cf
RN
789
790 t = vik_track_unmarshall(fi->data, fi->len);
791 // When copying - we'll create a new name based on the original
9748531a 792 name = trw_layer_new_unique_sublayer_name(vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, t->name);
ddc47a46 793 vik_trw_layer_add_track ( vtl, name, t );
ce4bd1cf
RN
794 track_convert (name, t, &vtl->coord_mode);
795
58cd1c43
RN
796 // Consider if redraw necessary for the new item
797 if ( vtl->vl.visible && vtl->tracks_visible && t->visible )
f01eebd4 798 vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
50a14534
EB
799 return TRUE;
800 }
801 return FALSE;
802}
803
804static void trw_layer_free_copied_item ( gint subtype, gpointer item )
805{
ddc47a46
AF
806 if (item) {
807 g_free(item);
50a14534
EB
808 }
809}
810
158b3642 811static gboolean trw_layer_set_param ( VikTrwLayer *vtl, guint16 id, VikLayerParamData data, VikViewport *vp, gboolean is_file_operation )
50a14534
EB
812{
813 switch ( id )
814 {
815 case PARAM_TV: vtl->tracks_visible = data.b; break;
816 case PARAM_WV: vtl->waypoints_visible = data.b; break;
817 case PARAM_DM: vtl->drawmode = data.u; break;
818 case PARAM_DP: vtl->drawpoints = data.b; break;
3a1c8e42
RN
819 case PARAM_DPS:
820 if ( data.u >= MIN_POINT_SIZE && data.u <= MAX_POINT_SIZE )
821 vtl->drawpoints_size = data.u;
822 break;
b42a25ba
EB
823 case PARAM_DE: vtl->drawelevation = data.b; break;
824 case PARAM_DS: vtl->drawstops = data.b; break;
50a14534 825 case PARAM_DL: vtl->drawlines = data.b; break;
08084371 826 case PARAM_DD: vtl->drawdirections = data.b; break;
3a1c8e42
RN
827 case PARAM_DDS:
828 if ( data.u >= MIN_ARROW_SIZE && data.u <= MAX_ARROW_SIZE )
829 vtl->drawdirections_size = data.u;
830 break;
b42a25ba
EB
831 case PARAM_SL: if ( data.u >= MIN_STOP_LENGTH && data.u <= MAX_STOP_LENGTH )
832 vtl->stop_length = data.u;
833 break;
834 case PARAM_EF: if ( data.u >= 1 && data.u <= 100 )
835 vtl->elevation_factor = data.u;
836 break;
50a14534
EB
837 case PARAM_LT: if ( data.u > 0 && data.u < 15 && data.u != vtl->line_thickness )
838 {
839 vtl->line_thickness = data.u;
840 trw_layer_new_track_gcs ( vtl, vp );
841 }
842 break;
f53c5b13 843 case PARAM_BLT: if ( data.u >= 0 && data.u <= 8 && data.u != vtl->bg_line_thickness )
50a14534
EB
844 {
845 vtl->bg_line_thickness = data.u;
846 trw_layer_new_track_gcs ( vtl, vp );
847 }
848 break;
50a14534 849 case PARAM_TBGC: gdk_gc_set_rgb_fg_color(vtl->track_bg_gc, &(data.c)); break;
18df6d42 850 case PARAM_TDSF: vtl->track_draw_speed_factor = data.d; break;
50a14534
EB
851 case PARAM_DLA: vtl->drawlabels = data.b; break;
852 case PARAM_DI: vtl->drawimages = data.b; break;
853 case PARAM_IS: if ( data.u != vtl->image_size )
854 {
855 vtl->image_size = data.u;
856 g_list_foreach ( vtl->image_cache->head, (GFunc) cached_pixbuf_free, NULL );
857 g_queue_free ( vtl->image_cache );
858 vtl->image_cache = g_queue_new ();
859 }
860 break;
861 case PARAM_IA: vtl->image_alpha = data.u; break;
862 case PARAM_ICS: vtl->image_cache_size = data.u;
863 while ( vtl->image_cache->length > vtl->image_cache_size ) /* if shrinking cache_size, free pixbuf ASAP */
864 cached_pixbuf_free ( g_queue_pop_tail ( vtl->image_cache ) );
865 break;
866 case PARAM_WPC: gdk_gc_set_rgb_fg_color(vtl->waypoint_gc, &(data.c)); break;
867 case PARAM_WPTC: gdk_gc_set_rgb_fg_color(vtl->waypoint_text_gc, &(data.c)); break;
868 case PARAM_WPBC: gdk_gc_set_rgb_fg_color(vtl->waypoint_bg_gc, &(data.c)); break;
869 case PARAM_WPBA: gdk_gc_set_function(vtl->waypoint_bg_gc, data.b ? GDK_AND : GDK_COPY ); break;
870 case PARAM_WPSYM: if ( data.u < WP_NUM_SYMBOLS ) vtl->wp_symbol = data.u; break;
871 case PARAM_WPSIZE: if ( data.u > 0 && data.u <= 64 ) vtl->wp_size = data.u; break;
ea3933fc 872 case PARAM_WPSYMS: vtl->wp_draw_symbols = data.b; break;
5a77ae71 873 case PARAM_WPFONTSIZE: if ( data.u < FS_NUM_SIZES ) vtl->wp_font_size = data.u; break;
50a14534
EB
874 }
875 return TRUE;
876}
877
158b3642 878static VikLayerParamData trw_layer_get_param ( VikTrwLayer *vtl, guint16 id, gboolean is_file_operation )
50a14534
EB
879{
880 VikLayerParamData rv;
881 switch ( id )
882 {
883 case PARAM_TV: rv.b = vtl->tracks_visible; break;
884 case PARAM_WV: rv.b = vtl->waypoints_visible; break;
885 case PARAM_DM: rv.u = vtl->drawmode; break;
886 case PARAM_DP: rv.b = vtl->drawpoints; break;
3a1c8e42 887 case PARAM_DPS: rv.u = vtl->drawpoints_size; break;
b42a25ba
EB
888 case PARAM_DE: rv.b = vtl->drawelevation; break;
889 case PARAM_EF: rv.u = vtl->elevation_factor; break;
890 case PARAM_DS: rv.b = vtl->drawstops; break;
891 case PARAM_SL: rv.u = vtl->stop_length; break;
50a14534 892 case PARAM_DL: rv.b = vtl->drawlines; break;
08084371 893 case PARAM_DD: rv.b = vtl->drawdirections; break;
3a1c8e42 894 case PARAM_DDS: rv.u = vtl->drawdirections_size; break;
50a14534
EB
895 case PARAM_LT: rv.u = vtl->line_thickness; break;
896 case PARAM_BLT: rv.u = vtl->bg_line_thickness; break;
50a14534
EB
897 case PARAM_DLA: rv.b = vtl->drawlabels; break;
898 case PARAM_DI: rv.b = vtl->drawimages; break;
899 case PARAM_TBGC: vik_gc_get_fg_color(vtl->track_bg_gc, &(rv.c)); break;
18df6d42 900 case PARAM_TDSF: rv.d = vtl->track_draw_speed_factor; break;
50a14534
EB
901 case PARAM_IS: rv.u = vtl->image_size; break;
902 case PARAM_IA: rv.u = vtl->image_alpha; break;
903 case PARAM_ICS: rv.u = vtl->image_cache_size; break;
904 case PARAM_WPC: vik_gc_get_fg_color(vtl->waypoint_gc, &(rv.c)); break;
905 case PARAM_WPTC: vik_gc_get_fg_color(vtl->waypoint_text_gc, &(rv.c)); break;
906 case PARAM_WPBC: vik_gc_get_fg_color(vtl->waypoint_bg_gc, &(rv.c)); break;
907 case PARAM_WPBA: rv.b = (vik_gc_get_function(vtl->waypoint_bg_gc)==GDK_AND); break;
908 case PARAM_WPSYM: rv.u = vtl->wp_symbol; break;
909 case PARAM_WPSIZE: rv.u = vtl->wp_size; break;
ea3933fc 910 case PARAM_WPSYMS: rv.b = vtl->wp_draw_symbols; break;
5a77ae71 911 case PARAM_WPFONTSIZE: rv.u = vtl->wp_font_size; break;
50a14534
EB
912 }
913 return rv;
914}
915
911400b5
AF
916static void trw_layer_marshall( VikTrwLayer *vtl, guint8 **data, gint *len )
917{
4efcd55c
RN
918 guint8 *pd;
919 gchar *dd;
920 gsize dl;
921 gint pl;
911400b5
AF
922 gchar *tmpname;
923 FILE *f;
924
925 *data = NULL;
926
927 if ((f = fdopen(g_file_open_tmp (NULL, &tmpname, NULL), "r+"))) {
208d2084 928 a_gpx_write_file(vtl, f, NULL);
911400b5
AF
929 vik_layer_marshall_params(VIK_LAYER(vtl), &pd, &pl);
930 fclose(f);
8c060406 931 f = NULL;
4efcd55c 932 g_file_get_contents(tmpname, &dd, &dl, NULL);
911400b5
AF
933 *len = sizeof(pl) + pl + dl;
934 *data = g_malloc(*len);
935 memcpy(*data, &pl, sizeof(pl));
936 memcpy(*data + sizeof(pl), pd, pl);
937 memcpy(*data + sizeof(pl) + pl, dd, dl);
938
939 g_free(pd);
940 g_free(dd);
8c060406 941 g_remove(tmpname);
911400b5
AF
942 g_free(tmpname);
943 }
944}
945
28612684 946static VikTrwLayer *trw_layer_unmarshall( guint8 *data, gint len, VikViewport *vvp )
911400b5
AF
947{
948 VikTrwLayer *rv = VIK_TRW_LAYER(vik_layer_create ( VIK_LAYER_TRW, vvp, NULL, FALSE ));
28612684 949 gint pl;
911400b5
AF
950 gchar *tmpname;
951 FILE *f;
952
953
954 memcpy(&pl, data, sizeof(pl));
955 data += sizeof(pl);
956 vik_layer_unmarshall_params ( VIK_LAYER(rv), data, pl, vvp );
957 data += pl;
958
959 if (!(f = fdopen(g_file_open_tmp (NULL, &tmpname, NULL), "r+"))) {
4258f4e2 960 g_critical("couldn't open temp file");
911400b5
AF
961 exit(1);
962 }
963 fwrite(data, len - pl - sizeof(pl), 1, f);
964 rewind(f);
965 a_gpx_read_file(rv, f);
966 fclose(f);
8c060406
MA
967 f = NULL;
968 g_remove(tmpname);
911400b5
AF
969 g_free(tmpname);
970 return rv;
971}
972
c9570f86
RN
973// Keep interesting hash function at least visible
974/*
8499a412
QT
975static guint strcase_hash(gconstpointer v)
976{
c9570f86 977 // 31 bit hash function
8499a412
QT
978 int i;
979 const gchar *t = v;
c9570f86 980 gchar s[128]; // malloc is too slow for reading big files
8499a412
QT
981 gchar *p = s;
982
983 for (i = 0; (i < (sizeof(s)- 1)) && t[i]; i++)
984 p[i] = toupper(t[i]);
985 p[i] = '\0';
986
987 p = s;
988 guint32 h = *p;
989 if (h) {
990 for (p += 1; *p != '\0'; p++)
991 h = (h << 5) - h + *p;
992 }
993
994 return h;
995}
c9570f86 996*/
8499a412 997
a7cd93ac 998static VikTrwLayer* trw_layer_new ( gint drawmode )
50a14534 999{
267b6db5 1000 if (trw_layer_params[PARAM_DM].widget_data == NULL)
753ec753 1001 trw_layer_params[PARAM_DM].widget_data = str_array_to_glist(params_drawmodes);
267b6db5 1002 if (trw_layer_params[PARAM_WPSYM].widget_data == NULL)
753ec753 1003 trw_layer_params[PARAM_WPSYM].widget_data = str_array_to_glist(params_wpsymbols);
267b6db5 1004
50a14534 1005 VikTrwLayer *rv = VIK_TRW_LAYER ( g_object_new ( VIK_TRW_LAYER_TYPE, NULL ) );
a0c65899 1006 vik_layer_set_type ( VIK_LAYER(rv), VIK_LAYER_TRW );
50a14534 1007
c9570f86
RN
1008 // It's not entirely clear the benefits of hash tables usage here - possibly the simplicity of first implementation for unique names
1009 // Now with the name of the item stored as part of the item - these tables are effectively straightforward lists
1010
1011 // For this reworking I've choosen to keep the use of hash tables since for the expected data sizes
1012 // - even many hundreds of waypoints and tracks is quite small in the grand scheme of things,
1013 // and with normal PC processing capabilities - it has negligibile performance impact
1014 // This also minimized the amount of rework - as the management of the hash tables already exists.
1015
1016 // The hash tables are indexed by simple integers acting as a UUID hash, which again shouldn't affect performance much
1017 // we have to maintain a uniqueness (as before when multiple names where not allowed),
1018 // this is to ensure it refers to the same item in the data structures used on the viewport and on the layers panel
1019
1020 rv->waypoints = g_hash_table_new_full ( g_direct_hash, g_direct_equal, NULL, (GDestroyNotify) vik_waypoint_free );
1021 rv->waypoints_iters = g_hash_table_new_full ( g_direct_hash, g_direct_equal, NULL, g_free );
ce4bd1cf
RN
1022 rv->tracks = g_hash_table_new_full ( g_direct_hash, g_direct_equal, NULL, (GDestroyNotify) vik_track_free );
1023 rv->tracks_iters = g_hash_table_new_full ( g_direct_hash, g_direct_equal, NULL, g_free );
50a14534
EB
1024
1025 /* TODO: constants at top */
1026 rv->waypoints_visible = rv->tracks_visible = TRUE;
1027 rv->drawmode = drawmode;
1028 rv->drawpoints = TRUE;
3a1c8e42
RN
1029 rv->drawpoints_size = MIN_POINT_SIZE;
1030 rv->drawdirections_size = 5;
b42a25ba
EB
1031 rv->drawstops = FALSE;
1032 rv->drawelevation = FALSE;
1033 rv->elevation_factor = 30;
1034 rv->stop_length = 60;
50a14534
EB
1035 rv->drawlines = TRUE;
1036 rv->wplabellayout = NULL;
1037 rv->wp_right_click_menu = NULL;
8bd81489 1038 rv->track_right_click_menu = NULL;
50a14534
EB
1039 rv->waypoint_gc = NULL;
1040 rv->waypoint_text_gc = NULL;
1041 rv->waypoint_bg_gc = NULL;
1042 rv->track_gc = NULL;
18df6d42 1043 rv->track_draw_speed_factor = 30.0;
50a14534 1044 rv->line_thickness = 1;
b42a25ba 1045 rv->bg_line_thickness = 0;
50a14534 1046 rv->current_wp = NULL;
c9570f86 1047 rv->current_wp_id = NULL;
50a14534
EB
1048 rv->current_track = NULL;
1049 rv->current_tpl = NULL;
ce4bd1cf
RN
1050 rv->current_tp_track = NULL;
1051 rv->current_tp_id = NULL;
50a14534
EB
1052 rv->moving_tp = FALSE;
1053 rv->moving_wp = FALSE;
1eef1bde 1054
745fda83 1055 rv->draw_sync_done = TRUE;
ef5e8132 1056 rv->draw_sync_do = TRUE;
7b203521 1057
7ff7d728
RN
1058 rv->route_finder_started = FALSE;
1059 rv->route_finder_check_added_track = FALSE;
7ff7d728
RN
1060 rv->route_finder_current_track = NULL;
1061 rv->route_finder_append = FALSE;
1eef1bde 1062
50a14534 1063 rv->waypoint_rightclick = FALSE;
50a14534
EB
1064 rv->tpwin = NULL;
1065 rv->image_cache = g_queue_new();
1066 rv->image_size = 64;
1067 rv->image_alpha = 255;
1068 rv->image_cache_size = 300;
1069 rv->drawimages = TRUE;
1070 rv->drawlabels = TRUE;
1071 return rv;
1072}
1073
1074
a7cd93ac 1075static void trw_layer_free ( VikTrwLayer *trwlayer )
50a14534
EB
1076{
1077 g_hash_table_destroy(trwlayer->waypoints);
1078 g_hash_table_destroy(trwlayer->tracks);
1079
1080 /* ODC: replace with GArray */
1081 trw_layer_free_track_gcs ( trwlayer );
1082
1083 if ( trwlayer->wp_right_click_menu )
4f14a010 1084 g_object_ref_sink ( G_OBJECT(trwlayer->wp_right_click_menu) );
50a14534 1085
8bd81489
RN
1086 if ( trwlayer->track_right_click_menu )
1087 gtk_object_sink ( GTK_OBJECT(trwlayer->track_right_click_menu) );
1088
50a14534
EB
1089 if ( trwlayer->wplabellayout != NULL)
1090 g_object_unref ( G_OBJECT ( trwlayer->wplabellayout ) );
1091
1092 if ( trwlayer->waypoint_gc != NULL )
1093 g_object_unref ( G_OBJECT ( trwlayer->waypoint_gc ) );
1094
1095 if ( trwlayer->waypoint_text_gc != NULL )
1096 g_object_unref ( G_OBJECT ( trwlayer->waypoint_text_gc ) );
1097
1098 if ( trwlayer->waypoint_bg_gc != NULL )
1099 g_object_unref ( G_OBJECT ( trwlayer->waypoint_bg_gc ) );
1100
50a14534
EB
1101 if ( trwlayer->tpwin != NULL )
1102 gtk_widget_destroy ( GTK_WIDGET(trwlayer->tpwin) );
1103
1104 g_list_foreach ( trwlayer->image_cache->head, (GFunc) cached_pixbuf_free, NULL );
1105 g_queue_free ( trwlayer->image_cache );
1106}
1107
08084371 1108static void init_drawing_params ( struct DrawingParams *dp, VikTrwLayer *vtl, VikViewport *vp )
50a14534 1109{
08084371 1110 dp->vtl = vtl;
50a14534
EB
1111 dp->vp = vp;
1112 dp->xmpp = vik_viewport_get_xmpp ( vp );
1113 dp->ympp = vik_viewport_get_ympp ( vp );
1114 dp->width = vik_viewport_get_width ( vp );
1115 dp->height = vik_viewport_get_height ( vp );
3a1c8e42
RN
1116 dp->cc = vtl->drawdirections_size*cos(45 * DEG2RAD); // Calculate once per vtl update - even if not used
1117 dp->ss = vtl->drawdirections_size*sin(45 * DEG2RAD); // Calculate once per vtl update - even if not used
08084371 1118
50a14534
EB
1119 dp->center = vik_viewport_get_center ( vp );
1120 dp->one_zone = vik_viewport_is_one_zone ( vp ); /* false if some other projection besides UTM */
1121 dp->lat_lon = vik_viewport_get_coord_mode ( vp ) == VIK_COORD_LATLON;
1122
1123 if ( dp->one_zone )
1124 {
1125 gint w2, h2;
1126 w2 = dp->xmpp * (dp->width / 2) + 1600 / dp->xmpp;
1127 h2 = dp->ympp * (dp->height / 2) + 1600 / dp->ympp;
1128 /* leniency -- for tracks. Obviously for waypoints this SHOULD be a lot smaller */
1129
1130 dp->ce1 = dp->center->east_west-w2;
1131 dp->ce2 = dp->center->east_west+w2;
1132 dp->cn1 = dp->center->north_south-h2;
1133 dp->cn2 = dp->center->north_south+h2;
1134 } else if ( dp->lat_lon ) {
1135 VikCoord upperleft, bottomright;
1136 /* quick & dirty calculation; really want to check all corners due to lat/lon smaller at top in northern hemisphere */
1137 /* this also DOESN'T WORK if you are crossing 180/-180 lon. I don't plan to in the near future... */
1138 vik_viewport_screen_to_coord ( vp, -500, -500, &upperleft );
1139 vik_viewport_screen_to_coord ( vp, dp->width+500, dp->height+500, &bottomright );
1140 dp->ce1 = upperleft.east_west;
1141 dp->ce2 = bottomright.east_west;
1142 dp->cn1 = bottomright.north_south;
1143 dp->cn2 = upperleft.north_south;
1144 }
1145
1146 dp->track_gc_iter = 0;
1147}
1148
18df6d42
RN
1149/*
1150 * Determine the colour of the trackpoint (and/or trackline) relative to the average speed
1151 * Here a simple traffic like light colour system is used:
1152 * . slow points are red
1153 * . average is yellow
1154 * . fast points are green
1155 */
1156static gint track_section_colour_by_speed ( VikTrwLayer *vtl, VikTrackpoint *tp1, VikTrackpoint *tp2, gdouble average_speed, gdouble low_speed, gdouble high_speed )
50a14534 1157{
18df6d42
RN
1158 gdouble rv = 0;
1159 if ( tp1->has_timestamp && tp2->has_timestamp ) {
1160 if ( average_speed > 0 ) {
1161 rv = ( vik_coord_diff ( &(tp1->coord), &(tp2->coord) ) / (tp1->timestamp - tp2->timestamp) );
1162 if ( rv < low_speed )
1163 return VIK_TRW_LAYER_TRACK_GC_SLOW;
1164 else if ( rv > high_speed )
1165 return VIK_TRW_LAYER_TRACK_GC_FAST;
1166 else
1167 return VIK_TRW_LAYER_TRACK_GC_AVER;
1168 }
1169 }
1170 return VIK_TRW_LAYER_TRACK_GC_BLACK;
50a14534
EB
1171}
1172
5b309036 1173static void draw_utm_skip_insignia ( VikViewport *vvp, GdkGC *gc, gint x, gint y )
50a14534
EB
1174{
1175 vik_viewport_draw_line ( vvp, gc, x+5, y, x-5, y );
1176 vik_viewport_draw_line ( vvp, gc, x, y+5, x, y-5 );
1177 vik_viewport_draw_line ( vvp, gc, x+5, y+5, x-5, y-5 );
1178 vik_viewport_draw_line ( vvp, gc, x+5, y-5, x-5, y+5 );
1179}
1180
0c9b1cf7 1181static void trw_layer_draw_track ( const gchar *name, VikTrack *track, struct DrawingParams *dp, gboolean draw_track_outline )
50a14534
EB
1182{
1183 /* TODO: this function is a mess, get rid of any redundancy */
1184 GList *list = track->trackpoints;
8e9c992d 1185 GdkGC *main_gc;
50a14534
EB
1186 gboolean useoldvals = TRUE;
1187
1188 gboolean drawpoints;
b42a25ba
EB
1189 gboolean drawstops;
1190 gboolean drawelevation;
941aa6e9 1191 gdouble min_alt, max_alt, alt_diff = 0;
50a14534 1192
3a1c8e42
RN
1193 const guint8 tp_size_reg = dp->vtl->drawpoints_size;
1194 const guint8 tp_size_cur = dp->vtl->drawpoints_size*2;
50a14534
EB
1195 guint8 tp_size;
1196
b42a25ba
EB
1197 if ( dp->vtl->drawelevation )
1198 {
1199 /* assume if it has elevation at the beginning, it has it throughout. not ness a true good assumption */
1200 if ( ( drawelevation = vik_track_get_minmax_alt ( track, &min_alt, &max_alt ) ) )
1201 alt_diff = max_alt - min_alt;
1202 }
1203
50a14534
EB
1204 if ( ! track->visible )
1205 return;
1206
1207 /* admittedly this is not an efficient way to do it because we go through the whole GC thing all over... */
0c9b1cf7 1208 if ( dp->vtl->bg_line_thickness && !draw_track_outline )
50a14534
EB
1209 trw_layer_draw_track ( name, track, dp, TRUE );
1210
0c9b1cf7 1211 if ( draw_track_outline )
b42a25ba
EB
1212 drawpoints = drawstops = FALSE;
1213 else {
50a14534 1214 drawpoints = dp->vtl->drawpoints;
b42a25ba
EB
1215 drawstops = dp->vtl->drawstops;
1216 }
50a14534 1217
18df6d42 1218 gboolean drawing_highlight = FALSE;
a5dcfdb7 1219 /* Current track - used for creation */
8e9c992d
EB
1220 if ( track == dp->vtl->current_track )
1221 main_gc = dp->vtl->current_track_gc;
a5dcfdb7
RN
1222 else {
1223 if ( vik_viewport_get_draw_highlight ( dp->vp ) ) {
1224 /* Draw all tracks of the layer in special colour */
1225 /* if track is member of selected layer or is the current selected track
1226 then draw in the highlight colour.
1227 NB this supercedes the drawmode */
1228 if ( dp->vtl && ( ( dp->vtl == vik_window_get_selected_trw_layer ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(dp->vtl) ) ) ||
1229 ( dp->vtl->tracks == vik_window_get_selected_tracks ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(dp->vtl) ) ) ||
1230 track == vik_window_get_selected_track ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(dp->vtl) ) ) ) {
480fb7e1 1231 main_gc = vik_viewport_get_gc_highlight (dp->vp);
18df6d42 1232 drawing_highlight = TRUE;
a5dcfdb7
RN
1233 }
1234 else {
1235 if ( dp->vtl->drawmode == DRAWMODE_ALL_BLACK )
1236 dp->track_gc_iter = VIK_TRW_LAYER_TRACK_GC_BLACK;
1237
1238 main_gc = g_array_index(dp->vtl->track_gc, GdkGC *, dp->track_gc_iter);
1239 }
1240 }
1241 else {
1242 if ( dp->vtl->drawmode == DRAWMODE_ALL_BLACK )
1243 dp->track_gc_iter = VIK_TRW_LAYER_TRACK_GC_BLACK;
1244
1245 main_gc = g_array_index(dp->vtl->track_gc, GdkGC *, dp->track_gc_iter);
1246 }
1247 }
8e9c992d 1248
50a14534
EB
1249 if (list) {
1250 int x, y, oldx, oldy;
1251 VikTrackpoint *tp = VIK_TRACKPOINT(list->data);
1252
1253 tp_size = (list == dp->vtl->current_tpl) ? tp_size_cur : tp_size_reg;
1254
1255 vik_viewport_coord_to_screen ( dp->vp, &(tp->coord), &x, &y );
1256
1257 if ( (drawpoints) && dp->track_gc_iter < VIK_TRW_LAYER_TRACK_GC )
1258 {
1259 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 1260 vik_viewport_draw_polygon ( dp->vp, main_gc, TRUE, trian, 3 );
50a14534
EB
1261 }
1262
1263 oldx = x;
1264 oldy = y;
1265
18df6d42
RN
1266 gdouble average_speed = 0.0;
1267 gdouble low_speed = 0.0;
1268 gdouble high_speed = 0.0;
1269 // If necessary calculate these values - which is done only once per track redraw
1270 if ( dp->vtl->drawmode == DRAWMODE_BY_SPEED ) {
1271 // the percentage factor away from the average speed determines transistions between the levels
1272 average_speed = vik_track_get_average_speed_moving(track, dp->vtl->stop_length);
1273 low_speed = average_speed - (average_speed*(dp->vtl->track_draw_speed_factor/100.0));
1274 high_speed = average_speed + (average_speed*(dp->vtl->track_draw_speed_factor/100.0));
1275 }
1276
50a14534
EB
1277 while ((list = g_list_next(list)))
1278 {
1279 tp = VIK_TRACKPOINT(list->data);
1280 tp_size = (list == dp->vtl->current_tpl) ? tp_size_cur : tp_size_reg;
1281
1282 /* check some stuff -- but only if we're in UTM and there's only ONE ZONE; or lat lon */
1283 if ( (!dp->one_zone && !dp->lat_lon) || /* UTM & zones; do everything */
1284 ( ((!dp->one_zone) || tp->coord.utm_zone == dp->center->utm_zone) && /* only check zones if UTM & one_zone */
1285 tp->coord.east_west < dp->ce2 && tp->coord.east_west > dp->ce1 && /* both UTM and lat lon */
1286 tp->coord.north_south > dp->cn1 && tp->coord.north_south < dp->cn2 ) )
1287 {
1288 vik_viewport_coord_to_screen ( dp->vp, &(tp->coord), &x, &y );
1289
adc271a8
GT
1290 /*
1291 * If points are the same in display coordinates, don't draw.
1292 */
1293 if ( useoldvals && x == oldx && y == oldy )
b827c5e0
RN
1294 {
1295 // Still need to process points to ensure 'stops' are drawn if required
0c9b1cf7 1296 if ( drawstops && drawpoints && ! draw_track_outline && list->next &&
b827c5e0
RN
1297 (VIK_TRACKPOINT(list->next->data)->timestamp - VIK_TRACKPOINT(list->data)->timestamp > dp->vtl->stop_length) )
1298 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 );
1299
adc271a8 1300 goto skip;
b827c5e0 1301 }
adc271a8 1302
18df6d42
RN
1303 VikTrackpoint *tp2 = VIK_TRACKPOINT(list->prev->data);
1304 if ( drawpoints || dp->vtl->drawlines ) {
1305 // setup main_gc for both point and line drawing
1306 if ( !drawing_highlight && (dp->vtl->drawmode == DRAWMODE_BY_SPEED) ) {
1307 dp->track_gc_iter = track_section_colour_by_speed ( dp->vtl, tp, tp2, average_speed, low_speed, high_speed );
1308 main_gc = g_array_index(dp->vtl->track_gc, GdkGC *, dp->track_gc_iter);
1309 }
1310 }
1311
0c9b1cf7 1312 if ( drawpoints && ! draw_track_outline )
50a14534 1313 {
18df6d42 1314
50a14534 1315 if ( list->next ) {
4511d71f 1316 /*
f7eaf554 1317 * The concept of drawing stops is that a trackpoint
4511d71f
GT
1318 * that is if the next trackpoint has a timestamp far into
1319 * the future, we draw a circle of 6x trackpoint size,
f7eaf554
RN
1320 * instead of a rectangle of 2x trackpoint size.
1321 * This is drawn first so the trackpoint will be drawn on top
4511d71f 1322 */
50a14534 1323 /* stops */
b42a25ba 1324 if ( drawstops && VIK_TRACKPOINT(list->next->data)->timestamp - VIK_TRACKPOINT(list->data)->timestamp > dp->vtl->stop_length )
4511d71f 1325 /* Stop point. Draw 6x circle. */
50a14534 1326 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 );
f7eaf554
RN
1327
1328 /* Regular point - draw 2x square. */
1329 vik_viewport_draw_rectangle ( dp->vp, main_gc, TRUE, x-tp_size, y-tp_size, 2*tp_size, 2*tp_size );
50a14534
EB
1330 }
1331 else
4511d71f 1332 /* Final point - draw 4x circle. */
8e9c992d 1333 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
1334 }
1335
1336 if ((!tp->newsegment) && (dp->vtl->drawlines))
1337 {
50a14534
EB
1338
1339 /* UTM only: zone check */
1340 if ( drawpoints && dp->vtl->coord_mode == VIK_COORD_UTM && tp->coord.utm_zone != dp->center->utm_zone )
8e9c992d 1341 draw_utm_skip_insignia ( dp->vp, main_gc, x, y);
50a14534 1342
50a14534
EB
1343 if (!useoldvals)
1344 vik_viewport_coord_to_screen ( dp->vp, &(tp2->coord), &oldx, &oldy );
1345
0c9b1cf7 1346 if ( draw_track_outline ) {
50a14534 1347 vik_viewport_draw_line ( dp->vp, dp->vtl->track_bg_gc, oldx, oldy, x, y);
8c4f1350
EB
1348 }
1349 else {
8c4f1350 1350
8e9c992d 1351 vik_viewport_draw_line ( dp->vp, main_gc, oldx, oldy, x, y);
08084371 1352
3b9970f8 1353 if ( dp->vtl->drawelevation && list->next && VIK_TRACKPOINT(list->next->data)->altitude != VIK_DEFAULT_ALTITUDE ) {
b42a25ba
EB
1354 GdkPoint tmp[4];
1355 #define FIXALTITUDE(what) ((VIK_TRACKPOINT((what))->altitude-min_alt)/alt_diff*DRAW_ELEVATION_FACTOR*dp->vtl->elevation_factor/dp->xmpp)
3b9970f8
RN
1356
1357 tmp[0].x = oldx;
1358 tmp[0].y = oldy;
1359 tmp[1].x = oldx;
1360 tmp[1].y = oldy-FIXALTITUDE(list->data);
1361 tmp[2].x = x;
1362 tmp[2].y = y-FIXALTITUDE(list->next->data);
1363 tmp[3].x = x;
1364 tmp[3].y = y;
1365
1366 GdkGC *tmp_gc;
1367 if ( ((oldx - x) > 0 && (oldy - y) > 0) || ((oldx - x) < 0 && (oldy - y) < 0))
1368 tmp_gc = GTK_WIDGET(dp->vp)->style->light_gc[3];
1369 else
1370 tmp_gc = GTK_WIDGET(dp->vp)->style->dark_gc[0];
1371 vik_viewport_draw_polygon ( dp->vp, tmp_gc, TRUE, tmp, 4);
1372
8e9c992d 1373 vik_viewport_draw_line ( dp->vp, main_gc, oldx, oldy-FIXALTITUDE(list->data), x, y-FIXALTITUDE(list->next->data));
8c4f1350
EB
1374 }
1375 }
50a14534
EB
1376 }
1377
08084371
RN
1378 if ( (!tp->newsegment) && dp->vtl->drawdirections ) {
1379 // Draw an arrow at the mid point to show the direction of the track
1380 // Code is a rework from vikwindow::draw_ruler()
1381 gint midx = (oldx + x) / 2;
1382 gint midy = (oldy + y) / 2;
1383
1384 gdouble len = sqrt ( ((midx-oldx) * (midx-oldx)) + ((midy-oldy) * (midy-oldy)) );
1385 // Avoid divide by zero and ensure at least 1 pixel big
1386 if ( len > 1 ) {
1387 gdouble dx = (oldx - midx) / len;
1388 gdouble dy = (oldy - midy) / len;
1389 vik_viewport_draw_line ( dp->vp, main_gc, midx, midy, midx + (dx * dp->cc + dy * dp->ss), midy + (dy * dp->cc - dx * dp->ss) );
1390 vik_viewport_draw_line ( dp->vp, main_gc, midx, midy, midx + (dx * dp->cc - dy * dp->ss), midy + (dy * dp->cc + dx * dp->ss) );
1391 }
1392 }
1393
adc271a8 1394 skip:
50a14534
EB
1395 oldx = x;
1396 oldy = y;
1397 useoldvals = TRUE;
1398 }
1399 else {
1400 if (useoldvals && dp->vtl->drawlines && (!tp->newsegment))
1401 {
1402 VikTrackpoint *tp2 = VIK_TRACKPOINT(list->prev->data);
1403 if ( dp->vtl->coord_mode != VIK_COORD_UTM || tp->coord.utm_zone == dp->center->utm_zone )
1404 {
1405 vik_viewport_coord_to_screen ( dp->vp, &(tp->coord), &x, &y );
18df6d42
RN
1406
1407 if ( !drawing_highlight && (dp->vtl->drawmode == DRAWMODE_BY_SPEED) ) {
1408 dp->track_gc_iter = track_section_colour_by_speed ( dp->vtl, tp, tp2, average_speed, low_speed, high_speed );
f66c1d0c 1409 main_gc = g_array_index(dp->vtl->track_gc, GdkGC *, dp->track_gc_iter);
18df6d42 1410 }
50a14534 1411
adc271a8
GT
1412 /*
1413 * If points are the same in display coordinates, don't draw.
1414 */
1415 if ( x != oldx || y != oldy )
1416 {
0c9b1cf7 1417 if ( draw_track_outline )
adc271a8
GT
1418 vik_viewport_draw_line ( dp->vp, dp->vtl->track_bg_gc, oldx, oldy, x, y);
1419 else
1420 vik_viewport_draw_line ( dp->vp, main_gc, oldx, oldy, x, y);
1421 }
50a14534
EB
1422 }
1423 else
1424 {
adc271a8
GT
1425 /*
1426 * If points are the same in display coordinates, don't draw.
1427 */
1428 if ( x != oldx && y != oldy )
1429 {
1430 vik_viewport_coord_to_screen ( dp->vp, &(tp2->coord), &x, &y );
1431 draw_utm_skip_insignia ( dp->vp, main_gc, x, y );
1432 }
50a14534
EB
1433 }
1434 }
1435 useoldvals = FALSE;
1436 }
1437 }
1438 }
1439 if ( dp->vtl->drawmode == DRAWMODE_BY_TRACK )
18df6d42 1440 if ( ++(dp->track_gc_iter) >= VIK_TRW_LAYER_TRACK_GC_MAX )
50a14534
EB
1441 dp->track_gc_iter = 0;
1442}
1443
1444/* the only reason this exists is so that trw_layer_draw_track can first call itself to draw the white track background */
1445static void trw_layer_draw_track_cb ( const gchar *name, VikTrack *track, struct DrawingParams *dp )
1446{
1447 trw_layer_draw_track ( name, track, dp, FALSE );
1448}
1449
1450static void cached_pixbuf_free ( CachedPixbuf *cp )
1451{
1452 g_object_unref ( G_OBJECT(cp->pixbuf) );
1453 g_free ( cp->image );
1454}
1455
1456static gint cached_pixbuf_cmp ( CachedPixbuf *cp, const gchar *name )
1457{
1458 return strcmp ( cp->image, name );
1459}
1460
1461static void trw_layer_draw_waypoint ( const gchar *name, VikWaypoint *wp, struct DrawingParams *dp )
1462{
1463 if ( wp->visible )
51f0884d 1464 if ( (!dp->one_zone && !dp->lat_lon) || ( ( dp->lat_lon || wp->coord.utm_zone == dp->center->utm_zone ) &&
50a14534
EB
1465 wp->coord.east_west < dp->ce2 && wp->coord.east_west > dp->ce1 &&
1466 wp->coord.north_south > dp->cn1 && wp->coord.north_south < dp->cn2 ) )
1467 {
1468 gint x, y;
a7f9c01e 1469 GdkPixbuf *sym = NULL;
50a14534
EB
1470 vik_viewport_coord_to_screen ( dp->vp, &(wp->coord), &x, &y );
1471
1472 /* if in shrunken_cache, get that. If not, get and add to shrunken_cache */
1473
1474 if ( wp->image && dp->vtl->drawimages )
1475 {
1476 GdkPixbuf *pixbuf = NULL;
1477 GList *l;
1478
1479 if ( dp->vtl->image_alpha == 0)
1480 return;
1481
1482 l = g_list_find_custom ( dp->vtl->image_cache->head, wp->image, (GCompareFunc) cached_pixbuf_cmp );
1483 if ( l )
1484 pixbuf = ((CachedPixbuf *) l->data)->pixbuf;
1485 else
1486 {
1487 gchar *image = wp->image;
1488 GdkPixbuf *regularthumb = a_thumbnails_get ( wp->image );
1489 if ( ! regularthumb )
1490 {
1491 regularthumb = a_thumbnails_get_default (); /* cache one 'not yet loaded' for all thumbs not loaded */
1492 image = "\x12\x00"; /* this shouldn't occur naturally. */
1493 }
1494 if ( regularthumb )
1495 {
1496 CachedPixbuf *cp = NULL;
1497 cp = g_malloc ( sizeof ( CachedPixbuf ) );
1498 if ( dp->vtl->image_size == 128 )
1499 cp->pixbuf = regularthumb;
1500 else
1501 {
1502 cp->pixbuf = a_thumbnails_scale_pixbuf(regularthumb, dp->vtl->image_size, dp->vtl->image_size);
1503 g_assert ( cp->pixbuf );
1504 g_object_unref ( G_OBJECT(regularthumb) );
1505 }
1506 cp->image = g_strdup ( image );
1507
1508 /* needed so 'click picture' tool knows how big the pic is; we don't
1509 * store it in cp because they may have been freed already. */
1510 wp->image_width = gdk_pixbuf_get_width ( cp->pixbuf );
1511 wp->image_height = gdk_pixbuf_get_height ( cp->pixbuf );
1512
1513 g_queue_push_head ( dp->vtl->image_cache, cp );
1514 if ( dp->vtl->image_cache->length > dp->vtl->image_cache_size )
1515 cached_pixbuf_free ( g_queue_pop_tail ( dp->vtl->image_cache ) );
1516
1517 pixbuf = cp->pixbuf;
1518 }
1519 else
1520 {
1521 pixbuf = a_thumbnails_get_default (); /* thumbnail not yet loaded */
1522 }
1523 }
1524 if ( pixbuf )
1525 {
1526 gint w, h;
1527 w = gdk_pixbuf_get_width ( pixbuf );
1528 h = gdk_pixbuf_get_height ( pixbuf );
1529
1530 if ( x+(w/2) > 0 && y+(h/2) > 0 && x-(w/2) < dp->width && y-(h/2) < dp->height ) /* always draw within boundaries */
1531 {
a5dcfdb7
RN
1532 if ( vik_viewport_get_draw_highlight ( dp->vp ) ) {
1533 if ( dp->vtl == vik_window_get_selected_trw_layer ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(dp->vtl) ) ||
1534 dp->vtl->waypoints == vik_window_get_selected_waypoints ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(dp->vtl) ) ||
1535 wp == vik_window_get_selected_waypoint ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(dp->vtl) ) ) {
1536 // Highlighted - so draw a little border around the chosen one
1537 // single line seems a little weak so draw 2 of them
480fb7e1 1538 vik_viewport_draw_rectangle (dp->vp, vik_viewport_get_gc_highlight (dp->vp), FALSE,
a5dcfdb7 1539 x - (w/2) - 1, y - (h/2) - 1, w + 2, h + 2 );
480fb7e1 1540 vik_viewport_draw_rectangle (dp->vp, vik_viewport_get_gc_highlight (dp->vp), FALSE,
a5dcfdb7
RN
1541 x - (w/2) - 2, y - (h/2) - 2, w + 4, h + 4 );
1542 }
1543 }
50a14534
EB
1544 if ( dp->vtl->image_alpha == 255 )
1545 vik_viewport_draw_pixbuf ( dp->vp, pixbuf, 0, 0, x - (w/2), y - (h/2), w, h );
1546 else
1547 vik_viewport_draw_pixbuf_with_alpha ( dp->vp, pixbuf, dp->vtl->image_alpha, 0, 0, x - (w/2), y - (h/2), w, h );
1548 }
1549 return; /* if failed to draw picture, default to drawing regular waypoint (below) */
1550 }
1551 }
1552
1553 /* DRAW ACTUAL DOT */
ea3933fc 1554 if ( dp->vtl->wp_draw_symbols && wp->symbol && (sym = a_get_wp_sym(wp->symbol)) ) {
acaf7113
AF
1555 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 );
1556 }
1557 else if ( wp == dp->vtl->current_wp ) {
50a14534
EB
1558 switch ( dp->vtl->wp_symbol ) {
1559 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;
1560 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;
1561 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;
1562 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 );
1563 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 );
1564 }
1565 }
1566 else {
1567 switch ( dp->vtl->wp_symbol ) {
1568 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;
1569 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;
1570 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;
1571 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 );
1572 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;
1573 }
1574 }
1575
1576 if ( dp->vtl->drawlabels )
1577 {
1578 /* thanks to the GPSDrive people (Fritz Ganter et al.) for hints on this part ... yah, I'm too lazy to study documentation */
a7f9c01e 1579 gint label_x, label_y;
50a14534 1580 gint width, height;
5a77ae71
RN
1581 // Hopefully name won't break the markup (may need to sanitize - g_markup_escape_text())
1582
1583 // Could this stored in the waypoint rather than recreating each pass?
1584 gchar *fsize = NULL;
1585 switch (dp->vtl->wp_font_size) {
1586 case FS_XX_SMALL: fsize = g_strdup ( "xx-small" ); break;
1587 case FS_X_SMALL: fsize = g_strdup ( "x-small" ); break;
1588 case FS_SMALL: fsize = g_strdup ( "small" ); break;
1589 case FS_LARGE: fsize = g_strdup ( "large" ); break;
1590 case FS_X_LARGE: fsize = g_strdup ( "x-large" ); break;
1591 case FS_XX_LARGE: fsize = g_strdup ( "xx-large" ); break;
1592 default: fsize = g_strdup ( "medium" ); break;
1593 }
1594
1595 gchar *wp_label_markup = g_strdup_printf ( "<span size=\"%s\">%s</span>", fsize, wp->name );
1596
1597 if ( pango_parse_markup ( wp_label_markup, -1, 0, NULL, NULL, NULL, NULL ) )
1598 pango_layout_set_markup ( dp->vtl->wplabellayout, wp_label_markup, -1 );
1599 else
1600 // Fallback if parse failure
1601 pango_layout_set_text ( dp->vtl->wplabellayout, wp->name, -1 );
1602
1603 g_free ( wp_label_markup );
1604 g_free ( fsize );
1605
50a14534 1606 pango_layout_get_pixel_size ( dp->vtl->wplabellayout, &width, &height );
a7f9c01e
QT
1607 label_x = x - width/2;
1608 if (sym)
1609 label_y = y - height - 2 - gdk_pixbuf_get_height(sym)/2;
1610 else
1611 label_y = y - dp->vtl->wp_size - height - 2;
1612
480fb7e1 1613 /* if highlight mode on, then draw background text in highlight colour */
a5dcfdb7
RN
1614 if ( vik_viewport_get_draw_highlight ( dp->vp ) ) {
1615 if ( dp->vtl == vik_window_get_selected_trw_layer ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(dp->vtl) ) ||
1616 dp->vtl->waypoints == vik_window_get_selected_waypoints ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(dp->vtl) ) ||
1617 wp == vik_window_get_selected_waypoint ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(dp->vtl) ) )
480fb7e1 1618 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
1619 else
1620 vik_viewport_draw_rectangle ( dp->vp, dp->vtl->waypoint_bg_gc, TRUE, label_x - 1, label_y-1,width+2,height+2);
1621 }
1622 else {
1623 vik_viewport_draw_rectangle ( dp->vp, dp->vtl->waypoint_bg_gc, TRUE, label_x - 1, label_y-1,width+2,height+2);
1624 }
a7f9c01e 1625 vik_viewport_draw_layout ( dp->vp, dp->vtl->waypoint_text_gc, label_x, label_y, dp->vtl->wplabellayout );
50a14534
EB
1626 }
1627 }
1628}
1629
a7cd93ac 1630static void trw_layer_draw ( VikTrwLayer *l, gpointer data )
50a14534
EB
1631{
1632 static struct DrawingParams dp;
1633 g_assert ( l != NULL );
1634
08084371 1635 init_drawing_params ( &dp, l, VIK_VIEWPORT(data) );
50a14534
EB
1636
1637 if ( l->tracks_visible )
1638 g_hash_table_foreach ( l->tracks, (GHFunc) trw_layer_draw_track_cb, &dp );
1639
1640 if (l->waypoints_visible)
1641 g_hash_table_foreach ( l->waypoints, (GHFunc) trw_layer_draw_waypoint, &dp );
1642}
1643
1644static void trw_layer_free_track_gcs ( VikTrwLayer *vtl )
1645{
1646 int i;
1647 if ( vtl->track_bg_gc )
1648 {
1649 g_object_unref ( vtl->track_bg_gc );
1650 vtl->track_bg_gc = NULL;
1651 }
8e9c992d
EB
1652 if ( vtl->current_track_gc )
1653 {
1654 g_object_unref ( vtl->current_track_gc );
1655 vtl->current_track_gc = NULL;
1656 }
745fda83
RN
1657 if ( vtl->current_track_newpoint_gc )
1658 {
1659 g_object_unref ( vtl->current_track_newpoint_gc );
1660 vtl->current_track_newpoint_gc = NULL;
1661 }
50a14534
EB
1662
1663 if ( ! vtl->track_gc )
1664 return;
1665 for ( i = vtl->track_gc->len - 1; i >= 0; i-- )
1666 g_object_unref ( g_array_index ( vtl->track_gc, GObject *, i ) );
1667 g_array_free ( vtl->track_gc, TRUE );
1668 vtl->track_gc = NULL;
1669}
1670
1671static void trw_layer_new_track_gcs ( VikTrwLayer *vtl, VikViewport *vp )
1672{
1673 GdkGC *gc[ VIK_TRW_LAYER_TRACK_GC ];
1674 gint width = vtl->line_thickness;
1675
1676 if ( vtl->track_gc )
1677 trw_layer_free_track_gcs ( vtl );
1678
1679 if ( vtl->track_bg_gc )
1680 g_object_unref ( vtl->track_bg_gc );
1681 vtl->track_bg_gc = vik_viewport_new_gc ( vp, "#FFFFFF", width + vtl->bg_line_thickness );
1682
98b7eddc
RN
1683 // Ensure new track drawing heeds line thickness setting
1684 // however always have a minium of 2, as 1 pixel is really narrow
1685 gint new_track_width = (vtl->line_thickness < 2) ? 2 : vtl->line_thickness;
1686
8e9c992d
EB
1687 if ( vtl->current_track_gc )
1688 g_object_unref ( vtl->current_track_gc );
98b7eddc
RN
1689 vtl->current_track_gc = vik_viewport_new_gc ( vp, "#FF0000", new_track_width );
1690 gdk_gc_set_line_attributes ( vtl->current_track_gc, new_track_width, GDK_LINE_ON_OFF_DASH, GDK_CAP_ROUND, GDK_JOIN_ROUND );
8e9c992d 1691
745fda83
RN
1692 // 'newpoint' gc is exactly the same as the current track gc
1693 if ( vtl->current_track_newpoint_gc )
1694 g_object_unref ( vtl->current_track_newpoint_gc );
98b7eddc
RN
1695 vtl->current_track_newpoint_gc = vik_viewport_new_gc ( vp, "#FF0000", new_track_width );
1696 gdk_gc_set_line_attributes ( vtl->current_track_newpoint_gc, new_track_width, GDK_LINE_ON_OFF_DASH, GDK_CAP_ROUND, GDK_JOIN_ROUND );
745fda83 1697
50a14534
EB
1698 vtl->track_gc = g_array_sized_new ( FALSE, FALSE, sizeof ( GdkGC * ), VIK_TRW_LAYER_TRACK_GC );
1699
1700 gc[0] = vik_viewport_new_gc ( vp, "#2d870a", width ); /* below range */
1701
1702 gc[1] = vik_viewport_new_gc ( vp, "#0a8742", width );
1703 gc[2] = vik_viewport_new_gc ( vp, "#0a8783", width );
1704 gc[3] = vik_viewport_new_gc ( vp, "#0a4d87", width );
1705 gc[4] = vik_viewport_new_gc ( vp, "#05469f", width );
1706 gc[5] = vik_viewport_new_gc ( vp, "#1b059f", width );
1707 gc[6] = vik_viewport_new_gc ( vp, "#2d059f", width );
1708 gc[7] = vik_viewport_new_gc ( vp, "#4a059f", width );
1709 gc[8] = vik_viewport_new_gc ( vp, "#84059f", width );
1710 gc[9] = vik_viewport_new_gc ( vp, "#96059f", width );
1711 gc[10] = vik_viewport_new_gc ( vp, "#f22ef2", width );
1712
16b01243 1713 gc[11] = vik_viewport_new_gc ( vp, "#874200", width ); /* above range */
50a14534
EB
1714
1715 gc[12] = vik_viewport_new_gc ( vp, "#000000", width ); /* black / no speed data */
1716
18df6d42
RN
1717 gc[VIK_TRW_LAYER_TRACK_GC_SLOW] = vik_viewport_new_gc ( vp, "#E6202E", width ); // red-ish
1718 gc[VIK_TRW_LAYER_TRACK_GC_AVER] = vik_viewport_new_gc ( vp, "#D2CD26", width ); // yellow-ish
1719 gc[VIK_TRW_LAYER_TRACK_GC_FAST] = vik_viewport_new_gc ( vp, "#2B8700", width ); // green-ish
1720
50a14534
EB
1721 g_array_append_vals ( vtl->track_gc, gc, VIK_TRW_LAYER_TRACK_GC );
1722}
1723
a7cd93ac 1724static VikTrwLayer* trw_layer_create ( VikViewport *vp )
50a14534 1725{
a7cd93ac 1726 VikTrwLayer *rv = trw_layer_new ( DRAWMODE_BY_TRACK );
ba8be43f
RN
1727 vik_layer_rename ( VIK_LAYER(rv), vik_trw_layer_interface.name );
1728
1729 if ( vp == NULL || GTK_WIDGET(vp)->window == NULL ) {
1730 /* early exit, as the rest is GUI related */
1731 return rv;
1732 }
1733
50a14534 1734 rv->wplabellayout = gtk_widget_create_pango_layout (GTK_WIDGET(vp), NULL);
56cb1807 1735 pango_layout_set_font_description (rv->wplabellayout, GTK_WIDGET(vp)->style->font_desc);
50a14534 1736
50a14534
EB
1737 trw_layer_new_track_gcs ( rv, vp );
1738
1739 rv->waypoint_gc = vik_viewport_new_gc ( vp, "#000000", 2 );
1740 rv->waypoint_text_gc = vik_viewport_new_gc ( vp, "#FFFFFF", 1 );
1741 rv->waypoint_bg_gc = vik_viewport_new_gc ( vp, "#8383C4", 1 );
1742 gdk_gc_set_function ( rv->waypoint_bg_gc, GDK_AND );
1743
50a14534
EB
1744 rv->has_verified_thumbnails = FALSE;
1745 rv->wp_symbol = WP_SYMBOL_FILLED_SQUARE;
1746 rv->wp_size = 4;
ea3933fc 1747 rv->wp_draw_symbols = TRUE;
56cb1807 1748 rv->wp_font_size = FS_MEDIUM;
50a14534
EB
1749
1750 rv->coord_mode = vik_viewport_get_coord_mode ( vp );
1751
20c7a3a0
QT
1752 rv->menu_selection = vik_layer_get_interface(VIK_LAYER(rv)->type)->menu_items_selection;
1753
50a14534
EB
1754 return rv;
1755}
1756
ce4bd1cf 1757static void trw_layer_realize_track ( gpointer id, VikTrack *track, gpointer pass_along[5] )
50a14534
EB
1758{
1759 GtkTreeIter *new_iter = g_malloc(sizeof(GtkTreeIter));
1760
1761#ifdef VIK_CONFIG_ALPHABETIZED_TRW
ce4bd1cf 1762 vik_treeview_add_sublayer_alphabetized ( (VikTreeview *) pass_along[3], (GtkTreeIter *) pass_along[0], (GtkTreeIter *) pass_along[1], track->name, pass_along[2], id, GPOINTER_TO_INT (pass_along[4]), NULL, TRUE, TRUE );
50a14534 1763#else
ce4bd1cf 1764 vik_treeview_add_sublayer ( (VikTreeview *) pass_along[3], (GtkTreeIter *) pass_along[0], (GtkTreeIter *) pass_along[1], track->name, pass_along[2], id, GPOINTER_TO_INT (pass_along[4]), NULL, TRUE, TRUE );
50a14534
EB
1765#endif
1766
1767 *new_iter = *((GtkTreeIter *) pass_along[1]);
ce4bd1cf 1768 g_hash_table_insert ( VIK_TRW_LAYER(pass_along[2])->tracks_iters, id, new_iter );
50a14534
EB
1769
1770 if ( ! track->visible )
1771 vik_treeview_item_set_visible ( (VikTreeview *) pass_along[3], (GtkTreeIter *) pass_along[1], FALSE );
1772}
1773
c9570f86 1774static void trw_layer_realize_waypoint ( gpointer id, VikWaypoint *wp, gpointer pass_along[5] )
50a14534
EB
1775{
1776 GtkTreeIter *new_iter = g_malloc(sizeof(GtkTreeIter));
1777#ifdef VIK_CONFIG_ALPHABETIZED_TRW
c9570f86 1778 vik_treeview_add_sublayer_alphabetized ( (VikTreeview *) pass_along[3], (GtkTreeIter *) pass_along[0], (GtkTreeIter *) pass_along[1], wp->name, pass_along[2], id, GPOINTER_TO_INT (pass_along[4]), NULL, TRUE, TRUE );
50a14534 1779#else
c9570f86 1780 vik_treeview_add_sublayer ( (VikTreeview *) pass_along[3], (GtkTreeIter *) pass_along[0], (GtkTreeIter *) pass_along[1], wp->name, pass_along[2], id, GPOINTER_TO_UINT (pass_along[4]), NULL, TRUE, TRUE );
50a14534
EB
1781#endif
1782
1783 *new_iter = *((GtkTreeIter *) pass_along[1]);
c9570f86 1784 g_hash_table_insert ( VIK_TRW_LAYER(pass_along[2])->waypoints_iters, id, new_iter );
50a14534
EB
1785
1786 if ( ! wp->visible )
1787 vik_treeview_item_set_visible ( (VikTreeview *) pass_along[3], (GtkTreeIter *) pass_along[1], FALSE );
1788}
1789
1790
a7cd93ac 1791static void trw_layer_realize ( VikTrwLayer *vtl, VikTreeview *vt, GtkTreeIter *layer_iter )
50a14534
EB
1792{
1793 GtkTreeIter iter2;
c9570f86 1794 gpointer pass_along[5] = { &(vtl->tracks_iter), &iter2, vtl, vt, GINT_TO_POINTER(VIK_TRW_LAYER_SUBLAYER_TRACK) };
50a14534
EB
1795
1796#ifdef VIK_CONFIG_ALPHABETIZED_TRW
4c77d5e0 1797 vik_treeview_add_sublayer_alphabetized ( (VikTreeview *) vt, layer_iter, &(vtl->tracks_iter), _("Tracks"), vtl, NULL, VIK_TRW_LAYER_SUBLAYER_TRACKS, NULL, TRUE, FALSE );
50a14534 1798#else
4c77d5e0 1799 vik_treeview_add_sublayer ( (VikTreeview *) vt, layer_iter, &(vtl->tracks_iter), _("Tracks"), vtl, NULL, VIK_TRW_LAYER_SUBLAYER_TRACKS, NULL, TRUE, FALSE );
50a14534
EB
1800#endif
1801 if ( ! vtl->tracks_visible )
1802 vik_treeview_item_set_visible ( (VikTreeview *) vt, &(vtl->tracks_iter), FALSE );
1803
1804 g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_realize_track, pass_along );
1805
1806#ifdef VIK_CONFIG_ALPHABETIZED_TRW
4c77d5e0 1807 vik_treeview_add_sublayer_alphabetized ( (VikTreeview *) vt, layer_iter, &(vtl->waypoints_iter), _("Waypoints"), vtl, NULL, VIK_TRW_LAYER_SUBLAYER_WAYPOINTS, NULL, TRUE, FALSE );
50a14534 1808#else
4c77d5e0 1809 vik_treeview_add_sublayer ( (VikTreeview *) vt, layer_iter, &(vtl->waypoints_iter), _("Waypoints"), vtl, NULL, VIK_TRW_LAYER_SUBLAYER_WAYPOINTS, NULL, TRUE, FALSE );
50a14534
EB
1810#endif
1811
1812 if ( ! vtl->waypoints_visible )
1813 vik_treeview_item_set_visible ( (VikTreeview *) vt, &(vtl->waypoints_iter), FALSE );
1814
1815 pass_along[0] = &(vtl->waypoints_iter);
c9570f86 1816 pass_along[4] = GINT_TO_POINTER(VIK_TRW_LAYER_SUBLAYER_WAYPOINT);
50a14534
EB
1817
1818 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_realize_waypoint, pass_along );
1819
1820}
1821
a7cd93ac 1822static gboolean trw_layer_sublayer_toggle_visible ( VikTrwLayer *l, gint subtype, gpointer sublayer )
50a14534
EB
1823{
1824 switch ( subtype )
1825 {
1826 case VIK_TRW_LAYER_SUBLAYER_TRACKS: return (l->tracks_visible ^= 1);
1827 case VIK_TRW_LAYER_SUBLAYER_WAYPOINTS: return (l->waypoints_visible ^= 1);
1828 case VIK_TRW_LAYER_SUBLAYER_TRACK:
1829 {
1830 VikTrack *t = g_hash_table_lookup ( l->tracks, sublayer );
1831 if (t)
1832 return (t->visible ^= 1);
1833 else
1834 return TRUE;
1835 }
1836 case VIK_TRW_LAYER_SUBLAYER_WAYPOINT:
1837 {
1838 VikWaypoint *t = g_hash_table_lookup ( l->waypoints, sublayer );
1839 if (t)
1840 return (t->visible ^= 1);
1841 else
1842 return TRUE;
1843 }
1844 }
1845 return TRUE;
1846}
1847
04f36d92
RN
1848/*
1849 * Return a property about tracks for this layer
1850 */
1851gint vik_trw_layer_get_property_tracks_line_thickness ( VikTrwLayer *vtl )
1852{
1853 return vtl->line_thickness;
1854}
1855
cb89c5a5
RN
1856// Structure to hold multiple track information for a layer
1857typedef struct {
1858 gdouble length;
1859 time_t start_time;
1860 time_t end_time;
1861 gint duration;
1862} tooltip_tracks;
1863
1864/*
1865 * Build up layer multiple track information via updating the tooltip_tracks structure
1866 */
1867static void trw_layer_tracks_tooltip ( const gchar *name, VikTrack *tr, tooltip_tracks *tt )
1868{
1869 tt->length = tt->length + vik_track_get_length (tr);
1870
1871 // Ensure times are available
1872 if ( tr->trackpoints &&
1873 VIK_TRACKPOINT(tr->trackpoints->data)->has_timestamp &&
1874 VIK_TRACKPOINT(g_list_last(tr->trackpoints)->data)->has_timestamp ) {
1875
1876 time_t t1, t2;
1877 t1 = VIK_TRACKPOINT(tr->trackpoints->data)->timestamp;
1878 t2 = VIK_TRACKPOINT(g_list_last(tr->trackpoints)->data)->timestamp;
1879
1880 // Assume never actually have a track with a time of 0 (1st Jan 1970)
1881 // Hence initialize to the first 'proper' value
1882 if ( tt->start_time == 0 )
1883 tt->start_time = t1;
1884 if ( tt->end_time == 0 )
1885 tt->end_time = t2;
1886
1887 // Update find the earliest / last times
1888 if ( t1 < tt->start_time )
1889 tt->start_time = t1;
1890 if ( t2 > tt->end_time )
1891 tt->end_time = t2;
1892
1893 // Keep track of total time
1894 // there maybe gaps within a track (eg segments)
1895 // but this should be generally good enough for a simple indicator
1896 tt->duration = tt->duration + (int)(t2-t1);
1897 }
1898}
1899
1900/*
1901 * Generate tooltip text for the layer.
1902 * This is relatively complicated as it considers information for
1903 * no tracks, a single track or multiple tracks
1904 * (which may or may not have timing information)
1905 */
1906static const gchar* trw_layer_layer_tooltip ( VikTrwLayer *vtl )
1907{
1908 gchar tbuf1[32];
1909 gchar tbuf2[64];
1910 gchar tbuf3[64];
1911 gchar tbuf4[10];
1912 tbuf1[0] = '\0';
1913 tbuf2[0] = '\0';
1914 tbuf3[0] = '\0';
1915 tbuf4[0] = '\0';
1916
1917 static gchar tmp_buf[128];
1918 tmp_buf[0] = '\0';
1919
1920 // For compact date format I'm using '%x' [The preferred date representation for the current locale without the time.]
1921
1922 // Safety check - I think these should always be valid
1923 if ( vtl->tracks && vtl->waypoints ) {
1924 tooltip_tracks tt = { 0.0, 0, 0 };
1925 g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_tracks_tooltip, &tt );
1926
1927 GDate* gdate_start = g_date_new ();
1928 g_date_set_time_t (gdate_start, tt.start_time);
1929
1930 GDate* gdate_end = g_date_new ();
1931 g_date_set_time_t (gdate_end, tt.end_time);
1932
1933 if ( g_date_compare (gdate_start, gdate_end) ) {
1934 // Dates differ so print range on separate line
1935 g_date_strftime (tbuf1, sizeof(tbuf1), "%x", gdate_start);
1936 g_date_strftime (tbuf2, sizeof(tbuf2), "%x", gdate_end);
1937 g_snprintf (tbuf3, sizeof(tbuf3), "%s to %s\n", tbuf1, tbuf2);
1938 }
1939 else {
1940 // Same date so just show it and keep rest of text on the same line - provided it's a valid time!
1941 if ( tt.start_time != 0 )
1942 g_date_strftime (tbuf3, sizeof(tbuf3), "%x: ", gdate_start);
1943 }
1944
1945 tbuf2[0] = '\0';
1946 if ( tt.length > 0.0 ) {
1947 gdouble len_in_units;
1948
1949 // Setup info dependent on distance units
1950 if ( a_vik_get_units_distance() == VIK_UNITS_DISTANCE_MILES ) {
1951 g_snprintf (tbuf4, sizeof(tbuf4), "miles");
a492ff13 1952 len_in_units = VIK_METERS_TO_MILES(tt.length);
cb89c5a5
RN
1953 }
1954 else {
1955 g_snprintf (tbuf4, sizeof(tbuf4), "kms");
1956 len_in_units = tt.length/1000.0;
1957 }
1958
1959 // Timing information if available
1960 tbuf1[0] = '\0';
1961 if ( tt.duration > 0 ) {
1962 g_snprintf (tbuf1, sizeof(tbuf1),
1963 _(" in %d:%02d hrs:mins"),
1964 (int)round(tt.duration/3600), (int)round((tt.duration/60)%60));
1965 }
1966 g_snprintf (tbuf2, sizeof(tbuf2),
1967 _("\n%sTotal Length %.1f %s%s"),
1968 tbuf3, len_in_units, tbuf4, tbuf1);
1969 }
1970
1971 // Put together all the elements to form compact tooltip text
1972 g_snprintf (tmp_buf, sizeof(tmp_buf),
1973 _("Tracks: %d - Waypoints: %d%s"),
1974 g_hash_table_size (vtl->tracks), g_hash_table_size (vtl->waypoints), tbuf2);
1975
1976 g_date_free (gdate_start);
1977 g_date_free (gdate_end);
1978
1979 }
1980
1981 return tmp_buf;
1982}
1983
c7060c4e
RN
1984static const gchar* trw_layer_sublayer_tooltip ( VikTrwLayer *l, gint subtype, gpointer sublayer )
1985{
1986 switch ( subtype )
1987 {
1988 case VIK_TRW_LAYER_SUBLAYER_TRACKS: return NULL;
1989 case VIK_TRW_LAYER_SUBLAYER_WAYPOINTS: return NULL;
1990 case VIK_TRW_LAYER_SUBLAYER_TRACK:
1991 {
1992 VikTrack *tr = g_hash_table_lookup ( l->tracks, sublayer );
1993 if ( tr ) {
1994 // Could be a better way of handling strings - but this works...
1995 gchar time_buf1[20];
1996 gchar time_buf2[20];
1997 time_buf1[0] = '\0';
1998 time_buf2[0] = '\0';
1999 static gchar tmp_buf[100];
2000 // Compact info: Short date eg (11/20/99), duration and length
2001 // Hopefully these are the things that are most useful and so promoted into the tooltip
2002 if ( tr->trackpoints && VIK_TRACKPOINT(tr->trackpoints->data)->has_timestamp ) {
2003 // %x The preferred date representation for the current locale without the time.
2004 strftime (time_buf1, sizeof(time_buf1), "%x: ", gmtime(&(VIK_TRACKPOINT(tr->trackpoints->data)->timestamp)));
2005 if ( VIK_TRACKPOINT(g_list_last(tr->trackpoints)->data)->has_timestamp ) {
2006 gint dur = ( (VIK_TRACKPOINT(g_list_last(tr->trackpoints)->data)->timestamp) - (VIK_TRACKPOINT(tr->trackpoints->data)->timestamp) );
2007 if ( dur > 0 )
cb89c5a5 2008 g_snprintf ( time_buf2, sizeof(time_buf2), _("- %d:%02d hrs:mins"), (int)round(dur/3600), (int)round((dur/60)%60) );
c7060c4e
RN
2009 }
2010 }
2011 // Get length and consider the appropriate distance units
2012 gdouble tr_len = vik_track_get_length(tr);
2013 vik_units_distance_t dist_units = a_vik_get_units_distance ();
2014 switch (dist_units) {
2015 case VIK_UNITS_DISTANCE_KILOMETRES:
2016 g_snprintf (tmp_buf, sizeof(tmp_buf), _("%s%.1f km %s"), time_buf1, tr_len/1000.0, time_buf2);
2017 break;
2018 case VIK_UNITS_DISTANCE_MILES:
a492ff13 2019 g_snprintf (tmp_buf, sizeof(tmp_buf), _("%s%.1f miles %s"), time_buf1, VIK_METERS_TO_MILES(tr_len), time_buf2);
c7060c4e
RN
2020 break;
2021 default:
2022 break;
2023 }
2024 return tmp_buf;
2025 }
2026 }
2027 break;
2028 case VIK_TRW_LAYER_SUBLAYER_WAYPOINT:
2029 {
2030 VikWaypoint *w = g_hash_table_lookup ( l->waypoints, sublayer );
2031 // NB It's OK to return NULL
c9570f86
RN
2032 if ( w )
2033 return w->comment;
c7060c4e
RN
2034 }
2035 break;
2036 default: break;
2037 }
2038 return NULL;
2039}
2040
95d1b757
RN
2041/*
2042 * Function to show basic track point information on the statusbar
2043 */
2044static void set_statusbar_msg_info_trkpt ( VikTrwLayer *vtl, VikTrackpoint *trkpt )
2045{
2046 gchar tmp_buf1[64];
2047 switch (a_vik_get_units_height ()) {
2048 case VIK_UNITS_HEIGHT_FEET:
2049 g_snprintf(tmp_buf1, sizeof(tmp_buf1), _("Trkpt: Alt %dft"), (int)round(VIK_METERS_TO_FEET(trkpt->altitude)));
2050 break;
2051 default:
2052 //VIK_UNITS_HEIGHT_METRES:
2053 g_snprintf(tmp_buf1, sizeof(tmp_buf1), _("Trkpt: Alt %dm"), (int)round(trkpt->altitude));
2054 }
2055
2056 gchar tmp_buf2[64];
2057 tmp_buf2[0] = '\0';
2058 if ( trkpt->has_timestamp ) {
2059 // Compact date time format
2060 strftime (tmp_buf2, sizeof(tmp_buf2), _(" | Time %x %X"), localtime(&(trkpt->timestamp)));
2061 }
2062
2063 // Position part
2064 // Position is put later on, as this bit may not be seen if the display is not big enough,
2065 // one can easily use the current pointer position to see this if needed
2066 gchar *lat = NULL, *lon = NULL;
2067 static struct LatLon ll;
2068 vik_coord_to_latlon (&(trkpt->coord), &ll);
2069 a_coords_latlon_to_string ( &ll, &lat, &lon );
2070
2071 // Track name
2072 // Again is put later on, as this bit may not be seen if the display is not big enough
2073 // trackname can be seen from the treeview (when enabled)
2074 // Also name could be very long to not leave room for anything else
2075 gchar tmp_buf3[64];
2076 tmp_buf3[0] = '\0';
ce4bd1cf
RN
2077 if ( vtl->current_tp_track ) {
2078 g_snprintf(tmp_buf3, sizeof(tmp_buf3), _(" | Track: %s"), vtl->current_tp_track->name );
95d1b757
RN
2079 }
2080
2081 // Combine parts to make overall message
2082 gchar *msg = g_strdup_printf (_("%s%s | %s %s %s"), tmp_buf1, tmp_buf2, lat, lon, tmp_buf3);
2083 vik_statusbar_set_message ( vik_window_get_statusbar (VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl))), VIK_STATUSBAR_INFO, msg );
2084 g_free ( lat );
2085 g_free ( lon );
2086 g_free ( msg );
2087}
2088
2089/*
2090 * Function to show basic waypoint information on the statusbar
2091 */
2092static void set_statusbar_msg_info_wpt ( VikTrwLayer *vtl, VikWaypoint *wpt )
2093{
2094 gchar tmp_buf1[64];
2095 switch (a_vik_get_units_height ()) {
2096 case VIK_UNITS_HEIGHT_FEET:
2097 g_snprintf(tmp_buf1, sizeof(tmp_buf1), _("Wpt: Alt %dft"), (int)round(VIK_METERS_TO_FEET(wpt->altitude)));
2098 break;
2099 default:
2100 //VIK_UNITS_HEIGHT_METRES:
2101 g_snprintf(tmp_buf1, sizeof(tmp_buf1), _("Wpt: Alt %dm"), (int)round(wpt->altitude));
2102 }
2103
2104 // Position part
2105 // Position is put last, as this bit is most likely not to be seen if the display is not big enough,
2106 // one can easily use the current pointer position to see this if needed
2107 gchar *lat = NULL, *lon = NULL;
2108 static struct LatLon ll;
2109 vik_coord_to_latlon (&(wpt->coord), &ll);
2110 a_coords_latlon_to_string ( &ll, &lat, &lon );
2111
2112 // Combine parts to make overall message
2113 gchar *msg;
2114 if ( wpt->comment )
2115 // Add comment if available
2116 msg = g_strdup_printf ( _("%s | %s %s | Comment: %s"), tmp_buf1, lat, lon, wpt->comment );
2117 else
2118 msg = g_strdup_printf ( _("%s | %s %s"), tmp_buf1, lat, lon );
2119 vik_statusbar_set_message ( vik_window_get_statusbar (VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl))), VIK_STATUSBAR_INFO, msg );
2120 g_free ( lat );
2121 g_free ( lon );
2122 g_free ( msg );
2123}
2124
a5dcfdb7
RN
2125/**
2126 * General layer selection function, find out which bit is selected and take appropriate action
2127 */
a7cd93ac 2128static gboolean trw_layer_selected ( VikTrwLayer *l, gint subtype, gpointer sublayer, gint type, gpointer vlp )
a5dcfdb7 2129{
c95d6b00 2130 // Reset
c9570f86
RN
2131 l->current_wp = NULL;
2132 l->current_wp_id = NULL;
c95d6b00
RN
2133 trw_layer_cancel_current_tp ( l, FALSE );
2134
95d1b757
RN
2135 // Clear statusbar
2136 vik_statusbar_set_message ( vik_window_get_statusbar (VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(l))), VIK_STATUSBAR_INFO, "" );
2137
a5dcfdb7
RN
2138 switch ( type )
2139 {
2140 case VIK_TREEVIEW_TYPE_LAYER:
2141 {
2142 vik_window_set_selected_trw_layer ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l), l );
2143 /* Mark for redraw */
2144 return TRUE;
2145 }
2146 break;
2147
2148 case VIK_TREEVIEW_TYPE_SUBLAYER:
2149 {
2150 switch ( subtype )
2151 {
2152 case VIK_TRW_LAYER_SUBLAYER_TRACKS:
2153 {
113c74f6 2154 vik_window_set_selected_tracks ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l), l->tracks, l );
a5dcfdb7
RN
2155 /* Mark for redraw */
2156 return TRUE;
2157 }
2158 break;
2159 case VIK_TRW_LAYER_SUBLAYER_TRACK:
2160 {
95d1b757 2161 VikTrack *track = g_hash_table_lookup ( l->tracks, sublayer );
b16effab 2162 vik_window_set_selected_track ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l), (gpointer)track, l );
a5dcfdb7
RN
2163 /* Mark for redraw */
2164 return TRUE;
2165 }
2166 break;
2167 case VIK_TRW_LAYER_SUBLAYER_WAYPOINTS:
2168 {
113c74f6 2169 vik_window_set_selected_waypoints ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l), l->waypoints, l );
a5dcfdb7
RN
2170 /* Mark for redraw */
2171 return TRUE;
2172 }
2173 break;
2174 case VIK_TRW_LAYER_SUBLAYER_WAYPOINT:
2175 {
b16effab
RN
2176 VikWaypoint *wpt = g_hash_table_lookup ( l->waypoints, sublayer );
2177 if ( wpt ) {
2178 vik_window_set_selected_waypoint ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l), (gpointer)wpt, l );
2179 // Show some waypoint info
2180 set_statusbar_msg_info_wpt ( l, wpt );
2181 /* Mark for redraw */
2182 return TRUE;
2183 }
a5dcfdb7
RN
2184 }
2185 break;
2186 default:
2187 {
2188 return vik_window_clear_highlight ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l) );
2189 }
2190 break;
2191 }
2192 return FALSE;
2193 }
2194 break;
2195
2196 default:
2197 return vik_window_clear_highlight ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l) );
2198 break;
2199 }
2200}
c7060c4e 2201
50a14534
EB
2202GHashTable *vik_trw_layer_get_tracks ( VikTrwLayer *l )
2203{
2204 return l->tracks;
2205}
2206
2207GHashTable *vik_trw_layer_get_waypoints ( VikTrwLayer *l )
2208{
2209 return l->waypoints;
2210}
2211
c9570f86
RN
2212/*
2213 * ATM use a case sensitive find
2214 * Finds the first one
2215 */
2216static gboolean trw_layer_waypoint_find ( const gpointer id, const VikWaypoint *wp, const gchar *name )
2217{
2218 if ( wp && wp->name )
2219 if ( ! strcmp ( wp->name, name ) )
2220 return TRUE;
2221 return FALSE;
2222}
2223
2224/*
2225 * Get waypoint by name - not guaranteed to be unique
2226 * Finds the first one
2227 */
2228VikWaypoint *vik_trw_layer_get_waypoint ( VikTrwLayer *vtl, const gchar *name )
2229{
2230 return g_hash_table_find ( vtl->waypoints, (GHRFunc) trw_layer_waypoint_find, (gpointer) name );
2231}
2232
ce4bd1cf
RN
2233/*
2234 * ATM use a case sensitive find
2235 * Finds the first one
2236 */
2237static gboolean trw_layer_track_find ( const gpointer id, const VikTrack *trk, const gchar *name )
2238{
2239 if ( trk && trk->name )
2240 if ( ! strcmp ( trk->name, name ) )
2241 return TRUE;
2242 return FALSE;
2243}
2244
2245/*
2246 * Get track by name - not guaranteed to be unique
2247 * Finds the first one
2248 */
c9570f86
RN
2249VikTrack *vik_trw_layer_get_track ( VikTrwLayer *vtl, const gchar *name )
2250{
ce4bd1cf 2251 return g_hash_table_find ( vtl->tracks, (GHRFunc) trw_layer_track_find, (gpointer) name );
c9570f86
RN
2252}
2253
50a14534
EB
2254static void trw_layer_find_maxmin_waypoints ( const gchar *name, const VikWaypoint *w, struct LatLon maxmin[2] )
2255{
2256 static VikCoord fixme;
2257 vik_coord_copy_convert ( &(w->coord), VIK_COORD_LATLON, &fixme );
2258 if ( VIK_LATLON(&fixme)->lat > maxmin[0].lat || maxmin[0].lat == 0.0 )
2259 maxmin[0].lat = VIK_LATLON(&fixme)->lat;
2260 if ( VIK_LATLON(&fixme)->lat < maxmin[1].lat || maxmin[1].lat == 0.0 )
2261 maxmin[1].lat = VIK_LATLON(&fixme)->lat;
2262 if ( VIK_LATLON(&fixme)->lon > maxmin[0].lon || maxmin[0].lon == 0.0 )
2263 maxmin[0].lon = VIK_LATLON(&fixme)->lon;
2264 if ( VIK_LATLON(&fixme)->lon < maxmin[1].lon || maxmin[1].lon == 0.0 )
2265 maxmin[1].lon = VIK_LATLON(&fixme)->lon;
2266}
2267
867f4673 2268static void trw_layer_find_maxmin_tracks ( const gchar *name, const VikTrack *trk, struct LatLon maxmin[2] )
50a14534 2269{
867f4673 2270 GList *tr = trk->trackpoints;
50a14534
EB
2271 static VikCoord fixme;
2272
2273 while ( tr )
2274 {
2275 vik_coord_copy_convert ( &(VIK_TRACKPOINT(tr->data)->coord), VIK_COORD_LATLON, &fixme );
2276 if ( VIK_LATLON(&fixme)->lat > maxmin[0].lat || maxmin[0].lat == 0.0 )
2277 maxmin[0].lat = VIK_LATLON(&fixme)->lat;
2278 if ( VIK_LATLON(&fixme)->lat < maxmin[1].lat || maxmin[1].lat == 0.0 )
2279 maxmin[1].lat = VIK_LATLON(&fixme)->lat;
2280 if ( VIK_LATLON(&fixme)->lon > maxmin[0].lon || maxmin[0].lon == 0.0 )
2281 maxmin[0].lon = VIK_LATLON(&fixme)->lon;
2282 if ( VIK_LATLON(&fixme)->lon < maxmin[1].lon || maxmin[1].lon == 0.0 )
2283 maxmin[1].lon = VIK_LATLON(&fixme)->lon;
2284 tr = tr->next;
2285 }
2286}
2287
165a4fa9
HR
2288static void trw_layer_find_maxmin (VikTrwLayer *vtl, struct LatLon maxmin[2])
2289{
2290 struct LatLon wpt_maxmin[2] = { {0.0,0.0}, {0.0,0.0} };
2291 struct LatLon trk_maxmin[2] = { {0.0,0.0}, {0.0,0.0} };
2292
2293 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_find_maxmin_waypoints, wpt_maxmin );
2294 g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_find_maxmin_tracks, trk_maxmin );
2295 if ((wpt_maxmin[0].lat != 0.0 && wpt_maxmin[0].lat > trk_maxmin[0].lat) || trk_maxmin[0].lat == 0.0) {
2296 maxmin[0].lat = wpt_maxmin[0].lat;
2297 }
2298 else {
2299 maxmin[0].lat = trk_maxmin[0].lat;
2300 }
2301 if ((wpt_maxmin[0].lon != 0.0 && wpt_maxmin[0].lon > trk_maxmin[0].lon) || trk_maxmin[0].lon == 0.0) {
2302 maxmin[0].lon = wpt_maxmin[0].lon;
2303 }
2304 else {
2305 maxmin[0].lon = trk_maxmin[0].lon;
2306 }
2307 if ((wpt_maxmin[1].lat != 0.0 && wpt_maxmin[1].lat < trk_maxmin[1].lat) || trk_maxmin[1].lat == 0.0) {
2308 maxmin[1].lat = wpt_maxmin[1].lat;
2309 }
2310 else {
2311 maxmin[1].lat = trk_maxmin[1].lat;
2312 }
2313 if ((wpt_maxmin[1].lon != 0.0 && wpt_maxmin[1].lon < trk_maxmin[1].lon) || trk_maxmin[1].lon == 0.0) {
2314 maxmin[1].lon = wpt_maxmin[1].lon;
2315 }
2316 else {
2317 maxmin[1].lon = trk_maxmin[1].lon;
2318 }
2319}
50a14534
EB
2320
2321gboolean vik_trw_layer_find_center ( VikTrwLayer *vtl, VikCoord *dest )
2322{
2323 /* 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... */
2324 struct LatLon maxmin[2] = { {0.0,0.0}, {0.0,0.0} };
165a4fa9 2325 trw_layer_find_maxmin (vtl, maxmin);
50a14534
EB
2326 if (maxmin[0].lat == 0.0 && maxmin[0].lon == 0.0 && maxmin[1].lat == 0.0 && maxmin[1].lon == 0.0)
2327 return FALSE;
2328 else
2329 {
2330 struct LatLon average = { (maxmin[0].lat+maxmin[1].lat)/2, (maxmin[0].lon+maxmin[1].lon)/2 };
2331 vik_coord_load_from_latlon ( dest, vtl->coord_mode, &average );
2332 return TRUE;
2333 }
2334}
2335
2336static void trw_layer_centerize ( gpointer layer_and_vlp[2] )
2337{
2338 VikCoord coord;
2339 if ( vik_trw_layer_find_center ( VIK_TRW_LAYER(layer_and_vlp[0]), &coord ) )
6bb72350 2340 goto_coord ( layer_and_vlp[1], NULL, NULL, &coord );
50a14534 2341 else
4c77d5e0 2342 a_dialog_info_msg ( VIK_GTK_WINDOW_FROM_LAYER(layer_and_vlp[0]), _("This layer has no waypoints or trackpoints.") );
50a14534
EB
2343}
2344
c5638216
RN
2345static void trw_layer_zoom_to_show_latlons ( VikTrwLayer *vtl, VikViewport *vvp, struct LatLon maxmin[2] )
2346{
2347 /* First set the center [in case previously viewing from elsewhere] */
2348 /* Then loop through zoom levels until provided positions are in view */
2349 /* This method is not particularly fast - but should work well enough */
2350 struct LatLon average = { (maxmin[0].lat+maxmin[1].lat)/2, (maxmin[0].lon+maxmin[1].lon)/2 };
2351 VikCoord coord;
2352 vik_coord_load_from_latlon ( &coord, vtl->coord_mode, &average );
2353 vik_viewport_set_center_coord ( vvp, &coord );
2354
2355 /* Convert into definite 'smallest' and 'largest' positions */
2356 struct LatLon minmin;
2357 if ( maxmin[0].lat < maxmin[1].lat )
2358 minmin.lat = maxmin[0].lat;
2359 else
2360 minmin.lat = maxmin[1].lat;
2361
2362 struct LatLon maxmax;
2363 if ( maxmin[0].lon > maxmin[1].lon )
2364 maxmax.lon = maxmin[0].lon;
2365 else
2366 maxmax.lon = maxmin[1].lon;
2367
2368 /* Never zoom in too far - generally not that useful, as too close ! */
2369 /* Always recalculate the 'best' zoom level */
2370 gdouble zoom = 1.0;
2371 vik_viewport_set_zoom ( vvp, zoom );
2372
2373 gdouble min_lat, max_lat, min_lon, max_lon;
2374 /* Should only be a maximum of about 18 iterations from min to max zoom levels */
2375 while ( zoom <= VIK_VIEWPORT_MAX_ZOOM ) {
2376 vik_viewport_get_min_max_lat_lon ( vvp, &min_lat, &max_lat, &min_lon, &max_lon );
2377 /* NB I think the logic used in this test to determine if the bounds is within view
2378 fails if track goes across 180 degrees longitude.
2379 Hopefully that situation is not too common...
2380 Mind you viking doesn't really do edge locations to well anyway */
2381 if ( min_lat < minmin.lat &&
2382 max_lat > minmin.lat &&
2383 min_lon < maxmax.lon &&
2384 max_lon > maxmax.lon )
2385 /* Found within zoom level */
2386 break;
2387
2388 /* Try next */
2389 zoom = zoom * 2;
2390 vik_viewport_set_zoom ( vvp, zoom );
2391 }
2392}
2393
2394gboolean vik_trw_layer_auto_set_view ( VikTrwLayer *vtl, VikViewport *vvp )
2395{
2396 /* TODO: what if there's only one waypoint @ 0,0, it will think nothing found. */
2397 struct LatLon maxmin[2] = { {0.0,0.0}, {0.0,0.0} };
2398 trw_layer_find_maxmin (vtl, maxmin);
2399 if (maxmin[0].lat == 0.0 && maxmin[0].lon == 0.0 && maxmin[1].lat == 0.0 && maxmin[1].lon == 0.0)
2400 return FALSE;
2401 else {
2402 trw_layer_zoom_to_show_latlons ( vtl, vvp, maxmin );
2403 return TRUE;
2404 }
2405}
2406
5a10c240
RN
2407static void trw_layer_auto_view ( gpointer layer_and_vlp[2] )
2408{
2409 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])) ) ) {
2410 vik_layers_panel_emit_update ( VIK_LAYERS_PANEL(layer_and_vlp[1]) );
2411 }
2412 else
2413 a_dialog_info_msg ( VIK_GTK_WINDOW_FROM_LAYER(layer_and_vlp[0]), _("This layer has no waypoints or trackpoints.") );
2414}
2415
ce4bd1cf 2416static void trw_layer_export ( gpointer layer_and_vlp[2], const gchar *title, const gchar* default_name, VikTrack* trk, guint file_type )
50a14534
EB
2417{
2418 GtkWidget *file_selector;
2419 const gchar *fn;
2420 gboolean failed = FALSE;
7f6757c4
RN
2421 file_selector = gtk_file_chooser_dialog_new (title,
2422 NULL,
2423 GTK_FILE_CHOOSER_ACTION_SAVE,
2424 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
2425 GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT,
2426 NULL);
2427 gtk_file_chooser_set_current_name (GTK_FILE_CHOOSER(file_selector), default_name);
6e4a49aa
MA
2428
2429 while ( gtk_dialog_run ( GTK_DIALOG(file_selector) ) == GTK_RESPONSE_ACCEPT )
50a14534 2430 {
6e4a49aa 2431 fn = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER(file_selector) );
45acf79e 2432 if ( g_file_test ( fn, G_FILE_TEST_EXISTS ) == FALSE )
50a14534
EB
2433 {
2434 gtk_widget_hide ( file_selector );
208d2084 2435 failed = ! a_file_export ( VIK_TRW_LAYER(layer_and_vlp[0]), fn, file_type, trk, TRUE );
50a14534
EB
2436 break;
2437 }
2438 else
2439 {
d91e5f2b 2440 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
2441 {
2442 gtk_widget_hide ( file_selector );
208d2084 2443 failed = ! a_file_export ( VIK_TRW_LAYER(layer_and_vlp[0]), fn, file_type, trk, TRUE );
50a14534
EB
2444 break;
2445 }
2446 }
2447 }
2448 gtk_widget_destroy ( file_selector );
2449 if ( failed )
4c77d5e0 2450 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
2451}
2452
2453static void trw_layer_export_gpspoint ( gpointer layer_and_vlp[2] )
2454{
f7f8a0a6 2455 trw_layer_export ( layer_and_vlp, _("Export Layer"), vik_layer_get_name(VIK_LAYER(layer_and_vlp[0])), NULL, FILE_TYPE_GPSPOINT );
50a14534
EB
2456}
2457
2458static void trw_layer_export_gpsmapper ( gpointer layer_and_vlp[2] )
2459{
f7f8a0a6 2460 trw_layer_export ( layer_and_vlp, _("Export Layer"), vik_layer_get_name(VIK_LAYER(layer_and_vlp[0])), NULL, FILE_TYPE_GPSMAPPER );
50a14534
EB
2461}
2462
561e6ad0
EB
2463static void trw_layer_export_gpx ( gpointer layer_and_vlp[2] )
2464{
18d0a1ed
RN
2465 /* Auto append '.gpx' to track name (providing it's not already there) for the default filename */
2466 gchar *auto_save_name = g_strdup ( vik_layer_get_name(VIK_LAYER(layer_and_vlp[0])) );
2467 if ( ! check_file_ext ( auto_save_name, ".gpx" ) )
2468 auto_save_name = g_strconcat ( auto_save_name, ".gpx", NULL );
2469
2470 trw_layer_export ( layer_and_vlp, _("Export Layer"), auto_save_name, NULL, FILE_TYPE_GPX );
2471
2472 g_free ( auto_save_name );
7f6757c4
RN
2473}
2474
ba9d0a00
RN
2475static void trw_layer_export_kml ( gpointer layer_and_vlp[2] )
2476{
2477 /* Auto append '.kml' to the name (providing it's not already there) for the default filename */
2478 gchar *auto_save_name = g_strdup ( vik_layer_get_name(VIK_LAYER(layer_and_vlp[0])) );
2479 if ( ! check_file_ext ( auto_save_name, ".kml" ) )
2480 auto_save_name = g_strconcat ( auto_save_name, ".kml", NULL );
2481
2482 trw_layer_export ( layer_and_vlp, _("Export Layer"), auto_save_name, NULL, FILE_TYPE_KML );
2483
2484 g_free ( auto_save_name );
2485}
2486
ccccf356
RN
2487/**
2488 * Convert the given TRW layer into a temporary GPX file and open it with the specified program
2489 *
2490 */
2491static void trw_layer_export_external_gpx ( gpointer layer_and_vlp[2], const gchar* external_program )
2492{
2493 gchar *name_used = NULL;
2494 int fd;
2495
2496 if ((fd = g_file_open_tmp("tmp-viking.XXXXXX.gpx", &name_used, NULL)) >= 0) {
208d2084 2497 gboolean failed = ! a_file_export ( VIK_TRW_LAYER(layer_and_vlp[0]), name_used, FILE_TYPE_GPX, NULL, TRUE);
ccccf356
RN
2498 if (failed) {
2499 a_dialog_error_msg (VIK_GTK_WINDOW_FROM_LAYER(layer_and_vlp[0]), _("Could not create temporary file for export.") );
2500 }
2501 else {
2502 GError *err = NULL;
2503 gchar *quoted_file = g_shell_quote ( name_used );
2504 gchar *cmd = g_strdup_printf ( "%s %s", external_program, quoted_file );
2505 g_free ( quoted_file );
2506 if ( ! g_spawn_command_line_async ( cmd, &err ) )
2507 {
2508 a_dialog_error_msg_extra ( VIK_GTK_WINDOW_FROM_LAYER( layer_and_vlp[0]), _("Could not launch %s."), external_program );
2509 g_error_free ( err );
2510 }
2511 g_free ( cmd );
2512 }
2513 // Note ATM the 'temporary' file is not deleted, as loading via another program is not instantaneous
2514 //g_remove ( name_used );
2515 // Perhaps should be deleted when the program ends?
2516 // For now leave it to the user to delete it / use system temp cleanup methods.
2517 g_free ( name_used );
2518 }
2519}
2520
2521static void trw_layer_export_external_gpx_1 ( gpointer layer_and_vlp[2] )
2522{
2523 trw_layer_export_external_gpx ( layer_and_vlp, a_vik_get_external_gpx_program_1() );
2524}
2525
ff02058b
RN
2526static void trw_layer_export_external_gpx_2 ( gpointer layer_and_vlp[2] )
2527{
2528 trw_layer_export_external_gpx ( layer_and_vlp, a_vik_get_external_gpx_program_2() );
2529}
2530
7f6757c4
RN
2531static void trw_layer_export_gpx_track ( gpointer pass_along[6] )
2532{
7f6757c4
RN
2533 gpointer layer_and_vlp[2];
2534 layer_and_vlp[0] = pass_along[0];
2535 layer_and_vlp[1] = pass_along[1];
ce4bd1cf
RN
2536 VikTrack *trk = g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, pass_along[3] );
2537
2538 if ( !trk || !trk->name )
2539 return;
7f6757c4
RN
2540
2541 /* Auto append '.gpx' to track name (providing it's not already there) for the default filename */
ce4bd1cf 2542 gchar *auto_save_name = g_strdup ( trk->name );
7f6757c4
RN
2543 if ( ! check_file_ext ( auto_save_name, ".gpx" ) )
2544 auto_save_name = g_strconcat ( auto_save_name, ".gpx", NULL );
2545
ce4bd1cf 2546 trw_layer_export ( layer_and_vlp, _("Export Track as GPX"), auto_save_name, trk, FILE_TYPE_GPX );
7f6757c4
RN
2547
2548 g_free ( auto_save_name );
561e6ad0
EB
2549}
2550
c9570f86
RN
2551typedef struct {
2552 VikWaypoint *wp; // input
2553 gpointer uuid; // output
2554} wpu_udata;
2555
2556static gboolean trw_layer_waypoint_find_uuid ( const gpointer id, const VikWaypoint *wp, gpointer udata )
2557{
2558 wpu_udata *user_data = udata;
2559 if ( wp == user_data->wp ) {
2560 user_data->uuid = id;
2561 return TRUE;
2562 }
2563 return FALSE;
2564}
2565
50a14534
EB
2566static void trw_layer_goto_wp ( gpointer layer_and_vlp[2] )
2567{
a77d62d8 2568 GtkWidget *dia = gtk_dialog_new_with_buttons (_("Find"),
50a14534
EB
2569 VIK_GTK_WINDOW_FROM_LAYER(layer_and_vlp[0]),
2570 GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
2571 GTK_STOCK_CANCEL,
2572 GTK_RESPONSE_REJECT,
2573 GTK_STOCK_OK,
2574 GTK_RESPONSE_ACCEPT,
2575 NULL);
2576
2577 GtkWidget *label, *entry;
4c77d5e0 2578 label = gtk_label_new(_("Waypoint Name:"));
50a14534
EB
2579 entry = gtk_entry_new();
2580
2581 gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dia)->vbox), label, FALSE, FALSE, 0);
2582 gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dia)->vbox), entry, FALSE, FALSE, 0);
2583 gtk_widget_show_all ( label );
2584 gtk_widget_show_all ( entry );
2585
7ea3cb11
RN
2586 gtk_dialog_set_default_response ( GTK_DIALOG(dia), GTK_RESPONSE_ACCEPT );
2587
50a14534
EB
2588 while ( gtk_dialog_run ( GTK_DIALOG(dia) ) == GTK_RESPONSE_ACCEPT )
2589 {
c9570f86
RN
2590 gchar *name = g_strdup(gtk_entry_get_text(GTK_ENTRY(entry)));
2591 // Find *first* wp with the given name
2592 VikWaypoint *wp = vik_trw_layer_get_waypoint ( VIK_TRW_LAYER(layer_and_vlp[0]), name );
50a14534 2593
c9570f86 2594 if ( !wp )
4c77d5e0 2595 a_dialog_error_msg ( VIK_GTK_WINDOW_FROM_LAYER(layer_and_vlp[0]), _("Waypoint not found in this layer.") );
50a14534
EB
2596 else
2597 {
2598 vik_viewport_set_center_coord ( vik_layers_panel_get_viewport(VIK_LAYERS_PANEL(layer_and_vlp[1])), &(wp->coord) );
2599 vik_layers_panel_emit_update ( VIK_LAYERS_PANEL(layer_and_vlp[1]) );
c9570f86
RN
2600
2601 // Find and select on the side panel
2602 wpu_udata udata;
2603 udata.wp = wp;
2604 udata.uuid = NULL;
2605
2606 // Hmmm, want key of it
2607 gpointer *wpf = g_hash_table_find ( VIK_TRW_LAYER(layer_and_vlp[0])->waypoints, (GHRFunc) trw_layer_waypoint_find_uuid, (gpointer) &udata );
2608
2609 if ( wpf && udata.uuid ) {
2610 GtkTreeIter *it = g_hash_table_lookup ( VIK_TRW_LAYER(layer_and_vlp[0])->waypoints_iters, udata.uuid );
2611 vik_treeview_select_iter ( VIK_LAYER(layer_and_vlp[0])->vt, it, TRUE );
2612 }
2613
50a14534
EB
2614 break;
2615 }
2616
c9570f86 2617 g_free ( name );
50a14534
EB
2618
2619 }
2620 gtk_widget_destroy ( dia );
2621}
2622
2623gboolean vik_trw_layer_new_waypoint ( VikTrwLayer *vtl, GtkWindow *w, const VikCoord *def_coord )
2624{
ac1bde8b 2625 gchar *default_name = highest_wp_number_get(vtl);
acaf7113 2626 VikWaypoint *wp = vik_waypoint_new();
ac1bde8b
RN
2627 gchar *returned_name;
2628 gboolean updated;
acaf7113 2629 wp->coord = *def_coord;
d60a672e
RN
2630
2631 // Attempt to auto set height if DEM data is available
2632 gint16 elev = a_dems_get_elev_by_coord ( &(wp->coord), VIK_DEM_INTERPOL_BEST );
2633 if ( elev != VIK_DEM_INVALID_ELEVATION )
2634 wp->altitude = (gdouble)elev;
50a14534 2635
c9570f86
RN
2636 returned_name = a_dialog_waypoint ( w, default_name, wp, vtl->coord_mode, TRUE, &updated );
2637
2638 if ( returned_name )
50a14534 2639 {
805d282e 2640 wp->visible = TRUE;
ac1bde8b
RN
2641 vik_trw_layer_add_waypoint ( vtl, returned_name, wp );
2642 g_free (default_name);
c9570f86 2643 g_free (returned_name);
50a14534
EB
2644 return TRUE;
2645 }
ac1bde8b 2646 g_free (default_name);
acaf7113 2647 vik_waypoint_free(wp);
50a14534
EB
2648 return FALSE;
2649}
2650
165a4fa9
HR
2651static void trw_layer_new_wikipedia_wp_viewport ( gpointer lav[2] )
2652{
2653 VikCoord one, two;
2654 struct LatLon one_ll, two_ll;
2655 struct LatLon maxmin[2] = { {0.0,0.0}, {0.0,0.0} };
2656
2657 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
2658 VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
2659 VikWindow *vw = (VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl));
2660 VikViewport *vvp = vik_window_viewport(vw);
2661 vik_viewport_screen_to_coord ( vvp, 0, 0, &one);
2662 vik_viewport_screen_to_coord ( vvp, vik_viewport_get_width(vvp), vik_viewport_get_height(vvp), &two);
2663 vik_coord_to_latlon(&one, &one_ll);
2664 vik_coord_to_latlon(&two, &two_ll);
2665 if (one_ll.lat > two_ll.lat) {
2666 maxmin[0].lat = one_ll.lat;
2667 maxmin[1].lat = two_ll.lat;
2668 }
2669 else {
2670 maxmin[0].lat = two_ll.lat;
2671 maxmin[1].lat = one_ll.lat;
2672 }
2673 if (one_ll.lon > two_ll.lon) {
2674 maxmin[0].lon = one_ll.lon;
2675 maxmin[1].lon = two_ll.lon;
2676 }
2677 else {
2678 maxmin[0].lon = two_ll.lon;
2679 maxmin[1].lon = one_ll.lon;
2680 }
2681 a_geonames_wikipedia_box((VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl)), vtl, vlp, maxmin);
2682}
2683
2684static void trw_layer_new_wikipedia_wp_layer ( gpointer lav[2] )
2685{
2686 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
2687 VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
2688 struct LatLon maxmin[2] = { {0.0,0.0}, {0.0,0.0} };
2689
2690 trw_layer_find_maxmin (vtl, maxmin);
2691 a_geonames_wikipedia_box((VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl)), vtl, vlp, maxmin);
2692}
2693
b3eb3b98
RN
2694#ifdef VIK_CONFIG_GEOTAG
2695static void trw_layer_geotagging_waypoint_mtime_keep ( gpointer pass_along[6] )
2696{
2697 VikWaypoint *wp = g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->waypoints, pass_along[3] );
2698 if ( wp )
2699 // Update directly - not changing the mtime
2700 a_geotag_write_exif_gps ( wp->image, wp->coord, wp->altitude, TRUE );
2701}
2702
2703static void trw_layer_geotagging_waypoint_mtime_update ( gpointer pass_along[6] )
2704{
2705 VikWaypoint *wp = g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->waypoints, pass_along[3] );
2706 if ( wp )
2707 // Update directly
2708 a_geotag_write_exif_gps ( wp->image, wp->coord, wp->altitude, FALSE );
2709}
2710
2711/*
2712 * Use code in separate file for this feature as reasonably complex
2713 */
2714static void trw_layer_geotagging_track ( gpointer pass_along[6] )
2715{
2716 VikTrwLayer *vtl = VIK_TRW_LAYER(pass_along[0]);
2717 VikTrack *track = g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, pass_along[3] );
2718 // Unset so can be reverified later if necessary
2719 vtl->has_verified_thumbnails = FALSE;
2720
2721 trw_layer_geotag_dialog ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
2722 vtl,
2723 track,
f182700c 2724 track->name );
b3eb3b98
RN
2725}
2726
2727static void trw_layer_geotagging ( gpointer lav[2] )
2728{
2729 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
2730 // Unset so can be reverified later if necessary
2731 vtl->has_verified_thumbnails = FALSE;
2732
2733 trw_layer_geotag_dialog ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
2734 vtl,
2735 NULL,
2736 NULL);
2737}
2738#endif
2739
16fc32f6
RN
2740// 'Acquires' - Same as in File Menu -> Acquire - applies into the selected TRW Layer //
2741
2742/*
2743 * Acquire into this TRW Layer straight from GPS Device
2744 */
2745static void trw_layer_acquire_gps_cb ( gpointer lav[2] )
2746{
2747 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
2748 VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
2749 VikWindow *vw = (VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl));
2750 VikViewport *vvp = vik_window_viewport(vw);
2751
2752 vik_datasource_gps_interface.mode = VIK_DATASOURCE_ADDTOLAYER;
2753 a_acquire ( vw, vlp, vvp, &vik_datasource_gps_interface );
2754}
2755
55340efa 2756#ifdef VIK_CONFIG_GOOGLE
16fc32f6
RN
2757/*
2758 * Acquire into this TRW Layer from Google Directions
2759 */
2760static void trw_layer_acquire_google_cb ( gpointer lav[2] )
2761{
2762 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
2763 VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
2764 VikWindow *vw = (VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl));
2765 VikViewport *vvp = vik_window_viewport(vw);
2766
2767 a_acquire ( vw, vlp, vvp, &vik_datasource_google_interface );
2768}
29332250 2769#endif
16fc32f6 2770
40f5740b
RN
2771#ifdef VIK_CONFIG_OPENSTREETMAP
2772/*
2773 * Acquire into this TRW Layer from OSM
2774 */
2775static void trw_layer_acquire_osm_cb ( gpointer lav[2] )
2776{
2777 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
2778 VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
2779 VikWindow *vw = (VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl));
2780 VikViewport *vvp = vik_window_viewport(vw);
2781
2782 a_acquire ( vw, vlp, vvp, &vik_datasource_osm_interface );
2783}
2784#endif
2785
16fc32f6
RN
2786#ifdef VIK_CONFIG_GEOCACHES
2787/*
2788 * Acquire into this TRW Layer from Geocaching.com
2789 */
2790static void trw_layer_acquire_geocache_cb ( gpointer lav[2] )
2791{
2792 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
2793 VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
2794 VikWindow *vw = (VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl));
2795 VikViewport *vvp = vik_window_viewport(vw);
2796
2797 a_acquire ( vw, vlp, vvp, &vik_datasource_gc_interface );
2798}
2799#endif
2800
68bab1bd
RN
2801#ifdef VIK_CONFIG_GEOTAG
2802/*
2803 * Acquire into this TRW Layer from images
2804 */
2805static void trw_layer_acquire_geotagged_cb ( gpointer lav[2] )
2806{
2807 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
2808 VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
2809 VikWindow *vw = (VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl));
2810 VikViewport *vvp = vik_window_viewport(vw);
2811
2812 vik_datasource_geotag_interface.mode = VIK_DATASOURCE_ADDTOLAYER;
2813 a_acquire ( vw, vlp, vvp, &vik_datasource_geotag_interface );
2814
2815 // Reverify thumbnails as they may have changed
2816 vtl->has_verified_thumbnails = FALSE;
2817 trw_layer_verify_thumbnails ( vtl, NULL );
2818}
2819#endif
2820
e50758c7
RN
2821static void trw_layer_gps_upload ( gpointer lav[2] )
2822{
2823 gpointer pass_along[6];
2824 pass_along[0] = lav[0];
2825 pass_along[1] = lav[1];
2826 pass_along[2] = NULL; // No track - operate on the layer
2827 pass_along[3] = NULL;
2828 pass_along[4] = NULL;
2829 pass_along[5] = NULL;
2830
2831 trw_layer_gps_upload_any ( pass_along );
2832}
2833
2834/**
2835 * If pass_along[3] is defined that this will upload just that track
2836 */
2837static void trw_layer_gps_upload_any ( gpointer pass_along[6] )
2838{
2839 VikTrwLayer *vtl = VIK_TRW_LAYER(pass_along[0]);
2840 VikLayersPanel *vlp = VIK_LAYERS_PANEL(pass_along[1]);
2841
2842 // May not actually get a track here as pass_along[3] can be null
2843 VikTrack *track = g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, pass_along[3] );
2844
2845 gboolean on_track = track ? TRUE : FALSE;
2846
2847 if (on_track && !track->visible) {
2848 a_dialog_error_msg ( VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Can not upload invisible track.") );
2849 return;
2850 }
2851
2852 GtkWidget *dialog = gtk_dialog_new_with_buttons ( _("GPS Upload"),
2853 VIK_GTK_WINDOW_FROM_LAYER(pass_along[0]),
2854 GTK_DIALOG_DESTROY_WITH_PARENT,
2855 GTK_STOCK_OK,
2856 GTK_RESPONSE_ACCEPT,
2857 GTK_STOCK_CANCEL,
2858 GTK_RESPONSE_REJECT,
2859 NULL );
2860
2861 gtk_dialog_set_default_response ( GTK_DIALOG(dialog), GTK_RESPONSE_ACCEPT );
2862 GtkWidget *response_w = NULL;
2863#if GTK_CHECK_VERSION (2, 20, 0)
2864 response_w = gtk_dialog_get_widget_for_response ( GTK_DIALOG(dialog), GTK_RESPONSE_ACCEPT );
2865#endif
2866
2867 if ( response_w )
2868 gtk_widget_grab_focus ( response_w );
2869
2870 gpointer dgs = datasource_gps_setup ( dialog, on_track );
2871
2872 if ( gtk_dialog_run ( GTK_DIALOG(dialog) ) != GTK_RESPONSE_ACCEPT ) {
2873 datasource_gps_clean_up ( dgs );
2874 gtk_widget_destroy ( dialog );
2875 return;
2876 }
2877
2878 // Get info from reused datasource dialog widgets
2879 gchar* protocol = datasource_gps_get_protocol ( dgs );
2880 gchar* port = datasource_gps_get_descriptor ( dgs );
2881 // NB don't free the above strings as they're references to values held elsewhere
2882 gboolean do_tracks = datasource_gps_get_do_tracks ( dgs );
2883 gboolean do_waypoints = datasource_gps_get_do_waypoints ( dgs );
2884 gboolean turn_off = datasource_gps_get_off ( dgs );
2885
2886 gtk_widget_destroy ( dialog );
2887
2888 // When called from the viewport - work the corresponding layerspanel:
2889 if ( !vlp ) {
2890 vlp = vik_window_layers_panel ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)) );
2891 }
2892
2893 // Apply settings to transfer to the GPS device
2894 vik_gps_comm ( vtl,
2895 track,
2896 GPS_UP,
2897 protocol,
2898 port,
2899 FALSE,
2900 vik_layers_panel_get_viewport (vlp),
2901 vlp,
2902 do_tracks,
2903 do_waypoints,
2904 turn_off );
2905}
2906
d7ac7564
RN
2907/*
2908 * Acquire into this TRW Layer from any GPS Babel supported file
2909 */
2910static void trw_layer_acquire_file_cb ( gpointer lav[2] )
2911{
2912 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
2913 VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
2914 VikWindow *vw = (VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl));
2915 VikViewport *vvp = vik_window_viewport(vw);
2916
2917 a_acquire ( vw, vlp, vvp, &vik_datasource_file_interface );
2918}
2919
50a14534
EB
2920static void trw_layer_new_wp ( gpointer lav[2] )
2921{
2922 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
2923 VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
2924 /* TODO longone: okay, if layer above (aggregate) is invisible but vtl->visible is true, this redraws for no reason.
2925 instead return true if you want to update. */
2926 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 )
2927 vik_layers_panel_emit_update ( vlp );
2928}
2929
535ed1ae
RN
2930static void trw_layer_auto_tracks_view ( gpointer lav[2] )
2931{
2932 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
2933 VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
2934
2935 if ( g_hash_table_size (vtl->tracks) > 0 ) {
2936 struct LatLon maxmin[2] = { {0,0}, {0,0} };
2937 g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_find_maxmin_tracks, maxmin );
2938 trw_layer_zoom_to_show_latlons ( vtl, vik_layers_panel_get_viewport (vlp), maxmin );
2939 vik_layers_panel_emit_update ( vlp );
2940 }
2941}
2942
fc59e8c7
RN
2943static void trw_layer_single_waypoint_jump ( const gchar *name, const VikWaypoint *wp, gpointer vvp )
2944{
2945 /* NB do not care if wp is visible or not */
2946 vik_viewport_set_center_coord ( VIK_VIEWPORT(vvp), &(wp->coord) );
2947}
2948
2949static void trw_layer_auto_waypoints_view ( gpointer lav[2] )
2950{
2951 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
2952 VikLayersPanel *vlp = VIK_LAYERS_PANEL(lav[1]);
2953
2954 /* Only 1 waypoint - jump straight to it */
2955 if ( g_hash_table_size (vtl->waypoints) == 1 ) {
2956 VikViewport *vvp = vik_layers_panel_get_viewport (vlp);
2957 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_single_waypoint_jump, (gpointer) vvp );
2958 }
2959 /* If at least 2 waypoints - find center and then zoom to fit */
2960 else if ( g_hash_table_size (vtl->waypoints) > 1 )
2961 {
2962 struct LatLon maxmin[2] = { {0,0}, {0,0} };
2963 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_find_maxmin_waypoints, maxmin );
2964 trw_layer_zoom_to_show_latlons ( vtl, vik_layers_panel_get_viewport (vlp), maxmin );
2965 }
2966
2967 vik_layers_panel_emit_update ( vlp );
2968}
2969
a7cd93ac 2970static void trw_layer_add_menu_items ( VikTrwLayer *vtl, GtkMenu *menu, gpointer vlp )
50a14534
EB
2971{
2972 static gpointer pass_along[2];
2973 GtkWidget *item;
98fcbbdb 2974 GtkWidget *export_submenu;
50a14534
EB
2975 pass_along[0] = vtl;
2976 pass_along[1] = vlp;
2977
2978 item = gtk_menu_item_new();
2979 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
2980 gtk_widget_show ( item );
2981
d6de71f9
RN
2982 /* Now with icons */
2983 item = gtk_image_menu_item_new_with_mnemonic ( _("_View Layer") );
2984 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ZOOM_FIT, GTK_ICON_SIZE_MENU) );
5a10c240
RN
2985 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_view), pass_along );
2986 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
2987 gtk_widget_show ( item );
2988
535ed1ae
RN
2989 item = gtk_menu_item_new_with_mnemonic ( _("View All Trac_ks") );
2990 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_tracks_view), pass_along );
2991 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
2992 gtk_widget_show ( item );
2993
fc59e8c7
RN
2994 item = gtk_menu_item_new_with_mnemonic ( _("V_iew All Waypoints") );
2995 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_waypoints_view), pass_along );
2996 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
2997 gtk_widget_show ( item );
2998
d6de71f9
RN
2999 item = gtk_image_menu_item_new_with_mnemonic ( _("_Goto Center of Layer") );
3000 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_JUMP_TO, GTK_ICON_SIZE_MENU) );
50a14534
EB
3001 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_centerize), pass_along );
3002 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3003 gtk_widget_show ( item );
3004
7306a492 3005 item = gtk_menu_item_new_with_mnemonic ( _("Goto _Waypoint...") );
50a14534
EB
3006 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_wp), pass_along );
3007 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3008 gtk_widget_show ( item );
3009
1bd88e66 3010 export_submenu = gtk_menu_new ();
d6de71f9
RN
3011 item = gtk_image_menu_item_new_with_mnemonic ( _("_Export Layer") );
3012 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_HARDDISK, GTK_ICON_SIZE_MENU) );
50a14534
EB
3013 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3014 gtk_widget_show ( item );
98fcbbdb 3015 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), export_submenu );
1bd88e66 3016
7306a492 3017 item = gtk_menu_item_new_with_mnemonic ( _("Export as GPS_Point...") );
1bd88e66
GB
3018 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_gpspoint), pass_along );
3019 gtk_menu_shell_append (GTK_MENU_SHELL (export_submenu), item);
3020 gtk_widget_show ( item );
50a14534 3021
7306a492 3022 item = gtk_menu_item_new_with_mnemonic ( _("Export as GPS_Mapper...") );
50a14534 3023 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_gpsmapper), pass_along );
1bd88e66 3024 gtk_menu_shell_append (GTK_MENU_SHELL (export_submenu), item);
50a14534
EB
3025 gtk_widget_show ( item );
3026
7306a492 3027 item = gtk_menu_item_new_with_mnemonic ( _("Export as _GPX...") );
561e6ad0 3028 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_gpx), pass_along );
1bd88e66 3029 gtk_menu_shell_append (GTK_MENU_SHELL (export_submenu), item);
561e6ad0
EB
3030 gtk_widget_show ( item );
3031
ba9d0a00
RN
3032 item = gtk_menu_item_new_with_mnemonic ( _("Export as _KML...") );
3033 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_kml), pass_along );
3034 gtk_menu_shell_append (GTK_MENU_SHELL (export_submenu), item);
3035 gtk_widget_show ( item );
3036
ccccf356
RN
3037 gchar* external1 = g_strconcat ( _("Open with External Program_1: "), a_vik_get_external_gpx_program_1(), NULL );
3038 item = gtk_menu_item_new_with_mnemonic ( external1 );
3039 g_free ( external1 );
3040 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_external_gpx_1), pass_along );
3041 gtk_menu_shell_append (GTK_MENU_SHELL (export_submenu), item);
3042 gtk_widget_show ( item );
3043
ff02058b
RN
3044 gchar* external2 = g_strconcat ( _("Open with External Program_2: "), a_vik_get_external_gpx_program_2(), NULL );
3045 item = gtk_menu_item_new_with_mnemonic ( external2 );
3046 g_free ( external2 );
3047 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_external_gpx_2), pass_along );
3048 gtk_menu_shell_append (GTK_MENU_SHELL (export_submenu), item);
3049 gtk_widget_show ( item );
3050
d6de71f9
RN
3051 item = gtk_image_menu_item_new_with_mnemonic ( _("_New Waypoint...") );
3052 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NEW, GTK_ICON_SIZE_MENU) );
50a14534
EB
3053 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_wp), pass_along );
3054 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3055 gtk_widget_show ( item );
3e7553ae 3056
da03b29d 3057#ifdef VIK_CONFIG_GEONAMES
ba164b00 3058 GtkWidget *wikipedia_submenu = gtk_menu_new();
d6de71f9
RN
3059 item = gtk_image_menu_item_new_with_mnemonic ( _("_Add Wikipedia Waypoints") );
3060 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ADD, GTK_ICON_SIZE_MENU) );
165a4fa9
HR
3061 gtk_menu_shell_append(GTK_MENU_SHELL (menu), item);
3062 gtk_widget_show(item);
3063 gtk_menu_item_set_submenu(GTK_MENU_ITEM(item), wikipedia_submenu);
3064
d6de71f9
RN
3065 item = gtk_image_menu_item_new_with_mnemonic ( _("Within _Layer Bounds") );
3066 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ZOOM_FIT, GTK_ICON_SIZE_MENU) );
165a4fa9
HR
3067 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_wikipedia_wp_layer), pass_along );
3068 gtk_menu_shell_append (GTK_MENU_SHELL (wikipedia_submenu), item);
3069 gtk_widget_show ( item );
3070
d6de71f9
RN
3071 item = gtk_image_menu_item_new_with_mnemonic ( _("Within _Current View") );
3072 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ZOOM_100, GTK_ICON_SIZE_MENU) );
165a4fa9
HR
3073 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_wikipedia_wp_viewport), pass_along );
3074 gtk_menu_shell_append (GTK_MENU_SHELL (wikipedia_submenu), item);
3075 gtk_widget_show ( item );
da03b29d 3076#endif
165a4fa9 3077
b3eb3b98
RN
3078#ifdef VIK_CONFIG_GEOTAG
3079 item = gtk_menu_item_new_with_mnemonic ( _("Geotag _Images...") );
3080 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_geotagging), pass_along );
3081 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3082 gtk_widget_show ( item );
3083#endif
3084
16fc32f6 3085 GtkWidget *acquire_submenu = gtk_menu_new ();
d6de71f9
RN
3086 item = gtk_image_menu_item_new_with_mnemonic ( _("Ac_quire") );
3087 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_DOWN, GTK_ICON_SIZE_MENU) );
16fc32f6
RN
3088 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3089 gtk_widget_show ( item );
3090 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), acquire_submenu );
3091
3092 item = gtk_menu_item_new_with_mnemonic ( _("From _GPS...") );
3093 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_acquire_gps_cb), pass_along );
3094 gtk_menu_shell_append (GTK_MENU_SHELL (acquire_submenu), item);
3095 gtk_widget_show ( item );
3096
55340efa 3097#ifdef VIK_CONFIG_GOOGLE
16fc32f6
RN
3098 item = gtk_menu_item_new_with_mnemonic ( _("From G_oogle Directions...") );
3099 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_acquire_google_cb), pass_along );
3100 gtk_menu_shell_append (GTK_MENU_SHELL (acquire_submenu), item);
3101 gtk_widget_show ( item );
29332250 3102#endif
16fc32f6 3103
40f5740b
RN
3104#ifdef VIK_CONFIG_OPENSTREETMAP
3105 item = gtk_menu_item_new_with_mnemonic ( _("From _OSM Traces...") );
3106 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_acquire_osm_cb), pass_along );
3107 gtk_menu_shell_append (GTK_MENU_SHELL (acquire_submenu), item);
3108 gtk_widget_show ( item );
3109#endif
3110
16fc32f6
RN
3111#ifdef VIK_CONFIG_GEOCACHES
3112 item = gtk_menu_item_new_with_mnemonic ( _("From Geo_caching...") );
3113 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_acquire_geocache_cb), pass_along );
3114 gtk_menu_shell_append (GTK_MENU_SHELL (acquire_submenu), item);
3115 gtk_widget_show ( item );
3116#endif
3117
68bab1bd
RN
3118#ifdef VIK_CONFIG_GEOTAG
3119 item = gtk_menu_item_new_with_mnemonic ( _("From Geotagged _Images...") );
3120 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_acquire_geotagged_cb), pass_along );
3121 gtk_menu_shell_append (GTK_MENU_SHELL (acquire_submenu), item);
3122 gtk_widget_show ( item );
3123#endif
3124
d7ac7564
RN
3125 item = gtk_menu_item_new_with_mnemonic ( _("From _File...") );
3126 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_acquire_file_cb), pass_along );
3127 gtk_menu_shell_append (GTK_MENU_SHELL (acquire_submenu), item);
3128 gtk_widget_show ( item );
3129
e50758c7
RN
3130 GtkWidget *upload_submenu = gtk_menu_new ();
3131 item = gtk_image_menu_item_new_with_mnemonic ( _("_Upload") );
3132 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_UP, GTK_ICON_SIZE_MENU) );
3133 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3134 gtk_widget_show ( item );
3135 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), upload_submenu );
3136
3e7553ae 3137#ifdef VIK_CONFIG_OPENSTREETMAP
d6de71f9
RN
3138 item = gtk_image_menu_item_new_with_mnemonic ( _("Upload to _OSM...") );
3139 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_UP, GTK_ICON_SIZE_MENU) );
3e7553ae 3140 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(osm_traces_upload_cb), pass_along );
e50758c7 3141 gtk_menu_shell_append (GTK_MENU_SHELL (upload_submenu), item);
3e7553ae
GB
3142 gtk_widget_show ( item );
3143#endif
28c82d8b 3144
e50758c7
RN
3145 item = gtk_image_menu_item_new_with_mnemonic ( _("Upload to _GPS...") );
3146 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_FORWARD, GTK_ICON_SIZE_MENU) );
3147 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_gps_upload), pass_along );
3148 gtk_menu_shell_append (GTK_MENU_SHELL (upload_submenu), item);
3149 gtk_widget_show ( item );
3150
c9a5cbf9 3151 GtkWidget *delete_submenu = gtk_menu_new ();
d6de71f9
RN
3152 item = gtk_image_menu_item_new_with_mnemonic ( _("De_lete") );
3153 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU) );
c9a5cbf9
RN
3154 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3155 gtk_widget_show ( item );
3156 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), delete_submenu );
3157
d6de71f9
RN
3158 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete All _Tracks") );
3159 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU) );
c9a5cbf9
RN
3160 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_all_tracks), pass_along );
3161 gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
3162 gtk_widget_show ( item );
3163
d6de71f9
RN
3164 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete Tracks _From Selection...") );
3165 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
20b671c3
RN
3166 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_tracks_from_selection), pass_along );
3167 gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
3168 gtk_widget_show ( item );
3169
d6de71f9
RN
3170 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete All _Waypoints") );
3171 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU) );
c9a5cbf9
RN
3172 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_all_waypoints), pass_along );
3173 gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
3174 gtk_widget_show ( item );
3175
d6de71f9
RN
3176 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete Waypoints From _Selection...") );
3177 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
20b671c3
RN
3178 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_waypoints_from_selection), pass_along );
3179 gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
3180 gtk_widget_show ( item );
3181
28c82d8b 3182 item = a_acquire_trwlayer_menu ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)), vlp,
d65659e7 3183 vik_layers_panel_get_viewport(VIK_LAYERS_PANEL(vlp)), vtl );
28c82d8b
EB
3184 if ( item ) {
3185 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3186 gtk_widget_show ( item );
3187 }
3188
3189 item = a_acquire_trwlayer_track_menu ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)), vlp,
d65659e7 3190 vik_layers_panel_get_viewport(VIK_LAYERS_PANEL(vlp)), vtl );
28c82d8b
EB
3191 if ( item ) {
3192 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3193 gtk_widget_show ( item );
3194 }
50a14534
EB
3195}
3196
c9570f86
RN
3197// Fake Waypoint UUIDs vi simple increasing integer
3198static guint wp_uuid = 0;
3199
50a14534
EB
3200void vik_trw_layer_add_waypoint ( VikTrwLayer *vtl, gchar *name, VikWaypoint *wp )
3201{
c9570f86
RN
3202 wp_uuid++;
3203
3204 vik_waypoint_set_name (wp, name);
3205
50a14534
EB
3206 if ( VIK_LAYER(vtl)->realized )
3207 {
c9570f86
RN
3208 GtkTreeIter *iter = g_malloc(sizeof(GtkTreeIter));
3209
3210 // Visibility column always needed for waypoints
50a14534 3211#ifdef VIK_CONFIG_ALPHABETIZED_TRW
c9570f86 3212 vik_treeview_add_sublayer_alphabetized ( VIK_LAYER(vtl)->vt, &(vtl->waypoints_iter), iter, name, vtl, GUINT_TO_POINTER(wp_uuid), VIK_TRW_LAYER_SUBLAYER_WAYPOINT, NULL, TRUE, TRUE );
50a14534 3213#else
ce4bd1cf 3214 vik_treeview_add_sublayer ( VIK_LAYER(vtl)->vt, &(vtl->waypoints_iter), iter, name, vtl, GUINT_TO_POINTER(wp_uuid), VIK_TRW_LAYER_SUBLAYER_WAYPOINT, NULL, TRUE, TRUE );
50a14534 3215#endif
c9570f86
RN
3216 // Actual setting of visibility dependent on the waypoint
3217 vik_treeview_item_set_visible ( VIK_LAYER(vtl)->vt, iter, wp->visible );
3218
3219 g_hash_table_insert ( vtl->waypoints_iters, GUINT_TO_POINTER(wp_uuid), iter );
50a14534 3220 }
50a14534 3221
a8fe53f8 3222 highest_wp_number_add_wp(vtl, name);
c9570f86 3223 g_hash_table_insert ( vtl->waypoints, GUINT_TO_POINTER(wp_uuid), wp );
50a14534
EB
3224
3225}
3226
ce4bd1cf
RN
3227// Fake Track UUIDs vi simple increasing integer
3228static guint tr_uuid = 0;
3229
50a14534
EB
3230void vik_trw_layer_add_track ( VikTrwLayer *vtl, gchar *name, VikTrack *t )
3231{
ce4bd1cf
RN
3232 tr_uuid++;
3233
3234 vik_track_set_name (t, name);
3235
50a14534
EB
3236 if ( VIK_LAYER(vtl)->realized )
3237 {
ce4bd1cf
RN
3238 GtkTreeIter *iter = g_malloc(sizeof(GtkTreeIter));
3239 // Visibility column always needed for tracks
50a14534 3240#ifdef VIK_CONFIG_ALPHABETIZED_TRW
ce4bd1cf 3241 vik_treeview_add_sublayer_alphabetized ( VIK_LAYER(vtl)->vt, &(vtl->tracks_iter), iter, name, vtl, GUINT_TO_POINTER(tr_uuid), VIK_TRW_LAYER_SUBLAYER_TRACK, NULL, TRUE, TRUE );
50a14534 3242#else
ce4bd1cf 3243 vik_treeview_add_sublayer ( VIK_LAYER(vtl)->vt, &(vtl->tracks_iter), iter, name, vtl, GUINT_TO_POINTER(tr_uuid), VIK_TRW_LAYER_SUBLAYER_TRACK, NULL, TRUE, TRUE );
50a14534 3244#endif
ce4bd1cf
RN
3245 // Actual setting of visibility dependent on the track
3246 vik_treeview_item_set_visible ( VIK_LAYER(vtl)->vt, iter, t->visible );
3247
3248 g_hash_table_insert ( vtl->tracks_iters, GUINT_TO_POINTER(tr_uuid), iter );
50a14534 3249 }
50a14534 3250
ce4bd1cf 3251 g_hash_table_insert ( vtl->tracks, GUINT_TO_POINTER(tr_uuid), t );
50a14534
EB
3252
3253}
3254
50a14534 3255/* to be called whenever a track has been deleted or may have been changed. */
ce4bd1cf 3256void trw_layer_cancel_tps_of_track ( VikTrwLayer *vtl, VikTrack *trk )
50a14534 3257{
ce4bd1cf 3258 if (vtl->current_tp_track == trk )
50a14534 3259 trw_layer_cancel_current_tp ( vtl, FALSE );
50a14534 3260}
e890a6e6 3261
9748531a 3262gchar *trw_layer_new_unique_sublayer_name (VikTrwLayer *vtl, gint sublayer_type, const gchar *name)
e890a6e6
EB
3263{
3264 gint i = 2;
3265 gchar *newname = g_strdup(name);
3266 while ((sublayer_type == VIK_TRW_LAYER_SUBLAYER_TRACK) ?
941aa6e9 3267 (void *)vik_trw_layer_get_track(vtl, newname) : (void *)vik_trw_layer_get_waypoint(vtl, newname)) {
e890a6e6
EB
3268 gchar *new_newname = g_strdup_printf("%s#%d", name, i);
3269 g_free(newname);
3270 newname = new_newname;
3271 i++;
3272 }
3273 return newname;
3274}
50a14534 3275
805d282e
EB
3276void vik_trw_layer_filein_add_waypoint ( VikTrwLayer *vtl, gchar *name, VikWaypoint *wp )
3277{
c9570f86
RN
3278 // No more uniqueness of name forced when loading from a file
3279 // This now makes this function a little redunant as we just flow the parameters through
3280 vik_trw_layer_add_waypoint ( vtl, name, wp );
805d282e 3281}
c9570f86 3282
805d282e
EB
3283void vik_trw_layer_filein_add_track ( VikTrwLayer *vtl, gchar *name, VikTrack *tr )
3284{
7ff7d728 3285 if ( vtl->route_finder_append && vtl->route_finder_current_track ) {
c3deba01 3286 vik_track_remove_dup_points ( tr ); /* make "double point" track work to undo */
7ff7d728 3287 vik_track_steal_and_append_trackpoints ( vtl->route_finder_current_track, tr );
bddd2056 3288 vik_track_free ( tr );
7ff7d728 3289 vtl->route_finder_append = FALSE; /* this means we have added it */
bddd2056 3290 } else {
ce4bd1cf
RN
3291
3292 // No more uniqueness of name forced when loading from a file
3293 vik_trw_layer_add_track ( vtl, name, tr );
bddd2056 3294
7ff7d728 3295 if ( vtl->route_finder_check_added_track ) {
c3deba01 3296 vik_track_remove_dup_points ( tr ); /* make "double point" track work to undo */
ce4bd1cf 3297 vtl->route_finder_added_track = tr;
bddd2056
EB
3298 }
3299 }
805d282e
EB
3300}
3301
ce4bd1cf 3302static void trw_layer_enum_item ( gpointer id, GList **tr, GList **l )
c48517ad 3303{
ce4bd1cf 3304 *l = g_list_append(*l, id);
c48517ad
AF
3305}
3306
ce4bd1cf
RN
3307/*
3308 * Move an item from one TRW layer to another TRW layer
3309 */
3310static void trw_layer_move_item ( VikTrwLayer *vtl_src, VikTrwLayer *vtl_dest, gpointer id, gint type )
c48517ad 3311{
c48517ad 3312 if (type == VIK_TRW_LAYER_SUBLAYER_TRACK) {
ce4bd1cf
RN
3313 VikTrack *trk = g_hash_table_lookup ( vtl_src->tracks, id );
3314
9748531a 3315 gchar *newname = trw_layer_new_unique_sublayer_name(vtl_dest, type, trk->name);
ce4bd1cf
RN
3316
3317 VikTrack *trk2 = vik_track_copy ( trk );
3318 vik_trw_layer_add_track ( vtl_dest, newname, trk2 );
3319 vik_trw_layer_delete_track ( vtl_src, trk );
c48517ad 3320 }
c9570f86 3321
ce4bd1cf
RN
3322 if (type == VIK_TRW_LAYER_SUBLAYER_WAYPOINT) {
3323 VikWaypoint *wp = g_hash_table_lookup ( vtl_src->waypoints, id );
c9570f86 3324
9748531a 3325 gchar *newname = trw_layer_new_unique_sublayer_name(vtl_dest, type, wp->name);
c9570f86 3326
ce4bd1cf
RN
3327 VikWaypoint *wp2 = vik_waypoint_copy ( wp );
3328 vik_trw_layer_add_waypoint ( vtl_dest, newname, wp2 );
c9570f86 3329 trw_layer_delete_waypoint ( vtl_src, wp );
c48517ad
AF
3330 }
3331}
3332
70a23263 3333static void trw_layer_drag_drop_request ( VikTrwLayer *vtl_src, VikTrwLayer *vtl_dest, GtkTreeIter *src_item_iter, GtkTreePath *dest_path )
e4afc73a
EB
3334{
3335 VikTreeview *vt = VIK_LAYER(vtl_src)->vt;
c48517ad
AF
3336 gint type = vik_treeview_item_get_data(vt, src_item_iter);
3337
e4afc73a 3338 if (!vik_treeview_item_get_pointer(vt, src_item_iter)) {
b637058e
EB
3339 GList *items = NULL;
3340 GList *iter;
e4afc73a 3341
c48517ad
AF
3342 if (type==VIK_TRW_LAYER_SUBLAYER_TRACKS) {
3343 g_hash_table_foreach ( vtl_src->tracks, (GHFunc)trw_layer_enum_item, &items);
3344 }
3345 if (type==VIK_TRW_LAYER_SUBLAYER_WAYPOINTS) {
3346 g_hash_table_foreach ( vtl_src->waypoints, (GHFunc)trw_layer_enum_item, &items);
3347 }
3348
3349 iter = items;
3350 while (iter) {
3351 if (type==VIK_TRW_LAYER_SUBLAYER_TRACKS) {
3352 trw_layer_move_item ( vtl_src, vtl_dest, iter->data, VIK_TRW_LAYER_SUBLAYER_TRACK);
3353 } else {
3354 trw_layer_move_item ( vtl_src, vtl_dest, iter->data, VIK_TRW_LAYER_SUBLAYER_WAYPOINT);
3355 }
3356 iter = iter->next;
e4afc73a 3357 }
c48517ad 3358 if (items)
b637058e 3359 g_list_free(items);
c48517ad
AF
3360 } else {
3361 gchar *name = vik_treeview_item_get_pointer(vt, src_item_iter);
3362 trw_layer_move_item(vtl_src, vtl_dest, name, type);
e4afc73a
EB
3363 }
3364}
3365
ce4bd1cf
RN
3366typedef struct {
3367 VikTrack *trk; // input
3368 gpointer uuid; // output
3369} trku_udata;
3370
3371static gboolean trw_layer_track_find_uuid ( const gpointer id, const VikTrack *trk, gpointer udata )
3372{
3373 trku_udata *user_data = udata;
3374 if ( trk == user_data->trk ) {
3375 user_data->uuid = id;
3376 return TRUE;
3377 }
3378 return FALSE;
3379}
3380
3381gboolean vik_trw_layer_delete_track ( VikTrwLayer *vtl, VikTrack *trk )
50a14534 3382{
50a14534 3383 gboolean was_visible = FALSE;
ce4bd1cf
RN
3384
3385 if ( trk && trk->name ) {
3386
3387 if ( trk == vtl->current_track ) {
50a14534 3388 vtl->current_track = NULL;
ce4bd1cf
RN
3389 vtl->current_tp_track = NULL;
3390 vtl->current_tp_id = NULL;
3391 vtl->moving_tp = FALSE;
77ad64fa 3392 }
ce4bd1cf
RN
3393
3394 was_visible = trk->visible;
3395
3396 if ( trk == vtl->route_finder_current_track )
7ff7d728
RN
3397 vtl->route_finder_current_track = NULL;
3398
ce4bd1cf
RN
3399 if ( trk == vtl->route_finder_added_track )
3400 vtl->route_finder_added_track = NULL;
3401
3402 trku_udata udata;
3403 udata.trk = trk;
3404 udata.uuid = NULL;
50a14534 3405
ce4bd1cf
RN
3406 // Hmmm, want key of it
3407 gpointer *trkf = g_hash_table_find ( vtl->tracks, (GHRFunc) trw_layer_track_find_uuid, &udata );
50a14534 3408
ce4bd1cf
RN
3409 if ( trkf && udata.uuid ) {
3410 /* could be current_tp, so we have to check */
3411 trw_layer_cancel_tps_of_track ( vtl, trk );
3412
3413 GtkTreeIter *it = g_hash_table_lookup ( vtl->tracks_iters, udata.uuid );
3414
3415 if ( it ) {
3416 vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, it );
3417 g_hash_table_remove ( vtl->tracks_iters, udata.uuid );
3418 g_hash_table_remove ( vtl->tracks, udata.uuid );
3419 }
3420 }
50a14534
EB
3421 }
3422 return was_visible;
3423}
3424
c9570f86 3425static gboolean trw_layer_delete_waypoint ( VikTrwLayer *vtl, VikWaypoint *wp )
e4afc73a
EB
3426{
3427 gboolean was_visible = FALSE;
e4afc73a 3428
c9570f86 3429 if ( wp && wp->name ) {
e4afc73a
EB
3430
3431 if ( wp == vtl->current_wp ) {
3432 vtl->current_wp = NULL;
c9570f86 3433 vtl->current_wp_id = NULL;
e4afc73a
EB
3434 vtl->moving_wp = FALSE;
3435 }
3436
3437 was_visible = wp->visible;
c9570f86
RN
3438
3439 wpu_udata udata;
3440 udata.wp = wp;
3441 udata.uuid = NULL;
3442
3443 // Hmmm, want key of it
3444 gpointer *wpf = g_hash_table_find ( vtl->waypoints, (GHRFunc) trw_layer_waypoint_find_uuid, (gpointer) &udata );
3445
3446 if ( wpf && udata.uuid ) {
3447 GtkTreeIter *it = g_hash_table_lookup ( vtl->waypoints_iters, udata.uuid );
3448
3449 if ( it ) {
3450 vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, it );
3451 g_hash_table_remove ( vtl->waypoints_iters, udata.uuid );
3452
3453 highest_wp_number_remove_wp(vtl, wp->name);
3454 g_hash_table_remove ( vtl->waypoints, udata.uuid ); // last because this frees the name
3455 }
3456 }
a8fe53f8 3457
e4afc73a
EB
3458 }
3459
3460 return was_visible;
3461}
3462
c9570f86
RN
3463// Only for temporary use by trw_layer_delete_waypoint_by_name
3464static gboolean trw_layer_waypoint_find_uuid_by_name ( const gpointer id, const VikWaypoint *wp, gpointer udata )
3465{
3466 wpu_udata *user_data = udata;
3467 if ( ! strcmp ( wp->name, user_data->wp->name ) ) {
3468 user_data->uuid = id;
3469 return TRUE;
3470 }
3471 return FALSE;
3472}
3473
3474/*
3475 * Delete a waypoint by the given name
3476 * NOTE: ATM this will delete the first encountered Waypoint with the specified name
3477 * as there be multiple waypoints with the same name
3478 */
3479static gboolean trw_layer_delete_waypoint_by_name ( VikTrwLayer *vtl, const gchar *name )
3480{
3481 wpu_udata udata;
3482 // Fake a waypoint with the given name
3483 udata.wp = vik_waypoint_new ();
3484 vik_waypoint_set_name (udata.wp, name);
3485 // Currently only the name is used in this waypoint find function
3486 udata.uuid = NULL;
3487
3488 // Hmmm, want key of it
3489 gpointer *wpf = g_hash_table_find ( vtl->waypoints, (GHRFunc) trw_layer_waypoint_find_uuid_by_name, (gpointer) &udata );
3490
3491 vik_waypoint_free (udata.wp);
3492
3493 if ( wpf && udata.uuid )
3494 return trw_layer_delete_waypoint (vtl, g_hash_table_lookup ( vtl->waypoints, udata.uuid ));
3495 else
3496 return FALSE;
3497}
3498
ce4bd1cf
RN
3499typedef struct {
3500 VikTrack *trk; // input
3501 gpointer uuid; // output
3502} tpu_udata;
3503
3504// Only for temporary use by trw_layer_delete_track_by_name
3505static gboolean trw_layer_track_find_uuid_by_name ( const gpointer id, const VikTrack *trk, gpointer udata )
3506{
3507 tpu_udata *user_data = udata;
3508 if ( ! strcmp ( trk->name, user_data->trk->name ) ) {
3509 user_data->uuid = id;
3510 return TRUE;
3511 }
3512 return FALSE;
3513}
3514
3515/*
3516 * Delete a track by the given name
3517 * NOTE: ATM this will delete the first encountered Track with the specified name
3518 * as there be multiple track with the same name
3519 */
3520static gboolean trw_layer_delete_track_by_name ( VikTrwLayer *vtl, const gchar *name )
3521{
3522 tpu_udata udata;
3523 // Fake a track with the given name
3524 udata.trk = vik_track_new ();
3525 vik_track_set_name (udata.trk, name);
3526 // Currently only the name is used in this waypoint find function
3527 udata.uuid = NULL;
3528
3529 // Hmmm, want key of it
3530 gpointer *trkf = g_hash_table_find ( vtl->tracks, (GHRFunc) trw_layer_track_find_uuid_by_name, &udata );
3531
3532 vik_track_free (udata.trk);
3533
3534 if ( trkf && udata.uuid )
3535 return vik_trw_layer_delete_track (vtl, g_hash_table_lookup ( vtl->tracks, udata.uuid ));
3536 else
3537 return FALSE;
3538}
3539
700b0908
QT
3540static void remove_item_from_treeview(const gchar *name, GtkTreeIter *it, VikTreeview * vt)
3541{
3542 vik_treeview_item_delete (vt, it );
3543}
3544
3545void vik_trw_layer_delete_all_tracks ( VikTrwLayer *vtl )
3546{
3547
3548 vtl->current_track = NULL;
7ff7d728 3549 vtl->route_finder_current_track = NULL;
ce4bd1cf
RN
3550 vtl->route_finder_added_track = NULL;
3551 if (vtl->current_tp_track)
700b0908 3552 trw_layer_cancel_current_tp(vtl, FALSE);
700b0908
QT
3553
3554 g_hash_table_foreach(vtl->tracks_iters, (GHFunc) remove_item_from_treeview, VIK_LAYER(vtl)->vt);
3555 g_hash_table_remove_all(vtl->tracks_iters);
3556 g_hash_table_remove_all(vtl->tracks);
3557
f01eebd4 3558 vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
700b0908
QT
3559}
3560
3561void vik_trw_layer_delete_all_waypoints ( VikTrwLayer *vtl )
3562{
3563 vtl->current_wp = NULL;
c9570f86 3564 vtl->current_wp_id = NULL;
700b0908
QT
3565 vtl->moving_wp = FALSE;
3566
a8fe53f8
EB
3567 highest_wp_number_reset(vtl);
3568
700b0908
QT
3569 g_hash_table_foreach(vtl->waypoints_iters, (GHFunc) remove_item_from_treeview, VIK_LAYER(vtl)->vt);
3570 g_hash_table_remove_all(vtl->waypoints_iters);
3571 g_hash_table_remove_all(vtl->waypoints);
3572
f01eebd4 3573 vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
700b0908
QT
3574}
3575
c9a5cbf9
RN
3576static void trw_layer_delete_all_tracks ( gpointer lav[2] )
3577{
3578 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
3579 // Get confirmation from the user
3580 if ( a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
3581 _("Are you sure you want to delete all tracks in %s?"),
3582 vik_layer_get_name ( VIK_LAYER(vtl) ) ) )
3583 vik_trw_layer_delete_all_tracks (vtl);
3584}
3585
3586static void trw_layer_delete_all_waypoints ( gpointer lav[2] )
3587{
3588 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
3589 // Get confirmation from the user
3590 if ( a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
3591 _("Are you sure you want to delete all waypoints in %s?"),
3592 vik_layer_get_name ( VIK_LAYER(vtl) ) ) )
3593 vik_trw_layer_delete_all_waypoints (vtl);
3594}
3595
6bb72350 3596static void trw_layer_delete_item ( gpointer pass_along[6] )
50a14534
EB
3597{
3598 VikTrwLayer *vtl = VIK_TRW_LAYER(pass_along[0]);
3599 gboolean was_visible = FALSE;
dc2c040e 3600 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_WAYPOINT )
50a14534 3601 {
c9570f86
RN
3602 VikWaypoint *wp = g_hash_table_lookup ( vtl->waypoints, pass_along[3] );
3603 if ( wp && wp->name ) {
3604 if ( GPOINTER_TO_INT ( pass_along[4]) )
3605 // Get confirmation from the user
3606 // Maybe this Waypoint Delete should be optional as is it could get annoying...
3607 if ( ! a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
3608 _("Are you sure you want to delete the waypoint \"%s\""),
3609 wp->name ) )
3610 return;
3611 was_visible = trw_layer_delete_waypoint ( vtl, wp );
3612 }
50a14534
EB
3613 }
3614 else
3615 {
ce4bd1cf
RN
3616 VikTrack *trk = g_hash_table_lookup ( vtl->tracks, pass_along[3] );
3617 if ( trk && trk->name ) {
3618 if ( GPOINTER_TO_INT ( pass_along[4]) )
3619 // Get confirmation from the user
3620 if ( ! a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
169acf64 3621 _("Are you sure you want to delete the track \"%s\""),
ce4bd1cf
RN
3622 trk->name ) )
3623 return;
3624 was_visible = vik_trw_layer_delete_track ( vtl, trk );
3625 }
50a14534 3626 }
50a14534 3627 if ( was_visible )
f01eebd4 3628 vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
50a14534
EB
3629}
3630
3631
c9570f86 3632static void trw_layer_properties_item ( gpointer pass_along[7] )
50a14534
EB
3633{
3634 VikTrwLayer *vtl = VIK_TRW_LAYER(pass_along[0]);
dc2c040e 3635 if ( GPOINTER_TO_INT (pass_along[2]) == VIK_TRW_LAYER_SUBLAYER_WAYPOINT )
50a14534 3636 {
c9570f86
RN
3637 VikWaypoint *wp = g_hash_table_lookup ( vtl->waypoints, pass_along[3] ); // sublayer
3638
3639 if ( wp && wp->name )
50a14534 3640 {
ac1bde8b 3641 gboolean updated = FALSE;
c9570f86 3642 a_dialog_waypoint ( VIK_GTK_WINDOW_FROM_LAYER(vtl), wp->name, wp, vtl->coord_mode, FALSE, &updated );
50a14534 3643
ac1bde8b 3644 if ( updated && VIK_LAYER(vtl)->visible )
f01eebd4 3645 vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
50a14534
EB
3646 }
3647 }
3648 else
3649 {
3650 VikTrack *tr = g_hash_table_lookup ( vtl->tracks, pass_along[3] );
ce4bd1cf 3651 if ( tr && tr->name )
50a14534 3652 {
21700912 3653 vik_trw_layer_propwin_run ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
6bb72350
RN
3654 vtl, tr,
3655 pass_along[1], /* vlp */
6bb72350 3656 pass_along[5] ); /* vvp */
50a14534
EB
3657 }
3658 }
3659}
3660
6bb72350
RN
3661/*
3662 Parameter 1 -> VikLayersPanel
3663 Parameter 2 -> VikLayer
3664 Parameter 3 -> VikViewport
3665*/
3666static void goto_coord ( gpointer *vlp, gpointer vl, gpointer vvp, const VikCoord *coord )
3667{
3668 if ( vlp ) {
3669 vik_viewport_set_center_coord ( vik_layers_panel_get_viewport (VIK_LAYERS_PANEL(vlp)), coord );
3670 vik_layers_panel_emit_update ( VIK_LAYERS_PANEL(vlp) );
3671 }
3672 else {
3673 /* since vlp not set, vl & vvp should be valid instead! */
3674 if ( vl && vvp ) {
3675 vik_viewport_set_center_coord ( VIK_VIEWPORT(vvp), coord );
f01eebd4 3676 vik_layer_emit_update ( VIK_LAYER(vl), FALSE );
6bb72350
RN
3677 }
3678 }
50a14534
EB
3679}
3680
6bb72350 3681static void trw_layer_goto_track_startpoint ( gpointer pass_along[6] )
50a14534
EB
3682{
3683 GList *trps = ((VikTrack *) g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, pass_along[3] ))->trackpoints;
3684 if ( trps && trps->data )
6bb72350 3685 goto_coord ( pass_along[1], pass_along[0], pass_along[5], &(((VikTrackpoint *) trps->data)->coord));
50a14534
EB
3686}
3687
6bb72350 3688static void trw_layer_goto_track_center ( gpointer pass_along[6] )
50a14534 3689{
867f4673
RN
3690 VikTrack *track = g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, pass_along[3] );
3691 if ( track && track->trackpoints )
50a14534
EB
3692 {
3693 struct LatLon average, maxmin[2] = { {0,0}, {0,0} };
3694 VikCoord coord;
867f4673 3695 trw_layer_find_maxmin_tracks ( NULL, track, maxmin );
50a14534
EB
3696 average.lat = (maxmin[0].lat+maxmin[1].lat)/2;
3697 average.lon = (maxmin[0].lon+maxmin[1].lon)/2;
3698 vik_coord_load_from_latlon ( &coord, VIK_TRW_LAYER(pass_along[0])->coord_mode, &average );
6bb72350 3699 goto_coord ( pass_along[1], pass_along[0], pass_along[5], &coord);
50a14534
EB
3700 }
3701}
3702
8fb71d6c
EB
3703static void trw_layer_extend_track_end ( gpointer pass_along[6] )
3704{
3705 VikTrwLayer *vtl = VIK_TRW_LAYER(pass_along[0]);
3706 VikTrack *track = g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, pass_along[3] );
3707
3708 vtl->current_track = track;
a7955c1d 3709 vik_window_enable_layer_tool ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)), VIK_LAYER_TRW, TOOL_CREATE_TRACK);
8fb71d6c
EB
3710
3711 if ( track->trackpoints )
6bb72350 3712 goto_coord ( pass_along[1], pass_along[0], pass_along[5], &(((VikTrackpoint *)g_list_last(track->trackpoints)->data)->coord) );
8fb71d6c
EB
3713}
3714
55340efa 3715#ifdef VIK_CONFIG_GOOGLE
e3154bef 3716/**
7ff7d728 3717 * extend a track using route finder
e3154bef 3718 */
7ff7d728 3719static void trw_layer_extend_track_end_route_finder ( gpointer pass_along[6] )
a7955c1d
AM
3720{
3721 VikTrwLayer *vtl = VIK_TRW_LAYER(pass_along[0]);
3722 VikTrack *track = g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, pass_along[3] );
3723 VikCoord last_coord = (((VikTrackpoint *)g_list_last(track->trackpoints)->data)->coord);
3724
3725 vik_window_enable_layer_tool ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)), VIK_LAYER_TRW, NUM_TOOLS );
7ff7d728
RN
3726 vtl->route_finder_coord = last_coord;
3727 vtl->route_finder_current_track = track;
3728 vtl->route_finder_started = TRUE;
a7955c1d
AM
3729
3730 if ( track->trackpoints )
6bb72350 3731 goto_coord ( pass_along[1], pass_along[0], pass_along[5], &last_coord) ;
a7955c1d
AM
3732
3733}
ebf1bd39 3734#endif
a7955c1d 3735
ad0a8c2d
EB
3736static void trw_layer_apply_dem_data ( gpointer pass_along[6] )
3737{
3738 /* TODO: check & warn if no DEM data, or no applicable DEM data. */
3739 /* Also warn if overwrite old elevation data */
3740 VikTrack *track = (VikTrack *) g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, pass_along[3] );
3741
55906514 3742 vik_track_apply_dem_data ( track );
ad0a8c2d
EB
3743}
3744
50a14534
EB
3745static void trw_layer_goto_track_endpoint ( gpointer pass_along[6] )
3746{
3747 GList *trps = ((VikTrack *) g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, pass_along[3] ))->trackpoints;
3748 if ( !trps )
3749 return;
111fa174 3750 trps = g_list_last(trps);
6bb72350 3751 goto_coord ( pass_along[1], pass_along[0], pass_along[5], &(((VikTrackpoint *) trps->data)->coord));
50a14534
EB
3752}
3753
6bb72350 3754static void trw_layer_goto_track_max_speed ( gpointer pass_along[6] )
03e7da75
RN
3755{
3756 VikTrackpoint* vtp = vik_track_get_tp_by_max_speed ( g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, pass_along[3] ) );
3757 if ( !vtp )
3758 return;
6bb72350 3759 goto_coord ( pass_along[1], pass_along[0], pass_along[5], &(vtp->coord));
03e7da75
RN
3760}
3761
6bb72350 3762static void trw_layer_goto_track_max_alt ( gpointer pass_along[6] )
c28faca8
RN
3763{
3764 VikTrackpoint* vtp = vik_track_get_tp_by_max_alt ( g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, pass_along[3] ) );
3765 if ( !vtp )
3766 return;
6bb72350 3767 goto_coord ( pass_along[1], pass_along[0], pass_along[5], &(vtp->coord));
c28faca8
RN
3768}
3769
6bb72350 3770static void trw_layer_goto_track_min_alt ( gpointer pass_along[6] )
c28faca8
RN
3771{
3772 VikTrackpoint* vtp = vik_track_get_tp_by_min_alt ( g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, pass_along[3] ) );
3773 if ( !vtp )
3774 return;
6bb72350 3775 goto_coord ( pass_along[1], pass_along[0], pass_along[5], &(vtp->coord));
c28faca8 3776}
111fa174 3777
116ea336 3778/*
469113fb 3779 * Automatically change the viewport to center on the track and zoom to see the extent of the track
116ea336
RN
3780 */
3781static void trw_layer_auto_track_view ( gpointer pass_along[6] )
469113fb 3782{
867f4673
RN
3783 VikTrack *trk = g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, pass_along[3] );
3784 if ( trk && trk->trackpoints )
469113fb
RN
3785 {
3786 struct LatLon maxmin[2] = { {0,0}, {0,0} };
867f4673 3787 trw_layer_find_maxmin_tracks ( NULL, trk, maxmin );
6bb72350
RN
3788 trw_layer_zoom_to_show_latlons ( VIK_TRW_LAYER(pass_along[0]), pass_along[5], maxmin );
3789 if ( pass_along[1] )
3790 vik_layers_panel_emit_update ( VIK_LAYERS_PANEL(pass_along[1]) );
3791 else
f01eebd4 3792 vik_layer_emit_update ( VIK_LAYER(pass_along[0]), FALSE );
469113fb
RN
3793 }
3794}
3795
c95d6b00
RN
3796static void trw_layer_edit_trackpoint ( gpointer pass_along[6] )
3797{
3798 VikTrwLayer *vtl = VIK_TRW_LAYER(pass_along[0]);
3799 trw_layer_tpwin_init ( vtl );
3800}
3801
111fa174
AF
3802/*************************************
3803 * merge/split by time routines
3804 *************************************/
3805
15f45edc 3806/* called for each key in track hash table.
5780603d 3807 * If the current track has the same time stamp type, add it to the result,
15f45edc
QT
3808 * except the one pointed by "exclude".
3809 * set exclude to NULL if there is no exclude to check.
5780603d 3810 * Note that the result is in reverse (for performance reasons).
15f45edc
QT
3811 */
3812typedef struct {
3813 GList **result;
3814 GList *exclude;
5780603d 3815 gboolean with_timestamps;
15f45edc 3816} twt_udata;
5780603d 3817static void find_tracks_with_timestamp_type(gpointer key, gpointer value, gpointer udata)
15f45edc
QT
3818{
3819 twt_udata *user_data = udata;
3820 VikTrackpoint *p1, *p2;
3821
3822 if (VIK_TRACK(value)->trackpoints == user_data->exclude) {
3823 return;
3824 }
3825
3826 if (VIK_TRACK(value)->trackpoints) {
3827 p1 = VIK_TRACKPOINT(VIK_TRACK(value)->trackpoints->data);
3828 p2 = VIK_TRACKPOINT(g_list_last(VIK_TRACK(value)->trackpoints)->data);
3829
5780603d
RN
3830 if ( user_data->with_timestamps ) {
3831 if (!p1->has_timestamp || !p2->has_timestamp) {
3832 return;
3833 }
3834 }
3835 else {
3836 // Don't add tracks with timestamps when getting non timestamp tracks
3837 if (p1->has_timestamp || p2->has_timestamp) {
3838 return;
3839 }
15f45edc 3840 }
15f45edc
QT
3841 }
3842
3843 *(user_data->result) = g_list_prepend(*(user_data->result), key);
3844}
3845
111fa174
AF
3846/* called for each key in track hash table. if original track user_data[1] is close enough
3847 * to the passed one, add it to list in user_data[0]
3848 */
fc92c977 3849static void find_nearby_tracks_by_time (gpointer key, gpointer value, gpointer user_data)
111fa174
AF
3850{
3851 time_t t1, t2;
3852 VikTrackpoint *p1, *p2;
fc92c977 3853 VikTrack *trk = VIK_TRACK(value);
111fa174
AF
3854
3855 GList **nearby_tracks = ((gpointer *)user_data)[0];
fc92c977 3856 GList *tpoints = ((gpointer *)user_data)[1];
111fa174 3857
a61b2619
AF
3858 /* outline:
3859 * detect reasons for not merging, and return
3860 * if no reason is found not to merge, then do it.
3861 */
111fa174 3862
fc92c977
RN
3863 // Exclude the original track from the compiled list
3864 if (trk->trackpoints == tpoints) {
111fa174
AF
3865 return;
3866 }
3867
fc92c977
RN
3868 t1 = VIK_TRACKPOINT(g_list_first(tpoints)->data)->timestamp;
3869 t2 = VIK_TRACKPOINT(g_list_last(tpoints)->data)->timestamp;
111fa174 3870
fc92c977
RN
3871 if (trk->trackpoints) {
3872 p1 = VIK_TRACKPOINT(g_list_first(trk->trackpoints)->data);
3873 p2 = VIK_TRACKPOINT(g_list_last(trk->trackpoints)->data);
a61b2619
AF
3874
3875 if (!p1->has_timestamp || !p2->has_timestamp) {
fc92c977 3876 //g_print("no timestamp\n");
a61b2619
AF
3877 return;
3878 }
111fa174 3879
fc92c977
RN
3880 guint threshold = GPOINTER_TO_UINT (((gpointer *)user_data)[2]);
3881 //g_print("Got track named %s, times %d, %d\n", trk->name, p1->timestamp, p2->timestamp);
3882 if (! (abs(t1 - p2->timestamp) < threshold ||
a61b2619 3883 /* p1 p2 t1 t2 */
fc92c977 3884 abs(p1->timestamp - t2) < threshold)
a61b2619
AF
3885 /* t1 t2 p1 p2 */
3886 ) {
3887 return;
3888 }
111fa174 3889 }
a61b2619 3890
fc92c977 3891 *nearby_tracks = g_list_prepend(*nearby_tracks, value);
111fa174
AF
3892}
3893
3894/* comparison function used to sort tracks; a and b are hash table keys */
02b5d347 3895/* Not actively used - can be restored if needed
111fa174
AF
3896static gint track_compare(gconstpointer a, gconstpointer b, gpointer user_data)
3897{
3898 GHashTable *tracks = user_data;
3899 time_t t1, t2;
3900
3901 t1 = VIK_TRACKPOINT(VIK_TRACK(g_hash_table_lookup(tracks, a))->trackpoints->data)->timestamp;
3902 t2 = VIK_TRACKPOINT(VIK_TRACK(g_hash_table_lookup(tracks, b))->trackpoints->data)->timestamp;
3903
3904 if (t1 < t2) return -1;
3905 if (t1 > t2) return 1;
3906 return 0;
3907}
02b5d347 3908*/
111fa174
AF
3909
3910/* comparison function used to sort trackpoints */
3911static gint trackpoint_compare(gconstpointer a, gconstpointer b)
3912{
3913 time_t t1 = VIK_TRACKPOINT(a)->timestamp, t2 = VIK_TRACKPOINT(b)->timestamp;
3914
3915 if (t1 < t2) return -1;
3916 if (t1 > t2) return 1;
3917 return 0;
3918}
3919
fb2306f7
RN
3920/**
3921 * comparison function which can be used to sort tracks or waypoints by name
3922 */
3923static gint sort_alphabetically (gconstpointer a, gconstpointer b, gpointer user_data)
3924{
3925 const gchar* namea = (const gchar*) a;
3926 const gchar* nameb = (const gchar*) b;
3927 if ( namea == NULL || nameb == NULL)
3928 return 0;
3929 else
3930 // Same sort method as used in the vik_treeview_*_alphabetize functions
3931 return strcmp ( namea, nameb );
3932}
fb2306f7 3933
5780603d
RN
3934/**
3935 * Attempt to merge selected track with other tracks specified by the user
3936 * Tracks to merge with must be of the same 'type' as the selected track -
3937 * either all with timestamps, or all without timestamps
3938 */
291edcab
HR
3939static void trw_layer_merge_with_other ( gpointer pass_along[6] )
3940{
3941 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
5780603d 3942 GList *other_tracks = NULL;
ce4bd1cf 3943 VikTrack *track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
15f45edc 3944
5780603d 3945 if ( !track->trackpoints )
15f45edc 3946 return;
15f45edc 3947
c1564279 3948 twt_udata udata;
5780603d 3949 udata.result = &other_tracks;
c1564279 3950 udata.exclude = track->trackpoints;
5780603d
RN
3951 // Allow merging with 'similar' time type time tracks
3952 // i.e. either those times, or those without
3953 udata.with_timestamps = (VIK_TRACKPOINT(track->trackpoints->data)->has_timestamp);
291edcab 3954
5780603d
RN
3955 g_hash_table_foreach(vtl->tracks, find_tracks_with_timestamp_type, (gpointer)&udata);
3956 other_tracks = g_list_reverse(other_tracks);
3957
3958 if ( !other_tracks ) {
3959 if ( udata.with_timestamps )
3960 a_dialog_error_msg(VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Failed. No other tracks with timestamps in this layer found"));
3961 else
3962 a_dialog_error_msg(VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Failed. No other tracks without timestamps in this layer found"));
15f45edc
QT
3963 return;
3964 }
3965
8352326e 3966 // Sort alphabetically for user presentation
ce4bd1cf
RN
3967 // Convert into list of names for usage with dialog function
3968 // TODO: Need to consider how to work best when we can have multiple tracks the same name...
3969 GList *other_tracks_names = NULL;
3970 GList *iter = g_list_first ( other_tracks );
3971 while ( iter ) {
3972 other_tracks_names = g_list_append ( other_tracks_names, VIK_TRACK(g_hash_table_lookup (vtl->tracks, iter->data))->name );
3973 iter = g_list_next ( iter );
3974 }
3975
3976 other_tracks_names = g_list_sort_with_data (other_tracks_names, sort_alphabetically, NULL);
8352326e 3977
7767aa02 3978 GList *merge_list = a_dialog_select_from_list(VIK_GTK_WINDOW_FROM_LAYER(vtl),
ce4bd1cf
RN
3979 other_tracks_names, TRUE,
3980 _("Merge with..."), _("Select track to merge with"));
5780603d 3981 g_list_free(other_tracks);
ce4bd1cf 3982 g_list_free(other_tracks_names);
291edcab 3983
7767aa02 3984 if (merge_list)
291edcab 3985 {
7767aa02
QT
3986 GList *l;
3987 for (l = merge_list; l != NULL; l = g_list_next(l)) {
ce4bd1cf 3988 VikTrack *merge_track = vik_trw_layer_get_track ( vtl, l->data );
7767aa02
QT
3989 if (merge_track) {
3990 track->trackpoints = g_list_concat(track->trackpoints, merge_track->trackpoints);
3991 merge_track->trackpoints = NULL;
ce4bd1cf 3992 vik_trw_layer_delete_track (vtl, merge_track);
7767aa02
QT
3993 track->trackpoints = g_list_sort(track->trackpoints, trackpoint_compare);
3994 }
291edcab 3995 }
7767aa02
QT
3996 /* TODO: free data before free merge_list */
3997 for (l = merge_list; l != NULL; l = g_list_next(l))
3998 g_free(l->data);
3999 g_list_free(merge_list);
f01eebd4 4000 vik_layer_emit_update( VIK_LAYER(vtl), FALSE );
291edcab 4001 }
291edcab
HR
4002}
4003
9c34f614
RN
4004// c.f. trw_layer_sorted_track_id_by_name_list
4005// but don't add the specified track to the list (normally current track)
4006static void trw_layer_sorted_track_id_by_name_list_exclude_self (const gpointer id, const VikTrack *trk, gpointer udata)
4007{
4008 twt_udata *user_data = udata;
4009
4010 // Skip self
4011 if (trk->trackpoints == user_data->exclude) {
4012 return;
4013 }
4014
4015 // Sort named list alphabetically
4016 *(user_data->result) = g_list_insert_sorted_with_data (*(user_data->result), trk->name, sort_alphabetically, NULL);
4017}
4018
4019/**
4020 * Join - this allows combining 'routes' and 'tracks'
4021 * i.e. doesn't care about whether tracks have consistent timestamps
4022 * ATM can only append one track at a time to the currently selected track
4023 */
4024static void trw_layer_append_track ( gpointer pass_along[6] )
4025{
4026
4027 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
4028 VikTrack *trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
4029
4030 GList *other_tracks_names = NULL;
4031
4032 // Sort alphabetically for user presentation
4033 // Convert into list of names for usage with dialog function
4034 // TODO: Need to consider how to work best when we can have multiple tracks the same name...
4035 twt_udata udata;
4036 udata.result = &other_tracks_names;
4037 udata.exclude = trk->trackpoints;
4038
4039 g_hash_table_foreach(vtl->tracks, (GHFunc) trw_layer_sorted_track_id_by_name_list_exclude_self, (gpointer)&udata);
4040
4041 // Note the limit to selecting one track only
4042 // this is to control the ordering of appending tracks, i.e. the selected track always goes after the current track
4043 // (otherwise with multiple select the ordering would not be controllable by the user - automatically being alphabetically)
4044 GList *append_list = a_dialog_select_from_list(VIK_GTK_WINDOW_FROM_LAYER(vtl),
4045 other_tracks_names,
4046 FALSE,
4047 _("Append Track"),
4048 _("Select the track to append after the current track"));
4049
4050 g_list_free(other_tracks_names);
4051
4052 // It's a list, but shouldn't contain more than one other track!
4053 if ( append_list ) {
4054 GList *l;
4055 for (l = append_list; l != NULL; l = g_list_next(l)) {
4056 // TODO: at present this uses the first track found by name,
4057 // which with potential multiple same named tracks may not be the one selected...
4058 VikTrack *append_track = vik_trw_layer_get_track ( vtl, l->data );
4059 if ( append_track ) {
4060 trk->trackpoints = g_list_concat(trk->trackpoints, append_track->trackpoints);
4061 append_track->trackpoints = NULL;
4062 vik_trw_layer_delete_track (vtl, append_track);
4063 }
4064 }
4065 for (l = append_list; l != NULL; l = g_list_next(l))
4066 g_free(l->data);
4067 g_list_free(append_list);
4068 vik_layer_emit_update( VIK_LAYER(vtl), FALSE );
4069 }
4070}
4071
24774c43
RN
4072/* merge by segments */
4073static void trw_layer_merge_by_segment ( gpointer pass_along[6] )
4074{
4075 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
4076 VikTrack *trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
4077 guint segments = vik_track_merge_segments ( trk );
4078 // NB currently no need to redraw as segments not actually shown on the display
4079 // However inform the user of what happened:
4080 gchar str[64];
4081 const gchar *tmp_str = ngettext("%d segment merged", "%d segments merged", segments);
4082 g_snprintf(str, 64, tmp_str, segments);
4083 a_dialog_info_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), str );
4084}
4085
111fa174
AF
4086/* merge by time routine */
4087static void trw_layer_merge_by_timestamp ( gpointer pass_along[6] )
4088{
a61b2619 4089 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
a61b2619 4090
02b5d347 4091 //time_t t1, t2;
111fa174 4092
c1564279 4093 GList *tracks_with_timestamp = NULL;
ce4bd1cf
RN
4094 VikTrack *orig_trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
4095 if (orig_trk->trackpoints &&
4096 !VIK_TRACKPOINT(orig_trk->trackpoints->data)->has_timestamp) {
c1564279 4097 a_dialog_error_msg(VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Failed. This track does not have timestamp"));
c1564279
RN
4098 return;
4099 }
4100
4101 twt_udata udata;
4102 udata.result = &tracks_with_timestamp;
ce4bd1cf 4103 udata.exclude = orig_trk->trackpoints;
5780603d
RN
4104 udata.with_timestamps = TRUE;
4105 g_hash_table_foreach(vtl->tracks, find_tracks_with_timestamp_type, (gpointer)&udata);
c1564279
RN
4106 tracks_with_timestamp = g_list_reverse(tracks_with_timestamp);
4107
4108 if (!tracks_with_timestamp) {
4109 a_dialog_error_msg(VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Failed. No other track in this layer has timestamp"));
c1564279
RN
4110 return;
4111 }
4112 g_list_free(tracks_with_timestamp);
4113
fc92c977
RN
4114 static guint threshold_in_minutes = 1;
4115 if (!a_dialog_time_threshold(VIK_GTK_WINDOW_FROM_LAYER(vtl),
4116 _("Merge Threshold..."),
4117 _("Merge when time between tracks less than:"),
4118 &threshold_in_minutes)) {
111fa174
AF
4119 return;
4120 }
4121
fc92c977
RN
4122 // keep attempting to merge all tracks until no merges within the time specified is possible
4123 gboolean attempt_merge = TRUE;
4124 GList *nearby_tracks = NULL;
4125 GList *trps;
4126 static gpointer params[3];
4127
4128 while ( attempt_merge ) {
4129
4130 // Don't try again unless tracks have changed
4131 attempt_merge = FALSE;
111fa174 4132
ce4bd1cf 4133 trps = orig_trk->trackpoints;
111fa174
AF
4134 if ( !trps )
4135 return;
4136
111fa174
AF
4137 if (nearby_tracks) {
4138 g_list_free(nearby_tracks);
4139 nearby_tracks = NULL;
4140 }
4141
02b5d347
RN
4142 //t1 = ((VikTrackpoint *)trps->data)->timestamp;
4143 //t2 = ((VikTrackpoint *)g_list_last(trps)->data)->timestamp;
111fa174 4144
70a23263 4145 /* g_print("Original track times: %d and %d\n", t1, t2); */
111fa174 4146 params[0] = &nearby_tracks;
fc92c977
RN
4147 params[1] = (gpointer)trps;
4148 params[2] = GUINT_TO_POINTER (threshold_in_minutes*60); // In seconds
111fa174
AF
4149
4150 /* get a list of adjacent-in-time tracks */
fc92c977 4151 g_hash_table_foreach(vtl->tracks, find_nearby_tracks_by_time, params);
111fa174 4152
111fa174 4153 /* merge them */
fc92c977
RN
4154 GList *l = nearby_tracks;
4155 while ( l ) {
4156 /*
4157#define get_first_trackpoint(x) VIK_TRACKPOINT(VIK_TRACK(x)->trackpoints->data)
4158#define get_last_trackpoint(x) VIK_TRACKPOINT(g_list_last(VIK_TRACK(x)->trackpoints)->data)
4159 time_t t1, t2;
4160 t1 = get_first_trackpoint(l)->timestamp;
4161 t2 = get_last_trackpoint(l)->timestamp;
ce4bd1cf
RN
4162#undef get_first_trackpoint
4163#undef get_last_trackpoint
fc92c977
RN
4164 g_print(" %20s: track %d - %d\n", VIK_TRACK(l->data)->name, (int)t1, (int)t2);
4165 */
111fa174 4166
fc92c977
RN
4167 /* remove trackpoints from merged track, delete track */
4168 orig_trk->trackpoints = g_list_concat(orig_trk->trackpoints, VIK_TRACK(l->data)->trackpoints);
4169 VIK_TRACK(l->data)->trackpoints = NULL;
4170 vik_trw_layer_delete_track (vtl, VIK_TRACK(l->data));
111fa174 4171
fc92c977
RN
4172 // Tracks have changed, therefore retry again against all the remaining tracks
4173 attempt_merge = TRUE;
ce4bd1cf 4174
fc92c977 4175 l = g_list_next(l);
111fa174 4176 }
fc92c977
RN
4177
4178 orig_trk->trackpoints = g_list_sort(orig_trk->trackpoints, trackpoint_compare);
4179 }
4180
111fa174 4181 g_list_free(nearby_tracks);
f01eebd4 4182 vik_layer_emit_update( VIK_LAYER(vtl), FALSE );
111fa174
AF
4183}
4184
84d3d9f9 4185/**
9914238e 4186 * Split a track at the currently selected trackpoint
84d3d9f9
RN
4187 */
4188static void trw_layer_split_at_selected_trackpoint ( VikTrwLayer *vtl )
4189{
4190 if ( !vtl->current_tpl )
4191 return;
4192
4193 if ( vtl->current_tpl->next && vtl->current_tpl->prev ) {
9748531a 4194 gchar *name = trw_layer_new_unique_sublayer_name(vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, vtl->current_tp_track->name);
9914238e 4195 if ( name ) {
84d3d9f9
RN
4196 VikTrack *tr = vik_track_new ();
4197 GList *newglist = g_list_alloc ();
4198 newglist->prev = NULL;
4199 newglist->next = vtl->current_tpl->next;
4200 newglist->data = vik_trackpoint_copy(VIK_TRACKPOINT(vtl->current_tpl->data));
4201 tr->trackpoints = newglist;
4202
4203 vtl->current_tpl->next->prev = newglist; /* end old track here */
4204 vtl->current_tpl->next = NULL;
4205
4206 vtl->current_tpl = newglist; /* change tp to first of new track. */
4207 vtl->current_tp_track = tr;
4208
4209 tr->visible = TRUE;
4210
4211 vik_trw_layer_add_track ( vtl, name, tr );
4212
4213 trku_udata udata;
4214 udata.trk = tr;
4215 udata.uuid = NULL;
4216
4217 // Also need id of newly created track
4218 gpointer *trkf = g_hash_table_find ( vtl->tracks, (GHRFunc) trw_layer_track_find_uuid, &udata );
4219 if ( trkf && udata.uuid )
4220 vtl->current_tp_id = udata.uuid;
4221 else
4222 vtl->current_tp_id = NULL;
4223
4224 vik_layer_emit_update(VIK_LAYER(vtl), FALSE);
4225 }
4226 }
4227}
4228
111fa174
AF
4229/* split by time routine */
4230static void trw_layer_split_by_timestamp ( gpointer pass_along[6] )
4231{
784b6042
RN
4232 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
4233 VikTrack *track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, pass_along[3] );
111fa174
AF
4234 GList *trps = track->trackpoints;
4235 GList *iter;
4236 GList *newlists = NULL;
4237 GList *newtps = NULL;
111fa174
AF
4238 static guint thr = 1;
4239
4240 time_t ts, prev_ts;
4241
4242 if ( !trps )
4243 return;
4244
4245 if (!a_dialog_time_threshold(VIK_GTK_WINDOW_FROM_LAYER(pass_along[0]),
4c77d5e0
GB
4246 _("Split Threshold..."),
4247 _("Split when time between trackpoints exceeds:"),
111fa174
AF
4248 &thr)) {
4249 return;
4250 }
4251
4252 /* iterate through trackpoints, and copy them into new lists without touching original list */
4253 prev_ts = VIK_TRACKPOINT(trps->data)->timestamp;
4254 iter = trps;
4255
4256 while (iter) {
4257 ts = VIK_TRACKPOINT(iter->data)->timestamp;
4258 if (ts < prev_ts) {
70a23263 4259 g_print("panic: ts < prev_ts: this should never happen!\n");
111fa174
AF
4260 return;
4261 }
4262 if (ts - prev_ts > thr*60) {
4263 /* flush accumulated trackpoints into new list */
aa9887a1 4264 newlists = g_list_append(newlists, g_list_reverse(newtps));
111fa174
AF
4265 newtps = NULL;
4266 }
4267
4268 /* accumulate trackpoint copies in newtps, in reverse order */
4269 newtps = g_list_prepend(newtps, vik_trackpoint_copy(VIK_TRACKPOINT(iter->data)));
4270 prev_ts = ts;
4271 iter = g_list_next(iter);
4272 }
4273 if (newtps) {
aa9887a1 4274 newlists = g_list_append(newlists, g_list_reverse(newtps));
111fa174
AF
4275 }
4276
4277 /* put lists of trackpoints into tracks */
4278 iter = newlists;
c9d8f273
RN
4279 // Only bother updating if the split results in new tracks
4280 if (g_list_length (newlists) > 1) {
4281 while (iter) {
4282 gchar *new_tr_name;
4283 VikTrack *tr;
111fa174 4284
c9d8f273
RN
4285 tr = vik_track_new();
4286 tr->visible = track->visible;
4287 tr->trackpoints = (GList *)(iter->data);
111fa174 4288
784b6042
RN
4289 new_tr_name = trw_layer_new_unique_sublayer_name ( vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, track->name);
4290 vik_trw_layer_add_track(vtl, new_tr_name, tr);
c9d8f273 4291 /* g_print("adding track %s, times %d - %d\n", new_tr_name, VIK_TRACKPOINT(tr->trackpoints->data)->timestamp,
111fa174
AF
4292 VIK_TRACKPOINT(g_list_last(tr->trackpoints)->data)->timestamp);*/
4293
c9d8f273
RN
4294 iter = g_list_next(iter);
4295 }
ce4bd1cf 4296 // Remove original track and then update the display
784b6042 4297 vik_trw_layer_delete_track (vtl, track);
f01eebd4 4298 vik_layer_emit_update(VIK_LAYER(pass_along[0]), FALSE);
111fa174
AF
4299 }
4300 g_list_free(newlists);
111fa174
AF
4301}
4302
af2341f3
RN
4303/**
4304 * Split a track by the number of points as specified by the user
4305 */
4306static void trw_layer_split_by_n_points ( gpointer pass_along[6] )
4307{
4308 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
4309 VikTrack *track = (VikTrack *)g_hash_table_lookup ( vtl->tracks, pass_along[3] );
4310
4311 // Check valid track
4312 GList *trps = track->trackpoints;
4313 if ( !trps )
4314 return;
4315
4316 gint points = a_dialog_get_positive_number(VIK_GTK_WINDOW_FROM_LAYER(pass_along[0]),
4317 _("Split Every Nth Point"),
4318 _("Split on every Nth point:"),
4319 250, // Default value as per typical limited track capacity of various GPS devices
4320 2, // Min
4321 65536, // Max
4322 5); // Step
4323 // Was a valid number returned?
4324 if (!points)
4325 return;
4326
4327 // Now split...
4328 GList *iter;
4329 GList *newlists = NULL;
4330 GList *newtps = NULL;
4331 gint count = 0;
4332 iter = trps;
4333
4334 while (iter) {
4335 /* accumulate trackpoint copies in newtps, in reverse order */
4336 newtps = g_list_prepend(newtps, vik_trackpoint_copy(VIK_TRACKPOINT(iter->data)));
4337 count++;
4338 if (count >= points) {
4339 /* flush accumulated trackpoints into new list */
4340 newlists = g_list_append(newlists, g_list_reverse(newtps));
4341 newtps = NULL;
4342 count = 0;
4343 }
4344 iter = g_list_next(iter);
4345 }
4346
4347 // If there is a remaining chunk put that into the new split list
4348 // This may well be the whole track if no split points were encountered
4349 if (newtps) {
4350 newlists = g_list_append(newlists, g_list_reverse(newtps));
4351 }
4352
4353 /* put lists of trackpoints into tracks */
4354 iter = newlists;
af2341f3
RN
4355 // Only bother updating if the split results in new tracks
4356 if (g_list_length (newlists) > 1) {
4357 while (iter) {
4358 gchar *new_tr_name;
4359 VikTrack *tr;
4360
4361 tr = vik_track_new();
4362 tr->visible = track->visible;
4363 tr->trackpoints = (GList *)(iter->data);
4364
784b6042
RN
4365 new_tr_name = trw_layer_new_unique_sublayer_name ( vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, track->name);
4366 vik_trw_layer_add_track(vtl, new_tr_name, tr);
af2341f3
RN
4367
4368 iter = g_list_next(iter);
4369 }
4370 // Remove original track and then update the display
784b6042 4371 vik_trw_layer_delete_track (vtl, track);
f01eebd4 4372 vik_layer_emit_update(VIK_LAYER(pass_along[0]), FALSE);
af2341f3
RN
4373 }
4374 g_list_free(newlists);
4375}
4376
a4aefe69
RN
4377/**
4378 * Split a track at the currently selected trackpoint
4379 */
4380static void trw_layer_split_at_trackpoint ( gpointer pass_along[6] )
4381{
4382 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
4383 trw_layer_split_at_selected_trackpoint ( vtl );
4384}
98e87078
RN
4385
4386/**
4387 * Split a track by its segments
4388 */
4389static void trw_layer_split_segments ( gpointer pass_along[6] )
4390{
4391 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
4392 VikTrack *trk = (VikTrack *)g_hash_table_lookup ( vtl->tracks, pass_along[3] );
4393 guint ntracks;
4394
4395 VikTrack **tracks = vik_track_split_into_segments (trk, &ntracks);
4396 gchar *new_tr_name;
4397 guint i;
4398 for ( i = 0; i < ntracks; i++ ) {
4399 if ( tracks[i] ) {
4400 new_tr_name = trw_layer_new_unique_sublayer_name ( vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, trk->name);
4401 vik_trw_layer_add_track ( vtl, new_tr_name, tracks[i] );
4402 }
4403 }
4404 if ( tracks ) {
4405 g_free ( tracks );
4406 // Remove original track
4407 vik_trw_layer_delete_track ( vtl, trk );
4408 vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
4409 }
4410 else {
4411 a_dialog_error_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Can not split track as it has no segments"));
4412 }
4413}
111fa174
AF
4414/* end of split/merge routines */
4415
b6eda120
RN
4416/**
4417 * Delete adjacent track points at the same position
4418 * AKA Delete Dulplicates on the Properties Window
4419 */
4420static void trw_layer_delete_points_same_position ( gpointer pass_along[6] )
4421{
4422 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
4423 VikTrack *trk = (VikTrack *)g_hash_table_lookup ( vtl->tracks, pass_along[3] );
4424
4425 gulong removed = vik_track_remove_dup_points ( trk );
4426
4427 // Track has been updated so update tps:
4428 trw_layer_cancel_tps_of_track ( vtl, trk );
4429
4430 // Inform user how much was deleted as it's not obvious from the normal view
4431 gchar str[64];
4432 const gchar *tmp_str = ngettext("Deleted %ld point", "Deleted %ld points", removed);
4433 g_snprintf(str, 64, tmp_str, removed);
4434 a_dialog_info_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), str);
4435
4436 vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
4437}
4438
6579ca1f
RN
4439/**
4440 * Delete adjacent track points with the same timestamp
4441 * Normally new tracks that are 'routes' won't have any timestamps so should be OK to clean up the track
4442 */
4443static void trw_layer_delete_points_same_time ( gpointer pass_along[6] )
4444{
4445 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
4446 VikTrack *trk = (VikTrack *)g_hash_table_lookup ( vtl->tracks, pass_along[3] );
4447
4448 gulong removed = vik_track_remove_same_time_points ( trk );
4449
4450 // Track has been updated so update tps:
4451 trw_layer_cancel_tps_of_track ( vtl, trk );
4452
4453 // Inform user how much was deleted as it's not obvious from the normal view
4454 gchar str[64];
4455 const gchar *tmp_str = ngettext("Deleted %ld point", "Deleted %ld points", removed);
4456 g_snprintf(str, 64, tmp_str, removed);
4457 a_dialog_info_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), str);
4458
4459 vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
4460}
4461
eb9fd106
RN
4462/**
4463 * Reverse a track
4464 */
4465static void trw_layer_reverse ( gpointer pass_along[6] )
4466{
4467 VikTrwLayer *vtl = (VikTrwLayer *)pass_along[0];
4468 VikTrack *track = (VikTrack *)g_hash_table_lookup ( vtl->tracks, pass_along[3] );
4469
4470 // Check valid track
4471 GList *trps = track->trackpoints;
4472 if ( !trps )
4473 return;
4474
4475 vik_track_reverse ( track );
4476
4477 vik_layer_emit_update ( VIK_LAYER(pass_along[0]), FALSE );
4478}
4479
20b671c3
RN
4480/**
4481 * Similar to trw_layer_enum_item, but this uses a sorted method
4482 */
ce4bd1cf 4483/* Currently unused
20b671c3
RN
4484static void trw_layer_sorted_name_list(gpointer key, gpointer value, gpointer udata)
4485{
4486 GList **list = (GList**)udata;
ce4bd1cf 4487 // *list = g_list_prepend(*all, key); //unsorted method
20b671c3
RN
4488 // Sort named list alphabetically
4489 *list = g_list_insert_sorted_with_data (*list, key, sort_alphabetically, NULL);
4490}
ce4bd1cf 4491*/
20b671c3 4492
c9570f86
RN
4493/**
4494 * Now Waypoint specific sort
4495 */
4496static void trw_layer_sorted_wp_id_by_name_list (const gpointer id, const VikWaypoint *wp, gpointer udata)
4497{
4498 GList **list = (GList**)udata;
4499 // Sort named list alphabetically
4500 *list = g_list_insert_sorted_with_data (*list, wp->name, sort_alphabetically, NULL);
4501}
4502
ce4bd1cf
RN
4503/**
4504 * Track specific sort
4505 */
4506static void trw_layer_sorted_track_id_by_name_list (const gpointer id, const VikTrack *trk, gpointer udata)
4507{
4508 GList **list = (GList**)udata;
4509 // Sort named list alphabetically
4510 *list = g_list_insert_sorted_with_data (*list, trk->name, sort_alphabetically, NULL);
4511}
4512
3d2ad4f8
RN
4513
4514typedef struct {
4515 gboolean has_same_track_name;
4516 const gchar *same_track_name;
4517} same_track_name_udata;
4518
4519static gint check_tracks_for_same_name ( gconstpointer aa, gconstpointer bb, gpointer udata )
4520{
4521 const gchar* namea = (const gchar*) aa;
4522 const gchar* nameb = (const gchar*) bb;
4523
4524 // the test
4525 gint result = strcmp ( namea, nameb );
4526
4527 if ( result == 0 ) {
4528 // Found two names the same
4529 same_track_name_udata *user_data = udata;
4530 user_data->has_same_track_name = TRUE;
4531 user_data->same_track_name = namea;
4532 }
4533
4534 // Leave ordering the same
4535 return 0;
4536}
4537
4538/**
4539 * Find out if any tracks have the same name in this layer
4540 */
4541static gboolean trw_layer_has_same_track_names ( VikTrwLayer *vtl )
4542{
4543 // Sort items by name, then compare if any next to each other are the same
4544
4545 GList *track_names = NULL;
4546 g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_sorted_track_id_by_name_list, &track_names );
4547
4548 // No tracks
4549 if ( ! track_names )
4550 return FALSE;
4551
4552 same_track_name_udata udata;
4553 udata.has_same_track_name = FALSE;
4554
4555 // Use sort routine to traverse list comparing items
4556 // Don't care how this list ends up ordered ( doesn't actually change ) - care about the returned status
4557 GList *dummy_list = g_list_sort_with_data ( track_names, check_tracks_for_same_name, &udata );
4558 // Still no tracks...
4559 if ( ! dummy_list )
4560 return FALSE;
4561
4562 return udata.has_same_track_name;
4563}
4564
4565/**
4566 * Force unqiue track names for this layer
4567 * Note the panel is a required parameter to enable the update of the names displayed
4568 */
4569static void vik_trw_layer_uniquify_tracks ( VikTrwLayer *vtl, VikLayersPanel *vlp )
4570{
4571 // . Search list for an instance of repeated name
4572 // . get track of this name
4573 // . create new name
4574 // . rename track & update equiv. treeview iter
4575 // . repeat until all different
4576
4577 same_track_name_udata udata;
4578
4579 GList *track_names = NULL;
4580 udata.has_same_track_name = FALSE;
4581 udata.same_track_name = NULL;
4582
4583 g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_sorted_track_id_by_name_list, &track_names );
4584
4585 // No tracks
4586 if ( ! track_names )
4587 return;
4588
4589 GList *dummy_list1 = g_list_sort_with_data ( track_names, check_tracks_for_same_name, &udata );
4590
4591 // Still no tracks...
4592 if ( ! dummy_list1 )
4593 return;
4594
4595 while ( udata.has_same_track_name ) {
4596
4597 // Find a track with the same name
4598 VikTrack *trk = vik_trw_layer_get_track ( vtl, (gpointer) udata.same_track_name );
4599
4600 if ( ! trk ) {
4601 // Broken :(
4602 g_critical("Houston, we've had a problem.");
4603 vik_statusbar_set_message ( vik_window_get_statusbar (VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl))), VIK_STATUSBAR_INFO,
4604 _("Internal Error in vik_trw_layer_uniquify_tracks") );
4605 return;
4606 }
4607
4608 // Rename it
9748531a 4609 gchar *newname = trw_layer_new_unique_sublayer_name ( vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, udata.same_track_name );
3d2ad4f8
RN
4610 vik_track_set_name ( trk, newname );
4611
4612 trku_udata udataU;
4613 udataU.trk = trk;
4614 udataU.uuid = NULL;
4615
4616 // Need want key of it for treeview update
4617 gpointer *trkf = g_hash_table_find ( vtl->tracks, (GHRFunc) trw_layer_track_find_uuid, &udataU );
4618
4619 if ( trkf && udataU.uuid ) {
4620
4621 GtkTreeIter *it = g_hash_table_lookup ( vtl->tracks_iters, udataU.uuid );
4622
4623 if ( it ) {
4624 vik_treeview_item_set_name ( VIK_LAYER(vtl)->vt, it, newname );
4625#ifdef VIK_CONFIG_ALPHABETIZED_TRW
4626 vik_treeview_sublayer_realphabetize ( VIK_LAYER(vtl)->vt, it, newname );
4627#endif
4628 }
4629 }
4630
4631 // Start trying to find same names again...
4632 track_names = NULL;
4633 g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_sorted_track_id_by_name_list, &track_names );
4634 udata.has_same_track_name = FALSE;
4635 GList *dummy_list2 = g_list_sort_with_data ( track_names, check_tracks_for_same_name, &udata );
4636
4637 // No tracks any more - give up searching
4638 if ( ! dummy_list2 )
4639 udata.has_same_track_name = FALSE;
4640 }
4641
4642 // Update
4643 vik_layers_panel_emit_update ( vlp );
4644}
4645
20b671c3
RN
4646/**
4647 *
4648 */
4649static void trw_layer_delete_tracks_from_selection ( gpointer lav[2] )
4650{
4651 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
4652 GList *all = NULL;
ce4bd1cf 4653
3d2ad4f8
RN
4654 // Ensure list of track names offered is unique
4655 if ( trw_layer_has_same_track_names ( vtl ) ) {
4656 if ( a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
4657 _("Multiple entries with the same name exist. This method only works with unique names. Force unique names now?"), NULL ) ) {
4658 vik_trw_layer_uniquify_tracks ( vtl, VIK_LAYERS_PANEL(lav[1]) );
4659 }
4660 else
4661 return;
4662 }
ce4bd1cf 4663
20b671c3 4664 // Sort list alphabetically for better presentation
ce4bd1cf 4665 g_hash_table_foreach(vtl->tracks, (GHFunc) trw_layer_sorted_track_id_by_name_list, &all);
20b671c3
RN
4666
4667 if ( ! all ) {
4668 a_dialog_error_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), _("No tracks found"));
4669 return;
4670 }
4671
4672 // Get list of items to delete from the user
4673 GList *delete_list = a_dialog_select_from_list(VIK_GTK_WINDOW_FROM_LAYER(vtl),
4674 all,
4675 TRUE,
4676 _("Delete Selection"),
4677 _("Select tracks to delete"));
4678 g_list_free(all);
4679
4680 // Delete requested tracks
4681 // since specificly requested, IMHO no need for extra confirmation
4682 if ( delete_list ) {
4683 GList *l;
4684 for (l = delete_list; l != NULL; l = g_list_next(l)) {
3d2ad4f8 4685 // This deletes first trk it finds of that name (but uniqueness is enforced above)
ce4bd1cf 4686 trw_layer_delete_track_by_name (vtl, l->data);
20b671c3
RN
4687 }
4688 g_list_free(delete_list);
f01eebd4 4689 vik_layer_emit_update( VIK_LAYER(vtl), FALSE );
20b671c3
RN
4690 }
4691}
4692
33a89785
RN
4693typedef struct {
4694 gboolean has_same_waypoint_name;
4695 const gchar *same_waypoint_name;
4696} same_waypoint_name_udata;
4697
4698static gint check_waypoints_for_same_name ( gconstpointer aa, gconstpointer bb, gpointer udata )
4699{
4700 const gchar* namea = (const gchar*) aa;
4701 const gchar* nameb = (const gchar*) bb;
4702
4703 // the test
4704 gint result = strcmp ( namea, nameb );
4705
4706 if ( result == 0 ) {
4707 // Found two names the same
4708 same_waypoint_name_udata *user_data = udata;
4709 user_data->has_same_waypoint_name = TRUE;
4710 user_data->same_waypoint_name = namea;
4711 }
4712
4713 // Leave ordering the same
4714 return 0;
4715}
4716
4717/**
4718 * Find out if any waypoints have the same name in this layer
4719 */
4720gboolean trw_layer_has_same_waypoint_names ( VikTrwLayer *vtl )
4721{
4722 // Sort items by name, then compare if any next to each other are the same
4723
4724 GList *waypoint_names = NULL;
4725 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_sorted_wp_id_by_name_list, &waypoint_names );
4726
4727 // No waypoints
4728 if ( ! waypoint_names )
4729 return FALSE;
4730
4731 same_waypoint_name_udata udata;
4732 udata.has_same_waypoint_name = FALSE;
4733
4734 // Use sort routine to traverse list comparing items
4735 // Don't care how this list ends up ordered ( doesn't actually change ) - care about the returned status
4736 GList *dummy_list = g_list_sort_with_data ( waypoint_names, check_waypoints_for_same_name, &udata );
4737 // Still no waypoints...
4738 if ( ! dummy_list )
4739 return FALSE;
4740
4741 return udata.has_same_waypoint_name;
4742}
4743
4744/**
4745 * Force unqiue waypoint names for this layer
4746 * Note the panel is a required parameter to enable the update of the names displayed
4747 */
4748static void vik_trw_layer_uniquify_waypoints ( VikTrwLayer *vtl, VikLayersPanel *vlp )
4749{
4750 // . Search list for an instance of repeated name
4751 // . get waypoint of this name
4752 // . create new name
4753 // . rename waypoint & update equiv. treeview iter
4754 // . repeat until all different
4755
4756 same_waypoint_name_udata udata;
4757
4758 GList *waypoint_names = NULL;
4759 udata.has_same_waypoint_name = FALSE;
4760 udata.same_waypoint_name = NULL;
4761
4762 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_sorted_wp_id_by_name_list, &waypoint_names );
4763
4764 // No waypoints
4765 if ( ! waypoint_names )
4766 return;
4767
4768 GList *dummy_list1 = g_list_sort_with_data ( waypoint_names, check_waypoints_for_same_name, &udata );
4769
4770 // Still no waypoints...
4771 if ( ! dummy_list1 )
4772 return;
4773
4774 while ( udata.has_same_waypoint_name ) {
4775
4776 // Find a waypoint with the same name
4777 VikWaypoint *waypoint = vik_trw_layer_get_waypoint ( vtl, (gpointer) udata.same_waypoint_name );
4778
4779 if ( ! waypoint ) {
4780 // Broken :(
4781 g_critical("Houston, we've had a problem.");
4782 vik_statusbar_set_message ( vik_window_get_statusbar (VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl))), VIK_STATUSBAR_INFO,
4783 _("Internal Error in vik_trw_layer_uniquify_waypoints") );
4784 return;
4785 }
4786
4787 // Rename it
9748531a 4788 gchar *newname = trw_layer_new_unique_sublayer_name ( vtl, VIK_TRW_LAYER_SUBLAYER_WAYPOINT, udata.same_waypoint_name );
33a89785
RN
4789 vik_waypoint_set_name ( waypoint, newname );
4790
4791 wpu_udata udataU;
4792 udataU.wp = waypoint;
4793 udataU.uuid = NULL;
4794
4795 // Need want key of it for treeview update
4796 gpointer *wpf = g_hash_table_find ( vtl->waypoints, (GHRFunc) trw_layer_waypoint_find_uuid, &udataU );
4797
4798 if ( wpf && udataU.uuid ) {
4799
4800 GtkTreeIter *it = g_hash_table_lookup ( vtl->waypoints_iters, udataU.uuid );
4801
4802 if ( it ) {
4803 vik_treeview_item_set_name ( VIK_LAYER(vtl)->vt, it, newname );
4804#ifdef VIK_CONFIG_ALPHABETIZED_TRW
4805 vik_treeview_sublayer_realphabetize ( VIK_LAYER(vtl)->vt, it, newname );
4806#endif
4807 }
4808 }
4809
4810 // Start trying to find same names again...
4811 waypoint_names = NULL;
4812 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_sorted_wp_id_by_name_list, &waypoint_names );
4813 udata.has_same_waypoint_name = FALSE;
4814 GList *dummy_list2 = g_list_sort_with_data ( waypoint_names, check_waypoints_for_same_name, &udata );
4815
4816 // No waypoints any more - give up searching
4817 if ( ! dummy_list2 )
4818 udata.has_same_waypoint_name = FALSE;
4819 }
4820
4821 // Update
4822 vik_layers_panel_emit_update ( vlp );
4823}
4824
20b671c3
RN
4825/**
4826 *
4827 */
4828static void trw_layer_delete_waypoints_from_selection ( gpointer lav[2] )
4829{
4830 VikTrwLayer *vtl = VIK_TRW_LAYER(lav[0]);
4831 GList *all = NULL;
4832
33a89785
RN
4833 // Ensure list of waypoint names offered is unique
4834 if ( trw_layer_has_same_waypoint_names ( vtl ) ) {
4835 if ( a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
4836 _("Multiple entries with the same name exist. This method only works with unique names. Force unique names now?"), NULL ) ) {
4837 vik_trw_layer_uniquify_waypoints ( vtl, VIK_LAYERS_PANEL(lav[1]) );
4838 }
4839 else
4840 return;
4841 }
c9570f86 4842
20b671c3 4843 // Sort list alphabetically for better presentation
c9570f86 4844 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_sorted_wp_id_by_name_list, &all);
20b671c3
RN
4845 if ( ! all ) {
4846 a_dialog_error_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), _("No waypoints found"));
4847 return;
4848 }
4849
4850 all = g_list_sort_with_data(all, sort_alphabetically, NULL);
4851
4852 // Get list of items to delete from the user
4853 GList *delete_list = a_dialog_select_from_list(VIK_GTK_WINDOW_FROM_LAYER(vtl),
4854 all,
4855 TRUE,
4856 _("Delete Selection"),
4857 _("Select waypoints to delete"));
4858 g_list_free(all);
4859
4860 // Delete requested waypoints
4861 // since specificly requested, IMHO no need for extra confirmation
4862 if ( delete_list ) {
4863 GList *l;
4864 for (l = delete_list; l != NULL; l = g_list_next(l)) {
33a89785 4865 // This deletes first waypoint it finds of that name (but uniqueness is enforced above)
c9570f86 4866 trw_layer_delete_waypoint_by_name (vtl, l->data);
20b671c3
RN
4867 }
4868 g_list_free(delete_list);
f01eebd4 4869 vik_layer_emit_update( VIK_LAYER(vtl), FALSE );
20b671c3
RN
4870 }
4871
4872}
111fa174 4873
6bb72350 4874static void trw_layer_goto_waypoint ( gpointer pass_along[6] )
50a14534
EB
4875{
4876 VikWaypoint *wp = g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->waypoints, pass_along[3] );
4877 if ( wp )
6bb72350 4878 goto_coord ( pass_along[1], pass_along[0], pass_along[5], &(wp->coord) );
50a14534
EB
4879}
4880
6bb72350 4881static void trw_layer_waypoint_gc_webpage ( gpointer pass_along[6] )
50a14534
EB
4882{
4883 gchar *webpage = g_strdup_printf("http://www.geocaching.com/seek/cache_details.aspx?wp=%s", (gchar *) pass_along[3] );
7d02a0b0 4884 open_url(VIK_GTK_WINDOW_FROM_LAYER(VIK_LAYER(pass_along[0])), webpage);
50a14534
EB
4885 g_free ( webpage );
4886}
4887
a7cd93ac 4888static const gchar* trw_layer_sublayer_rename_request ( VikTrwLayer *l, const gchar *newname, gpointer vlp, gint subtype, gpointer sublayer, GtkTreeIter *iter )
50a14534
EB
4889{
4890 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT )
4891 {
c9570f86 4892 VikWaypoint *wp = g_hash_table_lookup ( l->waypoints, sublayer );
50a14534 4893
c9570f86
RN
4894 // No actual change to the name supplied
4895 if (strcmp(newname, wp->name) == 0 )
50a14534
EB
4896 return NULL;
4897
c9570f86
RN
4898 VikWaypoint *wpf = vik_trw_layer_get_waypoint ( l, newname );
4899
4900 if ( wpf ) {
4901 // An existing waypoint has been found with the requested name
4902 if ( ! a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(l),
4903 _("A waypoint with the name \"%s\" already exists. Really create one with the same name?"),
4904 newname ) )
8499a412 4905 return NULL;
50a14534
EB
4906 }
4907
c9570f86
RN
4908 // Update WP name and refresh the treeview
4909 vik_waypoint_set_name (wp, newname);
50a14534 4910
50a14534 4911#ifdef VIK_CONFIG_ALPHABETIZED_TRW
c9570f86 4912 vik_treeview_sublayer_realphabetize ( VIK_LAYER(l)->vt, iter, newname );
50a14534
EB
4913#endif
4914
4915 vik_layers_panel_emit_update ( VIK_LAYERS_PANEL(vlp) );
c9570f86
RN
4916
4917 return newname;
50a14534 4918 }
c9570f86 4919
50a14534
EB
4920 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
4921 {
ce4bd1cf 4922 VikTrack *trk = g_hash_table_lookup ( l->tracks, sublayer );
50a14534 4923
ce4bd1cf
RN
4924 // No actual change to the name supplied
4925 if (strcmp(newname, trk->name) == 0)
50a14534
EB
4926 return NULL;
4927
ce4bd1cf
RN
4928 VikTrack *trkf = vik_trw_layer_get_track ( l, (gpointer) newname );
4929
4930 if ( trkf ) {
4931 // An existing track has been found with the requested name
4932 if ( ! a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(l),
4933 _("A track with the name \"%s\" already exists. Really create one with the same name?"),
4934 newname ) )
8499a412 4935 return NULL;
50a14534 4936 }
ce4bd1cf
RN
4937 // Update track name and refresh GUI parts
4938 vik_track_set_name (trk, newname);
50a14534 4939
ce4bd1cf
RN
4940 // Update any subwindows that could be displaying this track which has changed name
4941 // Only one Track Edit Window
4942 if ( l->current_tp_track == trk && l->tpwin ) {
4943 vik_trw_layer_tpwin_set_track_name ( l->tpwin, newname );
50a14534 4944 }
ce4bd1cf
RN
4945 // Property Dialog of the track
4946 vik_trw_layer_propwin_update ( trk );
50a14534
EB
4947
4948#ifdef VIK_CONFIG_ALPHABETIZED_TRW
ce4bd1cf 4949 vik_treeview_sublayer_realphabetize ( VIK_LAYER(l)->vt, iter, newname );
50a14534
EB
4950#endif
4951
4952 vik_layers_panel_emit_update ( VIK_LAYERS_PANEL(vlp) );
ce4bd1cf
RN
4953
4954 return newname;
50a14534
EB
4955 }
4956 return NULL;
4957}
4958
4959static gboolean is_valid_geocache_name ( gchar *str )
4960{
4961 gint len = strlen ( str );
0c1044e9 4962 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
4963}
4964
6bb72350 4965static void trw_layer_track_use_with_filter ( gpointer pass_along[6] )
28c82d8b 4966{
ce4bd1cf
RN
4967 VikTrack *trk = g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, pass_along[3] );
4968 a_acquire_set_filter_track ( trk );
28c82d8b
EB
4969}
4970
55340efa 4971#ifdef VIK_CONFIG_GOOGLE
ce4bd1cf 4972static gboolean is_valid_google_route ( VikTrwLayer *vtl, const gpointer track_id )
bddd2056 4973{
ce4bd1cf 4974 VikTrack *tr = g_hash_table_lookup ( vtl->tracks, track_id );
bddd2056
EB
4975 return ( tr && tr->comment && strlen(tr->comment) > 7 && !strncmp(tr->comment, "from:", 5) );
4976}
4977
6bb72350 4978static void trw_layer_track_google_route_webpage ( gpointer pass_along[6] )
bddd2056 4979{
ce4bd1cf 4980 VikTrack *tr = g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, pass_along[3] );
bddd2056
EB
4981 if ( tr ) {
4982 gchar *escaped = uri_escape ( tr->comment );
4983 gchar *webpage = g_strdup_printf("http://maps.google.com/maps?f=q&hl=en&q=%s", escaped );
7d02a0b0 4984 open_url(VIK_GTK_WINDOW_FROM_LAYER(VIK_LAYER(pass_along[0])), webpage);
bddd2056 4985 g_free ( escaped );
bddd2056
EB
4986 g_free ( webpage );
4987 }
4988}
ff1f2b3e 4989#endif
bddd2056 4990
50eadc64
RN
4991/* vlp can be NULL if necessary - i.e. right-click from a tool */
4992/* viewpoint is now available instead */
a7cd93ac 4993static gboolean trw_layer_sublayer_add_menu_items ( VikTrwLayer *l, GtkMenu *menu, gpointer vlp, gint subtype, gpointer sublayer, GtkTreeIter *iter, VikViewport *vvp )
50a14534 4994{
ce4bd1cf 4995 static gpointer pass_along[8];
50a14534
EB
4996 GtkWidget *item;
4997 gboolean rv = FALSE;
4998
4999 pass_along[0] = l;
5000 pass_along[1] = vlp;
dc2c040e 5001 pass_along[2] = GINT_TO_POINTER (subtype);
50a14534 5002 pass_along[3] = sublayer;
169acf64 5003 pass_along[4] = GINT_TO_POINTER (1); // Confirm delete request
6bb72350 5004 pass_along[5] = vvp;
c9570f86 5005 pass_along[6] = iter;
ce4bd1cf 5006 pass_along[7] = NULL; // For misc purposes - maybe track or waypoint
50a14534
EB
5007
5008 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT || subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
5009 {
5010 rv = TRUE;
5011
5012 item = gtk_image_menu_item_new_from_stock ( GTK_STOCK_PROPERTIES, NULL );
5013 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_properties_item), pass_along );
5014 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
5015 gtk_widget_show ( item );
5016
21700912
QT
5017 if (subtype == VIK_TRW_LAYER_SUBLAYER_TRACK) {
5018 VikTrwLayer *vtl = l;
5019 VikTrack *tr = g_hash_table_lookup ( vtl->tracks, sublayer );
5020 if (tr && tr->property_dialog)
5021 gtk_widget_set_sensitive(GTK_WIDGET(item), FALSE );
5022 }
5023
2cebc318
QT
5024 item = gtk_image_menu_item_new_from_stock ( GTK_STOCK_CUT, NULL );
5025 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_cut_item_cb), pass_along );
5026 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
5027 gtk_widget_show ( item );
5028
5029 item = gtk_image_menu_item_new_from_stock ( GTK_STOCK_COPY, NULL );
5030 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_copy_item_cb), pass_along );
5031 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
5032 gtk_widget_show ( item );
5033
50a14534
EB
5034 item = gtk_image_menu_item_new_from_stock ( GTK_STOCK_DELETE, NULL );
5035 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_item), pass_along );
5036 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
5037 gtk_widget_show ( item );
5038
5039 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT )
5040 {
5ede6aa6
RN
5041 gboolean separator_created = FALSE;
5042
50a14534
EB
5043 /* could be a right-click using the tool */
5044 if ( vlp != NULL ) {
5ede6aa6
RN
5045 item = gtk_menu_item_new ();
5046 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
5047 gtk_widget_show ( item );
5048
5049 separator_created = TRUE;
5050
d6de71f9
RN
5051 item = gtk_image_menu_item_new_with_mnemonic ( _("_Goto") );
5052 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_JUMP_TO, GTK_ICON_SIZE_MENU) );
50a14534
EB
5053 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_waypoint), pass_along );
5054 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
5055 gtk_widget_show ( item );
5056 }
5057
c9570f86 5058 VikWaypoint *wp = g_hash_table_lookup ( VIK_TRW_LAYER(l)->waypoints, sublayer );
5ede6aa6 5059
c9570f86
RN
5060 if ( wp && wp->name ) {
5061 if ( is_valid_geocache_name ( wp->name ) ) {
50a14534 5062
c9570f86
RN
5063 if ( !separator_created ) {
5064 item = gtk_menu_item_new ();
5065 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
5066 gtk_widget_show ( item );
5067 separator_created = TRUE;
5068 }
5069
5070 item = gtk_menu_item_new_with_mnemonic ( _("_Visit Geocache Webpage") );
5071 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_waypoint_gc_webpage), pass_along );
5072 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
5073 gtk_widget_show ( item );
5074 }
5075 }
a412f3f5
RN
5076
5077 if ( wp && wp->image )
5078 {
5079 if ( !separator_created ) {
5080 item = gtk_menu_item_new ();
5081 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
5082 gtk_widget_show ( item );
5083 separator_created = TRUE;
5084 }
5085
5086 // Set up image paramater
5087 pass_along[5] = wp->image;
5088
d6de71f9
RN
5089 item = gtk_image_menu_item_new_with_mnemonic ( _("_Show Picture...") );
5090 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock ("Show Picture", GTK_ICON_SIZE_MENU) ); // Own icon - see stock_icons in vikwindow.c
a412f3f5
RN
5091 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_show_picture), pass_along );
5092 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
5093 gtk_widget_show ( item );
b3eb3b98
RN
5094
5095#ifdef VIK_CONFIG_GEOTAG
5096 GtkWidget *geotag_submenu = gtk_menu_new ();
5097 item = gtk_image_menu_item_new_with_mnemonic ( _("Update Geotag on _Image") );
5098 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REFRESH, GTK_ICON_SIZE_MENU) );
5099 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
5100 gtk_widget_show ( item );
5101 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), geotag_submenu );
5102
5103 item = gtk_menu_item_new_with_mnemonic ( _("_Update") );
5104 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_geotagging_waypoint_mtime_update), pass_along );
5105 gtk_menu_shell_append (GTK_MENU_SHELL (geotag_submenu), item);
5106 gtk_widget_show ( item );
5107
5108 item = gtk_menu_item_new_with_mnemonic ( _("Update and _Keep File Timestamp") );
5109 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_geotagging_waypoint_mtime_keep), pass_along );
5110 gtk_menu_shell_append (GTK_MENU_SHELL (geotag_submenu), item);
5111 gtk_widget_show ( item );
5112#endif
a412f3f5
RN
5113 }
5114
50a14534
EB
5115 }
5116 }
5117
5ede6aa6
RN
5118 if ( vlp && (subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINTS || subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT) )
5119 {
b66bb4ab 5120 rv = TRUE;
d6de71f9
RN
5121 item = gtk_image_menu_item_new_with_mnemonic ( _("_New Waypoint...") );
5122 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NEW, GTK_ICON_SIZE_MENU) );
5ede6aa6
RN
5123 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_wp), pass_along );
5124 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
5125 gtk_widget_show ( item );
5126 }
5127
539ba038
RN
5128 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINTS )
5129 {
d6de71f9
RN
5130 item = gtk_image_menu_item_new_with_mnemonic ( _("_View All Waypoints") );
5131 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ZOOM_FIT, GTK_ICON_SIZE_MENU) );
539ba038
RN
5132 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_waypoints_view), pass_along );
5133 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
5134 gtk_widget_show ( item );
5135
d6de71f9
RN
5136 item = gtk_image_menu_item_new_with_mnemonic ( _("Goto _Waypoint...") );
5137 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_JUMP_TO, GTK_ICON_SIZE_MENU) );
539ba038
RN
5138 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_wp), pass_along );
5139 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
5140 gtk_widget_show ( item );
c9a5cbf9 5141
d6de71f9
RN
5142 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete _All Waypoints") );
5143 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU) );
c9a5cbf9
RN
5144 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_all_waypoints), pass_along );
5145 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
5146 gtk_widget_show ( item );
20b671c3 5147
d6de71f9
RN
5148 item = gtk_image_menu_item_new_with_mnemonic ( _("_Delete Waypoints From Selection...") );
5149 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
20b671c3
RN
5150 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_waypoints_from_selection), pass_along );
5151 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
5152 gtk_widget_show ( item );
539ba038
RN
5153 }
5154
f1e68516
RN
5155 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACKS )
5156 {
5157 rv = TRUE;
5158
d6de71f9
RN
5159 item = gtk_image_menu_item_new_with_mnemonic ( _("_View All Tracks") );
5160 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ZOOM_FIT, GTK_ICON_SIZE_MENU) );
f1e68516
RN
5161 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_tracks_view), pass_along );
5162 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
5163 gtk_widget_show ( item );
c9a5cbf9 5164
d6de71f9
RN
5165 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete _All Tracks") );
5166 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU) );
c9a5cbf9
RN
5167 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_all_tracks), pass_along );
5168 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
5169 gtk_widget_show ( item );
20b671c3 5170
d6de71f9
RN
5171 item = gtk_image_menu_item_new_with_mnemonic ( _("_Delete Tracks From Selection...") );
5172 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
20b671c3
RN
5173 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_tracks_from_selection), pass_along );
5174 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
5175 gtk_widget_show ( item );
f1e68516
RN
5176 }
5177
50a14534
EB
5178 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
5179 {
5180 item = gtk_menu_item_new ();
5181 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
5182 gtk_widget_show ( item );
5183
88a49424
RN
5184 item = gtk_image_menu_item_new_with_mnemonic ( _("_View Track") );
5185 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ZOOM_FIT, GTK_ICON_SIZE_MENU) );
5186 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_track_view), pass_along );
5187 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
5188 gtk_widget_show ( item );
5189
5190 GtkWidget *goto_submenu;
937b36ed 5191 goto_submenu = gtk_menu_new ();
d6de71f9
RN
5192 item = gtk_image_menu_item_new_with_mnemonic ( _("_Goto") );
5193 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_JUMP_TO, GTK_ICON_SIZE_MENU) );
937b36ed
RN
5194 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
5195 gtk_widget_show ( item );
5196 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), goto_submenu );
5197
d6de71f9
RN
5198 item = gtk_image_menu_item_new_with_mnemonic ( _("_Startpoint") );
5199 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GOTO_FIRST, GTK_ICON_SIZE_MENU) );
50a14534 5200 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_startpoint), pass_along );
937b36ed 5201 gtk_menu_shell_append ( GTK_MENU_SHELL(goto_submenu), item );
50a14534
EB
5202 gtk_widget_show ( item );
5203
d6de71f9
RN
5204 item = gtk_image_menu_item_new_with_mnemonic ( _("\"_Center\"") );
5205 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_JUMP_TO, GTK_ICON_SIZE_MENU) );
50a14534 5206 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_center), pass_along );
937b36ed 5207 gtk_menu_shell_append ( GTK_MENU_SHELL(goto_submenu), item );
50a14534
EB
5208 gtk_widget_show ( item );
5209
d6de71f9
RN
5210 item = gtk_image_menu_item_new_with_mnemonic ( _("_Endpoint") );
5211 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GOTO_LAST, GTK_ICON_SIZE_MENU) );
50a14534 5212 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_endpoint), pass_along );
937b36ed 5213 gtk_menu_shell_append ( GTK_MENU_SHELL(goto_submenu), item );
50a14534 5214 gtk_widget_show ( item );
111fa174 5215
d6de71f9
RN
5216 item = gtk_image_menu_item_new_with_mnemonic ( _("_Highest Altitude") );
5217 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GOTO_TOP, GTK_ICON_SIZE_MENU) );
c28faca8
RN
5218 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_max_alt), pass_along );
5219 gtk_menu_shell_append ( GTK_MENU_SHELL(goto_submenu), item );
5220 gtk_widget_show ( item );
5221
d6de71f9
RN
5222 item = gtk_image_menu_item_new_with_mnemonic ( _("_Lowest Altitude") );
5223 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GOTO_BOTTOM, GTK_ICON_SIZE_MENU) );
c28faca8
RN
5224 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_min_alt), pass_along );
5225 gtk_menu_shell_append ( GTK_MENU_SHELL(goto_submenu), item );
5226 gtk_widget_show ( item );
5227
d6de71f9
RN
5228 item = gtk_image_menu_item_new_with_mnemonic ( _("_Maximum Speed") );
5229 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_MEDIA_FORWARD, GTK_ICON_SIZE_MENU) );
03e7da75
RN
5230 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_max_speed), pass_along );
5231 gtk_menu_shell_append ( GTK_MENU_SHELL(goto_submenu), item );
5232 gtk_widget_show ( item );
5233
59f9414d
RN
5234 GtkWidget *combine_submenu;
5235 combine_submenu = gtk_menu_new ();
5236 item = gtk_image_menu_item_new_with_mnemonic ( _("Co_mbine") );
5237 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_CONNECT, GTK_ICON_SIZE_MENU) );
5238 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
469113fb 5239 gtk_widget_show ( item );
59f9414d 5240 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), combine_submenu );
469113fb 5241
7306a492 5242 item = gtk_menu_item_new_with_mnemonic ( _("_Merge By Time...") );
111fa174 5243 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_merge_by_timestamp), pass_along );
59f9414d 5244 gtk_menu_shell_append ( GTK_MENU_SHELL(combine_submenu), item );
111fa174
AF
5245 gtk_widget_show ( item );
5246
40a68e7c 5247 item = gtk_menu_item_new_with_mnemonic ( _("Merge _With Other Tracks...") );
291edcab 5248 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_merge_with_other), pass_along );
59f9414d 5249 gtk_menu_shell_append ( GTK_MENU_SHELL(combine_submenu), item );
291edcab
HR
5250 gtk_widget_show ( item );
5251
24774c43
RN
5252 item = gtk_menu_item_new_with_mnemonic ( _("Merge _Segments") );
5253 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_merge_by_segment), pass_along );
5254 gtk_menu_shell_append ( GTK_MENU_SHELL(combine_submenu), item );
5255 gtk_widget_show ( item );
5256
9c34f614
RN
5257 item = gtk_menu_item_new_with_mnemonic ( _("_Append Track...") );
5258 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_append_track), pass_along );
5259 gtk_menu_shell_append ( GTK_MENU_SHELL(combine_submenu), item );
5260 gtk_widget_show ( item );
5261
78ac928c
RN
5262 GtkWidget *split_submenu;
5263 split_submenu = gtk_menu_new ();
5264 item = gtk_image_menu_item_new_with_mnemonic ( _("_Split") );
5265 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_DISCONNECT, GTK_ICON_SIZE_MENU) );
5266 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
291edcab 5267 gtk_widget_show ( item );
78ac928c 5268 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), split_submenu );
291edcab 5269
7306a492 5270 item = gtk_menu_item_new_with_mnemonic ( _("_Split By Time...") );
111fa174 5271 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_split_by_timestamp), pass_along );
78ac928c 5272 gtk_menu_shell_append ( GTK_MENU_SHELL(split_submenu), item );
111fa174 5273 gtk_widget_show ( item );
7114e879 5274
7306a492 5275 item = gtk_menu_item_new_with_mnemonic ( _("Split By _Number of Points...") );
af2341f3 5276 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_split_by_n_points), pass_along );
78ac928c 5277 gtk_menu_shell_append ( GTK_MENU_SHELL(split_submenu), item );
af2341f3
RN
5278 gtk_widget_show ( item );
5279
98e87078
RN
5280 // ATM always enable this entry - don't want to have to analyse the track before displaying the menu - to keep the menu speedy
5281 item = gtk_menu_item_new_with_mnemonic ( _("Split Se_gments") );
5282 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_split_segments), pass_along );
5283 gtk_menu_shell_append ( GTK_MENU_SHELL(split_submenu), item );
5284 gtk_widget_show ( item );
5285
a4aefe69
RN
5286 item = gtk_menu_item_new_with_mnemonic ( _("Split at _Trackpoint") );
5287 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_split_at_trackpoint), pass_along );
5288 gtk_menu_shell_append ( GTK_MENU_SHELL(split_submenu), item );
5289 gtk_widget_show ( item );
5290 // Make it available only when a trackpoint is selected.
5291 gtk_widget_set_sensitive ( item, (gboolean)GPOINTER_TO_INT(l->current_tpl) );
5292
b6eda120
RN
5293 GtkWidget *delete_submenu;
5294 delete_submenu = gtk_menu_new ();
5295 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete Poi_nts") );
5296 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_DELETE, GTK_ICON_SIZE_MENU) );
5297 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
5298 gtk_widget_show ( item );
5299 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), delete_submenu );
5300
5301 item = gtk_menu_item_new_with_mnemonic ( _("Delete Points With The Same _Position") );
5302 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_points_same_position), pass_along );
5303 gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
5304 gtk_widget_show ( item );
5305
6579ca1f
RN
5306 item = gtk_menu_item_new_with_mnemonic ( _("Delete Points With The Same _Time") );
5307 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_points_same_time), pass_along );
5308 gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
af2341f3
RN
5309 gtk_widget_show ( item );
5310
eb9fd106
RN
5311 item = gtk_image_menu_item_new_with_mnemonic ( _("_Reverse Track") );
5312 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_BACK, GTK_ICON_SIZE_MENU) );
5313 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_reverse), pass_along );
5314 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
5315 gtk_widget_show ( item );
5316
6bb72350
RN
5317 /* ATM This function is only available via the layers panel, due to the method in finding out the maps in use */
5318 if ( vlp ) {
d6de71f9
RN
5319 item = gtk_image_menu_item_new_with_mnemonic ( _("Down_load Maps Along Track...") );
5320 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock ("Maps Download", GTK_ICON_SIZE_MENU) ); // Own icon - see stock_icons in vikwindow.c
6bb72350
RN
5321 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_download_map_along_track_cb), pass_along );
5322 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
5323 gtk_widget_show ( item );
5324 }
ad0a8c2d 5325
d6de71f9
RN
5326 item = gtk_image_menu_item_new_with_mnemonic ( _("_Apply DEM Data") );
5327 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock ("DEM Download/Import", GTK_ICON_SIZE_MENU) ); // Own icon - see stock_icons in vikwindow.c
ad0a8c2d
EB
5328 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_apply_dem_data), pass_along );
5329 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
8fb71d6c
EB
5330 gtk_widget_show ( item );
5331
d6de71f9
RN
5332 item = gtk_image_menu_item_new_with_mnemonic ( _("Export Trac_k as GPX...") );
5333 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_HARDDISK, GTK_ICON_SIZE_MENU) );
7f6757c4
RN
5334 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_gpx_track), pass_along );
5335 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
5336 gtk_widget_show ( item );
5337
d6de71f9
RN
5338 item = gtk_image_menu_item_new_with_mnemonic ( _("E_xtend Track End") );
5339 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ADD, GTK_ICON_SIZE_MENU) );
8fb71d6c
EB
5340 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_extend_track_end), pass_along );
5341 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
ad0a8c2d 5342 gtk_widget_show ( item );
5092de80 5343
55340efa 5344#ifdef VIK_CONFIG_GOOGLE
d6de71f9
RN
5345 item = gtk_image_menu_item_new_with_mnemonic ( _("Extend _Using Route Finder") );
5346 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock ("Route Finder", GTK_ICON_SIZE_MENU) ); // Own icon - see stock_icons in vikwindow.c
7ff7d728 5347 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_extend_track_end_route_finder), pass_along );
a7955c1d
AM
5348 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
5349 gtk_widget_show ( item );
ebf1bd39 5350#endif
a7955c1d 5351
e50758c7
RN
5352 GtkWidget *upload_submenu = gtk_menu_new ();
5353 item = gtk_image_menu_item_new_with_mnemonic ( _("_Upload") );
5354 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_UP, GTK_ICON_SIZE_MENU) );
5355 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
5356 gtk_widget_show ( item );
5357 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), upload_submenu );
5358
5092de80 5359#ifdef VIK_CONFIG_OPENSTREETMAP
d6de71f9 5360 item = gtk_image_menu_item_new_with_mnemonic ( _("Upload to _OSM...") );
ce4bd1cf
RN
5361 // Convert internal pointer into actual track for usage outside this file
5362 pass_along[7] = g_hash_table_lookup ( l->tracks, sublayer);
d6de71f9 5363 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_UP, GTK_ICON_SIZE_MENU) );
e50758c7
RN
5364 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(osm_traces_upload_track_cb), pass_along );
5365 gtk_menu_shell_append ( GTK_MENU_SHELL(upload_submenu), item );
5092de80
GB
5366 gtk_widget_show ( item );
5367#endif
bddd2056 5368
e50758c7
RN
5369 item = gtk_image_menu_item_new_with_mnemonic ( _("_Upload to GPS...") );
5370 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_FORWARD, GTK_ICON_SIZE_MENU) );
5371 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_gps_upload_any), pass_along );
5372 gtk_menu_shell_append ( GTK_MENU_SHELL(upload_submenu), item );
5373 gtk_widget_show ( item );
5374
55340efa 5375#ifdef VIK_CONFIG_GOOGLE
ce4bd1cf 5376 if ( is_valid_google_route ( l, sublayer ) )
bddd2056 5377 {
d6de71f9
RN
5378 item = gtk_image_menu_item_new_with_mnemonic ( _("_View Google Directions") );
5379 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NETWORK, GTK_ICON_SIZE_MENU) );
bddd2056
EB
5380 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_track_google_route_webpage), pass_along );
5381 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
5382 gtk_widget_show ( item );
5383 }
ff1f2b3e 5384#endif
bddd2056 5385
d6de71f9
RN
5386 item = gtk_image_menu_item_new_with_mnemonic ( _("Use with _Filter") );
5387 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
28c82d8b
EB
5388 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_track_use_with_filter), pass_along );
5389 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
5390 gtk_widget_show ( item );
5391
6bb72350
RN
5392 /* ATM This function is only available via the layers panel, due to needing a vlp */
5393 if ( vlp ) {
5394 item = a_acquire_track_menu ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(l)), vlp,
5395 vik_layers_panel_get_viewport(VIK_LAYERS_PANEL(vlp)),
5396 g_hash_table_lookup ( l->tracks, (gchar *) sublayer ) );
5397 if ( item ) {
5398 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
5399 gtk_widget_show ( item );
5400 }
5401 }
c95d6b00 5402
b3eb3b98
RN
5403#ifdef VIK_CONFIG_GEOTAG
5404 item = gtk_menu_item_new_with_mnemonic ( _("Geotag _Images...") );
5405 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_geotagging_track), pass_along );
5406 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
5407 gtk_widget_show ( item );
5408#endif
5409
c95d6b00
RN
5410 // Only show on viewport popmenu when a trackpoint is selected
5411 if ( ! vlp && l->current_tpl ) {
5412 // Add separator
5413 item = gtk_menu_item_new ();
5414 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
5415 gtk_widget_show ( item );
5416
5417 item = gtk_image_menu_item_new_with_mnemonic ( _("_Edit Trackpoint") );
5418 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_PROPERTIES, GTK_ICON_SIZE_MENU) );
5419 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_edit_trackpoint), pass_along );
5420 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
5421 gtk_widget_show ( item );
5422 }
5423
50a14534
EB
5424 }
5425
50a14534
EB
5426 return rv;
5427}
5428
db79f75f
RN
5429static void trw_layer_insert_tp_after_current_tp ( VikTrwLayer *vtl )
5430{
5431 /* sanity checks */
5432 if (!vtl->current_tpl)
5433 return;
5434 if (!vtl->current_tpl->next)
5435 return;
5436
5437 VikTrackpoint *tp_current = VIK_TRACKPOINT(vtl->current_tpl->data);
5438 VikTrackpoint *tp_next = VIK_TRACKPOINT(vtl->current_tpl->next->data);
5439
5440 /* Use current and next trackpoints to form a new track point which is inserted into the tracklist */
5441 if ( tp_next ) {
5442
5443 VikTrackpoint *tp_new = vik_trackpoint_new();
5444 struct LatLon ll_current, ll_next;
5445 vik_coord_to_latlon ( &tp_current->coord, &ll_current );
5446 vik_coord_to_latlon ( &tp_next->coord, &ll_next );
5447
5448 /* main positional interpolation */
5449 struct LatLon ll_new = { (ll_current.lat+ll_next.lat)/2, (ll_current.lon+ll_next.lon)/2 };
5450 vik_coord_load_from_latlon ( &(tp_new->coord), vtl->coord_mode, &ll_new );
5451
5452 /* Now other properties that can be interpolated */
5453 tp_new->altitude = (tp_current->altitude + tp_next->altitude) / 2;
5454
5455 if (tp_current->has_timestamp && tp_next->has_timestamp) {
5456 /* Note here the division is applied to each part, then added
5457 This is to avoid potential overflow issues with a 32 time_t for dates after midpoint of this Unix time on 2004/01/04 */
5458 tp_new->timestamp = (tp_current->timestamp/2) + (tp_next->timestamp/2);
5459 tp_new->has_timestamp = TRUE;
5460 }
5461
5462 if (tp_current->speed != NAN && tp_next->speed != NAN)
5463 tp_new->speed = (tp_current->speed + tp_next->speed)/2;
5464
5465 /* TODO - improve interpolation of course, as it may not be correct.
5466 if courses in degrees are 350 + 020, the mid course more likely to be 005 (not 185)
5467 [similar applies if value is in radians] */
5468 if (tp_current->course != NAN && tp_next->course != NAN)
5469 tp_new->speed = (tp_current->course + tp_next->course)/2;
5470
5471 /* DOP / sat values remain at defaults as not they do not seem applicable to a dreamt up point */
5472
5473 /* Insert new point into the trackpoints list after the current TP */
ce4bd1cf 5474 VikTrack *tr = g_hash_table_lookup ( vtl->tracks, vtl->current_tp_id );
db79f75f
RN
5475 gint index = g_list_index ( tr->trackpoints, tp_current );
5476 if ( index > -1 ) {
5477 tr->trackpoints = g_list_insert (tr->trackpoints, tp_new, index+1 );
5478 }
5479 }
5480}
50a14534 5481
50a14534
EB
5482static void trw_layer_cancel_current_tp ( VikTrwLayer *vtl, gboolean destroy )
5483{
5484 if ( vtl->tpwin )
5485 {
5486 if ( destroy)
5487 {
5488 gtk_widget_destroy ( GTK_WIDGET(vtl->tpwin) );
5489 vtl->tpwin = NULL;
5490 }
5491 else
5492 vik_trw_layer_tpwin_set_empty ( vtl->tpwin );
5493 }
5494 if ( vtl->current_tpl )
5495 {
5496 vtl->current_tpl = NULL;
ce4bd1cf
RN
5497 vtl->current_tp_track = NULL;
5498 vtl->current_tp_id = NULL;
f01eebd4 5499 vik_layer_emit_update(VIK_LAYER(vtl), FALSE);
50a14534
EB
5500 }
5501}
5502
5503static void trw_layer_tpwin_response ( VikTrwLayer *vtl, gint response )
5504{
5505 g_assert ( vtl->tpwin != NULL );
5506 if ( response == VIK_TRW_LAYER_TPWIN_CLOSE )
5507 trw_layer_cancel_current_tp ( vtl, TRUE );
0d601fd4
RN
5508
5509 if ( vtl->current_tpl == NULL )
5510 return;
5511
5512 if ( response == VIK_TRW_LAYER_TPWIN_SPLIT && vtl->current_tpl->next && vtl->current_tpl->prev )
50a14534 5513 {
84d3d9f9
RN
5514 trw_layer_split_at_selected_trackpoint ( vtl );
5515 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, vtl->current_tp_track->name );
50a14534
EB
5516 }
5517 else if ( response == VIK_TRW_LAYER_TPWIN_DELETE )
5518 {
ce4bd1cf
RN
5519 VikTrack *tr = g_hash_table_lookup ( vtl->tracks, vtl->current_tp_id );
5520 if ( tr == NULL )
5521 return;
50a14534 5522
ce4bd1cf 5523 GList *new_tpl;
50a14534 5524
ab4553c3 5525 // Find available adjacent trackpoint
50a14534
EB
5526 if ( (new_tpl = vtl->current_tpl->next) || (new_tpl = vtl->current_tpl->prev) )
5527 {
5528 if ( VIK_TRACKPOINT(vtl->current_tpl->data)->newsegment && vtl->current_tpl->next )
5529 VIK_TRACKPOINT(vtl->current_tpl->next->data)->newsegment = TRUE; /* don't concat segments on del */
5530
ab4553c3
RN
5531 // Delete current trackpoint
5532 vik_trackpoint_free ( vtl->current_tpl->data );
5533 tr->trackpoints = g_list_delete_link ( tr->trackpoints, vtl->current_tpl );
50a14534 5534
ab4553c3
RN
5535 // Set to current to the available adjacent trackpoint
5536 vtl->current_tpl = new_tpl;
5537
5538 // Reset dialog with the available adjacent trackpoint
ce4bd1cf
RN
5539 if ( vtl->current_tp_track )
5540 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, new_tpl, vtl->current_tp_track->name );
50a14534 5541
f01eebd4 5542 vik_layer_emit_update(VIK_LAYER(vtl), FALSE);
50a14534
EB
5543 }
5544 else
5545 {
ab4553c3
RN
5546 // Delete current trackpoint
5547 vik_trackpoint_free ( vtl->current_tpl->data );
5548 tr->trackpoints = g_list_delete_link ( tr->trackpoints, vtl->current_tpl );
50a14534
EB
5549 trw_layer_cancel_current_tp ( vtl, FALSE );
5550 }
5551 }
5552 else if ( response == VIK_TRW_LAYER_TPWIN_FORWARD && vtl->current_tpl->next )
5553 {
ce4bd1cf
RN
5554 if ( vtl->current_tp_track )
5555 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl = vtl->current_tpl->next, vtl->current_tp_track->name );
f01eebd4 5556 vik_layer_emit_update(VIK_LAYER(vtl), FALSE); /* TODO longone: either move or only update if tp is inside drawing window */
50a14534
EB
5557 }
5558 else if ( response == VIK_TRW_LAYER_TPWIN_BACK && vtl->current_tpl->prev )
5559 {
ce4bd1cf
RN
5560 if ( vtl->current_tp_track )
5561 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl = vtl->current_tpl->prev, vtl->current_tp_track->name );
f01eebd4 5562 vik_layer_emit_update(VIK_LAYER(vtl), FALSE);
50a14534 5563 }
2880a1de
RN
5564 else if ( response == VIK_TRW_LAYER_TPWIN_INSERT && vtl->current_tpl->next )
5565 {
5566 trw_layer_insert_tp_after_current_tp ( vtl );
f01eebd4 5567 vik_layer_emit_update(VIK_LAYER(vtl), FALSE);
2880a1de 5568 }
50a14534 5569 else if ( response == VIK_TRW_LAYER_TPWIN_DATA_CHANGED )
f01eebd4 5570 vik_layer_emit_update(VIK_LAYER(vtl), FALSE);
50a14534
EB
5571}
5572
5573static void trw_layer_tpwin_init ( VikTrwLayer *vtl )
5574{
5575 if ( ! vtl->tpwin )
5576 {
5577 vtl->tpwin = vik_trw_layer_tpwin_new ( VIK_GTK_WINDOW_FROM_LAYER(vtl) );
5578 g_signal_connect_swapped ( GTK_DIALOG(vtl->tpwin), "response", G_CALLBACK(trw_layer_tpwin_response), vtl );
5579 /* connect signals -- DELETE SIGNAL VERY IMPORTANT TO SET TO NULL */
5580 g_signal_connect_swapped ( vtl->tpwin, "delete-event", G_CALLBACK(trw_layer_cancel_current_tp), vtl );
5581 gtk_widget_show_all ( GTK_WIDGET(vtl->tpwin) );
5582 }
5583 if ( vtl->current_tpl )
ce4bd1cf
RN
5584 if ( vtl->current_tp_track )
5585 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, vtl->current_tp_track->name );
50a14534
EB
5586 /* set layer name and TP data */
5587}
5588
941aa6e9
AF
5589/***************************************************************************
5590 ** Tool code
5591 ***************************************************************************/
50a14534 5592
941aa6e9 5593/*** Utility data structures and functions ****/
50a14534
EB
5594
5595typedef struct {
5596 gint x, y;
5597 gint closest_x, closest_y;
c9570f86 5598 gpointer *closest_wp_id;
50a14534
EB
5599 VikWaypoint *closest_wp;
5600 VikViewport *vvp;
5601} WPSearchParams;
5602
941aa6e9
AF
5603typedef struct {
5604 gint x, y;
5605 gint closest_x, closest_y;
ce4bd1cf 5606 gpointer closest_track_id;
941aa6e9
AF
5607 VikTrackpoint *closest_tp;
5608 VikViewport *vvp;
5609 GList *closest_tpl;
5610} TPSearchParams;
5611
c9570f86 5612static void waypoint_search_closest_tp ( gpointer id, VikWaypoint *wp, WPSearchParams *params )
50a14534
EB
5613{
5614 gint x, y;
5615 if ( !wp->visible )
5616 return;
5617
5618 vik_viewport_coord_to_screen ( params->vvp, &(wp->coord), &x, &y );
59b0a97a
RN
5619
5620 // If waypoint has an image then use the image size to select
5621 if ( wp->image ) {
5622 gint slackx, slacky;
5623 slackx = wp->image_width / 2;
5624 slacky = wp->image_height / 2;
5625
5626 if ( x <= params->x + slackx && x >= params->x - slackx
5627 && y <= params->y + slacky && y >= params->y - slacky ) {
c9570f86 5628 params->closest_wp_id = id;
59b0a97a
RN
5629 params->closest_wp = wp;
5630 params->closest_x = x;
5631 params->closest_y = y;
5632 }
50a14534 5633 }
59b0a97a
RN
5634 else if ( abs (x - params->x) <= WAYPOINT_SIZE_APPROX && abs (y - params->y) <= WAYPOINT_SIZE_APPROX &&
5635 ((!params->closest_wp) || /* was the old waypoint we already found closer than this one? */
5636 abs(x - params->x)+abs(y - params->y) < abs(x - params->closest_x)+abs(y - params->closest_y)))
5637 {
c9570f86 5638 params->closest_wp_id = id;
59b0a97a
RN
5639 params->closest_wp = wp;
5640 params->closest_x = x;
5641 params->closest_y = y;
5642 }
50a14534
EB
5643}
5644
ce4bd1cf 5645static void track_search_closest_tp ( gpointer id, VikTrack *t, TPSearchParams *params )
50a14534 5646{
941aa6e9
AF
5647 GList *tpl = t->trackpoints;
5648 VikTrackpoint *tp;
50a14534 5649
941aa6e9
AF
5650 if ( !t->visible )
5651 return;
50a14534 5652
941aa6e9
AF
5653 while (tpl)
5654 {
5655 gint x, y;
5656 tp = VIK_TRACKPOINT(tpl->data);
5657
5658 vik_viewport_coord_to_screen ( params->vvp, &(tp->coord), &x, &y );
5659
5660 if ( abs (x - params->x) <= TRACKPOINT_SIZE_APPROX && abs (y - params->y) <= TRACKPOINT_SIZE_APPROX &&
5661 ((!params->closest_tp) || /* was the old trackpoint we already found closer than this one? */
5662 abs(x - params->x)+abs(y - params->y) < abs(x - params->closest_x)+abs(y - params->closest_y)))
50a14534 5663 {
ce4bd1cf 5664 params->closest_track_id = id;
941aa6e9
AF
5665 params->closest_tp = tp;
5666 params->closest_tpl = tpl;
5667 params->closest_x = x;
5668 params->closest_y = y;
50a14534 5669 }
941aa6e9 5670 tpl = tpl->next;
50a14534 5671 }
941aa6e9
AF
5672}
5673
5674static VikTrackpoint *closest_tp_in_five_pixel_interval ( VikTrwLayer *vtl, VikViewport *vvp, gint x, gint y )
5675{
5676 TPSearchParams params;
5677 params.x = x;
5678 params.y = y;
5679 params.vvp = vvp;
ce4bd1cf 5680 params.closest_track_id = NULL;
941aa6e9
AF
5681 params.closest_tp = NULL;
5682 g_hash_table_foreach ( vtl->tracks, (GHFunc) track_search_closest_tp, &params);
5683 return params.closest_tp;
50a14534
EB
5684}
5685
5686static VikWaypoint *closest_wp_in_five_pixel_interval ( VikTrwLayer *vtl, VikViewport *vvp, gint x, gint y )
5687{
5688 WPSearchParams params;
5689 params.x = x;
5690 params.y = y;
5691 params.vvp = vvp;
5692 params.closest_wp = NULL;
c9570f86 5693 params.closest_wp_id = NULL;
50a14534
EB
5694 g_hash_table_foreach ( vtl->waypoints, (GHFunc) waypoint_search_closest_tp, &params);
5695 return params.closest_wp;
5696}
5697
95d1b757 5698
08f14055
RN
5699// Some forward declarations
5700static void marker_begin_move ( tool_ed_t *t, gint x, gint y );
5701static void marker_moveto ( tool_ed_t *t, gint x, gint y );
5702static void marker_end_move ( tool_ed_t *t );
5703//
5704
5705static gboolean trw_layer_select_move ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp, tool_ed_t* t )
5706{
5707 if ( t->holding ) {
5708 VikCoord new_coord;
5709 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
5710
5711 // Here always allow snapping back to the original location
5712 // this is useful when one decides not to move the thing afterall
5713 // If one wants to move the item only a little bit then don't hold down the 'snap' key!
5714
5715 // snap to TP
5716 if ( event->state & GDK_CONTROL_MASK )
5717 {
5718 VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
5719 if ( tp )
5720 new_coord = tp->coord;
5721 }
5722
5723 // snap to WP
5724 if ( event->state & GDK_SHIFT_MASK )
5725 {
5726 VikWaypoint *wp = closest_wp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
5727 if ( wp )
5728 new_coord = wp->coord;
5729 }
5730
5731 gint x, y;
5732 vik_viewport_coord_to_screen ( vvp, &new_coord, &x, &y );
5733
5734 marker_moveto ( t, x, y );
5735
5736 return TRUE;
5737 }
5738 return FALSE;
5739}
5740
5741static gboolean trw_layer_select_release ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp, tool_ed_t* t )
5742{
5743 if ( t->holding && event->button == 1 )
5744 {
5745 VikCoord new_coord;
5746 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
5747
5748 // snap to TP
5749 if ( event->state & GDK_CONTROL_MASK )
5750 {
5751 VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
5752 if ( tp )
5753 new_coord = tp->coord;
5754 }
5755
5756 // snap to WP
5757 if ( event->state & GDK_SHIFT_MASK )
5758 {
5759 VikWaypoint *wp = closest_wp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
5760 if ( wp )
5761 new_coord = wp->coord;
5762 }
5763
5764 marker_end_move ( t );
5765
5766 // Determine if working on a waypoint or a trackpoint
5767 if ( t->is_waypoint )
5768 vtl->current_wp->coord = new_coord;
5769 else {
5770 if ( vtl->current_tpl ) {
ce4bd1cf 5771 VIK_TRACKPOINT(vtl->current_tpl->data)->coord = new_coord;
08f14055
RN
5772
5773 if ( vtl->tpwin )
ce4bd1cf
RN
5774 if ( vtl->current_tp_track )
5775 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, vtl->current_tp_track->name );
08f14055
RN
5776 }
5777 }
5778
5779 // Reset
c9570f86
RN
5780 vtl->current_wp = NULL;
5781 vtl->current_wp_id = NULL;
08f14055
RN
5782 trw_layer_cancel_current_tp ( vtl, FALSE );
5783
f01eebd4 5784 vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
08f14055
RN
5785 return TRUE;
5786 }
5787 return FALSE;
5788}
5789
77ad64fa
RN
5790/*
5791 Returns true if a waypoint or track is found near the requested event position for this particular layer
5792 The item found is automatically selected
5793 This is a tool like feature but routed via the layer interface, since it's instigated by a 'global' layer tool in vikwindow.c
5794 */
08f14055 5795static gboolean trw_layer_select_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp, tool_ed_t* tet )
77ad64fa
RN
5796{
5797 if ( event->button != 1 )
5798 return FALSE;
5799
5800 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
5801 return FALSE;
5802
5803 if ( !vtl->tracks_visible && !vtl->waypoints_visible )
5804 return FALSE;
5805
08f14055 5806 // 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
5807
5808 if (vtl->waypoints_visible) {
5809 WPSearchParams wp_params;
5810 wp_params.vvp = vvp;
5811 wp_params.x = event->x;
5812 wp_params.y = event->y;
c9570f86 5813 wp_params.closest_wp_id = NULL;
77ad64fa
RN
5814 wp_params.closest_wp = NULL;
5815
5816 g_hash_table_foreach ( vtl->waypoints, (GHFunc) waypoint_search_closest_tp, &wp_params);
5817
5818 if ( wp_params.closest_wp ) {
08f14055
RN
5819
5820 // Select
c9570f86 5821 vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, g_hash_table_lookup ( vtl->waypoints_iters, wp_params.closest_wp_id ), TRUE );
08f14055
RN
5822
5823 // Too easy to move it so must be holding shift to start immediately moving it
3c73e6c4 5824 // or otherwise be previously selected but not have an image (otherwise clicking within image bounds (again) moves it)
08f14055 5825 if ( event->state & GDK_SHIFT_MASK ||
3c73e6c4 5826 ( vtl->current_wp == wp_params.closest_wp && !vtl->current_wp->image ) ) {
08f14055
RN
5827 // Put into 'move buffer'
5828 // NB vvp & vw already set in tet
5829 tet->vtl = (gpointer)vtl;
5830 tet->is_waypoint = TRUE;
5831
5832 marker_begin_move (tet, event->x, event->y);
5833 }
5834
c9570f86
RN
5835 vtl->current_wp = wp_params.closest_wp;
5836 vtl->current_wp_id = wp_params.closest_wp_id;
08f14055 5837
f01eebd4 5838 vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
08f14055 5839
77ad64fa
RN
5840 return TRUE;
5841 }
5842 }
5843
5844 if (vtl->tracks_visible) {
5845 TPSearchParams tp_params;
5846 tp_params.vvp = vvp;
5847 tp_params.x = event->x;
5848 tp_params.y = event->y;
ce4bd1cf 5849 tp_params.closest_track_id = NULL;
77ad64fa
RN
5850 tp_params.closest_tp = NULL;
5851
5852 g_hash_table_foreach ( vtl->tracks, (GHFunc) track_search_closest_tp, &tp_params);
5853
5854 if ( tp_params.closest_tp ) {
08f14055
RN
5855
5856 // Always select + highlight the track
ce4bd1cf 5857 vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, g_hash_table_lookup ( vtl->tracks_iters, tp_params.closest_track_id ), TRUE );
08f14055
RN
5858
5859 tet->is_waypoint = FALSE;
5860
5861 // Select the Trackpoint
5862 // Can move it immediately when control held or it's the previously selected tp
5863 if ( event->state & GDK_CONTROL_MASK ||
5864 vtl->current_tpl == tp_params.closest_tpl ) {
5865 // Put into 'move buffer'
5866 // NB vvp & vw already set in tet
5867 tet->vtl = (gpointer)vtl;
5868 marker_begin_move (tet, event->x, event->y);
5869 }
5870
5871 vtl->current_tpl = tp_params.closest_tpl;
ce4bd1cf
RN
5872 vtl->current_tp_id = tp_params.closest_track_id;
5873 vtl->current_tp_track = g_hash_table_lookup ( vtl->tracks, tp_params.closest_track_id );
08f14055 5874
95d1b757
RN
5875 set_statusbar_msg_info_trkpt ( vtl, tp_params.closest_tp );
5876
08f14055 5877 if ( vtl->tpwin )
ce4bd1cf 5878 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, vtl->current_tp_track->name );
08f14055 5879
f01eebd4 5880 vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
77ad64fa
RN
5881 return TRUE;
5882 }
5883 }
5884
5885 /* these aren't the droids you're looking for */
c9570f86
RN
5886 vtl->current_wp = NULL;
5887 vtl->current_wp_id = NULL;
08f14055
RN
5888 trw_layer_cancel_current_tp ( vtl, FALSE );
5889
95d1b757
RN
5890 // Blank info
5891 vik_statusbar_set_message ( vik_window_get_statusbar (VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl))), VIK_STATUSBAR_INFO, "" );
5892
77ad64fa
RN
5893 return FALSE;
5894}
5895
e46f259a
RN
5896static gboolean trw_layer_show_selected_viewport_menu ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
5897{
5898 if ( event->button != 3 )
5899 return FALSE;
5900
5901 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
5902 return FALSE;
5903
5904 if ( !vtl->tracks_visible && !vtl->waypoints_visible )
5905 return FALSE;
5906
5907 /* Post menu for the currently selected item */
5908
5909 /* See if a track is selected */
5910 VikTrack *track = (VikTrack*)vik_window_get_selected_track ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(vtl) );
5911 if ( track && track->visible ) {
5912
60a69560 5913 if ( track->name ) {
e46f259a
RN
5914
5915 if ( vtl->track_right_click_menu )
60a69560 5916 gtk_object_sink ( GTK_OBJECT(vtl->track_right_click_menu) );
e46f259a
RN
5917
5918 vtl->track_right_click_menu = GTK_MENU ( gtk_menu_new () );
5919
60a69560
RN
5920 trku_udata udataU;
5921 udataU.trk = track;
5922 udataU.uuid = NULL;
5923
5924 gpointer *trkf = g_hash_table_find ( vtl->tracks, (GHRFunc) trw_layer_track_find_uuid, &udataU );
5925
5926 if ( trkf && udataU.uuid ) {
5927
5928 GtkTreeIter *iter = g_hash_table_lookup ( vtl->tracks_iters, udataU.uuid );
5929
5930 trw_layer_sublayer_add_menu_items ( vtl,
5931 vtl->track_right_click_menu,
5932 NULL,
5933 VIK_TRW_LAYER_SUBLAYER_TRACK,
5934 udataU.uuid,
5935 iter,
5936 vvp );
5937 }
e46f259a
RN
5938
5939 gtk_menu_popup ( vtl->track_right_click_menu, NULL, NULL, NULL, NULL, event->button, gtk_get_current_event_time() );
5940
5941 return TRUE;
5942 }
5943 }
5944
5945 /* See if a waypoint is selected */
5946 VikWaypoint *waypoint = (VikWaypoint*)vik_window_get_selected_waypoint ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(vtl) );
5947 if ( waypoint && waypoint->visible ) {
60a69560 5948 if ( waypoint->name ) {
e46f259a
RN
5949
5950 if ( vtl->wp_right_click_menu )
60a69560 5951 gtk_object_sink ( GTK_OBJECT(vtl->wp_right_click_menu) );
e46f259a
RN
5952
5953 vtl->wp_right_click_menu = GTK_MENU ( gtk_menu_new () );
60a69560
RN
5954
5955 wpu_udata udata;
5956 udata.wp = waypoint;
5957 udata.uuid = NULL;
5958
5959 gpointer *wpf = g_hash_table_find ( vtl->waypoints, (GHRFunc) trw_layer_waypoint_find_uuid, (gpointer) &udata );
5960
5961 if ( wpf && udata.uuid ) {
5962 GtkTreeIter *iter = g_hash_table_lookup ( vtl->waypoints_iters, udata.uuid );
5963
5964 trw_layer_sublayer_add_menu_items ( vtl,
5965 vtl->wp_right_click_menu,
5966 NULL,
5967 VIK_TRW_LAYER_SUBLAYER_WAYPOINT,
5968 udata.uuid,
5969 iter,
5970 vvp );
5971 }
e46f259a
RN
5972 gtk_menu_popup ( vtl->wp_right_click_menu, NULL, NULL, NULL, NULL, event->button, gtk_get_current_event_time() );
5973
5974 return TRUE;
5975 }
5976 }
5977
5978 return FALSE;
5979}
5980
7432fddf
AF
5981/* background drawing hook, to be passed the viewport */
5982static gboolean tool_sync_done = TRUE;
5983
5984static gboolean tool_sync(gpointer data)
5985{
5986 VikViewport *vvp = data;
5987 gdk_threads_enter();
5988 vik_viewport_sync(vvp);
5989 tool_sync_done = TRUE;
5990 gdk_threads_leave();
5991 return FALSE;
5992}
5993
7432fddf
AF
5994static void marker_begin_move ( tool_ed_t *t, gint x, gint y )
5995{
5996 t->holding = TRUE;
5997 t->gc = vik_viewport_new_gc (t->vvp, "black", 2);
5998 gdk_gc_set_function ( t->gc, GDK_INVERT );
5999 vik_viewport_draw_rectangle ( t->vvp, t->gc, FALSE, x-3, y-3, 6, 6 );
6000 vik_viewport_sync(t->vvp);
6001 t->oldx = x;
6002 t->oldy = y;
6003}
6004
6005static void marker_moveto ( tool_ed_t *t, gint x, gint y )
6006{
6007 VikViewport *vvp = t->vvp;
6008 vik_viewport_draw_rectangle ( vvp, t->gc, FALSE, t->oldx-3, t->oldy-3, 6, 6 );
6009 vik_viewport_draw_rectangle ( vvp, t->gc, FALSE, x-3, y-3, 6, 6 );
6010 t->oldx = x;
6011 t->oldy = y;
7b203521 6012
7432fddf
AF
6013 if (tool_sync_done) {
6014 g_idle_add_full (G_PRIORITY_HIGH_IDLE + 10, tool_sync, vvp, NULL);
6015 tool_sync_done = FALSE;
6016 }
6017}
6018
6019static void marker_end_move ( tool_ed_t *t )
6020{
6021 vik_viewport_draw_rectangle ( t->vvp, t->gc, FALSE, t->oldx-3, t->oldy-3, 6, 6 );
6022 g_object_unref ( t->gc );
6023 t->holding = FALSE;
6024}
6025
941aa6e9
AF
6026/*** Edit waypoint ****/
6027
6028static gpointer tool_edit_waypoint_create ( VikWindow *vw, VikViewport *vvp)
6029{
7432fddf
AF
6030 tool_ed_t *t = g_new(tool_ed_t, 1);
6031 t->vvp = vvp;
6032 t->holding = FALSE;
6033 return t;
941aa6e9
AF
6034}
6035
7432fddf 6036static gboolean tool_edit_waypoint_click ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data )
50a14534
EB
6037{
6038 WPSearchParams params;
7432fddf
AF
6039 tool_ed_t *t = data;
6040 VikViewport *vvp = t->vvp;
50a14534 6041
941aa6e9
AF
6042 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
6043 return FALSE;
7432fddf
AF
6044
6045 if ( t->holding ) {
6046 return TRUE;
6047 }
6048
87741170
RN
6049 if ( !vtl->vl.visible || !vtl->waypoints_visible )
6050 return FALSE;
6051
50a14534
EB
6052 if ( vtl->current_wp && vtl->current_wp->visible )
6053 {
6054 /* first check if current WP is within area (other may be 'closer', but we want to move the current) */
6055 gint x, y;
6056 vik_viewport_coord_to_screen ( vvp, &(vtl->current_wp->coord), &x, &y );
6057
c75d78d7
AF
6058 if ( abs(x - event->x) <= WAYPOINT_SIZE_APPROX &&
6059 abs(y - event->y) <= WAYPOINT_SIZE_APPROX )
50a14534
EB
6060 {
6061 if ( event->button == 3 )
6062 vtl->waypoint_rightclick = TRUE; /* remember that we're clicking; other layers will ignore release signal */
7432fddf
AF
6063 else {
6064 marker_begin_move(t, event->x, event->y);
6065 }
50a14534
EB
6066 return TRUE;
6067 }
6068 }
6069
6070 params.vvp = vvp;
6071 params.x = event->x;
6072 params.y = event->y;
c9570f86 6073 params.closest_wp_id = NULL;
50a14534
EB
6074 /* TODO: should get track listitem so we can break it up, make a new track, mess it up, all that. */
6075 params.closest_wp = NULL;
6076 g_hash_table_foreach ( vtl->waypoints, (GHFunc) waypoint_search_closest_tp, &params);
6077 if ( vtl->current_wp == params.closest_wp && vtl->current_wp != NULL )
6078 {
d84ade77 6079 // how do we get here?
7432fddf 6080 marker_begin_move(t, event->x, event->y);
7742da66 6081 g_critical("shouldn't be here");
d84ade77 6082 return FALSE;
50a14534
EB
6083 }
6084 else if ( params.closest_wp )
6085 {
6086 if ( event->button == 3 )
6087 vtl->waypoint_rightclick = TRUE; /* remember that we're clicking; other layers will ignore release signal */
6088 else
6089 vtl->waypoint_rightclick = FALSE;
6090
c9570f86 6091 vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, g_hash_table_lookup ( vtl->waypoints_iters, params.closest_wp_id ), TRUE );
8623d370 6092
50a14534 6093 vtl->current_wp = params.closest_wp;
c9570f86 6094 vtl->current_wp_id = params.closest_wp_id;
50a14534 6095
50a14534 6096 /* could make it so don't update if old WP is off screen and new is null but oh well */
f01eebd4 6097 vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
50a14534
EB
6098 return TRUE;
6099 }
6100
6101 vtl->current_wp = NULL;
c9570f86 6102 vtl->current_wp_id = NULL;
50a14534 6103 vtl->waypoint_rightclick = FALSE;
f01eebd4 6104 vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
7432fddf
AF
6105 return FALSE;
6106}
6107
dc2c040e 6108static gboolean tool_edit_waypoint_move ( VikTrwLayer *vtl, GdkEventMotion *event, gpointer data )
7432fddf
AF
6109{
6110 tool_ed_t *t = data;
6111 VikViewport *vvp = t->vvp;
6112
6113 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
6114 return FALSE;
6115
6116 if ( t->holding ) {
6117 VikCoord new_coord;
6118 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
6119
6120 /* snap to TP */
6121 if ( event->state & GDK_CONTROL_MASK )
6122 {
6123 VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
6124 if ( tp )
6125 new_coord = tp->coord;
6126 }
6127
6128 /* snap to WP */
6129 if ( event->state & GDK_SHIFT_MASK )
6130 {
6131 VikWaypoint *wp = closest_wp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
6132 if ( wp && wp != vtl->current_wp )
6133 new_coord = wp->coord;
6134 }
6135
6136 {
6137 gint x, y;
6138 vik_viewport_coord_to_screen ( vvp, &new_coord, &x, &y );
7b203521 6139
7432fddf
AF
6140 marker_moveto ( t, x, y );
6141 }
6142 return TRUE;
6143 }
50a14534
EB
6144 return FALSE;
6145}
6146
7432fddf 6147static gboolean tool_edit_waypoint_release ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data )
941aa6e9 6148{
7432fddf
AF
6149 tool_ed_t *t = data;
6150 VikViewport *vvp = t->vvp;
6151
941aa6e9
AF
6152 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
6153 return FALSE;
7432fddf
AF
6154
6155 if ( t->holding && event->button == 1 )
941aa6e9
AF
6156 {
6157 VikCoord new_coord;
941aa6e9
AF
6158 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
6159
6160 /* snap to TP */
6161 if ( event->state & GDK_CONTROL_MASK )
6162 {
6163 VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
6164 if ( tp )
6165 new_coord = tp->coord;
6166 }
6167
6168 /* snap to WP */
6169 if ( event->state & GDK_SHIFT_MASK )
6170 {
6171 VikWaypoint *wp = closest_wp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
6172 if ( wp && wp != vtl->current_wp )
6173 new_coord = wp->coord;
6174 }
6175
7432fddf
AF
6176 marker_end_move ( t );
6177
941aa6e9 6178 vtl->current_wp->coord = new_coord;
f01eebd4 6179 vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
941aa6e9
AF
6180 return TRUE;
6181 }
6182 /* PUT IN RIGHT PLACE!!! */
7432fddf 6183 if ( event->button == 3 && vtl->waypoint_rightclick )
941aa6e9
AF
6184 {
6185 if ( vtl->wp_right_click_menu )
4f14a010 6186 g_object_ref_sink ( G_OBJECT(vtl->wp_right_click_menu) );
0b951ebe
RN
6187 if ( vtl->current_wp ) {
6188 vtl->wp_right_click_menu = GTK_MENU ( gtk_menu_new () );
c9570f86 6189 trw_layer_sublayer_add_menu_items ( vtl, vtl->wp_right_click_menu, NULL, VIK_TRW_LAYER_SUBLAYER_WAYPOINT, vtl->current_wp_id, g_hash_table_lookup ( vtl->waypoints_iters, vtl->current_wp_id ), vvp );
0b951ebe
RN
6190 gtk_menu_popup ( vtl->wp_right_click_menu, NULL, NULL, NULL, NULL, event->button, gtk_get_current_event_time() );
6191 }
941aa6e9
AF
6192 vtl->waypoint_rightclick = FALSE;
6193 }
6194 return FALSE;
6195}
6196
98f5364d
EB
6197/**** Begin track ***/
6198static gpointer tool_begin_track_create ( VikWindow *vw, VikViewport *vvp)
6199{
6200 return vvp;
6201}
6202
6203static gboolean tool_begin_track_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
6204{
6205 vtl->current_track = NULL;
6206 return tool_new_track_click ( vtl, event, vvp );
6207}
6208
941aa6e9
AF
6209/*** New track ****/
6210
6211static gpointer tool_new_track_create ( VikWindow *vw, VikViewport *vvp)
6212{
6213 return vvp;
6214}
6215
7b203521
EB
6216typedef struct {
6217 VikTrwLayer *vtl;
745fda83
RN
6218 GdkDrawable *drawable;
6219 GdkGC *gc;
6220 GdkPixmap *pixmap;
6221} draw_sync_t;
7b203521 6222
745fda83
RN
6223/*
6224 * Draw specified pixmap
6225 */
6226static gboolean draw_sync ( gpointer data )
7b203521 6227{
745fda83 6228 draw_sync_t *ds = (draw_sync_t*) data;
ef5e8132
RN
6229 // Sometimes don't want to draw
6230 // normally because another update has taken precedent such as panning the display
6231 // which means this pixmap is no longer valid
6232 if ( ds->vtl->draw_sync_do ) {
6233 gdk_threads_enter();
6234 gdk_draw_drawable (ds->drawable,
6235 ds->gc,
6236 ds->pixmap,
6237 0, 0, 0, 0, -1, -1);
6238 ds->vtl->draw_sync_done = TRUE;
6239 gdk_threads_leave();
6240 }
7b203521
EB
6241 return FALSE;
6242}
6243
19a0d58f 6244static gchar* distance_string (gdouble distance)
8da84040
RN
6245{
6246 gchar str[128];
6247
6248 /* draw label with distance */
6249 vik_units_distance_t dist_units = a_vik_get_units_distance ();
6250 switch (dist_units) {
6251 case VIK_UNITS_DISTANCE_MILES:
6252 if (distance >= VIK_MILES_TO_METERS(1) && distance < VIK_MILES_TO_METERS(100)) {
6253 g_sprintf(str, "%3.2f miles", VIK_METERS_TO_MILES(distance));
6254 } else if (distance < 1609.4) {
6255 g_sprintf(str, "%d yards", (int)(distance*1.0936133));
6256 } else {
6257 g_sprintf(str, "%d miles", (int)VIK_METERS_TO_MILES(distance));
6258 }
6259 break;
6260 default:
6261 // VIK_UNITS_DISTANCE_KILOMETRES
6262 if (distance >= 1000 && distance < 100000) {
6263 g_sprintf(str, "%3.2f km", distance/1000.0);
6264 } else if (distance < 1000) {
6265 g_sprintf(str, "%d m", (int)distance);
6266 } else {
6267 g_sprintf(str, "%d km", (int)distance/1000);
6268 }
6269 break;
6270 }
6271 return g_strdup (str);
6272}
6273
6274/*
6275 * Actually set the message in statusbar
6276 */
6277static void statusbar_write (const gchar *distance_string, gdouble elev_gain, gdouble elev_loss, VikTrwLayer *vtl )
6278{
6279 // Only show elevation data when track has some elevation properties
6280 gchar str_gain_loss[64];
6281 str_gain_loss[0] = '\0';
6282
6283 if ( (elev_gain > 0.1) || (elev_loss > 0.1) ) {
6284 if ( a_vik_get_units_height () == VIK_UNITS_HEIGHT_METRES )
6285 g_sprintf(str_gain_loss, _(" - Gain %dm:Loss %dm"), (int)elev_gain, (int)elev_loss);
6286 else
6287 g_sprintf(str_gain_loss, _(" - Gain %dft:Loss %dft"), (int)VIK_METERS_TO_FEET(elev_gain), (int)VIK_METERS_TO_FEET(elev_loss));
6288 }
6289
6290 // Write with full gain/loss information
6291 gchar *msg = g_strdup_printf ( "%s%s", distance_string, str_gain_loss);
6292 vik_statusbar_set_message ( vik_window_get_statusbar (VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl))), VIK_STATUSBAR_INFO, msg );
6293 g_free ( msg );
6294}
6295
6296/*
6297 * Figure out what information should be set in the statusbar and then write it
6298 */
6299static void update_statusbar ( VikTrwLayer *vtl )
6300{
6301 // Get elevation data
6302 gdouble elev_gain, elev_loss;
6303 vik_track_get_total_elevation_gain ( vtl->current_track, &elev_gain, &elev_loss);
6304
6305 /* Find out actual distance of current track */
6306 gdouble distance = vik_track_get_length (vtl->current_track);
19a0d58f 6307 gchar *str = distance_string (distance);
8da84040
RN
6308
6309 statusbar_write (str, elev_gain, elev_loss, vtl);
6310
19a0d58f 6311 g_free (str);
8da84040
RN
6312}
6313
6314
dc2c040e 6315static VikLayerToolFuncStatus tool_new_track_move ( VikTrwLayer *vtl, GdkEventMotion *event, VikViewport *vvp )
7b203521
EB
6316{
6317 /* if we haven't sync'ed yet, we don't have time to do more. */
745fda83
RN
6318 if ( vtl->draw_sync_done && vtl->current_track && vtl->current_track->trackpoints ) {
6319 GList *iter = g_list_last ( vtl->current_track->trackpoints );
c31b3fbb 6320 VikTrackpoint *last_tpt = VIK_TRACKPOINT(iter->data);
745fda83
RN
6321
6322 static GdkPixmap *pixmap = NULL;
6323 int w1, h1, w2, h2;
6324 // Need to check in case window has been resized
6325 w1 = vik_viewport_get_width(vvp);
6326 h1 = vik_viewport_get_height(vvp);
6327 if (!pixmap) {
6328 pixmap = gdk_pixmap_new ( GTK_WIDGET(vvp)->window, w1, h1, -1 );
6329 }
6330 gdk_drawable_get_size (pixmap, &w2, &h2);
6331 if (w1 != w2 || h1 != h2) {
6332 g_object_unref ( G_OBJECT ( pixmap ) );
6333 pixmap = gdk_pixmap_new ( GTK_WIDGET(vvp)->window, w1, h1, -1 );
6334 }
6335
6336 // Reset to background
6337 gdk_draw_drawable (pixmap,
6338 vtl->current_track_newpoint_gc,
6339 vik_viewport_get_pixmap(vvp),
6340 0, 0, 0, 0, -1, -1);
6341
6342 draw_sync_t *passalong;
7b203521
EB
6343 gint x1, y1;
6344
c31b3fbb 6345 vik_viewport_coord_to_screen ( vvp, &(last_tpt->coord), &x1, &y1 );
745fda83
RN
6346
6347 // FOR SCREEN OVERLAYS WE MUST DRAW INTO THIS PIXMAP (when using the reset method)
6348 // otherwise using vik_viewport_draw_* functions puts the data into the base pixmap,
6349 // thus when we come to reset to the background it would include what we have already drawn!!
6350 gdk_draw_line ( pixmap,
6351 vtl->current_track_newpoint_gc,
6352 x1, y1, event->x, event->y );
6353 // Using this reset method is more reliable than trying to undraw previous efforts via the GDK_INVERT method
23ea1329 6354
23ea1329
RN
6355 /* Find out actual distance of current track */
6356 gdouble distance = vik_track_get_length (vtl->current_track);
6357
6358 // Now add distance to where the pointer is //
6359 VikCoord coord;
6360 struct LatLon ll;
6361 vik_viewport_screen_to_coord ( vvp, (gint) event->x, (gint) event->y, &coord );
6362 vik_coord_to_latlon ( &coord, &ll );
c31b3fbb 6363 distance = distance + vik_coord_diff( &coord, &(last_tpt->coord));
23ea1329 6364
8da84040
RN
6365 // Get elevation data
6366 gdouble elev_gain, elev_loss;
6367 vik_track_get_total_elevation_gain ( vtl->current_track, &elev_gain, &elev_loss);
6368
6369 // Adjust elevation data (if available) for the current pointer position
6370 gdouble elev_new;
6371 elev_new = (gdouble) a_dems_get_elev_by_coord ( &coord, VIK_DEM_INTERPOL_BEST );
6372 if ( elev_new != VIK_DEM_INVALID_ELEVATION ) {
c31b3fbb 6373 if ( last_tpt->altitude != VIK_DEFAULT_ALTITUDE ) {
8da84040 6374 // Adjust elevation of last track point
c31b3fbb 6375 if ( elev_new > last_tpt->altitude )
8da84040 6376 // Going up
c31b3fbb 6377 elev_gain += elev_new - last_tpt->altitude;
8da84040
RN
6378 else
6379 // Going down
c31b3fbb 6380 elev_loss += last_tpt->altitude - elev_new;
23ea1329 6381 }
23ea1329 6382 }
8da84040 6383
19a0d58f 6384 gchar *str = distance_string (distance);
6ba8e356 6385
061ccfdc 6386 PangoLayout *pl = gtk_widget_create_pango_layout (GTK_WIDGET(vvp), NULL);
56cb1807 6387 pango_layout_set_font_description (pl, GTK_WIDGET(vvp)->style->font_desc);
061ccfdc
RN
6388
6389 pango_layout_set_text (pl, str, -1);
6ba8e356
RN
6390 gint wd, hd;
6391 pango_layout_get_pixel_size ( pl, &wd, &hd );
6392
56cb1807
RN
6393 gint xd,yd;
6394 // offset from cursor a bit depending on font size
6395 xd = event->x + 10;
6396 yd = event->y - hd;
6397
6ba8e356
RN
6398 // Create a background block to make the text easier to read over the background map
6399 GdkGC *background_block_gc = vik_viewport_new_gc ( vvp, "#cccccc", 1);
6400 gdk_draw_rectangle (pixmap, background_block_gc, TRUE, xd-2, yd-2, wd+4, hd+2);
061ccfdc 6401 gdk_draw_layout (pixmap, vtl->current_track_newpoint_gc, xd, yd, pl);
745fda83 6402
6ba8e356
RN
6403 g_object_unref ( G_OBJECT ( pl ) );
6404 g_object_unref ( G_OBJECT ( background_block_gc ) );
6405
745fda83 6406 passalong = g_new(draw_sync_t,1); // freed by draw_sync()
7b203521 6407 passalong->vtl = vtl;
745fda83
RN
6408 passalong->pixmap = pixmap;
6409 passalong->drawable = GTK_WIDGET(vvp)->window;
6410 passalong->gc = vtl->current_track_newpoint_gc;
8da84040
RN
6411
6412 // Update statusbar with full gain/loss information
6413 statusbar_write (str, elev_gain, elev_loss, vtl);
7b203521 6414
19a0d58f 6415 g_free (str);
745fda83
RN
6416
6417 // draw pixmap when we have time to
6418 g_idle_add_full (G_PRIORITY_HIGH_IDLE + 10, draw_sync, passalong, NULL);
6419 vtl->draw_sync_done = FALSE;
165d30aa 6420 return VIK_LAYER_TOOL_ACK_GRAB_FOCUS;
7b203521 6421 }
165d30aa 6422 return VIK_LAYER_TOOL_ACK;
7b203521
EB
6423}
6424
777e2d4d
EB
6425static gboolean tool_new_track_key_press ( VikTrwLayer *vtl, GdkEventKey *event, VikViewport *vvp )
6426{
6427 if ( vtl->current_track && event->keyval == GDK_Escape ) {
6428 vtl->current_track = NULL;
f01eebd4 6429 vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
777e2d4d
EB
6430 return TRUE;
6431 } else if ( vtl->current_track && event->keyval == GDK_BackSpace ) {
6432 /* undo */
6433 if ( vtl->current_track->trackpoints )
6434 {
6435 GList *last = g_list_last(vtl->current_track->trackpoints);
6436 g_free ( last->data );
6437 vtl->current_track->trackpoints = g_list_remove_link ( vtl->current_track->trackpoints, last );
6438 }
8da84040
RN
6439
6440 update_statusbar ( vtl );
6441
f01eebd4 6442 vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
777e2d4d
EB
6443 return TRUE;
6444 }
6445 return FALSE;
6446}
6447
941aa6e9 6448static gboolean tool_new_track_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
50a14534
EB
6449{
6450 VikTrackpoint *tp;
6451
941aa6e9
AF
6452 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
6453 return FALSE;
6454
ef5e8132
RN
6455 if ( event->button == 2 ) {
6456 // As the display is panning, the new track pixmap is now invalid so don't draw it
6457 // otherwise this drawing done results in flickering back to an old image
6458 vtl->draw_sync_do = FALSE;
6459 return FALSE;
6460 }
6461
50a14534
EB
6462 if ( event->button == 3 && vtl->current_track )
6463 {
6464 /* undo */
6465 if ( vtl->current_track->trackpoints )
6466 {
6467 GList *last = g_list_last(vtl->current_track->trackpoints);
6468 g_free ( last->data );
6469 vtl->current_track->trackpoints = g_list_remove_link ( vtl->current_track->trackpoints, last );
6470 }
8da84040
RN
6471 update_statusbar ( vtl );
6472
f01eebd4 6473 vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
50a14534
EB
6474 return TRUE;
6475 }
6476
6477 if ( event->type == GDK_2BUTTON_PRESS )
6478 {
6479 /* subtract last (duplicate from double click) tp then end */
6480 if ( vtl->current_track && vtl->current_track->trackpoints && vtl->ct_x1 == vtl->ct_x2 && vtl->ct_y1 == vtl->ct_y2 )
6481 {
6482 GList *last = g_list_last(vtl->current_track->trackpoints);
6483 g_free ( last->data );
6484 vtl->current_track->trackpoints = g_list_remove_link ( vtl->current_track->trackpoints, last );
6485 /* undo last, then end */
6486 vtl->current_track = NULL;
6487 }
f01eebd4 6488 vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
50a14534
EB
6489 return TRUE;
6490 }
6491
6492 if ( ! vtl->current_track )
6493 {
9748531a 6494 gchar *name = trw_layer_new_unique_sublayer_name(vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, _("Track"));
e13ab673 6495 if ( ( name = a_dialog_new_track ( VIK_GTK_WINDOW_FROM_LAYER(vtl), vtl->tracks, name ) ) )
50a14534
EB
6496 {
6497 vtl->current_track = vik_track_new();
6498 vtl->current_track->visible = TRUE;
6499 vik_trw_layer_add_track ( vtl, name, vtl->current_track );
98f5364d
EB
6500
6501 /* incase it was created by begin track */
6502 vik_window_enable_layer_tool ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)), VIK_LAYER_TRW, TOOL_CREATE_TRACK );
50a14534
EB
6503 }
6504 else
6505 return TRUE;
6506 }
6507 tp = vik_trackpoint_new();
6508 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &(tp->coord) );
6509
6510 /* snap to other TP */
6511 if ( event->state & GDK_CONTROL_MASK )
6512 {
6513 VikTrackpoint *other_tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
6514 if ( other_tp )
6515 tp->coord = other_tp->coord;
6516 }
6517
6518 tp->newsegment = FALSE;
6519 tp->has_timestamp = FALSE;
6520 tp->timestamp = 0;
50a14534 6521 vtl->current_track->trackpoints = g_list_append ( vtl->current_track->trackpoints, tp );
e85535ea
RN
6522 /* Auto attempt to get elevation from DEM data (if it's available) */
6523 vik_track_apply_dem_data_last_trackpoint ( vtl->current_track );
50a14534
EB
6524
6525 vtl->ct_x1 = vtl->ct_x2;
6526 vtl->ct_y1 = vtl->ct_y2;
6527 vtl->ct_x2 = event->x;
6528 vtl->ct_y2 = event->y;
6529
f01eebd4 6530 vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
50a14534
EB
6531 return TRUE;
6532}
6533
ef5e8132
RN
6534static void tool_new_track_release ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
6535{
6536 if ( event->button == 2 ) {
6537 // Pan moving ended - enable potential point drawing again
6538 vtl->draw_sync_do = TRUE;
6539 vtl->draw_sync_done = TRUE;
6540 }
6541}
6542
941aa6e9
AF
6543/*** New waypoint ****/
6544
6545static gpointer tool_new_waypoint_create ( VikWindow *vw, VikViewport *vvp)
6546{
6547 return vvp;
6548}
6549
6550static gboolean tool_new_waypoint_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
6551{
6552 VikCoord coord;
6553 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
6554 return FALSE;
6555 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &coord );
6556 if (vik_trw_layer_new_waypoint ( vtl, VIK_GTK_WINDOW_FROM_LAYER(vtl), &coord ) && VIK_LAYER(vtl)->visible)
f01eebd4 6557 vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
941aa6e9
AF
6558 return TRUE;
6559}
6560
6561
6562/*** Edit trackpoint ****/
6563
6564static gpointer tool_edit_trackpoint_create ( VikWindow *vw, VikViewport *vvp)
6565{
7432fddf 6566 tool_ed_t *t = g_new(tool_ed_t, 1);
33534cd8
AF
6567 t->vvp = vvp;
6568 t->holding = FALSE;
6569 return t;
941aa6e9
AF
6570}
6571
33534cd8 6572static gboolean tool_edit_trackpoint_click ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data )
941aa6e9 6573{
7432fddf 6574 tool_ed_t *t = data;
33534cd8
AF
6575 VikViewport *vvp = t->vvp;
6576 TPSearchParams params;
941aa6e9
AF
6577 /* OUTDATED DOCUMENTATION:
6578 find 5 pixel range on each side. then put these UTM, and a pointer
6579 to the winning track name (and maybe the winning track itself), and a
6580 pointer to the winning trackpoint, inside an array or struct. pass
6581 this along, do a foreach on the tracks which will do a foreach on the
6582 trackpoints. */
6583 params.vvp = vvp;
6584 params.x = event->x;
6585 params.y = event->y;
ce4bd1cf 6586 params.closest_track_id = NULL;
941aa6e9
AF
6587 /* TODO: should get track listitem so we can break it up, make a new track, mess it up, all that. */
6588 params.closest_tp = NULL;
6589
7432fddf
AF
6590 if ( event->button != 1 )
6591 return FALSE;
6592
941aa6e9
AF
6593 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
6594 return FALSE;
6595
87741170
RN
6596 if ( !vtl->vl.visible || !vtl->tracks_visible )
6597 return FALSE;
6598
941aa6e9
AF
6599 if ( vtl->current_tpl )
6600 {
6601 /* 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.) */
6602 VikTrackpoint *tp = VIK_TRACKPOINT(vtl->current_tpl->data);
ce4bd1cf
RN
6603 VikTrack *current_tr = VIK_TRACK(g_hash_table_lookup(vtl->tracks, vtl->current_tp_id));
6604 if ( !current_tr )
6605 return FALSE;
941aa6e9 6606
ce4bd1cf 6607 gint x, y;
941aa6e9
AF
6608 vik_viewport_coord_to_screen ( vvp, &(tp->coord), &x, &y );
6609
6610 if ( current_tr->visible &&
6611 abs(x - event->x) < TRACKPOINT_SIZE_APPROX &&
7432fddf
AF
6612 abs(y - event->y) < TRACKPOINT_SIZE_APPROX ) {
6613 marker_begin_move ( t, event->x, event->y );
941aa6e9
AF
6614 return TRUE;
6615 }
6616
941aa6e9
AF
6617 }
6618
6619 g_hash_table_foreach ( vtl->tracks, (GHFunc) track_search_closest_tp, &params);
6620
6621 if ( params.closest_tp )
6622 {
ce4bd1cf 6623 vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, g_hash_table_lookup ( vtl->tracks_iters, params.closest_track_id ), TRUE );
941aa6e9 6624 vtl->current_tpl = params.closest_tpl;
ce4bd1cf 6625 vtl->current_tp_id = params.closest_track_id;
da14cc69 6626 vtl->current_tp_track = g_hash_table_lookup ( vtl->tracks, params.closest_track_id );
941aa6e9 6627 trw_layer_tpwin_init ( vtl );
95d1b757 6628 set_statusbar_msg_info_trkpt ( vtl, params.closest_tp );
f01eebd4 6629 vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
941aa6e9
AF
6630 return TRUE;
6631 }
6632
6633 /* these aren't the droids you're looking for */
6634 return FALSE;
6635}
6636
dc2c040e 6637static gboolean tool_edit_trackpoint_move ( VikTrwLayer *vtl, GdkEventMotion *event, gpointer data )
33534cd8 6638{
7432fddf 6639 tool_ed_t *t = data;
33534cd8
AF
6640 VikViewport *vvp = t->vvp;
6641
6642 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
6643 return FALSE;
6644
6645 if ( t->holding )
6646 {
6647 VikCoord new_coord;
33534cd8
AF
6648 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
6649
6650 /* snap to TP */
6651 if ( event->state & GDK_CONTROL_MASK )
6652 {
6653 VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
6654 if ( tp && tp != vtl->current_tpl->data )
6655 new_coord = tp->coord;
6656 }
6657 // VIK_TRACKPOINT(vtl->current_tpl->data)->coord = new_coord;
7432fddf
AF
6658 {
6659 gint x, y;
6660 vik_viewport_coord_to_screen ( vvp, &new_coord, &x, &y );
6661 marker_moveto ( t, x, y );
6662 }
33534cd8
AF
6663
6664 return TRUE;
6665 }
6666 return FALSE;
6667}
6668
33534cd8 6669static gboolean tool_edit_trackpoint_release ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data )
941aa6e9 6670{
7432fddf 6671 tool_ed_t *t = data;
33534cd8
AF
6672 VikViewport *vvp = t->vvp;
6673
941aa6e9
AF
6674 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
6675 return FALSE;
7432fddf
AF
6676 if ( event->button != 1)
6677 return FALSE;
33534cd8 6678
7432fddf 6679 if ( t->holding ) {
941aa6e9 6680 VikCoord new_coord;
941aa6e9
AF
6681 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
6682
6683 /* snap to TP */
6684 if ( event->state & GDK_CONTROL_MASK )
6685 {
6686 VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
6687 if ( tp && tp != vtl->current_tpl->data )
6688 new_coord = tp->coord;
6689 }
6690
6691 VIK_TRACKPOINT(vtl->current_tpl->data)->coord = new_coord;
6692
7432fddf 6693 marker_end_move ( t );
33534cd8 6694
941aa6e9 6695 /* diff dist is diff from orig */
46b6631a 6696 if ( vtl->tpwin )
ce4bd1cf 6697 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, vtl->current_tp_track->name );
941aa6e9 6698
f01eebd4 6699 vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
941aa6e9
AF
6700 return TRUE;
6701 }
6702 return FALSE;
6703}
6704
6705
55340efa 6706#ifdef VIK_CONFIG_GOOGLE
7ff7d728
RN
6707/*** Route Finder ***/
6708static gpointer tool_route_finder_create ( VikWindow *vw, VikViewport *vvp)
1eef1bde
QT
6709{
6710 return vvp;
6711}
6712
7ff7d728 6713static gboolean tool_route_finder_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
1eef1bde
QT
6714{
6715 VikCoord tmp;
0c1044e9 6716 if ( !vtl ) return FALSE;
1eef1bde 6717 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &tmp );
7ff7d728 6718 if ( event->button == 3 && vtl->route_finder_current_track ) {
c3deba01 6719 VikCoord *new_end;
7ff7d728 6720 new_end = vik_track_cut_back_to_double_point ( vtl->route_finder_current_track );
c3deba01 6721 if ( new_end ) {
7ff7d728 6722 vtl->route_finder_coord = *new_end;
c3deba01 6723 g_free ( new_end );
f01eebd4 6724 vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
c3deba01 6725 /* remove last ' to:...' */
7ff7d728
RN
6726 if ( vtl->route_finder_current_track->comment ) {
6727 gchar *last_to = strrchr ( vtl->route_finder_current_track->comment, 't' );
6728 if ( last_to && (last_to - vtl->route_finder_current_track->comment > 1) ) {
6729 gchar *new_comment = g_strndup ( vtl->route_finder_current_track->comment,
6730 last_to - vtl->route_finder_current_track->comment - 1);
6731 vik_track_set_comment_no_copy ( vtl->route_finder_current_track, new_comment );
c3deba01
EB
6732 }
6733 }
6734 }
6735 }
7ff7d728 6736 else if ( vtl->route_finder_started || (event->state & GDK_CONTROL_MASK && vtl->route_finder_current_track) ) {
1eef1bde 6737 struct LatLon start, end;
533bbf34
MA
6738 gchar startlat[G_ASCII_DTOSTR_BUF_SIZE], startlon[G_ASCII_DTOSTR_BUF_SIZE];
6739 gchar endlat[G_ASCII_DTOSTR_BUF_SIZE], endlon[G_ASCII_DTOSTR_BUF_SIZE];
6740 gchar *url;
bddd2056 6741
7ff7d728 6742 vik_coord_to_latlon ( &(vtl->route_finder_coord), &start );
1eef1bde 6743 vik_coord_to_latlon ( &(tmp), &end );
7ff7d728 6744 vtl->route_finder_coord = tmp; /* for continuations */
bddd2056
EB
6745
6746 /* these are checked when adding a track from a file (vik_trw_layer_filein_add_track) */
7ff7d728
RN
6747 if ( event->state & GDK_CONTROL_MASK && vtl->route_finder_current_track ) {
6748 vtl->route_finder_append = TRUE; // merge tracks. keep started true.
bddd2056 6749 } else {
7ff7d728
RN
6750 vtl->route_finder_check_added_track = TRUE;
6751 vtl->route_finder_started = FALSE;
bddd2056
EB
6752 }
6753
533bbf34
MA
6754 url = g_strdup_printf(GOOGLE_DIRECTIONS_STRING,
6755 g_ascii_dtostr (startlat, G_ASCII_DTOSTR_BUF_SIZE, (gdouble) start.lat),
6756 g_ascii_dtostr (startlon, G_ASCII_DTOSTR_BUF_SIZE, (gdouble) start.lon),
6757 g_ascii_dtostr (endlat, G_ASCII_DTOSTR_BUF_SIZE, (gdouble) end.lat),
6758 g_ascii_dtostr (endlon, G_ASCII_DTOSTR_BUF_SIZE, (gdouble) end.lon));
55340efa 6759 a_babel_convert_from_url ( vtl, url, "google", NULL, NULL );
533bbf34 6760 g_free ( url );
bddd2056
EB
6761
6762 /* see if anything was done -- a track was added or appended to */
ce4bd1cf
RN
6763 if ( vtl->route_finder_check_added_track && vtl->route_finder_added_track ) {
6764 vik_track_set_comment_no_copy ( vtl->route_finder_added_track, g_strdup_printf("from: %f,%f to: %f,%f", start.lat, start.lon, end.lat, end.lon ) );
7ff7d728
RN
6765 } else if ( vtl->route_finder_append == FALSE && vtl->route_finder_current_track ) {
6766 /* route_finder_append was originally TRUE but set to FALSE by filein_add_track */
6767 gchar *new_comment = g_strdup_printf("%s to: %f,%f", vtl->route_finder_current_track->comment, end.lat, end.lon );
6768 vik_track_set_comment_no_copy ( vtl->route_finder_current_track, new_comment );
bddd2056 6769 }
ce4bd1cf 6770 vtl->route_finder_added_track = NULL;
7ff7d728
RN
6771 vtl->route_finder_check_added_track = FALSE;
6772 vtl->route_finder_append = FALSE;
bddd2056 6773
f01eebd4 6774 vik_layer_emit_update ( VIK_LAYER(vtl), FALSE );
1eef1bde 6775 } else {
7ff7d728
RN
6776 vtl->route_finder_started = TRUE;
6777 vtl->route_finder_coord = tmp;
6778 vtl->route_finder_current_track = NULL;
1eef1bde 6779 }
1eef1bde
QT
6780 return TRUE;
6781}
ebf1bd39 6782#endif
1eef1bde 6783
941aa6e9
AF
6784/*** Show picture ****/
6785
6786static gpointer tool_show_picture_create ( VikWindow *vw, VikViewport *vvp)
6787{
6788 return vvp;
6789}
6790
6791/* Params are: vvp, event, last match found or NULL */
8a14f396 6792static void tool_show_picture_wp ( char *name, VikWaypoint *wp, gpointer params[3] )
941aa6e9
AF
6793{
6794 if ( wp->image && wp->visible )
6795 {
6796 gint x, y, slackx, slacky;
6797 GdkEventButton *event = (GdkEventButton *) params[1];
6798
6799 vik_viewport_coord_to_screen ( VIK_VIEWPORT(params[0]), &(wp->coord), &x, &y );
6800 slackx = wp->image_width / 2;
6801 slacky = wp->image_height / 2;
6802 if ( x <= event->x + slackx && x >= event->x - slackx
6803 && y <= event->y + slacky && y >= event->y - slacky )
6804 {
6805 params[2] = wp->image; /* we've found a match. however continue searching
6806 * since we want to find the last match -- that
6807 * is, the match that was drawn last. */
6808 }
6809 }
6810}
6811
a412f3f5
RN
6812static void trw_layer_show_picture ( gpointer pass_along[6] )
6813{
6814 /* thanks to the Gaim people for showing me ShellExecute and g_spawn_command_line_async */
6815#ifdef WINDOWS
8b0cdf69 6816 ShellExecute(NULL, "open", (char *) pass_along[5], NULL, NULL, SW_SHOWNORMAL);
a412f3f5
RN
6817#else /* WINDOWS */
6818 GError *err = NULL;
6819 gchar *quoted_file = g_shell_quote ( (gchar *) pass_along[5] );
3317dc4e 6820 gchar *cmd = g_strdup_printf ( "%s %s", a_vik_get_image_viewer(), quoted_file );
a412f3f5
RN
6821 g_free ( quoted_file );
6822 if ( ! g_spawn_command_line_async ( cmd, &err ) )
6823 {
3317dc4e 6824 a_dialog_error_msg_extra ( VIK_GTK_WINDOW_FROM_LAYER( pass_along[0]), _("Could not launch %s to open file."), a_vik_get_image_viewer() );
a412f3f5
RN
6825 g_error_free ( err );
6826 }
6827 g_free ( cmd );
6828#endif /* WINDOWS */
6829}
6830
941aa6e9
AF
6831static gboolean tool_show_picture_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
6832{
6833 gpointer params[3] = { vvp, event, NULL };
6834 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
6835 return FALSE;
6836 g_hash_table_foreach ( vtl->waypoints, (GHFunc) tool_show_picture_wp, params );
6837 if ( params[2] )
6838 {
a412f3f5
RN
6839 static gpointer pass_along[6];
6840 pass_along[0] = vtl;
6841 pass_along[5] = params[2];
6842 trw_layer_show_picture ( pass_along );
941aa6e9
AF
6843 return TRUE; /* found a match */
6844 }
6845 else
6846 return FALSE; /* go through other layers, searching for a match */
6847}
6848
6849/***************************************************************************
6850 ** End tool code
6851 ***************************************************************************/
6852
6853
6854
6855
6856
50a14534
EB
6857static void image_wp_make_list ( char *name, VikWaypoint *wp, GSList **pics )
6858{
6859 if ( wp->image && ( ! a_thumbnails_exists ( wp->image ) ) )
6860 *pics = g_slist_append ( *pics, (gpointer) g_strdup ( wp->image ) );
6861}
6862
91822ddd
RN
6863/* Structure for thumbnail creating data used in the background thread */
6864typedef struct {
6865 VikTrwLayer *vtl; // Layer needed for redrawing
6866 GSList *pics; // Image list
6867} thumbnail_create_thread_data;
6868
6869static int create_thumbnails_thread ( thumbnail_create_thread_data *tctd, gpointer threaddata )
50a14534 6870{
91822ddd
RN
6871 guint total = g_slist_length(tctd->pics), done = 0;
6872 while ( tctd->pics )
50a14534 6873 {
91822ddd 6874 a_thumbnails_create ( (gchar *) tctd->pics->data );
54861848
GB
6875 int result = a_background_thread_progress ( threaddata, ((gdouble) ++done) / total );
6876 if ( result != 0 )
6877 return -1; /* Abort thread */
6878
91822ddd 6879 tctd->pics = tctd->pics->next;
50a14534 6880 }
91822ddd
RN
6881
6882 // Redraw to show the thumbnails as they are now created
91822ddd 6883 if ( IS_VIK_LAYER(tctd->vtl) )
f01eebd4 6884 vik_layer_emit_update ( VIK_LAYER(tctd->vtl), TRUE ); // Yes update from background thread
91822ddd 6885
17c8aefa 6886 return 0;
50a14534
EB
6887}
6888
91822ddd 6889static void thumbnail_create_thread_free ( thumbnail_create_thread_data *tctd )
50a14534 6890{
91822ddd 6891 while ( tctd->pics )
50a14534 6892 {
91822ddd
RN
6893 g_free ( tctd->pics->data );
6894 tctd->pics = g_slist_delete_link ( tctd->pics, tctd->pics );
50a14534 6895 }
91822ddd 6896 g_free ( tctd );
50a14534
EB
6897}
6898
b3eb3b98 6899void trw_layer_verify_thumbnails ( VikTrwLayer *vtl, GtkWidget *vp )
50a14534
EB
6900{
6901 if ( ! vtl->has_verified_thumbnails )
6902 {
6903 GSList *pics = NULL;
6904 g_hash_table_foreach ( vtl->waypoints, (GHFunc) image_wp_make_list, &pics );
6905 if ( pics )
6906 {
6907 gint len = g_slist_length ( pics );
4c77d5e0 6908 gchar *tmp = g_strdup_printf ( _("Creating %d Image Thumbnails..."), len );
91822ddd
RN
6909 thumbnail_create_thread_data *tctd = g_malloc ( sizeof(thumbnail_create_thread_data) );
6910 tctd->vtl = vtl;
6911 tctd->pics = pics;
6912 a_background_thread ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
6913 tmp,
6914 (vik_thr_func) create_thumbnails_thread,
6915 tctd,
6916 (vik_thr_free_func) thumbnail_create_thread_free,
6917 NULL,
6918 len );
50a14534
EB
6919 g_free ( tmp );
6920 }
6921 }
6922}
6923
6924VikCoordMode vik_trw_layer_get_coord_mode ( VikTrwLayer *vtl )
6925{
6926 return vtl->coord_mode;
6927}
6928
073ede8c
RN
6929/**
6930 * Uniquify the whole layer
6931 * Also requires the layers panel as the names shown there need updating too
6932 * Returns whether the operation was successful or not
6933 */
6934gboolean vik_trw_layer_uniquify ( VikTrwLayer *vtl, VikLayersPanel *vlp )
6935{
6936 if ( vtl && vlp ) {
6937 vik_trw_layer_uniquify_tracks ( vtl, vlp );
6938 vik_trw_layer_uniquify_waypoints ( vtl, vlp );
6939 return TRUE;
6940 }
6941 return FALSE;
6942}
50a14534 6943
c9570f86 6944static void waypoint_convert ( const gpointer id, VikWaypoint *wp, VikCoordMode *dest_mode )
50a14534
EB
6945{
6946 vik_coord_convert ( &(wp->coord), *dest_mode );
6947}
6948
6949static void track_convert ( const gchar *name, VikTrack *tr, VikCoordMode *dest_mode )
6950{
6951 vik_track_convert ( tr, *dest_mode );
6952}
6953
6954static void trw_layer_change_coord_mode ( VikTrwLayer *vtl, VikCoordMode dest_mode )
6955{
6956 if ( vtl->coord_mode != dest_mode )
6957 {
6958 vtl->coord_mode = dest_mode;
6959 g_hash_table_foreach ( vtl->waypoints, (GHFunc) waypoint_convert, &dest_mode );
6960 g_hash_table_foreach ( vtl->tracks, (GHFunc) track_convert, &dest_mode );
6961 }
6962}
e4afc73a 6963
a7cd93ac 6964static void trw_layer_set_menu_selection ( VikTrwLayer *vtl, guint16 selection )
20c7a3a0
QT
6965{
6966 vtl->menu_selection = selection;
6967}
6968
a7cd93ac 6969static guint16 trw_layer_get_menu_selection ( VikTrwLayer *vtl )
20c7a3a0 6970{
a7cd93ac 6971 return (vtl->menu_selection);
20c7a3a0
QT
6972}
6973
7114e879
QT
6974/* ----------- Downloading maps along tracks --------------- */
6975
6976static int get_download_area_width(VikViewport *vvp, gdouble zoom_level, struct LatLon *wh)
6977{
6978 /* TODO: calculating based on current size of viewport */
6979 const gdouble w_at_zoom_0_125 = 0.0013;
6980 const gdouble h_at_zoom_0_125 = 0.0011;
6981 gdouble zoom_factor = zoom_level/0.125;
6982
6983 wh->lat = h_at_zoom_0_125 * zoom_factor;
6984 wh->lon = w_at_zoom_0_125 * zoom_factor;
6985
6986 return 0; /* all OK */
6987}
6988
35e22ed8
QT
6989static VikCoord *get_next_coord(VikCoord *from, VikCoord *to, struct LatLon *dist, gdouble gradient)
6990{
6991 if ((dist->lon >= ABS(to->east_west - from->east_west)) &&
6992 (dist->lat >= ABS(to->north_south - from->north_south)))
6993 return NULL;
6994
6995 VikCoord *coord = g_malloc(sizeof(VikCoord));
6996 coord->mode = VIK_COORD_LATLON;
6997
6998 if (ABS(gradient) < 1) {
6999 if (from->east_west > to->east_west)
7000 coord->east_west = from->east_west - dist->lon;
7001 else
7002 coord->east_west = from->east_west + dist->lon;
7003 coord->north_south = gradient * (coord->east_west - from->east_west) + from->north_south;
7004 } else {
7005 if (from->north_south > to->north_south)
7006 coord->north_south = from->north_south - dist->lat;
7007 else
7008 coord->north_south = from->north_south + dist->lat;
7009 coord->east_west = (1/gradient) * (coord->north_south - from->north_south) + from->north_south;
7010 }
7011
7012 return coord;
7013}
7014
7015static GList *add_fillins(GList *list, VikCoord *from, VikCoord *to, struct LatLon *dist)
7016{
7017 /* TODO: handle virtical track (to->east_west - from->east_west == 0) */
7018 gdouble gradient = (to->north_south - from->north_south)/(to->east_west - from->east_west);
7019
7020 VikCoord *next = from;
7021 while (TRUE) {
7022 if ((next = get_next_coord(next, to, dist, gradient)) == NULL)
7023 break;
7024 list = g_list_prepend(list, next);
7025 }
7026
7027 return list;
7028}
7029
7114e879
QT
7030void vik_track_download_map(VikTrack *tr, VikMapsLayer *vml, VikViewport *vvp, gdouble zoom_level)
7031{
7032 typedef struct _Rect {
7033 VikCoord tl;
7034 VikCoord br;
35e22ed8 7035 VikCoord center;
7114e879 7036 } Rect;
35e22ed8 7037#define GLRECT(iter) ((Rect *)((iter)->data))
7114e879
QT
7038
7039 struct LatLon wh;
35e22ed8 7040 GList *rects_to_download = NULL;
7114e879
QT
7041 GList *rect_iter;
7042
7043 if (get_download_area_width(vvp, zoom_level, &wh))
7044 return;
7045
7046 GList *iter = tr->trackpoints;
35e22ed8
QT
7047 if (!iter)
7048 return;
7114e879
QT
7049
7050 gboolean new_map = TRUE;
7051 VikCoord *cur_coord, tl, br;
7052 Rect *rect;
7114e879
QT
7053 while (iter) {
7054 cur_coord = &(VIK_TRACKPOINT(iter->data))->coord;
7055 if (new_map) {
7056 vik_coord_set_area(cur_coord, &wh, &tl, &br);
7057 rect = g_malloc(sizeof(Rect));
7058 rect->tl = tl;
7059 rect->br = br;
35e22ed8
QT
7060 rect->center = *cur_coord;
7061 rects_to_download = g_list_prepend(rects_to_download, rect);
7114e879
QT
7062 new_map = FALSE;
7063 iter = iter->next;
7064 continue;
7065 }
7066 gboolean found = FALSE;
35e22ed8
QT
7067 for (rect_iter = rects_to_download; rect_iter; rect_iter = rect_iter->next) {
7068 if (vik_coord_inside(cur_coord, &GLRECT(rect_iter)->tl, &GLRECT(rect_iter)->br)) {
7114e879
QT
7069 found = TRUE;
7070 break;
7071 }
7072 }
7073 if (found)
7074 iter = iter->next;
7075 else
7076 new_map = TRUE;
7077 }
35e22ed8 7078
35e22ed8 7079 GList *fillins = NULL;
b1e57d16
RN
7080 /* 'fillin' doesn't work in UTM mode - potentially ending up in massive loop continually allocating memory - hence don't do it */
7081 /* seems that ATM the function get_next_coord works only for LATLON */
7082 if ( cur_coord->mode == VIK_COORD_LATLON ) {
7083 /* fill-ins for far apart points */
7084 GList *cur_rect, *next_rect;
7085 for (cur_rect = rects_to_download;
7086 (next_rect = cur_rect->next) != NULL;
7087 cur_rect = cur_rect->next) {
7088 if ((wh.lon < ABS(GLRECT(cur_rect)->center.east_west - GLRECT(next_rect)->center.east_west)) ||
7089 (wh.lat < ABS(GLRECT(cur_rect)->center.north_south - GLRECT(next_rect)->center.north_south))) {
7090 fillins = add_fillins(fillins, &GLRECT(cur_rect)->center, &GLRECT(next_rect)->center, &wh);
7091 }
35e22ed8 7092 }
3cbbb49e
GB
7093 } else
7094 g_message("%s: this feature works only in Mercator mode", __FUNCTION__);
35e22ed8
QT
7095
7096 if (fillins) {
7097 GList *iter = fillins;
7098 while (iter) {
7099 cur_coord = (VikCoord *)(iter->data);
7100 vik_coord_set_area(cur_coord, &wh, &tl, &br);
7101 rect = g_malloc(sizeof(Rect));
7102 rect->tl = tl;
7103 rect->br = br;
7104 rect->center = *cur_coord;
7105 rects_to_download = g_list_prepend(rects_to_download, rect);
7106 iter = iter->next;
7107 }
7108 }
7109
7110 for (rect_iter = rects_to_download; rect_iter; rect_iter = rect_iter->next) {
903dc0b4 7111 maps_layer_download_section (vml, vvp, &(((Rect *)(rect_iter->data))->tl), &(((Rect *)(rect_iter->data))->br), zoom_level);
7114e879
QT
7112 }
7113
35e22ed8
QT
7114 if (fillins) {
7115 for (iter = fillins; iter; iter = iter->next)
7116 g_free(iter->data);
7117 g_list_free(fillins);
7118 }
7119 if (rects_to_download) {
7120 for (rect_iter = rects_to_download; rect_iter; rect_iter = rect_iter->next)
7121 g_free(rect_iter->data);
7122 g_list_free(rects_to_download);
7123 }
7114e879
QT
7124}
7125
6bb72350 7126static void trw_layer_download_map_along_track_cb ( gpointer pass_along[6] )
7114e879
QT
7127{
7128 VikMapsLayer *vml;
7129 gint selected_map, default_map;
7130 gchar *zoomlist[] = {"0.125", "0.25", "0.5", "1", "2", "4", "8", "16", "32", "64", "128", "256", "512", "1024", NULL };
7131 gdouble zoom_vals[] = {0.125, 0.25, 0.5, 1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024};
7132 gint selected_zoom, default_zoom;
7133 int i,j;
7134
7135
7136 VikTrwLayer *vtl = pass_along[0];
7137 VikLayersPanel *vlp = pass_along[1];
7138 VikTrack *tr = (VikTrack *) g_hash_table_lookup ( VIK_TRW_LAYER(pass_along[0])->tracks, pass_along[3] );
7139 VikViewport *vvp = vik_window_viewport((VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl)));
7140
aa7ed888 7141 GList *vmls = vik_layers_panel_get_all_layers_of_type(vlp, VIK_LAYER_MAPS, TRUE); // Includes hidden map layer types
7114e879
QT
7142 int num_maps = g_list_length(vmls);
7143
7144 if (!num_maps) {
4c77d5e0 7145 a_dialog_msg(VIK_GTK_WINDOW_FROM_LAYER(vtl), GTK_MESSAGE_ERROR, _("No map layer in use. Create one first"), NULL);
7114e879
QT
7146 return;
7147 }
7148
7149 gchar **map_names = g_malloc(1 + num_maps * sizeof(gpointer));
7150 VikMapsLayer **map_layers = g_malloc(1 + num_maps * sizeof(gpointer));
7151
7152 gchar **np = map_names;
7153 VikMapsLayer **lp = map_layers;
7154 for (i = 0; i < num_maps; i++) {
7155 gboolean dup = FALSE;
7156 vml = (VikMapsLayer *)(vmls->data);
7157 for (j = 0; j < i; j++) { /* no duplicate allowed */
7158 if (vik_maps_layer_get_map_type(vml) == vik_maps_layer_get_map_type(map_layers[j])) {
7159 dup = TRUE;
7160 break;
7161 }
7162 }
7163 if (!dup) {
7164 *lp++ = vml;
7165 *np++ = vik_maps_layer_get_map_label(vml);
7166 }
7167 vmls = vmls->next;
7168 }
7169 *lp = NULL;
7170 *np = NULL;
7171 num_maps = lp - map_layers;
7172
7173 for (default_map = 0; default_map < num_maps; default_map++) {
7174 /* TODO: check for parent layer's visibility */
7175 if (VIK_LAYER(map_layers[default_map])->visible)
7176 break;
7177 }
7178 default_map = (default_map == num_maps) ? 0 : default_map;
7179
7180 gdouble cur_zoom = vik_viewport_get_zoom(vvp);
7181 for (default_zoom = 0; default_zoom < sizeof(zoom_vals)/sizeof(gdouble); default_zoom++) {
7182 if (cur_zoom == zoom_vals[default_zoom])
7183 break;
7184 }
7185 default_zoom = (default_zoom == sizeof(zoom_vals)/sizeof(gdouble)) ? sizeof(zoom_vals)/sizeof(gdouble) - 1 : default_zoom;
7186
7187 if (!a_dialog_map_n_zoom(VIK_GTK_WINDOW_FROM_LAYER(vtl), map_names, default_map, zoomlist, default_zoom, &selected_map, &selected_zoom))
7188 goto done;
7189
7190 vik_track_download_map(tr, map_layers[selected_map], vvp, zoom_vals[selected_zoom]);
7191
7192done:
7193 for (i = 0; i < num_maps; i++)
7194 g_free(map_names[i]);
7195 g_free(map_names);
7196 g_free(map_layers);
7197
7198 g_list_free(vmls);
7199
7200}
0c1044e9 7201
a8fe53f8
EB
7202/**** lowest waypoint number calculation ***/
7203static gint highest_wp_number_name_to_number(const gchar *name) {
7204 if ( strlen(name) == 3 ) {
7205 int n = atoi(name);
7206 if ( n < 100 && name[0] != '0' )
7207 return -1;
7208 if ( n < 10 && name[0] != '0' )
7209 return -1;
7210 return n;
7211 }
7212 return -1;
7213}
7214
7215
7216static void highest_wp_number_reset(VikTrwLayer *vtl)
7217{
7218 vtl->highest_wp_number = -1;
7219}
7220
7221static void highest_wp_number_add_wp(VikTrwLayer *vtl, const gchar *new_wp_name)
7222{
7223 /* if is bigger that top, add it */
7224 gint new_wp_num = highest_wp_number_name_to_number(new_wp_name);
7225 if ( new_wp_num > vtl->highest_wp_number )
7226 vtl->highest_wp_number = new_wp_num;
7227}
7228
7229static void highest_wp_number_remove_wp(VikTrwLayer *vtl, const gchar *old_wp_name)
7230{
7231 /* if wasn't top, do nothing. if was top, count backwards until we find one used */
7232 gint old_wp_num = highest_wp_number_name_to_number(old_wp_name);
7233 if ( vtl->highest_wp_number == old_wp_num ) {
7234 gchar buf[4];
c9570f86 7235 vtl->highest_wp_number--;
a8fe53f8
EB
7236
7237 g_snprintf(buf,4,"%03d", vtl->highest_wp_number );
7238 /* search down until we find something that *does* exist */
7239
c9570f86
RN
7240 while ( vtl->highest_wp_number > 0 && ! vik_trw_layer_get_waypoint ( vtl, buf )) {
7241 vtl->highest_wp_number--;
a8fe53f8
EB
7242 g_snprintf(buf,4,"%03d", vtl->highest_wp_number );
7243 }
7244 }
7245}
7246
7247/* get lowest unused number */
7248static gchar *highest_wp_number_get(VikTrwLayer *vtl)
7249{
7250 gchar buf[4];
7251 if ( vtl->highest_wp_number < 0 || vtl->highest_wp_number >= 999 )
3ce17b61 7252 return NULL;
a8fe53f8
EB
7253 g_snprintf(buf,4,"%03d", vtl->highest_wp_number+1 );
7254 return g_strdup(buf);
7255}