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