]> git.street.me.uk Git - andy/viking.git/blame - src/viktrwlayer.c
Isolate Viking specific code in otherwise reusable code.
[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>
9f30939a 9 * Copyright (c) 2012-2013, Guilhem Bonnefille <guilhem.bonnefille@gmail.com>
50a14534
EB
10 *
11 * This program is free software; you can redistribute it and/or modify
12 * it under the terms of the GNU General Public License as published by
13 * the Free Software Foundation; either version 2 of the License, or
14 * (at your option) any later version.
15 *
16 * This program is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 * GNU General Public License for more details.
20 *
21 * You should have received a copy of the GNU General Public License
22 * along with this program; if not, write to the Free Software
23 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
24 *
25 */
50a14534 26/* WARNING: If you go beyond this point, we are NOT responsible for any ill effects on your sanity */
0d2b891f 27/* viktrwlayer.c -- 8000+ lines can make a difference in the state of things */
50a14534 28
3e7553ae
GB
29#ifdef HAVE_CONFIG_H
30#include "config.h"
31#endif
32
50a14534 33#include "viking.h"
7114e879 34#include "vikmapslayer.h"
e50758c7 35#include "vikgpslayer.h"
e4a11fbe 36#include "viktrwlayer_export.h"
50a14534
EB
37#include "viktrwlayer_tpwin.h"
38#include "viktrwlayer_propwin.h"
43984ba1 39#include "viktrwlayer_analysis.h"
07c9d42b 40#include "viktrwlayer_tracklist.h"
00176e85 41#include "viktrwlayer_waypointlist.h"
b3eb3b98
RN
42#ifdef VIK_CONFIG_GEOTAG
43#include "viktrwlayer_geotag.h"
44#include "geotag_exif.h"
45#endif
acaf7113 46#include "garminsymbols.h"
50a14534
EB
47#include "thumbnails.h"
48#include "background.h"
911400b5 49#include "gpx.h"
e0b0b9c1 50#include "babel.h"
ad0a8c2d
EB
51#include "dem.h"
52#include "dems.h"
165a4fa9 53#include "geonamessearch.h"
3e7553ae
GB
54#ifdef VIK_CONFIG_OPENSTREETMAP
55#include "osm-traces.h"
56#endif
28c82d8b 57#include "acquire.h"
16fc32f6 58#include "datasources.h"
e50758c7 59#include "datasource_gps.h"
82993cc7 60#include "vikexttool_datasources.h"
7d02a0b0 61#include "util.h"
60626a3e 62#include "vikutils.h"
50a14534 63
9f30939a
GB
64#include "vikrouting.h"
65
bce3a7b0
EB
66#include "icons/icons.h"
67
8c00358d 68#ifdef HAVE_MATH_H
50a14534 69#include <math.h>
8c00358d
GB
70#endif
71#ifdef HAVE_STRING_H
50a14534 72#include <string.h>
8c00358d
GB
73#endif
74#ifdef HAVE_STDLIB_H
50a14534 75#include <stdlib.h>
8c00358d 76#endif
8c060406 77#include <stdio.h>
50a14534
EB
78#include <ctype.h>
79
777e2d4d 80#include <gdk/gdkkeysyms.h>
8c060406
MA
81#include <glib.h>
82#include <glib/gstdio.h>
4c77d5e0 83#include <glib/gi18n.h>
777e2d4d 84
074b1067 85#define VIK_TRW_LAYER_TRACK_GC 6
b1453c16
RN
86#define VIK_TRW_LAYER_TRACK_GCS 10
87#define VIK_TRW_LAYER_TRACK_GC_BLACK 0
88#define VIK_TRW_LAYER_TRACK_GC_SLOW 1
89#define VIK_TRW_LAYER_TRACK_GC_AVER 2
90#define VIK_TRW_LAYER_TRACK_GC_FAST 3
91#define VIK_TRW_LAYER_TRACK_GC_STOP 4
074b1067 92#define VIK_TRW_LAYER_TRACK_GC_SINGLE 5
50a14534
EB
93
94#define DRAWMODE_BY_TRACK 0
18df6d42 95#define DRAWMODE_BY_SPEED 1
074b1067 96#define DRAWMODE_ALL_SAME_COLOR 2
18df6d42
RN
97// Note using DRAWMODE_BY_SPEED may be slow especially for vast numbers of trackpoints
98// as we are (re)calculating the colour for every point
50a14534
EB
99
100#define POINTS 1
101#define LINES 2
102
103/* this is how it knows when you click if you are clicking close to a trackpoint. */
104#define TRACKPOINT_SIZE_APPROX 5
105#define WAYPOINT_SIZE_APPROX 5
106
b42a25ba
EB
107#define MIN_STOP_LENGTH 15
108#define MAX_STOP_LENGTH 86400
109#define DRAW_ELEVATION_FACTOR 30 /* height of elevation plotting, sort of relative to zoom level ("mpp" that isn't mpp necessarily) */
110 /* this is multiplied by user-inputted value from 1-100. */
50a14534
EB
111
112enum { WP_SYMBOL_FILLED_SQUARE, WP_SYMBOL_SQUARE, WP_SYMBOL_CIRCLE, WP_SYMBOL_X, WP_NUM_SYMBOLS };
113
5a77ae71
RN
114// See http://developer.gnome.org/pango/stable/PangoMarkupFormat.html
115typedef enum {
116 FS_XX_SMALL = 0, // 'xx-small'
117 FS_X_SMALL,
118 FS_SMALL,
119 FS_MEDIUM, // DEFAULT
120 FS_LARGE,
121 FS_X_LARGE,
122 FS_XX_LARGE,
123 FS_NUM_SIZES
124} font_size_t;
125
50a14534
EB
126struct _VikTrwLayer {
127 VikLayer vl;
128 GHashTable *tracks;
129 GHashTable *tracks_iters;
0d2b891f
RN
130 GHashTable *routes;
131 GHashTable *routes_iters;
50a14534
EB
132 GHashTable *waypoints_iters;
133 GHashTable *waypoints;
0d2b891f
RN
134 GtkTreeIter tracks_iter, routes_iter, waypoints_iter;
135 gboolean tracks_visible, routes_visible, waypoints_visible;
aa0665e5
RN
136 LatLonBBox waypoints_bbox;
137
387ff7ac 138 gboolean track_draw_labels;
50a14534
EB
139 guint8 drawmode;
140 guint8 drawpoints;
3a1c8e42 141 guint8 drawpoints_size;
b42a25ba
EB
142 guint8 drawelevation;
143 guint8 elevation_factor;
144 guint8 drawstops;
145 guint32 stop_length;
50a14534 146 guint8 drawlines;
08084371 147 guint8 drawdirections;
3a1c8e42 148 guint8 drawdirections_size;
50a14534
EB
149 guint8 line_thickness;
150 guint8 bg_line_thickness;
c9cac058 151 vik_layer_sort_order_t track_sort_order;
50a14534 152
6ba42f1e
RN
153 // Metadata
154 VikTRWMetadata *metadata;
155
387ff7ac
RN
156 PangoLayout *tracklabellayout;
157 font_size_t track_font_size;
158 gchar *track_fsize_str;
159
50a14534
EB
160 guint8 wp_symbol;
161 guint8 wp_size;
ea3933fc 162 gboolean wp_draw_symbols;
5a77ae71 163 font_size_t wp_font_size;
63ffa244 164 gchar *wp_fsize_str;
c9cac058 165 vik_layer_sort_order_t wp_sort_order;
50a14534 166
18df6d42 167 gdouble track_draw_speed_factor;
50a14534 168 GArray *track_gc;
b1453c16 169 GdkGC *track_1color_gc;
074b1067 170 GdkColor track_color;
8e9c992d 171 GdkGC *current_track_gc;
745fda83
RN
172 // Separate GC for a track's potential new point as drawn via separate method
173 // (compared to the actual track points drawn in the main trw_layer_draw_track function)
174 GdkGC *current_track_newpoint_gc;
a7023a1b
RN
175 GdkGC *track_bg_gc; GdkColor track_bg_color;
176 GdkGC *waypoint_gc; GdkColor waypoint_color;
177 GdkGC *waypoint_text_gc; GdkColor waypoint_text_color;
178 GdkGC *waypoint_bg_gc; GdkColor waypoint_bg_color;
179 gboolean wpbgand;
0d2b891f
RN
180 GdkFont *waypoint_font;
181 VikTrack *current_track; // ATM shared between new tracks and new routes
50a14534 182 guint16 ct_x1, ct_y1, ct_x2, ct_y2;
745fda83 183 gboolean draw_sync_done;
ef5e8132 184 gboolean draw_sync_do;
50a14534
EB
185
186 VikCoordMode coord_mode;
187
188 /* wp editing tool */
189 VikWaypoint *current_wp;
c9570f86 190 gpointer current_wp_id;
50a14534
EB
191 gboolean moving_wp;
192 gboolean waypoint_rightclick;
193
194 /* track editing tool */
195 GList *current_tpl;
ce4bd1cf
RN
196 VikTrack *current_tp_track;
197 gpointer current_tp_id;
50a14534
EB
198 VikTrwLayerTpwin *tpwin;
199
50a14534
EB
200 /* track editing tool -- more specifically, moving tps */
201 gboolean moving_tp;
202
7ff7d728
RN
203 /* route finder tool */
204 gboolean route_finder_started;
7ff7d728 205 gboolean route_finder_check_added_track;
ce4bd1cf 206 VikTrack *route_finder_added_track;
7ff7d728 207 gboolean route_finder_append;
1eef1bde 208
50a14534
EB
209 gboolean drawlabels;
210 gboolean drawimages;
211 guint8 image_alpha;
212 GQueue *image_cache;
213 guint8 image_size;
214 guint16 image_cache_size;
215
216 /* for waypoint text */
217 PangoLayout *wplabellayout;
218
219 gboolean has_verified_thumbnails;
220
221 GtkMenu *wp_right_click_menu;
8bd81489 222 GtkMenu *track_right_click_menu;
50a14534 223
20c7a3a0
QT
224 /* menu */
225 VikStdLayerMenuItem menu_selection;
226
a8fe53f8 227 gint highest_wp_number;
43984ba1
RN
228
229 // One per layer
230 GtkWidget *tracks_analysis_dialog;
50a14534
EB
231};
232
233/* A caached waypoint image. */
234typedef struct {
235 GdkPixbuf *pixbuf;
236 gchar *image; /* filename */
237} CachedPixbuf;
238
239struct DrawingParams {
240 VikViewport *vp;
241 VikTrwLayer *vtl;
b2ddff02 242 VikWindow *vw;
50a14534
EB
243 gdouble xmpp, ympp;
244 guint16 width, height;
08084371
RN
245 gdouble cc; // Cosine factor in track directions
246 gdouble ss; // Sine factor in track directions
50a14534 247 const VikCoord *center;
50a14534
EB
248 gboolean one_zone, lat_lon;
249 gdouble ce1, ce2, cn1, cn2;
20981fd6 250 LatLonBBox bbox;
bc07590a 251 gboolean highlight;
50a14534
EB
252};
253
c9570f86
RN
254static gboolean trw_layer_delete_waypoint ( VikTrwLayer *vtl, VikWaypoint *wp );
255
1c2083ba
RN
256typedef enum {
257 MA_VTL = 0,
258 MA_VLP,
19782ffd
RN
259 MA_SUBTYPE, // OR END for Layer only
260 MA_SUBLAYER_ID,
261 MA_CONFIRM,
262 MA_VVP,
263 MA_TV_ITER,
264 MA_MISC,
265 MA_LAST,
1c2083ba
RN
266} menu_array_index;
267
19782ffd
RN
268typedef gpointer menu_array_layer[2];
269typedef gpointer menu_array_sublayer[MA_LAST];
1c2083ba 270
19782ffd
RN
271static void trw_layer_delete_item ( menu_array_sublayer values );
272static void trw_layer_copy_item_cb ( menu_array_sublayer values );
273static void trw_layer_cut_item_cb ( menu_array_sublayer values );
33534cd8 274
9e212bfc 275static void trw_layer_find_maxmin_tracks ( const gpointer id, const VikTrack *trk, struct LatLon maxmin[2] );
165a4fa9 276static void trw_layer_find_maxmin (VikTrwLayer *vtl, struct LatLon maxmin[2]);
50a14534
EB
277
278static void trw_layer_new_track_gcs ( VikTrwLayer *vtl, VikViewport *vp );
279static void trw_layer_free_track_gcs ( VikTrwLayer *vtl );
280
9e212bfc
RN
281static void trw_layer_draw_track_cb ( const gpointer id, VikTrack *track, struct DrawingParams *dp );
282static void trw_layer_draw_waypoint ( const gpointer id, VikWaypoint *wp, struct DrawingParams *dp );
50a14534 283
6bb72350 284static void goto_coord ( gpointer *vlp, gpointer vvp, gpointer vl, const VikCoord *coord );
19782ffd
RN
285static void trw_layer_goto_track_startpoint ( menu_array_sublayer values );
286static void trw_layer_goto_track_endpoint ( menu_array_sublayer values );
287static void trw_layer_goto_track_max_speed ( menu_array_sublayer values );
288static void trw_layer_goto_track_max_alt ( menu_array_sublayer values );
289static void trw_layer_goto_track_min_alt ( menu_array_sublayer values );
290static void trw_layer_goto_track_center ( menu_array_sublayer values );
291static void trw_layer_merge_by_segment ( menu_array_sublayer values );
292static void trw_layer_merge_by_timestamp ( menu_array_sublayer values );
293static void trw_layer_merge_with_other ( menu_array_sublayer values );
294static void trw_layer_append_track ( menu_array_sublayer values );
295static void trw_layer_split_by_timestamp ( menu_array_sublayer values );
296static void trw_layer_split_by_n_points ( menu_array_sublayer values );
297static void trw_layer_split_at_trackpoint ( menu_array_sublayer values );
298static void trw_layer_split_segments ( menu_array_sublayer values );
299static void trw_layer_delete_point_selected ( menu_array_sublayer values );
300static void trw_layer_delete_points_same_position ( menu_array_sublayer values );
301static void trw_layer_delete_points_same_time ( menu_array_sublayer values );
302static void trw_layer_reverse ( menu_array_sublayer values );
303static void trw_layer_download_map_along_track_cb ( menu_array_sublayer values );
304static void trw_layer_edit_trackpoint ( menu_array_sublayer values );
305static void trw_layer_show_picture ( menu_array_sublayer values );
306static void trw_layer_gps_upload_any ( menu_array_sublayer values );
c95d6b00 307
1c2083ba
RN
308static void trw_layer_centerize ( menu_array_layer values );
309static void trw_layer_auto_view ( menu_array_layer values );
1c2083ba
RN
310static void trw_layer_goto_wp ( menu_array_layer values );
311static void trw_layer_new_wp ( menu_array_layer values );
312static void trw_layer_new_track ( menu_array_layer values );
313static void trw_layer_new_route ( menu_array_layer values );
314static void trw_layer_finish_track ( menu_array_layer values );
315static void trw_layer_auto_waypoints_view ( menu_array_layer values );
316static void trw_layer_auto_tracks_view ( menu_array_layer values );
317static void trw_layer_delete_all_tracks ( menu_array_layer values );
318static void trw_layer_delete_tracks_from_selection ( menu_array_layer values );
319static void trw_layer_delete_all_waypoints ( menu_array_layer values );
320static void trw_layer_delete_waypoints_from_selection ( menu_array_layer values );
321static void trw_layer_new_wikipedia_wp_viewport ( menu_array_layer values );
322static void trw_layer_new_wikipedia_wp_layer ( menu_array_layer values );
b3eb3b98 323#ifdef VIK_CONFIG_GEOTAG
19782ffd
RN
324static void trw_layer_geotagging_waypoint_mtime_keep ( menu_array_sublayer values );
325static void trw_layer_geotagging_waypoint_mtime_update ( menu_array_sublayer values );
326static void trw_layer_geotagging_track ( menu_array_sublayer values );
1c2083ba 327static void trw_layer_geotagging ( menu_array_layer values );
b3eb3b98 328#endif
1c2083ba
RN
329static void trw_layer_acquire_gps_cb ( menu_array_layer values );
330static void trw_layer_acquire_routing_cb ( menu_array_layer values );
331static void trw_layer_acquire_url_cb ( menu_array_layer values );
40f5740b 332#ifdef VIK_CONFIG_OPENSTREETMAP
1c2083ba
RN
333static void trw_layer_acquire_osm_cb ( menu_array_layer values );
334static void trw_layer_acquire_osm_my_traces_cb ( menu_array_layer values );
40f5740b 335#endif
16fc32f6 336#ifdef VIK_CONFIG_GEOCACHES
1c2083ba 337static void trw_layer_acquire_geocache_cb ( menu_array_layer values );
16fc32f6 338#endif
68bab1bd 339#ifdef VIK_CONFIG_GEOTAG
1c2083ba 340static void trw_layer_acquire_geotagged_cb ( menu_array_layer values );
68bab1bd 341#endif
1c2083ba
RN
342static void trw_layer_acquire_file_cb ( menu_array_layer values );
343static void trw_layer_gps_upload ( menu_array_layer values );
50a14534 344
19782ffd 345static void trw_layer_track_list_dialog_single ( menu_array_sublayer values );
1c2083ba
RN
346static void trw_layer_track_list_dialog ( menu_array_layer values );
347static void trw_layer_waypoint_list_dialog ( menu_array_layer values );
07c9d42b 348
0d2b891f
RN
349// Specific route versions:
350// Most track handling functions can handle operating on the route list
351// However these ones are easier in separate functions
1c2083ba
RN
352static void trw_layer_auto_routes_view ( menu_array_layer values );
353static void trw_layer_delete_all_routes ( menu_array_layer values );
354static void trw_layer_delete_routes_from_selection ( menu_array_layer values );
0d2b891f 355
50a14534 356/* pop-up items */
19782ffd
RN
357static void trw_layer_properties_item ( gpointer pass_along[7] ); //TODO??
358static void trw_layer_goto_waypoint ( menu_array_sublayer values );
359static void trw_layer_waypoint_gc_webpage ( menu_array_sublayer values );
360static void trw_layer_waypoint_webpage ( menu_array_sublayer values );
50a14534 361
c9570f86 362static void trw_layer_realize_waypoint ( gpointer id, VikWaypoint *wp, gpointer pass_along[5] );
ce4bd1cf 363static void trw_layer_realize_track ( gpointer id, VikTrack *track, gpointer pass_along[5] );
50a14534 364
491794be 365static void trw_layer_insert_tp_beside_current_tp ( VikTrwLayer *vtl, gboolean );
50a14534
EB
366static void trw_layer_cancel_current_tp ( VikTrwLayer *vtl, gboolean destroy );
367static void trw_layer_tpwin_response ( VikTrwLayer *vtl, gint response );
368static void trw_layer_tpwin_init ( VikTrwLayer *vtl );
50a14534 369
941aa6e9 370static gpointer tool_edit_trackpoint_create ( VikWindow *vw, VikViewport *vvp);
919ed63e 371static void tool_edit_trackpoint_destroy ( tool_ed_t *t );
33534cd8 372static gboolean tool_edit_trackpoint_click ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data );
dc2c040e 373static gboolean tool_edit_trackpoint_move ( VikTrwLayer *vtl, GdkEventMotion *event, gpointer data );
33534cd8 374static gboolean tool_edit_trackpoint_release ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data );
941aa6e9
AF
375static gpointer tool_show_picture_create ( VikWindow *vw, VikViewport *vvp);
376static gboolean tool_show_picture_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp );
377static gpointer tool_edit_waypoint_create ( VikWindow *vw, VikViewport *vvp);
919ed63e 378static void tool_edit_waypoint_destroy ( tool_ed_t *t );
7432fddf 379static gboolean tool_edit_waypoint_click ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data );
dc2c040e 380static gboolean tool_edit_waypoint_move ( VikTrwLayer *vtl, GdkEventMotion *event, gpointer data );
7432fddf 381static gboolean tool_edit_waypoint_release ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data );
e37b2a6d
RN
382static gpointer tool_new_route_create ( VikWindow *vw, VikViewport *vvp);
383static gboolean tool_new_route_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp );
941aa6e9
AF
384static gpointer tool_new_track_create ( VikWindow *vw, VikViewport *vvp);
385static gboolean tool_new_track_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp );
dc2c040e 386static VikLayerToolFuncStatus tool_new_track_move ( VikTrwLayer *vtl, GdkEventMotion *event, VikViewport *vvp );
ef5e8132 387static void tool_new_track_release ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp );
777e2d4d 388static gboolean tool_new_track_key_press ( VikTrwLayer *vtl, GdkEventKey *event, VikViewport *vvp );
941aa6e9
AF
389static gpointer tool_new_waypoint_create ( VikWindow *vw, VikViewport *vvp);
390static gboolean tool_new_waypoint_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp );
ee43b101
MH
391static gpointer tool_extended_route_finder_create ( VikWindow *vw, VikViewport *vvp);
392static gboolean tool_extended_route_finder_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp );
393static gboolean tool_extended_route_finder_key_press ( VikTrwLayer *vtl, GdkEventKey *event, VikViewport *vvp );
50a14534 394
50a14534
EB
395static void cached_pixbuf_free ( CachedPixbuf *cp );
396static gint cached_pixbuf_cmp ( CachedPixbuf *cp, const gchar *name );
50a14534
EB
397
398static VikTrackpoint *closest_tp_in_five_pixel_interval ( VikTrwLayer *vtl, VikViewport *vvp, gint x, gint y );
399static VikWaypoint *closest_wp_in_five_pixel_interval ( VikTrwLayer *vtl, VikViewport *vvp, gint x, gint y );
400
c9570f86 401static void waypoint_convert ( const gpointer id, VikWaypoint *wp, VikCoordMode *dest_mode );
9e212bfc 402static void track_convert ( const gpointer id, VikTrack *tr, VikCoordMode *dest_mode );
e890a6e6 403
a8fe53f8
EB
404static gchar *highest_wp_number_get(VikTrwLayer *vtl);
405static void highest_wp_number_reset(VikTrwLayer *vtl);
406static void highest_wp_number_add_wp(VikTrwLayer *vtl, const gchar *new_wp_name);
407static void highest_wp_number_remove_wp(VikTrwLayer *vtl, const gchar *old_wp_name);
408
79dce0cb
RN
409// Note for the following tool GtkRadioActionEntry texts:
410// the very first text value is an internal name not displayed anywhere
411// the first N_ text value is the name used for menu entries - hence has an underscore for the keyboard accelerator
412// * remember not to clash with the values used for VikWindow level tools (Pan, Zoom, Ruler + Select)
413// the second N_ text value is used for the button tooltip (i.e. generally don't want an underscore here)
414// the value is always set to 0 and the tool loader in VikWindow will set the actual appropriate value used
50a14534 415static VikToolInterface trw_layer_tools[] = {
79dce0cb
RN
416 { { "CreateWaypoint", "vik-icon-Create Waypoint", N_("Create _Waypoint"), "<control><shift>W", N_("Create Waypoint"), 0 },
417 (VikToolConstructorFunc) tool_new_waypoint_create, NULL, NULL, NULL,
ef5e8132
RN
418 (VikToolMouseFunc) tool_new_waypoint_click, NULL, NULL, (VikToolKeyFunc) NULL,
419 FALSE,
63959706 420 GDK_CURSOR_IS_PIXMAP, &cursor_addwp_pixbuf, NULL },
941aa6e9 421
79dce0cb
RN
422 { { "CreateTrack", "vik-icon-Create Track", N_("Create _Track"), "<control><shift>T", N_("Create Track"), 0 },
423 (VikToolConstructorFunc) tool_new_track_create, NULL, NULL, NULL,
ef5e8132
RN
424 (VikToolMouseFunc) tool_new_track_click,
425 (VikToolMouseMoveFunc) tool_new_track_move,
426 (VikToolMouseFunc) tool_new_track_release,
427 (VikToolKeyFunc) tool_new_track_key_press,
428 TRUE, // Still need to handle clicks when in PAN mode to disable the potential trackpoint drawing
63959706 429 GDK_CURSOR_IS_PIXMAP, &cursor_addtr_pixbuf, NULL },
50a14534 430
e37b2a6d
RN
431 { { "CreateRoute", "vik-icon-Create Route", N_("Create _Route"), "<control><shift>B", N_("Create Route"), 0 },
432 (VikToolConstructorFunc) tool_new_route_create, NULL, NULL, NULL,
433 (VikToolMouseFunc) tool_new_route_click,
434 (VikToolMouseMoveFunc) tool_new_track_move, // -\#
435 (VikToolMouseFunc) tool_new_track_release, // -> Reuse these track methods on a route
436 (VikToolKeyFunc) tool_new_track_key_press, // -/#
437 TRUE, // Still need to handle clicks when in PAN mode to disable the potential trackpoint drawing
63959706 438 GDK_CURSOR_IS_PIXMAP, &cursor_new_route_pixbuf, NULL },
98f5364d 439
ee43b101
MH
440 { { "ExtendedRouteFinder", "vik-icon-Route Finder", N_("Route _Finder"), "<control><shift>F", N_("Route Finder"), 0 },
441 (VikToolConstructorFunc) tool_extended_route_finder_create, NULL, NULL, NULL,
442 (VikToolMouseFunc) tool_extended_route_finder_click,
443 (VikToolMouseMoveFunc) tool_new_track_move, // -\#
444 (VikToolMouseFunc) tool_new_track_release, // -> Reuse these track methods on a route
445 (VikToolKeyFunc) tool_extended_route_finder_key_press,
446 TRUE, // Still need to handle clicks when in PAN mode to disable the potential trackpoint drawing
447 GDK_CURSOR_IS_PIXMAP, &cursor_route_finder_pixbuf, NULL },
448
79dce0cb 449 { { "EditWaypoint", "vik-icon-Edit Waypoint", N_("_Edit Waypoint"), "<control><shift>E", N_("Edit Waypoint"), 0 },
919ed63e
RN
450 (VikToolConstructorFunc) tool_edit_waypoint_create,
451 (VikToolDestructorFunc) tool_edit_waypoint_destroy,
452 NULL, NULL,
7432fddf 453 (VikToolMouseFunc) tool_edit_waypoint_click,
dc2c040e 454 (VikToolMouseMoveFunc) tool_edit_waypoint_move,
ef5e8132
RN
455 (VikToolMouseFunc) tool_edit_waypoint_release, (VikToolKeyFunc) NULL,
456 FALSE,
63959706 457 GDK_CURSOR_IS_PIXMAP, &cursor_edwp_pixbuf, NULL },
941aa6e9 458
79dce0cb 459 { { "EditTrackpoint", "vik-icon-Edit Trackpoint", N_("Edit Trac_kpoint"), "<control><shift>K", N_("Edit Trackpoint"), 0 },
919ed63e
RN
460 (VikToolConstructorFunc) tool_edit_trackpoint_create,
461 (VikToolDestructorFunc) tool_edit_trackpoint_destroy,
462 NULL, NULL,
33534cd8 463 (VikToolMouseFunc) tool_edit_trackpoint_click,
dc2c040e 464 (VikToolMouseMoveFunc) tool_edit_trackpoint_move,
ef5e8132
RN
465 (VikToolMouseFunc) tool_edit_trackpoint_release, (VikToolKeyFunc) NULL,
466 FALSE,
63959706 467 GDK_CURSOR_IS_PIXMAP, &cursor_edtr_pixbuf, NULL },
941aa6e9 468
79dce0cb
RN
469 { { "ShowPicture", "vik-icon-Show Picture", N_("Show P_icture"), "<control><shift>I", N_("Show Picture"), 0 },
470 (VikToolConstructorFunc) tool_show_picture_create, NULL, NULL, NULL,
ef5e8132
RN
471 (VikToolMouseFunc) tool_show_picture_click, NULL, NULL, (VikToolKeyFunc) NULL,
472 FALSE,
63959706 473 GDK_CURSOR_IS_PIXMAP, &cursor_showpic_pixbuf, NULL },
1eef1bde 474
941aa6e9 475};
37baade6
RN
476
477enum {
478 TOOL_CREATE_WAYPOINT=0,
479 TOOL_CREATE_TRACK,
480 TOOL_CREATE_ROUTE,
ee43b101 481 TOOL_ROUTE_FINDER,
37baade6
RN
482 TOOL_EDIT_WAYPOINT,
483 TOOL_EDIT_TRACKPOINT,
484 TOOL_SHOW_PICTURE,
37baade6
RN
485 NUM_TOOLS
486};
50a14534
EB
487
488/****** PARAMETERS ******/
489
6ba42f1e
RN
490static gchar *params_groups[] = { N_("Waypoints"), N_("Tracks"), N_("Waypoint Images"), N_("Tracks Advanced"), N_("Metadata") };
491enum { GROUP_WAYPOINTS, GROUP_TRACKS, GROUP_IMAGES, GROUP_TRACKS_ADV, GROUP_METADATA };
50a14534 492
074b1067 493static gchar *params_drawmodes[] = { N_("Draw by Track"), N_("Draw by Speed"), N_("All Tracks Same Color"), NULL };
4c77d5e0 494static gchar *params_wpsymbols[] = { N_("Filled Square"), N_("Square"), N_("Circle"), N_("X"), 0 };
50a14534 495
3a1c8e42
RN
496#define MIN_POINT_SIZE 2
497#define MAX_POINT_SIZE 10
498
499#define MIN_ARROW_SIZE 3
500#define MAX_ARROW_SIZE 20
b42a25ba 501
50a14534
EB
502static VikLayerParamScale params_scales[] = {
503 /* min max step digits */
504 { 1, 10, 1, 0 }, /* line_thickness */
18df6d42
RN
505 { 0, 100, 1, 0 }, /* track draw speed factor */
506 { 1.0, 100.0, 1.0, 2 }, /* UNUSED */
50a14534 507 /* 5 * step == how much to turn */
3a1c8e42
RN
508 { 16, 128, 4, 0 }, // 3: image_size - NB step size ignored when an HSCALE used
509 { 0, 255, 5, 0 }, // 4: image alpha - " " " "
510 { 5, 500, 5, 0 }, // 5: image cache_size - " "
511 { 0, 8, 1, 0 }, // 6: Background line thickness
50a14534 512 { 1, 64, 1, 0 }, /* wpsize */
b42a25ba 513 { MIN_STOP_LENGTH, MAX_STOP_LENGTH, 1, 0 }, /* stop_length */
3a1c8e42
RN
514 { 1, 100, 1, 0 }, // 9: elevation factor
515 { MIN_POINT_SIZE, MAX_POINT_SIZE, 1, 0 }, // 10: track point size
516 { MIN_ARROW_SIZE, MAX_ARROW_SIZE, 1, 0 }, // 11: direction arrow size
50a14534
EB
517};
518
5a77ae71
RN
519static gchar* params_font_sizes[] = {
520 N_("Extra Extra Small"),
521 N_("Extra Small"),
522 N_("Small"),
523 N_("Medium"),
524 N_("Large"),
525 N_("Extra Large"),
526 N_("Extra Extra Large"),
527 NULL };
528
c9cac058
RN
529// Needs to align with vik_layer_sort_order_t
530static gchar* params_sort_order[] = {
531 N_("None"),
532 N_("Name Ascending"),
533 N_("Name Descending"),
534 NULL
535};
536
a7023a1b
RN
537static VikLayerParamData black_color_default ( void ) {
538 VikLayerParamData data; gdk_color_parse ( "#000000", &data.c ); return data; // Black
539}
540static VikLayerParamData drawmode_default ( void ) { return VIK_LPD_UINT ( DRAWMODE_BY_TRACK ); }
541static VikLayerParamData line_thickness_default ( void ) { return VIK_LPD_UINT ( 1 ); }
542static VikLayerParamData trkpointsize_default ( void ) { return VIK_LPD_UINT ( MIN_POINT_SIZE ); }
543static VikLayerParamData trkdirectionsize_default ( void ) { return VIK_LPD_UINT ( 5 ); }
544static VikLayerParamData bg_line_thickness_default ( void ) { return VIK_LPD_UINT ( 0 ); }
545static VikLayerParamData trackbgcolor_default ( void ) {
546 VikLayerParamData data; gdk_color_parse ( "#FFFFFF", &data.c ); return data; // White
547}
548static VikLayerParamData elevation_factor_default ( void ) { return VIK_LPD_UINT ( 30 ); }
549static VikLayerParamData stop_length_default ( void ) { return VIK_LPD_UINT ( 60 ); }
550static VikLayerParamData speed_factor_default ( void ) { return VIK_LPD_DOUBLE ( 30.0 ); }
551
387ff7ac 552static VikLayerParamData tnfontsize_default ( void ) { return VIK_LPD_UINT ( FS_MEDIUM ); }
a7023a1b
RN
553static VikLayerParamData wpfontsize_default ( void ) { return VIK_LPD_UINT ( FS_MEDIUM ); }
554static VikLayerParamData wptextcolor_default ( void ) {
555 VikLayerParamData data; gdk_color_parse ( "#FFFFFF", &data.c ); return data; // White
556}
557static VikLayerParamData wpbgcolor_default ( void ) {
558 VikLayerParamData data; gdk_color_parse ( "#8383C4", &data.c ); return data; // Kind of Blue
559}
560static VikLayerParamData wpsize_default ( void ) { return VIK_LPD_UINT ( 4 ); }
561static VikLayerParamData wpsymbol_default ( void ) { return VIK_LPD_UINT ( WP_SYMBOL_FILLED_SQUARE ); }
562
563static VikLayerParamData image_size_default ( void ) { return VIK_LPD_UINT ( 64 ); }
564static VikLayerParamData image_alpha_default ( void ) { return VIK_LPD_UINT ( 255 ); }
565static VikLayerParamData image_cache_size_default ( void ) { return VIK_LPD_UINT ( 300 ); }
566
c9cac058
RN
567static VikLayerParamData sort_order_default ( void ) { return VIK_LPD_UINT ( 0 ); }
568
6ba42f1e
RN
569static VikLayerParamData string_default ( void )
570{
571 VikLayerParamData data;
572 data.s = "";
573 return data;
574}
575
50a14534 576VikLayerParam trw_layer_params[] = {
a87f8fa1
RN
577 { VIK_LAYER_TRW, "tracks_visible", VIK_LAYER_PARAM_BOOLEAN, VIK_LAYER_NOT_IN_PROPERTIES, NULL, 0, NULL, NULL, NULL, vik_lpd_true_default, NULL, NULL },
578 { VIK_LAYER_TRW, "waypoints_visible", VIK_LAYER_PARAM_BOOLEAN, VIK_LAYER_NOT_IN_PROPERTIES, NULL, 0, NULL, NULL, NULL, vik_lpd_true_default, NULL, NULL },
579 { VIK_LAYER_TRW, "routes_visible", VIK_LAYER_PARAM_BOOLEAN, VIK_LAYER_NOT_IN_PROPERTIES, NULL, 0, NULL, NULL, NULL, vik_lpd_true_default, NULL, NULL },
a7023a1b 580
387ff7ac
RN
581 { VIK_LAYER_TRW, "trackdrawlabels", VIK_LAYER_PARAM_BOOLEAN, GROUP_TRACKS, N_("Draw Labels"), VIK_LAYER_WIDGET_CHECKBUTTON, NULL, NULL,
582 N_("Note: the individual track controls what labels may be displayed"), vik_lpd_true_default, NULL, NULL },
d1496bfc 583 { VIK_LAYER_TRW, "trackfontsize", VIK_LAYER_PARAM_UINT, GROUP_TRACKS_ADV, N_("Track Labels Font Size:"), VIK_LAYER_WIDGET_COMBOBOX, params_font_sizes, NULL, NULL, tnfontsize_default, NULL, NULL },
a87f8fa1 584 { VIK_LAYER_TRW, "drawmode", VIK_LAYER_PARAM_UINT, GROUP_TRACKS, N_("Track Drawing Mode:"), VIK_LAYER_WIDGET_COMBOBOX, params_drawmodes, NULL, NULL, drawmode_default, NULL, NULL },
a7023a1b 585 { VIK_LAYER_TRW, "trackcolor", VIK_LAYER_PARAM_COLOR, GROUP_TRACKS, N_("All Tracks Color:"), VIK_LAYER_WIDGET_COLOR, NULL, NULL,
a87f8fa1
RN
586 N_("The color used when 'All Tracks Same Color' drawing mode is selected"), black_color_default, NULL, NULL },
587 { VIK_LAYER_TRW, "drawlines", VIK_LAYER_PARAM_BOOLEAN, GROUP_TRACKS, N_("Draw Track Lines"), VIK_LAYER_WIDGET_CHECKBUTTON, NULL, NULL, NULL, vik_lpd_true_default, NULL, NULL },
d1496bfc 588 { VIK_LAYER_TRW, "line_thickness", VIK_LAYER_PARAM_UINT, GROUP_TRACKS_ADV, N_("Track Thickness:"), VIK_LAYER_WIDGET_SPINBUTTON, &params_scales[0], NULL, NULL, line_thickness_default, NULL, NULL },
a87f8fa1 589 { VIK_LAYER_TRW, "drawdirections", VIK_LAYER_PARAM_BOOLEAN, GROUP_TRACKS, N_("Draw Track Direction"), VIK_LAYER_WIDGET_CHECKBUTTON, NULL, NULL, NULL, vik_lpd_false_default, NULL, NULL },
d1496bfc 590 { VIK_LAYER_TRW, "trkdirectionsize", VIK_LAYER_PARAM_UINT, GROUP_TRACKS_ADV, N_("Direction Size:"), VIK_LAYER_WIDGET_SPINBUTTON, &params_scales[11], NULL, NULL, trkdirectionsize_default, NULL, NULL },
a87f8fa1 591 { VIK_LAYER_TRW, "drawpoints", VIK_LAYER_PARAM_BOOLEAN, GROUP_TRACKS, N_("Draw Trackpoints"), VIK_LAYER_WIDGET_CHECKBUTTON, NULL, NULL, NULL, vik_lpd_true_default, NULL, NULL },
d1496bfc 592 { VIK_LAYER_TRW, "trkpointsize", VIK_LAYER_PARAM_UINT, GROUP_TRACKS_ADV, N_("Trackpoint Size:"), VIK_LAYER_WIDGET_SPINBUTTON, &params_scales[10], NULL, NULL, trkpointsize_default, NULL, NULL },
a87f8fa1 593 { VIK_LAYER_TRW, "drawelevation", VIK_LAYER_PARAM_BOOLEAN, GROUP_TRACKS, N_("Draw Elevation"), VIK_LAYER_WIDGET_CHECKBUTTON, NULL, NULL, NULL, vik_lpd_false_default, NULL, NULL },
d1496bfc 594 { VIK_LAYER_TRW, "elevation_factor", VIK_LAYER_PARAM_UINT, GROUP_TRACKS_ADV, N_("Draw Elevation Height %:"), VIK_LAYER_WIDGET_HSCALE, &params_scales[9], NULL, NULL, elevation_factor_default, NULL, NULL },
a7023a1b 595 { VIK_LAYER_TRW, "drawstops", VIK_LAYER_PARAM_BOOLEAN, GROUP_TRACKS, N_("Draw Stops"), VIK_LAYER_WIDGET_CHECKBUTTON, NULL, NULL,
a87f8fa1 596 N_("Whether to draw a marker when trackpoints are at the same position but over the minimum stop length apart in time"), vik_lpd_false_default, NULL, NULL },
d1496bfc 597 { VIK_LAYER_TRW, "stop_length", VIK_LAYER_PARAM_UINT, GROUP_TRACKS_ADV, N_("Min Stop Length (seconds):"), VIK_LAYER_WIDGET_SPINBUTTON, &params_scales[8], NULL, NULL, stop_length_default, NULL, NULL },
a7023a1b 598
d1496bfc
RN
599 { VIK_LAYER_TRW, "bg_line_thickness", VIK_LAYER_PARAM_UINT, GROUP_TRACKS_ADV, N_("Track BG Thickness:"), VIK_LAYER_WIDGET_SPINBUTTON, &params_scales[6], NULL, NULL, bg_line_thickness_default, NULL, NULL },
600 { VIK_LAYER_TRW, "trackbgcolor", VIK_LAYER_PARAM_COLOR, GROUP_TRACKS_ADV, N_("Track Background Color"), VIK_LAYER_WIDGET_COLOR, NULL, NULL, NULL, trackbgcolor_default, NULL, NULL },
601 { VIK_LAYER_TRW, "speed_factor", VIK_LAYER_PARAM_DOUBLE, GROUP_TRACKS_ADV, N_("Draw by Speed Factor (%):"), VIK_LAYER_WIDGET_HSCALE, &params_scales[1], NULL,
a87f8fa1 602 N_("The percentage factor away from the average speed determining the color used"), speed_factor_default, NULL, NULL },
d1496bfc 603 { VIK_LAYER_TRW, "tracksortorder", VIK_LAYER_PARAM_UINT, GROUP_TRACKS_ADV, N_("Track Sort Order:"), VIK_LAYER_WIDGET_COMBOBOX, params_sort_order, NULL, NULL, sort_order_default, NULL, NULL },
a87f8fa1
RN
604
605 { VIK_LAYER_TRW, "drawlabels", VIK_LAYER_PARAM_BOOLEAN, GROUP_WAYPOINTS, N_("Draw Labels"), VIK_LAYER_WIDGET_CHECKBUTTON, NULL, NULL, NULL, vik_lpd_true_default, NULL, NULL },
606 { VIK_LAYER_TRW, "wpfontsize", VIK_LAYER_PARAM_UINT, GROUP_WAYPOINTS, N_("Waypoint Font Size:"), VIK_LAYER_WIDGET_COMBOBOX, params_font_sizes, NULL, NULL, wpfontsize_default, NULL, NULL },
607 { VIK_LAYER_TRW, "wpcolor", VIK_LAYER_PARAM_COLOR, GROUP_WAYPOINTS, N_("Waypoint Color:"), VIK_LAYER_WIDGET_COLOR, NULL, NULL, NULL, black_color_default, NULL, NULL },
608 { VIK_LAYER_TRW, "wptextcolor", VIK_LAYER_PARAM_COLOR, GROUP_WAYPOINTS, N_("Waypoint Text:"), VIK_LAYER_WIDGET_COLOR, NULL, NULL, NULL, wptextcolor_default, NULL, NULL },
609 { VIK_LAYER_TRW, "wpbgcolor", VIK_LAYER_PARAM_COLOR, GROUP_WAYPOINTS, N_("Background:"), VIK_LAYER_WIDGET_COLOR, NULL, NULL, NULL, wpbgcolor_default, NULL, NULL },
610 { VIK_LAYER_TRW, "wpbgand", VIK_LAYER_PARAM_BOOLEAN, GROUP_WAYPOINTS, N_("Fake BG Color Translucency:"), VIK_LAYER_WIDGET_CHECKBUTTON, NULL, NULL, NULL, vik_lpd_false_default, NULL, NULL },
611 { VIK_LAYER_TRW, "wpsymbol", VIK_LAYER_PARAM_UINT, GROUP_WAYPOINTS, N_("Waypoint marker:"), VIK_LAYER_WIDGET_COMBOBOX, params_wpsymbols, NULL, NULL, wpsymbol_default, NULL, NULL },
612 { VIK_LAYER_TRW, "wpsize", VIK_LAYER_PARAM_UINT, GROUP_WAYPOINTS, N_("Waypoint size:"), VIK_LAYER_WIDGET_SPINBUTTON, &params_scales[7], NULL, NULL, wpsize_default, NULL, NULL },
613 { VIK_LAYER_TRW, "wpsyms", VIK_LAYER_PARAM_BOOLEAN, GROUP_WAYPOINTS, N_("Draw Waypoint Symbols:"), VIK_LAYER_WIDGET_CHECKBUTTON, NULL, NULL, NULL, vik_lpd_true_default, NULL, NULL },
614 { VIK_LAYER_TRW, "wpsortorder", VIK_LAYER_PARAM_UINT, GROUP_WAYPOINTS, N_("Waypoint Sort Order:"), VIK_LAYER_WIDGET_COMBOBOX, params_sort_order, NULL, NULL, sort_order_default, NULL, NULL },
615
616 { VIK_LAYER_TRW, "drawimages", VIK_LAYER_PARAM_BOOLEAN, GROUP_IMAGES, N_("Draw Waypoint Images"), VIK_LAYER_WIDGET_CHECKBUTTON, NULL, NULL, NULL, vik_lpd_true_default, NULL, NULL },
617 { VIK_LAYER_TRW, "image_size", VIK_LAYER_PARAM_UINT, GROUP_IMAGES, N_("Image Size (pixels):"), VIK_LAYER_WIDGET_HSCALE, &params_scales[3], NULL, NULL, image_size_default, NULL, NULL },
618 { VIK_LAYER_TRW, "image_alpha", VIK_LAYER_PARAM_UINT, GROUP_IMAGES, N_("Image Alpha:"), VIK_LAYER_WIDGET_HSCALE, &params_scales[4], NULL, NULL, image_alpha_default, NULL, NULL },
619 { VIK_LAYER_TRW, "image_cache_size", VIK_LAYER_PARAM_UINT, GROUP_IMAGES, N_("Image Memory Cache Size:"), VIK_LAYER_WIDGET_HSCALE, &params_scales[5], NULL, NULL, image_cache_size_default, NULL, NULL },
6ba42f1e
RN
620
621 { VIK_LAYER_TRW, "metadatadesc", VIK_LAYER_PARAM_STRING, GROUP_METADATA, N_("Description"), VIK_LAYER_WIDGET_ENTRY, NULL, NULL, NULL, string_default, NULL, NULL },
622 { VIK_LAYER_TRW, "metadataauthor", VIK_LAYER_PARAM_STRING, GROUP_METADATA, N_("Author"), VIK_LAYER_WIDGET_ENTRY, NULL, NULL, NULL, string_default, NULL, NULL },
623 { VIK_LAYER_TRW, "metadatatime", VIK_LAYER_PARAM_STRING, GROUP_METADATA, N_("Creation Time"), VIK_LAYER_WIDGET_ENTRY, NULL, NULL, NULL, string_default, NULL, NULL },
624 { VIK_LAYER_TRW, "metadatakeywords", VIK_LAYER_PARAM_STRING, GROUP_METADATA, N_("Keywords"), VIK_LAYER_WIDGET_ENTRY, NULL, NULL, NULL, string_default, NULL, NULL },
50a14534
EB
625};
626
3a1c8e42 627// ENUMERATION MUST BE IN THE SAME ORDER AS THE NAMED PARAMS ABOVE
08084371
RN
628enum {
629 // Sublayer visibilities
630 PARAM_TV,
631 PARAM_WV,
0d2b891f 632 PARAM_RV,
08084371 633 // Tracks
387ff7ac
RN
634 PARAM_TDL,
635 PARAM_TLFONTSIZE,
08084371 636 PARAM_DM,
074b1067 637 PARAM_TC,
08084371 638 PARAM_DL,
3a1c8e42 639 PARAM_LT,
08084371 640 PARAM_DD,
3a1c8e42 641 PARAM_DDS,
08084371 642 PARAM_DP,
3a1c8e42 643 PARAM_DPS,
08084371
RN
644 PARAM_DE,
645 PARAM_EF,
646 PARAM_DS,
647 PARAM_SL,
08084371
RN
648 PARAM_BLT,
649 PARAM_TBGC,
650 PARAM_TDSF,
c9cac058 651 PARAM_TSO,
08084371 652 // Waypoints
3a1c8e42 653 PARAM_DLA,
08084371
RN
654 PARAM_WPFONTSIZE,
655 PARAM_WPC,
656 PARAM_WPTC,
657 PARAM_WPBC,
658 PARAM_WPBA,
659 PARAM_WPSYM,
660 PARAM_WPSIZE,
661 PARAM_WPSYMS,
c9cac058 662 PARAM_WPSO,
08084371
RN
663 // WP images
664 PARAM_DI,
665 PARAM_IS,
666 PARAM_IA,
667 PARAM_ICS,
6ba42f1e
RN
668 // Metadata
669 PARAM_MDDESC,
670 PARAM_MDAUTH,
671 PARAM_MDTIME,
672 PARAM_MDKEYS,
08084371
RN
673 NUM_PARAMS
674};
ad0a8c2d
EB
675
676/*** TO ADD A PARAM:
677 *** 1) Add to trw_layer_params and enumeration
678 *** 2) Handle in get_param & set_param (presumably adding on to VikTrwLayer struct)
ad0a8c2d 679 ***/
50a14534
EB
680
681/****** END PARAMETERS ******/
682
a7cd93ac
RN
683/* Layer Interface function definitions */
684static VikTrwLayer* trw_layer_create ( VikViewport *vp );
685static void trw_layer_realize ( VikTrwLayer *vtl, VikTreeview *vt, GtkTreeIter *layer_iter );
20981fd6 686static void trw_layer_post_read ( VikTrwLayer *vtl, GtkWidget *vvp, gboolean from_file );
a7cd93ac
RN
687static void trw_layer_free ( VikTrwLayer *trwlayer );
688static void trw_layer_draw ( VikTrwLayer *l, gpointer data );
689static void trw_layer_change_coord_mode ( VikTrwLayer *vtl, VikCoordMode dest_mode );
690static void trw_layer_set_menu_selection ( VikTrwLayer *vtl, guint16 );
691static guint16 trw_layer_get_menu_selection ( VikTrwLayer *vtl );
692static void trw_layer_add_menu_items ( VikTrwLayer *vtl, GtkMenu *menu, gpointer vlp );
693static gboolean trw_layer_sublayer_add_menu_items ( VikTrwLayer *l, GtkMenu *menu, gpointer vlp, gint subtype, gpointer sublayer, GtkTreeIter *iter, VikViewport *vvp );
694static const gchar* trw_layer_sublayer_rename_request ( VikTrwLayer *l, const gchar *newname, gpointer vlp, gint subtype, gpointer sublayer, GtkTreeIter *iter );
695static gboolean trw_layer_sublayer_toggle_visible ( VikTrwLayer *l, gint subtype, gpointer sublayer );
696static const gchar* trw_layer_layer_tooltip ( VikTrwLayer *vtl );
697static const gchar* trw_layer_sublayer_tooltip ( VikTrwLayer *l, gint subtype, gpointer sublayer );
698static gboolean trw_layer_selected ( VikTrwLayer *l, gint subtype, gpointer sublayer, gint type, gpointer vlp );
699static void trw_layer_marshall ( VikTrwLayer *vtl, guint8 **data, gint *len );
700static VikTrwLayer *trw_layer_unmarshall ( guint8 *data, gint len, VikViewport *vvp );
701static gboolean trw_layer_set_param ( VikTrwLayer *vtl, guint16 id, VikLayerParamData data, VikViewport *vp, gboolean is_file_operation );
702static VikLayerParamData trw_layer_get_param ( VikTrwLayer *vtl, guint16 id, gboolean is_file_operation );
db43cfa4 703static void trw_layer_change_param ( GtkWidget *widget, ui_change_values values );
a7cd93ac
RN
704static void trw_layer_del_item ( VikTrwLayer *vtl, gint subtype, gpointer sublayer );
705static void trw_layer_cut_item ( VikTrwLayer *vtl, gint subtype, gpointer sublayer );
706static void trw_layer_copy_item ( VikTrwLayer *vtl, gint subtype, gpointer sublayer, guint8 **item, guint *len );
707static gboolean trw_layer_paste_item ( VikTrwLayer *vtl, gint subtype, guint8 *item, guint len );
708static void trw_layer_free_copied_item ( gint subtype, gpointer item );
709static void trw_layer_drag_drop_request ( VikTrwLayer *vtl_src, VikTrwLayer *vtl_dest, GtkTreeIter *src_item_iter, GtkTreePath *dest_path );
710static gboolean trw_layer_select_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp, tool_ed_t *t );
711static gboolean trw_layer_select_move ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp, tool_ed_t *t );
712static gboolean trw_layer_select_release ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp, tool_ed_t *t );
713static gboolean trw_layer_show_selected_viewport_menu ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp );
714/* End Layer Interface function definitions */
715
50a14534 716VikLayerInterface vik_trw_layer_interface = {
db386630 717 "TrackWaypoint",
affcc0f2 718 N_("TrackWaypoint"),
75078768 719 "<control><shift>Y",
5bfafde9 720 &viktrwlayer_pixbuf,
50a14534
EB
721
722 trw_layer_tools,
723 sizeof(trw_layer_tools) / sizeof(VikToolInterface),
724
725 trw_layer_params,
726 NUM_PARAMS,
727 params_groups, /* params_groups */
728 sizeof(params_groups)/sizeof(params_groups[0]), /* number of groups */
729
5a4a28bf
QT
730 VIK_MENU_ITEM_ALL,
731
a7cd93ac
RN
732 (VikLayerFuncCreate) trw_layer_create,
733 (VikLayerFuncRealize) trw_layer_realize,
b1453c16 734 (VikLayerFuncPostRead) trw_layer_post_read,
a7cd93ac 735 (VikLayerFuncFree) trw_layer_free,
50a14534
EB
736
737 (VikLayerFuncProperties) NULL,
a7cd93ac 738 (VikLayerFuncDraw) trw_layer_draw,
50a14534
EB
739 (VikLayerFuncChangeCoordMode) trw_layer_change_coord_mode,
740
a7cd93ac
RN
741 (VikLayerFuncSetMenuItemsSelection) trw_layer_set_menu_selection,
742 (VikLayerFuncGetMenuItemsSelection) trw_layer_get_menu_selection,
20c7a3a0 743
a7cd93ac
RN
744 (VikLayerFuncAddMenuItems) trw_layer_add_menu_items,
745 (VikLayerFuncSublayerAddMenuItems) trw_layer_sublayer_add_menu_items,
50a14534 746
a7cd93ac
RN
747 (VikLayerFuncSublayerRenameRequest) trw_layer_sublayer_rename_request,
748 (VikLayerFuncSublayerToggleVisible) trw_layer_sublayer_toggle_visible,
c7060c4e 749 (VikLayerFuncSublayerTooltip) trw_layer_sublayer_tooltip,
cb89c5a5 750 (VikLayerFuncLayerTooltip) trw_layer_layer_tooltip,
a7cd93ac 751 (VikLayerFuncLayerSelected) trw_layer_selected,
50a14534 752
911400b5
AF
753 (VikLayerFuncMarshall) trw_layer_marshall,
754 (VikLayerFuncUnmarshall) trw_layer_unmarshall,
50a14534
EB
755
756 (VikLayerFuncSetParam) trw_layer_set_param,
757 (VikLayerFuncGetParam) trw_layer_get_param,
db43cfa4 758 (VikLayerFuncChangeParam) trw_layer_change_param,
50a14534
EB
759
760 (VikLayerFuncReadFileData) a_gpspoint_read_file,
761 (VikLayerFuncWriteFileData) a_gpspoint_write_file,
762
33534cd8 763 (VikLayerFuncDeleteItem) trw_layer_del_item,
d5874ef9 764 (VikLayerFuncCutItem) trw_layer_cut_item,
50a14534
EB
765 (VikLayerFuncCopyItem) trw_layer_copy_item,
766 (VikLayerFuncPasteItem) trw_layer_paste_item,
767 (VikLayerFuncFreeCopiedItem) trw_layer_free_copied_item,
70a23263
AF
768
769 (VikLayerFuncDragDropRequest) trw_layer_drag_drop_request,
77ad64fa
RN
770
771 (VikLayerFuncSelectClick) trw_layer_select_click,
08f14055
RN
772 (VikLayerFuncSelectMove) trw_layer_select_move,
773 (VikLayerFuncSelectRelease) trw_layer_select_release,
e46f259a 774 (VikLayerFuncSelectedViewportMenu) trw_layer_show_selected_viewport_menu,
50a14534
EB
775};
776
be3b5aa8
RN
777static gboolean have_diary_program = FALSE;
778
779// NB Only performed once per program run
780static void vik_trwlayer_class_init ( VikTrwLayerClass *klass )
781{
782 if ( g_find_program_in_path( "rednotebook" ) ) {
783 gchar *stdout = NULL;
784 gchar *stderr = NULL;
785 // Needs RedNotebook 1.7.3+ for support of opening on a specified date
786 if ( g_spawn_command_line_sync ( "rednotebook --version", &stdout, &stderr, NULL, NULL ) ) {
787 // Annoyingly 1.7.1|2|3 versions of RedNotebook prints the version to stderr!!
788 if ( stdout )
789 g_debug ("Diary: %s", stdout ); // Should be something like 'RedNotebook 1.4'
790 if ( stderr )
791 g_warning ("Diary: stderr: %s", stderr );
792
793 gchar **tokens = NULL;
794 if ( stdout && g_strcmp0(stdout, "") )
795 tokens = g_strsplit(stdout, " ", 0);
796 else if ( stderr )
797 tokens = g_strsplit(stderr, " ", 0);
798
799 gint num = 0;
800 gchar *token = tokens[num];
801 while ( token && num < 2 ) {
802 if (num == 1) {
803 if ( viking_version_to_number(token) >= viking_version_to_number("1.7.3") )
804 have_diary_program = TRUE;
805 }
806 num++;
807 token = tokens[num];
808 }
809 g_strfreev ( tokens );
810 }
811 g_free ( stdout );
812 g_free ( stderr );
813 }
814}
815
50a14534
EB
816GType vik_trw_layer_get_type ()
817{
818 static GType vtl_type = 0;
819
820 if (!vtl_type)
821 {
822 static const GTypeInfo vtl_info =
823 {
824 sizeof (VikTrwLayerClass),
825 NULL, /* base_init */
826 NULL, /* base_finalize */
be3b5aa8 827 (GClassInitFunc) vik_trwlayer_class_init, /* class init */
50a14534
EB
828 NULL, /* class_finalize */
829 NULL, /* class_data */
830 sizeof (VikTrwLayer),
831 0,
832 NULL /* instance init */
833 };
834 vtl_type = g_type_register_static ( VIK_LAYER_TYPE, "VikTrwLayer", &vtl_info, 0 );
835 }
50a14534
EB
836 return vtl_type;
837}
838
6ba42f1e
RN
839VikTRWMetadata *vik_trw_metadata_new()
840{
841 return (VikTRWMetadata*)g_malloc0(sizeof(VikTRWMetadata));
842}
843
844void vik_trw_metadata_free ( VikTRWMetadata *metadata)
845{
846 g_free (metadata);
847}
848
849VikTRWMetadata *vik_trw_layer_get_metadata ( VikTrwLayer *vtl )
850{
851 return vtl->metadata;
852}
853
854void vik_trw_layer_set_metadata ( VikTrwLayer *vtl, VikTRWMetadata *metadata)
855{
856 if ( vtl->metadata )
857 vik_trw_metadata_free ( vtl->metadata );
858 vtl->metadata = metadata;
859}
860
a77c32c8
RN
861typedef struct {
862 gboolean found;
863 const gchar *date_str;
864 const VikTrack *trk;
865 const VikWaypoint *wpt;
866 gpointer *trk_id;
867 gpointer *wpt_id;
868} date_finder_type;
869
870static gboolean trw_layer_find_date_track ( const gpointer id, const VikTrack *trk, date_finder_type *df )
871{
872 gchar date_buf[20];
873 date_buf[0] = '\0';
874 // Might be an easier way to compare dates rather than converting the strings all the time...
875 if ( trk->trackpoints && VIK_TRACKPOINT(trk->trackpoints->data)->has_timestamp ) {
876 strftime (date_buf, sizeof(date_buf), "%Y-%m-%d", gmtime(&(VIK_TRACKPOINT(trk->trackpoints->data)->timestamp)));
877
878 if ( ! g_strcmp0 ( df->date_str, date_buf ) ) {
879 df->found = TRUE;
880 df->trk = trk;
881 df->trk_id = id;
882 }
883 }
884 return df->found;
885}
886
887static gboolean trw_layer_find_date_waypoint ( const gpointer id, const VikWaypoint *wpt, date_finder_type *df )
888{
889 gchar date_buf[20];
890 date_buf[0] = '\0';
891 // Might be an easier way to compare dates rather than converting the strings all the time...
892 if ( wpt->has_timestamp ) {
893 strftime (date_buf, sizeof(date_buf), "%Y-%m-%d", gmtime(&(wpt->timestamp)));
894
895 if ( ! g_strcmp0 ( df->date_str, date_buf ) ) {
896 df->found = TRUE;
897 df->wpt = wpt;
898 df->wpt_id = id;
899 }
900 }
901 return df->found;
902}
903
904/**
905 * Find an item by date
906 */
907gboolean vik_trw_layer_find_date ( VikTrwLayer *vtl, const gchar *date_str, VikCoord *position, VikViewport *vvp, gboolean do_tracks, gboolean select )
908{
909 date_finder_type df;
910 df.found = FALSE;
911 df.date_str = date_str;
912 df.trk = NULL;
913 df.wpt = NULL;
914 // Only tracks ATM
915 if ( do_tracks )
916 g_hash_table_find ( vtl->tracks, (GHRFunc) trw_layer_find_date_track, &df );
917 else
918 g_hash_table_find ( vtl->waypoints, (GHRFunc) trw_layer_find_date_waypoint, &df );
919
920 if ( select && df.found ) {
921 if ( do_tracks && df.trk ) {
922 struct LatLon maxmin[2] = { {0,0}, {0,0} };
923 trw_layer_find_maxmin_tracks ( NULL, df.trk, maxmin );
924 trw_layer_zoom_to_show_latlons ( vtl, vvp, maxmin );
925 vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, g_hash_table_lookup (vtl->tracks_iters, df.trk_id), TRUE );
926 }
927 else if ( df.wpt ) {
be5554c5 928 vik_viewport_set_center_coord ( vvp, &(df.wpt->coord), TRUE );
a77c32c8
RN
929 vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, g_hash_table_lookup (vtl->waypoints_iters, df.wpt_id), TRUE );
930 }
931 vik_layer_emit_update ( VIK_LAYER(vtl) );
932 }
933 return df.found;
934}
935
33534cd8
AF
936static void trw_layer_del_item ( VikTrwLayer *vtl, gint subtype, gpointer sublayer )
937{
19782ffd 938 static menu_array_sublayer values;
33534cd8
AF
939 if (!sublayer) {
940 return;
941 }
33534cd8 942
19782ffd
RN
943 gint ii;
944 for ( ii = MA_VTL; ii < MA_LAST; ii++ )
945 values[ii] = NULL;
946
947 values[MA_VTL] = vtl;
948 values[MA_SUBTYPE] = GINT_TO_POINTER (subtype);
949 values[MA_SUBLAYER_ID] = sublayer;
950 values[MA_CONFIRM] = GINT_TO_POINTER (1); // Confirm delete request
951
952 trw_layer_delete_item ( values );
33534cd8 953}
50a14534 954
d5874ef9
RN
955static void trw_layer_cut_item ( VikTrwLayer *vtl, gint subtype, gpointer sublayer )
956{
19782ffd 957 static menu_array_sublayer values;
d5874ef9
RN
958 if (!sublayer) {
959 return;
960 }
961
19782ffd
RN
962 gint ii;
963 for ( ii = MA_VTL; ii < MA_LAST; ii++ )
964 values[ii] = NULL;
965
966 values[MA_VTL] = vtl;
967 values[MA_SUBTYPE] = GINT_TO_POINTER (subtype);
968 values[MA_SUBLAYER_ID] = sublayer;
969 values[MA_CONFIRM] = GINT_TO_POINTER (1); // Confirm delete request
d5874ef9 970
19782ffd
RN
971 trw_layer_copy_item_cb(values);
972 trw_layer_cut_item_cb(values);
d5874ef9
RN
973}
974
19782ffd 975static void trw_layer_copy_item_cb ( menu_array_sublayer values)
2cebc318 976{
19782ffd
RN
977 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
978 gint subtype = GPOINTER_TO_INT (values[MA_SUBTYPE]);
979 gpointer * sublayer = values[MA_SUBLAYER_ID];
2cebc318
QT
980 guint8 *data = NULL;
981 guint len;
982
983 trw_layer_copy_item( vtl, subtype, sublayer, &data, &len);
984
985 if (data) {
c9570f86
RN
986 const gchar* name;
987 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT ) {
988 VikWaypoint *wp = g_hash_table_lookup ( vtl->waypoints, sublayer);
989 if ( wp && wp->name )
990 name = wp->name;
991 else
992 name = NULL; // Broken :(
993 }
0d2b891f 994 else if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK ) {
ce4bd1cf
RN
995 VikTrack *trk = g_hash_table_lookup ( vtl->tracks, sublayer);
996 if ( trk && trk->name )
997 name = trk->name;
998 else
999 name = NULL; // Broken :(
1000 }
0d2b891f
RN
1001 else {
1002 VikTrack *trk = g_hash_table_lookup ( vtl->routes, sublayer);
1003 if ( trk && trk->name )
1004 name = trk->name;
1005 else
1006 name = NULL; // Broken :(
1007 }
c9570f86 1008
2cebc318 1009 a_clipboard_copy( VIK_CLIPBOARD_DATA_SUBLAYER, VIK_LAYER_TRW,
c9570f86 1010 subtype, len, name, data);
2cebc318
QT
1011 }
1012}
1013
19782ffd 1014static void trw_layer_cut_item_cb ( menu_array_sublayer values)
2cebc318 1015{
19782ffd
RN
1016 trw_layer_copy_item_cb(values);
1017 values[MA_CONFIRM] = GINT_TO_POINTER (0); // Never need to confirm automatic delete
1018 trw_layer_delete_item(values);
2cebc318
QT
1019}
1020
19782ffd 1021static void trw_layer_paste_item_cb ( menu_array_sublayer values)
0d80642e
RN
1022{
1023 // Slightly cheating method, routing via the panels capability
19782ffd 1024 a_clipboard_paste (VIK_LAYERS_PANEL(values[MA_VLP]));
0d80642e
RN
1025}
1026
ddc47a46 1027static void trw_layer_copy_item ( VikTrwLayer *vtl, gint subtype, gpointer sublayer, guint8 **item, guint *len )
50a14534 1028{
ddc47a46
AF
1029 guint8 *id;
1030 guint il;
1031
1032 if (!sublayer) {
1033 *item = NULL;
1034 return;
50a14534 1035 }
ddc47a46 1036
a50640dd
RN
1037 GByteArray *ba = g_byte_array_new ();
1038
1039 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT ) {
ddc47a46 1040 vik_waypoint_marshall ( g_hash_table_lookup ( vtl->waypoints, sublayer ), &id, &il );
0d2b891f 1041 } else if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK ) {
ddc47a46 1042 vik_track_marshall ( g_hash_table_lookup ( vtl->tracks, sublayer ), &id, &il );
0d2b891f
RN
1043 } else {
1044 vik_track_marshall ( g_hash_table_lookup ( vtl->routes, sublayer ), &id, &il );
50a14534 1045 }
ddc47a46 1046
a50640dd
RN
1047 g_byte_array_append ( ba, id, il );
1048
ddc47a46 1049 g_free(id);
a50640dd
RN
1050
1051 *len = ba->len;
1052 *item = ba->data;
50a14534
EB
1053}
1054
ddc47a46 1055static gboolean trw_layer_paste_item ( VikTrwLayer *vtl, gint subtype, guint8 *item, guint len )
50a14534 1056{
a50640dd
RN
1057 if ( !item )
1058 return FALSE;
ddc47a46 1059
a50640dd
RN
1060 gchar *name;
1061
1062 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT )
50a14534 1063 {
ddc47a46 1064 VikWaypoint *w;
ddc47a46 1065
a50640dd 1066 w = vik_waypoint_unmarshall ( item, len );
c9570f86 1067 // When copying - we'll create a new name based on the original
9748531a 1068 name = trw_layer_new_unique_sublayer_name(vtl, VIK_TRW_LAYER_SUBLAYER_WAYPOINT, w->name);
ddc47a46 1069 vik_trw_layer_add_waypoint ( vtl, name, w );
c9570f86 1070 waypoint_convert (NULL, w, &vtl->coord_mode);
1613e468 1071 g_free ( name );
c9570f86 1072
aa0665e5
RN
1073 trw_layer_calculate_bounds_waypoints ( vtl );
1074
58cd1c43
RN
1075 // Consider if redraw necessary for the new item
1076 if ( vtl->vl.visible && vtl->waypoints_visible && w->visible )
da121f9b 1077 vik_layer_emit_update ( VIK_LAYER(vtl) );
50a14534
EB
1078 return TRUE;
1079 }
a50640dd 1080 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
50a14534 1081 {
ddc47a46 1082 VikTrack *t;
ce4bd1cf 1083
a50640dd 1084 t = vik_track_unmarshall ( item, len );
ce4bd1cf 1085 // When copying - we'll create a new name based on the original
9748531a 1086 name = trw_layer_new_unique_sublayer_name(vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, t->name);
ddc47a46 1087 vik_trw_layer_add_track ( vtl, name, t );
0d2b891f 1088 vik_track_convert (t, vtl->coord_mode);
1613e468 1089 g_free ( name );
ce4bd1cf 1090
58cd1c43
RN
1091 // Consider if redraw necessary for the new item
1092 if ( vtl->vl.visible && vtl->tracks_visible && t->visible )
da121f9b 1093 vik_layer_emit_update ( VIK_LAYER(vtl) );
50a14534
EB
1094 return TRUE;
1095 }
a50640dd 1096 if ( subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE )
0d2b891f
RN
1097 {
1098 VikTrack *t;
0d2b891f 1099
a50640dd 1100 t = vik_track_unmarshall ( item, len );
0d2b891f
RN
1101 // When copying - we'll create a new name based on the original
1102 name = trw_layer_new_unique_sublayer_name(vtl, VIK_TRW_LAYER_SUBLAYER_ROUTE, t->name);
1103 vik_trw_layer_add_route ( vtl, name, t );
1104 vik_track_convert (t, vtl->coord_mode);
1613e468 1105 g_free ( name );
0d2b891f
RN
1106
1107 // Consider if redraw necessary for the new item
1108 if ( vtl->vl.visible && vtl->routes_visible && t->visible )
da121f9b 1109 vik_layer_emit_update ( VIK_LAYER(vtl) );
0d2b891f
RN
1110 return TRUE;
1111 }
50a14534
EB
1112 return FALSE;
1113}
1114
1115static void trw_layer_free_copied_item ( gint subtype, gpointer item )
1116{
ddc47a46
AF
1117 if (item) {
1118 g_free(item);
50a14534
EB
1119 }
1120}
1121
158b3642 1122static gboolean trw_layer_set_param ( VikTrwLayer *vtl, guint16 id, VikLayerParamData data, VikViewport *vp, gboolean is_file_operation )
50a14534
EB
1123{
1124 switch ( id )
1125 {
1126 case PARAM_TV: vtl->tracks_visible = data.b; break;
1127 case PARAM_WV: vtl->waypoints_visible = data.b; break;
0d2b891f 1128 case PARAM_RV: vtl->routes_visible = data.b; break;
387ff7ac
RN
1129 case PARAM_TDL: vtl->track_draw_labels = data.b; break;
1130 case PARAM_TLFONTSIZE:
1131 if ( data.u < FS_NUM_SIZES ) {
1132 vtl->track_font_size = data.u;
1133 g_free ( vtl->track_fsize_str );
1134 switch ( vtl->track_font_size ) {
1135 case FS_XX_SMALL: vtl->track_fsize_str = g_strdup ( "xx-small" ); break;
1136 case FS_X_SMALL: vtl->track_fsize_str = g_strdup ( "x-small" ); break;
1137 case FS_SMALL: vtl->track_fsize_str = g_strdup ( "small" ); break;
1138 case FS_LARGE: vtl->track_fsize_str = g_strdup ( "large" ); break;
1139 case FS_X_LARGE: vtl->track_fsize_str = g_strdup ( "x-large" ); break;
1140 case FS_XX_LARGE: vtl->track_fsize_str = g_strdup ( "xx-large" ); break;
1141 default: vtl->track_fsize_str = g_strdup ( "medium" ); break;
1142 }
1143 }
1144 break;
50a14534 1145 case PARAM_DM: vtl->drawmode = data.u; break;
074b1067
RN
1146 case PARAM_TC:
1147 vtl->track_color = data.c;
8aff54f2 1148 if ( vp ) trw_layer_new_track_gcs ( vtl, vp );
074b1067 1149 break;
50a14534 1150 case PARAM_DP: vtl->drawpoints = data.b; break;
3a1c8e42
RN
1151 case PARAM_DPS:
1152 if ( data.u >= MIN_POINT_SIZE && data.u <= MAX_POINT_SIZE )
1153 vtl->drawpoints_size = data.u;
1154 break;
b42a25ba
EB
1155 case PARAM_DE: vtl->drawelevation = data.b; break;
1156 case PARAM_DS: vtl->drawstops = data.b; break;
50a14534 1157 case PARAM_DL: vtl->drawlines = data.b; break;
08084371 1158 case PARAM_DD: vtl->drawdirections = data.b; break;
3a1c8e42
RN
1159 case PARAM_DDS:
1160 if ( data.u >= MIN_ARROW_SIZE && data.u <= MAX_ARROW_SIZE )
1161 vtl->drawdirections_size = data.u;
1162 break;
b42a25ba
EB
1163 case PARAM_SL: if ( data.u >= MIN_STOP_LENGTH && data.u <= MAX_STOP_LENGTH )
1164 vtl->stop_length = data.u;
1165 break;
1166 case PARAM_EF: if ( data.u >= 1 && data.u <= 100 )
1167 vtl->elevation_factor = data.u;
1168 break;
50a14534
EB
1169 case PARAM_LT: if ( data.u > 0 && data.u < 15 && data.u != vtl->line_thickness )
1170 {
1171 vtl->line_thickness = data.u;
8aff54f2 1172 if ( vp ) trw_layer_new_track_gcs ( vtl, vp );
50a14534
EB
1173 }
1174 break;
5e610fc3 1175 case PARAM_BLT: if ( data.u <= 8 && data.u != vtl->bg_line_thickness )
50a14534
EB
1176 {
1177 vtl->bg_line_thickness = data.u;
8aff54f2 1178 if ( vp ) trw_layer_new_track_gcs ( vtl, vp );
50a14534
EB
1179 }
1180 break;
a7023a1b
RN
1181 case PARAM_TBGC:
1182 vtl->track_bg_color = data.c;
1183 if ( vtl->track_bg_gc )
1184 gdk_gc_set_rgb_fg_color(vtl->track_bg_gc, &(vtl->track_bg_color));
1185 break;
18df6d42 1186 case PARAM_TDSF: vtl->track_draw_speed_factor = data.d; break;
c9cac058 1187 case PARAM_TSO: if ( data.u < VL_SO_LAST ) vtl->track_sort_order = data.u; break;
50a14534
EB
1188 case PARAM_DLA: vtl->drawlabels = data.b; break;
1189 case PARAM_DI: vtl->drawimages = data.b; break;
1190 case PARAM_IS: if ( data.u != vtl->image_size )
1191 {
1192 vtl->image_size = data.u;
1193 g_list_foreach ( vtl->image_cache->head, (GFunc) cached_pixbuf_free, NULL );
1194 g_queue_free ( vtl->image_cache );
1195 vtl->image_cache = g_queue_new ();
1196 }
1197 break;
1198 case PARAM_IA: vtl->image_alpha = data.u; break;
1199 case PARAM_ICS: vtl->image_cache_size = data.u;
1200 while ( vtl->image_cache->length > vtl->image_cache_size ) /* if shrinking cache_size, free pixbuf ASAP */
1201 cached_pixbuf_free ( g_queue_pop_tail ( vtl->image_cache ) );
1202 break;
a7023a1b
RN
1203 case PARAM_WPC:
1204 vtl->waypoint_color = data.c;
1205 if ( vtl->waypoint_gc )
1206 gdk_gc_set_rgb_fg_color(vtl->waypoint_gc, &(vtl->waypoint_color));
1207 break;
1208 case PARAM_WPTC:
1209 vtl->waypoint_text_color = data.c;
1210 if ( vtl->waypoint_text_gc )
1211 gdk_gc_set_rgb_fg_color(vtl->waypoint_text_gc, &(vtl->waypoint_text_color));
1212 break;
1213 case PARAM_WPBC:
1214 vtl->waypoint_bg_color = data.c;
1215 if ( vtl->waypoint_bg_gc )
1216 gdk_gc_set_rgb_fg_color(vtl->waypoint_bg_gc, &(vtl->waypoint_bg_color));
1217 break;
1218 case PARAM_WPBA:
1219 vtl->wpbgand = data.b;
1220 if ( vtl->waypoint_bg_gc )
1221 gdk_gc_set_function(vtl->waypoint_bg_gc, data.b ? GDK_AND : GDK_COPY );
1222 break;
50a14534
EB
1223 case PARAM_WPSYM: if ( data.u < WP_NUM_SYMBOLS ) vtl->wp_symbol = data.u; break;
1224 case PARAM_WPSIZE: if ( data.u > 0 && data.u <= 64 ) vtl->wp_size = data.u; break;
ea3933fc 1225 case PARAM_WPSYMS: vtl->wp_draw_symbols = data.b; break;
63ffa244
RN
1226 case PARAM_WPFONTSIZE:
1227 if ( data.u < FS_NUM_SIZES ) {
1228 vtl->wp_font_size = data.u;
1229 g_free ( vtl->wp_fsize_str );
1230 switch ( vtl->wp_font_size ) {
1231 case FS_XX_SMALL: vtl->wp_fsize_str = g_strdup ( "xx-small" ); break;
1232 case FS_X_SMALL: vtl->wp_fsize_str = g_strdup ( "x-small" ); break;
1233 case FS_SMALL: vtl->wp_fsize_str = g_strdup ( "small" ); break;
1234 case FS_LARGE: vtl->wp_fsize_str = g_strdup ( "large" ); break;
1235 case FS_X_LARGE: vtl->wp_fsize_str = g_strdup ( "x-large" ); break;
1236 case FS_XX_LARGE: vtl->wp_fsize_str = g_strdup ( "xx-large" ); break;
1237 default: vtl->wp_fsize_str = g_strdup ( "medium" ); break;
1238 }
1239 }
1240 break;
c9cac058 1241 case PARAM_WPSO: if ( data.u < VL_SO_LAST ) vtl->wp_sort_order = data.u; break;
6ba42f1e
RN
1242 // Metadata
1243 case PARAM_MDDESC: if ( data.s && vtl->metadata ) vtl->metadata->description = g_strdup (data.s); break;
1244 case PARAM_MDAUTH: if ( data.s && vtl->metadata ) vtl->metadata->author = g_strdup (data.s); break;
1245 case PARAM_MDTIME: if ( data.s && vtl->metadata ) vtl->metadata->timestamp = g_strdup (data.s); break;
1246 case PARAM_MDKEYS: if ( data.s && vtl->metadata ) vtl->metadata->keywords = g_strdup (data.s); break;
1247 default: break;
50a14534
EB
1248 }
1249 return TRUE;
1250}
1251
158b3642 1252static VikLayerParamData trw_layer_get_param ( VikTrwLayer *vtl, guint16 id, gboolean is_file_operation )
50a14534
EB
1253{
1254 VikLayerParamData rv;
1255 switch ( id )
1256 {
1257 case PARAM_TV: rv.b = vtl->tracks_visible; break;
1258 case PARAM_WV: rv.b = vtl->waypoints_visible; break;
0d2b891f 1259 case PARAM_RV: rv.b = vtl->routes_visible; break;
387ff7ac
RN
1260 case PARAM_TDL: rv.b = vtl->track_draw_labels; break;
1261 case PARAM_TLFONTSIZE: rv.u = vtl->track_font_size; break;
50a14534 1262 case PARAM_DM: rv.u = vtl->drawmode; break;
074b1067 1263 case PARAM_TC: rv.c = vtl->track_color; break;
50a14534 1264 case PARAM_DP: rv.b = vtl->drawpoints; break;
3a1c8e42 1265 case PARAM_DPS: rv.u = vtl->drawpoints_size; break;
b42a25ba
EB
1266 case PARAM_DE: rv.b = vtl->drawelevation; break;
1267 case PARAM_EF: rv.u = vtl->elevation_factor; break;
1268 case PARAM_DS: rv.b = vtl->drawstops; break;
1269 case PARAM_SL: rv.u = vtl->stop_length; break;
50a14534 1270 case PARAM_DL: rv.b = vtl->drawlines; break;
08084371 1271 case PARAM_DD: rv.b = vtl->drawdirections; break;
3a1c8e42 1272 case PARAM_DDS: rv.u = vtl->drawdirections_size; break;
50a14534
EB
1273 case PARAM_LT: rv.u = vtl->line_thickness; break;
1274 case PARAM_BLT: rv.u = vtl->bg_line_thickness; break;
50a14534
EB
1275 case PARAM_DLA: rv.b = vtl->drawlabels; break;
1276 case PARAM_DI: rv.b = vtl->drawimages; break;
a7023a1b 1277 case PARAM_TBGC: rv.c = vtl->track_bg_color; break;
18df6d42 1278 case PARAM_TDSF: rv.d = vtl->track_draw_speed_factor; break;
c9cac058 1279 case PARAM_TSO: rv.u = vtl->track_sort_order; break;
50a14534
EB
1280 case PARAM_IS: rv.u = vtl->image_size; break;
1281 case PARAM_IA: rv.u = vtl->image_alpha; break;
1282 case PARAM_ICS: rv.u = vtl->image_cache_size; break;
a7023a1b
RN
1283 case PARAM_WPC: rv.c = vtl->waypoint_color; break;
1284 case PARAM_WPTC: rv.c = vtl->waypoint_text_color; break;
1285 case PARAM_WPBC: rv.c = vtl->waypoint_bg_color; break;
1286 case PARAM_WPBA: rv.b = vtl->wpbgand; break;
50a14534
EB
1287 case PARAM_WPSYM: rv.u = vtl->wp_symbol; break;
1288 case PARAM_WPSIZE: rv.u = vtl->wp_size; break;
ea3933fc 1289 case PARAM_WPSYMS: rv.b = vtl->wp_draw_symbols; break;
5a77ae71 1290 case PARAM_WPFONTSIZE: rv.u = vtl->wp_font_size; break;
c9cac058 1291 case PARAM_WPSO: rv.u = vtl->wp_sort_order; break;
6ba42f1e
RN
1292 // Metadata
1293 case PARAM_MDDESC: if (vtl->metadata) { rv.s = vtl->metadata->description; } break;
1294 case PARAM_MDAUTH: if (vtl->metadata) { rv.s = vtl->metadata->author; } break;
1295 case PARAM_MDTIME: if (vtl->metadata) { rv.s = vtl->metadata->timestamp; } break;
1296 case PARAM_MDKEYS: if (vtl->metadata) { rv.s = vtl->metadata->keywords; } break;
1297 default: break;
50a14534
EB
1298 }
1299 return rv;
1300}
1301
db43cfa4
RN
1302static void trw_layer_change_param ( GtkWidget *widget, ui_change_values values )
1303{
1304 // This '-3' is to account for the first few parameters not in the properties
1305 const gint OFFSET = -3;
1306
1307 switch ( GPOINTER_TO_INT(values[UI_CHG_PARAM_ID]) ) {
1308 // Alter sensitivity of waypoint draw image related widgets according to the draw image setting.
1309 case PARAM_DI: {
1310 // Get new value
1311 VikLayerParamData vlpd = a_uibuilder_widget_get_value ( widget, values[UI_CHG_PARAM] );
1312 GtkWidget **ww1 = values[UI_CHG_WIDGETS];
1313 GtkWidget **ww2 = values[UI_CHG_LABELS];
1314 GtkWidget *w1 = ww1[OFFSET + PARAM_IS];
1315 GtkWidget *w2 = ww2[OFFSET + PARAM_IS];
1316 GtkWidget *w3 = ww1[OFFSET + PARAM_IA];
1317 GtkWidget *w4 = ww2[OFFSET + PARAM_IA];
1318 GtkWidget *w5 = ww1[OFFSET + PARAM_ICS];
1319 GtkWidget *w6 = ww2[OFFSET + PARAM_ICS];
1320 if ( w1 ) gtk_widget_set_sensitive ( w1, vlpd.b );
1321 if ( w2 ) gtk_widget_set_sensitive ( w2, vlpd.b );
1322 if ( w3 ) gtk_widget_set_sensitive ( w3, vlpd.b );
1323 if ( w4 ) gtk_widget_set_sensitive ( w4, vlpd.b );
1324 if ( w5 ) gtk_widget_set_sensitive ( w5, vlpd.b );
1325 if ( w6 ) gtk_widget_set_sensitive ( w6, vlpd.b );
1326 break;
1327 }
1328 // Alter sensitivity of waypoint label related widgets according to the draw label setting.
1329 case PARAM_DLA: {
1330 // Get new value
1331 VikLayerParamData vlpd = a_uibuilder_widget_get_value ( widget, values[UI_CHG_PARAM] );
1332 GtkWidget **ww1 = values[UI_CHG_WIDGETS];
1333 GtkWidget **ww2 = values[UI_CHG_LABELS];
1334 GtkWidget *w1 = ww1[OFFSET + PARAM_WPTC];
1335 GtkWidget *w2 = ww2[OFFSET + PARAM_WPTC];
1336 GtkWidget *w3 = ww1[OFFSET + PARAM_WPBC];
1337 GtkWidget *w4 = ww2[OFFSET + PARAM_WPBC];
1338 GtkWidget *w5 = ww1[OFFSET + PARAM_WPBA];
1339 GtkWidget *w6 = ww2[OFFSET + PARAM_WPBA];
1340 GtkWidget *w7 = ww1[OFFSET + PARAM_WPFONTSIZE];
1341 GtkWidget *w8 = ww2[OFFSET + PARAM_WPFONTSIZE];
1342 if ( w1 ) gtk_widget_set_sensitive ( w1, vlpd.b );
1343 if ( w2 ) gtk_widget_set_sensitive ( w2, vlpd.b );
1344 if ( w3 ) gtk_widget_set_sensitive ( w3, vlpd.b );
1345 if ( w4 ) gtk_widget_set_sensitive ( w4, vlpd.b );
1346 if ( w5 ) gtk_widget_set_sensitive ( w5, vlpd.b );
1347 if ( w6 ) gtk_widget_set_sensitive ( w6, vlpd.b );
1348 if ( w7 ) gtk_widget_set_sensitive ( w7, vlpd.b );
1349 if ( w8 ) gtk_widget_set_sensitive ( w8, vlpd.b );
1350 break;
1351 }
1352 // Alter sensitivity of all track colours according to the draw track mode.
1353 case PARAM_DM: {
1354 // Get new value
1355 VikLayerParamData vlpd = a_uibuilder_widget_get_value ( widget, values[UI_CHG_PARAM] );
1356 gboolean sensitive = ( vlpd.u == DRAWMODE_ALL_SAME_COLOR );
1357 GtkWidget **ww1 = values[UI_CHG_WIDGETS];
1358 GtkWidget **ww2 = values[UI_CHG_LABELS];
1359 GtkWidget *w1 = ww1[OFFSET + PARAM_TC];
1360 GtkWidget *w2 = ww2[OFFSET + PARAM_TC];
1361 if ( w1 ) gtk_widget_set_sensitive ( w1, sensitive );
1362 if ( w2 ) gtk_widget_set_sensitive ( w2, sensitive );
1363 break;
1364 }
6ba42f1e
RN
1365 case PARAM_MDTIME: {
1366 // Force metadata->timestamp to be always read-only for now.
1367 GtkWidget **ww = values[UI_CHG_WIDGETS];
1368 GtkWidget *w1 = ww[OFFSET + PARAM_MDTIME];
1369 if ( w1 ) gtk_widget_set_sensitive ( w1, FALSE );
1370 }
db43cfa4
RN
1371 // NB Since other track settings have been split across tabs,
1372 // I don't think it's useful to set sensitivities on widgets you can't immediately see
1373 default: break;
1374 }
1375}
1376
911400b5
AF
1377static void trw_layer_marshall( VikTrwLayer *vtl, guint8 **data, gint *len )
1378{
4efcd55c 1379 guint8 *pd;
4efcd55c 1380 gint pl;
911400b5
AF
1381
1382 *data = NULL;
1383
7b956cdd
RN
1384 // Use byte arrays to store sublayer data
1385 // much like done elsewhere e.g. vik_layer_marshall_params()
1386 GByteArray *ba = g_byte_array_new ( );
1387
1388 guint8 *sl_data;
1389 guint sl_len;
1390
1391 guint object_length;
1392 guint subtype;
1393 // store:
1394 // the length of the item
1395 // the sublayer type of item
1396 // the the actual item
1397#define tlm_append(object_pointer, size, type) \
1398 subtype = (type); \
1399 object_length = (size); \
1400 g_byte_array_append ( ba, (guint8 *)&object_length, sizeof(object_length) ); \
1401 g_byte_array_append ( ba, (guint8 *)&subtype, sizeof(subtype) ); \
1402 g_byte_array_append ( ba, (object_pointer), object_length );
1403
1404 // Layer parameters first
1405 vik_layer_marshall_params(VIK_LAYER(vtl), &pd, &pl);
1406 g_byte_array_append ( ba, (guint8 *)&pl, sizeof(pl) ); \
1407 g_byte_array_append ( ba, pd, pl );
1408 g_free ( pd );
1409
1410 // Now sublayer data
1411 GHashTableIter iter;
1412 gpointer key, value;
1413
1414 // Waypoints
1415 g_hash_table_iter_init ( &iter, vtl->waypoints );
1416 while ( g_hash_table_iter_next (&iter, &key, &value) ) {
1417 vik_waypoint_marshall ( VIK_WAYPOINT(value), &sl_data, &sl_len );
1418 tlm_append ( sl_data, sl_len, VIK_TRW_LAYER_SUBLAYER_WAYPOINT );
1419 g_free ( sl_data );
1420 }
1421
1422 // Tracks
1423 g_hash_table_iter_init ( &iter, vtl->tracks );
1424 while ( g_hash_table_iter_next (&iter, &key, &value) ) {
1425 vik_track_marshall ( VIK_TRACK(value), &sl_data, &sl_len );
1426 tlm_append ( sl_data, sl_len, VIK_TRW_LAYER_SUBLAYER_TRACK );
1427 g_free ( sl_data );
911400b5 1428 }
7b956cdd
RN
1429
1430 // Routes
1431 g_hash_table_iter_init ( &iter, vtl->routes );
1432 while ( g_hash_table_iter_next (&iter, &key, &value) ) {
1433 vik_track_marshall ( VIK_TRACK(value), &sl_data, &sl_len );
1434 tlm_append ( sl_data, sl_len, VIK_TRW_LAYER_SUBLAYER_ROUTE );
1435 g_free ( sl_data );
1436 }
1437
1438#undef tlm_append
1439
1440 *data = ba->data;
1441 *len = ba->len;
911400b5
AF
1442}
1443
28612684 1444static VikTrwLayer *trw_layer_unmarshall( guint8 *data, gint len, VikViewport *vvp )
911400b5 1445{
0ab35525 1446 VikTrwLayer *vtl = VIK_TRW_LAYER(vik_layer_create ( VIK_LAYER_TRW, vvp, FALSE ));
28612684 1447 gint pl;
7b956cdd 1448 gint consumed_length;
911400b5 1449
7b956cdd 1450 // First the overall layer parameters
911400b5
AF
1451 memcpy(&pl, data, sizeof(pl));
1452 data += sizeof(pl);
7b956cdd 1453 vik_layer_unmarshall_params ( VIK_LAYER(vtl), data, pl, vvp );
911400b5
AF
1454 data += pl;
1455
7b956cdd
RN
1456 consumed_length = pl;
1457 const gint sizeof_len_and_subtype = sizeof(gint) + sizeof(gint);
1458
1459#define tlm_size (*(gint *)data)
1460 // See marshalling above for order of how this is written
1461#define tlm_next \
1462 data += sizeof_len_and_subtype + tlm_size;
1463
1464 // Now the individual sublayers:
1465
1466 while ( *data && consumed_length < len ) {
1467 // Normally four extra bytes at the end of the datastream
1468 // (since it's a GByteArray and that's where it's length is stored)
1469 // So only attempt read when there's an actual block of sublayer data
1470 if ( consumed_length + tlm_size < len ) {
1471
1472 // Reuse pl to read the subtype from the data stream
1473 memcpy(&pl, data+sizeof(gint), sizeof(pl));
1474
1475 if ( pl == VIK_TRW_LAYER_SUBLAYER_TRACK ) {
1476 VikTrack *trk = vik_track_unmarshall ( data + sizeof_len_and_subtype, 0 );
1477 gchar *name = g_strdup ( trk->name );
1478 vik_trw_layer_add_track ( vtl, name, trk );
1479 g_free ( name );
1480 }
1481 if ( pl == VIK_TRW_LAYER_SUBLAYER_WAYPOINT ) {
1482 VikWaypoint *wp = vik_waypoint_unmarshall ( data + sizeof_len_and_subtype, 0 );
1483 gchar *name = g_strdup ( wp->name );
1484 vik_trw_layer_add_waypoint ( vtl, name, wp );
1485 g_free ( name );
1486 }
1487 if ( pl == VIK_TRW_LAYER_SUBLAYER_ROUTE ) {
1488 VikTrack *trk = vik_track_unmarshall ( data + sizeof_len_and_subtype, 0 );
1489 gchar *name = g_strdup ( trk->name );
1490 vik_trw_layer_add_route ( vtl, name, trk );
1491 g_free ( name );
1492 }
1493 }
1494 consumed_length += tlm_size + sizeof_len_and_subtype;
1495 tlm_next;
911400b5 1496 }
7b956cdd
RN
1497 //g_debug ("consumed_length %d vs len %d", consumed_length, len);
1498
aa0665e5
RN
1499 // Not stored anywhere else so need to regenerate
1500 trw_layer_calculate_bounds_waypoints ( vtl );
1501
7b956cdd 1502 return vtl;
911400b5
AF
1503}
1504
c9570f86
RN
1505// Keep interesting hash function at least visible
1506/*
8499a412
QT
1507static guint strcase_hash(gconstpointer v)
1508{
c9570f86 1509 // 31 bit hash function
8499a412
QT
1510 int i;
1511 const gchar *t = v;
c9570f86 1512 gchar s[128]; // malloc is too slow for reading big files
8499a412
QT
1513 gchar *p = s;
1514
1515 for (i = 0; (i < (sizeof(s)- 1)) && t[i]; i++)
1516 p[i] = toupper(t[i]);
1517 p[i] = '\0';
1518
1519 p = s;
1520 guint32 h = *p;
1521 if (h) {
1522 for (p += 1; *p != '\0'; p++)
1523 h = (h << 5) - h + *p;
1524 }
1525
1526 return h;
1527}
c9570f86 1528*/
8499a412 1529
a7023a1b
RN
1530// Stick a 1 at the end of the function name to make it more unique
1531// thus more easily searchable in a simple text editor
1532static VikTrwLayer* trw_layer_new1 ( VikViewport *vvp )
50a14534
EB
1533{
1534 VikTrwLayer *rv = VIK_TRW_LAYER ( g_object_new ( VIK_TRW_LAYER_TYPE, NULL ) );
a0c65899 1535 vik_layer_set_type ( VIK_LAYER(rv), VIK_LAYER_TRW );
50a14534 1536
c9570f86
RN
1537 // It's not entirely clear the benefits of hash tables usage here - possibly the simplicity of first implementation for unique names
1538 // Now with the name of the item stored as part of the item - these tables are effectively straightforward lists
1539
1540 // For this reworking I've choosen to keep the use of hash tables since for the expected data sizes
1541 // - even many hundreds of waypoints and tracks is quite small in the grand scheme of things,
1542 // and with normal PC processing capabilities - it has negligibile performance impact
1543 // This also minimized the amount of rework - as the management of the hash tables already exists.
1544
1545 // The hash tables are indexed by simple integers acting as a UUID hash, which again shouldn't affect performance much
1546 // we have to maintain a uniqueness (as before when multiple names where not allowed),
1547 // this is to ensure it refers to the same item in the data structures used on the viewport and on the layers panel
1548
1549 rv->waypoints = g_hash_table_new_full ( g_direct_hash, g_direct_equal, NULL, (GDestroyNotify) vik_waypoint_free );
1550 rv->waypoints_iters = g_hash_table_new_full ( g_direct_hash, g_direct_equal, NULL, g_free );
ce4bd1cf
RN
1551 rv->tracks = g_hash_table_new_full ( g_direct_hash, g_direct_equal, NULL, (GDestroyNotify) vik_track_free );
1552 rv->tracks_iters = g_hash_table_new_full ( g_direct_hash, g_direct_equal, NULL, g_free );
0d2b891f
RN
1553 rv->routes = g_hash_table_new_full ( g_direct_hash, g_direct_equal, NULL, (GDestroyNotify) vik_track_free );
1554 rv->routes_iters = g_hash_table_new_full ( g_direct_hash, g_direct_equal, NULL, g_free );
50a14534 1555
a7023a1b
RN
1556 rv->image_cache = g_queue_new(); // Must be performed before set_params via set_defaults
1557
1558 vik_layer_set_defaults ( VIK_LAYER(rv), vvp );
1559
1560 // Param settings that are not available via the GUI
1561 // Force to on after processing params (which defaults them to off with a zero value)
0d2b891f 1562 rv->waypoints_visible = rv->tracks_visible = rv->routes_visible = TRUE;
1eef1bde 1563
6ba42f1e 1564 rv->metadata = vik_trw_metadata_new ();
745fda83 1565 rv->draw_sync_done = TRUE;
ef5e8132 1566 rv->draw_sync_do = TRUE;
56afe641
RN
1567 // Everything else is 0, FALSE or NULL
1568
50a14534
EB
1569 return rv;
1570}
1571
1572
a7cd93ac 1573static void trw_layer_free ( VikTrwLayer *trwlayer )
50a14534
EB
1574{
1575 g_hash_table_destroy(trwlayer->waypoints);
2b403651 1576 g_hash_table_destroy(trwlayer->waypoints_iters);
50a14534 1577 g_hash_table_destroy(trwlayer->tracks);
2b403651
RN
1578 g_hash_table_destroy(trwlayer->tracks_iters);
1579 g_hash_table_destroy(trwlayer->routes);
1580 g_hash_table_destroy(trwlayer->routes_iters);
50a14534
EB
1581
1582 /* ODC: replace with GArray */
1583 trw_layer_free_track_gcs ( trwlayer );
1584
1585 if ( trwlayer->wp_right_click_menu )
4f14a010 1586 g_object_ref_sink ( G_OBJECT(trwlayer->wp_right_click_menu) );
50a14534 1587
8bd81489 1588 if ( trwlayer->track_right_click_menu )
1a7a0378 1589 g_object_ref_sink ( G_OBJECT(trwlayer->track_right_click_menu) );
8bd81489 1590
387ff7ac
RN
1591 if ( trwlayer->tracklabellayout != NULL)
1592 g_object_unref ( G_OBJECT ( trwlayer->tracklabellayout ) );
1593
50a14534
EB
1594 if ( trwlayer->wplabellayout != NULL)
1595 g_object_unref ( G_OBJECT ( trwlayer->wplabellayout ) );
1596
1597 if ( trwlayer->waypoint_gc != NULL )
1598 g_object_unref ( G_OBJECT ( trwlayer->waypoint_gc ) );
1599
1600 if ( trwlayer->waypoint_text_gc != NULL )
1601 g_object_unref ( G_OBJECT ( trwlayer->waypoint_text_gc ) );
1602
1603 if ( trwlayer->waypoint_bg_gc != NULL )
1604 g_object_unref ( G_OBJECT ( trwlayer->waypoint_bg_gc ) );
1605
63ffa244 1606 g_free ( trwlayer->wp_fsize_str );
387ff7ac 1607 g_free ( trwlayer->track_fsize_str );
63ffa244 1608
50a14534
EB
1609 if ( trwlayer->tpwin != NULL )
1610 gtk_widget_destroy ( GTK_WIDGET(trwlayer->tpwin) );
1611
43984ba1
RN
1612 if ( trwlayer->tracks_analysis_dialog != NULL )
1613 gtk_widget_destroy ( GTK_WIDGET(trwlayer->tracks_analysis_dialog) );
1614
50a14534
EB
1615 g_list_foreach ( trwlayer->image_cache->head, (GFunc) cached_pixbuf_free, NULL );
1616 g_queue_free ( trwlayer->image_cache );
1617}
1618
bc07590a 1619static void init_drawing_params ( struct DrawingParams *dp, VikTrwLayer *vtl, VikViewport *vp, gboolean highlight )
50a14534 1620{
08084371 1621 dp->vtl = vtl;
50a14534 1622 dp->vp = vp;
bc07590a 1623 dp->highlight = highlight;
b2ddff02 1624 dp->vw = (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(dp->vtl);
50a14534
EB
1625 dp->xmpp = vik_viewport_get_xmpp ( vp );
1626 dp->ympp = vik_viewport_get_ympp ( vp );
1627 dp->width = vik_viewport_get_width ( vp );
1628 dp->height = vik_viewport_get_height ( vp );
81765f5e
GB
1629 dp->cc = vtl->drawdirections_size*cos(DEG2RAD(45)); // Calculate once per vtl update - even if not used
1630 dp->ss = vtl->drawdirections_size*sin(DEG2RAD(45)); // Calculate once per vtl update - even if not used
08084371 1631
50a14534
EB
1632 dp->center = vik_viewport_get_center ( vp );
1633 dp->one_zone = vik_viewport_is_one_zone ( vp ); /* false if some other projection besides UTM */
1634 dp->lat_lon = vik_viewport_get_coord_mode ( vp ) == VIK_COORD_LATLON;
1635
1636 if ( dp->one_zone )
1637 {
1638 gint w2, h2;
1639 w2 = dp->xmpp * (dp->width / 2) + 1600 / dp->xmpp;
1640 h2 = dp->ympp * (dp->height / 2) + 1600 / dp->ympp;
1641 /* leniency -- for tracks. Obviously for waypoints this SHOULD be a lot smaller */
1642
1643 dp->ce1 = dp->center->east_west-w2;
1644 dp->ce2 = dp->center->east_west+w2;
1645 dp->cn1 = dp->center->north_south-h2;
1646 dp->cn2 = dp->center->north_south+h2;
1647 } else if ( dp->lat_lon ) {
1648 VikCoord upperleft, bottomright;
1649 /* quick & dirty calculation; really want to check all corners due to lat/lon smaller at top in northern hemisphere */
1650 /* this also DOESN'T WORK if you are crossing 180/-180 lon. I don't plan to in the near future... */
1651 vik_viewport_screen_to_coord ( vp, -500, -500, &upperleft );
1652 vik_viewport_screen_to_coord ( vp, dp->width+500, dp->height+500, &bottomright );
1653 dp->ce1 = upperleft.east_west;
1654 dp->ce2 = bottomright.east_west;
1655 dp->cn1 = bottomright.north_south;
1656 dp->cn2 = upperleft.north_south;
1657 }
20981fd6
RN
1658
1659 vik_viewport_get_min_max_lat_lon ( vp, &(dp->bbox.south), &(dp->bbox.north), &(dp->bbox.west), &(dp->bbox.east) );
50a14534
EB
1660}
1661
18df6d42
RN
1662/*
1663 * Determine the colour of the trackpoint (and/or trackline) relative to the average speed
1664 * Here a simple traffic like light colour system is used:
1665 * . slow points are red
1666 * . average is yellow
1667 * . fast points are green
1668 */
1669static gint track_section_colour_by_speed ( VikTrwLayer *vtl, VikTrackpoint *tp1, VikTrackpoint *tp2, gdouble average_speed, gdouble low_speed, gdouble high_speed )
50a14534 1670{
18df6d42
RN
1671 gdouble rv = 0;
1672 if ( tp1->has_timestamp && tp2->has_timestamp ) {
1673 if ( average_speed > 0 ) {
1674 rv = ( vik_coord_diff ( &(tp1->coord), &(tp2->coord) ) / (tp1->timestamp - tp2->timestamp) );
1675 if ( rv < low_speed )
1676 return VIK_TRW_LAYER_TRACK_GC_SLOW;
1677 else if ( rv > high_speed )
1678 return VIK_TRW_LAYER_TRACK_GC_FAST;
1679 else
1680 return VIK_TRW_LAYER_TRACK_GC_AVER;
1681 }
1682 }
1683 return VIK_TRW_LAYER_TRACK_GC_BLACK;
50a14534
EB
1684}
1685
5b309036 1686static void draw_utm_skip_insignia ( VikViewport *vvp, GdkGC *gc, gint x, gint y )
50a14534
EB
1687{
1688 vik_viewport_draw_line ( vvp, gc, x+5, y, x-5, y );
1689 vik_viewport_draw_line ( vvp, gc, x, y+5, x, y-5 );
1690 vik_viewport_draw_line ( vvp, gc, x+5, y+5, x-5, y-5 );
1691 vik_viewport_draw_line ( vvp, gc, x+5, y-5, x-5, y+5 );
1692}
1693
387ff7ac
RN
1694
1695static void trw_layer_draw_track_label ( gchar *name, gchar *fgcolour, gchar *bgcolour, struct DrawingParams *dp, VikCoord *coord )
1696{
1697 gchar *label_markup = g_strdup_printf ( "<span foreground=\"%s\" background=\"%s\" size=\"%s\">%s</span>", fgcolour, bgcolour, dp->vtl->track_fsize_str, name );
1698
1699 if ( pango_parse_markup ( label_markup, -1, 0, NULL, NULL, NULL, NULL ) )
1700 pango_layout_set_markup ( dp->vtl->tracklabellayout, label_markup, -1 );
1701 else
1702 // Fallback if parse failure
1703 pango_layout_set_text ( dp->vtl->tracklabellayout, name, -1 );
1704
1705 g_free ( label_markup );
1706
1707 gint label_x, label_y;
1708 gint width, height;
1709 pango_layout_get_pixel_size ( dp->vtl->tracklabellayout, &width, &height );
1710
1711 vik_viewport_coord_to_screen ( dp->vp, coord, &label_x, &label_y );
1712 vik_viewport_draw_layout ( dp->vp, dp->vtl->track_bg_gc, label_x-width/2, label_y-height/2, dp->vtl->tracklabellayout );
1713}
1714
1715/**
1716 * distance_in_preferred_units:
1717 * @dist: The source distance in standard SI Units (i.e. metres)
1718 *
1719 * TODO: This is a generic function that could be moved into globals.c or utils.c
1720 *
1721 * Probably best used if you have a only few conversions to perform.
1722 * However if doing many points (such as on all points along a track) then this may be a bit slow,
1723 * since it will be doing the preference check on each call
1724 *
1725 * Returns: The distance in the units as specified by the preferences
1726 */
1727static gdouble distance_in_preferred_units ( gdouble dist )
1728{
1729 gdouble mydist;
1730 vik_units_distance_t dist_units = a_vik_get_units_distance ();
1731 switch (dist_units) {
1732 case VIK_UNITS_DISTANCE_MILES:
1733 mydist = VIK_METERS_TO_MILES(dist);
1734 break;
1735 // VIK_UNITS_DISTANCE_KILOMETRES:
1736 default:
1737 mydist = dist/1000.0;
1738 break;
1739 }
1740 return mydist;
1741}
1742
1743/**
1744 * trw_layer_draw_dist_labels:
1745 *
1746 * Draw a few labels along a track at nicely seperated distances
1747 * This might slow things down if there's many tracks being displayed with this on.
1748 */
1749static void trw_layer_draw_dist_labels ( struct DrawingParams *dp, VikTrack *trk, gboolean drawing_highlight )
1750{
1751 static const gdouble chunksd[] = {0.25, 0.5, 1.0, 2.0, 5.0, 10.0, 15.0, 20.0,
1752 25.0, 40.0, 50.0, 75.0, 100.0,
1753 150.0, 200.0, 250.0, 500.0, 1000.0};
1754
1755 gdouble dist = vik_track_get_length_including_gaps ( trk ) / (trk->max_number_dist_labels+1);
1756
1757 // Convert to specified unit to find the friendly breakdown value
1758 dist = distance_in_preferred_units ( dist );
1759
1760 gint index = 0;
1761 gint i=0;
1762 for ( i = 0; i < G_N_ELEMENTS(chunksd); i++ ) {
1763 if ( chunksd[i] > dist ) {
1764 index = i;
1765 dist = chunksd[index];
1766 break;
1767 }
1768 }
1769
1770 vik_units_distance_t dist_units = a_vik_get_units_distance ();
1771
1772 for ( i = 1; i < trk->max_number_dist_labels+1; i++ ) {
1773 gdouble dist_i = dist * i;
1774
1775 // Convert distance back into metres for use in finding a trackpoint
1776 switch (dist_units) {
1777 case VIK_UNITS_DISTANCE_MILES:
1778 dist_i = VIK_MILES_TO_METERS(dist_i);
1779 break;
1780 // VIK_UNITS_DISTANCE_KILOMETRES:
1781 default:
1782 dist_i = dist_i*1000.0;
1783 break;
1784 }
1785
1786 gdouble dist_current = 0.0;
1787 VikTrackpoint *tp_current = vik_track_get_tp_by_dist ( trk, dist_i, FALSE, &dist_current );
1788 gdouble dist_next = 0.0;
1789 VikTrackpoint *tp_next = vik_track_get_tp_by_dist ( trk, dist_i, TRUE, &dist_next );
1790
1791 gdouble dist_between_tps = fabs (dist_next - dist_current);
1792 gdouble ratio = 0.0;
1793 // Prevent division by 0 errors
1794 if ( dist_between_tps > 0.0 )
1795 ratio = fabs(dist_i-dist_current)/dist_between_tps;
1796
1797 if ( tp_current && tp_next ) {
1798 // Construct the name based on the distance value
1799 gchar *name;
1800 gchar *units;
1801 switch (dist_units) {
1802 case VIK_UNITS_DISTANCE_MILES:
1803 units = g_strdup ( _("miles") );
1804 break;
1805 // VIK_UNITS_DISTANCE_KILOMETRES:
1806 default:
1807 units = g_strdup ( _("km") );
1808 break;
1809 }
1810
1811 // Convert for display
1812 dist_i = distance_in_preferred_units ( dist_i );
1813
1814 // Make the precision of the output related to the unit size.
1815 if ( index == 0 )
1816 name = g_strdup_printf ( "%.2f %s", dist_i, units);
1817 else if ( index == 1 )
1818 name = g_strdup_printf ( "%.1f %s", dist_i, units);
1819 else
1820 name = g_strdup_printf ( "%d %s", (gint)round(dist_i), units); // TODO single vs plurals
1821 g_free ( units );
1822
1823 struct LatLon ll_current, ll_next;
1824 vik_coord_to_latlon ( &tp_current->coord, &ll_current );
1825 vik_coord_to_latlon ( &tp_next->coord, &ll_next );
1826
1827 // positional interpolation
1828 // Using a simple ratio - may not be perfectly correct due to lat/long projections
1829 // but should be good enough over the small scale that I anticipate usage on
1830 struct LatLon ll_new = { ll_current.lat + (ll_next.lat-ll_current.lat)*ratio,
1831 ll_current.lon + (ll_next.lon-ll_current.lon)*ratio };
1832 VikCoord coord;
1833 vik_coord_load_from_latlon ( &coord, dp->vtl->coord_mode, &ll_new );
1834
1835 gchar *fgcolour;
1836 if ( dp->vtl->drawmode == DRAWMODE_BY_TRACK )
1837 fgcolour = gdk_color_to_string ( &(trk->color) );
1838 else
1839 fgcolour = gdk_color_to_string ( &(dp->vtl->track_color) );
1840
1841 // if highlight mode on, then colour the background in the highlight colour
1842 gchar *bgcolour;
1843 if ( drawing_highlight )
1844 bgcolour = g_strdup ( vik_viewport_get_highlight_color ( dp->vp ) );
1845 else
1846 bgcolour = gdk_color_to_string ( &(dp->vtl->track_bg_color) );
1847
1848 trw_layer_draw_track_label ( name, fgcolour, bgcolour, dp, &coord );
1849
1850 g_free ( fgcolour );
1851 g_free ( bgcolour );
1852 g_free ( name );
1853 }
1854 }
1855}
1856
1857/**
1858 * trw_layer_draw_track_name_labels:
1859 *
1860 * Draw a label (or labels) for the track name somewhere depending on the track's properties
1861 */
1862static void trw_layer_draw_track_name_labels ( struct DrawingParams *dp, VikTrack *trk, gboolean drawing_highlight )
1863{
1864 gchar *fgcolour;
1865 if ( dp->vtl->drawmode == DRAWMODE_BY_TRACK )
1866 fgcolour = gdk_color_to_string ( &(trk->color) );
1867 else
1868 fgcolour = gdk_color_to_string ( &(dp->vtl->track_color) );
1869
1870 // if highlight mode on, then colour the background in the highlight colour
1871 gchar *bgcolour;
1872 if ( drawing_highlight )
1873 bgcolour = g_strdup ( vik_viewport_get_highlight_color ( dp->vp ) );
1874 else
1875 bgcolour = gdk_color_to_string ( &(dp->vtl->track_bg_color) );
1876
1877 gchar *ename = g_markup_escape_text ( trk->name, -1 );
1878
1879 if ( trk->draw_name_mode == TRACK_DRAWNAME_START_END_CENTRE ||
1880 trk->draw_name_mode == TRACK_DRAWNAME_CENTRE ) {
1881 struct LatLon average, maxmin[2] = { {0,0}, {0,0} };
1882 trw_layer_find_maxmin_tracks ( NULL, trk, maxmin );
1883 average.lat = (maxmin[0].lat+maxmin[1].lat)/2;
1884 average.lon = (maxmin[0].lon+maxmin[1].lon)/2;
1885 VikCoord coord;
1886 vik_coord_load_from_latlon ( &coord, dp->vtl->coord_mode, &average );
1887
1888 trw_layer_draw_track_label ( ename, fgcolour, bgcolour, dp, &coord );
1889 }
1890
1891 if ( trk->draw_name_mode == TRACK_DRAWNAME_CENTRE )
1892 // No other labels to draw
1893 return;
1894
215ebe59
RN
1895 VikTrackpoint *tp_end = vik_track_get_tp_last ( trk );
1896 if ( !tp_end )
1897 return;
1898 VikTrackpoint *tp_begin = vik_track_get_tp_first ( trk );
1899 if ( !tp_begin )
1900 return;
1901 VikCoord begin_coord = tp_begin->coord;
1902 VikCoord end_coord = tp_end->coord;
387ff7ac
RN
1903
1904 gboolean done_start_end = FALSE;
1905
1906 if ( trk->draw_name_mode == TRACK_DRAWNAME_START_END ||
1907 trk->draw_name_mode == TRACK_DRAWNAME_START_END_CENTRE ) {
1908
1909 // This number can be configured via the settings if you really want to change it
1910 gdouble distance_diff;
1911 if ( ! a_settings_get_double ( "trackwaypoint_start_end_distance_diff", &distance_diff ) )
1912 distance_diff = 100.0; // Metres
1913
1914 if ( vik_coord_diff ( &begin_coord, &end_coord ) < distance_diff ) {
1915 // Start and end 'close' together so only draw one label at an average location
1916 gint x1, x2, y1, y2;
1917 vik_viewport_coord_to_screen ( dp->vp, &begin_coord, &x1, &y1);
1918 vik_viewport_coord_to_screen ( dp->vp, &end_coord, &x2, &y2);
1919 VikCoord av_coord;
1920 vik_viewport_screen_to_coord ( dp->vp, (x1 + x2) / 2, (y1 + y2) / 2, &av_coord );
1921
1922 gchar *name = g_strdup_printf ( "%s: %s", ename, _("start/end") );
1923 trw_layer_draw_track_label ( name, fgcolour, bgcolour, dp, &av_coord );
1924 g_free ( name );
1925
1926 done_start_end = TRUE;
1927 }
1928 }
1929
1930 if ( ! done_start_end ) {
1931 if ( trk->draw_name_mode == TRACK_DRAWNAME_START ||
1932 trk->draw_name_mode == TRACK_DRAWNAME_START_END ||
1933 trk->draw_name_mode == TRACK_DRAWNAME_START_END_CENTRE ) {
1934 gchar *name_start = g_strdup_printf ( "%s: %s", ename, _("start") );
1935 trw_layer_draw_track_label ( name_start, fgcolour, bgcolour, dp, &begin_coord );
1936 g_free ( name_start );
1937 }
1938 // Don't draw end label if this is the one being created
1939 if ( trk != dp->vtl->current_track ) {
1940 if ( trk->draw_name_mode == TRACK_DRAWNAME_END ||
1941 trk->draw_name_mode == TRACK_DRAWNAME_START_END ||
1942 trk->draw_name_mode == TRACK_DRAWNAME_START_END_CENTRE ) {
1943 gchar *name_end = g_strdup_printf ( "%s: %s", ename, _("end") );
1944 trw_layer_draw_track_label ( name_end, fgcolour, bgcolour, dp, &end_coord );
1945 g_free ( name_end );
1946 }
1947 }
1948 }
1949
1950 g_free ( fgcolour );
1951 g_free ( bgcolour );
1952 g_free ( ename );
1953}
1954
9e212bfc 1955static void trw_layer_draw_track ( const gpointer id, VikTrack *track, struct DrawingParams *dp, gboolean draw_track_outline )
50a14534 1956{
20981fd6
RN
1957 if ( ! track->visible )
1958 return;
1959
50a14534
EB
1960 /* TODO: this function is a mess, get rid of any redundancy */
1961 GList *list = track->trackpoints;
8e9c992d 1962 GdkGC *main_gc;
50a14534
EB
1963 gboolean useoldvals = TRUE;
1964
1965 gboolean drawpoints;
b42a25ba
EB
1966 gboolean drawstops;
1967 gboolean drawelevation;
941aa6e9 1968 gdouble min_alt, max_alt, alt_diff = 0;
50a14534 1969
3a1c8e42
RN
1970 const guint8 tp_size_reg = dp->vtl->drawpoints_size;
1971 const guint8 tp_size_cur = dp->vtl->drawpoints_size*2;
50a14534
EB
1972 guint8 tp_size;
1973
b42a25ba
EB
1974 if ( dp->vtl->drawelevation )
1975 {
1976 /* assume if it has elevation at the beginning, it has it throughout. not ness a true good assumption */
1977 if ( ( drawelevation = vik_track_get_minmax_alt ( track, &min_alt, &max_alt ) ) )
1978 alt_diff = max_alt - min_alt;
1979 }
1980
50a14534 1981 /* admittedly this is not an efficient way to do it because we go through the whole GC thing all over... */
0c9b1cf7 1982 if ( dp->vtl->bg_line_thickness && !draw_track_outline )
9e212bfc 1983 trw_layer_draw_track ( id, track, dp, TRUE );
50a14534 1984
0c9b1cf7 1985 if ( draw_track_outline )
b42a25ba
EB
1986 drawpoints = drawstops = FALSE;
1987 else {
50a14534 1988 drawpoints = dp->vtl->drawpoints;
b42a25ba
EB
1989 drawstops = dp->vtl->drawstops;
1990 }
50a14534 1991
18df6d42 1992 gboolean drawing_highlight = FALSE;
a5dcfdb7 1993 /* Current track - used for creation */
8e9c992d
EB
1994 if ( track == dp->vtl->current_track )
1995 main_gc = dp->vtl->current_track_gc;
a5dcfdb7 1996 else {
bc07590a
RN
1997 if ( dp->highlight ) {
1998 /* Draw all tracks of the layer in special colour
1999 NB this supercedes the drawmode */
2000 main_gc = vik_viewport_get_gc_highlight (dp->vp);
2001 drawing_highlight = TRUE;
a5dcfdb7 2002 }
b1453c16
RN
2003 if ( !drawing_highlight ) {
2004 // Still need to figure out the gc according to the drawing mode:
2005 switch ( dp->vtl->drawmode ) {
2006 case DRAWMODE_BY_TRACK:
2007 if ( dp->vtl->track_1color_gc )
2008 g_object_unref ( dp->vtl->track_1color_gc );
2009 dp->vtl->track_1color_gc = vik_viewport_new_gc_from_color ( dp->vp, &track->color, dp->vtl->line_thickness );
2010 main_gc = dp->vtl->track_1color_gc;
2011 break;
2012 default:
074b1067 2013 // Mostly for DRAWMODE_ALL_SAME_COLOR
b1453c16 2014 // but includes DRAWMODE_BY_SPEED, main_gc is set later on as necessary
074b1067 2015 main_gc = g_array_index(dp->vtl->track_gc, GdkGC *, VIK_TRW_LAYER_TRACK_GC_SINGLE);
b1453c16
RN
2016 break;
2017 }
a5dcfdb7
RN
2018 }
2019 }
8e9c992d 2020
50a14534
EB
2021 if (list) {
2022 int x, y, oldx, oldy;
2023 VikTrackpoint *tp = VIK_TRACKPOINT(list->data);
2024
2025 tp_size = (list == dp->vtl->current_tpl) ? tp_size_cur : tp_size_reg;
2026
2027 vik_viewport_coord_to_screen ( dp->vp, &(tp->coord), &x, &y );
2028
b1453c16
RN
2029 // Draw the first point as something a bit different from the normal points
2030 // ATM it's slightly bigger and a triangle
2031 if ( drawpoints ) {
50a14534 2032 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 2033 vik_viewport_draw_polygon ( dp->vp, main_gc, TRUE, trian, 3 );
50a14534
EB
2034 }
2035
2036 oldx = x;
2037 oldy = y;
2038
18df6d42
RN
2039 gdouble average_speed = 0.0;
2040 gdouble low_speed = 0.0;
2041 gdouble high_speed = 0.0;
2042 // If necessary calculate these values - which is done only once per track redraw
2043 if ( dp->vtl->drawmode == DRAWMODE_BY_SPEED ) {
2044 // the percentage factor away from the average speed determines transistions between the levels
2045 average_speed = vik_track_get_average_speed_moving(track, dp->vtl->stop_length);
2046 low_speed = average_speed - (average_speed*(dp->vtl->track_draw_speed_factor/100.0));
2047 high_speed = average_speed + (average_speed*(dp->vtl->track_draw_speed_factor/100.0));
2048 }
2049
50a14534
EB
2050 while ((list = g_list_next(list)))
2051 {
2052 tp = VIK_TRACKPOINT(list->data);
2053 tp_size = (list == dp->vtl->current_tpl) ? tp_size_cur : tp_size_reg;
2054
2055 /* check some stuff -- but only if we're in UTM and there's only ONE ZONE; or lat lon */
2056 if ( (!dp->one_zone && !dp->lat_lon) || /* UTM & zones; do everything */
2057 ( ((!dp->one_zone) || tp->coord.utm_zone == dp->center->utm_zone) && /* only check zones if UTM & one_zone */
2058 tp->coord.east_west < dp->ce2 && tp->coord.east_west > dp->ce1 && /* both UTM and lat lon */
2059 tp->coord.north_south > dp->cn1 && tp->coord.north_south < dp->cn2 ) )
2060 {
2061 vik_viewport_coord_to_screen ( dp->vp, &(tp->coord), &x, &y );
2062
adc271a8
GT
2063 /*
2064 * If points are the same in display coordinates, don't draw.
2065 */
2066 if ( useoldvals && x == oldx && y == oldy )
b827c5e0
RN
2067 {
2068 // Still need to process points to ensure 'stops' are drawn if required
0c9b1cf7 2069 if ( drawstops && drawpoints && ! draw_track_outline && list->next &&
b827c5e0
RN
2070 (VIK_TRACKPOINT(list->next->data)->timestamp - VIK_TRACKPOINT(list->data)->timestamp > dp->vtl->stop_length) )
2071 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 );
2072
adc271a8 2073 goto skip;
b827c5e0 2074 }
adc271a8 2075
18df6d42
RN
2076 VikTrackpoint *tp2 = VIK_TRACKPOINT(list->prev->data);
2077 if ( drawpoints || dp->vtl->drawlines ) {
2078 // setup main_gc for both point and line drawing
2079 if ( !drawing_highlight && (dp->vtl->drawmode == DRAWMODE_BY_SPEED) ) {
b1453c16 2080 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
2081 }
2082 }
2083
0c9b1cf7 2084 if ( drawpoints && ! draw_track_outline )
50a14534 2085 {
18df6d42 2086
50a14534 2087 if ( list->next ) {
4511d71f 2088 /*
f7eaf554 2089 * The concept of drawing stops is that a trackpoint
4511d71f
GT
2090 * that is if the next trackpoint has a timestamp far into
2091 * the future, we draw a circle of 6x trackpoint size,
f7eaf554
RN
2092 * instead of a rectangle of 2x trackpoint size.
2093 * This is drawn first so the trackpoint will be drawn on top
4511d71f 2094 */
50a14534 2095 /* stops */
b42a25ba 2096 if ( drawstops && VIK_TRACKPOINT(list->next->data)->timestamp - VIK_TRACKPOINT(list->data)->timestamp > dp->vtl->stop_length )
b1453c16
RN
2097 /* Stop point. Draw 6x circle. Always in redish colour */
2098 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
2099
2100 /* Regular point - draw 2x square. */
2101 vik_viewport_draw_rectangle ( dp->vp, main_gc, TRUE, x-tp_size, y-tp_size, 2*tp_size, 2*tp_size );
50a14534
EB
2102 }
2103 else
4511d71f 2104 /* Final point - draw 4x circle. */
8e9c992d 2105 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
2106 }
2107
2108 if ((!tp->newsegment) && (dp->vtl->drawlines))
2109 {
50a14534
EB
2110
2111 /* UTM only: zone check */
2112 if ( drawpoints && dp->vtl->coord_mode == VIK_COORD_UTM && tp->coord.utm_zone != dp->center->utm_zone )
8e9c992d 2113 draw_utm_skip_insignia ( dp->vp, main_gc, x, y);
50a14534 2114
50a14534
EB
2115 if (!useoldvals)
2116 vik_viewport_coord_to_screen ( dp->vp, &(tp2->coord), &oldx, &oldy );
2117
0c9b1cf7 2118 if ( draw_track_outline ) {
50a14534 2119 vik_viewport_draw_line ( dp->vp, dp->vtl->track_bg_gc, oldx, oldy, x, y);
8c4f1350
EB
2120 }
2121 else {
8c4f1350 2122
8e9c992d 2123 vik_viewport_draw_line ( dp->vp, main_gc, oldx, oldy, x, y);
08084371 2124
3b9970f8 2125 if ( dp->vtl->drawelevation && list->next && VIK_TRACKPOINT(list->next->data)->altitude != VIK_DEFAULT_ALTITUDE ) {
b42a25ba
EB
2126 GdkPoint tmp[4];
2127 #define FIXALTITUDE(what) ((VIK_TRACKPOINT((what))->altitude-min_alt)/alt_diff*DRAW_ELEVATION_FACTOR*dp->vtl->elevation_factor/dp->xmpp)
3b9970f8
RN
2128
2129 tmp[0].x = oldx;
2130 tmp[0].y = oldy;
2131 tmp[1].x = oldx;
2132 tmp[1].y = oldy-FIXALTITUDE(list->data);
2133 tmp[2].x = x;
2134 tmp[2].y = y-FIXALTITUDE(list->next->data);
2135 tmp[3].x = x;
2136 tmp[3].y = y;
2137
2138 GdkGC *tmp_gc;
2139 if ( ((oldx - x) > 0 && (oldy - y) > 0) || ((oldx - x) < 0 && (oldy - y) < 0))
ff37db21 2140 tmp_gc = gtk_widget_get_style(GTK_WIDGET(dp->vp))->light_gc[3];
3b9970f8 2141 else
ff37db21 2142 tmp_gc = gtk_widget_get_style(GTK_WIDGET(dp->vp))->dark_gc[0];
3b9970f8
RN
2143 vik_viewport_draw_polygon ( dp->vp, tmp_gc, TRUE, tmp, 4);
2144
8e9c992d 2145 vik_viewport_draw_line ( dp->vp, main_gc, oldx, oldy-FIXALTITUDE(list->data), x, y-FIXALTITUDE(list->next->data));
8c4f1350
EB
2146 }
2147 }
50a14534
EB
2148 }
2149
08084371
RN
2150 if ( (!tp->newsegment) && dp->vtl->drawdirections ) {
2151 // Draw an arrow at the mid point to show the direction of the track
2152 // Code is a rework from vikwindow::draw_ruler()
2153 gint midx = (oldx + x) / 2;
2154 gint midy = (oldy + y) / 2;
2155
2156 gdouble len = sqrt ( ((midx-oldx) * (midx-oldx)) + ((midy-oldy) * (midy-oldy)) );
2157 // Avoid divide by zero and ensure at least 1 pixel big
2158 if ( len > 1 ) {
2159 gdouble dx = (oldx - midx) / len;
2160 gdouble dy = (oldy - midy) / len;
2161 vik_viewport_draw_line ( dp->vp, main_gc, midx, midy, midx + (dx * dp->cc + dy * dp->ss), midy + (dy * dp->cc - dx * dp->ss) );
2162 vik_viewport_draw_line ( dp->vp, main_gc, midx, midy, midx + (dx * dp->cc - dy * dp->ss), midy + (dy * dp->cc + dx * dp->ss) );
2163 }
2164 }
2165
adc271a8 2166 skip:
50a14534
EB
2167 oldx = x;
2168 oldy = y;
2169 useoldvals = TRUE;
2170 }
2171 else {
2172 if (useoldvals && dp->vtl->drawlines && (!tp->newsegment))
2173 {
2174 VikTrackpoint *tp2 = VIK_TRACKPOINT(list->prev->data);
2175 if ( dp->vtl->coord_mode != VIK_COORD_UTM || tp->coord.utm_zone == dp->center->utm_zone )
2176 {
2177 vik_viewport_coord_to_screen ( dp->vp, &(tp->coord), &x, &y );
18df6d42
RN
2178
2179 if ( !drawing_highlight && (dp->vtl->drawmode == DRAWMODE_BY_SPEED) ) {
b1453c16 2180 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 2181 }
50a14534 2182
adc271a8
GT
2183 /*
2184 * If points are the same in display coordinates, don't draw.
2185 */
2186 if ( x != oldx || y != oldy )
2187 {
0c9b1cf7 2188 if ( draw_track_outline )
adc271a8
GT
2189 vik_viewport_draw_line ( dp->vp, dp->vtl->track_bg_gc, oldx, oldy, x, y);
2190 else
2191 vik_viewport_draw_line ( dp->vp, main_gc, oldx, oldy, x, y);
2192 }
50a14534
EB
2193 }
2194 else
2195 {
adc271a8
GT
2196 /*
2197 * If points are the same in display coordinates, don't draw.
2198 */
2199 if ( x != oldx && y != oldy )
2200 {
2201 vik_viewport_coord_to_screen ( dp->vp, &(tp2->coord), &x, &y );
2202 draw_utm_skip_insignia ( dp->vp, main_gc, x, y );
2203 }
50a14534
EB
2204 }
2205 }
2206 useoldvals = FALSE;
2207 }
2208 }
387ff7ac
RN
2209
2210 // Labels drawn after the trackpoints, so the labels are on top
2211 if ( dp->vtl->track_draw_labels ) {
2212 if ( track->max_number_dist_labels > 0 ) {
2213 trw_layer_draw_dist_labels ( dp, track, drawing_highlight );
2214 }
2215
2216 if ( track->draw_name_mode != TRACK_DRAWNAME_NO ) {
2217 trw_layer_draw_track_name_labels ( dp, track, drawing_highlight );
2218 }
2219 }
50a14534 2220 }
50a14534
EB
2221}
2222
9e212bfc 2223static void trw_layer_draw_track_cb ( const gpointer id, VikTrack *track, struct DrawingParams *dp )
50a14534 2224{
20981fd6
RN
2225 if ( BBOX_INTERSECT ( track->bbox, dp->bbox ) ) {
2226 trw_layer_draw_track ( id, track, dp, FALSE );
2227 }
50a14534
EB
2228}
2229
2230static void cached_pixbuf_free ( CachedPixbuf *cp )
2231{
2232 g_object_unref ( G_OBJECT(cp->pixbuf) );
2233 g_free ( cp->image );
2234}
2235
2236static gint cached_pixbuf_cmp ( CachedPixbuf *cp, const gchar *name )
2237{
2238 return strcmp ( cp->image, name );
2239}
2240
9e212bfc 2241static void trw_layer_draw_waypoint ( const gpointer id, VikWaypoint *wp, struct DrawingParams *dp )
50a14534
EB
2242{
2243 if ( wp->visible )
51f0884d 2244 if ( (!dp->one_zone && !dp->lat_lon) || ( ( dp->lat_lon || wp->coord.utm_zone == dp->center->utm_zone ) &&
50a14534
EB
2245 wp->coord.east_west < dp->ce2 && wp->coord.east_west > dp->ce1 &&
2246 wp->coord.north_south > dp->cn1 && wp->coord.north_south < dp->cn2 ) )
2247 {
2248 gint x, y;
2249 vik_viewport_coord_to_screen ( dp->vp, &(wp->coord), &x, &y );
2250
2251 /* if in shrunken_cache, get that. If not, get and add to shrunken_cache */
2252
2253 if ( wp->image && dp->vtl->drawimages )
2254 {
2255 GdkPixbuf *pixbuf = NULL;
2256 GList *l;
2257
2258 if ( dp->vtl->image_alpha == 0)
2259 return;
2260
2261 l = g_list_find_custom ( dp->vtl->image_cache->head, wp->image, (GCompareFunc) cached_pixbuf_cmp );
2262 if ( l )
2263 pixbuf = ((CachedPixbuf *) l->data)->pixbuf;
2264 else
2265 {
2266 gchar *image = wp->image;
2267 GdkPixbuf *regularthumb = a_thumbnails_get ( wp->image );
2268 if ( ! regularthumb )
2269 {
2270 regularthumb = a_thumbnails_get_default (); /* cache one 'not yet loaded' for all thumbs not loaded */
2271 image = "\x12\x00"; /* this shouldn't occur naturally. */
2272 }
2273 if ( regularthumb )
2274 {
2275 CachedPixbuf *cp = NULL;
2276 cp = g_malloc ( sizeof ( CachedPixbuf ) );
2277 if ( dp->vtl->image_size == 128 )
2278 cp->pixbuf = regularthumb;
2279 else
2280 {
2281 cp->pixbuf = a_thumbnails_scale_pixbuf(regularthumb, dp->vtl->image_size, dp->vtl->image_size);
2282 g_assert ( cp->pixbuf );
2283 g_object_unref ( G_OBJECT(regularthumb) );
2284 }
2285 cp->image = g_strdup ( image );
2286
2287 /* needed so 'click picture' tool knows how big the pic is; we don't
2288 * store it in cp because they may have been freed already. */
2289 wp->image_width = gdk_pixbuf_get_width ( cp->pixbuf );
2290 wp->image_height = gdk_pixbuf_get_height ( cp->pixbuf );
2291
2292 g_queue_push_head ( dp->vtl->image_cache, cp );
2293 if ( dp->vtl->image_cache->length > dp->vtl->image_cache_size )
2294 cached_pixbuf_free ( g_queue_pop_tail ( dp->vtl->image_cache ) );
2295
2296 pixbuf = cp->pixbuf;
2297 }
2298 else
2299 {
2300 pixbuf = a_thumbnails_get_default (); /* thumbnail not yet loaded */
2301 }
2302 }
2303 if ( pixbuf )
2304 {
2305 gint w, h;
2306 w = gdk_pixbuf_get_width ( pixbuf );
2307 h = gdk_pixbuf_get_height ( pixbuf );
2308
2309 if ( x+(w/2) > 0 && y+(h/2) > 0 && x-(w/2) < dp->width && y-(h/2) < dp->height ) /* always draw within boundaries */
2310 {
bc07590a
RN
2311 if ( dp->highlight ) {
2312 // Highlighted - so draw a little border around the chosen one
2313 // single line seems a little weak so draw 2 of them
2314 vik_viewport_draw_rectangle (dp->vp, vik_viewport_get_gc_highlight (dp->vp), FALSE,
2315 x - (w/2) - 1, y - (h/2) - 1, w + 2, h + 2 );
2316 vik_viewport_draw_rectangle (dp->vp, vik_viewport_get_gc_highlight (dp->vp), FALSE,
2317 x - (w/2) - 2, y - (h/2) - 2, w + 4, h + 4 );
2318 }
2319
50a14534
EB
2320 if ( dp->vtl->image_alpha == 255 )
2321 vik_viewport_draw_pixbuf ( dp->vp, pixbuf, 0, 0, x - (w/2), y - (h/2), w, h );
2322 else
2323 vik_viewport_draw_pixbuf_with_alpha ( dp->vp, pixbuf, dp->vtl->image_alpha, 0, 0, x - (w/2), y - (h/2), w, h );
2324 }
2325 return; /* if failed to draw picture, default to drawing regular waypoint (below) */
2326 }
2327 }
2328
98acb9a1
RN
2329 // Draw appropriate symbol - either symbol image or simple types
2330 if ( dp->vtl->wp_draw_symbols && wp->symbol && wp->symbol_pixbuf ) {
2331 vik_viewport_draw_pixbuf ( dp->vp, wp->symbol_pixbuf, 0, 0, x - gdk_pixbuf_get_width(wp->symbol_pixbuf)/2, y - gdk_pixbuf_get_height(wp->symbol_pixbuf)/2, -1, -1 );
acaf7113
AF
2332 }
2333 else if ( wp == dp->vtl->current_wp ) {
50a14534
EB
2334 switch ( dp->vtl->wp_symbol ) {
2335 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;
2336 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;
2337 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;
2338 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 );
2339 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 );
9351abc4 2340 default: break;
50a14534
EB
2341 }
2342 }
2343 else {
2344 switch ( dp->vtl->wp_symbol ) {
2345 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;
2346 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;
2347 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;
2348 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 );
2349 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;
9351abc4 2350 default: break;
50a14534
EB
2351 }
2352 }
2353
2354 if ( dp->vtl->drawlabels )
2355 {
2356 /* thanks to the GPSDrive people (Fritz Ganter et al.) for hints on this part ... yah, I'm too lazy to study documentation */
a7f9c01e 2357 gint label_x, label_y;
50a14534 2358 gint width, height;
5a77ae71
RN
2359 // Hopefully name won't break the markup (may need to sanitize - g_markup_escape_text())
2360
2361 // Could this stored in the waypoint rather than recreating each pass?
63ffa244 2362 gchar *wp_label_markup = g_strdup_printf ( "<span size=\"%s\">%s</span>", dp->vtl->wp_fsize_str, wp->name );
5a77ae71
RN
2363
2364 if ( pango_parse_markup ( wp_label_markup, -1, 0, NULL, NULL, NULL, NULL ) )
2365 pango_layout_set_markup ( dp->vtl->wplabellayout, wp_label_markup, -1 );
2366 else
2367 // Fallback if parse failure
2368 pango_layout_set_text ( dp->vtl->wplabellayout, wp->name, -1 );
2369
2370 g_free ( wp_label_markup );
5a77ae71 2371
50a14534 2372 pango_layout_get_pixel_size ( dp->vtl->wplabellayout, &width, &height );
a7f9c01e 2373 label_x = x - width/2;
98acb9a1
RN
2374 if ( wp->symbol_pixbuf )
2375 label_y = y - height - 2 - gdk_pixbuf_get_height(wp->symbol_pixbuf)/2;
a7f9c01e
QT
2376 else
2377 label_y = y - dp->vtl->wp_size - height - 2;
2378
480fb7e1 2379 /* if highlight mode on, then draw background text in highlight colour */
bc07590a
RN
2380 if ( dp->highlight )
2381 vik_viewport_draw_rectangle ( dp->vp, vik_viewport_get_gc_highlight (dp->vp), TRUE, label_x - 1, label_y-1,width+2,height+2);
2382 else
2383 vik_viewport_draw_rectangle ( dp->vp, dp->vtl->waypoint_bg_gc, TRUE, label_x - 1, label_y-1,width+2,height+2);
a7f9c01e 2384 vik_viewport_draw_layout ( dp->vp, dp->vtl->waypoint_text_gc, label_x, label_y, dp->vtl->wplabellayout );
50a14534
EB
2385 }
2386 }
2387}
2388
aa0665e5
RN
2389static void trw_layer_draw_waypoint_cb ( gpointer id, VikWaypoint *wp, struct DrawingParams *dp )
2390{
2391 if ( BBOX_INTERSECT ( dp->vtl->waypoints_bbox, dp->bbox ) ) {
2392 trw_layer_draw_waypoint ( id, wp, dp );
2393 }
2394}
2395
bc07590a 2396static void trw_layer_draw_with_highlight ( VikTrwLayer *l, gpointer data, gboolean highlight )
50a14534
EB
2397{
2398 static struct DrawingParams dp;
2399 g_assert ( l != NULL );
2400
bc07590a 2401 init_drawing_params ( &dp, l, VIK_VIEWPORT(data), highlight );
50a14534
EB
2402
2403 if ( l->tracks_visible )
2404 g_hash_table_foreach ( l->tracks, (GHFunc) trw_layer_draw_track_cb, &dp );
2405
0d2b891f
RN
2406 if ( l->routes_visible )
2407 g_hash_table_foreach ( l->routes, (GHFunc) trw_layer_draw_track_cb, &dp );
2408
50a14534 2409 if (l->waypoints_visible)
aa0665e5 2410 g_hash_table_foreach ( l->waypoints, (GHFunc) trw_layer_draw_waypoint_cb, &dp );
50a14534
EB
2411}
2412
bc07590a
RN
2413static void trw_layer_draw ( VikTrwLayer *l, gpointer data )
2414{
21ad165a
RN
2415 // If this layer is to be highlighted - then don't draw now - as it will be drawn later on in the specific highlight draw stage
2416 // This may seem slightly inefficient to test each time for every layer
2417 // but for a layer with *lots* of tracks & waypoints this can save some effort by not drawing the items twice
2418 if ( vik_viewport_get_draw_highlight ( (VikViewport*)data ) &&
2419 vik_window_get_selected_trw_layer ((VikWindow*)VIK_GTK_WINDOW_FROM_LAYER((VikLayer*)l)) == l )
2420 return;
2421 trw_layer_draw_with_highlight ( l, data, FALSE );
bc07590a
RN
2422}
2423
2424void vik_trw_layer_draw_highlight ( VikTrwLayer *vtl, VikViewport *vvp )
2425{
2426 // Check the layer for visibility (including all the parents visibilities)
2427 if ( !vik_treeview_item_get_visible_tree (VIK_LAYER(vtl)->vt, &(VIK_LAYER(vtl)->iter)) )
2428 return;
2429 trw_layer_draw_with_highlight ( vtl, vvp, TRUE );
2430}
2431
2432/**
2433 * vik_trw_layer_draw_highlight_item:
2434 *
2435 * Only handles a single track or waypoint ATM
2436 * It assumes the track or waypoint belongs to the TRW Layer (it doesn't check this is the case)
2437 */
2438void vik_trw_layer_draw_highlight_item ( VikTrwLayer *vtl, VikTrack *trk, VikWaypoint *wpt, VikViewport *vvp )
2439{
2440 // Check the layer for visibility (including all the parents visibilities)
2441 if ( !vik_treeview_item_get_visible_tree (VIK_LAYER(vtl)->vt, &(VIK_LAYER(vtl)->iter)) )
2442 return;
2443
2444 static struct DrawingParams dp;
2445 init_drawing_params ( &dp, vtl, vvp, TRUE );
2446
2447 if ( trk ) {
2448 gboolean draw = ( trk->is_route && vtl->routes_visible ) || ( !trk->is_route && vtl->tracks_visible );
2449 if ( draw )
2450 trw_layer_draw_track_cb ( NULL, trk, &dp );
2451 }
2452 if ( vtl->waypoints_visible && wpt ) {
2453 trw_layer_draw_waypoint_cb ( NULL, wpt, &dp );
2454 }
2455}
2456
2457/**
2458 * vik_trw_layer_draw_highlight_item:
2459 *
2460 * Generally for drawing all tracks or routes or waypoints
2461 * trks may be actually routes
2462 * It assumes they belong to the TRW Layer (it doesn't check this is the case)
2463 */
2464void vik_trw_layer_draw_highlight_items ( VikTrwLayer *vtl, GHashTable *trks, GHashTable *wpts, VikViewport *vvp )
2465{
2466 // Check the layer for visibility (including all the parents visibilities)
2467 if ( !vik_treeview_item_get_visible_tree (VIK_LAYER(vtl)->vt, &(VIK_LAYER(vtl)->iter)) )
2468 return;
2469
2470 static struct DrawingParams dp;
2471 init_drawing_params ( &dp, vtl, vvp, TRUE );
2472
2473 if ( trks ) {
2474 gboolean is_routes = (trks == vtl->routes);
2475 gboolean draw = ( is_routes && vtl->routes_visible ) || ( !is_routes && vtl->tracks_visible );
2476 if ( draw )
2477 g_hash_table_foreach ( trks, (GHFunc) trw_layer_draw_track_cb, &dp );
2478 }
2479
2480 if ( vtl->waypoints_visible && wpts )
2481 g_hash_table_foreach ( wpts, (GHFunc) trw_layer_draw_waypoint_cb, &dp );
2482}
2483
50a14534
EB
2484static void trw_layer_free_track_gcs ( VikTrwLayer *vtl )
2485{
2486 int i;
2487 if ( vtl->track_bg_gc )
2488 {
2489 g_object_unref ( vtl->track_bg_gc );
2490 vtl->track_bg_gc = NULL;
2491 }
b1453c16
RN
2492 if ( vtl->track_1color_gc )
2493 {
2494 g_object_unref ( vtl->track_1color_gc );
2495 vtl->track_1color_gc = NULL;
2496 }
8e9c992d
EB
2497 if ( vtl->current_track_gc )
2498 {
2499 g_object_unref ( vtl->current_track_gc );
2500 vtl->current_track_gc = NULL;
2501 }
745fda83
RN
2502 if ( vtl->current_track_newpoint_gc )
2503 {
2504 g_object_unref ( vtl->current_track_newpoint_gc );
2505 vtl->current_track_newpoint_gc = NULL;
2506 }
50a14534
EB
2507
2508 if ( ! vtl->track_gc )
2509 return;
2510 for ( i = vtl->track_gc->len - 1; i >= 0; i-- )
2511 g_object_unref ( g_array_index ( vtl->track_gc, GObject *, i ) );
2512 g_array_free ( vtl->track_gc, TRUE );
2513 vtl->track_gc = NULL;
2514}
2515
2516static void trw_layer_new_track_gcs ( VikTrwLayer *vtl, VikViewport *vp )
2517{
2518 GdkGC *gc[ VIK_TRW_LAYER_TRACK_GC ];
2519 gint width = vtl->line_thickness;
2520
2521 if ( vtl->track_gc )
2522 trw_layer_free_track_gcs ( vtl );
2523
2524 if ( vtl->track_bg_gc )
2525 g_object_unref ( vtl->track_bg_gc );
a7023a1b 2526 vtl->track_bg_gc = vik_viewport_new_gc_from_color ( vp, &(vtl->track_bg_color), width + vtl->bg_line_thickness );
50a14534 2527
98b7eddc
RN
2528 // Ensure new track drawing heeds line thickness setting
2529 // however always have a minium of 2, as 1 pixel is really narrow
2530 gint new_track_width = (vtl->line_thickness < 2) ? 2 : vtl->line_thickness;
2531
8e9c992d
EB
2532 if ( vtl->current_track_gc )
2533 g_object_unref ( vtl->current_track_gc );
98b7eddc
RN
2534 vtl->current_track_gc = vik_viewport_new_gc ( vp, "#FF0000", new_track_width );
2535 gdk_gc_set_line_attributes ( vtl->current_track_gc, new_track_width, GDK_LINE_ON_OFF_DASH, GDK_CAP_ROUND, GDK_JOIN_ROUND );
8e9c992d 2536
745fda83
RN
2537 // 'newpoint' gc is exactly the same as the current track gc
2538 if ( vtl->current_track_newpoint_gc )
2539 g_object_unref ( vtl->current_track_newpoint_gc );
98b7eddc
RN
2540 vtl->current_track_newpoint_gc = vik_viewport_new_gc ( vp, "#FF0000", new_track_width );
2541 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 2542
50a14534
EB
2543 vtl->track_gc = g_array_sized_new ( FALSE, FALSE, sizeof ( GdkGC * ), VIK_TRW_LAYER_TRACK_GC );
2544
b1453c16
RN
2545 gc[VIK_TRW_LAYER_TRACK_GC_STOP] = vik_viewport_new_gc ( vp, "#874200", width );
2546 gc[VIK_TRW_LAYER_TRACK_GC_BLACK] = vik_viewport_new_gc ( vp, "#000000", width ); // black
50a14534 2547
18df6d42
RN
2548 gc[VIK_TRW_LAYER_TRACK_GC_SLOW] = vik_viewport_new_gc ( vp, "#E6202E", width ); // red-ish
2549 gc[VIK_TRW_LAYER_TRACK_GC_AVER] = vik_viewport_new_gc ( vp, "#D2CD26", width ); // yellow-ish
2550 gc[VIK_TRW_LAYER_TRACK_GC_FAST] = vik_viewport_new_gc ( vp, "#2B8700", width ); // green-ish
2551
074b1067
RN
2552 gc[VIK_TRW_LAYER_TRACK_GC_SINGLE] = vik_viewport_new_gc_from_color ( vp, &(vtl->track_color), width );
2553
50a14534
EB
2554 g_array_append_vals ( vtl->track_gc, gc, VIK_TRW_LAYER_TRACK_GC );
2555}
2556
a7cd93ac 2557static VikTrwLayer* trw_layer_create ( VikViewport *vp )
50a14534 2558{
a7023a1b 2559 VikTrwLayer *rv = trw_layer_new1 ( vp );
ba8be43f
RN
2560 vik_layer_rename ( VIK_LAYER(rv), vik_trw_layer_interface.name );
2561
9b082b39 2562 if ( vp == NULL || gtk_widget_get_window(GTK_WIDGET(vp)) == NULL ) {
ba8be43f
RN
2563 /* early exit, as the rest is GUI related */
2564 return rv;
2565 }
2566
50a14534 2567 rv->wplabellayout = gtk_widget_create_pango_layout (GTK_WIDGET(vp), NULL);
ff37db21 2568 pango_layout_set_font_description (rv->wplabellayout, gtk_widget_get_style(GTK_WIDGET(vp))->font_desc);
50a14534 2569
387ff7ac
RN
2570 rv->tracklabellayout = gtk_widget_create_pango_layout (GTK_WIDGET(vp), NULL);
2571 pango_layout_set_font_description (rv->tracklabellayout, gtk_widget_get_style(GTK_WIDGET(vp))->font_desc);
2572
50a14534
EB
2573 trw_layer_new_track_gcs ( rv, vp );
2574
a7023a1b
RN
2575 rv->waypoint_gc = vik_viewport_new_gc_from_color ( vp, &(rv->waypoint_color), 2 );
2576 rv->waypoint_text_gc = vik_viewport_new_gc_from_color ( vp, &(rv->waypoint_text_color), 1 );
2577 rv->waypoint_bg_gc = vik_viewport_new_gc_from_color ( vp, &(rv->waypoint_bg_color), 1 );
2578 gdk_gc_set_function ( rv->waypoint_bg_gc, rv->wpbgand );
50a14534
EB
2579
2580 rv->coord_mode = vik_viewport_get_coord_mode ( vp );
2581
20c7a3a0
QT
2582 rv->menu_selection = vik_layer_get_interface(VIK_LAYER(rv)->type)->menu_items_selection;
2583
50a14534
EB
2584 return rv;
2585}
2586
70cefedc
RN
2587#define SMALL_ICON_SIZE 18
2588/*
2589 * Can accept a null symbol, and may return null value
2590 */
d18f2933 2591GdkPixbuf* get_wp_sym_small ( gchar *symbol )
70cefedc
RN
2592{
2593 GdkPixbuf* wp_icon = a_get_wp_sym (symbol);
2594 // ATM a_get_wp_sym returns a cached icon, with the size dependent on the preferences.
2595 // So needing a small icon for the treeview may need some resizing:
2596 if ( wp_icon && gdk_pixbuf_get_width ( wp_icon ) != SMALL_ICON_SIZE )
2597 wp_icon = gdk_pixbuf_scale_simple ( wp_icon, SMALL_ICON_SIZE, SMALL_ICON_SIZE, GDK_INTERP_BILINEAR );
2598 return wp_icon;
2599}
2600
ce4bd1cf 2601static void trw_layer_realize_track ( gpointer id, VikTrack *track, gpointer pass_along[5] )
50a14534
EB
2602{
2603 GtkTreeIter *new_iter = g_malloc(sizeof(GtkTreeIter));
2604
b1453c16
RN
2605 GdkPixbuf *pixbuf = NULL;
2606
2607 if ( track->has_color ) {
2608 pixbuf = gdk_pixbuf_new ( GDK_COLORSPACE_RGB, FALSE, 8, SMALL_ICON_SIZE, SMALL_ICON_SIZE );
2609 // Annoyingly the GdkColor.pixel does not give the correct color when passed to gdk_pixbuf_fill (even when alloc'ed)
2610 // Here is some magic found to do the conversion
2611 // http://www.cs.binghamton.edu/~sgreene/cs360-2011s/topics/gtk+-2.20.1/gtk/gtkcolorbutton.c
2612 guint32 pixel = ((track->color.red & 0xff00) << 16) |
2613 ((track->color.green & 0xff00) << 8) |
2614 (track->color.blue & 0xff00);
2615
2616 gdk_pixbuf_fill ( pixbuf, pixel );
2617 }
2618
c9cac058 2619 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 2620
b1453c16
RN
2621 if ( pixbuf )
2622 g_object_unref (pixbuf);
2623
50a14534 2624 *new_iter = *((GtkTreeIter *) pass_along[1]);
0d2b891f
RN
2625 if ( track->is_route )
2626 g_hash_table_insert ( VIK_TRW_LAYER(pass_along[2])->routes_iters, id, new_iter );
2627 else
2628 g_hash_table_insert ( VIK_TRW_LAYER(pass_along[2])->tracks_iters, id, new_iter );
50a14534
EB
2629
2630 if ( ! track->visible )
2631 vik_treeview_item_set_visible ( (VikTreeview *) pass_along[3], (GtkTreeIter *) pass_along[1], FALSE );
2632}
2633
c9570f86 2634static void trw_layer_realize_waypoint ( gpointer id, VikWaypoint *wp, gpointer pass_along[5] )
50a14534
EB
2635{
2636 GtkTreeIter *new_iter = g_malloc(sizeof(GtkTreeIter));
70cefedc 2637
c9cac058 2638 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
2639
2640 *new_iter = *((GtkTreeIter *) pass_along[1]);
c9570f86 2641 g_hash_table_insert ( VIK_TRW_LAYER(pass_along[2])->waypoints_iters, id, new_iter );
50a14534
EB
2642
2643 if ( ! wp->visible )
2644 vik_treeview_item_set_visible ( (VikTreeview *) pass_along[3], (GtkTreeIter *) pass_along[1], FALSE );
2645}
2646
cd3f311e 2647static void trw_layer_add_sublayer_tracks ( VikTrwLayer *vtl, VikTreeview *vt, GtkTreeIter *layer_iter )
50a14534 2648{
c9cac058 2649 vik_treeview_add_sublayer ( (VikTreeview *) vt, layer_iter, &(vtl->tracks_iter), _("Tracks"), vtl, NULL, VIK_TRW_LAYER_SUBLAYER_TRACKS, NULL, TRUE, FALSE );
cd3f311e 2650}
50a14534 2651
cd3f311e
RN
2652static void trw_layer_add_sublayer_waypoints ( VikTrwLayer *vtl, VikTreeview *vt, GtkTreeIter *layer_iter )
2653{
c9cac058 2654 vik_treeview_add_sublayer ( (VikTreeview *) vt, layer_iter, &(vtl->waypoints_iter), _("Waypoints"), vtl, NULL, VIK_TRW_LAYER_SUBLAYER_WAYPOINTS, NULL, TRUE, FALSE );
cd3f311e 2655}
50a14534 2656
0d2b891f
RN
2657static void trw_layer_add_sublayer_routes ( VikTrwLayer *vtl, VikTreeview *vt, GtkTreeIter *layer_iter )
2658{
c9cac058 2659 vik_treeview_add_sublayer ( (VikTreeview *) vt, layer_iter, &(vtl->routes_iter), _("Routes"), vtl, NULL, VIK_TRW_LAYER_SUBLAYER_ROUTES, NULL, TRUE, FALSE );
0d2b891f
RN
2660}
2661
cd3f311e
RN
2662static void trw_layer_realize ( VikTrwLayer *vtl, VikTreeview *vt, GtkTreeIter *layer_iter )
2663{
2664 GtkTreeIter iter2;
2665 gpointer pass_along[5] = { &(vtl->tracks_iter), &iter2, vtl, vt, GINT_TO_POINTER(VIK_TRW_LAYER_SUBLAYER_TRACK) };
50a14534 2666
cd3f311e
RN
2667 if ( g_hash_table_size (vtl->tracks) > 0 ) {
2668 trw_layer_add_sublayer_tracks ( vtl, vt , layer_iter );
9414408e 2669
cd3f311e 2670 g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_realize_track, pass_along );
50a14534 2671
c9cac058 2672 vik_treeview_item_set_visible ( vt, &(vtl->tracks_iter), vtl->tracks_visible );
cd3f311e 2673 }
50a14534 2674
0d2b891f 2675 if ( g_hash_table_size (vtl->routes) > 0 ) {
0d2b891f
RN
2676 trw_layer_add_sublayer_routes ( vtl, vt, layer_iter );
2677
2678 pass_along[0] = &(vtl->routes_iter);
2679 pass_along[4] = GINT_TO_POINTER(VIK_TRW_LAYER_SUBLAYER_ROUTE);
2680
2681 g_hash_table_foreach ( vtl->routes, (GHFunc) trw_layer_realize_track, pass_along );
2682
2683 vik_treeview_item_set_visible ( (VikTreeview *) vt, &(vtl->routes_iter), vtl->routes_visible );
2684 }
2685
cd3f311e 2686 if ( g_hash_table_size (vtl->waypoints) > 0 ) {
0d2b891f 2687 trw_layer_add_sublayer_waypoints ( vtl, vt, layer_iter );
cd3f311e
RN
2688
2689 pass_along[0] = &(vtl->waypoints_iter);
2690 pass_along[4] = GINT_TO_POINTER(VIK_TRW_LAYER_SUBLAYER_WAYPOINT);
2691
2692 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_realize_waypoint, pass_along );
2693
2694 vik_treeview_item_set_visible ( (VikTreeview *) vt, &(vtl->waypoints_iter), vtl->waypoints_visible );
2695 }
0d2b891f 2696
50a14534
EB
2697}
2698
a7cd93ac 2699static gboolean trw_layer_sublayer_toggle_visible ( VikTrwLayer *l, gint subtype, gpointer sublayer )
50a14534
EB
2700{
2701 switch ( subtype )
2702 {
2703 case VIK_TRW_LAYER_SUBLAYER_TRACKS: return (l->tracks_visible ^= 1);
2704 case VIK_TRW_LAYER_SUBLAYER_WAYPOINTS: return (l->waypoints_visible ^= 1);
0d2b891f 2705 case VIK_TRW_LAYER_SUBLAYER_ROUTES: return (l->routes_visible ^= 1);
50a14534
EB
2706 case VIK_TRW_LAYER_SUBLAYER_TRACK:
2707 {
2708 VikTrack *t = g_hash_table_lookup ( l->tracks, sublayer );
2709 if (t)
2710 return (t->visible ^= 1);
2711 else
2712 return TRUE;
2713 }
2714 case VIK_TRW_LAYER_SUBLAYER_WAYPOINT:
2715 {
2716 VikWaypoint *t = g_hash_table_lookup ( l->waypoints, sublayer );
2717 if (t)
2718 return (t->visible ^= 1);
2719 else
2720 return TRUE;
2721 }
0d2b891f
RN
2722 case VIK_TRW_LAYER_SUBLAYER_ROUTE:
2723 {
2724 VikTrack *t = g_hash_table_lookup ( l->routes, sublayer );
2725 if (t)
2726 return (t->visible ^= 1);
2727 else
2728 return TRUE;
2729 }
9351abc4 2730 default: break;
50a14534
EB
2731 }
2732 return TRUE;
2733}
2734
04f36d92
RN
2735/*
2736 * Return a property about tracks for this layer
2737 */
2738gint vik_trw_layer_get_property_tracks_line_thickness ( VikTrwLayer *vtl )
2739{
2740 return vtl->line_thickness;
2741}
2742
cb89c5a5
RN
2743// Structure to hold multiple track information for a layer
2744typedef struct {
2745 gdouble length;
2746 time_t start_time;
2747 time_t end_time;
2748 gint duration;
2749} tooltip_tracks;
2750
2751/*
2752 * Build up layer multiple track information via updating the tooltip_tracks structure
2753 */
2754static void trw_layer_tracks_tooltip ( const gchar *name, VikTrack *tr, tooltip_tracks *tt )
2755{
2756 tt->length = tt->length + vik_track_get_length (tr);
2757
2758 // Ensure times are available
2759 if ( tr->trackpoints &&
215ebe59
RN
2760 vik_track_get_tp_first(tr)->has_timestamp &&
2761 vik_track_get_tp_last(tr)->has_timestamp ) {
cb89c5a5
RN
2762
2763 time_t t1, t2;
215ebe59
RN
2764 t1 = vik_track_get_tp_first(tr)->timestamp;
2765 t2 = vik_track_get_tp_last(tr)->timestamp;
cb89c5a5
RN
2766
2767 // Assume never actually have a track with a time of 0 (1st Jan 1970)
2768 // Hence initialize to the first 'proper' value
2769 if ( tt->start_time == 0 )
2770 tt->start_time = t1;
2771 if ( tt->end_time == 0 )
2772 tt->end_time = t2;
2773
2774 // Update find the earliest / last times
2775 if ( t1 < tt->start_time )
2776 tt->start_time = t1;
2777 if ( t2 > tt->end_time )
2778 tt->end_time = t2;
2779
2780 // Keep track of total time
2781 // there maybe gaps within a track (eg segments)
2782 // but this should be generally good enough for a simple indicator
2783 tt->duration = tt->duration + (int)(t2-t1);
2784 }
2785}
2786
2787/*
2788 * Generate tooltip text for the layer.
2789 * This is relatively complicated as it considers information for
2790 * no tracks, a single track or multiple tracks
2791 * (which may or may not have timing information)
2792 */
2793static const gchar* trw_layer_layer_tooltip ( VikTrwLayer *vtl )
2794{
2795 gchar tbuf1[32];
2796 gchar tbuf2[64];
2797 gchar tbuf3[64];
2798 gchar tbuf4[10];
2799 tbuf1[0] = '\0';
2800 tbuf2[0] = '\0';
2801 tbuf3[0] = '\0';
2802 tbuf4[0] = '\0';
2803
2804 static gchar tmp_buf[128];
2805 tmp_buf[0] = '\0';
2806
2807 // For compact date format I'm using '%x' [The preferred date representation for the current locale without the time.]
2808
2809 // Safety check - I think these should always be valid
2810 if ( vtl->tracks && vtl->waypoints ) {
63959706 2811 tooltip_tracks tt = { 0.0, 0, 0, 0 };
cb89c5a5
RN
2812 g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_tracks_tooltip, &tt );
2813
2814 GDate* gdate_start = g_date_new ();
2815 g_date_set_time_t (gdate_start, tt.start_time);
2816
2817 GDate* gdate_end = g_date_new ();
2818 g_date_set_time_t (gdate_end, tt.end_time);
2819
2820 if ( g_date_compare (gdate_start, gdate_end) ) {
2821 // Dates differ so print range on separate line
2822 g_date_strftime (tbuf1, sizeof(tbuf1), "%x", gdate_start);
2823 g_date_strftime (tbuf2, sizeof(tbuf2), "%x", gdate_end);
2824 g_snprintf (tbuf3, sizeof(tbuf3), "%s to %s\n", tbuf1, tbuf2);
2825 }
2826 else {
2827 // Same date so just show it and keep rest of text on the same line - provided it's a valid time!
2828 if ( tt.start_time != 0 )
2829 g_date_strftime (tbuf3, sizeof(tbuf3), "%x: ", gdate_start);
2830 }
2831
2832 tbuf2[0] = '\0';
2833 if ( tt.length > 0.0 ) {
2834 gdouble len_in_units;
2835
2836 // Setup info dependent on distance units
2837 if ( a_vik_get_units_distance() == VIK_UNITS_DISTANCE_MILES ) {
2838 g_snprintf (tbuf4, sizeof(tbuf4), "miles");
a492ff13 2839 len_in_units = VIK_METERS_TO_MILES(tt.length);
cb89c5a5
RN
2840 }
2841 else {
2842 g_snprintf (tbuf4, sizeof(tbuf4), "kms");
2843 len_in_units = tt.length/1000.0;
2844 }
2845
2846 // Timing information if available
2847 tbuf1[0] = '\0';
2848 if ( tt.duration > 0 ) {
2849 g_snprintf (tbuf1, sizeof(tbuf1),
2850 _(" in %d:%02d hrs:mins"),
2851 (int)round(tt.duration/3600), (int)round((tt.duration/60)%60));
2852 }
2853 g_snprintf (tbuf2, sizeof(tbuf2),
2854 _("\n%sTotal Length %.1f %s%s"),
2855 tbuf3, len_in_units, tbuf4, tbuf1);
2856 }
2857
2858 // Put together all the elements to form compact tooltip text
2859 g_snprintf (tmp_buf, sizeof(tmp_buf),
0d2b891f
RN
2860 _("Tracks: %d - Waypoints: %d - Routes: %d%s"),
2861 g_hash_table_size (vtl->tracks), g_hash_table_size (vtl->waypoints), g_hash_table_size (vtl->routes), tbuf2);
cb89c5a5
RN
2862
2863 g_date_free (gdate_start);
2864 g_date_free (gdate_end);
2865
2866 }
2867
2868 return tmp_buf;
2869}
2870
c7060c4e
RN
2871static const gchar* trw_layer_sublayer_tooltip ( VikTrwLayer *l, gint subtype, gpointer sublayer )
2872{
2873 switch ( subtype )
2874 {
cc837b7a
RN
2875 case VIK_TRW_LAYER_SUBLAYER_TRACKS:
2876 {
2877 // Very simple tooltip - may expand detail in the future...
2878 static gchar tmp_buf[32];
2879 g_snprintf (tmp_buf, sizeof(tmp_buf),
2880 _("Tracks: %d"),
2881 g_hash_table_size (l->tracks));
2882 return tmp_buf;
2883 }
2884 break;
0d2b891f
RN
2885 case VIK_TRW_LAYER_SUBLAYER_ROUTES:
2886 {
2887 // Very simple tooltip - may expand detail in the future...
2888 static gchar tmp_buf[32];
2889 g_snprintf (tmp_buf, sizeof(tmp_buf),
2890 _("Routes: %d"),
2891 g_hash_table_size (l->routes));
2892 return tmp_buf;
2893 }
2894 break;
2895
2896 case VIK_TRW_LAYER_SUBLAYER_ROUTE:
2897 // Same tooltip for a route
c7060c4e
RN
2898 case VIK_TRW_LAYER_SUBLAYER_TRACK:
2899 {
0d2b891f
RN
2900 VikTrack *tr;
2901 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
2902 tr = g_hash_table_lookup ( l->tracks, sublayer );
2903 else
2904 tr = g_hash_table_lookup ( l->routes, sublayer );
2905
c7060c4e
RN
2906 if ( tr ) {
2907 // Could be a better way of handling strings - but this works...
2908 gchar time_buf1[20];
2909 gchar time_buf2[20];
2910 time_buf1[0] = '\0';
2911 time_buf2[0] = '\0';
2912 static gchar tmp_buf[100];
2913 // Compact info: Short date eg (11/20/99), duration and length
2914 // Hopefully these are the things that are most useful and so promoted into the tooltip
215ebe59 2915 if ( tr->trackpoints && vik_track_get_tp_first(tr)->has_timestamp ) {
c7060c4e 2916 // %x The preferred date representation for the current locale without the time.
215ebe59
RN
2917 strftime (time_buf1, sizeof(time_buf1), "%x: ", gmtime(&(vik_track_get_tp_first(tr)->timestamp)));
2918 if ( vik_track_get_tp_last(tr)->has_timestamp ) {
2919 gint dur = ( (vik_track_get_tp_last(tr)->timestamp) - (vik_track_get_tp_first(tr)->timestamp) );
c7060c4e 2920 if ( dur > 0 )
cb89c5a5 2921 g_snprintf ( time_buf2, sizeof(time_buf2), _("- %d:%02d hrs:mins"), (int)round(dur/3600), (int)round((dur/60)%60) );
c7060c4e
RN
2922 }
2923 }
2924 // Get length and consider the appropriate distance units
2925 gdouble tr_len = vik_track_get_length(tr);
2926 vik_units_distance_t dist_units = a_vik_get_units_distance ();
2927 switch (dist_units) {
2928 case VIK_UNITS_DISTANCE_KILOMETRES:
2929 g_snprintf (tmp_buf, sizeof(tmp_buf), _("%s%.1f km %s"), time_buf1, tr_len/1000.0, time_buf2);
2930 break;
2931 case VIK_UNITS_DISTANCE_MILES:
a492ff13 2932 g_snprintf (tmp_buf, sizeof(tmp_buf), _("%s%.1f miles %s"), time_buf1, VIK_METERS_TO_MILES(tr_len), time_buf2);
c7060c4e
RN
2933 break;
2934 default:
2935 break;
2936 }
2937 return tmp_buf;
2938 }
2939 }
2940 break;
cc837b7a
RN
2941 case VIK_TRW_LAYER_SUBLAYER_WAYPOINTS:
2942 {
2943 // Very simple tooltip - may expand detail in the future...
2944 static gchar tmp_buf[32];
2945 g_snprintf (tmp_buf, sizeof(tmp_buf),
2946 _("Waypoints: %d"),
2947 g_hash_table_size (l->waypoints));
2948 return tmp_buf;
2949 }
2950 break;
c7060c4e
RN
2951 case VIK_TRW_LAYER_SUBLAYER_WAYPOINT:
2952 {
2953 VikWaypoint *w = g_hash_table_lookup ( l->waypoints, sublayer );
2954 // NB It's OK to return NULL
382b8734
RN
2955 if ( w ) {
2956 if ( w->comment )
2957 return w->comment;
2958 else
2959 return w->description;
2960 }
c7060c4e
RN
2961 }
2962 break;
2963 default: break;
2964 }
2965 return NULL;
2966}
2967
60626a3e
RN
2968#define VIK_SETTINGS_TRKPT_SELECTED_STATUSBAR_FORMAT "trkpt_selected_statusbar_format"
2969
2970/**
2971 * set_statusbar_msg_info_trkpt:
2972 *
2973 * Function to show track point information on the statusbar
2974 * Items displayed is controlled by the settings format code
95d1b757
RN
2975 */
2976static void set_statusbar_msg_info_trkpt ( VikTrwLayer *vtl, VikTrackpoint *trkpt )
2977{
60626a3e
RN
2978 gchar *statusbar_format_code = NULL;
2979 gboolean need2free = FALSE;
2980 if ( !a_settings_get_string ( VIK_SETTINGS_TRKPT_SELECTED_STATUSBAR_FORMAT, &statusbar_format_code ) ) {
2981 // Otherwise use default
b45865b4 2982 statusbar_format_code = g_strdup ( "KEATDN" );
60626a3e 2983 need2free = TRUE;
95d1b757
RN
2984 }
2985
60626a3e 2986 gchar *msg = vu_trackpoint_formatted_message ( statusbar_format_code, trkpt, NULL, vtl->current_tp_track );
95d1b757 2987 vik_statusbar_set_message ( vik_window_get_statusbar (VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl))), VIK_STATUSBAR_INFO, msg );
95d1b757 2988 g_free ( msg );
60626a3e
RN
2989
2990 if ( need2free )
2991 g_free ( statusbar_format_code );
95d1b757
RN
2992}
2993
2994/*
2995 * Function to show basic waypoint information on the statusbar
2996 */
2997static void set_statusbar_msg_info_wpt ( VikTrwLayer *vtl, VikWaypoint *wpt )
2998{
2999 gchar tmp_buf1[64];
3000 switch (a_vik_get_units_height ()) {
3001 case VIK_UNITS_HEIGHT_FEET:
3002 g_snprintf(tmp_buf1, sizeof(tmp_buf1), _("Wpt: Alt %dft"), (int)round(VIK_METERS_TO_FEET(wpt->altitude)));
3003 break;
3004 default:
3005 //VIK_UNITS_HEIGHT_METRES:
3006 g_snprintf(tmp_buf1, sizeof(tmp_buf1), _("Wpt: Alt %dm"), (int)round(wpt->altitude));
3007 }
3008
3009 // Position part
3010 // Position is put last, as this bit is most likely not to be seen if the display is not big enough,
3011 // one can easily use the current pointer position to see this if needed
3012 gchar *lat = NULL, *lon = NULL;
3013 static struct LatLon ll;
3014 vik_coord_to_latlon (&(wpt->coord), &ll);
3015 a_coords_latlon_to_string ( &ll, &lat, &lon );
3016
3017 // Combine parts to make overall message
3018 gchar *msg;
3019 if ( wpt->comment )
3020 // Add comment if available
3021 msg = g_strdup_printf ( _("%s | %s %s | Comment: %s"), tmp_buf1, lat, lon, wpt->comment );
3022 else
3023 msg = g_strdup_printf ( _("%s | %s %s"), tmp_buf1, lat, lon );
3024 vik_statusbar_set_message ( vik_window_get_statusbar (VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl))), VIK_STATUSBAR_INFO, msg );
3025 g_free ( lat );
3026 g_free ( lon );
3027 g_free ( msg );
3028}
3029
a5dcfdb7
RN
3030/**
3031 * General layer selection function, find out which bit is selected and take appropriate action
3032 */
a7cd93ac 3033static gboolean trw_layer_selected ( VikTrwLayer *l, gint subtype, gpointer sublayer, gint type, gpointer vlp )
a5dcfdb7 3034{
c95d6b00 3035 // Reset
c9570f86
RN
3036 l->current_wp = NULL;
3037 l->current_wp_id = NULL;
c95d6b00
RN
3038 trw_layer_cancel_current_tp ( l, FALSE );
3039
95d1b757
RN
3040 // Clear statusbar
3041 vik_statusbar_set_message ( vik_window_get_statusbar (VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(l))), VIK_STATUSBAR_INFO, "" );
3042
a5dcfdb7
RN
3043 switch ( type )
3044 {
3045 case VIK_TREEVIEW_TYPE_LAYER:
3046 {
3047 vik_window_set_selected_trw_layer ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l), l );
3048 /* Mark for redraw */
3049 return TRUE;
3050 }
3051 break;
3052
3053 case VIK_TREEVIEW_TYPE_SUBLAYER:
3054 {
3055 switch ( subtype )
3056 {
3057 case VIK_TRW_LAYER_SUBLAYER_TRACKS:
3058 {
113c74f6 3059 vik_window_set_selected_tracks ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l), l->tracks, l );
a5dcfdb7
RN
3060 /* Mark for redraw */
3061 return TRUE;
3062 }
3063 break;
3064 case VIK_TRW_LAYER_SUBLAYER_TRACK:
3065 {
95d1b757 3066 VikTrack *track = g_hash_table_lookup ( l->tracks, sublayer );
b16effab 3067 vik_window_set_selected_track ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l), (gpointer)track, l );
a5dcfdb7
RN
3068 /* Mark for redraw */
3069 return TRUE;
3070 }
3071 break;
0d2b891f
RN
3072 case VIK_TRW_LAYER_SUBLAYER_ROUTES:
3073 {
3074 vik_window_set_selected_tracks ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l), l->routes, l );
3075 /* Mark for redraw */
3076 return TRUE;
3077 }
3078 break;
3079 case VIK_TRW_LAYER_SUBLAYER_ROUTE:
3080 {
3081 VikTrack *track = g_hash_table_lookup ( l->routes, sublayer );
3082 vik_window_set_selected_track ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l), (gpointer)track, l );
3083 /* Mark for redraw */
3084 return TRUE;
3085 }
3086 break;
a5dcfdb7
RN
3087 case VIK_TRW_LAYER_SUBLAYER_WAYPOINTS:
3088 {
113c74f6 3089 vik_window_set_selected_waypoints ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l), l->waypoints, l );
a5dcfdb7
RN
3090 /* Mark for redraw */
3091 return TRUE;
3092 }
3093 break;
3094 case VIK_TRW_LAYER_SUBLAYER_WAYPOINT:
3095 {
b16effab
RN
3096 VikWaypoint *wpt = g_hash_table_lookup ( l->waypoints, sublayer );
3097 if ( wpt ) {
3098 vik_window_set_selected_waypoint ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l), (gpointer)wpt, l );
3099 // Show some waypoint info
3100 set_statusbar_msg_info_wpt ( l, wpt );
3101 /* Mark for redraw */
3102 return TRUE;
3103 }
a5dcfdb7
RN
3104 }
3105 break;
3106 default:
3107 {
3108 return vik_window_clear_highlight ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l) );
3109 }
3110 break;
3111 }
3112 return FALSE;
3113 }
3114 break;
3115
3116 default:
3117 return vik_window_clear_highlight ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l) );
3118 break;
3119 }
3120}
c7060c4e 3121
50a14534
EB
3122GHashTable *vik_trw_layer_get_tracks ( VikTrwLayer *l )
3123{
3124 return l->tracks;
3125}
3126
0d2b891f
RN
3127GHashTable *vik_trw_layer_get_routes ( VikTrwLayer *l )
3128{
3129 return l->routes;
3130}
3131
50a14534
EB
3132GHashTable *vik_trw_layer_get_waypoints ( VikTrwLayer *l )
3133{
3134 return l->waypoints;
3135}
3136
c98e2f73
RN
3137GHashTable *vik_trw_layer_get_tracks_iters ( VikTrwLayer *vtl )
3138{
3139 return vtl->tracks_iters;
3140}
3141
3142GHashTable *vik_trw_layer_get_routes_iters ( VikTrwLayer *vtl )
3143{
3144 return vtl->routes_iters;
3145}
3146
3147GHashTable *vik_trw_layer_get_waypoints_iters ( VikTrwLayer *vtl )
3148{
3149 return vtl->waypoints;
3150}
3151
bec82ff5
RN
3152gboolean vik_trw_layer_is_empty ( VikTrwLayer *vtl )
3153{
3154 return ! ( g_hash_table_size ( vtl->tracks ) ||
3155 g_hash_table_size ( vtl->routes ) ||
3156 g_hash_table_size ( vtl->waypoints ) );
3157}
3158
0082c2f6
RN
3159gboolean vik_trw_layer_get_tracks_visibility ( VikTrwLayer *vtl )
3160{
3161 return vtl->tracks_visible;
3162}
3163
3164gboolean vik_trw_layer_get_routes_visibility ( VikTrwLayer *vtl )
3165{
3166 return vtl->routes_visible;
3167}
3168
3169gboolean vik_trw_layer_get_waypoints_visibility ( VikTrwLayer *vtl )
3170{
3171 return vtl->waypoints_visible;
3172}
3173
c9570f86
RN
3174/*
3175 * ATM use a case sensitive find
3176 * Finds the first one
3177 */
3178static gboolean trw_layer_waypoint_find ( const gpointer id, const VikWaypoint *wp, const gchar *name )
3179{
3180 if ( wp && wp->name )
3181 if ( ! strcmp ( wp->name, name ) )
3182 return TRUE;
3183 return FALSE;
3184}
3185
3186/*
3187 * Get waypoint by name - not guaranteed to be unique
3188 * Finds the first one
3189 */
3190VikWaypoint *vik_trw_layer_get_waypoint ( VikTrwLayer *vtl, const gchar *name )
3191{
3192 return g_hash_table_find ( vtl->waypoints, (GHRFunc) trw_layer_waypoint_find, (gpointer) name );
3193}
3194
ce4bd1cf
RN
3195/*
3196 * ATM use a case sensitive find
3197 * Finds the first one
3198 */
3199static gboolean trw_layer_track_find ( const gpointer id, const VikTrack *trk, const gchar *name )
3200{
3201 if ( trk && trk->name )
3202 if ( ! strcmp ( trk->name, name ) )
3203 return TRUE;
3204 return FALSE;
3205}
3206
3207/*
3208 * Get track by name - not guaranteed to be unique
3209 * Finds the first one
3210 */
c9570f86
RN
3211VikTrack *vik_trw_layer_get_track ( VikTrwLayer *vtl, const gchar *name )
3212{
ce4bd1cf 3213 return g_hash_table_find ( vtl->tracks, (GHRFunc) trw_layer_track_find, (gpointer) name );
c9570f86
RN
3214}
3215
0d2b891f
RN
3216/*
3217 * Get route by name - not guaranteed to be unique
3218 * Finds the first one
3219 */
3220VikTrack *vik_trw_layer_get_route ( VikTrwLayer *vtl, const gchar *name )
3221{
3222 return g_hash_table_find ( vtl->routes, (GHRFunc) trw_layer_track_find, (gpointer) name );
3223}
3224
9e212bfc 3225static void trw_layer_find_maxmin_tracks ( const gpointer id, const VikTrack *trk, struct LatLon maxmin[2] )
50a14534 3226{
a8e8ffe1
RN
3227 if ( trk->bbox.north > maxmin[0].lat || maxmin[0].lat == 0.0 )
3228 maxmin[0].lat = trk->bbox.north;
3229 if ( trk->bbox.south < maxmin[1].lat || maxmin[1].lat == 0.0 )
3230 maxmin[1].lat = trk->bbox.south;
3231 if ( trk->bbox.east > maxmin[0].lon || maxmin[0].lon == 0.0 )
3232 maxmin[0].lon = trk->bbox.east;
3233 if ( trk->bbox.west < maxmin[1].lon || maxmin[1].lon == 0.0 )
3234 maxmin[1].lon = trk->bbox.west;
50a14534
EB
3235}
3236
165a4fa9
HR
3237static void trw_layer_find_maxmin (VikTrwLayer *vtl, struct LatLon maxmin[2])
3238{
0d2b891f 3239 // Continually reuse maxmin to find the latest maximum and minimum values
a8e8ffe1
RN
3240 // First set to waypoints bounds
3241 maxmin[0].lat = vtl->waypoints_bbox.north;
3242 maxmin[1].lat = vtl->waypoints_bbox.south;
3243 maxmin[0].lon = vtl->waypoints_bbox.east;
3244 maxmin[1].lon = vtl->waypoints_bbox.west;
0d2b891f
RN
3245 g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_find_maxmin_tracks, maxmin );
3246 g_hash_table_foreach ( vtl->routes, (GHFunc) trw_layer_find_maxmin_tracks, maxmin );
165a4fa9 3247}
50a14534
EB
3248
3249gboolean vik_trw_layer_find_center ( VikTrwLayer *vtl, VikCoord *dest )
3250{
3251 /* 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... */
3252 struct LatLon maxmin[2] = { {0.0,0.0}, {0.0,0.0} };
165a4fa9 3253 trw_layer_find_maxmin (vtl, maxmin);
50a14534
EB
3254 if (maxmin[0].lat == 0.0 && maxmin[0].lon == 0.0 && maxmin[1].lat == 0.0 && maxmin[1].lon == 0.0)
3255 return FALSE;
3256 else
3257 {
3258 struct LatLon average = { (maxmin[0].lat+maxmin[1].lat)/2, (maxmin[0].lon+maxmin[1].lon)/2 };
3259 vik_coord_load_from_latlon ( dest, vtl->coord_mode, &average );
3260 return TRUE;
3261 }
3262}
3263
1c2083ba 3264static void trw_layer_centerize ( menu_array_layer values )
50a14534 3265{
1c2083ba 3266 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
50a14534 3267 VikCoord coord;
1c2083ba
RN
3268 if ( vik_trw_layer_find_center ( vtl, &coord ) )
3269 goto_coord ( values[MA_VLP], NULL, NULL, &coord );
50a14534 3270 else
1c2083ba 3271 a_dialog_info_msg ( VIK_GTK_WINDOW_FROM_LAYER(vtl), _("This layer has no waypoints or trackpoints.") );
50a14534
EB
3272}
3273
c98e2f73 3274void trw_layer_zoom_to_show_latlons ( VikTrwLayer *vtl, VikViewport *vvp, struct LatLon maxmin[2] )
c5638216
RN
3275{
3276 /* First set the center [in case previously viewing from elsewhere] */
3277 /* Then loop through zoom levels until provided positions are in view */
3278 /* This method is not particularly fast - but should work well enough */
3279 struct LatLon average = { (maxmin[0].lat+maxmin[1].lat)/2, (maxmin[0].lon+maxmin[1].lon)/2 };
3280 VikCoord coord;
3281 vik_coord_load_from_latlon ( &coord, vtl->coord_mode, &average );
be5554c5 3282 vik_viewport_set_center_coord ( vvp, &coord, TRUE );
c5638216
RN
3283
3284 /* Convert into definite 'smallest' and 'largest' positions */
3285 struct LatLon minmin;
3286 if ( maxmin[0].lat < maxmin[1].lat )
3287 minmin.lat = maxmin[0].lat;
3288 else
3289 minmin.lat = maxmin[1].lat;
3290
3291 struct LatLon maxmax;
3292 if ( maxmin[0].lon > maxmin[1].lon )
3293 maxmax.lon = maxmin[0].lon;
3294 else
3295 maxmax.lon = maxmin[1].lon;
3296
3297 /* Never zoom in too far - generally not that useful, as too close ! */
3298 /* Always recalculate the 'best' zoom level */
3299 gdouble zoom = 1.0;
3300 vik_viewport_set_zoom ( vvp, zoom );
3301
3302 gdouble min_lat, max_lat, min_lon, max_lon;
3303 /* Should only be a maximum of about 18 iterations from min to max zoom levels */
3304 while ( zoom <= VIK_VIEWPORT_MAX_ZOOM ) {
3305 vik_viewport_get_min_max_lat_lon ( vvp, &min_lat, &max_lat, &min_lon, &max_lon );
3306 /* NB I think the logic used in this test to determine if the bounds is within view
3307 fails if track goes across 180 degrees longitude.
3308 Hopefully that situation is not too common...
3309 Mind you viking doesn't really do edge locations to well anyway */
3310 if ( min_lat < minmin.lat &&
3311 max_lat > minmin.lat &&
3312 min_lon < maxmax.lon &&
3313 max_lon > maxmax.lon )
3314 /* Found within zoom level */
3315 break;
3316
3317 /* Try next */
3318 zoom = zoom * 2;
3319 vik_viewport_set_zoom ( vvp, zoom );
3320 }
3321}
3322
3323gboolean vik_trw_layer_auto_set_view ( VikTrwLayer *vtl, VikViewport *vvp )
3324{
3325 /* TODO: what if there's only one waypoint @ 0,0, it will think nothing found. */
3326 struct LatLon maxmin[2] = { {0.0,0.0}, {0.0,0.0} };
3327 trw_layer_find_maxmin (vtl, maxmin);
3328 if (maxmin[0].lat == 0.0 && maxmin[0].lon == 0.0 && maxmin[1].lat == 0.0 && maxmin[1].lon == 0.0)
3329 return FALSE;
3330 else {
3331 trw_layer_zoom_to_show_latlons ( vtl, vvp, maxmin );
3332 return TRUE;
3333 }
3334}
3335
1c2083ba 3336static void trw_layer_auto_view ( menu_array_layer values )
5a10c240 3337{
1c2083ba
RN
3338 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
3339 VikLayersPanel *vlp = VIK_LAYERS_PANEL(values[MA_VLP]);
3340 if ( vik_trw_layer_auto_set_view ( vtl, vik_layers_panel_get_viewport (vlp) ) ) {
3341 vik_layers_panel_emit_update ( vlp );
5a10c240
RN
3342 }
3343 else
1c2083ba 3344 a_dialog_info_msg ( VIK_GTK_WINDOW_FROM_LAYER(vtl), _("This layer has no waypoints or trackpoints.") );
5a10c240
RN
3345}
3346
e4a11fbe 3347static void trw_layer_export_gpspoint ( menu_array_layer values )
50a14534 3348{
e4a11fbe 3349 gchar *auto_save_name = append_file_ext ( vik_layer_get_name(VIK_LAYER(values[MA_VTL])), FILE_TYPE_GPSPOINT );
6e4a49aa 3350
e4a11fbe 3351 vik_trw_layer_export ( VIK_TRW_LAYER (values[MA_VTL]), _("Export Layer"), auto_save_name, NULL, FILE_TYPE_GPSPOINT );
50a14534 3352
e4a11fbe 3353 g_free ( auto_save_name );
50a14534
EB
3354}
3355
1c2083ba 3356static void trw_layer_export_gpsmapper ( menu_array_layer values )
50a14534 3357{
e4a11fbe
GB
3358 gchar *auto_save_name = append_file_ext ( vik_layer_get_name(VIK_LAYER(values[MA_VTL])), FILE_TYPE_GPSMAPPER );
3359
3360 vik_trw_layer_export ( VIK_TRW_LAYER (values[MA_VTL]), _("Export Layer"), auto_save_name, NULL, FILE_TYPE_GPSMAPPER );
3361
3362 g_free ( auto_save_name );
50a14534
EB
3363}
3364
1c2083ba 3365static void trw_layer_export_gpx ( menu_array_layer values )
561e6ad0 3366{
e4a11fbe 3367 gchar *auto_save_name = append_file_ext ( vik_layer_get_name(VIK_LAYER(values[MA_VTL])), FILE_TYPE_GPX );
18d0a1ed 3368
e4a11fbe 3369 vik_trw_layer_export ( VIK_TRW_LAYER (values[MA_VTL]), _("Export Layer"), auto_save_name, NULL, FILE_TYPE_GPX );
18d0a1ed
RN
3370
3371 g_free ( auto_save_name );
7f6757c4
RN
3372}
3373
1c2083ba 3374static void trw_layer_export_kml ( menu_array_layer values )
ba9d0a00 3375{
e4a11fbe 3376 gchar *auto_save_name = append_file_ext ( vik_layer_get_name(VIK_LAYER(values[MA_VTL])), FILE_TYPE_KML );
ba9d0a00 3377
e4a11fbe 3378 vik_trw_layer_export ( VIK_TRW_LAYER (values[MA_VTL]), _("Export Layer"), auto_save_name, NULL, FILE_TYPE_KML );
ba9d0a00
RN
3379
3380 g_free ( auto_save_name );
3381}
3382
17720e63
GB
3383static void trw_layer_export_babel ( gpointer layer_and_vlp[2] )
3384{
c3c4b41c 3385 const gchar *auto_save_name = vik_layer_get_name(VIK_LAYER(layer_and_vlp[0]));
17720e63
GB
3386 vik_trw_layer_export_gpsbabel ( VIK_TRW_LAYER (layer_and_vlp[0]), _("Export Layer"), auto_save_name );
3387}
ccccf356 3388
1c2083ba 3389static void trw_layer_export_external_gpx_1 ( menu_array_layer values )
ccccf356 3390{
e4a11fbe 3391 vik_trw_layer_export_external_gpx ( VIK_TRW_LAYER (values[MA_VTL]), a_vik_get_external_gpx_program_1() );
ccccf356
RN
3392}
3393
1c2083ba 3394static void trw_layer_export_external_gpx_2 ( menu_array_layer values )
ff02058b 3395{
e4a11fbe 3396 vik_trw_layer_export_external_gpx ( VIK_TRW_LAYER (values[MA_VTL]), a_vik_get_external_gpx_program_2() );
ff02058b
RN
3397}
3398
19782ffd 3399static void trw_layer_export_gpx_track ( menu_array_sublayer values )
7f6757c4 3400{
19782ffd 3401 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
0d2b891f 3402 VikTrack *trk;
19782ffd
RN
3403 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
3404 trk = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
0d2b891f 3405 else
19782ffd 3406 trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
ce4bd1cf
RN
3407
3408 if ( !trk || !trk->name )
3409 return;
7f6757c4 3410
e4a11fbe 3411 gchar *auto_save_name = append_file_ext ( trk->name, FILE_TYPE_GPX );
7f6757c4 3412
0f704e14 3413 gchar *label = NULL;
19782ffd 3414 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
0f704e14
GB
3415 label = _("Export Route as GPX");
3416 else
3417 label = _("Export Track as GPX");
e4a11fbe 3418 vik_trw_layer_export ( VIK_TRW_LAYER (values[MA_VTL]), label, auto_save_name, trk, FILE_TYPE_GPX );
7f6757c4
RN
3419
3420 g_free ( auto_save_name );
561e6ad0
EB
3421}
3422
d18f2933 3423gboolean trw_layer_waypoint_find_uuid ( const gpointer id, const VikWaypoint *wp, gpointer udata )
c9570f86
RN
3424{
3425 wpu_udata *user_data = udata;
3426 if ( wp == user_data->wp ) {
3427 user_data->uuid = id;
3428 return TRUE;
3429 }
3430 return FALSE;
3431}
3432
1c2083ba 3433static void trw_layer_goto_wp ( menu_array_layer values )
50a14534 3434{
1c2083ba
RN
3435 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
3436 VikLayersPanel *vlp = VIK_LAYERS_PANEL(values[MA_VLP]);
a77d62d8 3437 GtkWidget *dia = gtk_dialog_new_with_buttons (_("Find"),
1c2083ba 3438 VIK_GTK_WINDOW_FROM_LAYER(vtl),
50a14534
EB
3439 GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
3440 GTK_STOCK_CANCEL,
3441 GTK_RESPONSE_REJECT,
3442 GTK_STOCK_OK,
3443 GTK_RESPONSE_ACCEPT,
3444 NULL);
3445
3446 GtkWidget *label, *entry;
4c77d5e0 3447 label = gtk_label_new(_("Waypoint Name:"));
50a14534
EB
3448 entry = gtk_entry_new();
3449
9b082b39
RN
3450 gtk_box_pack_start(GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dia))), label, FALSE, FALSE, 0);
3451 gtk_box_pack_start(GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dia))), entry, FALSE, FALSE, 0);
50a14534
EB
3452 gtk_widget_show_all ( label );
3453 gtk_widget_show_all ( entry );
3454
7ea3cb11
RN
3455 gtk_dialog_set_default_response ( GTK_DIALOG(dia), GTK_RESPONSE_ACCEPT );
3456
50a14534
EB
3457 while ( gtk_dialog_run ( GTK_DIALOG(dia) ) == GTK_RESPONSE_ACCEPT )
3458 {
c9570f86
RN
3459 gchar *name = g_strdup(gtk_entry_get_text(GTK_ENTRY(entry)));
3460 // Find *first* wp with the given name
1c2083ba 3461 VikWaypoint *wp = vik_trw_layer_get_waypoint ( vtl, name );
50a14534 3462
c9570f86 3463 if ( !wp )
1c2083ba 3464 a_dialog_error_msg ( VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Waypoint not found in this layer.") );
50a14534
EB
3465 else
3466 {
be5554c5 3467 vik_viewport_set_center_coord ( vik_layers_panel_get_viewport(vlp), &(wp->coord), TRUE );
1c2083ba 3468 vik_layers_panel_emit_update ( vlp );
c9570f86
RN
3469
3470 // Find and select on the side panel
3471 wpu_udata udata;
3472 udata.wp = wp;
3473 udata.uuid = NULL;
3474
3475 // Hmmm, want key of it
1c2083ba 3476 gpointer *wpf = g_hash_table_find ( vtl->waypoints, (GHRFunc) trw_layer_waypoint_find_uuid, (gpointer) &udata );
c9570f86
RN
3477
3478 if ( wpf && udata.uuid ) {
1c2083ba
RN
3479 GtkTreeIter *it = g_hash_table_lookup ( vtl->waypoints_iters, udata.uuid );
3480 vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, it, TRUE );
c9570f86
RN
3481 }
3482
50a14534
EB
3483 break;
3484 }
3485
c9570f86 3486 g_free ( name );
50a14534
EB
3487
3488 }
3489 gtk_widget_destroy ( dia );
3490}
3491
3492gboolean vik_trw_layer_new_waypoint ( VikTrwLayer *vtl, GtkWindow *w, const VikCoord *def_coord )
3493{
ac1bde8b 3494 gchar *default_name = highest_wp_number_get(vtl);
acaf7113 3495 VikWaypoint *wp = vik_waypoint_new();
ac1bde8b
RN
3496 gchar *returned_name;
3497 gboolean updated;
acaf7113 3498 wp->coord = *def_coord;
d60a672e
RN
3499
3500 // Attempt to auto set height if DEM data is available
a90338e5 3501 vik_waypoint_apply_dem_data ( wp, TRUE );
50a14534 3502
d6175f49 3503 returned_name = a_dialog_waypoint ( w, default_name, vtl, wp, vtl->coord_mode, TRUE, &updated );
c9570f86
RN
3504
3505 if ( returned_name )
50a14534 3506 {
805d282e 3507 wp->visible = TRUE;
ac1bde8b
RN
3508 vik_trw_layer_add_waypoint ( vtl, returned_name, wp );
3509 g_free (default_name);
c9570f86 3510 g_free (returned_name);
50a14534
EB
3511 return TRUE;
3512 }
ac1bde8b 3513 g_free (default_name);
acaf7113 3514 vik_waypoint_free(wp);
50a14534
EB
3515 return FALSE;
3516}
3517
1c2083ba 3518static void trw_layer_new_wikipedia_wp_viewport ( menu_array_layer values )
165a4fa9 3519{
165a4fa9 3520 struct LatLon maxmin[2] = { {0.0,0.0}, {0.0,0.0} };
1c2083ba
RN
3521 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
3522 VikLayersPanel *vlp = VIK_LAYERS_PANEL(values[MA_VLP]);
165a4fa9
HR
3523 VikWindow *vw = (VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl));
3524 VikViewport *vvp = vik_window_viewport(vw);
42d6236f
RN
3525
3526 // Note the order is max part first then min part - thus reverse order of use in min_max function:
3527 vik_viewport_get_min_max_lat_lon ( vvp, &maxmin[1].lat, &maxmin[0].lat, &maxmin[1].lon, &maxmin[0].lon );
120ab662 3528 a_geonames_wikipedia_box((VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl)), vtl, maxmin);
aa0665e5 3529 trw_layer_calculate_bounds_waypoints ( vtl );
120ab662 3530 vik_layers_panel_emit_update ( vlp );
165a4fa9
HR
3531}
3532
1c2083ba 3533static void trw_layer_new_wikipedia_wp_layer ( menu_array_layer values )
165a4fa9 3534{
1c2083ba
RN
3535 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
3536 VikLayersPanel *vlp = VIK_LAYERS_PANEL(values[MA_VLP]);
165a4fa9
HR
3537 struct LatLon maxmin[2] = { {0.0,0.0}, {0.0,0.0} };
3538
3539 trw_layer_find_maxmin (vtl, maxmin);
120ab662 3540 a_geonames_wikipedia_box((VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl)), vtl, maxmin);
aa0665e5 3541 trw_layer_calculate_bounds_waypoints ( vtl );
120ab662 3542 vik_layers_panel_emit_update ( vlp );
165a4fa9
HR
3543}
3544
b3eb3b98 3545#ifdef VIK_CONFIG_GEOTAG
19782ffd 3546static void trw_layer_geotagging_waypoint_mtime_keep ( menu_array_sublayer values )
b3eb3b98 3547{
19782ffd 3548 VikWaypoint *wp = g_hash_table_lookup ( VIK_TRW_LAYER(values[MA_VTL])->waypoints, values[MA_SUBLAYER_ID] );
b3eb3b98
RN
3549 if ( wp )
3550 // Update directly - not changing the mtime
3551 a_geotag_write_exif_gps ( wp->image, wp->coord, wp->altitude, TRUE );
3552}
3553
19782ffd 3554static void trw_layer_geotagging_waypoint_mtime_update ( menu_array_sublayer values )
b3eb3b98 3555{
19782ffd 3556 VikWaypoint *wp = g_hash_table_lookup ( VIK_TRW_LAYER(values[MA_VTL])->waypoints, values[MA_SUBLAYER_ID] );
b3eb3b98
RN
3557 if ( wp )
3558 // Update directly
3559 a_geotag_write_exif_gps ( wp->image, wp->coord, wp->altitude, FALSE );
3560}
3561
3562/*
3563 * Use code in separate file for this feature as reasonably complex
3564 */
19782ffd 3565static void trw_layer_geotagging_track ( menu_array_sublayer values )
b3eb3b98 3566{
19782ffd
RN
3567 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
3568 VikTrack *track = g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
b3eb3b98
RN
3569 // Unset so can be reverified later if necessary
3570 vtl->has_verified_thumbnails = FALSE;
3571
3572 trw_layer_geotag_dialog ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
41f4abac
RN
3573 vtl,
3574 NULL,
3575 track );
3576}
3577
19782ffd 3578static void trw_layer_geotagging_waypoint ( menu_array_sublayer values )
41f4abac 3579{
19782ffd
RN
3580 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
3581 VikWaypoint *wpt = g_hash_table_lookup ( vtl->waypoints, values[MA_SUBLAYER_ID] );
41f4abac
RN
3582
3583 trw_layer_geotag_dialog ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
3584 vtl,
3585 wpt,
3586 NULL );
b3eb3b98
RN
3587}
3588
1c2083ba 3589static void trw_layer_geotagging ( menu_array_layer values )
b3eb3b98 3590{
1c2083ba 3591 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
b3eb3b98
RN
3592 // Unset so can be reverified later if necessary
3593 vtl->has_verified_thumbnails = FALSE;
3594
3595 trw_layer_geotag_dialog ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
41f4abac
RN
3596 vtl,
3597 NULL,
3598 NULL );
b3eb3b98
RN
3599}
3600#endif
3601
16fc32f6
RN
3602// 'Acquires' - Same as in File Menu -> Acquire - applies into the selected TRW Layer //
3603
1c2083ba 3604static void trw_layer_acquire ( menu_array_layer values, VikDataSourceInterface *datasource )
16fc32f6 3605{
1c2083ba
RN
3606 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
3607 VikLayersPanel *vlp = VIK_LAYERS_PANEL(values[MA_VLP]);
16fc32f6
RN
3608 VikWindow *vw = (VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl));
3609 VikViewport *vvp = vik_window_viewport(vw);
3610
995e9fd9
GB
3611 a_acquire ( vw, vlp, vvp, datasource, NULL, NULL );
3612}
3613
3614/*
3615 * Acquire into this TRW Layer straight from GPS Device
3616 */
1c2083ba 3617static void trw_layer_acquire_gps_cb ( menu_array_layer values )
995e9fd9 3618{
16fc32f6 3619 vik_datasource_gps_interface.mode = VIK_DATASOURCE_ADDTOLAYER;
1c2083ba 3620 trw_layer_acquire ( values, &vik_datasource_gps_interface );
16fc32f6
RN
3621}
3622
3623/*
7f95fd54 3624 * Acquire into this TRW Layer from Directions
16fc32f6 3625 */
1c2083ba 3626static void trw_layer_acquire_routing_cb ( menu_array_layer values )
16fc32f6 3627{
1c2083ba 3628 trw_layer_acquire ( values, &vik_datasource_routing_interface );
16fc32f6
RN
3629}
3630
c6acf18d
RN
3631/*
3632 * Acquire into this TRW Layer from an entered URL
3633 */
1c2083ba 3634static void trw_layer_acquire_url_cb ( menu_array_layer values )
c6acf18d 3635{
c6acf18d 3636 vik_datasource_url_interface.mode = VIK_DATASOURCE_ADDTOLAYER;
1c2083ba 3637 trw_layer_acquire ( values, &vik_datasource_url_interface );
c6acf18d
RN
3638}
3639
40f5740b
RN
3640#ifdef VIK_CONFIG_OPENSTREETMAP
3641/*
3642 * Acquire into this TRW Layer from OSM
3643 */
1c2083ba 3644static void trw_layer_acquire_osm_cb ( menu_array_layer values )
40f5740b 3645{
1c2083ba 3646 trw_layer_acquire ( values, &vik_datasource_osm_interface );
40f5740b 3647}
3cc57413
RN
3648
3649/**
3650 * Acquire into this TRW Layer from OSM for 'My' Traces
3651 */
1c2083ba 3652static void trw_layer_acquire_osm_my_traces_cb ( menu_array_layer values )
3cc57413 3653{
1c2083ba 3654 trw_layer_acquire ( values, &vik_datasource_osm_my_traces_interface );
3cc57413 3655}
40f5740b
RN
3656#endif
3657
16fc32f6
RN
3658#ifdef VIK_CONFIG_GEOCACHES
3659/*
3660 * Acquire into this TRW Layer from Geocaching.com
3661 */
1c2083ba 3662static void trw_layer_acquire_geocache_cb ( menu_array_layer values )
16fc32f6 3663{
1c2083ba 3664 trw_layer_acquire ( values, &vik_datasource_gc_interface );
16fc32f6
RN
3665}
3666#endif
3667
68bab1bd
RN
3668#ifdef VIK_CONFIG_GEOTAG
3669/*
3670 * Acquire into this TRW Layer from images
3671 */
1c2083ba 3672static void trw_layer_acquire_geotagged_cb ( menu_array_layer values )
68bab1bd 3673{
1c2083ba 3674 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
68bab1bd
RN
3675
3676 vik_datasource_geotag_interface.mode = VIK_DATASOURCE_ADDTOLAYER;
1c2083ba 3677 trw_layer_acquire ( values, &vik_datasource_geotag_interface );
68bab1bd
RN
3678
3679 // Reverify thumbnails as they may have changed
3680 vtl->has_verified_thumbnails = FALSE;
3681 trw_layer_verify_thumbnails ( vtl, NULL );
3682}
3683#endif
3684
1c2083ba 3685static void trw_layer_gps_upload ( menu_array_layer values )
e50758c7 3686{
19782ffd
RN
3687 menu_array_sublayer data;
3688 gint ii;
3689 for ( ii = MA_VTL; ii < MA_LAST; ii++ )
3690 data[ii] = NULL;
3691 data[MA_VTL] = values[MA_VTL];
3692 data[MA_VLP] = values[MA_VLP];
e50758c7 3693
19782ffd 3694 trw_layer_gps_upload_any ( data );
e50758c7
RN
3695}
3696
3697/**
3698 * If pass_along[3] is defined that this will upload just that track
3699 */
19782ffd 3700static void trw_layer_gps_upload_any ( menu_array_sublayer values )
e50758c7 3701{
19782ffd
RN
3702 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
3703 VikLayersPanel *vlp = VIK_LAYERS_PANEL(values[MA_VLP]);
e50758c7 3704
19782ffd 3705 // May not actually get a track here as values[2&3] can be null
0d2b891f
RN
3706 VikTrack *track = NULL;
3707 vik_gps_xfer_type xfer_type = TRK; // VIK_TRW_LAYER_SUBLAYER_TRACKS = 0 so hard to test different from NULL!
3708 gboolean xfer_all = FALSE;
e50758c7 3709
19782ffd 3710 if ( values[MA_SUBTYPE] ) {
0d2b891f 3711 xfer_all = FALSE;
19782ffd
RN
3712 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE ) {
3713 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
0d2b891f
RN
3714 xfer_type = RTE;
3715 }
19782ffd
RN
3716 else if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_TRACK ) {
3717 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
0d2b891f
RN
3718 xfer_type = TRK;
3719 }
19782ffd 3720 else if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_WAYPOINTS ) {
0d2b891f
RN
3721 xfer_type = WPT;
3722 }
19782ffd 3723 else if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTES ) {
0d2b891f
RN
3724 xfer_type = RTE;
3725 }
3726 }
19782ffd 3727 else if ( !values[MA_CONFIRM] )
0d2b891f 3728 xfer_all = TRUE; // i.e. whole layer
e50758c7 3729
0d2b891f 3730 if (track && !track->visible) {
e50758c7
RN
3731 a_dialog_error_msg ( VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Can not upload invisible track.") );
3732 return;
3733 }
3734
3735 GtkWidget *dialog = gtk_dialog_new_with_buttons ( _("GPS Upload"),
19782ffd 3736 VIK_GTK_WINDOW_FROM_LAYER(vtl),
e50758c7
RN
3737 GTK_DIALOG_DESTROY_WITH_PARENT,
3738 GTK_STOCK_OK,
3739 GTK_RESPONSE_ACCEPT,
3740 GTK_STOCK_CANCEL,
3741 GTK_RESPONSE_REJECT,
3742 NULL );
3743
3744 gtk_dialog_set_default_response ( GTK_DIALOG(dialog), GTK_RESPONSE_ACCEPT );
3745 GtkWidget *response_w = NULL;
3746#if GTK_CHECK_VERSION (2, 20, 0)
3747 response_w = gtk_dialog_get_widget_for_response ( GTK_DIALOG(dialog), GTK_RESPONSE_ACCEPT );
3748#endif
3749
3750 if ( response_w )
3751 gtk_widget_grab_focus ( response_w );
3752
0d2b891f 3753 gpointer dgs = datasource_gps_setup ( dialog, xfer_type, xfer_all );
e50758c7
RN
3754
3755 if ( gtk_dialog_run ( GTK_DIALOG(dialog) ) != GTK_RESPONSE_ACCEPT ) {
3756 datasource_gps_clean_up ( dgs );
3757 gtk_widget_destroy ( dialog );
3758 return;
3759 }
3760
3761 // Get info from reused datasource dialog widgets
3762 gchar* protocol = datasource_gps_get_protocol ( dgs );
3763 gchar* port = datasource_gps_get_descriptor ( dgs );
3764 // NB don't free the above strings as they're references to values held elsewhere
3765 gboolean do_tracks = datasource_gps_get_do_tracks ( dgs );
0d2b891f 3766 gboolean do_routes = datasource_gps_get_do_routes ( dgs );
e50758c7
RN
3767 gboolean do_waypoints = datasource_gps_get_do_waypoints ( dgs );
3768 gboolean turn_off = datasource_gps_get_off ( dgs );
3769
3770 gtk_widget_destroy ( dialog );
3771
3772 // When called from the viewport - work the corresponding layerspanel:
3773 if ( !vlp ) {
3774 vlp = vik_window_layers_panel ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)) );
3775 }
3776
3777 // Apply settings to transfer to the GPS device
3778 vik_gps_comm ( vtl,
3779 track,
3780 GPS_UP,
3781 protocol,
3782 port,
3783 FALSE,
3784 vik_layers_panel_get_viewport (vlp),
3785 vlp,
3786 do_tracks,
0d2b891f 3787 do_routes,
e50758c7
RN
3788 do_waypoints,
3789 turn_off );
3790}
3791
d7ac7564
RN
3792/*
3793 * Acquire into this TRW Layer from any GPS Babel supported file
3794 */
1c2083ba 3795static void trw_layer_acquire_file_cb ( menu_array_layer values )
d7ac7564 3796{
1c2083ba
RN
3797 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
3798 VikLayersPanel *vlp = VIK_LAYERS_PANEL(values[MA_VLP]);
d7ac7564
RN
3799 VikWindow *vw = (VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl));
3800 VikViewport *vvp = vik_window_viewport(vw);
3801
2801a19b 3802 a_acquire ( vw, vlp, vvp, &vik_datasource_file_interface, NULL, NULL );
d7ac7564
RN
3803}
3804
1c2083ba 3805static void trw_layer_new_wp ( menu_array_layer values )
50a14534 3806{
1c2083ba
RN
3807 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
3808 VikLayersPanel *vlp = VIK_LAYERS_PANEL(values[MA_VLP]);
50a14534
EB
3809 /* TODO longone: okay, if layer above (aggregate) is invisible but vtl->visible is true, this redraws for no reason.
3810 instead return true if you want to update. */
aa0665e5
RN
3811 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 ) {
3812 trw_layer_calculate_bounds_waypoints ( vtl );
50a14534 3813 vik_layers_panel_emit_update ( vlp );
aa0665e5 3814 }
50a14534
EB
3815}
3816
2f154c90
RN
3817static void new_track_create_common ( VikTrwLayer *vtl, gchar *name )
3818{
3819 vtl->current_track = vik_track_new();
387ff7ac 3820 vik_track_set_defaults ( vtl->current_track );
2f154c90
RN
3821 vtl->current_track->visible = TRUE;
3822 if ( vtl->drawmode == DRAWMODE_ALL_SAME_COLOR )
3823 // Create track with the preferred colour from the layer properties
3824 vtl->current_track->color = vtl->track_color;
3825 else
3826 gdk_color_parse ( "#000000", &(vtl->current_track->color) );
3827 vtl->current_track->has_color = TRUE;
3828 vik_trw_layer_add_track ( vtl, name, vtl->current_track );
3829}
3830
1c2083ba 3831static void trw_layer_new_track ( menu_array_layer values )
37615c52 3832{
1c2083ba 3833 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
37615c52
RN
3834
3835 if ( ! vtl->current_track ) {
3836 gchar *name = trw_layer_new_unique_sublayer_name ( vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, _("Track")) ;
2f154c90 3837 new_track_create_common ( vtl, name );
1613e468 3838 g_free ( name );
37615c52
RN
3839
3840 vik_window_enable_layer_tool ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)), VIK_LAYER_TRW, TOOL_CREATE_TRACK );
3841 }
3842}
3843
b1453c16
RN
3844static void new_route_create_common ( VikTrwLayer *vtl, gchar *name )
3845{
3846 vtl->current_track = vik_track_new();
387ff7ac 3847 vik_track_set_defaults ( vtl->current_track );
b1453c16
RN
3848 vtl->current_track->visible = TRUE;
3849 vtl->current_track->is_route = TRUE;
3850 // By default make all routes red
3851 vtl->current_track->has_color = TRUE;
3852 gdk_color_parse ( "red", &vtl->current_track->color );
3853 vik_trw_layer_add_route ( vtl, name, vtl->current_track );
3854}
3855
1c2083ba 3856static void trw_layer_new_route ( menu_array_layer values )
0d2b891f 3857{
1c2083ba 3858 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
0d2b891f
RN
3859
3860 if ( ! vtl->current_track ) {
3861 gchar *name = trw_layer_new_unique_sublayer_name ( vtl, VIK_TRW_LAYER_SUBLAYER_ROUTE, _("Route")) ;
b1453c16 3862 new_route_create_common ( vtl, name );
1613e468 3863 g_free ( name );
e37b2a6d 3864 vik_window_enable_layer_tool ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)), VIK_LAYER_TRW, TOOL_CREATE_ROUTE );
0d2b891f
RN
3865 }
3866}
3867
1c2083ba 3868static void trw_layer_auto_routes_view ( menu_array_layer values )
0d2b891f 3869{
1c2083ba
RN
3870 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
3871 VikLayersPanel *vlp = VIK_LAYERS_PANEL(values[MA_VLP]);
0d2b891f
RN
3872
3873 if ( g_hash_table_size (vtl->routes) > 0 ) {
3874 struct LatLon maxmin[2] = { {0,0}, {0,0} };
3875 g_hash_table_foreach ( vtl->routes, (GHFunc) trw_layer_find_maxmin_tracks, maxmin );
3876 trw_layer_zoom_to_show_latlons ( vtl, vik_layers_panel_get_viewport (vlp), maxmin );
3877 vik_layers_panel_emit_update ( vlp );
3878 }
3879}
3880
3881
1c2083ba 3882static void trw_layer_finish_track ( menu_array_layer values )
37615c52 3883{
1c2083ba 3884 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
37615c52 3885 vtl->current_track = NULL;
ee43b101 3886 vtl->route_finder_started = FALSE;
da121f9b 3887 vik_layer_emit_update ( VIK_LAYER(vtl) );
37615c52
RN
3888}
3889
1c2083ba 3890static void trw_layer_auto_tracks_view ( menu_array_layer values )
535ed1ae 3891{
1c2083ba
RN
3892 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
3893 VikLayersPanel *vlp = VIK_LAYERS_PANEL(values[MA_VLP]);
535ed1ae
RN
3894
3895 if ( g_hash_table_size (vtl->tracks) > 0 ) {
3896 struct LatLon maxmin[2] = { {0,0}, {0,0} };
3897 g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_find_maxmin_tracks, maxmin );
3898 trw_layer_zoom_to_show_latlons ( vtl, vik_layers_panel_get_viewport (vlp), maxmin );
3899 vik_layers_panel_emit_update ( vlp );
3900 }
3901}
3902
9e212bfc 3903static void trw_layer_single_waypoint_jump ( const gpointer id, const VikWaypoint *wp, gpointer vvp )
fc59e8c7
RN
3904{
3905 /* NB do not care if wp is visible or not */
be5554c5 3906 vik_viewport_set_center_coord ( VIK_VIEWPORT(vvp), &(wp->coord), TRUE );
fc59e8c7
RN
3907}
3908
1c2083ba 3909static void trw_layer_auto_waypoints_view ( menu_array_layer values )
fc59e8c7 3910{
1c2083ba
RN
3911 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
3912 VikLayersPanel *vlp = VIK_LAYERS_PANEL(values[MA_VLP]);
fc59e8c7
RN
3913
3914 /* Only 1 waypoint - jump straight to it */
3915 if ( g_hash_table_size (vtl->waypoints) == 1 ) {
3916 VikViewport *vvp = vik_layers_panel_get_viewport (vlp);
3917 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_single_waypoint_jump, (gpointer) vvp );
3918 }
3919 /* If at least 2 waypoints - find center and then zoom to fit */
3920 else if ( g_hash_table_size (vtl->waypoints) > 1 )
3921 {
3922 struct LatLon maxmin[2] = { {0,0}, {0,0} };
a8e8ffe1
RN
3923 maxmin[0].lat = vtl->waypoints_bbox.north;
3924 maxmin[1].lat = vtl->waypoints_bbox.south;
3925 maxmin[0].lon = vtl->waypoints_bbox.east;
3926 maxmin[1].lon = vtl->waypoints_bbox.west;
fc59e8c7
RN
3927 trw_layer_zoom_to_show_latlons ( vtl, vik_layers_panel_get_viewport (vlp), maxmin );
3928 }
3929
3930 vik_layers_panel_emit_update ( vlp );
3931}
3932
1c2083ba 3933void trw_layer_osm_traces_upload_cb ( menu_array_layer values )
3185b5d3 3934{
1c2083ba 3935 osm_traces_upload_viktrwlayer(VIK_TRW_LAYER(values[MA_VTL]), NULL);
3185b5d3
RN
3936}
3937
d6c110f1 3938void trw_layer_osm_traces_upload_track_cb ( menu_array_sublayer values )
3185b5d3 3939{
d6c110f1
RN
3940 if ( values[MA_MISC] ) {
3941 VikTrack *trk = VIK_TRACK(values[MA_MISC]);
3942 osm_traces_upload_viktrwlayer(VIK_TRW_LAYER(values[MA_VTL]), trk);
3185b5d3
RN
3943 }
3944}
3945
a7cd93ac 3946static void trw_layer_add_menu_items ( VikTrwLayer *vtl, GtkMenu *menu, gpointer vlp )
50a14534 3947{
1c2083ba 3948 static menu_array_layer pass_along;
50a14534 3949 GtkWidget *item;
98fcbbdb 3950 GtkWidget *export_submenu;
1c2083ba
RN
3951 pass_along[MA_VTL] = vtl;
3952 pass_along[MA_VLP] = vlp;
50a14534
EB
3953
3954 item = gtk_menu_item_new();
3955 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
3956 gtk_widget_show ( item );
3957
37615c52 3958 if ( vtl->current_track ) {
0d2b891f
RN
3959 if ( vtl->current_track->is_route )
3960 item = gtk_menu_item_new_with_mnemonic ( _("_Finish Route") );
3961 else
3962 item = gtk_menu_item_new_with_mnemonic ( _("_Finish Track") );
37615c52
RN
3963 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_finish_track), pass_along );
3964 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3965 gtk_widget_show ( item );
3966
3967 // Add separator
3968 item = gtk_menu_item_new ();
3969 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
3970 gtk_widget_show ( item );
3971 }
3972
d6de71f9
RN
3973 /* Now with icons */
3974 item = gtk_image_menu_item_new_with_mnemonic ( _("_View Layer") );
3975 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ZOOM_FIT, GTK_ICON_SIZE_MENU) );
5a10c240
RN
3976 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_view), pass_along );
3977 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3978 gtk_widget_show ( item );
3979
48d28f21
RN
3980 GtkWidget *view_submenu = gtk_menu_new();
3981 item = gtk_image_menu_item_new_with_mnemonic ( _("V_iew") );
3982 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_FIND, GTK_ICON_SIZE_MENU) );
535ed1ae
RN
3983 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3984 gtk_widget_show ( item );
48d28f21 3985 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), view_submenu );
535ed1ae 3986
48d28f21
RN
3987 item = gtk_menu_item_new_with_mnemonic ( _("View All _Tracks") );
3988 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_tracks_view), pass_along );
3989 gtk_menu_shell_append (GTK_MENU_SHELL (view_submenu), item);
3990 gtk_widget_show ( item );
3991
0d2b891f
RN
3992 item = gtk_menu_item_new_with_mnemonic ( _("View All _Routes") );
3993 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_routes_view), pass_along );
3994 gtk_menu_shell_append (GTK_MENU_SHELL (view_submenu), item);
3995 gtk_widget_show ( item );
3996
48d28f21 3997 item = gtk_menu_item_new_with_mnemonic ( _("View All _Waypoints") );
fc59e8c7 3998 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_waypoints_view), pass_along );
48d28f21 3999 gtk_menu_shell_append (GTK_MENU_SHELL (view_submenu), item);
fc59e8c7
RN
4000 gtk_widget_show ( item );
4001
d6de71f9
RN
4002 item = gtk_image_menu_item_new_with_mnemonic ( _("_Goto Center of Layer") );
4003 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_JUMP_TO, GTK_ICON_SIZE_MENU) );
50a14534
EB
4004 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_centerize), pass_along );
4005 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
4006 gtk_widget_show ( item );
4007
7306a492 4008 item = gtk_menu_item_new_with_mnemonic ( _("Goto _Waypoint...") );
50a14534
EB
4009 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_wp), pass_along );
4010 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
4011 gtk_widget_show ( item );
4012
1bd88e66 4013 export_submenu = gtk_menu_new ();
d6de71f9
RN
4014 item = gtk_image_menu_item_new_with_mnemonic ( _("_Export Layer") );
4015 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_HARDDISK, GTK_ICON_SIZE_MENU) );
50a14534
EB
4016 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
4017 gtk_widget_show ( item );
98fcbbdb 4018 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), export_submenu );
1bd88e66 4019
7306a492 4020 item = gtk_menu_item_new_with_mnemonic ( _("Export as GPS_Point...") );
1bd88e66
GB
4021 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_gpspoint), pass_along );
4022 gtk_menu_shell_append (GTK_MENU_SHELL (export_submenu), item);
4023 gtk_widget_show ( item );
50a14534 4024
7306a492 4025 item = gtk_menu_item_new_with_mnemonic ( _("Export as GPS_Mapper...") );
50a14534 4026 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_gpsmapper), pass_along );
1bd88e66 4027 gtk_menu_shell_append (GTK_MENU_SHELL (export_submenu), item);
50a14534
EB
4028 gtk_widget_show ( item );
4029
7306a492 4030 item = gtk_menu_item_new_with_mnemonic ( _("Export as _GPX...") );
561e6ad0 4031 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_gpx), pass_along );
1bd88e66 4032 gtk_menu_shell_append (GTK_MENU_SHELL (export_submenu), item);
561e6ad0
EB
4033 gtk_widget_show ( item );
4034
ba9d0a00
RN
4035 item = gtk_menu_item_new_with_mnemonic ( _("Export as _KML...") );
4036 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_kml), pass_along );
4037 gtk_menu_shell_append (GTK_MENU_SHELL (export_submenu), item);
4038 gtk_widget_show ( item );
4039
17720e63
GB
4040 item = gtk_menu_item_new_with_mnemonic ( _("Export via GPSbabel...") );
4041 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_babel), pass_along );
4042 gtk_menu_shell_append (GTK_MENU_SHELL (export_submenu), item);
4043 gtk_widget_show ( item );
4044
6e4bf640 4045 gchar* external1 = g_strdup_printf ( _("Open with External Program_1: %s"), a_vik_get_external_gpx_program_1() );
ccccf356
RN
4046 item = gtk_menu_item_new_with_mnemonic ( external1 );
4047 g_free ( external1 );
4048 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_external_gpx_1), pass_along );
4049 gtk_menu_shell_append (GTK_MENU_SHELL (export_submenu), item);
4050 gtk_widget_show ( item );
4051
6e4bf640 4052 gchar* external2 = g_strdup_printf ( _("Open with External Program_2: %s"), a_vik_get_external_gpx_program_2() );
ff02058b
RN
4053 item = gtk_menu_item_new_with_mnemonic ( external2 );
4054 g_free ( external2 );
4055 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_external_gpx_2), pass_along );
4056 gtk_menu_shell_append (GTK_MENU_SHELL (export_submenu), item);
4057 gtk_widget_show ( item );
4058
e7a8a2f4
RN
4059 GtkWidget *new_submenu = gtk_menu_new();
4060 item = gtk_image_menu_item_new_with_mnemonic ( _("_New") );
4061 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NEW, GTK_ICON_SIZE_MENU) );
4062 gtk_menu_shell_append(GTK_MENU_SHELL (menu), item);
4063 gtk_widget_show(item);
4064 gtk_menu_item_set_submenu(GTK_MENU_ITEM(item), new_submenu);
4065
4066 item = gtk_image_menu_item_new_with_mnemonic ( _("New _Waypoint...") );
d6de71f9 4067 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NEW, GTK_ICON_SIZE_MENU) );
50a14534 4068 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_wp), pass_along );
e7a8a2f4 4069 gtk_menu_shell_append (GTK_MENU_SHELL (new_submenu), item);
50a14534 4070 gtk_widget_show ( item );
3e7553ae 4071
37615c52
RN
4072 item = gtk_image_menu_item_new_with_mnemonic ( _("New _Track") );
4073 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NEW, GTK_ICON_SIZE_MENU) );
4074 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_track), pass_along );
4075 gtk_menu_shell_append (GTK_MENU_SHELL (new_submenu), item);
4076 gtk_widget_show ( item );
4077 // Make it available only when a new track *not* already in progress
4078 gtk_widget_set_sensitive ( item, ! (gboolean)GPOINTER_TO_INT(vtl->current_track) );
4079
0d2b891f
RN
4080 item = gtk_image_menu_item_new_with_mnemonic ( _("New _Route") );
4081 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NEW, GTK_ICON_SIZE_MENU) );
4082 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_route), pass_along );
4083 gtk_menu_shell_append (GTK_MENU_SHELL (new_submenu), item);
4084 gtk_widget_show ( item );
4085 // Make it available only when a new track *not* already in progress
4086 gtk_widget_set_sensitive ( item, ! (gboolean)GPOINTER_TO_INT(vtl->current_track) );
4087
b3eb3b98
RN
4088#ifdef VIK_CONFIG_GEOTAG
4089 item = gtk_menu_item_new_with_mnemonic ( _("Geotag _Images...") );
4090 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_geotagging), pass_along );
4091 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
4092 gtk_widget_show ( item );
4093#endif
4094
16fc32f6 4095 GtkWidget *acquire_submenu = gtk_menu_new ();
aaecf368 4096 item = gtk_image_menu_item_new_with_mnemonic ( _("_Acquire") );
d6de71f9 4097 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_DOWN, GTK_ICON_SIZE_MENU) );
16fc32f6
RN
4098 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
4099 gtk_widget_show ( item );
4100 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), acquire_submenu );
4101
4102 item = gtk_menu_item_new_with_mnemonic ( _("From _GPS...") );
4103 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_acquire_gps_cb), pass_along );
4104 gtk_menu_shell_append (GTK_MENU_SHELL (acquire_submenu), item);
4105 gtk_widget_show ( item );
4106
7f95fd54
GB
4107 /* FIXME: only add menu when at least a routing engine has support for Directions */
4108 item = gtk_menu_item_new_with_mnemonic ( _("From _Directions...") );
4109 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_acquire_routing_cb), pass_along );
16fc32f6
RN
4110 gtk_menu_shell_append (GTK_MENU_SHELL (acquire_submenu), item);
4111 gtk_widget_show ( item );
4112
40f5740b
RN
4113#ifdef VIK_CONFIG_OPENSTREETMAP
4114 item = gtk_menu_item_new_with_mnemonic ( _("From _OSM Traces...") );
4115 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_acquire_osm_cb), pass_along );
4116 gtk_menu_shell_append (GTK_MENU_SHELL (acquire_submenu), item);
4117 gtk_widget_show ( item );
3cc57413
RN
4118
4119 item = gtk_menu_item_new_with_mnemonic ( _("From _My OSM Traces...") );
4120 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_acquire_osm_my_traces_cb), pass_along );
4121 gtk_menu_shell_append (GTK_MENU_SHELL (acquire_submenu), item);
4122 gtk_widget_show ( item );
40f5740b
RN
4123#endif
4124
c6acf18d
RN
4125 item = gtk_menu_item_new_with_mnemonic ( _("From _URL...") );
4126 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_acquire_url_cb), pass_along );
4127 gtk_menu_shell_append (GTK_MENU_SHELL (acquire_submenu), item);
4128 gtk_widget_show ( item );
4129
aaecf368
RN
4130#ifdef VIK_CONFIG_GEONAMES
4131 GtkWidget *wikipedia_submenu = gtk_menu_new();
4132 item = gtk_image_menu_item_new_with_mnemonic ( _("From _Wikipedia Waypoints") );
4133 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ADD, GTK_ICON_SIZE_MENU) );
4134 gtk_menu_shell_append(GTK_MENU_SHELL (acquire_submenu), item);
4135 gtk_widget_show(item);
4136 gtk_menu_item_set_submenu(GTK_MENU_ITEM(item), wikipedia_submenu);
4137
4138 item = gtk_image_menu_item_new_with_mnemonic ( _("Within _Layer Bounds") );
4139 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ZOOM_FIT, GTK_ICON_SIZE_MENU) );
4140 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_wikipedia_wp_layer), pass_along );
4141 gtk_menu_shell_append (GTK_MENU_SHELL (wikipedia_submenu), item);
4142 gtk_widget_show ( item );
4143
4144 item = gtk_image_menu_item_new_with_mnemonic ( _("Within _Current View") );
4145 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ZOOM_100, GTK_ICON_SIZE_MENU) );
4146 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_wikipedia_wp_viewport), pass_along );
4147 gtk_menu_shell_append (GTK_MENU_SHELL (wikipedia_submenu), item);
4148 gtk_widget_show ( item );
4149#endif
4150
16fc32f6
RN
4151#ifdef VIK_CONFIG_GEOCACHES
4152 item = gtk_menu_item_new_with_mnemonic ( _("From Geo_caching...") );
4153 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_acquire_geocache_cb), pass_along );
4154 gtk_menu_shell_append (GTK_MENU_SHELL (acquire_submenu), item);
4155 gtk_widget_show ( item );
4156#endif
4157
68bab1bd
RN
4158#ifdef VIK_CONFIG_GEOTAG
4159 item = gtk_menu_item_new_with_mnemonic ( _("From Geotagged _Images...") );
4160 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_acquire_geotagged_cb), pass_along );
4161 gtk_menu_shell_append (GTK_MENU_SHELL (acquire_submenu), item);
4162 gtk_widget_show ( item );
4163#endif
4164
d7ac7564
RN
4165 item = gtk_menu_item_new_with_mnemonic ( _("From _File...") );
4166 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_acquire_file_cb), pass_along );
4167 gtk_menu_shell_append (GTK_MENU_SHELL (acquire_submenu), item);
49f6b9d3 4168 gtk_widget_set_tooltip_text (item, _("Import File With GPS_Babel..."));
d7ac7564
RN
4169 gtk_widget_show ( item );
4170
82993cc7
RN
4171 vik_ext_tool_datasources_add_menu_items_to_menu ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)), GTK_MENU (acquire_submenu) );
4172
e50758c7
RN
4173 GtkWidget *upload_submenu = gtk_menu_new ();
4174 item = gtk_image_menu_item_new_with_mnemonic ( _("_Upload") );
4175 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_UP, GTK_ICON_SIZE_MENU) );
4176 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
4177 gtk_widget_show ( item );
4178 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), upload_submenu );
4179
0d2b891f
RN
4180 item = gtk_image_menu_item_new_with_mnemonic ( _("Upload to _GPS...") );
4181 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_FORWARD, GTK_ICON_SIZE_MENU) );
4182 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_gps_upload), pass_along );
4183 gtk_menu_shell_append (GTK_MENU_SHELL (upload_submenu), item);
4184 gtk_widget_show ( item );
4185
3e7553ae 4186#ifdef VIK_CONFIG_OPENSTREETMAP
d6de71f9
RN
4187 item = gtk_image_menu_item_new_with_mnemonic ( _("Upload to _OSM...") );
4188 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_UP, GTK_ICON_SIZE_MENU) );
3185b5d3 4189 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_osm_traces_upload_cb), pass_along );
e50758c7 4190 gtk_menu_shell_append (GTK_MENU_SHELL (upload_submenu), item);
3e7553ae
GB
4191 gtk_widget_show ( item );
4192#endif
28c82d8b 4193
c9a5cbf9 4194 GtkWidget *delete_submenu = gtk_menu_new ();
d6de71f9
RN
4195 item = gtk_image_menu_item_new_with_mnemonic ( _("De_lete") );
4196 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU) );
c9a5cbf9
RN
4197 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
4198 gtk_widget_show ( item );
4199 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), delete_submenu );
4200
d6de71f9
RN
4201 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete All _Tracks") );
4202 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU) );
c9a5cbf9
RN
4203 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_all_tracks), pass_along );
4204 gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
4205 gtk_widget_show ( item );
4206
d6de71f9
RN
4207 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete Tracks _From Selection...") );
4208 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
20b671c3
RN
4209 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_tracks_from_selection), pass_along );
4210 gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
4211 gtk_widget_show ( item );
58517f22
RN
4212
4213 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete _All Routes") );
4214 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU) );
4215 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_all_routes), pass_along );
4216 gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
4217 gtk_widget_show ( item );
4218
4219 item = gtk_image_menu_item_new_with_mnemonic ( _("_Delete Routes From Selection...") );
4220 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
4221 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_routes_from_selection), pass_along );
4222 gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
4223 gtk_widget_show ( item );
20b671c3 4224
d6de71f9
RN
4225 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete All _Waypoints") );
4226 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU) );
c9a5cbf9
RN
4227 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_all_waypoints), pass_along );
4228 gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
4229 gtk_widget_show ( item );
4230
d6de71f9
RN
4231 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete Waypoints From _Selection...") );
4232 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
20b671c3
RN
4233 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_waypoints_from_selection), pass_along );
4234 gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
4235 gtk_widget_show ( item );
4236
28c82d8b 4237 item = a_acquire_trwlayer_menu ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)), vlp,
d65659e7 4238 vik_layers_panel_get_viewport(VIK_LAYERS_PANEL(vlp)), vtl );
28c82d8b
EB
4239 if ( item ) {
4240 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
4241 gtk_widget_show ( item );
4242 }
4243
4244 item = a_acquire_trwlayer_track_menu ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)), vlp,
d65659e7 4245 vik_layers_panel_get_viewport(VIK_LAYERS_PANEL(vlp)), vtl );
28c82d8b
EB
4246 if ( item ) {
4247 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
4248 gtk_widget_show ( item );
07c9d42b
RN
4249 }
4250
4251 item = gtk_image_menu_item_new_with_mnemonic ( _("Track _List...") );
4252 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
4253 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_track_list_dialog), pass_along );
4254 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
4255 gtk_widget_show ( item );
4256 gtk_widget_set_sensitive ( item, (gboolean)(g_hash_table_size (vtl->tracks)+g_hash_table_size (vtl->routes)) );
00176e85
RN
4257
4258 item = gtk_image_menu_item_new_with_mnemonic ( _("_Waypoint List...") );
4259 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
4260 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_waypoint_list_dialog), pass_along );
4261 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
4262 gtk_widget_show ( item );
4263 gtk_widget_set_sensitive ( item, (gboolean)(g_hash_table_size (vtl->waypoints)) );
50a14534
EB
4264}
4265
c9570f86
RN
4266// Fake Waypoint UUIDs vi simple increasing integer
4267static guint wp_uuid = 0;
4268
50a14534
EB
4269void vik_trw_layer_add_waypoint ( VikTrwLayer *vtl, gchar *name, VikWaypoint *wp )
4270{
c9570f86
RN
4271 wp_uuid++;
4272
4273 vik_waypoint_set_name (wp, name);
4274
50a14534
EB
4275 if ( VIK_LAYER(vtl)->realized )
4276 {
cd3f311e
RN
4277 // Do we need to create the sublayer:
4278 if ( g_hash_table_size (vtl->waypoints) == 0 ) {
4279 trw_layer_add_sublayer_waypoints ( vtl, VIK_LAYER(vtl)->vt, &(VIK_LAYER(vtl)->iter) );
4280 }
4281
c9570f86
RN
4282 GtkTreeIter *iter = g_malloc(sizeof(GtkTreeIter));
4283
4284 // Visibility column always needed for waypoints
c9cac058 4285 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 );
56afe641 4286
c9570f86
RN
4287 // Actual setting of visibility dependent on the waypoint
4288 vik_treeview_item_set_visible ( VIK_LAYER(vtl)->vt, iter, wp->visible );
4289
4290 g_hash_table_insert ( vtl->waypoints_iters, GUINT_TO_POINTER(wp_uuid), iter );
7a52aac6
RN
4291
4292 // Sort now as post_read is not called on a realized waypoint
4293 vik_treeview_sort_children ( VIK_LAYER(vtl)->vt, &(vtl->waypoints_iter), vtl->wp_sort_order );
50a14534 4294 }
50a14534 4295
a8fe53f8 4296 highest_wp_number_add_wp(vtl, name);
c9570f86 4297 g_hash_table_insert ( vtl->waypoints, GUINT_TO_POINTER(wp_uuid), wp );
50a14534
EB
4298
4299}
4300
ce4bd1cf
RN
4301// Fake Track UUIDs vi simple increasing integer
4302static guint tr_uuid = 0;
4303
50a14534
EB
4304void vik_trw_layer_add_track ( VikTrwLayer *vtl, gchar *name, VikTrack *t )
4305{
ce4bd1cf
RN
4306 tr_uuid++;
4307
4308 vik_track_set_name (t, name);
4309
50a14534
EB
4310 if ( VIK_LAYER(vtl)->realized )
4311 {
cd3f311e
RN
4312 // Do we need to create the sublayer:
4313 if ( g_hash_table_size (vtl->tracks) == 0 ) {
4314 trw_layer_add_sublayer_tracks ( vtl, VIK_LAYER(vtl)->vt, &(VIK_LAYER(vtl)->iter) );
4315 }
4316
ce4bd1cf
RN
4317 GtkTreeIter *iter = g_malloc(sizeof(GtkTreeIter));
4318 // Visibility column always needed for tracks
c9cac058 4319 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 );
56afe641 4320
ce4bd1cf
RN
4321 // Actual setting of visibility dependent on the track
4322 vik_treeview_item_set_visible ( VIK_LAYER(vtl)->vt, iter, t->visible );
4323
4324 g_hash_table_insert ( vtl->tracks_iters, GUINT_TO_POINTER(tr_uuid), iter );
c9cac058
RN
4325
4326 // Sort now as post_read is not called on a realized track
4327 vik_treeview_sort_children ( VIK_LAYER(vtl)->vt, &(vtl->tracks_iter), vtl->track_sort_order );
50a14534 4328 }
50a14534 4329
ce4bd1cf 4330 g_hash_table_insert ( vtl->tracks, GUINT_TO_POINTER(tr_uuid), t );
b1453c16 4331
93ee73b3 4332 trw_layer_update_treeview ( vtl, t );
50a14534
EB
4333}
4334
0d2b891f
RN
4335// Fake Route UUIDs vi simple increasing integer
4336static guint rt_uuid = 0;
4337
4338void vik_trw_layer_add_route ( VikTrwLayer *vtl, gchar *name, VikTrack *t )
4339{
4340 rt_uuid++;
4341
4342 vik_track_set_name (t, name);
4343
4344 if ( VIK_LAYER(vtl)->realized )
4345 {
4346 // Do we need to create the sublayer:
4347 if ( g_hash_table_size (vtl->routes) == 0 ) {
4348 trw_layer_add_sublayer_routes ( vtl, VIK_LAYER(vtl)->vt, &(VIK_LAYER(vtl)->iter) );
4349 }
4350
4351 GtkTreeIter *iter = g_malloc(sizeof(GtkTreeIter));
c9cac058
RN
4352 // Visibility column always needed for routes
4353 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 );
4354 // Actual setting of visibility dependent on the route
0d2b891f
RN
4355 vik_treeview_item_set_visible ( VIK_LAYER(vtl)->vt, iter, t->visible );
4356
4357 g_hash_table_insert ( vtl->routes_iters, GUINT_TO_POINTER(rt_uuid), iter );
c9cac058
RN
4358
4359 // Sort now as post_read is not called on a realized route
4360 vik_treeview_sort_children ( VIK_LAYER(vtl)->vt, &(vtl->routes_iter), vtl->track_sort_order );
0d2b891f
RN
4361 }
4362
4363 g_hash_table_insert ( vtl->routes, GUINT_TO_POINTER(rt_uuid), t );
4364
93ee73b3 4365 trw_layer_update_treeview ( vtl, t );
0d2b891f
RN
4366}
4367
50a14534 4368/* to be called whenever a track has been deleted or may have been changed. */
ce4bd1cf 4369void trw_layer_cancel_tps_of_track ( VikTrwLayer *vtl, VikTrack *trk )
50a14534 4370{
ce4bd1cf 4371 if (vtl->current_tp_track == trk )
50a14534 4372 trw_layer_cancel_current_tp ( vtl, FALSE );
50a14534 4373}
0d2b891f 4374
98acb9a1
RN
4375/**
4376 * Normally this is done to due the waypoint size preference having changed
4377 */
4378void vik_trw_layer_reset_waypoints ( VikTrwLayer *vtl )
4379{
4380 GHashTableIter iter;
4381 gpointer key, value;
4382
4383 // Foreach waypoint
4384 g_hash_table_iter_init ( &iter, vtl->waypoints );
4385 while ( g_hash_table_iter_next ( &iter, &key, &value ) ) {
4386 VikWaypoint *wp = VIK_WAYPOINT(value);
4387 if ( wp->symbol ) {
4388 // Reapply symbol setting to update the pixbuf
4389 gchar *tmp_symbol = g_strdup ( wp->symbol );
4390 vik_waypoint_set_symbol ( wp, tmp_symbol );
4391 g_free ( tmp_symbol );
4392 }
4393 }
4394}
4395
1613e468
RN
4396/**
4397 * trw_layer_new_unique_sublayer_name:
4398 *
4399 * Allocates a unique new name
4400 */
9748531a 4401gchar *trw_layer_new_unique_sublayer_name (VikTrwLayer *vtl, gint sublayer_type, const gchar *name)
e890a6e6 4402{
0d2b891f
RN
4403 gint i = 2;
4404 gchar *newname = g_strdup(name);
4405
4406 gpointer id = NULL;
4407 do {
4408 id = NULL;
4409 switch ( sublayer_type ) {
4410 case VIK_TRW_LAYER_SUBLAYER_TRACK:
4411 id = (gpointer) vik_trw_layer_get_track ( vtl, newname );
4412 break;
4413 case VIK_TRW_LAYER_SUBLAYER_WAYPOINT:
4414 id = (gpointer) vik_trw_layer_get_waypoint ( vtl, newname );
4415 break;
4416 default:
4417 id = (gpointer) vik_trw_layer_get_route ( vtl, newname );
4418 break;
4419 }
4420 // If found a name already in use try adding 1 to it and we try again
4421 if ( id ) {
4422 gchar *new_newname = g_strdup_printf("%s#%d", name, i);
4423 g_free(newname);
4424 newname = new_newname;
4425 i++;
4426 }
4427 } while ( id != NULL);
4428
e890a6e6
EB
4429 return newname;
4430}
50a14534 4431
805d282e
EB
4432void vik_trw_layer_filein_add_waypoint ( VikTrwLayer *vtl, gchar *name, VikWaypoint *wp )
4433{
c9570f86
RN
4434 // No more uniqueness of name forced when loading from a file
4435 // This now makes this function a little redunant as we just flow the parameters through
4436 vik_trw_layer_add_waypoint ( vtl, name, wp );
805d282e 4437}
c9570f86 4438
805d282e
EB
4439void vik_trw_layer_filein_add_track ( VikTrwLayer *vtl, gchar *name, VikTrack *tr )
4440{
ee43b101 4441 if ( vtl->route_finder_append && vtl->current_track ) {
c3deba01 4442 vik_track_remove_dup_points ( tr ); /* make "double point" track work to undo */
ee43b101
MH
4443
4444 // enforce end of current track equal to start of tr
4445 VikTrackpoint *cur_end = vik_track_get_tp_last ( vtl->current_track );
4446 VikTrackpoint *new_start = vik_track_get_tp_first ( tr );
4447 if ( ! vik_coord_equals ( &cur_end->coord, &new_start->coord ) ) {
4448 vik_track_add_trackpoint ( vtl->current_track,
4449 vik_trackpoint_copy ( cur_end ),
4450 FALSE );
4451 }
4452
4453 vik_track_steal_and_append_trackpoints ( vtl->current_track, tr );
bddd2056 4454 vik_track_free ( tr );
7ff7d728 4455 vtl->route_finder_append = FALSE; /* this means we have added it */
bddd2056 4456 } else {
ce4bd1cf
RN
4457
4458 // No more uniqueness of name forced when loading from a file
0d2b891f
RN
4459 if ( tr->is_route )
4460 vik_trw_layer_add_route ( vtl, name, tr );
4461 else
4462 vik_trw_layer_add_track ( vtl, name, tr );
bddd2056 4463
7ff7d728 4464 if ( vtl->route_finder_check_added_track ) {
c3deba01 4465 vik_track_remove_dup_points ( tr ); /* make "double point" track work to undo */
ce4bd1cf 4466 vtl->route_finder_added_track = tr;
bddd2056
EB
4467 }
4468 }
805d282e
EB
4469}
4470
ce4bd1cf 4471static void trw_layer_enum_item ( gpointer id, GList **tr, GList **l )
c48517ad 4472{
ce4bd1cf 4473 *l = g_list_append(*l, id);
c48517ad
AF
4474}
4475
ce4bd1cf
RN
4476/*
4477 * Move an item from one TRW layer to another TRW layer
4478 */
4479static void trw_layer_move_item ( VikTrwLayer *vtl_src, VikTrwLayer *vtl_dest, gpointer id, gint type )
c48517ad 4480{
c9cac058 4481 // TODO reconsider strategy when moving within layer (if anything...)
762cb457 4482 gboolean rename = ( vtl_src != vtl_dest );
c9cac058
RN
4483 if ( ! rename )
4484 return;
762cb457 4485
c48517ad 4486 if (type == VIK_TRW_LAYER_SUBLAYER_TRACK) {
ce4bd1cf
RN
4487 VikTrack *trk = g_hash_table_lookup ( vtl_src->tracks, id );
4488
762cb457
RN
4489 gchar *newname;
4490 if ( rename )
4491 newname = trw_layer_new_unique_sublayer_name ( vtl_dest, type, trk->name );
4492 else
4493 newname = g_strdup ( trk->name );
ce4bd1cf 4494
03817fbf 4495 VikTrack *trk2 = vik_track_copy ( trk, TRUE );
ce4bd1cf 4496 vik_trw_layer_add_track ( vtl_dest, newname, trk2 );
1613e468 4497 g_free ( newname );
ce4bd1cf 4498 vik_trw_layer_delete_track ( vtl_src, trk );
c48517ad 4499 }
c9570f86 4500
0d2b891f
RN
4501 if (type == VIK_TRW_LAYER_SUBLAYER_ROUTE) {
4502 VikTrack *trk = g_hash_table_lookup ( vtl_src->routes, id );
4503
762cb457
RN
4504 gchar *newname;
4505 if ( rename )
4506 newname = trw_layer_new_unique_sublayer_name ( vtl_dest, type, trk->name );
4507 else
4508 newname = g_strdup ( trk->name );
0d2b891f 4509
03817fbf 4510 VikTrack *trk2 = vik_track_copy ( trk, TRUE );
0d2b891f 4511 vik_trw_layer_add_route ( vtl_dest, newname, trk2 );
1613e468 4512 g_free ( newname );
0d2b891f
RN
4513 vik_trw_layer_delete_route ( vtl_src, trk );
4514 }
4515
ce4bd1cf
RN
4516 if (type == VIK_TRW_LAYER_SUBLAYER_WAYPOINT) {
4517 VikWaypoint *wp = g_hash_table_lookup ( vtl_src->waypoints, id );
c9570f86 4518
762cb457
RN
4519 gchar *newname;
4520 if ( rename )
4521 newname = trw_layer_new_unique_sublayer_name ( vtl_dest, type, wp->name );
4522 else
4523 newname = g_strdup ( wp->name );
c9570f86 4524
ce4bd1cf
RN
4525 VikWaypoint *wp2 = vik_waypoint_copy ( wp );
4526 vik_trw_layer_add_waypoint ( vtl_dest, newname, wp2 );
1613e468 4527 g_free ( newname );
c9570f86 4528 trw_layer_delete_waypoint ( vtl_src, wp );
aa0665e5 4529
26e66078
RN
4530 // Recalculate bounds even if not renamed as maybe dragged between layers
4531 trw_layer_calculate_bounds_waypoints ( vtl_dest );
4532 trw_layer_calculate_bounds_waypoints ( vtl_src );
c48517ad
AF
4533 }
4534}
4535
70a23263 4536static void trw_layer_drag_drop_request ( VikTrwLayer *vtl_src, VikTrwLayer *vtl_dest, GtkTreeIter *src_item_iter, GtkTreePath *dest_path )
e4afc73a
EB
4537{
4538 VikTreeview *vt = VIK_LAYER(vtl_src)->vt;
c48517ad
AF
4539 gint type = vik_treeview_item_get_data(vt, src_item_iter);
4540
e4afc73a 4541 if (!vik_treeview_item_get_pointer(vt, src_item_iter)) {
b637058e
EB
4542 GList *items = NULL;
4543 GList *iter;
e4afc73a 4544
c48517ad
AF
4545 if (type==VIK_TRW_LAYER_SUBLAYER_TRACKS) {
4546 g_hash_table_foreach ( vtl_src->tracks, (GHFunc)trw_layer_enum_item, &items);
4547 }
4548 if (type==VIK_TRW_LAYER_SUBLAYER_WAYPOINTS) {
4549 g_hash_table_foreach ( vtl_src->waypoints, (GHFunc)trw_layer_enum_item, &items);
4550 }
0d2b891f
RN
4551 if (type==VIK_TRW_LAYER_SUBLAYER_ROUTES) {
4552 g_hash_table_foreach ( vtl_src->routes, (GHFunc)trw_layer_enum_item, &items);
4553 }
4554
c48517ad
AF
4555 iter = items;
4556 while (iter) {
4557 if (type==VIK_TRW_LAYER_SUBLAYER_TRACKS) {
0d2b891f 4558 trw_layer_move_item ( vtl_src, vtl_dest, iter->data, VIK_TRW_LAYER_SUBLAYER_TRACK);
c9cac058 4559 } else if (type==VIK_TRW_LAYER_SUBLAYER_ROUTES) {
0d2b891f 4560 trw_layer_move_item ( vtl_src, vtl_dest, iter->data, VIK_TRW_LAYER_SUBLAYER_ROUTE);
c48517ad 4561 } else {
0d2b891f 4562 trw_layer_move_item ( vtl_src, vtl_dest, iter->data, VIK_TRW_LAYER_SUBLAYER_WAYPOINT);
c48517ad
AF
4563 }
4564 iter = iter->next;
e4afc73a 4565 }
c48517ad 4566 if (items)
b637058e 4567 g_list_free(items);
c48517ad
AF
4568 } else {
4569 gchar *name = vik_treeview_item_get_pointer(vt, src_item_iter);
4570 trw_layer_move_item(vtl_src, vtl_dest, name, type);
e4afc73a
EB
4571 }
4572}
4573
c98e2f73 4574gboolean trw_layer_track_find_uuid ( const gpointer id, const VikTrack *trk, gpointer udata )
ce4bd1cf
RN
4575{
4576 trku_udata *user_data = udata;
4577 if ( trk == user_data->trk ) {
4578 user_data->uuid = id;
4579 return TRUE;
4580 }
4581 return FALSE;
4582}
4583
4584gboolean vik_trw_layer_delete_track ( VikTrwLayer *vtl, VikTrack *trk )
50a14534 4585{
50a14534 4586 gboolean was_visible = FALSE;
ce4bd1cf
RN
4587 if ( trk && trk->name ) {
4588
4589 if ( trk == vtl->current_track ) {
50a14534 4590 vtl->current_track = NULL;
ce4bd1cf
RN
4591 vtl->current_tp_track = NULL;
4592 vtl->current_tp_id = NULL;
4593 vtl->moving_tp = FALSE;
ee43b101 4594 vtl->route_finder_started = FALSE;
77ad64fa 4595 }
ce4bd1cf
RN
4596
4597 was_visible = trk->visible;
4598
ce4bd1cf
RN
4599 if ( trk == vtl->route_finder_added_track )
4600 vtl->route_finder_added_track = NULL;
4601
4602 trku_udata udata;
4603 udata.trk = trk;
4604 udata.uuid = NULL;
50a14534 4605
ce4bd1cf
RN
4606 // Hmmm, want key of it
4607 gpointer *trkf = g_hash_table_find ( vtl->tracks, (GHRFunc) trw_layer_track_find_uuid, &udata );
50a14534 4608
ce4bd1cf
RN
4609 if ( trkf && udata.uuid ) {
4610 /* could be current_tp, so we have to check */
4611 trw_layer_cancel_tps_of_track ( vtl, trk );
4612
4613 GtkTreeIter *it = g_hash_table_lookup ( vtl->tracks_iters, udata.uuid );
4614
4615 if ( it ) {
4616 vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, it );
4617 g_hash_table_remove ( vtl->tracks_iters, udata.uuid );
4618 g_hash_table_remove ( vtl->tracks, udata.uuid );
cd3f311e
RN
4619
4620 // If last sublayer, then remove sublayer container
4621 if ( g_hash_table_size (vtl->tracks) == 0 ) {
4622 vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, &(vtl->tracks_iter) );
4623 }
ce4bd1cf 4624 }
bc07590a
RN
4625 // Incase it was selected (no item delete signal ATM)
4626 vik_window_clear_highlight ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)) );
ce4bd1cf 4627 }
50a14534
EB
4628 }
4629 return was_visible;
4630}
4631
0d2b891f
RN
4632gboolean vik_trw_layer_delete_route ( VikTrwLayer *vtl, VikTrack *trk )
4633{
4634 gboolean was_visible = FALSE;
4635
4636 if ( trk && trk->name ) {
4637
4638 if ( trk == vtl->current_track ) {
4639 vtl->current_track = NULL;
4640 vtl->current_tp_track = NULL;
4641 vtl->current_tp_id = NULL;
4642 vtl->moving_tp = FALSE;
4643 }
4644
4645 was_visible = trk->visible;
4646
0d2b891f
RN
4647 if ( trk == vtl->route_finder_added_track )
4648 vtl->route_finder_added_track = NULL;
4649
4650 trku_udata udata;
4651 udata.trk = trk;
4652 udata.uuid = NULL;
4653
4654 // Hmmm, want key of it
4655 gpointer *trkf = g_hash_table_find ( vtl->routes, (GHRFunc) trw_layer_track_find_uuid, &udata );
4656
4657 if ( trkf && udata.uuid ) {
4658 /* could be current_tp, so we have to check */
4659 trw_layer_cancel_tps_of_track ( vtl, trk );
4660
4661 GtkTreeIter *it = g_hash_table_lookup ( vtl->routes_iters, udata.uuid );
4662
4663 if ( it ) {
4664 vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, it );
4665 g_hash_table_remove ( vtl->routes_iters, udata.uuid );
4666 g_hash_table_remove ( vtl->routes, udata.uuid );
4667
4668 // If last sublayer, then remove sublayer container
4669 if ( g_hash_table_size (vtl->routes) == 0 ) {
4670 vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, &(vtl->routes_iter) );
4671 }
4672 }
bc07590a
RN
4673 // Incase it was selected (no item delete signal ATM)
4674 vik_window_clear_highlight ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)) );
0d2b891f
RN
4675 }
4676 }
4677 return was_visible;
4678}
4679
c9570f86 4680static gboolean trw_layer_delete_waypoint ( VikTrwLayer *vtl, VikWaypoint *wp )
e4afc73a
EB
4681{
4682 gboolean was_visible = FALSE;
e4afc73a 4683
c9570f86 4684 if ( wp && wp->name ) {
e4afc73a
EB
4685
4686 if ( wp == vtl->current_wp ) {
4687 vtl->current_wp = NULL;
c9570f86 4688 vtl->current_wp_id = NULL;
e4afc73a
EB
4689 vtl->moving_wp = FALSE;
4690 }
4691
4692 was_visible = wp->visible;
c9570f86
RN
4693
4694 wpu_udata udata;
4695 udata.wp = wp;
4696 udata.uuid = NULL;
4697
4698 // Hmmm, want key of it
4699 gpointer *wpf = g_hash_table_find ( vtl->waypoints, (GHRFunc) trw_layer_waypoint_find_uuid, (gpointer) &udata );
4700
4701 if ( wpf && udata.uuid ) {
4702 GtkTreeIter *it = g_hash_table_lookup ( vtl->waypoints_iters, udata.uuid );
4703
4704 if ( it ) {
4705 vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, it );
4706 g_hash_table_remove ( vtl->waypoints_iters, udata.uuid );
4707
4708 highest_wp_number_remove_wp(vtl, wp->name);
4709 g_hash_table_remove ( vtl->waypoints, udata.uuid ); // last because this frees the name
cd3f311e
RN
4710
4711 // If last sublayer, then remove sublayer container
4712 if ( g_hash_table_size (vtl->waypoints) == 0 ) {
4713 vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, &(vtl->waypoints_iter) );
4714 }
c9570f86 4715 }
bc07590a
RN
4716 // Incase it was selected (no item delete signal ATM)
4717 vik_window_clear_highlight ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)) );
c9570f86 4718 }
a8fe53f8 4719
e4afc73a
EB
4720 }
4721
4722 return was_visible;
4723}
4724
c9570f86
RN
4725// Only for temporary use by trw_layer_delete_waypoint_by_name
4726static gboolean trw_layer_waypoint_find_uuid_by_name ( const gpointer id, const VikWaypoint *wp, gpointer udata )
4727{
4728 wpu_udata *user_data = udata;
4729 if ( ! strcmp ( wp->name, user_data->wp->name ) ) {
4730 user_data->uuid = id;
4731 return TRUE;
4732 }
4733 return FALSE;
4734}
4735
4736/*
4737 * Delete a waypoint by the given name
4738 * NOTE: ATM this will delete the first encountered Waypoint with the specified name
4739 * as there be multiple waypoints with the same name
4740 */
4741static gboolean trw_layer_delete_waypoint_by_name ( VikTrwLayer *vtl, const gchar *name )
4742{
4743 wpu_udata udata;
4744 // Fake a waypoint with the given name
4745 udata.wp = vik_waypoint_new ();
4746 vik_waypoint_set_name (udata.wp, name);
4747 // Currently only the name is used in this waypoint find function
4748 udata.uuid = NULL;
4749
4750 // Hmmm, want key of it
4751 gpointer *wpf = g_hash_table_find ( vtl->waypoints, (GHRFunc) trw_layer_waypoint_find_uuid_by_name, (gpointer) &udata );
4752
4753 vik_waypoint_free (udata.wp);
4754
4755 if ( wpf && udata.uuid )
4756 return trw_layer_delete_waypoint (vtl, g_hash_table_lookup ( vtl->waypoints, udata.uuid ));
4757 else
4758 return FALSE;
4759}
4760
ce4bd1cf
RN
4761typedef struct {
4762 VikTrack *trk; // input
4763 gpointer uuid; // output
4764} tpu_udata;
4765
4766// Only for temporary use by trw_layer_delete_track_by_name
4767static gboolean trw_layer_track_find_uuid_by_name ( const gpointer id, const VikTrack *trk, gpointer udata )
4768{
4769 tpu_udata *user_data = udata;
4770 if ( ! strcmp ( trk->name, user_data->trk->name ) ) {
4771 user_data->uuid = id;
4772 return TRUE;
4773 }
4774 return FALSE;
4775}
4776
4777/*
4778 * Delete a track by the given name
4779 * NOTE: ATM this will delete the first encountered Track with the specified name
0d2b891f 4780 * as there may be multiple tracks with the same name within the specified hash table
ce4bd1cf 4781 */
0d2b891f 4782static gboolean trw_layer_delete_track_by_name ( VikTrwLayer *vtl, const gchar *name, GHashTable *ht_tracks )
ce4bd1cf
RN
4783{
4784 tpu_udata udata;
4785 // Fake a track with the given name
4786 udata.trk = vik_track_new ();
4787 vik_track_set_name (udata.trk, name);
4788 // Currently only the name is used in this waypoint find function
4789 udata.uuid = NULL;
4790
4791 // Hmmm, want key of it
0d2b891f 4792 gpointer *trkf = g_hash_table_find ( ht_tracks, (GHRFunc) trw_layer_track_find_uuid_by_name, &udata );
ce4bd1cf
RN
4793
4794 vik_track_free (udata.trk);
4795
0d2b891f
RN
4796 if ( trkf && udata.uuid ) {
4797 // This could be a little better written...
4798 if ( vtl->tracks == ht_tracks )
4799 return vik_trw_layer_delete_track (vtl, g_hash_table_lookup ( ht_tracks, udata.uuid ));
4800 if ( vtl->routes == ht_tracks )
4801 return vik_trw_layer_delete_route (vtl, g_hash_table_lookup ( ht_tracks, udata.uuid ));
4802 return FALSE;
4803 }
ce4bd1cf
RN
4804 else
4805 return FALSE;
4806}
4807
9e212bfc 4808static void remove_item_from_treeview ( const gpointer id, GtkTreeIter *it, VikTreeview * vt )
700b0908
QT
4809{
4810 vik_treeview_item_delete (vt, it );
4811}
4812
0d2b891f
RN
4813void vik_trw_layer_delete_all_routes ( VikTrwLayer *vtl )
4814{
4815
4816 vtl->current_track = NULL;
0d2b891f
RN
4817 vtl->route_finder_added_track = NULL;
4818 if (vtl->current_tp_track)
4819 trw_layer_cancel_current_tp(vtl, FALSE);
4820
4821 g_hash_table_foreach(vtl->routes_iters, (GHFunc) remove_item_from_treeview, VIK_LAYER(vtl)->vt);
4822 g_hash_table_remove_all(vtl->routes_iters);
4823 g_hash_table_remove_all(vtl->routes);
4824
4825 vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, &(vtl->routes_iter) );
4826
da121f9b 4827 vik_layer_emit_update ( VIK_LAYER(vtl) );
0d2b891f
RN
4828}
4829
700b0908
QT
4830void vik_trw_layer_delete_all_tracks ( VikTrwLayer *vtl )
4831{
4832
4833 vtl->current_track = NULL;
ce4bd1cf
RN
4834 vtl->route_finder_added_track = NULL;
4835 if (vtl->current_tp_track)
700b0908 4836 trw_layer_cancel_current_tp(vtl, FALSE);
700b0908
QT
4837
4838 g_hash_table_foreach(vtl->tracks_iters, (GHFunc) remove_item_from_treeview, VIK_LAYER(vtl)->vt);
4839 g_hash_table_remove_all(vtl->tracks_iters);
4840 g_hash_table_remove_all(vtl->tracks);
4841
cd3f311e
RN
4842 vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, &(vtl->tracks_iter) );
4843
da121f9b 4844 vik_layer_emit_update ( VIK_LAYER(vtl) );
700b0908
QT
4845}
4846
4847void vik_trw_layer_delete_all_waypoints ( VikTrwLayer *vtl )
4848{
4849 vtl->current_wp = NULL;
c9570f86 4850 vtl->current_wp_id = NULL;
700b0908
QT
4851 vtl->moving_wp = FALSE;
4852
a8fe53f8
EB
4853 highest_wp_number_reset(vtl);
4854
700b0908
QT
4855 g_hash_table_foreach(vtl->waypoints_iters, (GHFunc) remove_item_from_treeview, VIK_LAYER(vtl)->vt);
4856 g_hash_table_remove_all(vtl->waypoints_iters);
4857 g_hash_table_remove_all(vtl->waypoints);
4858
cd3f311e
RN
4859 vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, &(vtl->waypoints_iter) );
4860
da121f9b 4861 vik_layer_emit_update ( VIK_LAYER(vtl) );
700b0908
QT
4862}
4863
1c2083ba 4864static void trw_layer_delete_all_tracks ( menu_array_layer values )
c9a5cbf9 4865{
1c2083ba 4866 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
c9a5cbf9
RN
4867 // Get confirmation from the user
4868 if ( a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
4869 _("Are you sure you want to delete all tracks in %s?"),
4870 vik_layer_get_name ( VIK_LAYER(vtl) ) ) )
4871 vik_trw_layer_delete_all_tracks (vtl);
4872}
4873
1c2083ba 4874static void trw_layer_delete_all_routes ( menu_array_layer values )
c9a5cbf9 4875{
1c2083ba 4876 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
c9a5cbf9
RN
4877 // Get confirmation from the user
4878 if ( a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
0d2b891f
RN
4879 _("Are you sure you want to delete all routes in %s?"),
4880 vik_layer_get_name ( VIK_LAYER(vtl) ) ) )
4881 vik_trw_layer_delete_all_routes (vtl);
c9a5cbf9
RN
4882}
4883
1c2083ba 4884static void trw_layer_delete_all_waypoints ( menu_array_layer values )
0d2b891f 4885{
1c2083ba 4886 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
0d2b891f
RN
4887 // Get confirmation from the user
4888 if ( a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
4889 _("Are you sure you want to delete all waypoints in %s?"),
4890 vik_layer_get_name ( VIK_LAYER(vtl) ) ) )
4891 vik_trw_layer_delete_all_waypoints (vtl);
4892}
4893
19782ffd 4894static void trw_layer_delete_item ( menu_array_sublayer values )
50a14534 4895{
19782ffd 4896 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
50a14534 4897 gboolean was_visible = FALSE;
19782ffd 4898 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_WAYPOINT )
50a14534 4899 {
19782ffd 4900 VikWaypoint *wp = g_hash_table_lookup ( vtl->waypoints, values[MA_SUBLAYER_ID] );
c9570f86 4901 if ( wp && wp->name ) {
19782ffd 4902 if ( GPOINTER_TO_INT (values[MA_CONFIRM]) )
c9570f86
RN
4903 // Get confirmation from the user
4904 // Maybe this Waypoint Delete should be optional as is it could get annoying...
4905 if ( ! a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
ea3a6072 4906 _("Are you sure you want to delete the waypoint \"%s\"?"),
c9570f86
RN
4907 wp->name ) )
4908 return;
4909 was_visible = trw_layer_delete_waypoint ( vtl, wp );
4910 }
50a14534 4911 }
19782ffd 4912 else if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_TRACK )
50a14534 4913 {
19782ffd 4914 VikTrack *trk = g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
ce4bd1cf 4915 if ( trk && trk->name ) {
19782ffd 4916 if ( GPOINTER_TO_INT (values[MA_CONFIRM]) )
ce4bd1cf
RN
4917 // Get confirmation from the user
4918 if ( ! a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
ea3a6072 4919 _("Are you sure you want to delete the track \"%s\"?"),
ce4bd1cf
RN
4920 trk->name ) )
4921 return;
4922 was_visible = vik_trw_layer_delete_track ( vtl, trk );
4923 }
50a14534 4924 }
0d2b891f
RN
4925 else
4926 {
19782ffd 4927 VikTrack *trk = g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
0d2b891f 4928 if ( trk && trk->name ) {
19782ffd 4929 if ( GPOINTER_TO_INT (values[MA_CONFIRM]) )
0d2b891f
RN
4930 // Get confirmation from the user
4931 if ( ! a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
ea3a6072 4932 _("Are you sure you want to delete the route \"%s\"?"),
0d2b891f
RN
4933 trk->name ) )
4934 return;
4935 was_visible = vik_trw_layer_delete_route ( vtl, trk );
4936 }
4937 }
50a14534 4938 if ( was_visible )
da121f9b 4939 vik_layer_emit_update ( VIK_LAYER(vtl) );
50a14534
EB
4940}
4941
6761d8a4
RN
4942/**
4943 * Rename waypoint and maintain corresponding name of waypoint in the treeview
4944 */
4f556508 4945void trw_layer_waypoint_rename ( VikTrwLayer *vtl, VikWaypoint *wp, const gchar *new_name )
6761d8a4
RN
4946{
4947 vik_waypoint_set_name ( wp, new_name );
4948
4949 // Now update the treeview as well
4950 wpu_udata udataU;
4951 udataU.wp = wp;
4952 udataU.uuid = NULL;
4953
4954 // Need key of it for treeview update
4955 gpointer *wpf = g_hash_table_find ( vtl->waypoints, (GHRFunc) trw_layer_waypoint_find_uuid, &udataU );
4956
4957 if ( wpf && udataU.uuid ) {
4958 GtkTreeIter *it = g_hash_table_lookup ( vtl->waypoints_iters, udataU.uuid );
4959
4960 if ( it ) {
4961 vik_treeview_item_set_name ( VIK_LAYER(vtl)->vt, it, new_name );
c9cac058 4962 vik_treeview_sort_children ( VIK_LAYER(vtl)->vt, &(vtl->waypoints_iter), vtl->wp_sort_order );
6761d8a4
RN
4963 }
4964 }
4965}
50a14534 4966
4f556508
RN
4967/**
4968 * Maintain icon of waypoint in the treeview
4969 */
4970void trw_layer_waypoint_reset_icon ( VikTrwLayer *vtl, VikWaypoint *wp )
4971{
4972 // update the treeview
4973 wpu_udata udataU;
4974 udataU.wp = wp;
4975 udataU.uuid = NULL;
4976
4977 // Need key of it for treeview update
4978 gpointer *wpf = g_hash_table_find ( vtl->waypoints, (GHRFunc) trw_layer_waypoint_find_uuid, &udataU );
4979
4980 if ( wpf && udataU.uuid ) {
4981 GtkTreeIter *it = g_hash_table_lookup ( vtl->waypoints_iters, udataU.uuid );
4982
4983 if ( it ) {
4984 vik_treeview_item_set_icon ( VIK_LAYER(vtl)->vt, it, get_wp_sym_small (wp->symbol) );
4985 }
4986 }
4987}
4988
19782ffd 4989static void trw_layer_properties_item ( menu_array_sublayer values )
50a14534 4990{
19782ffd
RN
4991 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
4992 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_WAYPOINT )
50a14534 4993 {
19782ffd 4994 VikWaypoint *wp = g_hash_table_lookup ( vtl->waypoints, values[MA_SUBLAYER_ID] );
c9570f86
RN
4995
4996 if ( wp && wp->name )
50a14534 4997 {
ac1bde8b 4998 gboolean updated = FALSE;
6761d8a4
RN
4999 gchar *new_name = a_dialog_waypoint ( VIK_GTK_WINDOW_FROM_LAYER(vtl), wp->name, vtl, wp, vtl->coord_mode, FALSE, &updated );
5000 if ( new_name )
5001 trw_layer_waypoint_rename ( vtl, wp, new_name );
50a14534 5002
19782ffd
RN
5003 if ( updated && values[MA_TV_ITER] )
5004 vik_treeview_item_set_icon ( VIK_LAYER(vtl)->vt, values[MA_TV_ITER], get_wp_sym_small (wp->symbol) );
70cefedc 5005
ac1bde8b 5006 if ( updated && VIK_LAYER(vtl)->visible )
da121f9b 5007 vik_layer_emit_update ( VIK_LAYER(vtl) );
50a14534
EB
5008 }
5009 }
5010 else
5011 {
0d2b891f 5012 VikTrack *tr;
19782ffd
RN
5013 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_TRACK )
5014 tr = g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
0d2b891f 5015 else
19782ffd 5016 tr = g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
0d2b891f 5017
ce4bd1cf 5018 if ( tr && tr->name )
50a14534 5019 {
21700912 5020 vik_trw_layer_propwin_run ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
19782ffd 5021 vtl,
b1453c16 5022 tr,
19782ffd
RN
5023 values[MA_VLP],
5024 values[MA_VVP],
626de648 5025 FALSE );
50a14534
EB
5026 }
5027 }
5028}
5029
626de648
RN
5030/**
5031 * trw_layer_track_statistics:
5032 *
5033 * Show track statistics.
5034 * ATM jump to the stats page in the properties
5035 * TODO: consider separating the stats into an individual dialog?
5036 */
19782ffd 5037static void trw_layer_track_statistics ( menu_array_sublayer values )
626de648 5038{
19782ffd 5039 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
626de648 5040 VikTrack *trk;
19782ffd
RN
5041 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_TRACK )
5042 trk = g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
626de648 5043 else
19782ffd 5044 trk = g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
626de648
RN
5045
5046 if ( trk && trk->name ) {
5047 vik_trw_layer_propwin_run ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
5048 vtl,
5049 trk,
19782ffd
RN
5050 values[MA_VLP],
5051 values[MA_VVP],
626de648
RN
5052 TRUE );
5053 }
5054}
5055
b1453c16
RN
5056/*
5057 * Update the treeview of the track id - primarily to update the icon
5058 */
93ee73b3 5059void trw_layer_update_treeview ( VikTrwLayer *vtl, VikTrack *trk )
b1453c16
RN
5060{
5061 trku_udata udata;
5062 udata.trk = trk;
5063 udata.uuid = NULL;
5064
5065 gpointer *trkf = NULL;
5066 if ( trk->is_route )
5067 trkf = g_hash_table_find ( vtl->routes, (GHRFunc) trw_layer_track_find_uuid, &udata );
5068 else
5069 trkf = g_hash_table_find ( vtl->tracks, (GHRFunc) trw_layer_track_find_uuid, &udata );
5070
5071 if ( trkf && udata.uuid ) {
5072
5073 GtkTreeIter *iter = NULL;
5074 if ( trk->is_route )
5075 iter = g_hash_table_lookup ( vtl->routes_iters, udata.uuid );
5076 else
5077 iter = g_hash_table_lookup ( vtl->tracks_iters, udata.uuid );
5078
5079 if ( iter ) {
5080 // TODO: Make this a function
5081 GdkPixbuf *pixbuf = gdk_pixbuf_new ( GDK_COLORSPACE_RGB, FALSE, 8, 18, 18);
5082 guint32 pixel = ((trk->color.red & 0xff00) << 16) |
5083 ((trk->color.green & 0xff00) << 8) |
5084 (trk->color.blue & 0xff00);
5085 gdk_pixbuf_fill ( pixbuf, pixel );
5086 vik_treeview_item_set_icon ( VIK_LAYER(vtl)->vt, iter, pixbuf );
5087 g_object_unref (pixbuf);
5088 }
5089
5090 }
5091}
5092
6bb72350
RN
5093/*
5094 Parameter 1 -> VikLayersPanel
5095 Parameter 2 -> VikLayer
5096 Parameter 3 -> VikViewport
5097*/
5098static void goto_coord ( gpointer *vlp, gpointer vl, gpointer vvp, const VikCoord *coord )
5099{
5100 if ( vlp ) {
be5554c5 5101 vik_viewport_set_center_coord ( vik_layers_panel_get_viewport (VIK_LAYERS_PANEL(vlp)), coord, TRUE );
6bb72350
RN
5102 vik_layers_panel_emit_update ( VIK_LAYERS_PANEL(vlp) );
5103 }
5104 else {
5105 /* since vlp not set, vl & vvp should be valid instead! */
5106 if ( vl && vvp ) {
be5554c5 5107 vik_viewport_set_center_coord ( VIK_VIEWPORT(vvp), coord, TRUE );
da121f9b 5108 vik_layer_emit_update ( VIK_LAYER(vl) );
6bb72350
RN
5109 }
5110 }
50a14534
EB
5111}
5112
19782ffd 5113static void trw_layer_goto_track_startpoint ( menu_array_sublayer values )
50a14534 5114{
19782ffd 5115 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
0d2b891f 5116 VikTrack *track;
19782ffd
RN
5117 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5118 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
0d2b891f 5119 else
19782ffd 5120 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
0d2b891f
RN
5121
5122 if ( track && track->trackpoints )
215ebe59 5123 goto_coord ( values[MA_VLP], vtl, values[MA_VVP], &(vik_track_get_tp_first(track)->coord) );
50a14534
EB
5124}
5125
19782ffd 5126static void trw_layer_goto_track_center ( menu_array_sublayer values )
50a14534 5127{
19782ffd 5128 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
0d2b891f 5129 VikTrack *track;
19782ffd
RN
5130 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5131 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
0d2b891f 5132 else
19782ffd 5133 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
0d2b891f 5134
867f4673 5135 if ( track && track->trackpoints )
50a14534
EB
5136 {
5137 struct LatLon average, maxmin[2] = { {0,0}, {0,0} };
5138 VikCoord coord;
867f4673 5139 trw_layer_find_maxmin_tracks ( NULL, track, maxmin );
50a14534
EB
5140 average.lat = (maxmin[0].lat+maxmin[1].lat)/2;
5141 average.lon = (maxmin[0].lon+maxmin[1].lon)/2;
0d2b891f 5142 vik_coord_load_from_latlon ( &coord, vtl->coord_mode, &average );
19782ffd 5143 goto_coord ( values[MA_VLP], vtl, values[MA_VVP], &coord);
50a14534
EB
5144 }
5145}
5146
19782ffd 5147static void trw_layer_convert_track_route ( menu_array_sublayer values )
2f5d7ea1 5148{
19782ffd 5149 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
2f5d7ea1 5150 VikTrack *trk;
19782ffd
RN
5151 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5152 trk = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
2f5d7ea1 5153 else
19782ffd 5154 trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
2f5d7ea1
RN
5155
5156 if ( !trk )
5157 return;
5158
5159 // Converting a track to a route can be a bit more complicated,
5160 // so give a chance to change our minds:
5161 if ( !trk->is_route &&
5162 ( ( vik_track_get_segment_count ( trk ) > 1 ) ||
5163 ( vik_track_get_average_speed ( trk ) > 0.0 ) ) ) {
5164
5165 if ( ! a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
5166 _("Converting a track to a route removes extra track data such as segments, timestamps, etc...\nDo you want to continue?"), NULL ) )
5167 return;
5168}
5169
5170 // Copy it
03817fbf 5171 VikTrack *trk_copy = vik_track_copy ( trk, TRUE );
2f5d7ea1
RN
5172
5173 // Convert
5174 trk_copy->is_route = !trk_copy->is_route;
5175
5176 // ATM can't set name to self - so must create temporary copy
5177 gchar *name = g_strdup ( trk_copy->name );
5178
5179 // Delete old one and then add new one
5180 if ( trk->is_route ) {
5181 vik_trw_layer_delete_route ( vtl, trk );
5182 vik_trw_layer_add_track ( vtl, name, trk_copy );
5183 }
5184 else {
5185 // Extra route conversion bits...
5186 vik_track_merge_segments ( trk_copy );
5187 vik_track_to_routepoints ( trk_copy );
5188
5189 vik_trw_layer_delete_track ( vtl, trk );
5190 vik_trw_layer_add_route ( vtl, name, trk_copy );
5191 }
5192 g_free ( name );
5193
5194 // Update in case color of track / route changes when moving between sublayers
19782ffd 5195 vik_layer_emit_update ( VIK_LAYER(vtl) );
2f5d7ea1
RN
5196}
5197
19782ffd 5198static void trw_layer_anonymize_times ( menu_array_sublayer values )
76b14439 5199{
19782ffd 5200 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
76b14439 5201 VikTrack *track;
19782ffd
RN
5202 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5203 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
76b14439 5204 else
19782ffd 5205 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
76b14439
RN
5206
5207 if ( track )
5208 vik_track_anonymize_times ( track );
5209}
2f5d7ea1 5210
19782ffd 5211static void trw_layer_extend_track_end ( menu_array_sublayer values )
8fb71d6c 5212{
19782ffd 5213 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
0d2b891f 5214 VikTrack *track;
19782ffd
RN
5215 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5216 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
0d2b891f 5217 else
19782ffd 5218 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
0d2b891f
RN
5219
5220 if ( !track )
5221 return;
8fb71d6c
EB
5222
5223 vtl->current_track = track;
e37b2a6d 5224 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
5225
5226 if ( track->trackpoints )
215ebe59 5227 goto_coord ( values[MA_VLP], vtl, values[MA_VVP], &(vik_track_get_tp_last(track)->coord) );
8fb71d6c
EB
5228}
5229
e3154bef 5230/**
7ff7d728 5231 * extend a track using route finder
e3154bef 5232 */
19782ffd 5233static void trw_layer_extend_track_end_route_finder ( menu_array_sublayer values )
a7955c1d 5234{
19782ffd
RN
5235 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
5236 VikTrack *track = g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
008e972c
RN
5237 if ( !track )
5238 return;
a7955c1d 5239
37baade6 5240 vik_window_enable_layer_tool ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)), VIK_LAYER_TRW, TOOL_ROUTE_FINDER );
ee43b101 5241 vtl->current_track = track;
7ff7d728 5242 vtl->route_finder_started = TRUE;
a7955c1d 5243
ee43b101
MH
5244 if ( track->trackpoints )
5245 goto_coord ( values[MA_VLP], vtl, values[MA_VVP], &vik_track_get_tp_last(track)->coord );
a7955c1d 5246}
a7955c1d 5247
4d333042 5248/**
4d333042 5249 *
4d333042 5250 */
667fda15 5251static gboolean trw_layer_dem_test ( VikTrwLayer *vtl, VikLayersPanel *vlp )
4d333042 5252{
73747c42
RN
5253 // If have a vlp then perform a basic test to see if any DEM info available...
5254 if ( vlp ) {
5255 GList *dems = vik_layers_panel_get_all_layers_of_type (vlp, VIK_LAYER_DEM, TRUE); // Includes hidden DEM layer types
5256
5257 if ( !g_list_length(dems) ) {
5258 a_dialog_error_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), _("No DEM layers available, thus no DEM values can be applied.") );
667fda15 5259 return FALSE;
73747c42
RN
5260 }
5261 }
667fda15
RN
5262 return TRUE;
5263}
5264
5265/**
5266 * apply_dem_data_common:
5267 *
5268 * A common function for applying the DEM values and reporting the results.
5269 */
5270static void apply_dem_data_common ( VikTrwLayer *vtl, VikLayersPanel *vlp, VikTrack *track, gboolean skip_existing_elevations )
5271{
5272 if ( !trw_layer_dem_test ( vtl, vlp ) )
5273 return;
73747c42 5274
4d333042
RN
5275 gulong changed = vik_track_apply_dem_data ( track, skip_existing_elevations );
5276 // Inform user how much was changed
5277 gchar str[64];
5278 const gchar *tmp_str = ngettext("%ld point adjusted", "%ld points adjusted", changed);
5279 g_snprintf(str, 64, tmp_str, changed);
5280 a_dialog_info_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), str);
5281}
5282
19782ffd 5283static void trw_layer_apply_dem_data_all ( menu_array_sublayer values )
4d333042 5284{
19782ffd 5285 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
4d333042 5286 VikTrack *track;
19782ffd
RN
5287 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5288 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
4d333042 5289 else
19782ffd 5290 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
4d333042
RN
5291
5292 if ( track )
19782ffd 5293 apply_dem_data_common ( vtl, values[MA_VLP], track, FALSE );
4d333042
RN
5294}
5295
19782ffd 5296static void trw_layer_apply_dem_data_only_missing ( menu_array_sublayer values )
ad0a8c2d 5297{
19782ffd 5298 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
0d2b891f 5299 VikTrack *track;
19782ffd
RN
5300 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5301 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
0d2b891f 5302 else
19782ffd 5303 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
ad0a8c2d 5304
0d2b891f 5305 if ( track )
19782ffd 5306 apply_dem_data_common ( vtl, values[MA_VLP], track, TRUE );
ad0a8c2d
EB
5307}
5308
81ac2835
RN
5309/**
5310 * smooth_it:
5311 *
5312 * A common function for applying the elevation smoothing and reporting the results.
5313 */
5314static void smooth_it ( VikTrwLayer *vtl, VikTrack *track, gboolean flat )
5315{
5316 gulong changed = vik_track_smooth_missing_elevation_data ( track, flat );
5317 // Inform user how much was changed
5318 gchar str[64];
5319 const gchar *tmp_str = ngettext("%ld point adjusted", "%ld points adjusted", changed);
5320 g_snprintf(str, 64, tmp_str, changed);
5321 a_dialog_info_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), str);
5322}
5323
5324/**
5325 *
5326 */
19782ffd 5327static void trw_layer_missing_elevation_data_interp ( menu_array_sublayer values )
81ac2835 5328{
19782ffd 5329 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
81ac2835 5330 VikTrack *track;
19782ffd
RN
5331 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5332 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
81ac2835 5333 else
19782ffd 5334 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
81ac2835
RN
5335
5336 if ( !track )
5337 return;
5338
5339 smooth_it ( vtl, track, FALSE );
5340}
5341
19782ffd 5342static void trw_layer_missing_elevation_data_flat ( menu_array_sublayer values )
81ac2835 5343{
19782ffd 5344 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
81ac2835 5345 VikTrack *track;
19782ffd
RN
5346 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5347 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
81ac2835 5348 else
19782ffd 5349 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
81ac2835
RN
5350
5351 if ( !track )
5352 return;
5353
5354 smooth_it ( vtl, track, TRUE );
5355}
5356
667fda15
RN
5357/**
5358 * Commonal helper function
5359 */
5360static void wp_changed_message ( VikTrwLayer *vtl, gint changed )
5361{
5362 gchar str[64];
5363 const gchar *tmp_str = ngettext("%ld waypoint changed", "%ld waypoints changed", changed);
5364 g_snprintf(str, 64, tmp_str, changed);
5365 a_dialog_info_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), str);
5366}
5367
19782ffd 5368static void trw_layer_apply_dem_data_wpt_all ( menu_array_sublayer values )
667fda15 5369{
19782ffd
RN
5370 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
5371 VikLayersPanel *vlp = (VikLayersPanel *)values[MA_VLP];
667fda15
RN
5372
5373 if ( !trw_layer_dem_test ( vtl, vlp ) )
5374 return;
5375
5376 gint changed = 0;
19782ffd 5377 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_WAYPOINT ) {
667fda15 5378 // Single Waypoint
19782ffd 5379 VikWaypoint *wp = (VikWaypoint *) g_hash_table_lookup ( vtl->waypoints, values[MA_SUBLAYER_ID] );
667fda15
RN
5380 if ( wp )
5381 changed = (gint)vik_waypoint_apply_dem_data ( wp, FALSE );
5382 }
5383 else {
5384 // All waypoints
5385 GHashTableIter iter;
5386 gpointer key, value;
5387
5388 g_hash_table_iter_init ( &iter, vtl->waypoints );
5389 while ( g_hash_table_iter_next (&iter, &key, &value) ) {
5390 VikWaypoint *wp = VIK_WAYPOINT(value);
5391 changed = changed + (gint)vik_waypoint_apply_dem_data ( wp, FALSE );
5392 }
5393 }
5394 wp_changed_message ( vtl, changed );
5395}
5396
19782ffd 5397static void trw_layer_apply_dem_data_wpt_only_missing ( menu_array_sublayer values )
667fda15 5398{
19782ffd
RN
5399 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
5400 VikLayersPanel *vlp = (VikLayersPanel *)values[MA_VLP];
667fda15
RN
5401
5402 if ( !trw_layer_dem_test ( vtl, vlp ) )
5403 return;
5404
5405 gint changed = 0;
19782ffd 5406 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_WAYPOINT ) {
667fda15 5407 // Single Waypoint
19782ffd 5408 VikWaypoint *wp = (VikWaypoint *) g_hash_table_lookup ( vtl->waypoints, values[MA_SUBLAYER_ID] );
667fda15
RN
5409 if ( wp )
5410 changed = (gint)vik_waypoint_apply_dem_data ( wp, TRUE );
5411 }
5412 else {
5413 // All waypoints
5414 GHashTableIter iter;
5415 gpointer key, value;
5416
5417 g_hash_table_iter_init ( &iter, vtl->waypoints );
5418 while ( g_hash_table_iter_next (&iter, &key, &value) ) {
5419 VikWaypoint *wp = VIK_WAYPOINT(value);
5420 changed = changed + (gint)vik_waypoint_apply_dem_data ( wp, TRUE );
5421 }
5422 }
5423 wp_changed_message ( vtl, changed );
5424}
5425
19782ffd 5426static void trw_layer_goto_track_endpoint ( menu_array_sublayer values )
50a14534 5427{
19782ffd 5428 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
0d2b891f 5429 VikTrack *track;
19782ffd
RN
5430 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5431 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
0d2b891f 5432 else
19782ffd 5433 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
0d2b891f
RN
5434
5435 if ( !track )
5436 return;
215ebe59 5437 if ( !track->trackpoints )
50a14534 5438 return;
215ebe59 5439 goto_coord ( values[MA_VLP], vtl, values[MA_VVP], &(vik_track_get_tp_last(track)->coord));
50a14534
EB
5440}
5441
19782ffd 5442static void trw_layer_goto_track_max_speed ( menu_array_sublayer values )
03e7da75 5443{
19782ffd 5444 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
0d2b891f 5445 VikTrack *track;
19782ffd
RN
5446 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5447 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
0d2b891f 5448 else
19782ffd 5449 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
0d2b891f
RN
5450
5451 if ( !track )
5452 return;
5453
5454 VikTrackpoint* vtp = vik_track_get_tp_by_max_speed ( track );
03e7da75
RN
5455 if ( !vtp )
5456 return;
19782ffd 5457 goto_coord ( values[MA_VLP], vtl, values[MA_VVP], &(vtp->coord));
03e7da75
RN
5458}
5459
19782ffd 5460static void trw_layer_goto_track_max_alt ( menu_array_sublayer values )
c28faca8 5461{
19782ffd 5462 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
0d2b891f 5463 VikTrack *track;
19782ffd
RN
5464 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5465 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
0d2b891f 5466 else
19782ffd 5467 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
0d2b891f
RN
5468
5469 if ( !track )
5470 return;
5471
5472 VikTrackpoint* vtp = vik_track_get_tp_by_max_alt ( track );
c28faca8
RN
5473 if ( !vtp )
5474 return;
19782ffd 5475 goto_coord ( values[MA_VLP], vtl, values[MA_VVP], &(vtp->coord));
c28faca8
RN
5476}
5477
19782ffd 5478static void trw_layer_goto_track_min_alt ( menu_array_sublayer values )
c28faca8 5479{
19782ffd 5480 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
0d2b891f 5481 VikTrack *track;
19782ffd
RN
5482 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5483 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
0d2b891f 5484 else
19782ffd 5485 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
0d2b891f
RN
5486
5487 if ( !track )
5488 return;
5489
5490 VikTrackpoint* vtp = vik_track_get_tp_by_min_alt ( track );
c28faca8
RN
5491 if ( !vtp )
5492 return;
19782ffd 5493 goto_coord ( values[MA_VLP], vtl, values[MA_VVP], &(vtp->coord));
c28faca8 5494}
111fa174 5495
116ea336 5496/*
469113fb 5497 * Automatically change the viewport to center on the track and zoom to see the extent of the track
116ea336 5498 */
19782ffd 5499static void trw_layer_auto_track_view ( menu_array_sublayer values )
469113fb 5500{
19782ffd 5501 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
0d2b891f 5502 VikTrack *trk;
19782ffd
RN
5503 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5504 trk = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
0d2b891f 5505 else
19782ffd 5506 trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
0d2b891f 5507
867f4673 5508 if ( trk && trk->trackpoints )
469113fb
RN
5509 {
5510 struct LatLon maxmin[2] = { {0,0}, {0,0} };
867f4673 5511 trw_layer_find_maxmin_tracks ( NULL, trk, maxmin );
19782ffd
RN
5512 trw_layer_zoom_to_show_latlons ( vtl, values[MA_VVP], maxmin );
5513 if ( values[MA_VLP] )
5514 vik_layers_panel_emit_update ( VIK_LAYERS_PANEL(values[MA_VLP]) );
6bb72350 5515 else
11651e51 5516 vik_layer_emit_update ( VIK_LAYER(vtl) );
469113fb
RN
5517 }
5518}
5519
1a3be6a8
GB
5520/*
5521 * Refine the selected track/route with a routing engine.
5522 * The routing engine is selected by the user, when requestiong the job.
5523 */
19782ffd 5524static void trw_layer_route_refine ( menu_array_sublayer values )
1a3be6a8
GB
5525{
5526 static gint last_engine = 0;
19782ffd 5527 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
1a3be6a8
GB
5528 VikTrack *trk;
5529
19782ffd
RN
5530 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5531 trk = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
1a3be6a8 5532 else
19782ffd 5533 trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
1a3be6a8
GB
5534
5535 if ( trk && trk->trackpoints )
5536 {
80846d50
GB
5537 /* Check size of the route */
5538 int nb = vik_track_get_tp_count(trk);
5539 if (nb > 100) {
5540 GtkWidget *dialog = gtk_message_dialog_new (VIK_GTK_WINDOW_FROM_LAYER (vtl),
5541 GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
5542 GTK_MESSAGE_WARNING,
5543 GTK_BUTTONS_OK_CANCEL,
5544 _("Refining a track with many points (%d) is unlikely to yield sensible results. Do you want to Continue?"),
5545 nb);
5546 gint response = gtk_dialog_run ( GTK_DIALOG(dialog) );
5547 gtk_widget_destroy ( dialog );
5548 if (response != GTK_RESPONSE_OK )
5549 return;
5550 }
1a3be6a8
GB
5551 /* Select engine from dialog */
5552 GtkWidget *dialog = gtk_dialog_new_with_buttons (_("Refine Route with Routing Engine..."),
5553 VIK_GTK_WINDOW_FROM_LAYER (vtl),
5554 GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
5555 GTK_STOCK_CANCEL,
5556 GTK_RESPONSE_REJECT,
5557 GTK_STOCK_OK,
5558 GTK_RESPONSE_ACCEPT,
5559 NULL);
5560 GtkWidget *label = gtk_label_new ( _("Select routing engine") );
5561 gtk_widget_show_all(label);
5562
5563 gtk_box_pack_start ( GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), label, TRUE, TRUE, 0 );
5564
5565 GtkWidget * combo = vik_routing_ui_selector_new ( (Predicate)vik_routing_engine_supports_refine, NULL );
5566 gtk_combo_box_set_active (GTK_COMBO_BOX (combo), last_engine);
5567 gtk_widget_show_all(combo);
5568
5569 gtk_box_pack_start ( GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), combo, TRUE, TRUE, 0 );
5570
5571 gtk_dialog_set_default_response ( GTK_DIALOG(dialog), GTK_RESPONSE_ACCEPT );
5572
5573 if ( gtk_dialog_run ( GTK_DIALOG(dialog) ) == GTK_RESPONSE_ACCEPT )
5574 {
5575 /* Dialog validated: retrieve selected engine and do the job */
5576 last_engine = gtk_combo_box_get_active ( GTK_COMBO_BOX(combo) );
5577 VikRoutingEngine *routing = vik_routing_ui_selector_get_nth (combo, last_engine);
5578
5579 /* Change cursor */
19782ffd 5580 vik_window_set_busy_cursor ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)) );
1a3be6a8
GB
5581
5582 /* Force saving track */
5583 /* FIXME: remove or rename this hack */
5584 vtl->route_finder_check_added_track = TRUE;
5585
5586 /* the job */
5587 vik_routing_engine_refine (routing, vtl, trk);
5588
5589 /* FIXME: remove or rename this hack */
5590 if ( vtl->route_finder_added_track )
5591 vik_track_calculate_bounds ( vtl->route_finder_added_track );
5592
5593 vtl->route_finder_added_track = NULL;
5594 vtl->route_finder_check_added_track = FALSE;
5595
5596 vik_layer_emit_update ( VIK_LAYER(vtl) );
5597
5598 /* Restore cursor */
19782ffd 5599 vik_window_clear_busy_cursor ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)) );
1a3be6a8
GB
5600 }
5601 gtk_widget_destroy ( dialog );
5602 }
5603}
5604
19782ffd 5605static void trw_layer_edit_trackpoint ( menu_array_sublayer values )
c95d6b00 5606{
19782ffd 5607 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
c95d6b00
RN
5608 trw_layer_tpwin_init ( vtl );
5609}
5610
111fa174
AF
5611/*************************************
5612 * merge/split by time routines
5613 *************************************/
5614
15f45edc 5615/* called for each key in track hash table.
5780603d 5616 * If the current track has the same time stamp type, add it to the result,
15f45edc
QT
5617 * except the one pointed by "exclude".
5618 * set exclude to NULL if there is no exclude to check.
5780603d 5619 * Note that the result is in reverse (for performance reasons).
15f45edc
QT
5620 */
5621typedef struct {
5622 GList **result;
9cdc601f 5623 VikTrack *exclude;
5780603d 5624 gboolean with_timestamps;
15f45edc 5625} twt_udata;
5780603d 5626static void find_tracks_with_timestamp_type(gpointer key, gpointer value, gpointer udata)
15f45edc
QT
5627{
5628 twt_udata *user_data = udata;
5629 VikTrackpoint *p1, *p2;
215ebe59 5630 VikTrack *trk = VIK_TRACK(value);
9cdc601f 5631 if (trk == user_data->exclude) {
15f45edc
QT
5632 return;
5633 }
5634
215ebe59
RN
5635 if (trk->trackpoints) {
5636 p1 = vik_track_get_tp_first(trk);
5637 p2 = vik_track_get_tp_last(trk);
15f45edc 5638
5780603d
RN
5639 if ( user_data->with_timestamps ) {
5640 if (!p1->has_timestamp || !p2->has_timestamp) {
5641 return;
5642 }
5643 }
5644 else {
5645 // Don't add tracks with timestamps when getting non timestamp tracks
5646 if (p1->has_timestamp || p2->has_timestamp) {
5647 return;
5648 }
15f45edc 5649 }
15f45edc
QT
5650 }
5651
5652 *(user_data->result) = g_list_prepend(*(user_data->result), key);
5653}
5654
111fa174
AF
5655/* called for each key in track hash table. if original track user_data[1] is close enough
5656 * to the passed one, add it to list in user_data[0]
5657 */
fc92c977 5658static void find_nearby_tracks_by_time (gpointer key, gpointer value, gpointer user_data)
111fa174
AF
5659{
5660 time_t t1, t2;
5661 VikTrackpoint *p1, *p2;
fc92c977 5662 VikTrack *trk = VIK_TRACK(value);
111fa174
AF
5663
5664 GList **nearby_tracks = ((gpointer *)user_data)[0];
111fa174 5665
a61b2619
AF
5666 /* outline:
5667 * detect reasons for not merging, and return
5668 * if no reason is found not to merge, then do it.
5669 */
111fa174 5670
9cdc601f 5671 twt_udata *udata = user_data;
fc92c977 5672 // Exclude the original track from the compiled list
9cdc601f 5673 if (trk == udata->exclude) {
111fa174
AF
5674 return;
5675 }
5676
215ebe59
RN
5677 t1 = vik_track_get_tp_first(trk)->timestamp;
5678 t2 = vik_track_get_tp_last(trk)->timestamp;
111fa174 5679
fc92c977 5680 if (trk->trackpoints) {
215ebe59
RN
5681 p1 = vik_track_get_tp_first(trk);
5682 p2 = vik_track_get_tp_last(trk);
a61b2619
AF
5683
5684 if (!p1->has_timestamp || !p2->has_timestamp) {
fc92c977 5685 //g_print("no timestamp\n");
a61b2619
AF
5686 return;
5687 }
111fa174 5688
fc92c977
RN
5689 guint threshold = GPOINTER_TO_UINT (((gpointer *)user_data)[2]);
5690 //g_print("Got track named %s, times %d, %d\n", trk->name, p1->timestamp, p2->timestamp);
5691 if (! (abs(t1 - p2->timestamp) < threshold ||
a61b2619 5692 /* p1 p2 t1 t2 */
fc92c977 5693 abs(p1->timestamp - t2) < threshold)
a61b2619
AF
5694 /* t1 t2 p1 p2 */
5695 ) {
5696 return;
5697 }
111fa174 5698 }
a61b2619 5699
fc92c977 5700 *nearby_tracks = g_list_prepend(*nearby_tracks, value);
111fa174
AF
5701}
5702
5703/* comparison function used to sort tracks; a and b are hash table keys */
02b5d347 5704/* Not actively used - can be restored if needed
111fa174
AF
5705static gint track_compare(gconstpointer a, gconstpointer b, gpointer user_data)
5706{
5707 GHashTable *tracks = user_data;
5708 time_t t1, t2;
5709
5710 t1 = VIK_TRACKPOINT(VIK_TRACK(g_hash_table_lookup(tracks, a))->trackpoints->data)->timestamp;
5711 t2 = VIK_TRACKPOINT(VIK_TRACK(g_hash_table_lookup(tracks, b))->trackpoints->data)->timestamp;
5712
5713 if (t1 < t2) return -1;
5714 if (t1 > t2) return 1;
5715 return 0;
5716}
02b5d347 5717*/
111fa174
AF
5718
5719/* comparison function used to sort trackpoints */
5720static gint trackpoint_compare(gconstpointer a, gconstpointer b)
5721{
5722 time_t t1 = VIK_TRACKPOINT(a)->timestamp, t2 = VIK_TRACKPOINT(b)->timestamp;
5723
5724 if (t1 < t2) return -1;
5725 if (t1 > t2) return 1;
5726 return 0;
5727}
5728
fb2306f7
RN
5729/**
5730 * comparison function which can be used to sort tracks or waypoints by name
5731 */
5732static gint sort_alphabetically (gconstpointer a, gconstpointer b, gpointer user_data)
5733{
5734 const gchar* namea = (const gchar*) a;
5735 const gchar* nameb = (const gchar*) b;
5736 if ( namea == NULL || nameb == NULL)
5737 return 0;
5738 else
5739 // Same sort method as used in the vik_treeview_*_alphabetize functions
5740 return strcmp ( namea, nameb );
5741}
fb2306f7 5742
5780603d
RN
5743/**
5744 * Attempt to merge selected track with other tracks specified by the user
5745 * Tracks to merge with must be of the same 'type' as the selected track -
5746 * either all with timestamps, or all without timestamps
5747 */
19782ffd 5748static void trw_layer_merge_with_other ( menu_array_sublayer values )
291edcab 5749{
19782ffd 5750 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
5780603d 5751 GList *other_tracks = NULL;
0d2b891f 5752 GHashTable *ght_tracks;
19782ffd 5753 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
0d2b891f
RN
5754 ght_tracks = vtl->routes;
5755 else
5756 ght_tracks = vtl->tracks;
5757
19782ffd 5758 VikTrack *track = (VikTrack *) g_hash_table_lookup ( ght_tracks, values[MA_SUBLAYER_ID] );
0d2b891f
RN
5759
5760 if ( !track )
5761 return;
15f45edc 5762
5780603d 5763 if ( !track->trackpoints )
15f45edc 5764 return;
15f45edc 5765
c1564279 5766 twt_udata udata;
5780603d 5767 udata.result = &other_tracks;
9cdc601f 5768 udata.exclude = track;
5780603d
RN
5769 // Allow merging with 'similar' time type time tracks
5770 // i.e. either those times, or those without
215ebe59 5771 udata.with_timestamps = vik_track_get_tp_first(track)->has_timestamp;
291edcab 5772
0d2b891f 5773 g_hash_table_foreach(ght_tracks, find_tracks_with_timestamp_type, (gpointer)&udata);
5780603d
RN
5774 other_tracks = g_list_reverse(other_tracks);
5775
5776 if ( !other_tracks ) {
5777 if ( udata.with_timestamps )
5778 a_dialog_error_msg(VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Failed. No other tracks with timestamps in this layer found"));
5779 else
5780 a_dialog_error_msg(VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Failed. No other tracks without timestamps in this layer found"));
15f45edc
QT
5781 return;
5782 }
5783
8352326e 5784 // Sort alphabetically for user presentation
ce4bd1cf
RN
5785 // Convert into list of names for usage with dialog function
5786 // TODO: Need to consider how to work best when we can have multiple tracks the same name...
5787 GList *other_tracks_names = NULL;
5788 GList *iter = g_list_first ( other_tracks );
5789 while ( iter ) {
0d2b891f 5790 other_tracks_names = g_list_append ( other_tracks_names, VIK_TRACK(g_hash_table_lookup (ght_tracks, iter->data))->name );
ce4bd1cf
RN
5791 iter = g_list_next ( iter );
5792 }
5793
5794 other_tracks_names = g_list_sort_with_data (other_tracks_names, sort_alphabetically, NULL);
8352326e 5795
7767aa02 5796 GList *merge_list = a_dialog_select_from_list(VIK_GTK_WINDOW_FROM_LAYER(vtl),
0d2b891f
RN
5797 other_tracks_names,
5798 TRUE,
5799 _("Merge with..."),
5800 track->is_route ? _("Select route to merge with") : _("Select track to merge with"));
5780603d 5801 g_list_free(other_tracks);
ce4bd1cf 5802 g_list_free(other_tracks_names);
291edcab 5803
7767aa02 5804 if (merge_list)
291edcab 5805 {
7767aa02
QT
5806 GList *l;
5807 for (l = merge_list; l != NULL; l = g_list_next(l)) {
0d2b891f
RN
5808 VikTrack *merge_track;
5809 if ( track->is_route )
5810 merge_track = vik_trw_layer_get_route ( vtl, l->data );
5811 else
5812 merge_track = vik_trw_layer_get_track ( vtl, l->data );
5813
7767aa02 5814 if (merge_track) {
0a9ed7dc 5815 vik_track_steal_and_append_trackpoints ( track, merge_track );
0d2b891f
RN
5816 if ( track->is_route )
5817 vik_trw_layer_delete_route (vtl, merge_track);
5818 else
5819 vik_trw_layer_delete_track (vtl, merge_track);
7767aa02
QT
5820 track->trackpoints = g_list_sort(track->trackpoints, trackpoint_compare);
5821 }
291edcab 5822 }
7767aa02
QT
5823 for (l = merge_list; l != NULL; l = g_list_next(l))
5824 g_free(l->data);
5825 g_list_free(merge_list);
20981fd6 5826
da121f9b 5827 vik_layer_emit_update( VIK_LAYER(vtl) );
291edcab 5828 }
291edcab
HR
5829}
5830
9c34f614
RN
5831// c.f. trw_layer_sorted_track_id_by_name_list
5832// but don't add the specified track to the list (normally current track)
5833static void trw_layer_sorted_track_id_by_name_list_exclude_self (const gpointer id, const VikTrack *trk, gpointer udata)
5834{
5835 twt_udata *user_data = udata;
5836
5837 // Skip self
9cdc601f 5838 if (trk == user_data->exclude) {
9c34f614
RN
5839 return;
5840 }
5841
5842 // Sort named list alphabetically
5843 *(user_data->result) = g_list_insert_sorted_with_data (*(user_data->result), trk->name, sort_alphabetically, NULL);
5844}
5845
5846/**
0d2b891f 5847 * Join - this allows combining 'tracks' and 'track routes'
9c34f614
RN
5848 * i.e. doesn't care about whether tracks have consistent timestamps
5849 * ATM can only append one track at a time to the currently selected track
5850 */
19782ffd 5851static void trw_layer_append_track ( menu_array_sublayer values )
9c34f614
RN
5852{
5853
19782ffd 5854 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
0d2b891f
RN
5855 VikTrack *trk;
5856 GHashTable *ght_tracks;
19782ffd 5857 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
0d2b891f
RN
5858 ght_tracks = vtl->routes;
5859 else
5860 ght_tracks = vtl->tracks;
5861
19782ffd 5862 trk = (VikTrack *) g_hash_table_lookup ( ght_tracks, values[MA_SUBLAYER_ID] );
0d2b891f
RN
5863
5864 if ( !trk )
5865 return;
9c34f614
RN
5866
5867 GList *other_tracks_names = NULL;
5868
5869 // Sort alphabetically for user presentation
5870 // Convert into list of names for usage with dialog function
5871 // TODO: Need to consider how to work best when we can have multiple tracks the same name...
5872 twt_udata udata;
5873 udata.result = &other_tracks_names;
9cdc601f 5874 udata.exclude = trk;
9c34f614 5875
0d2b891f 5876 g_hash_table_foreach(ght_tracks, (GHFunc) trw_layer_sorted_track_id_by_name_list_exclude_self, (gpointer)&udata);
9c34f614
RN
5877
5878 // Note the limit to selecting one track only
5879 // this is to control the ordering of appending tracks, i.e. the selected track always goes after the current track
5880 // (otherwise with multiple select the ordering would not be controllable by the user - automatically being alphabetically)
5881 GList *append_list = a_dialog_select_from_list(VIK_GTK_WINDOW_FROM_LAYER(vtl),
0d2b891f
RN
5882 other_tracks_names,
5883 FALSE,
5884 trk->is_route ? _("Append Route"): _("Append Track"),
5885 trk->is_route ? _("Select the route to append after the current route") :
5886 _("Select the track to append after the current track") );
9c34f614
RN
5887
5888 g_list_free(other_tracks_names);
5889
5890 // It's a list, but shouldn't contain more than one other track!
5891 if ( append_list ) {
5892 GList *l;
5893 for (l = append_list; l != NULL; l = g_list_next(l)) {
5894 // TODO: at present this uses the first track found by name,
5895 // which with potential multiple same named tracks may not be the one selected...
0d2b891f
RN
5896 VikTrack *append_track;
5897 if ( trk->is_route )
5898 append_track = vik_trw_layer_get_route ( vtl, l->data );
5899 else
5900 append_track = vik_trw_layer_get_track ( vtl, l->data );
5901
9c34f614 5902 if ( append_track ) {
0a9ed7dc 5903 vik_track_steal_and_append_trackpoints ( trk, append_track );
0d2b891f
RN
5904 if ( trk->is_route )
5905 vik_trw_layer_delete_route (vtl, append_track);
5906 else
5907 vik_trw_layer_delete_track (vtl, append_track);
9c34f614
RN
5908 }
5909 }
5910 for (l = append_list; l != NULL; l = g_list_next(l))
5911 g_free(l->data);
5912 g_list_free(append_list);
20981fd6 5913
da121f9b 5914 vik_layer_emit_update( VIK_LAYER(vtl) );
9c34f614
RN
5915 }
5916}
5917
6b5b6c47
RN
5918/**
5919 * Very similar to trw_layer_append_track for joining
5920 * but this allows selection from the 'other' list
5921 * If a track is selected, then is shows routes and joins the selected one
5922 * If a route is selected, then is shows tracks and joins the selected one
5923 */
19782ffd 5924static void trw_layer_append_other ( menu_array_sublayer values )
6b5b6c47
RN
5925{
5926
19782ffd 5927 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
6b5b6c47
RN
5928 VikTrack *trk;
5929 GHashTable *ght_mykind, *ght_others;
19782ffd 5930 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE ) {
6b5b6c47
RN
5931 ght_mykind = vtl->routes;
5932 ght_others = vtl->tracks;
5933 }
5934 else {
5935 ght_mykind = vtl->tracks;
5936 ght_others = vtl->routes;
5937 }
5938
19782ffd 5939 trk = (VikTrack *) g_hash_table_lookup ( ght_mykind, values[MA_SUBLAYER_ID] );
6b5b6c47
RN
5940
5941 if ( !trk )
5942 return;
5943
5944 GList *other_tracks_names = NULL;
5945
5946 // Sort alphabetically for user presentation
5947 // Convert into list of names for usage with dialog function
5948 // TODO: Need to consider how to work best when we can have multiple tracks the same name...
5949 twt_udata udata;
5950 udata.result = &other_tracks_names;
9cdc601f 5951 udata.exclude = trk;
6b5b6c47
RN
5952
5953 g_hash_table_foreach(ght_others, (GHFunc) trw_layer_sorted_track_id_by_name_list_exclude_self, (gpointer)&udata);
5954
5955 // Note the limit to selecting one track only
5956 // this is to control the ordering of appending tracks, i.e. the selected track always goes after the current track
5957 // (otherwise with multiple select the ordering would not be controllable by the user - automatically being alphabetically)
5958 GList *append_list = a_dialog_select_from_list(VIK_GTK_WINDOW_FROM_LAYER(vtl),
5959 other_tracks_names,
5960 FALSE,
5961 trk->is_route ? _("Append Track"): _("Append Route"),
5962 trk->is_route ? _("Select the track to append after the current route") :
5963 _("Select the route to append after the current track") );
5964
5965 g_list_free(other_tracks_names);
5966
5967 // It's a list, but shouldn't contain more than one other track!
5968 if ( append_list ) {
5969 GList *l;
5970 for (l = append_list; l != NULL; l = g_list_next(l)) {
5971 // TODO: at present this uses the first track found by name,
5972 // which with potential multiple same named tracks may not be the one selected...
5973
5974 // Get FROM THE OTHER TYPE list
5975 VikTrack *append_track;
5976 if ( trk->is_route )
5977 append_track = vik_trw_layer_get_track ( vtl, l->data );
5978 else
5979 append_track = vik_trw_layer_get_route ( vtl, l->data );
5980
5981 if ( append_track ) {
5982
5983 if ( !append_track->is_route &&
5984 ( ( vik_track_get_segment_count ( append_track ) > 1 ) ||
5985 ( vik_track_get_average_speed ( append_track ) > 0.0 ) ) ) {
5986
5987 if ( a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
5988 _("Converting a track to a route removes extra track data such as segments, timestamps, etc...\nDo you want to continue?"), NULL ) ) {
5989 vik_track_merge_segments ( append_track );
5990 vik_track_to_routepoints ( append_track );
5991 }
5992 else {
5993 break;
5994 }
5995 }
5996
0a9ed7dc 5997 vik_track_steal_and_append_trackpoints ( trk, append_track );
6b5b6c47
RN
5998
5999 // Delete copied which is FROM THE OTHER TYPE list
6000 if ( trk->is_route )
6001 vik_trw_layer_delete_track (vtl, append_track);
6002 else
6003 vik_trw_layer_delete_route (vtl, append_track);
6004 }
6005 }
6006 for (l = append_list; l != NULL; l = g_list_next(l))
6007 g_free(l->data);
6008 g_list_free(append_list);
da121f9b 6009 vik_layer_emit_update( VIK_LAYER(vtl) );
6b5b6c47
RN
6010 }
6011}
6012
24774c43 6013/* merge by segments */
19782ffd 6014static void trw_layer_merge_by_segment ( menu_array_sublayer values )
24774c43 6015{
19782ffd
RN
6016 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
6017 VikTrack *trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
24774c43
RN
6018 guint segments = vik_track_merge_segments ( trk );
6019 // NB currently no need to redraw as segments not actually shown on the display
6020 // However inform the user of what happened:
6021 gchar str[64];
6022 const gchar *tmp_str = ngettext("%d segment merged", "%d segments merged", segments);
6023 g_snprintf(str, 64, tmp_str, segments);
6024 a_dialog_info_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), str );
6025}
6026
111fa174 6027/* merge by time routine */
19782ffd 6028static void trw_layer_merge_by_timestamp ( menu_array_sublayer values )
111fa174 6029{
19782ffd 6030 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
a61b2619 6031
02b5d347 6032 //time_t t1, t2;
111fa174 6033
c1564279 6034 GList *tracks_with_timestamp = NULL;
19782ffd 6035 VikTrack *orig_trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
ce4bd1cf 6036 if (orig_trk->trackpoints &&
215ebe59 6037 !vik_track_get_tp_first(orig_trk)->has_timestamp) {
c1564279 6038 a_dialog_error_msg(VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Failed. This track does not have timestamp"));
c1564279
RN
6039 return;
6040 }
6041
6042 twt_udata udata;
6043 udata.result = &tracks_with_timestamp;
9cdc601f 6044 udata.exclude = orig_trk;
5780603d
RN
6045 udata.with_timestamps = TRUE;
6046 g_hash_table_foreach(vtl->tracks, find_tracks_with_timestamp_type, (gpointer)&udata);
c1564279
RN
6047 tracks_with_timestamp = g_list_reverse(tracks_with_timestamp);
6048
6049 if (!tracks_with_timestamp) {
6050 a_dialog_error_msg(VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Failed. No other track in this layer has timestamp"));
c1564279
RN
6051 return;
6052 }
6053 g_list_free(tracks_with_timestamp);
6054
fc92c977
RN
6055 static guint threshold_in_minutes = 1;
6056 if (!a_dialog_time_threshold(VIK_GTK_WINDOW_FROM_LAYER(vtl),
6057 _("Merge Threshold..."),
6058 _("Merge when time between tracks less than:"),
6059 &threshold_in_minutes)) {
111fa174
AF
6060 return;
6061 }
6062
fc92c977
RN
6063 // keep attempting to merge all tracks until no merges within the time specified is possible
6064 gboolean attempt_merge = TRUE;
6065 GList *nearby_tracks = NULL;
6066 GList *trps;
6067 static gpointer params[3];
6068
6069 while ( attempt_merge ) {
6070
6071 // Don't try again unless tracks have changed
6072 attempt_merge = FALSE;
111fa174 6073
ce4bd1cf 6074 trps = orig_trk->trackpoints;
111fa174
AF
6075 if ( !trps )
6076 return;
6077
111fa174
AF
6078 if (nearby_tracks) {
6079 g_list_free(nearby_tracks);
6080 nearby_tracks = NULL;
6081 }
6082
111fa174 6083 params[0] = &nearby_tracks;
fc92c977
RN
6084 params[1] = (gpointer)trps;
6085 params[2] = GUINT_TO_POINTER (threshold_in_minutes*60); // In seconds
111fa174
AF
6086
6087 /* get a list of adjacent-in-time tracks */
fc92c977 6088 g_hash_table_foreach(vtl->tracks, find_nearby_tracks_by_time, params);
111fa174 6089
111fa174 6090 /* merge them */
fc92c977
RN
6091 GList *l = nearby_tracks;
6092 while ( l ) {
fc92c977 6093 /* remove trackpoints from merged track, delete track */
0a9ed7dc 6094 vik_track_steal_and_append_trackpoints ( orig_trk, VIK_TRACK(l->data) );
fc92c977 6095 vik_trw_layer_delete_track (vtl, VIK_TRACK(l->data));
111fa174 6096
fc92c977
RN
6097 // Tracks have changed, therefore retry again against all the remaining tracks
6098 attempt_merge = TRUE;
ce4bd1cf 6099
fc92c977 6100 l = g_list_next(l);
111fa174 6101 }
fc92c977
RN
6102
6103 orig_trk->trackpoints = g_list_sort(orig_trk->trackpoints, trackpoint_compare);
6104 }
6105
111fa174 6106 g_list_free(nearby_tracks);
20981fd6 6107
da121f9b 6108 vik_layer_emit_update( VIK_LAYER(vtl) );
111fa174
AF
6109}
6110
84d3d9f9 6111/**
9914238e 6112 * Split a track at the currently selected trackpoint
84d3d9f9 6113 */
0d2b891f 6114static void trw_layer_split_at_selected_trackpoint ( VikTrwLayer *vtl, gint subtype )
84d3d9f9
RN
6115{
6116 if ( !vtl->current_tpl )
6117 return;
6118
6119 if ( vtl->current_tpl->next && vtl->current_tpl->prev ) {
0d2b891f 6120 gchar *name = trw_layer_new_unique_sublayer_name(vtl, subtype, vtl->current_tp_track->name);
9914238e 6121 if ( name ) {
03817fbf 6122 VikTrack *tr = vik_track_copy ( vtl->current_tp_track, FALSE );
84d3d9f9
RN
6123 GList *newglist = g_list_alloc ();
6124 newglist->prev = NULL;
6125 newglist->next = vtl->current_tpl->next;
6126 newglist->data = vik_trackpoint_copy(VIK_TRACKPOINT(vtl->current_tpl->data));
6127 tr->trackpoints = newglist;
6128
6129 vtl->current_tpl->next->prev = newglist; /* end old track here */
6130 vtl->current_tpl->next = NULL;
6131
20981fd6
RN
6132 // Bounds of the selected track changed due to the split
6133 vik_track_calculate_bounds ( vtl->current_tp_track );
6134
84d3d9f9
RN
6135 vtl->current_tpl = newglist; /* change tp to first of new track. */
6136 vtl->current_tp_track = tr;
6137
0d2b891f
RN
6138 if ( tr->is_route )
6139 vik_trw_layer_add_route ( vtl, name, tr );
6140 else
6141 vik_trw_layer_add_track ( vtl, name, tr );
84d3d9f9 6142
20981fd6
RN
6143 // Bounds of the new track created by the split
6144 vik_track_calculate_bounds ( tr );
6145
84d3d9f9
RN
6146 trku_udata udata;
6147 udata.trk = tr;
6148 udata.uuid = NULL;
6149
6150 // Also need id of newly created track
0d2b891f
RN
6151 gpointer *trkf;
6152 if ( tr->is_route )
6153 trkf = g_hash_table_find ( vtl->routes, (GHRFunc) trw_layer_track_find_uuid, &udata );
6154 else
6155 trkf = g_hash_table_find ( vtl->tracks, (GHRFunc) trw_layer_track_find_uuid, &udata );
6156
84d3d9f9
RN
6157 if ( trkf && udata.uuid )
6158 vtl->current_tp_id = udata.uuid;
6159 else
6160 vtl->current_tp_id = NULL;
6161
da121f9b 6162 vik_layer_emit_update(VIK_LAYER(vtl));
84d3d9f9 6163 }
1613e468 6164 g_free ( name );
84d3d9f9
RN
6165 }
6166}
6167
111fa174 6168/* split by time routine */
19782ffd 6169static void trw_layer_split_by_timestamp ( menu_array_sublayer values )
111fa174 6170{
19782ffd
RN
6171 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
6172 VikTrack *track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
111fa174
AF
6173 GList *trps = track->trackpoints;
6174 GList *iter;
6175 GList *newlists = NULL;
6176 GList *newtps = NULL;
111fa174
AF
6177 static guint thr = 1;
6178
6179 time_t ts, prev_ts;
6180
6181 if ( !trps )
6182 return;
6183
19782ffd 6184 if (!a_dialog_time_threshold(VIK_GTK_WINDOW_FROM_LAYER(vtl),
4c77d5e0
GB
6185 _("Split Threshold..."),
6186 _("Split when time between trackpoints exceeds:"),
111fa174
AF
6187 &thr)) {
6188 return;
6189 }
6190
6191 /* iterate through trackpoints, and copy them into new lists without touching original list */
6192 prev_ts = VIK_TRACKPOINT(trps->data)->timestamp;
6193 iter = trps;
6194
6195 while (iter) {
6196 ts = VIK_TRACKPOINT(iter->data)->timestamp;
1e3b44c4
RN
6197
6198 // Check for unordered time points - this is quite a rare occurence - unless one has reversed a track.
111fa174 6199 if (ts < prev_ts) {
1e3b44c4
RN
6200 gchar tmp_str[64];
6201 strftime ( tmp_str, sizeof(tmp_str), "%c", localtime(&ts) );
6202 if ( a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
6203 _("Can not split track due to trackpoints not ordered in time - such as at %s.\n\nGoto this trackpoint?"),
6204 tmp_str ) ) {
19782ffd 6205 goto_coord ( values[MA_VLP], vtl, values[MA_VVP], &(VIK_TRACKPOINT(iter->data)->coord) );
1e3b44c4 6206 }
111fa174
AF
6207 return;
6208 }
1e3b44c4 6209
111fa174
AF
6210 if (ts - prev_ts > thr*60) {
6211 /* flush accumulated trackpoints into new list */
aa9887a1 6212 newlists = g_list_append(newlists, g_list_reverse(newtps));
111fa174
AF
6213 newtps = NULL;
6214 }
6215
6216 /* accumulate trackpoint copies in newtps, in reverse order */
6217 newtps = g_list_prepend(newtps, vik_trackpoint_copy(VIK_TRACKPOINT(iter->data)));
6218 prev_ts = ts;
6219 iter = g_list_next(iter);
6220 }
6221 if (newtps) {
aa9887a1 6222 newlists = g_list_append(newlists, g_list_reverse(newtps));
111fa174
AF
6223 }
6224
6225 /* put lists of trackpoints into tracks */
6226 iter = newlists;
c9d8f273
RN
6227 // Only bother updating if the split results in new tracks
6228 if (g_list_length (newlists) > 1) {
6229 while (iter) {
6230 gchar *new_tr_name;
6231 VikTrack *tr;
111fa174 6232
03817fbf 6233 tr = vik_track_copy ( track, FALSE );
c9d8f273 6234 tr->trackpoints = (GList *)(iter->data);
111fa174 6235
784b6042
RN
6236 new_tr_name = trw_layer_new_unique_sublayer_name ( vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, track->name);
6237 vik_trw_layer_add_track(vtl, new_tr_name, tr);
1613e468 6238 g_free ( new_tr_name );
20981fd6 6239 vik_track_calculate_bounds ( tr );
c9d8f273
RN
6240 iter = g_list_next(iter);
6241 }
ce4bd1cf 6242 // Remove original track and then update the display
784b6042 6243 vik_trw_layer_delete_track (vtl, track);
19782ffd 6244 vik_layer_emit_update(VIK_LAYER(vtl));
111fa174
AF
6245 }
6246 g_list_free(newlists);
111fa174
AF
6247}
6248
af2341f3
RN
6249/**
6250 * Split a track by the number of points as specified by the user
6251 */
19782ffd 6252static void trw_layer_split_by_n_points ( menu_array_sublayer values )
af2341f3 6253{
19782ffd 6254 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
0d2b891f 6255 VikTrack *track;
19782ffd
RN
6256 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
6257 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
0d2b891f 6258 else
19782ffd 6259 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
0d2b891f
RN
6260
6261 if ( !track )
6262 return;
af2341f3
RN
6263
6264 // Check valid track
6265 GList *trps = track->trackpoints;
6266 if ( !trps )
6267 return;
6268
19782ffd 6269 gint points = a_dialog_get_positive_number(VIK_GTK_WINDOW_FROM_LAYER(vtl),
af2341f3
RN
6270 _("Split Every Nth Point"),
6271 _("Split on every Nth point:"),
6272 250, // Default value as per typical limited track capacity of various GPS devices
6273 2, // Min
6274 65536, // Max
6275 5); // Step
6276 // Was a valid number returned?
6277 if (!points)
6278 return;
6279
6280 // Now split...
6281 GList *iter;
6282 GList *newlists = NULL;
6283 GList *newtps = NULL;
6284 gint count = 0;
6285 iter = trps;
6286
6287 while (iter) {
6288 /* accumulate trackpoint copies in newtps, in reverse order */
6289 newtps = g_list_prepend(newtps, vik_trackpoint_copy(VIK_TRACKPOINT(iter->data)));
6290 count++;
6291 if (count >= points) {
6292 /* flush accumulated trackpoints into new list */
6293 newlists = g_list_append(newlists, g_list_reverse(newtps));
6294 newtps = NULL;
6295 count = 0;
6296 }
6297 iter = g_list_next(iter);
6298 }
6299
6300 // If there is a remaining chunk put that into the new split list
6301 // This may well be the whole track if no split points were encountered
6302 if (newtps) {
6303 newlists = g_list_append(newlists, g_list_reverse(newtps));
6304 }
6305
6306 /* put lists of trackpoints into tracks */
6307 iter = newlists;
af2341f3
RN
6308 // Only bother updating if the split results in new tracks
6309 if (g_list_length (newlists) > 1) {
6310 while (iter) {
6311 gchar *new_tr_name;
6312 VikTrack *tr;
6313
03817fbf 6314 tr = vik_track_copy ( track, FALSE );
af2341f3
RN
6315 tr->trackpoints = (GList *)(iter->data);
6316
0d2b891f
RN
6317 if ( track->is_route ) {
6318 new_tr_name = trw_layer_new_unique_sublayer_name ( vtl, VIK_TRW_LAYER_SUBLAYER_ROUTE, track->name);
6319 vik_trw_layer_add_route(vtl, new_tr_name, tr);
6320 }
6321 else {
6322 new_tr_name = trw_layer_new_unique_sublayer_name ( vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, track->name);
6323 vik_trw_layer_add_track(vtl, new_tr_name, tr);
6324 }
1613e468 6325 g_free ( new_tr_name );
20981fd6
RN
6326 vik_track_calculate_bounds ( tr );
6327
af2341f3
RN
6328 iter = g_list_next(iter);
6329 }
6330 // Remove original track and then update the display
0d2b891f
RN
6331 if ( track->is_route )
6332 vik_trw_layer_delete_route (vtl, track);
6333 else
6334 vik_trw_layer_delete_track (vtl, track);
19782ffd 6335 vik_layer_emit_update(VIK_LAYER(vtl));
af2341f3
RN
6336 }
6337 g_list_free(newlists);
6338}
6339
a4aefe69
RN
6340/**
6341 * Split a track at the currently selected trackpoint
6342 */
19782ffd 6343static void trw_layer_split_at_trackpoint ( menu_array_sublayer values )
a4aefe69 6344{
19782ffd
RN
6345 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
6346 gint subtype = GPOINTER_TO_INT (values[MA_SUBTYPE]);
0d2b891f 6347 trw_layer_split_at_selected_trackpoint ( vtl, subtype );
a4aefe69 6348}
98e87078
RN
6349
6350/**
6351 * Split a track by its segments
0d2b891f 6352 * Routes do not have segments so don't call this for routes
98e87078 6353 */
19782ffd 6354static void trw_layer_split_segments ( menu_array_sublayer values )
98e87078 6355{
19782ffd
RN
6356 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
6357 VikTrack *trk = g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
0d2b891f
RN
6358
6359 if ( !trk )
6360 return;
6361
98e87078
RN
6362 guint ntracks;
6363
6364 VikTrack **tracks = vik_track_split_into_segments (trk, &ntracks);
6365 gchar *new_tr_name;
6366 guint i;
6367 for ( i = 0; i < ntracks; i++ ) {
6368 if ( tracks[i] ) {
6369 new_tr_name = trw_layer_new_unique_sublayer_name ( vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, trk->name);
6370 vik_trw_layer_add_track ( vtl, new_tr_name, tracks[i] );
1613e468 6371 g_free ( new_tr_name );
98e87078
RN
6372 }
6373 }
6374 if ( tracks ) {
6375 g_free ( tracks );
6376 // Remove original track
6377 vik_trw_layer_delete_track ( vtl, trk );
da121f9b 6378 vik_layer_emit_update ( VIK_LAYER(vtl) );
98e87078
RN
6379 }
6380 else {
6381 a_dialog_error_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Can not split track as it has no segments"));
6382 }
6383}
111fa174
AF
6384/* end of split/merge routines */
6385
a56baa08
RN
6386static void trw_layer_trackpoint_selected_delete ( VikTrwLayer *vtl, VikTrack *trk )
6387{
6388 GList *new_tpl;
6389
6390 // Find available adjacent trackpoint
6391 if ( (new_tpl = vtl->current_tpl->next) || (new_tpl = vtl->current_tpl->prev) ) {
6392 if ( VIK_TRACKPOINT(vtl->current_tpl->data)->newsegment && vtl->current_tpl->next )
6393 VIK_TRACKPOINT(vtl->current_tpl->next->data)->newsegment = TRUE; /* don't concat segments on del */
6394
6395 // Delete current trackpoint
6396 vik_trackpoint_free ( vtl->current_tpl->data );
6397 trk->trackpoints = g_list_delete_link ( trk->trackpoints, vtl->current_tpl );
6398
6399 // Set to current to the available adjacent trackpoint
6400 vtl->current_tpl = new_tpl;
6401
6402 if ( vtl->current_tp_track ) {
6403 vik_track_calculate_bounds ( vtl->current_tp_track );
6404 }
6405 }
6406 else {
6407 // Delete current trackpoint
6408 vik_trackpoint_free ( vtl->current_tpl->data );
6409 trk->trackpoints = g_list_delete_link ( trk->trackpoints, vtl->current_tpl );
6410 trw_layer_cancel_current_tp ( vtl, FALSE );
6411 }
6412}
6413
6414/**
6415 * Delete the selected point
6416 */
19782ffd 6417static void trw_layer_delete_point_selected ( menu_array_sublayer values )
a56baa08 6418{
19782ffd 6419 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
a56baa08 6420 VikTrack *trk;
19782ffd
RN
6421 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
6422 trk = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
a56baa08 6423 else
19782ffd 6424 trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
a56baa08
RN
6425
6426 if ( !trk )
6427 return;
6428
6429 if ( !vtl->current_tpl )
6430 return;
6431
6432 trw_layer_trackpoint_selected_delete ( vtl, trk );
6433
6434 // Track has been updated so update tps:
6435 trw_layer_cancel_tps_of_track ( vtl, trk );
6436
6437 vik_layer_emit_update ( VIK_LAYER(vtl) );
6438}
6439
b6eda120
RN
6440/**
6441 * Delete adjacent track points at the same position
6442 * AKA Delete Dulplicates on the Properties Window
6443 */
19782ffd 6444static void trw_layer_delete_points_same_position ( menu_array_sublayer values )
b6eda120 6445{
19782ffd 6446 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
0d2b891f 6447 VikTrack *trk;
19782ffd
RN
6448 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
6449 trk = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
0d2b891f 6450 else
19782ffd 6451 trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
0d2b891f
RN
6452
6453 if ( !trk )
6454 return;
b6eda120
RN
6455
6456 gulong removed = vik_track_remove_dup_points ( trk );
6457
6458 // Track has been updated so update tps:
6459 trw_layer_cancel_tps_of_track ( vtl, trk );
6460
6461 // Inform user how much was deleted as it's not obvious from the normal view
6462 gchar str[64];
6463 const gchar *tmp_str = ngettext("Deleted %ld point", "Deleted %ld points", removed);
6464 g_snprintf(str, 64, tmp_str, removed);
6465 a_dialog_info_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), str);
6466
da121f9b 6467 vik_layer_emit_update ( VIK_LAYER(vtl) );
b6eda120
RN
6468}
6469
6579ca1f
RN
6470/**
6471 * Delete adjacent track points with the same timestamp
6472 * Normally new tracks that are 'routes' won't have any timestamps so should be OK to clean up the track
6473 */
19782ffd 6474static void trw_layer_delete_points_same_time ( menu_array_sublayer values )
6579ca1f 6475{
19782ffd 6476 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
0d2b891f 6477 VikTrack *trk;
19782ffd
RN
6478 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
6479 trk = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
0d2b891f 6480 else
19782ffd 6481 trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
0d2b891f
RN
6482
6483 if ( !trk )
6484 return;
6579ca1f
RN
6485
6486 gulong removed = vik_track_remove_same_time_points ( trk );
6487
6488 // Track has been updated so update tps:
6489 trw_layer_cancel_tps_of_track ( vtl, trk );
6490
6491 // Inform user how much was deleted as it's not obvious from the normal view
6492 gchar str[64];
6493 const gchar *tmp_str = ngettext("Deleted %ld point", "Deleted %ld points", removed);
6494 g_snprintf(str, 64, tmp_str, removed);
6495 a_dialog_info_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), str);
6496
da121f9b 6497 vik_layer_emit_update ( VIK_LAYER(vtl) );
6579ca1f
RN
6498}
6499
491794be
RN
6500/**
6501 * Insert a point
6502 */
19782ffd 6503static void trw_layer_insert_point_after ( menu_array_sublayer values )
491794be 6504{
19782ffd 6505 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
491794be 6506 VikTrack *track;
19782ffd
RN
6507 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
6508 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
491794be 6509 else
19782ffd 6510 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
491794be
RN
6511
6512 if ( ! track )
6513 return;
6514
6515 trw_layer_insert_tp_beside_current_tp ( vtl, FALSE );
6516
6517 vik_layer_emit_update ( VIK_LAYER(vtl) );
6518}
6519
19782ffd 6520static void trw_layer_insert_point_before ( menu_array_sublayer values )
491794be 6521{
19782ffd 6522 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
491794be 6523 VikTrack *track;
19782ffd
RN
6524 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
6525 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
491794be 6526 else
19782ffd 6527 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
491794be
RN
6528
6529 if ( ! track )
6530 return;
6531
6532 trw_layer_insert_tp_beside_current_tp ( vtl, TRUE );
6533
6534 vik_layer_emit_update ( VIK_LAYER(vtl) );
6535}
6536
eb9fd106
RN
6537/**
6538 * Reverse a track
6539 */
19782ffd 6540static void trw_layer_reverse ( menu_array_sublayer values )
eb9fd106 6541{
19782ffd 6542 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
0d2b891f 6543 VikTrack *track;
19782ffd
RN
6544 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
6545 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
0d2b891f 6546 else
19782ffd 6547 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
0d2b891f
RN
6548
6549 if ( ! track )
6550 return;
eb9fd106 6551
eb9fd106
RN
6552 vik_track_reverse ( track );
6553
19782ffd 6554 vik_layer_emit_update ( VIK_LAYER(vtl) );
eb9fd106
RN
6555}
6556
be3b5aa8
RN
6557/**
6558 * Open a diary at the specified date
6559 */
6560static void trw_layer_diary_open ( VikTrwLayer *vtl, const gchar *date_str )
6561{
6562 GError *err = NULL;
6563 gchar *cmd = g_strdup_printf ( "%s%s", "rednotebook --date=", date_str );
6564 if ( ! g_spawn_command_line_async ( cmd, &err ) ) {
6565 a_dialog_error_msg_extra ( VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Could not launch %s to open file."), "rednotebook" );
6566 g_error_free ( err );
6567 }
6568 g_free ( cmd );
6569}
6570
6571/**
6572 * Open a diary at the date of the track or waypoint
6573 */
6574static void trw_layer_diary ( menu_array_sublayer values )
6575{
6576 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
6577
6578 if ( GPOINTER_TO_INT(values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_TRACK ) {
6579 VikTrack *trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
6580 if ( ! trk )
6581 return;
6582
6583 gchar date_buf[20];
6584 date_buf[0] = '\0';
6585 if ( trk->trackpoints && VIK_TRACKPOINT(trk->trackpoints->data)->has_timestamp ) {
6586 strftime (date_buf, sizeof(date_buf), "%Y-%m-%d", gmtime(&(VIK_TRACKPOINT(trk->trackpoints->data)->timestamp)));
6587 trw_layer_diary_open ( vtl, date_buf );
6588 }
6589 else
6590 a_dialog_info_msg ( VIK_GTK_WINDOW_FROM_LAYER(vtl), _("This track has no date information.") );
6591 }
6592 else if ( GPOINTER_TO_INT(values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_WAYPOINT ) {
6593 VikWaypoint *wpt = (VikWaypoint *) g_hash_table_lookup ( vtl->waypoints, values[MA_SUBLAYER_ID] );
6594 if ( ! wpt )
6595 return;
6596
6597 gchar date_buf[20];
6598 date_buf[0] = '\0';
6599 if ( wpt->has_timestamp ) {
6600 strftime (date_buf, sizeof(date_buf), "%Y-%m-%d", gmtime(&(wpt->timestamp)));
6601 trw_layer_diary_open ( vtl, date_buf );
6602 }
6603 else
6604 a_dialog_info_msg ( VIK_GTK_WINDOW_FROM_LAYER(vtl), _("This waypoint has no date information.") );
6605 }
6606}
6607
20b671c3
RN
6608/**
6609 * Similar to trw_layer_enum_item, but this uses a sorted method
6610 */
ce4bd1cf 6611/* Currently unused
20b671c3
RN
6612static void trw_layer_sorted_name_list(gpointer key, gpointer value, gpointer udata)
6613{
6614 GList **list = (GList**)udata;
ce4bd1cf 6615 // *list = g_list_prepend(*all, key); //unsorted method
20b671c3
RN
6616 // Sort named list alphabetically
6617 *list = g_list_insert_sorted_with_data (*list, key, sort_alphabetically, NULL);
6618}
ce4bd1cf 6619*/
20b671c3 6620
c9570f86
RN
6621/**
6622 * Now Waypoint specific sort
6623 */
6624static void trw_layer_sorted_wp_id_by_name_list (const gpointer id, const VikWaypoint *wp, gpointer udata)
6625{
6626 GList **list = (GList**)udata;
6627 // Sort named list alphabetically
6628 *list = g_list_insert_sorted_with_data (*list, wp->name, sort_alphabetically, NULL);
6629}
6630
ce4bd1cf
RN
6631/**
6632 * Track specific sort
6633 */
6634static void trw_layer_sorted_track_id_by_name_list (const gpointer id, const VikTrack *trk, gpointer udata)
6635{
6636 GList **list = (GList**)udata;
6637 // Sort named list alphabetically
6638 *list = g_list_insert_sorted_with_data (*list, trk->name, sort_alphabetically, NULL);
6639}
6640
3d2ad4f8
RN
6641
6642typedef struct {
6643 gboolean has_same_track_name;
6644 const gchar *same_track_name;
6645} same_track_name_udata;
6646
6647static gint check_tracks_for_same_name ( gconstpointer aa, gconstpointer bb, gpointer udata )
6648{
6649 const gchar* namea = (const gchar*) aa;
6650 const gchar* nameb = (const gchar*) bb;
6651
6652 // the test
6653 gint result = strcmp ( namea, nameb );
6654
6655 if ( result == 0 ) {
6656 // Found two names the same
6657 same_track_name_udata *user_data = udata;
6658 user_data->has_same_track_name = TRUE;
6659 user_data->same_track_name = namea;
6660 }
6661
6662 // Leave ordering the same
6663 return 0;
6664}
6665
6666/**
0d2b891f 6667 * Find out if any tracks have the same name in this hash table
3d2ad4f8 6668 */
0d2b891f 6669static gboolean trw_layer_has_same_track_names ( GHashTable *ht_tracks )
3d2ad4f8
RN
6670{
6671 // Sort items by name, then compare if any next to each other are the same
6672
6673 GList *track_names = NULL;
0d2b891f 6674 g_hash_table_foreach ( ht_tracks, (GHFunc) trw_layer_sorted_track_id_by_name_list, &track_names );
3d2ad4f8
RN
6675
6676 // No tracks
6677 if ( ! track_names )
6678 return FALSE;
6679
6680 same_track_name_udata udata;
6681 udata.has_same_track_name = FALSE;
6682
6683 // Use sort routine to traverse list comparing items
6684 // Don't care how this list ends up ordered ( doesn't actually change ) - care about the returned status
6685 GList *dummy_list = g_list_sort_with_data ( track_names, check_tracks_for_same_name, &udata );
6686 // Still no tracks...
6687 if ( ! dummy_list )
6688 return FALSE;
6689
6690 return udata.has_same_track_name;
6691}
6692
6693/**
0d2b891f 6694 * Force unqiue track names for the track table specified
3d2ad4f8 6695 * Note the panel is a required parameter to enable the update of the names displayed
0d2b891f 6696 * Specify if on tracks or else on routes
3d2ad4f8 6697 */
0d2b891f 6698static void vik_trw_layer_uniquify_tracks ( VikTrwLayer *vtl, VikLayersPanel *vlp, GHashTable *track_table, gboolean ontrack )
3d2ad4f8
RN
6699{
6700 // . Search list for an instance of repeated name
6701 // . get track of this name
6702 // . create new name
6703 // . rename track & update equiv. treeview iter
6704 // . repeat until all different
6705
6706 same_track_name_udata udata;
6707
6708 GList *track_names = NULL;
6709 udata.has_same_track_name = FALSE;
6710 udata.same_track_name = NULL;
6711
0d2b891f 6712 g_hash_table_foreach ( track_table, (GHFunc) trw_layer_sorted_track_id_by_name_list, &track_names );
3d2ad4f8
RN
6713
6714 // No tracks
6715 if ( ! track_names )
6716 return;
6717
6718 GList *dummy_list1 = g_list_sort_with_data ( track_names, check_tracks_for_same_name, &udata );
6719
6720 // Still no tracks...
6721 if ( ! dummy_list1 )
6722 return;
6723
6724 while ( udata.has_same_track_name ) {
6725
6726 // Find a track with the same name
0d2b891f
RN
6727 VikTrack *trk;
6728 if ( ontrack )
6729 trk = vik_trw_layer_get_track ( vtl, (gpointer) udata.same_track_name );
6730 else
6731 trk = vik_trw_layer_get_route ( vtl, (gpointer) udata.same_track_name );
3d2ad4f8
RN
6732
6733 if ( ! trk ) {
6734 // Broken :(
6735 g_critical("Houston, we've had a problem.");
6736 vik_statusbar_set_message ( vik_window_get_statusbar (VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl))), VIK_STATUSBAR_INFO,
6737 _("Internal Error in vik_trw_layer_uniquify_tracks") );
6738 return;
6739 }
6740
6741 // Rename it
9748531a 6742 gchar *newname = trw_layer_new_unique_sublayer_name ( vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, udata.same_track_name );
3d2ad4f8
RN
6743 vik_track_set_name ( trk, newname );
6744
6745 trku_udata udataU;
6746 udataU.trk = trk;
6747 udataU.uuid = NULL;
6748
6749 // Need want key of it for treeview update
0d2b891f 6750 gpointer *trkf = g_hash_table_find ( track_table, (GHRFunc) trw_layer_track_find_uuid, &udataU );
3d2ad4f8
RN
6751
6752 if ( trkf && udataU.uuid ) {
6753
0d2b891f
RN
6754 GtkTreeIter *it;
6755 if ( ontrack )
6756 it = g_hash_table_lookup ( vtl->tracks_iters, udataU.uuid );
6757 else
6758 it = g_hash_table_lookup ( vtl->routes_iters, udataU.uuid );
3d2ad4f8
RN
6759
6760 if ( it ) {
6761 vik_treeview_item_set_name ( VIK_LAYER(vtl)->vt, it, newname );
c9cac058
RN
6762 if ( ontrack )
6763 vik_treeview_sort_children ( VIK_LAYER(vtl)->vt, &(vtl->tracks_iter), vtl->wp_sort_order );
6764 else
6765 vik_treeview_sort_children ( VIK_LAYER(vtl)->vt, &(vtl->routes_iter), vtl->wp_sort_order );
3d2ad4f8
RN
6766 }
6767 }
6768
6769 // Start trying to find same names again...
6770 track_names = NULL;
0d2b891f 6771 g_hash_table_foreach ( track_table, (GHFunc) trw_layer_sorted_track_id_by_name_list, &track_names );
3d2ad4f8
RN
6772 udata.has_same_track_name = FALSE;
6773 GList *dummy_list2 = g_list_sort_with_data ( track_names, check_tracks_for_same_name, &udata );
6774
6775 // No tracks any more - give up searching
6776 if ( ! dummy_list2 )
6777 udata.has_same_track_name = FALSE;
6778 }
6779
6780 // Update
6781 vik_layers_panel_emit_update ( vlp );
6782}
6783
19782ffd 6784static void trw_layer_sort_order_a2z ( menu_array_sublayer values )
c9cac058 6785{
19782ffd 6786 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
c9cac058
RN
6787 GtkTreeIter *iter;
6788
19782ffd 6789 switch (GPOINTER_TO_INT (values[MA_SUBTYPE])) {
c9cac058
RN
6790 case VIK_TRW_LAYER_SUBLAYER_TRACKS:
6791 iter = &(vtl->tracks_iter);
6792 vtl->track_sort_order = VL_SO_ALPHABETICAL_ASCENDING;
6793 break;
6794 case VIK_TRW_LAYER_SUBLAYER_ROUTES:
6795 iter = &(vtl->routes_iter);
6796 vtl->track_sort_order = VL_SO_ALPHABETICAL_ASCENDING;
6797 break;
6798 default: // VIK_TRW_LAYER_SUBLAYER_WAYPOINTS:
6799 iter = &(vtl->waypoints_iter);
6800 vtl->wp_sort_order = VL_SO_ALPHABETICAL_ASCENDING;
6801 break;
6802 }
6803
6804 vik_treeview_sort_children ( VIK_LAYER(vtl)->vt, iter, VL_SO_ALPHABETICAL_ASCENDING );
6805}
6806
19782ffd 6807static void trw_layer_sort_order_z2a ( menu_array_sublayer values )
c9cac058 6808{
19782ffd 6809 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
c9cac058
RN
6810 GtkTreeIter *iter;
6811
19782ffd 6812 switch (GPOINTER_TO_INT (values[MA_SUBTYPE])) {
c9cac058
RN
6813 case VIK_TRW_LAYER_SUBLAYER_TRACKS:
6814 iter = &(vtl->tracks_iter);
6815 vtl->track_sort_order = VL_SO_ALPHABETICAL_DESCENDING;
6816 break;
6817 case VIK_TRW_LAYER_SUBLAYER_ROUTES:
6818 iter = &(vtl->routes_iter);
6819 vtl->track_sort_order = VL_SO_ALPHABETICAL_DESCENDING;
6820 break;
6821 default: // VIK_TRW_LAYER_SUBLAYER_WAYPOINTS:
6822 iter = &(vtl->waypoints_iter);
6823 vtl->wp_sort_order = VL_SO_ALPHABETICAL_DESCENDING;
6824 break;
6825 }
6826
6827 vik_treeview_sort_children ( VIK_LAYER(vtl)->vt, iter, VL_SO_ALPHABETICAL_DESCENDING );
6828}
6829
20b671c3
RN
6830/**
6831 *
6832 */
1c2083ba 6833static void trw_layer_delete_tracks_from_selection ( menu_array_layer values )
20b671c3 6834{
1c2083ba 6835 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
20b671c3 6836 GList *all = NULL;
ce4bd1cf 6837
3d2ad4f8 6838 // Ensure list of track names offered is unique
0d2b891f 6839 if ( trw_layer_has_same_track_names ( vtl->tracks ) ) {
3d2ad4f8
RN
6840 if ( a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
6841 _("Multiple entries with the same name exist. This method only works with unique names. Force unique names now?"), NULL ) ) {
1c2083ba 6842 vik_trw_layer_uniquify_tracks ( vtl, VIK_LAYERS_PANEL(values[MA_VLP]), vtl->tracks, TRUE );
3d2ad4f8
RN
6843 }
6844 else
6845 return;
6846 }
ce4bd1cf 6847
20b671c3 6848 // Sort list alphabetically for better presentation
ce4bd1cf 6849 g_hash_table_foreach(vtl->tracks, (GHFunc) trw_layer_sorted_track_id_by_name_list, &all);
20b671c3
RN
6850
6851 if ( ! all ) {
6852 a_dialog_error_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), _("No tracks found"));
6853 return;
6854 }
6855
6856 // Get list of items to delete from the user
6857 GList *delete_list = a_dialog_select_from_list(VIK_GTK_WINDOW_FROM_LAYER(vtl),
6858 all,
6859 TRUE,
6860 _("Delete Selection"),
6861 _("Select tracks to delete"));
6862 g_list_free(all);
6863
6864 // Delete requested tracks
6865 // since specificly requested, IMHO no need for extra confirmation
6866 if ( delete_list ) {
6867 GList *l;
6868 for (l = delete_list; l != NULL; l = g_list_next(l)) {
3d2ad4f8 6869 // This deletes first trk it finds of that name (but uniqueness is enforced above)
0d2b891f
RN
6870 trw_layer_delete_track_by_name (vtl, l->data, vtl->tracks);
6871 }
6872 g_list_free(delete_list);
da121f9b 6873 vik_layer_emit_update( VIK_LAYER(vtl) );
0d2b891f
RN
6874 }
6875}
6876
6877/**
6878 *
6879 */
1c2083ba 6880static void trw_layer_delete_routes_from_selection ( menu_array_layer values )
0d2b891f 6881{
1c2083ba 6882 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
0d2b891f
RN
6883 GList *all = NULL;
6884
6885 // Ensure list of track names offered is unique
6886 if ( trw_layer_has_same_track_names ( vtl->routes ) ) {
6887 if ( a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
6888 _("Multiple entries with the same name exist. This method only works with unique names. Force unique names now?"), NULL ) ) {
1c2083ba 6889 vik_trw_layer_uniquify_tracks ( vtl, VIK_LAYERS_PANEL(values[MA_VLP]), vtl->routes, FALSE );
0d2b891f
RN
6890 }
6891 else
6892 return;
6893 }
6894
6895 // Sort list alphabetically for better presentation
6896 g_hash_table_foreach(vtl->routes, (GHFunc) trw_layer_sorted_track_id_by_name_list, &all);
6897
6898 if ( ! all ) {
6899 a_dialog_error_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), _("No routes found"));
6900 return;
6901 }
6902
6903 // Get list of items to delete from the user
6904 GList *delete_list = a_dialog_select_from_list ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
6905 all,
6906 TRUE,
6907 _("Delete Selection"),
6908 _("Select routes to delete") );
6909 g_list_free(all);
6910
6911 // Delete requested routes
6912 // since specificly requested, IMHO no need for extra confirmation
6913 if ( delete_list ) {
6914 GList *l;
6915 for (l = delete_list; l != NULL; l = g_list_next(l)) {
6916 // This deletes first route it finds of that name (but uniqueness is enforced above)
6917 trw_layer_delete_track_by_name (vtl, l->data, vtl->routes);
20b671c3
RN
6918 }
6919 g_list_free(delete_list);
da121f9b 6920 vik_layer_emit_update( VIK_LAYER(vtl) );
20b671c3
RN
6921 }
6922}
6923
33a89785
RN
6924typedef struct {
6925 gboolean has_same_waypoint_name;
6926 const gchar *same_waypoint_name;
6927} same_waypoint_name_udata;
6928
6929static gint check_waypoints_for_same_name ( gconstpointer aa, gconstpointer bb, gpointer udata )
6930{
6931 const gchar* namea = (const gchar*) aa;
6932 const gchar* nameb = (const gchar*) bb;
6933
6934 // the test
6935 gint result = strcmp ( namea, nameb );
6936
6937 if ( result == 0 ) {
6938 // Found two names the same
6939 same_waypoint_name_udata *user_data = udata;
6940 user_data->has_same_waypoint_name = TRUE;
6941 user_data->same_waypoint_name = namea;
6942 }
6943
6944 // Leave ordering the same
6945 return 0;
6946}
6947
6948/**
6949 * Find out if any waypoints have the same name in this layer
6950 */
6951gboolean trw_layer_has_same_waypoint_names ( VikTrwLayer *vtl )
6952{
6953 // Sort items by name, then compare if any next to each other are the same
6954
6955 GList *waypoint_names = NULL;
6956 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_sorted_wp_id_by_name_list, &waypoint_names );
6957
6958 // No waypoints
6959 if ( ! waypoint_names )
6960 return FALSE;
6961
6962 same_waypoint_name_udata udata;
6963 udata.has_same_waypoint_name = FALSE;
6964
6965 // Use sort routine to traverse list comparing items
6966 // Don't care how this list ends up ordered ( doesn't actually change ) - care about the returned status
6967 GList *dummy_list = g_list_sort_with_data ( waypoint_names, check_waypoints_for_same_name, &udata );
6968 // Still no waypoints...
6969 if ( ! dummy_list )
6970 return FALSE;
6971
6972 return udata.has_same_waypoint_name;
6973}
6974
6975/**
6976 * Force unqiue waypoint names for this layer
6977 * Note the panel is a required parameter to enable the update of the names displayed
6978 */
6979static void vik_trw_layer_uniquify_waypoints ( VikTrwLayer *vtl, VikLayersPanel *vlp )
6980{
6981 // . Search list for an instance of repeated name
6982 // . get waypoint of this name
6983 // . create new name
6984 // . rename waypoint & update equiv. treeview iter
6985 // . repeat until all different
6986
6987 same_waypoint_name_udata udata;
6988
6989 GList *waypoint_names = NULL;
6990 udata.has_same_waypoint_name = FALSE;
6991 udata.same_waypoint_name = NULL;
6992
6993 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_sorted_wp_id_by_name_list, &waypoint_names );
6994
6995 // No waypoints
6996 if ( ! waypoint_names )
6997 return;
6998
6999 GList *dummy_list1 = g_list_sort_with_data ( waypoint_names, check_waypoints_for_same_name, &udata );
7000
7001 // Still no waypoints...
7002 if ( ! dummy_list1 )
7003 return;
7004
7005 while ( udata.has_same_waypoint_name ) {
7006
7007 // Find a waypoint with the same name
7008 VikWaypoint *waypoint = vik_trw_layer_get_waypoint ( vtl, (gpointer) udata.same_waypoint_name );
7009
7010 if ( ! waypoint ) {
7011 // Broken :(
7012 g_critical("Houston, we've had a problem.");
7013 vik_statusbar_set_message ( vik_window_get_statusbar (VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl))), VIK_STATUSBAR_INFO,
7014 _("Internal Error in vik_trw_layer_uniquify_waypoints") );
7015 return;
7016 }
7017
7018 // Rename it
9748531a 7019 gchar *newname = trw_layer_new_unique_sublayer_name ( vtl, VIK_TRW_LAYER_SUBLAYER_WAYPOINT, udata.same_waypoint_name );
33a89785 7020
6761d8a4 7021 trw_layer_waypoint_rename ( vtl, waypoint, newname );
33a89785
RN
7022
7023 // Start trying to find same names again...
7024 waypoint_names = NULL;
7025 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_sorted_wp_id_by_name_list, &waypoint_names );
7026 udata.has_same_waypoint_name = FALSE;
7027 GList *dummy_list2 = g_list_sort_with_data ( waypoint_names, check_waypoints_for_same_name, &udata );
7028
7029 // No waypoints any more - give up searching
7030 if ( ! dummy_list2 )
7031 udata.has_same_waypoint_name = FALSE;
7032 }
7033
7034 // Update
7035 vik_layers_panel_emit_update ( vlp );
7036}
7037
20b671c3
RN
7038/**
7039 *
7040 */
1c2083ba 7041static void trw_layer_delete_waypoints_from_selection ( menu_array_layer values )
20b671c3 7042{
1c2083ba 7043 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
20b671c3
RN
7044 GList *all = NULL;
7045
33a89785
RN
7046 // Ensure list of waypoint names offered is unique
7047 if ( trw_layer_has_same_waypoint_names ( vtl ) ) {
7048 if ( a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
7049 _("Multiple entries with the same name exist. This method only works with unique names. Force unique names now?"), NULL ) ) {
1c2083ba 7050 vik_trw_layer_uniquify_waypoints ( vtl, VIK_LAYERS_PANEL(values[MA_VLP]) );
33a89785
RN
7051 }
7052 else
7053 return;
7054 }
c9570f86 7055
20b671c3 7056 // Sort list alphabetically for better presentation
c9570f86 7057 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_sorted_wp_id_by_name_list, &all);
20b671c3
RN
7058 if ( ! all ) {
7059 a_dialog_error_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), _("No waypoints found"));
7060 return;
7061 }
7062
7063 all = g_list_sort_with_data(all, sort_alphabetically, NULL);
7064
7065 // Get list of items to delete from the user
7066 GList *delete_list = a_dialog_select_from_list(VIK_GTK_WINDOW_FROM_LAYER(vtl),
7067 all,
7068 TRUE,
7069 _("Delete Selection"),
7070 _("Select waypoints to delete"));
7071 g_list_free(all);
7072
7073 // Delete requested waypoints
7074 // since specificly requested, IMHO no need for extra confirmation
7075 if ( delete_list ) {
7076 GList *l;
7077 for (l = delete_list; l != NULL; l = g_list_next(l)) {
33a89785 7078 // This deletes first waypoint it finds of that name (but uniqueness is enforced above)
c9570f86 7079 trw_layer_delete_waypoint_by_name (vtl, l->data);
20b671c3
RN
7080 }
7081 g_list_free(delete_list);
aa0665e5
RN
7082
7083 trw_layer_calculate_bounds_waypoints ( vtl );
da121f9b 7084 vik_layer_emit_update( VIK_LAYER(vtl) );
20b671c3
RN
7085 }
7086
7087}
111fa174 7088
89fdc417
RN
7089/**
7090 *
7091 */
7092static void trw_layer_iter_visibility_toggle ( gpointer id, GtkTreeIter *it, VikTreeview *vt )
7093{
7094 vik_treeview_item_toggle_visible ( vt, it );
7095}
7096
7097/**
7098 *
7099 */
7100static void trw_layer_iter_visibility ( gpointer id, GtkTreeIter *it, gpointer vis_data[2] )
7101{
7102 vik_treeview_item_set_visible ( (VikTreeview*)vis_data[0], it, GPOINTER_TO_INT (vis_data[1]) );
7103}
7104
7105/**
7106 *
7107 */
7108static void trw_layer_waypoints_visibility ( gpointer id, VikWaypoint *wp, gpointer on_off )
7109{
7110 wp->visible = GPOINTER_TO_INT (on_off);
7111}
7112
7113/**
7114 *
7115 */
7116static void trw_layer_waypoints_toggle_visibility ( gpointer id, VikWaypoint *wp )
7117{
7118 wp->visible = !wp->visible;
7119}
7120
7121/**
7122 *
7123 */
1c2083ba 7124static void trw_layer_waypoints_visibility_off ( menu_array_layer values )
89fdc417 7125{
1c2083ba 7126 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
89fdc417
RN
7127 gpointer vis_data[2] = { VIK_LAYER(vtl)->vt, GINT_TO_POINTER(FALSE) };
7128 g_hash_table_foreach ( vtl->waypoints_iters, (GHFunc) trw_layer_iter_visibility, vis_data );
7129 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_waypoints_visibility, vis_data[1] );
7130 // Redraw
7131 vik_layer_emit_update ( VIK_LAYER(vtl) );
7132}
7133
7134/**
7135 *
7136 */
1c2083ba 7137static void trw_layer_waypoints_visibility_on ( menu_array_layer values )
89fdc417 7138{
1c2083ba 7139 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
89fdc417
RN
7140 gpointer vis_data[2] = { VIK_LAYER(vtl)->vt, GINT_TO_POINTER(TRUE) };
7141 g_hash_table_foreach ( vtl->waypoints_iters, (GHFunc) trw_layer_iter_visibility, vis_data );
7142 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_waypoints_visibility, vis_data[1] );
7143 // Redraw
7144 vik_layer_emit_update ( VIK_LAYER(vtl) );
7145}
7146
7147/**
7148 *
7149 */
1c2083ba 7150static void trw_layer_waypoints_visibility_toggle ( menu_array_layer values )
89fdc417 7151{
1c2083ba 7152 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
89fdc417
RN
7153 g_hash_table_foreach ( vtl->waypoints_iters, (GHFunc) trw_layer_iter_visibility_toggle, VIK_LAYER(vtl)->vt );
7154 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_waypoints_toggle_visibility, NULL );
7155 // Redraw
7156 vik_layer_emit_update ( VIK_LAYER(vtl) );
7157}
7158
7159/**
7160 *
7161 */
7162static void trw_layer_tracks_visibility ( gpointer id, VikTrack *trk, gpointer on_off )
7163{
7164 trk->visible = GPOINTER_TO_INT (on_off);
7165}
7166
7167/**
7168 *
7169 */
7170static void trw_layer_tracks_toggle_visibility ( gpointer id, VikTrack *trk )
7171{
7172 trk->visible = !trk->visible;
7173}
7174
7175/**
7176 *
7177 */
1c2083ba 7178static void trw_layer_tracks_visibility_off ( menu_array_layer values )
89fdc417 7179{
1c2083ba 7180 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
89fdc417
RN
7181 gpointer vis_data[2] = { VIK_LAYER(vtl)->vt, GINT_TO_POINTER(FALSE) };
7182 g_hash_table_foreach ( vtl->tracks_iters, (GHFunc) trw_layer_iter_visibility, vis_data );
7183 g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_tracks_visibility, vis_data[1] );
7184 // Redraw
7185 vik_layer_emit_update ( VIK_LAYER(vtl) );
7186}
7187
7188/**
7189 *
7190 */
1c2083ba 7191static void trw_layer_tracks_visibility_on ( menu_array_layer values )
89fdc417 7192{
1c2083ba 7193 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
89fdc417
RN
7194 gpointer vis_data[2] = { VIK_LAYER(vtl)->vt, GINT_TO_POINTER(TRUE) };
7195 g_hash_table_foreach ( vtl->tracks_iters, (GHFunc) trw_layer_iter_visibility, vis_data );
7196 g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_tracks_visibility, vis_data[1] );
7197 // Redraw
7198 vik_layer_emit_update ( VIK_LAYER(vtl) );
7199}
7200
7201/**
7202 *
7203 */
1c2083ba 7204static void trw_layer_tracks_visibility_toggle ( menu_array_layer values )
89fdc417 7205{
1c2083ba 7206 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
89fdc417
RN
7207 g_hash_table_foreach ( vtl->tracks_iters, (GHFunc) trw_layer_iter_visibility_toggle, VIK_LAYER(vtl)->vt );
7208 g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_tracks_toggle_visibility, NULL );
7209 // Redraw
7210 vik_layer_emit_update ( VIK_LAYER(vtl) );
7211}
7212
7213/**
7214 *
7215 */
1c2083ba 7216static void trw_layer_routes_visibility_off ( menu_array_layer values )
89fdc417 7217{
1c2083ba 7218 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
89fdc417
RN
7219 gpointer vis_data[2] = { VIK_LAYER(vtl)->vt, GINT_TO_POINTER(FALSE) };
7220 g_hash_table_foreach ( vtl->routes_iters, (GHFunc) trw_layer_iter_visibility, vis_data );
7221 g_hash_table_foreach ( vtl->routes, (GHFunc) trw_layer_tracks_visibility, vis_data[1] );
7222 // Redraw
7223 vik_layer_emit_update ( VIK_LAYER(vtl) );
7224}
7225
7226/**
7227 *
7228 */
1c2083ba 7229static void trw_layer_routes_visibility_on ( menu_array_layer values )
89fdc417 7230{
1c2083ba 7231 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
89fdc417
RN
7232 gpointer vis_data[2] = { VIK_LAYER(vtl)->vt, GINT_TO_POINTER(TRUE) };
7233 g_hash_table_foreach ( vtl->routes_iters, (GHFunc) trw_layer_iter_visibility, vis_data );
7234 g_hash_table_foreach ( vtl->routes, (GHFunc) trw_layer_tracks_visibility, vis_data[1] );
7235 // Redraw
7236 vik_layer_emit_update ( VIK_LAYER(vtl) );
7237}
7238
7239/**
7240 *
7241 */
1c2083ba 7242static void trw_layer_routes_visibility_toggle ( menu_array_layer values )
89fdc417 7243{
1c2083ba 7244 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
89fdc417
RN
7245 g_hash_table_foreach ( vtl->routes_iters, (GHFunc) trw_layer_iter_visibility_toggle, VIK_LAYER(vtl)->vt );
7246 g_hash_table_foreach ( vtl->routes, (GHFunc) trw_layer_tracks_toggle_visibility, NULL );
7247 // Redraw
7248 vik_layer_emit_update ( VIK_LAYER(vtl) );
7249}
7250
00176e85
RN
7251/**
7252 * vik_trw_layer_build_waypoint_list_t:
7253 *
7254 * Helper function to construct a list of #vik_trw_waypoint_list_t
7255 */
7256GList *vik_trw_layer_build_waypoint_list_t ( VikTrwLayer *vtl, GList *waypoints )
7257{
7258 GList *waypoints_and_layers = NULL;
7259 // build waypoints_and_layers list
7260 while ( waypoints ) {
7261 vik_trw_waypoint_list_t *vtdl = g_malloc (sizeof(vik_trw_waypoint_list_t));
7262 vtdl->wpt = VIK_WAYPOINT(waypoints->data);
7263 vtdl->vtl = vtl;
7264 waypoints_and_layers = g_list_prepend ( waypoints_and_layers, vtdl );
7265 waypoints = g_list_next ( waypoints );
7266 }
7267 return waypoints_and_layers;
7268}
7269
7270/**
7271 * trw_layer_create_waypoint_list:
7272 *
7273 * Create the latest list of waypoints with the associated layer(s)
7274 * Although this will always be from a single layer here
7275 */
7276static GList* trw_layer_create_waypoint_list ( VikLayer *vl, gpointer user_data )
7277{
7278 VikTrwLayer *vtl = VIK_TRW_LAYER(vl);
7279 GList *waypoints = g_hash_table_get_values ( vik_trw_layer_get_waypoints(vtl) );
7280
7281 return vik_trw_layer_build_waypoint_list_t ( vtl, waypoints );
7282}
7283
43984ba1
RN
7284/**
7285 * trw_layer_analyse_close:
7286 *
7287 * Stuff to do on dialog closure
7288 */
7289static void trw_layer_analyse_close ( GtkWidget *dialog, gint resp, VikLayer* vl )
7290{
7291 VikTrwLayer *vtl = VIK_TRW_LAYER(vl);
7292 gtk_widget_destroy ( dialog );
7293 vtl->tracks_analysis_dialog = NULL;
7294}
7295
7296/**
184ec0fa 7297 * vik_trw_layer_build_track_list_t:
43984ba1 7298 *
184ec0fa 7299 * Helper function to construct a list of #vik_trw_track_list_t
43984ba1 7300 */
184ec0fa 7301GList *vik_trw_layer_build_track_list_t ( VikTrwLayer *vtl, GList *tracks )
43984ba1 7302{
43984ba1
RN
7303 GList *tracks_and_layers = NULL;
7304 // build tracks_and_layers list
43984ba1
RN
7305 while ( tracks ) {
7306 vik_trw_track_list_t *vtdl = g_malloc (sizeof(vik_trw_track_list_t));
7307 vtdl->trk = VIK_TRACK(tracks->data);
7308 vtdl->vtl = vtl;
7309 tracks_and_layers = g_list_prepend ( tracks_and_layers, vtdl );
7310 tracks = g_list_next ( tracks );
7311 }
43984ba1
RN
7312 return tracks_and_layers;
7313}
7314
184ec0fa
RN
7315/**
7316 * trw_layer_create_track_list:
7317 *
7318 * Create the latest list of tracks with the associated layer(s)
7319 * Although this will always be from a single layer here
7320 */
7321static GList* trw_layer_create_track_list ( VikLayer *vl, gpointer user_data )
7322{
7323 VikTrwLayer *vtl = VIK_TRW_LAYER(vl);
7324 GList *tracks = NULL;
7325 if ( GPOINTER_TO_INT(user_data) == VIK_TRW_LAYER_SUBLAYER_TRACKS )
7326 tracks = g_hash_table_get_values ( vik_trw_layer_get_tracks(vtl) );
7327 else
7328 tracks = g_hash_table_get_values ( vik_trw_layer_get_routes(vtl) );
7329
7330 return vik_trw_layer_build_track_list_t ( vtl, tracks );
7331}
7332
1c2083ba 7333static void trw_layer_tracks_stats ( menu_array_layer values )
43984ba1 7334{
1c2083ba 7335 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
43984ba1
RN
7336 // There can only be one!
7337 if ( vtl->tracks_analysis_dialog )
7338 return;
7339
7340 vtl->tracks_analysis_dialog = vik_trw_layer_analyse_this ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
7341 VIK_LAYER(vtl)->name,
7342 VIK_LAYER(vtl),
7343 GINT_TO_POINTER(VIK_TRW_LAYER_SUBLAYER_TRACKS),
184ec0fa 7344 trw_layer_create_track_list,
43984ba1
RN
7345 trw_layer_analyse_close );
7346}
7347
7348/**
7349 *
7350 */
1c2083ba 7351static void trw_layer_routes_stats ( menu_array_layer values )
43984ba1 7352{
1c2083ba 7353 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
43984ba1
RN
7354 // There can only be one!
7355 if ( vtl->tracks_analysis_dialog )
7356 return;
7357
7358 vtl->tracks_analysis_dialog = vik_trw_layer_analyse_this ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
7359 VIK_LAYER(vtl)->name,
7360 VIK_LAYER(vtl),
7361 GINT_TO_POINTER(VIK_TRW_LAYER_SUBLAYER_ROUTES),
184ec0fa 7362 trw_layer_create_track_list,
43984ba1
RN
7363 trw_layer_analyse_close );
7364}
7365
19782ffd 7366static void trw_layer_goto_waypoint ( menu_array_sublayer values )
50a14534 7367{
19782ffd
RN
7368 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
7369 VikWaypoint *wp = g_hash_table_lookup ( vtl->waypoints, values[MA_SUBLAYER_ID] );
50a14534 7370 if ( wp )
19782ffd 7371 goto_coord ( values[MA_VLP], vtl, values[MA_VVP], &(wp->coord) );
50a14534
EB
7372}
7373
19782ffd 7374static void trw_layer_waypoint_gc_webpage ( menu_array_sublayer values )
50a14534 7375{
19782ffd
RN
7376 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
7377 VikWaypoint *wp = g_hash_table_lookup ( vtl->waypoints, values[MA_SUBLAYER_ID] );
d64e2724
RN
7378 if ( !wp )
7379 return;
7380 gchar *webpage = g_strdup_printf("http://www.geocaching.com/seek/cache_details.aspx?wp=%s", wp->name );
19782ffd 7381 open_url(VIK_GTK_WINDOW_FROM_LAYER(VIK_LAYER(vtl)), webpage);
50a14534
EB
7382 g_free ( webpage );
7383}
7384
19782ffd 7385static void trw_layer_waypoint_webpage ( menu_array_sublayer values )
3c13fff8 7386{
19782ffd
RN
7387 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
7388 VikWaypoint *wp = g_hash_table_lookup ( vtl->waypoints, values[MA_SUBLAYER_ID] );
3c13fff8
RN
7389 if ( !wp )
7390 return;
7391 if ( !strncmp(wp->comment, "http", 4) ) {
19782ffd 7392 open_url(VIK_GTK_WINDOW_FROM_LAYER(VIK_LAYER(vtl)), wp->comment);
3c13fff8 7393 } else if ( !strncmp(wp->description, "http", 4) ) {
19782ffd 7394 open_url(VIK_GTK_WINDOW_FROM_LAYER(VIK_LAYER(vtl)), wp->description);
3c13fff8
RN
7395 }
7396}
7397
a7cd93ac 7398static const gchar* trw_layer_sublayer_rename_request ( VikTrwLayer *l, const gchar *newname, gpointer vlp, gint subtype, gpointer sublayer, GtkTreeIter *iter )
50a14534
EB
7399{
7400 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT )
7401 {
c9570f86 7402 VikWaypoint *wp = g_hash_table_lookup ( l->waypoints, sublayer );
50a14534 7403
c9570f86 7404 // No actual change to the name supplied
27d267f4
RN
7405 if ( wp->name )
7406 if (strcmp(newname, wp->name) == 0 )
7407 return NULL;
50a14534 7408
c9570f86
RN
7409 VikWaypoint *wpf = vik_trw_layer_get_waypoint ( l, newname );
7410
7411 if ( wpf ) {
7412 // An existing waypoint has been found with the requested name
7413 if ( ! a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(l),
77bca466 7414 _("A waypoint with the name \"%s\" already exists. Really rename to the same name?"),
c9570f86 7415 newname ) )
8499a412 7416 return NULL;
50a14534
EB
7417 }
7418
c9570f86
RN
7419 // Update WP name and refresh the treeview
7420 vik_waypoint_set_name (wp, newname);
50a14534 7421
c9cac058
RN
7422 vik_treeview_item_set_name ( VIK_LAYER(l)->vt, iter, newname );
7423 vik_treeview_sort_children ( VIK_LAYER(l)->vt, &(l->waypoints_iter), l->wp_sort_order );
50a14534
EB
7424
7425 vik_layers_panel_emit_update ( VIK_LAYERS_PANEL(vlp) );
c9570f86
RN
7426
7427 return newname;
50a14534 7428 }
c9570f86 7429
50a14534
EB
7430 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
7431 {
ce4bd1cf 7432 VikTrack *trk = g_hash_table_lookup ( l->tracks, sublayer );
50a14534 7433
ce4bd1cf 7434 // No actual change to the name supplied
27d267f4
RN
7435 if ( trk->name )
7436 if (strcmp(newname, trk->name) == 0)
7437 return NULL;
50a14534 7438
ce4bd1cf
RN
7439 VikTrack *trkf = vik_trw_layer_get_track ( l, (gpointer) newname );
7440
7441 if ( trkf ) {
7442 // An existing track has been found with the requested name
7443 if ( ! a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(l),
77bca466 7444 _("A track with the name \"%s\" already exists. Really rename to the same name?"),
ce4bd1cf 7445 newname ) )
8499a412 7446 return NULL;
50a14534 7447 }
ce4bd1cf
RN
7448 // Update track name and refresh GUI parts
7449 vik_track_set_name (trk, newname);
50a14534 7450
ce4bd1cf
RN
7451 // Update any subwindows that could be displaying this track which has changed name
7452 // Only one Track Edit Window
7453 if ( l->current_tp_track == trk && l->tpwin ) {
7454 vik_trw_layer_tpwin_set_track_name ( l->tpwin, newname );
50a14534 7455 }
ce4bd1cf
RN
7456 // Property Dialog of the track
7457 vik_trw_layer_propwin_update ( trk );
50a14534 7458
c9cac058
RN
7459 vik_treeview_item_set_name ( VIK_LAYER(l)->vt, iter, newname );
7460 vik_treeview_sort_children ( VIK_LAYER(l)->vt, &(l->tracks_iter), l->track_sort_order );
0d2b891f
RN
7461
7462 vik_layers_panel_emit_update ( VIK_LAYERS_PANEL(vlp) );
7463
7464 return newname;
7465 }
7466
7467 if ( subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE )
7468 {
7469 VikTrack *trk = g_hash_table_lookup ( l->routes, sublayer );
7470
7471 // No actual change to the name supplied
27d267f4
RN
7472 if ( trk->name )
7473 if (strcmp(newname, trk->name) == 0)
7474 return NULL;
0d2b891f
RN
7475
7476 VikTrack *trkf = vik_trw_layer_get_route ( l, (gpointer) newname );
7477
7478 if ( trkf ) {
7479 // An existing track has been found with the requested name
7480 if ( ! a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(l),
7481 _("A route with the name \"%s\" already exists. Really rename to the same name?"),
7482 newname ) )
7483 return NULL;
7484 }
7485 // Update track name and refresh GUI parts
7486 vik_track_set_name (trk, newname);
7487
7488 // Update any subwindows that could be displaying this track which has changed name
7489 // Only one Track Edit Window
7490 if ( l->current_tp_track == trk && l->tpwin ) {
7491 vik_trw_layer_tpwin_set_track_name ( l->tpwin, newname );
7492 }
7493 // Property Dialog of the track
7494 vik_trw_layer_propwin_update ( trk );
7495
c9cac058
RN
7496 vik_treeview_item_set_name ( VIK_LAYER(l)->vt, iter, newname );
7497 vik_treeview_sort_children ( VIK_LAYER(l)->vt, &(l->tracks_iter), l->track_sort_order );
50a14534
EB
7498
7499 vik_layers_panel_emit_update ( VIK_LAYERS_PANEL(vlp) );
ce4bd1cf
RN
7500
7501 return newname;
50a14534
EB
7502 }
7503 return NULL;
7504}
7505
7506static gboolean is_valid_geocache_name ( gchar *str )
7507{
7508 gint len = strlen ( str );
0c1044e9 7509 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
7510}
7511
19782ffd 7512static void trw_layer_track_use_with_filter ( menu_array_sublayer values )
28c82d8b 7513{
19782ffd 7514 VikTrack *trk = g_hash_table_lookup ( VIK_TRW_LAYER(values[MA_VTL])->tracks, values[MA_SUBLAYER_ID] );
ce4bd1cf 7515 a_acquire_set_filter_track ( trk );
28c82d8b
EB
7516}
7517
55340efa 7518#ifdef VIK_CONFIG_GOOGLE
ce4bd1cf 7519static gboolean is_valid_google_route ( VikTrwLayer *vtl, const gpointer track_id )
bddd2056 7520{
008e972c 7521 VikTrack *tr = g_hash_table_lookup ( vtl->routes, track_id );
bddd2056
EB
7522 return ( tr && tr->comment && strlen(tr->comment) > 7 && !strncmp(tr->comment, "from:", 5) );
7523}
7524
19782ffd 7525static void trw_layer_google_route_webpage ( menu_array_sublayer values )
bddd2056 7526{
19782ffd 7527 VikTrack *tr = g_hash_table_lookup ( VIK_TRW_LAYER(values[MA_VTL])->routes, values[MA_SUBLAYER_ID] );
bddd2056
EB
7528 if ( tr ) {
7529 gchar *escaped = uri_escape ( tr->comment );
7530 gchar *webpage = g_strdup_printf("http://maps.google.com/maps?f=q&hl=en&q=%s", escaped );
19782ffd 7531 open_url(VIK_GTK_WINDOW_FROM_LAYER(VIK_LAYER(values[MA_VTL])), webpage);
bddd2056 7532 g_free ( escaped );
bddd2056
EB
7533 g_free ( webpage );
7534 }
7535}
ff1f2b3e 7536#endif
bddd2056 7537
50eadc64
RN
7538/* vlp can be NULL if necessary - i.e. right-click from a tool */
7539/* viewpoint is now available instead */
a7cd93ac 7540static gboolean trw_layer_sublayer_add_menu_items ( VikTrwLayer *l, GtkMenu *menu, gpointer vlp, gint subtype, gpointer sublayer, GtkTreeIter *iter, VikViewport *vvp )
50a14534 7541{
19782ffd 7542 static menu_array_sublayer pass_along;
50a14534
EB
7543 GtkWidget *item;
7544 gboolean rv = FALSE;
7545
19782ffd
RN
7546 pass_along[MA_VTL] = l;
7547 pass_along[MA_VLP] = vlp;
7548 pass_along[MA_SUBTYPE] = GINT_TO_POINTER (subtype);
7549 pass_along[MA_SUBLAYER_ID] = sublayer;
7550 pass_along[MA_CONFIRM] = GINT_TO_POINTER (1); // Confirm delete request
7551 pass_along[MA_VVP] = vvp;
7552 pass_along[MA_TV_ITER] = iter;
7553 pass_along[MA_MISC] = NULL; // For misc purposes - maybe track or waypoint
50a14534 7554
0d2b891f 7555 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT || subtype == VIK_TRW_LAYER_SUBLAYER_TRACK || subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE )
50a14534
EB
7556 {
7557 rv = TRUE;
7558
7559 item = gtk_image_menu_item_new_from_stock ( GTK_STOCK_PROPERTIES, NULL );
7560 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_properties_item), pass_along );
7561 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7562 gtk_widget_show ( item );
7563
21700912 7564 if (subtype == VIK_TRW_LAYER_SUBLAYER_TRACK) {
0d2b891f
RN
7565 VikTrack *tr = g_hash_table_lookup ( l->tracks, sublayer );
7566 if (tr && tr->property_dialog)
7567 gtk_widget_set_sensitive(GTK_WIDGET(item), FALSE );
7568 }
7569 if (subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE) {
7570 VikTrack *tr = g_hash_table_lookup ( l->routes, sublayer );
21700912
QT
7571 if (tr && tr->property_dialog)
7572 gtk_widget_set_sensitive(GTK_WIDGET(item), FALSE );
7573 }
7574
2cebc318
QT
7575 item = gtk_image_menu_item_new_from_stock ( GTK_STOCK_CUT, NULL );
7576 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_cut_item_cb), pass_along );
7577 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7578 gtk_widget_show ( item );
7579
7580 item = gtk_image_menu_item_new_from_stock ( GTK_STOCK_COPY, NULL );
7581 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_copy_item_cb), pass_along );
7582 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7583 gtk_widget_show ( item );
7584
50a14534
EB
7585 item = gtk_image_menu_item_new_from_stock ( GTK_STOCK_DELETE, NULL );
7586 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_item), pass_along );
7587 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7588 gtk_widget_show ( item );
7589
7590 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT )
7591 {
a8952eb1
RN
7592 // Always create separator as now there is always at least the transform menu option
7593 item = gtk_menu_item_new ();
7594 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7595 gtk_widget_show ( item );
5ede6aa6 7596
50a14534
EB
7597 /* could be a right-click using the tool */
7598 if ( vlp != NULL ) {
d6de71f9 7599 item = gtk_image_menu_item_new_with_mnemonic ( _("_Goto") );
a8952eb1 7600 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_JUMP_TO, GTK_ICON_SIZE_MENU) );
50a14534
EB
7601 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_waypoint), pass_along );
7602 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7603 gtk_widget_show ( item );
7604 }
7605
c9570f86 7606 VikWaypoint *wp = g_hash_table_lookup ( VIK_TRW_LAYER(l)->waypoints, sublayer );
5ede6aa6 7607
c9570f86
RN
7608 if ( wp && wp->name ) {
7609 if ( is_valid_geocache_name ( wp->name ) ) {
c9570f86
RN
7610 item = gtk_menu_item_new_with_mnemonic ( _("_Visit Geocache Webpage") );
7611 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_waypoint_gc_webpage), pass_along );
7612 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7613 gtk_widget_show ( item );
7614 }
41f4abac
RN
7615#ifdef VIK_CONFIG_GEOTAG
7616 item = gtk_menu_item_new_with_mnemonic ( _("Geotag _Images...") );
7617 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_geotagging_waypoint), pass_along );
7618 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7619 gtk_widget_set_tooltip_text (item, _("Geotag multiple images against this waypoint"));
7620 gtk_widget_show ( item );
7621#endif
c9570f86 7622 }
a412f3f5
RN
7623
7624 if ( wp && wp->image )
7625 {
19782ffd
RN
7626 // Set up image paramater
7627 pass_along[MA_MISC] = wp->image;
a412f3f5 7628
d6de71f9 7629 item = gtk_image_menu_item_new_with_mnemonic ( _("_Show Picture...") );
43b5e335 7630 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock ("vik-icon-Show Picture", GTK_ICON_SIZE_MENU) ); // Own icon - see stock_icons in vikwindow.c
a412f3f5
RN
7631 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_show_picture), pass_along );
7632 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7633 gtk_widget_show ( item );
b3eb3b98
RN
7634
7635#ifdef VIK_CONFIG_GEOTAG
7636 GtkWidget *geotag_submenu = gtk_menu_new ();
7637 item = gtk_image_menu_item_new_with_mnemonic ( _("Update Geotag on _Image") );
7638 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REFRESH, GTK_ICON_SIZE_MENU) );
7639 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7640 gtk_widget_show ( item );
7641 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), geotag_submenu );
7642
7643 item = gtk_menu_item_new_with_mnemonic ( _("_Update") );
7644 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_geotagging_waypoint_mtime_update), pass_along );
7645 gtk_menu_shell_append (GTK_MENU_SHELL (geotag_submenu), item);
7646 gtk_widget_show ( item );
7647
7648 item = gtk_menu_item_new_with_mnemonic ( _("Update and _Keep File Timestamp") );
7649 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_geotagging_waypoint_mtime_keep), pass_along );
7650 gtk_menu_shell_append (GTK_MENU_SHELL (geotag_submenu), item);
7651 gtk_widget_show ( item );
7652#endif
a412f3f5
RN
7653 }
7654
3c13fff8
RN
7655 if ( wp )
7656 {
7657 if ( ( wp->comment && !strncmp(wp->comment, "http", 4) ) ||
7658 ( wp->description && !strncmp(wp->description, "http", 4) )) {
7659 item = gtk_image_menu_item_new_with_mnemonic ( _("Visit _Webpage") );
7660 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NETWORK, GTK_ICON_SIZE_MENU) );
7661 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_waypoint_webpage), pass_along );
7662 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7663 gtk_widget_show ( item );
7664 }
7665 }
50a14534
EB
7666 }
7667 }
7668
0d2b891f 7669 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINTS || subtype == VIK_TRW_LAYER_SUBLAYER_TRACKS || subtype == VIK_TRW_LAYER_SUBLAYER_ROUTES ) {
0d80642e
RN
7670 item = gtk_image_menu_item_new_from_stock ( GTK_STOCK_PASTE, NULL );
7671 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_paste_item_cb), pass_along );
7672 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7673 gtk_widget_show ( item );
7674 // TODO: only enable if suitable item is in clipboard - want to determine *which* sublayer type
7675 if ( a_clipboard_type ( ) == VIK_CLIPBOARD_DATA_SUBLAYER )
7676 gtk_widget_set_sensitive ( item, TRUE );
7677 else
7678 gtk_widget_set_sensitive ( item, FALSE );
7679
7680 // Add separator
7681 item = gtk_menu_item_new ();
7682 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7683 gtk_widget_show ( item );
7684 }
7685
5ede6aa6
RN
7686 if ( vlp && (subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINTS || subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT) )
7687 {
b66bb4ab 7688 rv = TRUE;
d6de71f9
RN
7689 item = gtk_image_menu_item_new_with_mnemonic ( _("_New Waypoint...") );
7690 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NEW, GTK_ICON_SIZE_MENU) );
5ede6aa6
RN
7691 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_wp), pass_along );
7692 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7693 gtk_widget_show ( item );
7694 }
7695
539ba038
RN
7696 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINTS )
7697 {
d6de71f9
RN
7698 item = gtk_image_menu_item_new_with_mnemonic ( _("_View All Waypoints") );
7699 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ZOOM_FIT, GTK_ICON_SIZE_MENU) );
539ba038
RN
7700 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_waypoints_view), pass_along );
7701 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7702 gtk_widget_show ( item );
7703
d6de71f9
RN
7704 item = gtk_image_menu_item_new_with_mnemonic ( _("Goto _Waypoint...") );
7705 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_JUMP_TO, GTK_ICON_SIZE_MENU) );
539ba038
RN
7706 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_wp), pass_along );
7707 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7708 gtk_widget_show ( item );
c9a5cbf9 7709
d6de71f9
RN
7710 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete _All Waypoints") );
7711 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU) );
c9a5cbf9
RN
7712 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_all_waypoints), pass_along );
7713 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7714 gtk_widget_show ( item );
20b671c3 7715
d6de71f9
RN
7716 item = gtk_image_menu_item_new_with_mnemonic ( _("_Delete Waypoints From Selection...") );
7717 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
20b671c3
RN
7718 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_waypoints_from_selection), pass_along );
7719 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7720 gtk_widget_show ( item );
89fdc417
RN
7721
7722 GtkWidget *vis_submenu = gtk_menu_new ();
7723 item = gtk_menu_item_new_with_mnemonic ( _("_Visibility") );
7724 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7725 gtk_widget_show ( item );
7726 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), vis_submenu );
7727
7728 item = gtk_image_menu_item_new_with_mnemonic ( _("_Show All Waypoints") );
7729 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_APPLY, GTK_ICON_SIZE_MENU) );
7730 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_waypoints_visibility_on), pass_along );
7731 gtk_menu_shell_append ( GTK_MENU_SHELL(vis_submenu), item );
7732 gtk_widget_show ( item );
7733
7734 item = gtk_image_menu_item_new_with_mnemonic ( _("_Hide All Waypoints") );
7735 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_CLEAR, GTK_ICON_SIZE_MENU) );
7736 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_waypoints_visibility_off), pass_along );
7737 gtk_menu_shell_append ( GTK_MENU_SHELL(vis_submenu), item );
7738 gtk_widget_show ( item );
7739
7740 item = gtk_image_menu_item_new_with_mnemonic ( _("_Toggle") );
7741 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REFRESH, GTK_ICON_SIZE_MENU) );
7742 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_waypoints_visibility_toggle), pass_along );
7743 gtk_menu_shell_append ( GTK_MENU_SHELL(vis_submenu), item );
7744 gtk_widget_show ( item );
00176e85
RN
7745
7746 item = gtk_image_menu_item_new_with_mnemonic ( _("_List Waypoints...") );
7747 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
7748 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_waypoint_list_dialog), pass_along );
7749 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
539ba038
RN
7750 }
7751
f1e68516
RN
7752 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACKS )
7753 {
7754 rv = TRUE;
7755
0d2b891f 7756 if ( l->current_track && !l->current_track->is_route ) {
37615c52
RN
7757 item = gtk_menu_item_new_with_mnemonic ( _("_Finish Track") );
7758 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_finish_track), pass_along );
7759 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7760 gtk_widget_show ( item );
7761 // Add separator
7762 item = gtk_menu_item_new ();
7763 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7764 gtk_widget_show ( item );
7765 }
7766
d6de71f9
RN
7767 item = gtk_image_menu_item_new_with_mnemonic ( _("_View All Tracks") );
7768 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ZOOM_FIT, GTK_ICON_SIZE_MENU) );
f1e68516
RN
7769 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_tracks_view), pass_along );
7770 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7771 gtk_widget_show ( item );
c9a5cbf9 7772
37615c52
RN
7773 item = gtk_image_menu_item_new_with_mnemonic ( _("_New Track") );
7774 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NEW, GTK_ICON_SIZE_MENU) );
7775 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_track), pass_along );
7776 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7777 gtk_widget_show ( item );
7778 // Make it available only when a new track *not* already in progress
7779 gtk_widget_set_sensitive ( item, ! (gboolean)GPOINTER_TO_INT(l->current_track) );
7780
d6de71f9
RN
7781 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete _All Tracks") );
7782 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU) );
c9a5cbf9
RN
7783 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_all_tracks), pass_along );
7784 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7785 gtk_widget_show ( item );
20b671c3 7786
d6de71f9
RN
7787 item = gtk_image_menu_item_new_with_mnemonic ( _("_Delete Tracks From Selection...") );
7788 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
20b671c3
RN
7789 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_tracks_from_selection), pass_along );
7790 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7791 gtk_widget_show ( item );
89fdc417
RN
7792
7793 GtkWidget *vis_submenu = gtk_menu_new ();
7794 item = gtk_menu_item_new_with_mnemonic ( _("_Visibility") );
7795 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7796 gtk_widget_show ( item );
7797 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), vis_submenu );
7798
7799 item = gtk_image_menu_item_new_with_mnemonic ( _("_Show All Tracks") );
7800 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_APPLY, GTK_ICON_SIZE_MENU) );
7801 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_tracks_visibility_on), pass_along );
7802 gtk_menu_shell_append ( GTK_MENU_SHELL(vis_submenu), item );
7803 gtk_widget_show ( item );
7804
7805 item = gtk_image_menu_item_new_with_mnemonic ( _("_Hide All Tracks") );
7806 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_CLEAR, GTK_ICON_SIZE_MENU) );
7807 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_tracks_visibility_off), pass_along );
7808 gtk_menu_shell_append ( GTK_MENU_SHELL(vis_submenu), item );
7809 gtk_widget_show ( item );
7810
7811 item = gtk_image_menu_item_new_with_mnemonic ( _("_Toggle") );
7812 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REFRESH, GTK_ICON_SIZE_MENU) );
7813 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_tracks_visibility_toggle), pass_along );
7814 gtk_menu_shell_append ( GTK_MENU_SHELL(vis_submenu), item );
07c9d42b
RN
7815
7816 item = gtk_image_menu_item_new_with_mnemonic ( _("_List Tracks...") );
7817 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
7818 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_track_list_dialog_single), pass_along );
7819 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
89fdc417 7820 gtk_widget_show ( item );
43984ba1
RN
7821
7822 item = gtk_menu_item_new_with_mnemonic ( _("_Statistics") );
7823 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_tracks_stats), pass_along );
7824 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7825 gtk_widget_show ( item );
f1e68516
RN
7826 }
7827
0d2b891f
RN
7828 if ( subtype == VIK_TRW_LAYER_SUBLAYER_ROUTES )
7829 {
7830 rv = TRUE;
7831
7832 if ( l->current_track && l->current_track->is_route ) {
7833 item = gtk_menu_item_new_with_mnemonic ( _("_Finish Route") );
7834 // Reuse finish track method
7835 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_finish_track), pass_along );
7836 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7837 gtk_widget_show ( item );
7838 // Add separator
7839 item = gtk_menu_item_new ();
7840 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7841 gtk_widget_show ( item );
7842 }
7843
7844 item = gtk_image_menu_item_new_with_mnemonic ( _("_View All Routes") );
7845 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ZOOM_FIT, GTK_ICON_SIZE_MENU) );
7846 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_routes_view), pass_along );
7847 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7848 gtk_widget_show ( item );
7849
7850 item = gtk_image_menu_item_new_with_mnemonic ( _("_New Route") );
7851 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NEW, GTK_ICON_SIZE_MENU) );
7852 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_route), pass_along );
7853 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7854 gtk_widget_show ( item );
7855 // Make it available only when a new track *not* already in progress
7856 gtk_widget_set_sensitive ( item, ! (gboolean)GPOINTER_TO_INT(l->current_track) );
7857
7858 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete _All Routes") );
7859 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU) );
7860 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_all_routes), pass_along );
7861 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7862 gtk_widget_show ( item );
7863
7864 item = gtk_image_menu_item_new_with_mnemonic ( _("_Delete Routes From Selection...") );
7865 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
7866 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_routes_from_selection), pass_along );
7867 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7868 gtk_widget_show ( item );
89fdc417
RN
7869
7870 GtkWidget *vis_submenu = gtk_menu_new ();
7871 item = gtk_menu_item_new_with_mnemonic ( _("_Visibility") );
7872 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7873 gtk_widget_show ( item );
7874 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), vis_submenu );
7875
7876 item = gtk_image_menu_item_new_with_mnemonic ( _("_Show All Routes") );
7877 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_APPLY, GTK_ICON_SIZE_MENU) );
7878 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_routes_visibility_on), pass_along );
7879 gtk_menu_shell_append ( GTK_MENU_SHELL(vis_submenu), item );
7880 gtk_widget_show ( item );
7881
7882 item = gtk_image_menu_item_new_with_mnemonic ( _("_Hide All Routes") );
7883 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_CLEAR, GTK_ICON_SIZE_MENU) );
7884 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_routes_visibility_off), pass_along );
7885 gtk_menu_shell_append ( GTK_MENU_SHELL(vis_submenu), item );
7886 gtk_widget_show ( item );
7887
7888 item = gtk_image_menu_item_new_with_mnemonic ( _("_Toggle") );
7889 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REFRESH, GTK_ICON_SIZE_MENU) );
7890 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_routes_visibility_toggle), pass_along );
7891 gtk_menu_shell_append ( GTK_MENU_SHELL(vis_submenu), item );
07c9d42b
RN
7892
7893 item = gtk_image_menu_item_new_with_mnemonic ( _("_List Routes...") );
7894 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
7895 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_track_list_dialog_single), pass_along );
7896 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7897
89fdc417 7898 gtk_widget_show ( item );
43984ba1
RN
7899
7900 item = gtk_menu_item_new_with_mnemonic ( _("_Statistics") );
7901 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_routes_stats), pass_along );
7902 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7903 gtk_widget_show ( item );
0d2b891f
RN
7904 }
7905
c9cac058
RN
7906
7907 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINTS || subtype == VIK_TRW_LAYER_SUBLAYER_TRACKS || subtype == VIK_TRW_LAYER_SUBLAYER_ROUTES ) {
7908 GtkWidget *submenu_sort = gtk_menu_new ();
7909 item = gtk_image_menu_item_new_with_mnemonic ( _("_Sort") );
7910 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REFRESH, GTK_ICON_SIZE_MENU) );
7911 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7912 gtk_widget_show ( item );
7913 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), submenu_sort );
7914
7915 item = gtk_image_menu_item_new_with_mnemonic ( _("Name _Ascending") );
7916 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_SORT_ASCENDING, GTK_ICON_SIZE_MENU) );
7917 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_sort_order_a2z), pass_along );
7918 gtk_menu_shell_append ( GTK_MENU_SHELL(submenu_sort), item );
7919 gtk_widget_show ( item );
7920
7921 item = gtk_image_menu_item_new_with_mnemonic ( _("Name _Descending") );
7922 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_SORT_DESCENDING, GTK_ICON_SIZE_MENU) );
7923 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_sort_order_z2a), pass_along );
7924 gtk_menu_shell_append ( GTK_MENU_SHELL(submenu_sort), item );
7925 gtk_widget_show ( item );
7926 }
7927
0d2b891f
RN
7928 GtkWidget *upload_submenu = gtk_menu_new ();
7929
7930 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK || subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE )
50a14534
EB
7931 {
7932 item = gtk_menu_item_new ();
7933 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7934 gtk_widget_show ( item );
7935
0d2b891f 7936 if ( l->current_track && subtype == VIK_TRW_LAYER_SUBLAYER_TRACK && !l->current_track->is_route )
37615c52 7937 item = gtk_menu_item_new_with_mnemonic ( _("_Finish Track") );
0d2b891f
RN
7938 if ( l->current_track && subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE && l->current_track->is_route )
7939 item = gtk_menu_item_new_with_mnemonic ( _("_Finish Route") );
7940 if ( l->current_track ) {
37615c52
RN
7941 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_finish_track), pass_along );
7942 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7943 gtk_widget_show ( item );
7944
7945 // Add separator
7946 item = gtk_menu_item_new ();
7947 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7948 gtk_widget_show ( item );
7949 }
7950
0d2b891f
RN
7951 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
7952 item = gtk_image_menu_item_new_with_mnemonic ( _("_View Track") );
7953 else
7954 item = gtk_image_menu_item_new_with_mnemonic ( _("_View Route") );
88a49424
RN
7955 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ZOOM_FIT, GTK_ICON_SIZE_MENU) );
7956 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_track_view), pass_along );
7957 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7958 gtk_widget_show ( item );
7959
626de648
RN
7960 item = gtk_menu_item_new_with_mnemonic ( _("_Statistics") );
7961 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_track_statistics), pass_along );
7962 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7963 gtk_widget_show ( item );
7964
88a49424 7965 GtkWidget *goto_submenu;
937b36ed 7966 goto_submenu = gtk_menu_new ();
d6de71f9
RN
7967 item = gtk_image_menu_item_new_with_mnemonic ( _("_Goto") );
7968 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_JUMP_TO, GTK_ICON_SIZE_MENU) );
937b36ed
RN
7969 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7970 gtk_widget_show ( item );
7971 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), goto_submenu );
7972
d6de71f9
RN
7973 item = gtk_image_menu_item_new_with_mnemonic ( _("_Startpoint") );
7974 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GOTO_FIRST, GTK_ICON_SIZE_MENU) );
50a14534 7975 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_startpoint), pass_along );
937b36ed 7976 gtk_menu_shell_append ( GTK_MENU_SHELL(goto_submenu), item );
50a14534
EB
7977 gtk_widget_show ( item );
7978
d6de71f9
RN
7979 item = gtk_image_menu_item_new_with_mnemonic ( _("\"_Center\"") );
7980 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_JUMP_TO, GTK_ICON_SIZE_MENU) );
50a14534 7981 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_center), pass_along );
937b36ed 7982 gtk_menu_shell_append ( GTK_MENU_SHELL(goto_submenu), item );
50a14534
EB
7983 gtk_widget_show ( item );
7984
d6de71f9
RN
7985 item = gtk_image_menu_item_new_with_mnemonic ( _("_Endpoint") );
7986 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GOTO_LAST, GTK_ICON_SIZE_MENU) );
50a14534 7987 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_endpoint), pass_along );
937b36ed 7988 gtk_menu_shell_append ( GTK_MENU_SHELL(goto_submenu), item );
50a14534 7989 gtk_widget_show ( item );
111fa174 7990
d6de71f9
RN
7991 item = gtk_image_menu_item_new_with_mnemonic ( _("_Highest Altitude") );
7992 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GOTO_TOP, GTK_ICON_SIZE_MENU) );
c28faca8
RN
7993 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_max_alt), pass_along );
7994 gtk_menu_shell_append ( GTK_MENU_SHELL(goto_submenu), item );
7995 gtk_widget_show ( item );
7996
d6de71f9
RN
7997 item = gtk_image_menu_item_new_with_mnemonic ( _("_Lowest Altitude") );
7998 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GOTO_BOTTOM, GTK_ICON_SIZE_MENU) );
c28faca8
RN
7999 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_min_alt), pass_along );
8000 gtk_menu_shell_append ( GTK_MENU_SHELL(goto_submenu), item );
8001 gtk_widget_show ( item );
8002
0d2b891f
RN
8003 // Routes don't have speeds
8004 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK ) {
8005 item = gtk_image_menu_item_new_with_mnemonic ( _("_Maximum Speed") );
8006 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_MEDIA_FORWARD, GTK_ICON_SIZE_MENU) );
8007 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_max_speed), pass_along );
8008 gtk_menu_shell_append ( GTK_MENU_SHELL(goto_submenu), item );
8009 gtk_widget_show ( item );
8010 }
03e7da75 8011
59f9414d
RN
8012 GtkWidget *combine_submenu;
8013 combine_submenu = gtk_menu_new ();
8014 item = gtk_image_menu_item_new_with_mnemonic ( _("Co_mbine") );
8015 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_CONNECT, GTK_ICON_SIZE_MENU) );
8016 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
469113fb 8017 gtk_widget_show ( item );
59f9414d 8018 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), combine_submenu );
469113fb 8019
0d2b891f
RN
8020 // Routes don't have times or segments...
8021 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK ) {
8022 item = gtk_menu_item_new_with_mnemonic ( _("_Merge By Time...") );
8023 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_merge_by_timestamp), pass_along );
8024 gtk_menu_shell_append ( GTK_MENU_SHELL(combine_submenu), item );
8025 gtk_widget_show ( item );
8026
8027 item = gtk_menu_item_new_with_mnemonic ( _("Merge _Segments") );
8028 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_merge_by_segment), pass_along );
8029 gtk_menu_shell_append ( GTK_MENU_SHELL(combine_submenu), item );
8030 gtk_widget_show ( item );
8031 }
111fa174 8032
40a68e7c 8033 item = gtk_menu_item_new_with_mnemonic ( _("Merge _With Other Tracks...") );
291edcab 8034 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_merge_with_other), pass_along );
59f9414d 8035 gtk_menu_shell_append ( GTK_MENU_SHELL(combine_submenu), item );
291edcab
HR
8036 gtk_widget_show ( item );
8037
0d2b891f
RN
8038 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
8039 item = gtk_menu_item_new_with_mnemonic ( _("_Append Track...") );
8040 else
8041 item = gtk_menu_item_new_with_mnemonic ( _("_Append Route...") );
9c34f614
RN
8042 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_append_track), pass_along );
8043 gtk_menu_shell_append ( GTK_MENU_SHELL(combine_submenu), item );
8044 gtk_widget_show ( item );
8045
6b5b6c47
RN
8046 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
8047 item = gtk_menu_item_new_with_mnemonic ( _("Append _Route...") );
8048 else
8049 item = gtk_menu_item_new_with_mnemonic ( _("Append _Track...") );
8050 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_append_other), pass_along );
8051 gtk_menu_shell_append ( GTK_MENU_SHELL(combine_submenu), item );
8052 gtk_widget_show ( item );
8053
78ac928c
RN
8054 GtkWidget *split_submenu;
8055 split_submenu = gtk_menu_new ();
8056 item = gtk_image_menu_item_new_with_mnemonic ( _("_Split") );
8057 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_DISCONNECT, GTK_ICON_SIZE_MENU) );
8058 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
291edcab 8059 gtk_widget_show ( item );
78ac928c 8060 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), split_submenu );
291edcab 8061
0d2b891f
RN
8062 // Routes don't have times or segments...
8063 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK ) {
8064 item = gtk_menu_item_new_with_mnemonic ( _("_Split By Time...") );
8065 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_split_by_timestamp), pass_along );
8066 gtk_menu_shell_append ( GTK_MENU_SHELL(split_submenu), item );
8067 gtk_widget_show ( item );
8068
8069 // ATM always enable this entry - don't want to have to analyse the track before displaying the menu - to keep the menu speedy
8070 item = gtk_menu_item_new_with_mnemonic ( _("Split Se_gments") );
8071 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_split_segments), pass_along );
8072 gtk_menu_shell_append ( GTK_MENU_SHELL(split_submenu), item );
8073 gtk_widget_show ( item );
8074 }
7114e879 8075
7306a492 8076 item = gtk_menu_item_new_with_mnemonic ( _("Split By _Number of Points...") );
af2341f3 8077 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_split_by_n_points), pass_along );
78ac928c 8078 gtk_menu_shell_append ( GTK_MENU_SHELL(split_submenu), item );
af2341f3
RN
8079 gtk_widget_show ( item );
8080
a4aefe69
RN
8081 item = gtk_menu_item_new_with_mnemonic ( _("Split at _Trackpoint") );
8082 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_split_at_trackpoint), pass_along );
8083 gtk_menu_shell_append ( GTK_MENU_SHELL(split_submenu), item );
8084 gtk_widget_show ( item );
8085 // Make it available only when a trackpoint is selected.
8086 gtk_widget_set_sensitive ( item, (gboolean)GPOINTER_TO_INT(l->current_tpl) );
8087
491794be
RN
8088 GtkWidget *insert_submenu = gtk_menu_new ();
8089 item = gtk_image_menu_item_new_with_mnemonic ( _("_Insert Points") );
8090 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ADD, GTK_ICON_SIZE_MENU) );
8091 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
8092 gtk_widget_show ( item );
8093 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), insert_submenu );
8094
8095 item = gtk_menu_item_new_with_mnemonic ( _("Insert Point _Before Selected Point") );
8096 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_insert_point_before), pass_along );
8097 gtk_menu_shell_append ( GTK_MENU_SHELL(insert_submenu), item );
8098 gtk_widget_show ( item );
8099 // Make it available only when a point is selected
8100 gtk_widget_set_sensitive ( item, (gboolean)GPOINTER_TO_INT(l->current_tpl) );
8101
8102 item = gtk_menu_item_new_with_mnemonic ( _("Insert Point _After Selected Point") );
8103 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_insert_point_after), pass_along );
8104 gtk_menu_shell_append ( GTK_MENU_SHELL(insert_submenu), item );
8105 gtk_widget_show ( item );
8106 // Make it available only when a point is selected
8107 gtk_widget_set_sensitive ( item, (gboolean)GPOINTER_TO_INT(l->current_tpl) );
8108
b6eda120
RN
8109 GtkWidget *delete_submenu;
8110 delete_submenu = gtk_menu_new ();
8111 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete Poi_nts") );
8112 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_DELETE, GTK_ICON_SIZE_MENU) );
8113 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
8114 gtk_widget_show ( item );
8115 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), delete_submenu );
8116
a56baa08
RN
8117 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete _Selected Point") );
8118 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_DELETE, GTK_ICON_SIZE_MENU) );
8119 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_point_selected), pass_along );
8120 gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
8121 gtk_widget_show ( item );
8122 // Make it available only when a point is selected
8123 gtk_widget_set_sensitive ( item, (gboolean)GPOINTER_TO_INT(l->current_tpl) );
8124
b6eda120
RN
8125 item = gtk_menu_item_new_with_mnemonic ( _("Delete Points With The Same _Position") );
8126 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_points_same_position), pass_along );
8127 gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
8128 gtk_widget_show ( item );
8129
6579ca1f
RN
8130 item = gtk_menu_item_new_with_mnemonic ( _("Delete Points With The Same _Time") );
8131 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_points_same_time), pass_along );
8132 gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
af2341f3
RN
8133 gtk_widget_show ( item );
8134
89d310d1
RN
8135 GtkWidget *transform_submenu;
8136 transform_submenu = gtk_menu_new ();
8137 item = gtk_image_menu_item_new_with_mnemonic ( _("_Transform") );
8138 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_CONVERT, GTK_ICON_SIZE_MENU) );
8139 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
8140 gtk_widget_show ( item );
8141 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), transform_submenu );
8142
4d333042
RN
8143 GtkWidget *dem_submenu;
8144 dem_submenu = gtk_menu_new ();
89d310d1
RN
8145 item = gtk_image_menu_item_new_with_mnemonic ( _("_Apply DEM Data") );
8146 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock ("vik-icon-DEM Download", GTK_ICON_SIZE_MENU) ); // Own icon - see stock_icons in vikwindow.c
89d310d1 8147 gtk_menu_shell_append ( GTK_MENU_SHELL(transform_submenu), item );
4d333042
RN
8148 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), dem_submenu );
8149
8150 item = gtk_image_menu_item_new_with_mnemonic ( _("_Overwrite") );
8151 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_apply_dem_data_all), pass_along );
8152 gtk_menu_shell_append ( GTK_MENU_SHELL(dem_submenu), item );
8153 gtk_widget_set_tooltip_text (item, _("Overwrite any existing elevation values with DEM values"));
8154 gtk_widget_show ( item );
8155
8156 item = gtk_image_menu_item_new_with_mnemonic ( _("_Keep Existing") );
8157 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_apply_dem_data_only_missing), pass_along );
8158 gtk_menu_shell_append ( GTK_MENU_SHELL(dem_submenu), item );
8159 gtk_widget_set_tooltip_text (item, _("Keep existing elevation values, only attempt for missing values"));
89d310d1
RN
8160 gtk_widget_show ( item );
8161
81ac2835
RN
8162 GtkWidget *smooth_submenu;
8163 smooth_submenu = gtk_menu_new ();
8164 item = gtk_menu_item_new_with_mnemonic ( _("_Smooth Missing Elevation Data") );
8165 gtk_menu_shell_append ( GTK_MENU_SHELL(transform_submenu), item );
8166 gtk_widget_show ( item );
8167 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), smooth_submenu );
8168
8169 item = gtk_image_menu_item_new_with_mnemonic ( _("_Interpolated") );
8170 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_missing_elevation_data_interp), pass_along );
8171 gtk_menu_shell_append ( GTK_MENU_SHELL(smooth_submenu), item );
8172 gtk_widget_set_tooltip_text (item, _("Interpolate between known elevation values to derive values for the missing elevations"));
8173 gtk_widget_show ( item );
8174
8175 item = gtk_image_menu_item_new_with_mnemonic ( _("_Flat") );
8176 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_missing_elevation_data_flat), pass_along );
8177 gtk_menu_shell_append ( GTK_MENU_SHELL(smooth_submenu), item );
8178 gtk_widget_set_tooltip_text (item, _("Set unknown elevation values to the last known value"));
8179 gtk_widget_show ( item );
8180
89d310d1
RN
8181 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
8182 item = gtk_image_menu_item_new_with_mnemonic ( _("C_onvert to a Route") );
8183 else
8184 item = gtk_image_menu_item_new_with_mnemonic ( _("C_onvert to a Track") );
8185 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_CONVERT, GTK_ICON_SIZE_MENU) );
8186 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_convert_track_route), pass_along );
8187 gtk_menu_shell_append ( GTK_MENU_SHELL(transform_submenu), item );
8188 gtk_widget_show ( item );
8189
76b14439
RN
8190 // Routes don't have timestamps - so this is only available for tracks
8191 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK ) {
8192 item = gtk_image_menu_item_new_with_mnemonic ( _("_Anonymize Times") );
8193 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_anonymize_times), pass_along );
8194 gtk_menu_shell_append ( GTK_MENU_SHELL(transform_submenu), item );
8195 gtk_widget_set_tooltip_text (item, _("Shift timestamps to a relative offset from 1901-01-01"));
8196 gtk_widget_show ( item );
8197 }
8198
0d2b891f
RN
8199 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
8200 item = gtk_image_menu_item_new_with_mnemonic ( _("_Reverse Track") );
8201 else
8202 item = gtk_image_menu_item_new_with_mnemonic ( _("_Reverse Route") );
eb9fd106
RN
8203 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_BACK, GTK_ICON_SIZE_MENU) );
8204 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_reverse), pass_along );
8205 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
8206 gtk_widget_show ( item );
8207
1a3be6a8
GB
8208 if ( subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE ) {
8209 item = gtk_image_menu_item_new_with_mnemonic ( _("Refine Route...") );
8210 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_FIND, GTK_ICON_SIZE_MENU) );
8211 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_route_refine), pass_along );
8212 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
8213 gtk_widget_show ( item );
8214 }
8215
6bb72350
RN
8216 /* ATM This function is only available via the layers panel, due to the method in finding out the maps in use */
8217 if ( vlp ) {
0d2b891f
RN
8218 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
8219 item = gtk_image_menu_item_new_with_mnemonic ( _("Down_load Maps Along Track...") );
8220 else
8221 item = gtk_image_menu_item_new_with_mnemonic ( _("Down_load Maps Along Route...") );
43b5e335 8222 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock ("vik-icon-Maps Download", GTK_ICON_SIZE_MENU) ); // Own icon - see stock_icons in vikwindow.c
6bb72350
RN
8223 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_download_map_along_track_cb), pass_along );
8224 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
8225 gtk_widget_show ( item );
8226 }
ad0a8c2d 8227
0d2b891f
RN
8228 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
8229 item = gtk_image_menu_item_new_with_mnemonic ( _("_Export Track as GPX...") );
8230 else
8231 item = gtk_image_menu_item_new_with_mnemonic ( _("_Export Route as GPX...") );
d6de71f9 8232 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_HARDDISK, GTK_ICON_SIZE_MENU) );
7f6757c4
RN
8233 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_gpx_track), pass_along );
8234 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
8235 gtk_widget_show ( item );
8236
0d2b891f
RN
8237 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
8238 item = gtk_image_menu_item_new_with_mnemonic ( _("E_xtend Track End") );
8239 else
8240 item = gtk_image_menu_item_new_with_mnemonic ( _("E_xtend Route End") );
d6de71f9 8241 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ADD, GTK_ICON_SIZE_MENU) );
8fb71d6c
EB
8242 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_extend_track_end), pass_along );
8243 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
ad0a8c2d 8244 gtk_widget_show ( item );
5092de80 8245
008e972c
RN
8246 if ( subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE ) {
8247 item = gtk_image_menu_item_new_with_mnemonic ( _("Extend _Using Route Finder") );
43b5e335 8248 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock ("vik-icon-Route Finder", GTK_ICON_SIZE_MENU) ); // Own icon - see stock_icons in vikwindow.c
008e972c
RN
8249 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_extend_track_end_route_finder), pass_along );
8250 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
8251 gtk_widget_show ( item );
8252 }
a7955c1d 8253
0d2b891f
RN
8254 // ATM can't upload a single waypoint but can do waypoints to a GPS
8255 if ( subtype != VIK_TRW_LAYER_SUBLAYER_WAYPOINT ) {
8256 item = gtk_image_menu_item_new_with_mnemonic ( _("_Upload") );
8257 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_UP, GTK_ICON_SIZE_MENU) );
8258 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
8259 gtk_widget_show ( item );
8260 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), upload_submenu );
8261
8262 item = gtk_image_menu_item_new_with_mnemonic ( _("_Upload to GPS...") );
8263 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_FORWARD, GTK_ICON_SIZE_MENU) );
8264 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_gps_upload_any), pass_along );
8265 gtk_menu_shell_append ( GTK_MENU_SHELL(upload_submenu), item );
8266 gtk_widget_show ( item );
8267 }
8268 }
e50758c7 8269
be3b5aa8
RN
8270 // Only made available if a suitable program is installed
8271 if ( have_diary_program ) {
8272 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK || subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT ) {
8273 item = gtk_image_menu_item_new_with_mnemonic ( _("Diar_y") );
8274 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_SPELL_CHECK, GTK_ICON_SIZE_MENU) );
8275 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_diary), pass_along );
8276 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
8277 gtk_widget_show ( item );
8278 }
8279 }
8280
008e972c
RN
8281#ifdef VIK_CONFIG_GOOGLE
8282 if ( subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE && is_valid_google_route ( l, sublayer ) )
8283 {
8284 item = gtk_image_menu_item_new_with_mnemonic ( _("_View Google Directions") );
8285 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NETWORK, GTK_ICON_SIZE_MENU) );
8286 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_google_route_webpage), pass_along );
8287 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
8288 gtk_widget_show ( item );
8289 }
8290#endif
8291
0d2b891f
RN
8292 // Some things aren't usable with routes
8293 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK ) {
5092de80 8294#ifdef VIK_CONFIG_OPENSTREETMAP
d6de71f9 8295 item = gtk_image_menu_item_new_with_mnemonic ( _("Upload to _OSM...") );
d6c110f1
RN
8296 // Convert internal pointer into track
8297 pass_along[MA_MISC] = g_hash_table_lookup ( l->tracks, sublayer);
d6de71f9 8298 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_UP, GTK_ICON_SIZE_MENU) );
3185b5d3 8299 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_osm_traces_upload_track_cb), pass_along );
e50758c7 8300 gtk_menu_shell_append ( GTK_MENU_SHELL(upload_submenu), item );
5092de80
GB
8301 gtk_widget_show ( item );
8302#endif
bddd2056 8303
d6de71f9
RN
8304 item = gtk_image_menu_item_new_with_mnemonic ( _("Use with _Filter") );
8305 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
28c82d8b
EB
8306 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_track_use_with_filter), pass_along );
8307 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
8308 gtk_widget_show ( item );
8309
6bb72350
RN
8310 /* ATM This function is only available via the layers panel, due to needing a vlp */
8311 if ( vlp ) {
8312 item = a_acquire_track_menu ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(l)), vlp,
0d2b891f
RN
8313 vik_layers_panel_get_viewport(VIK_LAYERS_PANEL(vlp)),
8314 g_hash_table_lookup ( l->tracks, (gchar *) sublayer ) );
6bb72350 8315 if ( item ) {
0d2b891f
RN
8316 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
8317 gtk_widget_show ( item );
6bb72350
RN
8318 }
8319 }
c95d6b00 8320
b3eb3b98 8321#ifdef VIK_CONFIG_GEOTAG
0d2b891f
RN
8322 item = gtk_menu_item_new_with_mnemonic ( _("Geotag _Images...") );
8323 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_geotagging_track), pass_along );
8324 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
8325 gtk_widget_show ( item );
b3eb3b98 8326#endif
0d2b891f 8327 }
b3eb3b98 8328
0d2b891f 8329 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK || subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE ) {
c95d6b00
RN
8330 // Only show on viewport popmenu when a trackpoint is selected
8331 if ( ! vlp && l->current_tpl ) {
8332 // Add separator
8333 item = gtk_menu_item_new ();
8334 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
8335 gtk_widget_show ( item );
8336
8337 item = gtk_image_menu_item_new_with_mnemonic ( _("_Edit Trackpoint") );
8338 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_PROPERTIES, GTK_ICON_SIZE_MENU) );
8339 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_edit_trackpoint), pass_along );
8340 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
8341 gtk_widget_show ( item );
8342 }
50a14534
EB
8343 }
8344
667fda15
RN
8345 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINTS || subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT ) {
8346 GtkWidget *transform_submenu;
8347 transform_submenu = gtk_menu_new ();
8348 item = gtk_image_menu_item_new_with_mnemonic ( _("_Transform") );
8349 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_CONVERT, GTK_ICON_SIZE_MENU) );
8350 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
8351 gtk_widget_show ( item );
8352 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), transform_submenu );
8353
8354 GtkWidget *dem_submenu;
8355 dem_submenu = gtk_menu_new ();
8356 item = gtk_image_menu_item_new_with_mnemonic ( _("_Apply DEM Data") );
8357 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock ("vik-icon-DEM Download", GTK_ICON_SIZE_MENU) ); // Own icon - see stock_icons in vikwindow.c
8358 gtk_menu_shell_append ( GTK_MENU_SHELL(transform_submenu), item );
8359 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), dem_submenu );
8360
8361 item = gtk_image_menu_item_new_with_mnemonic ( _("_Overwrite") );
8362 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_apply_dem_data_wpt_all), pass_along );
8363 gtk_menu_shell_append ( GTK_MENU_SHELL(dem_submenu), item );
8364 gtk_widget_set_tooltip_text (item, _("Overwrite any existing elevation values with DEM values"));
8365 gtk_widget_show ( item );
8366
8367 item = gtk_image_menu_item_new_with_mnemonic ( _("_Keep Existing") );
8368 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_apply_dem_data_wpt_only_missing), pass_along );
8369 gtk_menu_shell_append ( GTK_MENU_SHELL(dem_submenu), item );
8370 gtk_widget_set_tooltip_text (item, _("Keep existing elevation values, only attempt for missing values"));
8371 gtk_widget_show ( item );
8372 }
8373
4d333042
RN
8374 gtk_widget_show_all ( GTK_WIDGET(menu) );
8375
50a14534
EB
8376 return rv;
8377}
8378
491794be
RN
8379// TODO: Probably better to rework this track manipulation in viktrack.c
8380static void trw_layer_insert_tp_beside_current_tp ( VikTrwLayer *vtl, gboolean before )
db79f75f 8381{
491794be 8382 // sanity check
db79f75f
RN
8383 if (!vtl->current_tpl)
8384 return;
db79f75f
RN
8385
8386 VikTrackpoint *tp_current = VIK_TRACKPOINT(vtl->current_tpl->data);
491794be
RN
8387 VikTrackpoint *tp_other = NULL;
8388
8389 if ( before ) {
8390 if (!vtl->current_tpl->prev)
8391 return;
8392 tp_other = VIK_TRACKPOINT(vtl->current_tpl->prev->data);
8393 } else {
8394 if (!vtl->current_tpl->next)
8395 return;
8396 tp_other = VIK_TRACKPOINT(vtl->current_tpl->next->data);
8397 }
db79f75f 8398
491794be
RN
8399 // Use current and other trackpoints to form a new track point which is inserted into the tracklist
8400 if ( tp_other ) {
db79f75f
RN
8401
8402 VikTrackpoint *tp_new = vik_trackpoint_new();
491794be 8403 struct LatLon ll_current, ll_other;
db79f75f 8404 vik_coord_to_latlon ( &tp_current->coord, &ll_current );
491794be 8405 vik_coord_to_latlon ( &tp_other->coord, &ll_other );
db79f75f
RN
8406
8407 /* main positional interpolation */
491794be 8408 struct LatLon ll_new = { (ll_current.lat+ll_other.lat)/2, (ll_current.lon+ll_other.lon)/2 };
db79f75f
RN
8409 vik_coord_load_from_latlon ( &(tp_new->coord), vtl->coord_mode, &ll_new );
8410
8411 /* Now other properties that can be interpolated */
491794be 8412 tp_new->altitude = (tp_current->altitude + tp_other->altitude) / 2;
db79f75f 8413
491794be 8414 if (tp_current->has_timestamp && tp_other->has_timestamp) {
db79f75f
RN
8415 /* Note here the division is applied to each part, then added
8416 This is to avoid potential overflow issues with a 32 time_t for dates after midpoint of this Unix time on 2004/01/04 */
491794be 8417 tp_new->timestamp = (tp_current->timestamp/2) + (tp_other->timestamp/2);
db79f75f
RN
8418 tp_new->has_timestamp = TRUE;
8419 }
8420
491794be
RN
8421 if (tp_current->speed != NAN && tp_other->speed != NAN)
8422 tp_new->speed = (tp_current->speed + tp_other->speed)/2;
db79f75f
RN
8423
8424 /* TODO - improve interpolation of course, as it may not be correct.
8425 if courses in degrees are 350 + 020, the mid course more likely to be 005 (not 185)
8426 [similar applies if value is in radians] */
491794be
RN
8427 if (tp_current->course != NAN && tp_other->course != NAN)
8428 tp_new->course = (tp_current->course + tp_other->course)/2;
db79f75f
RN
8429
8430 /* DOP / sat values remain at defaults as not they do not seem applicable to a dreamt up point */
8431
491794be 8432 // Insert new point into the appropriate trackpoint list, either before or after the current trackpoint as directed
0d2b891f
RN
8433 VikTrack *trk = g_hash_table_lookup ( vtl->tracks, vtl->current_tp_id );
8434 if ( !trk )
8435 // Otherwise try routes
8436 trk = g_hash_table_lookup ( vtl->routes, vtl->current_tp_id );
8437 if ( !trk )
8438 return;
8439
8440 gint index = g_list_index ( trk->trackpoints, tp_current );
db79f75f 8441 if ( index > -1 ) {
491794be
RN
8442 if ( !before )
8443 index = index + 1;
20981fd6 8444 // NB no recalculation of bounds since it is inserted between points
491794be 8445 trk->trackpoints = g_list_insert ( trk->trackpoints, tp_new, index );
db79f75f
RN
8446 }
8447 }
8448}
50a14534 8449
50a14534
EB
8450static void trw_layer_cancel_current_tp ( VikTrwLayer *vtl, gboolean destroy )
8451{
8452 if ( vtl->tpwin )
8453 {
8454 if ( destroy)
8455 {
8456 gtk_widget_destroy ( GTK_WIDGET(vtl->tpwin) );
8457 vtl->tpwin = NULL;
8458 }
8459 else
8460 vik_trw_layer_tpwin_set_empty ( vtl->tpwin );
8461 }
8462 if ( vtl->current_tpl )
8463 {
8464 vtl->current_tpl = NULL;
ce4bd1cf
RN
8465 vtl->current_tp_track = NULL;
8466 vtl->current_tp_id = NULL;
da121f9b 8467 vik_layer_emit_update(VIK_LAYER(vtl));
50a14534
EB
8468 }
8469}
8470
8471static void trw_layer_tpwin_response ( VikTrwLayer *vtl, gint response )
8472{
8473 g_assert ( vtl->tpwin != NULL );
8474 if ( response == VIK_TRW_LAYER_TPWIN_CLOSE )
8475 trw_layer_cancel_current_tp ( vtl, TRUE );
0d601fd4
RN
8476
8477 if ( vtl->current_tpl == NULL )
8478 return;
8479
8480 if ( response == VIK_TRW_LAYER_TPWIN_SPLIT && vtl->current_tpl->next && vtl->current_tpl->prev )
50a14534 8481 {
0d2b891f 8482 trw_layer_split_at_selected_trackpoint ( vtl, vtl->current_tp_track->is_route ? VIK_TRW_LAYER_SUBLAYER_ROUTE : VIK_TRW_LAYER_SUBLAYER_TRACK );
84d3d9f9 8483 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, vtl->current_tp_track->name );
50a14534
EB
8484 }
8485 else if ( response == VIK_TRW_LAYER_TPWIN_DELETE )
8486 {
ce4bd1cf 8487 VikTrack *tr = g_hash_table_lookup ( vtl->tracks, vtl->current_tp_id );
0d2b891f
RN
8488 if ( tr == NULL )
8489 tr = g_hash_table_lookup ( vtl->routes, vtl->current_tp_id );
ce4bd1cf
RN
8490 if ( tr == NULL )
8491 return;
50a14534 8492
a56baa08 8493 trw_layer_trackpoint_selected_delete ( vtl, tr );
ab4553c3 8494
a56baa08 8495 if ( vtl->current_tpl )
ab4553c3 8496 // Reset dialog with the available adjacent trackpoint
a56baa08 8497 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, vtl->current_tp_track->name );
50a14534 8498
a56baa08 8499 vik_layer_emit_update(VIK_LAYER(vtl));
50a14534
EB
8500 }
8501 else if ( response == VIK_TRW_LAYER_TPWIN_FORWARD && vtl->current_tpl->next )
8502 {
ce4bd1cf
RN
8503 if ( vtl->current_tp_track )
8504 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl = vtl->current_tpl->next, vtl->current_tp_track->name );
da121f9b 8505 vik_layer_emit_update(VIK_LAYER(vtl)); /* TODO longone: either move or only update if tp is inside drawing window */
50a14534
EB
8506 }
8507 else if ( response == VIK_TRW_LAYER_TPWIN_BACK && vtl->current_tpl->prev )
8508 {
ce4bd1cf
RN
8509 if ( vtl->current_tp_track )
8510 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl = vtl->current_tpl->prev, vtl->current_tp_track->name );
da121f9b 8511 vik_layer_emit_update(VIK_LAYER(vtl));
50a14534 8512 }
2880a1de
RN
8513 else if ( response == VIK_TRW_LAYER_TPWIN_INSERT && vtl->current_tpl->next )
8514 {
491794be 8515 trw_layer_insert_tp_beside_current_tp ( vtl, FALSE );
da121f9b 8516 vik_layer_emit_update(VIK_LAYER(vtl));
2880a1de 8517 }
50a14534 8518 else if ( response == VIK_TRW_LAYER_TPWIN_DATA_CHANGED )
da121f9b 8519 vik_layer_emit_update(VIK_LAYER(vtl));
50a14534
EB
8520}
8521
d6175f49
RN
8522/**
8523 * trw_layer_dialog_shift:
8524 * @vertical: The reposition strategy. If Vertical moves dialog vertically, otherwise moves it horizontally
8525 *
8526 * Try to reposition a dialog if it's over the specified coord
8527 * so to not obscure the item of interest
8528 */
8529void trw_layer_dialog_shift ( VikTrwLayer *vtl, GtkWindow *dialog, VikCoord *coord, gboolean vertical )
8530{
8531 GtkWindow *parent = VIK_GTK_WINDOW_FROM_LAYER(vtl); //i.e. the main window
8532
06ce5b5a
RN
8533 // Attempt force dialog to be shown so we can find out where it is more reliably...
8534 while ( gtk_events_pending() )
8535 gtk_main_iteration ();
8536
d6175f49
RN
8537 // get parent window position & size
8538 gint win_pos_x, win_pos_y;
8539 gtk_window_get_position ( parent, &win_pos_x, &win_pos_y );
8540
8541 gint win_size_x, win_size_y;
8542 gtk_window_get_size ( parent, &win_size_x, &win_size_y );
8543
8544 // get own dialog size
8545 gint dia_size_x, dia_size_y;
8546 gtk_window_get_size ( dialog, &dia_size_x, &dia_size_y );
8547
8548 // get own dialog position
8549 gint dia_pos_x, dia_pos_y;
8550 gtk_window_get_position ( dialog, &dia_pos_x, &dia_pos_y );
8551
8552 // Dialog not 'realized'/positioned - so can't really do any repositioning logic
8553 if ( dia_pos_x > 2 && dia_pos_y > 2 ) {
8554
8555 VikViewport *vvp = vik_window_viewport ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)) );
8556
8557 gint vp_xx, vp_yy; // In viewport pixels
8558 vik_viewport_coord_to_screen ( vvp, coord, &vp_xx, &vp_yy );
8559
8560 // Work out the 'bounding box' in pixel terms of the dialog and only move it when over the position
8561
8562 gint dest_x = 0;
8563 gint dest_y = 0;
8564 if ( gtk_widget_translate_coordinates ( GTK_WIDGET(vvp), GTK_WIDGET(parent), 0, 0, &dest_x, &dest_y ) ) {
8565
8566 // Transform Viewport pixels into absolute pixels
8567 gint tmp_xx = vp_xx + dest_x + win_pos_x - 10;
8568 gint tmp_yy = vp_yy + dest_y + win_pos_y - 10;
8569
8570 // Is dialog over the point (to within an ^^ edge value)
8571 if ( (tmp_xx > dia_pos_x) && (tmp_xx < (dia_pos_x + dia_size_x)) &&
8572 (tmp_yy > dia_pos_y) && (tmp_yy < (dia_pos_y + dia_size_y)) ) {
8573
8574 if ( vertical ) {
8575 // Shift up<->down
8576 gint hh = vik_viewport_get_height ( vvp );
8577
8578 // Consider the difference in viewport to the full window
8579 gint offset_y = dest_y;
8580 // Add difference between dialog and window sizes
8581 offset_y += win_pos_y + (hh/2 - dia_size_y)/2;
8582
8583 if ( vp_yy > hh/2 ) {
8584 // Point in bottom half, move window to top half
8585 gtk_window_move ( dialog, dia_pos_x, offset_y );
8586 }
8587 else {
8588 // Point in top half, move dialog down
8589 gtk_window_move ( dialog, dia_pos_x, hh/2 + offset_y );
8590 }
8591 }
8592 else {
8593 // Shift left<->right
8594 gint ww = vik_viewport_get_width ( vvp );
8595
8596 // Consider the difference in viewport to the full window
8597 gint offset_x = dest_x;
8598 // Add difference between dialog and window sizes
8599 offset_x += win_pos_x + (ww/2 - dia_size_x)/2;
8600
8601 if ( vp_xx > ww/2 ) {
8602 // Point on right, move window to left
8603 gtk_window_move ( dialog, offset_x, dia_pos_y );
8604 }
8605 else {
8606 // Point on left, move right
8607 gtk_window_move ( dialog, ww/2 + offset_x, dia_pos_y );
8608 }
8609 }
8610 }
8611 }
8612 }
8613}
8614
50a14534
EB
8615static void trw_layer_tpwin_init ( VikTrwLayer *vtl )
8616{
8617 if ( ! vtl->tpwin )
8618 {
8619 vtl->tpwin = vik_trw_layer_tpwin_new ( VIK_GTK_WINDOW_FROM_LAYER(vtl) );
8620 g_signal_connect_swapped ( GTK_DIALOG(vtl->tpwin), "response", G_CALLBACK(trw_layer_tpwin_response), vtl );
8621 /* connect signals -- DELETE SIGNAL VERY IMPORTANT TO SET TO NULL */
8622 g_signal_connect_swapped ( vtl->tpwin, "delete-event", G_CALLBACK(trw_layer_cancel_current_tp), vtl );
d6175f49 8623
50a14534 8624 gtk_widget_show_all ( GTK_WIDGET(vtl->tpwin) );
d6175f49
RN
8625
8626 if ( vtl->current_tpl ) {
8627 // get tp pixel position
8628 VikTrackpoint *tp = VIK_TRACKPOINT(vtl->current_tpl->data);
8629
8630 // Shift up<->down to try not to obscure the trackpoint.
8631 trw_layer_dialog_shift ( vtl, GTK_WINDOW(vtl->tpwin), &(tp->coord), TRUE );
8632 }
50a14534 8633 }
d6175f49 8634
50a14534 8635 if ( vtl->current_tpl )
ce4bd1cf
RN
8636 if ( vtl->current_tp_track )
8637 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, vtl->current_tp_track->name );
50a14534
EB
8638 /* set layer name and TP data */
8639}
8640
941aa6e9
AF
8641/***************************************************************************
8642 ** Tool code
8643 ***************************************************************************/
50a14534 8644
941aa6e9 8645/*** Utility data structures and functions ****/
50a14534
EB
8646
8647typedef struct {
8648 gint x, y;
8649 gint closest_x, closest_y;
7d961c6c 8650 gboolean draw_images;
c9570f86 8651 gpointer *closest_wp_id;
50a14534
EB
8652 VikWaypoint *closest_wp;
8653 VikViewport *vvp;
8654} WPSearchParams;
8655
941aa6e9
AF
8656typedef struct {
8657 gint x, y;
8658 gint closest_x, closest_y;
ce4bd1cf 8659 gpointer closest_track_id;
941aa6e9
AF
8660 VikTrackpoint *closest_tp;
8661 VikViewport *vvp;
8662 GList *closest_tpl;
79773236 8663 LatLonBBox bbox;
941aa6e9
AF
8664} TPSearchParams;
8665
c9570f86 8666static void waypoint_search_closest_tp ( gpointer id, VikWaypoint *wp, WPSearchParams *params )
50a14534
EB
8667{
8668 gint x, y;
8669 if ( !wp->visible )
8670 return;
8671
8672 vik_viewport_coord_to_screen ( params->vvp, &(wp->coord), &x, &y );
59b0a97a
RN
8673
8674 // If waypoint has an image then use the image size to select
7d961c6c 8675 if ( params->draw_images && wp->image ) {
59b0a97a
RN
8676 gint slackx, slacky;
8677 slackx = wp->image_width / 2;
8678 slacky = wp->image_height / 2;
8679
8680 if ( x <= params->x + slackx && x >= params->x - slackx
8681 && y <= params->y + slacky && y >= params->y - slacky ) {
c9570f86 8682 params->closest_wp_id = id;
59b0a97a
RN
8683 params->closest_wp = wp;
8684 params->closest_x = x;
8685 params->closest_y = y;
8686 }
50a14534 8687 }
59b0a97a
RN
8688 else if ( abs (x - params->x) <= WAYPOINT_SIZE_APPROX && abs (y - params->y) <= WAYPOINT_SIZE_APPROX &&
8689 ((!params->closest_wp) || /* was the old waypoint we already found closer than this one? */
8690 abs(x - params->x)+abs(y - params->y) < abs(x - params->closest_x)+abs(y - params->closest_y)))
8691 {
c9570f86 8692 params->closest_wp_id = id;
59b0a97a
RN
8693 params->closest_wp = wp;
8694 params->closest_x = x;
8695 params->closest_y = y;
8696 }
50a14534
EB
8697}
8698
ce4bd1cf 8699static void track_search_closest_tp ( gpointer id, VikTrack *t, TPSearchParams *params )
50a14534 8700{
941aa6e9
AF
8701 GList *tpl = t->trackpoints;
8702 VikTrackpoint *tp;
50a14534 8703
941aa6e9
AF
8704 if ( !t->visible )
8705 return;
50a14534 8706
79773236
RN
8707 if ( ! BBOX_INTERSECT ( t->bbox, params->bbox ) )
8708 return;
8709
941aa6e9
AF
8710 while (tpl)
8711 {
8712 gint x, y;
8713 tp = VIK_TRACKPOINT(tpl->data);
8714
8715 vik_viewport_coord_to_screen ( params->vvp, &(tp->coord), &x, &y );
8716
8717 if ( abs (x - params->x) <= TRACKPOINT_SIZE_APPROX && abs (y - params->y) <= TRACKPOINT_SIZE_APPROX &&
8718 ((!params->closest_tp) || /* was the old trackpoint we already found closer than this one? */
8719 abs(x - params->x)+abs(y - params->y) < abs(x - params->closest_x)+abs(y - params->closest_y)))
50a14534 8720 {
ce4bd1cf 8721 params->closest_track_id = id;
941aa6e9
AF
8722 params->closest_tp = tp;
8723 params->closest_tpl = tpl;
8724 params->closest_x = x;
8725 params->closest_y = y;
50a14534 8726 }
941aa6e9 8727 tpl = tpl->next;
50a14534 8728 }
941aa6e9
AF
8729}
8730
0d2b891f
RN
8731// ATM: Leave this as 'Track' only.
8732// Not overly bothered about having a snap to route trackpoint capability
941aa6e9
AF
8733static VikTrackpoint *closest_tp_in_five_pixel_interval ( VikTrwLayer *vtl, VikViewport *vvp, gint x, gint y )
8734{
8735 TPSearchParams params;
8736 params.x = x;
8737 params.y = y;
8738 params.vvp = vvp;
ce4bd1cf 8739 params.closest_track_id = NULL;
941aa6e9 8740 params.closest_tp = NULL;
79773236 8741 vik_viewport_get_min_max_lat_lon ( params.vvp, &(params.bbox.south), &(params.bbox.north), &(params.bbox.west), &(params.bbox.east) );
941aa6e9
AF
8742 g_hash_table_foreach ( vtl->tracks, (GHFunc) track_search_closest_tp, &params);
8743 return params.closest_tp;
50a14534
EB
8744}
8745
8746static VikWaypoint *closest_wp_in_five_pixel_interval ( VikTrwLayer *vtl, VikViewport *vvp, gint x, gint y )
8747{
8748 WPSearchParams params;
8749 params.x = x;
8750 params.y = y;
8751 params.vvp = vvp;
7d961c6c 8752 params.draw_images = vtl->drawimages;
50a14534 8753 params.closest_wp = NULL;
c9570f86 8754 params.closest_wp_id = NULL;
50a14534
EB
8755 g_hash_table_foreach ( vtl->waypoints, (GHFunc) waypoint_search_closest_tp, &params);
8756 return params.closest_wp;
8757}
8758
95d1b757 8759
08f14055
RN
8760// Some forward declarations
8761static void marker_begin_move ( tool_ed_t *t, gint x, gint y );
8762static void marker_moveto ( tool_ed_t *t, gint x, gint y );
8763static void marker_end_move ( tool_ed_t *t );
8764//
8765
8766static gboolean trw_layer_select_move ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp, tool_ed_t* t )
8767{
8768 if ( t->holding ) {
8769 VikCoord new_coord;
8770 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
8771
8772 // Here always allow snapping back to the original location
8773 // this is useful when one decides not to move the thing afterall
8774 // If one wants to move the item only a little bit then don't hold down the 'snap' key!
8775
8776 // snap to TP
8777 if ( event->state & GDK_CONTROL_MASK )
8778 {
8779 VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
8780 if ( tp )
8781 new_coord = tp->coord;
8782 }
8783
8784 // snap to WP
8785 if ( event->state & GDK_SHIFT_MASK )
8786 {
8787 VikWaypoint *wp = closest_wp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
8788 if ( wp )
8789 new_coord = wp->coord;
8790 }
8791
8792 gint x, y;
8793 vik_viewport_coord_to_screen ( vvp, &new_coord, &x, &y );
8794
8795 marker_moveto ( t, x, y );
8796
8797 return TRUE;
8798 }
8799 return FALSE;
8800}
8801
8802static gboolean trw_layer_select_release ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp, tool_ed_t* t )
8803{
8804 if ( t->holding && event->button == 1 )
8805 {
8806 VikCoord new_coord;
8807 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
8808
8809 // snap to TP
8810 if ( event->state & GDK_CONTROL_MASK )
8811 {
8812 VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
8813 if ( tp )
8814 new_coord = tp->coord;
8815 }
8816
8817 // snap to WP
8818 if ( event->state & GDK_SHIFT_MASK )
8819 {
8820 VikWaypoint *wp = closest_wp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
8821 if ( wp )
8822 new_coord = wp->coord;
8823 }
8824
8825 marker_end_move ( t );
8826
8827 // Determine if working on a waypoint or a trackpoint
aa0665e5 8828 if ( t->is_waypoint ) {
bd78ae7d 8829 // Update waypoint position
08f14055 8830 vtl->current_wp->coord = new_coord;
aa0665e5 8831 trw_layer_calculate_bounds_waypoints ( vtl );
bd78ae7d
RN
8832 // Reset waypoint pointer
8833 vtl->current_wp = NULL;
8834 vtl->current_wp_id = NULL;
aa0665e5 8835 }
08f14055
RN
8836 else {
8837 if ( vtl->current_tpl ) {
ce4bd1cf 8838 VIK_TRACKPOINT(vtl->current_tpl->data)->coord = new_coord;
20981fd6
RN
8839
8840 if ( vtl->current_tp_track )
8841 vik_track_calculate_bounds ( vtl->current_tp_track );
8842
bd78ae7d 8843 if ( vtl->tpwin )
ce4bd1cf
RN
8844 if ( vtl->current_tp_track )
8845 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, vtl->current_tp_track->name );
bd78ae7d 8846 // NB don't reset the selected trackpoint, thus ensuring it's still in the tpwin
08f14055
RN
8847 }
8848 }
8849
da121f9b 8850 vik_layer_emit_update ( VIK_LAYER(vtl) );
08f14055
RN
8851 return TRUE;
8852 }
8853 return FALSE;
8854}
8855
77ad64fa
RN
8856/*
8857 Returns true if a waypoint or track is found near the requested event position for this particular layer
8858 The item found is automatically selected
8859 This is a tool like feature but routed via the layer interface, since it's instigated by a 'global' layer tool in vikwindow.c
8860 */
08f14055 8861static gboolean trw_layer_select_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp, tool_ed_t* tet )
77ad64fa
RN
8862{
8863 if ( event->button != 1 )
8864 return FALSE;
8865
8866 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
8867 return FALSE;
8868
0d2b891f 8869 if ( !vtl->tracks_visible && !vtl->waypoints_visible && !vtl->routes_visible )
77ad64fa
RN
8870 return FALSE;
8871
79773236
RN
8872 LatLonBBox bbox;
8873 vik_viewport_get_min_max_lat_lon ( vvp, &(bbox.south), &(bbox.north), &(bbox.west), &(bbox.east) );
8874
08f14055 8875 // 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 8876
79773236 8877 if ( vtl->waypoints_visible && BBOX_INTERSECT (vtl->waypoints_bbox, bbox ) ) {
77ad64fa
RN
8878 WPSearchParams wp_params;
8879 wp_params.vvp = vvp;
8880 wp_params.x = event->x;
8881 wp_params.y = event->y;
7d961c6c 8882 wp_params.draw_images = vtl->drawimages;
c9570f86 8883 wp_params.closest_wp_id = NULL;
77ad64fa
RN
8884 wp_params.closest_wp = NULL;
8885
8886 g_hash_table_foreach ( vtl->waypoints, (GHFunc) waypoint_search_closest_tp, &wp_params);
8887
8888 if ( wp_params.closest_wp ) {
08f14055
RN
8889
8890 // Select
c9570f86 8891 vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, g_hash_table_lookup ( vtl->waypoints_iters, wp_params.closest_wp_id ), TRUE );
08f14055
RN
8892
8893 // Too easy to move it so must be holding shift to start immediately moving it
3c73e6c4 8894 // or otherwise be previously selected but not have an image (otherwise clicking within image bounds (again) moves it)
08f14055 8895 if ( event->state & GDK_SHIFT_MASK ||
3c73e6c4 8896 ( vtl->current_wp == wp_params.closest_wp && !vtl->current_wp->image ) ) {
08f14055
RN
8897 // Put into 'move buffer'
8898 // NB vvp & vw already set in tet
8899 tet->vtl = (gpointer)vtl;
8900 tet->is_waypoint = TRUE;
8901
8902 marker_begin_move (tet, event->x, event->y);
8903 }
8904
c9570f86
RN
8905 vtl->current_wp = wp_params.closest_wp;
8906 vtl->current_wp_id = wp_params.closest_wp_id;
08f14055 8907
89af1ff6
RN
8908 if ( event->type == GDK_2BUTTON_PRESS ) {
8909 if ( vtl->current_wp->image ) {
8910 menu_array_sublayer values;
8911 values[MA_VTL] = vtl;
8912 values[MA_MISC] = vtl->current_wp->image;
8913 trw_layer_show_picture ( values );
8914 }
8915 }
8916
da121f9b 8917 vik_layer_emit_update ( VIK_LAYER(vtl) );
08f14055 8918
77ad64fa
RN
8919 return TRUE;
8920 }
8921 }
8922
0d2b891f
RN
8923 // Used for both track and route lists
8924 TPSearchParams tp_params;
8925 tp_params.vvp = vvp;
8926 tp_params.x = event->x;
8927 tp_params.y = event->y;
8928 tp_params.closest_track_id = NULL;
8929 tp_params.closest_tp = NULL;
9414408e 8930 tp_params.closest_tpl = NULL;
79773236 8931 tp_params.bbox = bbox;
77ad64fa 8932
0d2b891f 8933 if (vtl->tracks_visible) {
77ad64fa
RN
8934 g_hash_table_foreach ( vtl->tracks, (GHFunc) track_search_closest_tp, &tp_params);
8935
8936 if ( tp_params.closest_tp ) {
08f14055
RN
8937
8938 // Always select + highlight the track
ce4bd1cf 8939 vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, g_hash_table_lookup ( vtl->tracks_iters, tp_params.closest_track_id ), TRUE );
08f14055
RN
8940
8941 tet->is_waypoint = FALSE;
8942
8943 // Select the Trackpoint
8944 // Can move it immediately when control held or it's the previously selected tp
8945 if ( event->state & GDK_CONTROL_MASK ||
8946 vtl->current_tpl == tp_params.closest_tpl ) {
8947 // Put into 'move buffer'
8948 // NB vvp & vw already set in tet
8949 tet->vtl = (gpointer)vtl;
8950 marker_begin_move (tet, event->x, event->y);
8951 }
8952
8953 vtl->current_tpl = tp_params.closest_tpl;
ce4bd1cf
RN
8954 vtl->current_tp_id = tp_params.closest_track_id;
8955 vtl->current_tp_track = g_hash_table_lookup ( vtl->tracks, tp_params.closest_track_id );
08f14055 8956
95d1b757
RN
8957 set_statusbar_msg_info_trkpt ( vtl, tp_params.closest_tp );
8958
08f14055 8959 if ( vtl->tpwin )
ce4bd1cf 8960 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, vtl->current_tp_track->name );
08f14055 8961
da121f9b 8962 vik_layer_emit_update ( VIK_LAYER(vtl) );
77ad64fa
RN
8963 return TRUE;
8964 }
8965 }
8966
0d2b891f
RN
8967 // Try again for routes
8968 if (vtl->routes_visible) {
8969 g_hash_table_foreach ( vtl->routes, (GHFunc) track_search_closest_tp, &tp_params);
8970
8971 if ( tp_params.closest_tp ) {
8972
8973 // Always select + highlight the track
8974 vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, g_hash_table_lookup ( vtl->routes_iters, tp_params.closest_track_id ), TRUE );
8975
8976 tet->is_waypoint = FALSE;
8977
8978 // Select the Trackpoint
8979 // Can move it immediately when control held or it's the previously selected tp
8980 if ( event->state & GDK_CONTROL_MASK ||
8981 vtl->current_tpl == tp_params.closest_tpl ) {
8982 // Put into 'move buffer'
8983 // NB vvp & vw already set in tet
8984 tet->vtl = (gpointer)vtl;
8985 marker_begin_move (tet, event->x, event->y);
8986 }
8987
8988 vtl->current_tpl = tp_params.closest_tpl;
8989 vtl->current_tp_id = tp_params.closest_track_id;
8990 vtl->current_tp_track = g_hash_table_lookup ( vtl->routes, tp_params.closest_track_id );
8991
8992 set_statusbar_msg_info_trkpt ( vtl, tp_params.closest_tp );
8993
8994 if ( vtl->tpwin )
8995 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, vtl->current_tp_track->name );
8996
da121f9b 8997 vik_layer_emit_update ( VIK_LAYER(vtl) );
0d2b891f
RN
8998 return TRUE;
8999 }
9000 }
9001
77ad64fa 9002 /* these aren't the droids you're looking for */
c9570f86
RN
9003 vtl->current_wp = NULL;
9004 vtl->current_wp_id = NULL;
08f14055
RN
9005 trw_layer_cancel_current_tp ( vtl, FALSE );
9006
95d1b757
RN
9007 // Blank info
9008 vik_statusbar_set_message ( vik_window_get_statusbar (VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl))), VIK_STATUSBAR_INFO, "" );
9009
77ad64fa
RN
9010 return FALSE;
9011}
9012
e46f259a
RN
9013static gboolean trw_layer_show_selected_viewport_menu ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
9014{
9015 if ( event->button != 3 )
9016 return FALSE;
9017
9018 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
9019 return FALSE;
9020
0d2b891f 9021 if ( !vtl->tracks_visible && !vtl->waypoints_visible && !vtl->routes_visible )
e46f259a
RN
9022 return FALSE;
9023
9024 /* Post menu for the currently selected item */
9025
9026 /* See if a track is selected */
9027 VikTrack *track = (VikTrack*)vik_window_get_selected_track ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(vtl) );
9028 if ( track && track->visible ) {
9029
60a69560 9030 if ( track->name ) {
e46f259a
RN
9031
9032 if ( vtl->track_right_click_menu )
1a7a0378 9033 g_object_ref_sink ( G_OBJECT(vtl->track_right_click_menu) );
e46f259a
RN
9034
9035 vtl->track_right_click_menu = GTK_MENU ( gtk_menu_new () );
9036
60a69560
RN
9037 trku_udata udataU;
9038 udataU.trk = track;
9039 udataU.uuid = NULL;
9040
0d2b891f
RN
9041 gpointer *trkf;
9042 if ( track->is_route )
9043 trkf = g_hash_table_find ( vtl->routes, (GHRFunc) trw_layer_track_find_uuid, &udataU );
9044 else
9045 trkf = g_hash_table_find ( vtl->tracks, (GHRFunc) trw_layer_track_find_uuid, &udataU );
60a69560
RN
9046
9047 if ( trkf && udataU.uuid ) {
9048
0d2b891f
RN
9049 GtkTreeIter *iter;
9050 if ( track->is_route )
9051 iter = g_hash_table_lookup ( vtl->routes_iters, udataU.uuid );
9052 else
9053 iter = g_hash_table_lookup ( vtl->tracks_iters, udataU.uuid );
60a69560
RN
9054
9055 trw_layer_sublayer_add_menu_items ( vtl,
9056 vtl->track_right_click_menu,
9057 NULL,
0d2b891f 9058 track->is_route ? VIK_TRW_LAYER_SUBLAYER_ROUTE : VIK_TRW_LAYER_SUBLAYER_TRACK,
60a69560
RN
9059 udataU.uuid,
9060 iter,
9061 vvp );
9062 }
e46f259a
RN
9063
9064 gtk_menu_popup ( vtl->track_right_click_menu, NULL, NULL, NULL, NULL, event->button, gtk_get_current_event_time() );
9065
9066 return TRUE;
9067 }
9068 }
9069
9070 /* See if a waypoint is selected */
9071 VikWaypoint *waypoint = (VikWaypoint*)vik_window_get_selected_waypoint ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(vtl) );
9072 if ( waypoint && waypoint->visible ) {
60a69560 9073 if ( waypoint->name ) {
e46f259a
RN
9074
9075 if ( vtl->wp_right_click_menu )
1a7a0378 9076 g_object_ref_sink ( G_OBJECT(vtl->wp_right_click_menu) );
e46f259a
RN
9077
9078 vtl->wp_right_click_menu = GTK_MENU ( gtk_menu_new () );
60a69560
RN
9079
9080 wpu_udata udata;
9081 udata.wp = waypoint;
9082 udata.uuid = NULL;
9083
9084 gpointer *wpf = g_hash_table_find ( vtl->waypoints, (GHRFunc) trw_layer_waypoint_find_uuid, (gpointer) &udata );
9085
9086 if ( wpf && udata.uuid ) {
9087 GtkTreeIter *iter = g_hash_table_lookup ( vtl->waypoints_iters, udata.uuid );
9088
9089 trw_layer_sublayer_add_menu_items ( vtl,
9090 vtl->wp_right_click_menu,
9091 NULL,
9092 VIK_TRW_LAYER_SUBLAYER_WAYPOINT,
9093 udata.uuid,
9094 iter,
9095 vvp );
9096 }
e46f259a
RN
9097 gtk_menu_popup ( vtl->wp_right_click_menu, NULL, NULL, NULL, NULL, event->button, gtk_get_current_event_time() );
9098
9099 return TRUE;
9100 }
9101 }
9102
9103 return FALSE;
9104}
9105
7432fddf
AF
9106/* background drawing hook, to be passed the viewport */
9107static gboolean tool_sync_done = TRUE;
9108
9109static gboolean tool_sync(gpointer data)
9110{
9111 VikViewport *vvp = data;
9112 gdk_threads_enter();
9113 vik_viewport_sync(vvp);
9114 tool_sync_done = TRUE;
9115 gdk_threads_leave();
9116 return FALSE;
9117}
9118
7432fddf
AF
9119static void marker_begin_move ( tool_ed_t *t, gint x, gint y )
9120{
9121 t->holding = TRUE;
9122 t->gc = vik_viewport_new_gc (t->vvp, "black", 2);
9123 gdk_gc_set_function ( t->gc, GDK_INVERT );
9124 vik_viewport_draw_rectangle ( t->vvp, t->gc, FALSE, x-3, y-3, 6, 6 );
9125 vik_viewport_sync(t->vvp);
9126 t->oldx = x;
9127 t->oldy = y;
9128}
9129
9130static void marker_moveto ( tool_ed_t *t, gint x, gint y )
9131{
9132 VikViewport *vvp = t->vvp;
9133 vik_viewport_draw_rectangle ( vvp, t->gc, FALSE, t->oldx-3, t->oldy-3, 6, 6 );
9134 vik_viewport_draw_rectangle ( vvp, t->gc, FALSE, x-3, y-3, 6, 6 );
9135 t->oldx = x;
9136 t->oldy = y;
7b203521 9137
7432fddf
AF
9138 if (tool_sync_done) {
9139 g_idle_add_full (G_PRIORITY_HIGH_IDLE + 10, tool_sync, vvp, NULL);
9140 tool_sync_done = FALSE;
9141 }
9142}
9143
9144static void marker_end_move ( tool_ed_t *t )
9145{
9146 vik_viewport_draw_rectangle ( t->vvp, t->gc, FALSE, t->oldx-3, t->oldy-3, 6, 6 );
9147 g_object_unref ( t->gc );
9148 t->holding = FALSE;
9149}
9150
941aa6e9
AF
9151/*** Edit waypoint ****/
9152
9153static gpointer tool_edit_waypoint_create ( VikWindow *vw, VikViewport *vvp)
9154{
7432fddf
AF
9155 tool_ed_t *t = g_new(tool_ed_t, 1);
9156 t->vvp = vvp;
9157 t->holding = FALSE;
9158 return t;
941aa6e9
AF
9159}
9160
919ed63e
RN
9161static void tool_edit_waypoint_destroy ( tool_ed_t *t )
9162{
9163 g_free ( t );
9164}
9165
7432fddf 9166static gboolean tool_edit_waypoint_click ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data )
50a14534
EB
9167{
9168 WPSearchParams params;
7432fddf
AF
9169 tool_ed_t *t = data;
9170 VikViewport *vvp = t->vvp;
50a14534 9171
941aa6e9
AF
9172 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
9173 return FALSE;
7432fddf
AF
9174
9175 if ( t->holding ) {
9176 return TRUE;
9177 }
9178
87741170
RN
9179 if ( !vtl->vl.visible || !vtl->waypoints_visible )
9180 return FALSE;
9181
50a14534
EB
9182 if ( vtl->current_wp && vtl->current_wp->visible )
9183 {
9184 /* first check if current WP is within area (other may be 'closer', but we want to move the current) */
9185 gint x, y;
9186 vik_viewport_coord_to_screen ( vvp, &(vtl->current_wp->coord), &x, &y );
9187
c75d78d7
AF
9188 if ( abs(x - event->x) <= WAYPOINT_SIZE_APPROX &&
9189 abs(y - event->y) <= WAYPOINT_SIZE_APPROX )
50a14534
EB
9190 {
9191 if ( event->button == 3 )
9192 vtl->waypoint_rightclick = TRUE; /* remember that we're clicking; other layers will ignore release signal */
7432fddf
AF
9193 else {
9194 marker_begin_move(t, event->x, event->y);
9195 }
50a14534
EB
9196 return TRUE;
9197 }
9198 }
9199
9200 params.vvp = vvp;
9201 params.x = event->x;
9202 params.y = event->y;
7d961c6c 9203 params.draw_images = vtl->drawimages;
c9570f86 9204 params.closest_wp_id = NULL;
50a14534
EB
9205 params.closest_wp = NULL;
9206 g_hash_table_foreach ( vtl->waypoints, (GHFunc) waypoint_search_closest_tp, &params);
98356138 9207 if ( vtl->current_wp && (vtl->current_wp == params.closest_wp) )
50a14534 9208 {
a129aa51
RN
9209 if ( event->button == 3 )
9210 vtl->waypoint_rightclick = TRUE; /* remember that we're clicking; other layers will ignore release signal */
9211 else
9212 marker_begin_move(t, event->x, event->y);
d84ade77 9213 return FALSE;
50a14534
EB
9214 }
9215 else if ( params.closest_wp )
9216 {
9217 if ( event->button == 3 )
9218 vtl->waypoint_rightclick = TRUE; /* remember that we're clicking; other layers will ignore release signal */
9219 else
9220 vtl->waypoint_rightclick = FALSE;
9221
c9570f86 9222 vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, g_hash_table_lookup ( vtl->waypoints_iters, params.closest_wp_id ), TRUE );
8623d370 9223
50a14534 9224 vtl->current_wp = params.closest_wp;
c9570f86 9225 vtl->current_wp_id = params.closest_wp_id;
50a14534 9226
50a14534 9227 /* could make it so don't update if old WP is off screen and new is null but oh well */
da121f9b 9228 vik_layer_emit_update ( VIK_LAYER(vtl) );
50a14534
EB
9229 return TRUE;
9230 }
9231
9232 vtl->current_wp = NULL;
c9570f86 9233 vtl->current_wp_id = NULL;
50a14534 9234 vtl->waypoint_rightclick = FALSE;
da121f9b 9235 vik_layer_emit_update ( VIK_LAYER(vtl) );
7432fddf
AF
9236 return FALSE;
9237}
9238
dc2c040e 9239static gboolean tool_edit_waypoint_move ( VikTrwLayer *vtl, GdkEventMotion *event, gpointer data )
7432fddf
AF
9240{
9241 tool_ed_t *t = data;
9242 VikViewport *vvp = t->vvp;
9243
9244 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
9245 return FALSE;
9246
9247 if ( t->holding ) {
9248 VikCoord new_coord;
9249 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
9250
9251 /* snap to TP */
9252 if ( event->state & GDK_CONTROL_MASK )
9253 {
9254 VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
9255 if ( tp )
9256 new_coord = tp->coord;
9257 }
9258
9259 /* snap to WP */
9260 if ( event->state & GDK_SHIFT_MASK )
9261 {
9262 VikWaypoint *wp = closest_wp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
9263 if ( wp && wp != vtl->current_wp )
9264 new_coord = wp->coord;
9265 }
9266
9267 {
9268 gint x, y;
9269 vik_viewport_coord_to_screen ( vvp, &new_coord, &x, &y );
7b203521 9270
7432fddf
AF
9271 marker_moveto ( t, x, y );
9272 }
9273 return TRUE;
9274 }
50a14534
EB
9275 return FALSE;
9276}
9277
7432fddf 9278static gboolean tool_edit_waypoint_release ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data )
941aa6e9 9279{
7432fddf
AF
9280 tool_ed_t *t = data;
9281 VikViewport *vvp = t->vvp;
9282
941aa6e9
AF
9283 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
9284 return FALSE;
7432fddf
AF
9285
9286 if ( t->holding && event->button == 1 )
941aa6e9
AF
9287 {
9288 VikCoord new_coord;
941aa6e9
AF
9289 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
9290
9291 /* snap to TP */
9292 if ( event->state & GDK_CONTROL_MASK )
9293 {
9294 VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
9295 if ( tp )
9296 new_coord = tp->coord;
9297 }
9298
9299 /* snap to WP */
9300 if ( event->state & GDK_SHIFT_MASK )
9301 {
9302 VikWaypoint *wp = closest_wp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
9303 if ( wp && wp != vtl->current_wp )
9304 new_coord = wp->coord;
9305 }
9306
7432fddf
AF
9307 marker_end_move ( t );
9308
941aa6e9 9309 vtl->current_wp->coord = new_coord;
aa0665e5
RN
9310
9311 trw_layer_calculate_bounds_waypoints ( vtl );
da121f9b 9312 vik_layer_emit_update ( VIK_LAYER(vtl) );
941aa6e9
AF
9313 return TRUE;
9314 }
9315 /* PUT IN RIGHT PLACE!!! */
7432fddf 9316 if ( event->button == 3 && vtl->waypoint_rightclick )
941aa6e9
AF
9317 {
9318 if ( vtl->wp_right_click_menu )
4f14a010 9319 g_object_ref_sink ( G_OBJECT(vtl->wp_right_click_menu) );
0b951ebe
RN
9320 if ( vtl->current_wp ) {
9321 vtl->wp_right_click_menu = GTK_MENU ( gtk_menu_new () );
c9570f86 9322 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
9323 gtk_menu_popup ( vtl->wp_right_click_menu, NULL, NULL, NULL, NULL, event->button, gtk_get_current_event_time() );
9324 }
941aa6e9
AF
9325 vtl->waypoint_rightclick = FALSE;
9326 }
9327 return FALSE;
9328}
9329
9330/*** New track ****/
9331
9332static gpointer tool_new_track_create ( VikWindow *vw, VikViewport *vvp)
9333{
9334 return vvp;
9335}
9336
7b203521
EB
9337typedef struct {
9338 VikTrwLayer *vtl;
745fda83
RN
9339 GdkDrawable *drawable;
9340 GdkGC *gc;
9341 GdkPixmap *pixmap;
9342} draw_sync_t;
7b203521 9343
745fda83
RN
9344/*
9345 * Draw specified pixmap
9346 */
9347static gboolean draw_sync ( gpointer data )
7b203521 9348{
745fda83 9349 draw_sync_t *ds = (draw_sync_t*) data;
ef5e8132
RN
9350 // Sometimes don't want to draw
9351 // normally because another update has taken precedent such as panning the display
9352 // which means this pixmap is no longer valid
9353 if ( ds->vtl->draw_sync_do ) {
9354 gdk_threads_enter();
9355 gdk_draw_drawable (ds->drawable,
9356 ds->gc,
9357 ds->pixmap,
9358 0, 0, 0, 0, -1, -1);
9359 ds->vtl->draw_sync_done = TRUE;
9360 gdk_threads_leave();
9361 }
351eb253 9362 g_free ( ds );
7b203521
EB
9363 return FALSE;
9364}
9365
19a0d58f 9366static gchar* distance_string (gdouble distance)
8da84040
RN
9367{
9368 gchar str[128];
9369
9370 /* draw label with distance */
9371 vik_units_distance_t dist_units = a_vik_get_units_distance ();
9372 switch (dist_units) {
9373 case VIK_UNITS_DISTANCE_MILES:
9374 if (distance >= VIK_MILES_TO_METERS(1) && distance < VIK_MILES_TO_METERS(100)) {
9375 g_sprintf(str, "%3.2f miles", VIK_METERS_TO_MILES(distance));
9376 } else if (distance < 1609.4) {
9377 g_sprintf(str, "%d yards", (int)(distance*1.0936133));
9378 } else {
9379 g_sprintf(str, "%d miles", (int)VIK_METERS_TO_MILES(distance));
9380 }
9381 break;
9382 default:
9383 // VIK_UNITS_DISTANCE_KILOMETRES
9384 if (distance >= 1000 && distance < 100000) {
9385 g_sprintf(str, "%3.2f km", distance/1000.0);
9386 } else if (distance < 1000) {
9387 g_sprintf(str, "%d m", (int)distance);
9388 } else {
9389 g_sprintf(str, "%d km", (int)distance/1000);
9390 }
9391 break;
9392 }
9393 return g_strdup (str);
9394}
9395
9396/*
9397 * Actually set the message in statusbar
9398 */
9a3538f5 9399static void statusbar_write (gdouble distance, gdouble elev_gain, gdouble elev_loss, gdouble last_step, gdouble angle, VikTrwLayer *vtl )
8da84040
RN
9400{
9401 // Only show elevation data when track has some elevation properties
9402 gchar str_gain_loss[64];
9403 str_gain_loss[0] = '\0';
9a3538f5
GB
9404 gchar str_last_step[64];
9405 str_last_step[0] = '\0';
9406 gchar *str_total = distance_string (distance);
8da84040
RN
9407
9408 if ( (elev_gain > 0.1) || (elev_loss > 0.1) ) {
9409 if ( a_vik_get_units_height () == VIK_UNITS_HEIGHT_METRES )
9410 g_sprintf(str_gain_loss, _(" - Gain %dm:Loss %dm"), (int)elev_gain, (int)elev_loss);
9411 else
9412 g_sprintf(str_gain_loss, _(" - Gain %dft:Loss %dft"), (int)VIK_METERS_TO_FEET(elev_gain), (int)VIK_METERS_TO_FEET(elev_loss));
9413 }
9a3538f5
GB
9414
9415 if ( last_step > 0 ) {
9416 gchar *tmp = distance_string (last_step);
0da53bd9 9417 g_sprintf(str_last_step, _(" - Bearing %3.1f° - Step %s"), RAD2DEG(angle), tmp);
9a3538f5
GB
9418 g_free ( tmp );
9419 }
9420
9421 VikWindow *vw = VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl));
8da84040
RN
9422
9423 // Write with full gain/loss information
9a3538f5
GB
9424 gchar *msg = g_strdup_printf ( "Total %s%s%s", str_total, str_last_step, str_gain_loss);
9425 vik_statusbar_set_message ( vik_window_get_statusbar (vw), VIK_STATUSBAR_INFO, msg );
8da84040 9426 g_free ( msg );
9a3538f5 9427 g_free ( str_total );
8da84040
RN
9428}
9429
9430/*
9431 * Figure out what information should be set in the statusbar and then write it
9432 */
9433static void update_statusbar ( VikTrwLayer *vtl )
9434{
9435 // Get elevation data
9436 gdouble elev_gain, elev_loss;
9437 vik_track_get_total_elevation_gain ( vtl->current_track, &elev_gain, &elev_loss);
9438
9439 /* Find out actual distance of current track */
9440 gdouble distance = vik_track_get_length (vtl->current_track);
8da84040 9441
9a3538f5 9442 statusbar_write (distance, elev_gain, elev_loss, 0, 0, vtl);
8da84040
RN
9443}
9444
9445
dc2c040e 9446static VikLayerToolFuncStatus tool_new_track_move ( VikTrwLayer *vtl, GdkEventMotion *event, VikViewport *vvp )
7b203521
EB
9447{
9448 /* if we haven't sync'ed yet, we don't have time to do more. */
745fda83 9449 if ( vtl->draw_sync_done && vtl->current_track && vtl->current_track->trackpoints ) {
215ebe59 9450 VikTrackpoint *last_tpt = vik_track_get_tp_last(vtl->current_track);
745fda83
RN
9451
9452 static GdkPixmap *pixmap = NULL;
9453 int w1, h1, w2, h2;
9454 // Need to check in case window has been resized
9455 w1 = vik_viewport_get_width(vvp);
9456 h1 = vik_viewport_get_height(vvp);
9457 if (!pixmap) {
9b082b39 9458 pixmap = gdk_pixmap_new ( gtk_widget_get_window(GTK_WIDGET(vvp)), w1, h1, -1 );
745fda83
RN
9459 }
9460 gdk_drawable_get_size (pixmap, &w2, &h2);
9461 if (w1 != w2 || h1 != h2) {
9462 g_object_unref ( G_OBJECT ( pixmap ) );
9b082b39 9463 pixmap = gdk_pixmap_new ( gtk_widget_get_window(GTK_WIDGET(vvp)), w1, h1, -1 );
745fda83
RN
9464 }
9465
9466 // Reset to background
9467 gdk_draw_drawable (pixmap,
9468 vtl->current_track_newpoint_gc,
9469 vik_viewport_get_pixmap(vvp),
9470 0, 0, 0, 0, -1, -1);
9471
9472 draw_sync_t *passalong;
7b203521
EB
9473 gint x1, y1;
9474
c31b3fbb 9475 vik_viewport_coord_to_screen ( vvp, &(last_tpt->coord), &x1, &y1 );
745fda83
RN
9476
9477 // FOR SCREEN OVERLAYS WE MUST DRAW INTO THIS PIXMAP (when using the reset method)
9478 // otherwise using vik_viewport_draw_* functions puts the data into the base pixmap,
9479 // thus when we come to reset to the background it would include what we have already drawn!!
9480 gdk_draw_line ( pixmap,
9481 vtl->current_track_newpoint_gc,
9482 x1, y1, event->x, event->y );
9483 // Using this reset method is more reliable than trying to undraw previous efforts via the GDK_INVERT method
23ea1329 9484
23ea1329
RN
9485 /* Find out actual distance of current track */
9486 gdouble distance = vik_track_get_length (vtl->current_track);
9487
9488 // Now add distance to where the pointer is //
9489 VikCoord coord;
9490 struct LatLon ll;
9491 vik_viewport_screen_to_coord ( vvp, (gint) event->x, (gint) event->y, &coord );
9492 vik_coord_to_latlon ( &coord, &ll );
9a3538f5
GB
9493 gdouble last_step = vik_coord_diff( &coord, &(last_tpt->coord));
9494 distance = distance + last_step;
23ea1329 9495
8da84040
RN
9496 // Get elevation data
9497 gdouble elev_gain, elev_loss;
9498 vik_track_get_total_elevation_gain ( vtl->current_track, &elev_gain, &elev_loss);
9499
9500 // Adjust elevation data (if available) for the current pointer position
9501 gdouble elev_new;
9502 elev_new = (gdouble) a_dems_get_elev_by_coord ( &coord, VIK_DEM_INTERPOL_BEST );
9503 if ( elev_new != VIK_DEM_INVALID_ELEVATION ) {
c31b3fbb 9504 if ( last_tpt->altitude != VIK_DEFAULT_ALTITUDE ) {
8da84040 9505 // Adjust elevation of last track point
c31b3fbb 9506 if ( elev_new > last_tpt->altitude )
8da84040 9507 // Going up
c31b3fbb 9508 elev_gain += elev_new - last_tpt->altitude;
8da84040
RN
9509 else
9510 // Going down
c31b3fbb 9511 elev_loss += last_tpt->altitude - elev_new;
23ea1329 9512 }
23ea1329 9513 }
6ba8e356 9514
90611609
RN
9515 //
9516 // Display of the distance 'tooltip' during track creation is controlled by a preference
9517 //
9518 if ( a_vik_get_create_track_tooltip() ) {
9519
9520 gchar *str = distance_string (distance);
061ccfdc 9521
90611609
RN
9522 PangoLayout *pl = gtk_widget_create_pango_layout (GTK_WIDGET(vvp), NULL);
9523 pango_layout_set_font_description (pl, gtk_widget_get_style(GTK_WIDGET(vvp))->font_desc);
9524 pango_layout_set_text (pl, str, -1);
9525 gint wd, hd;
9526 pango_layout_get_pixel_size ( pl, &wd, &hd );
6ba8e356 9527
90611609
RN
9528 gint xd,yd;
9529 // offset from cursor a bit depending on font size
9530 xd = event->x + 10;
9531 yd = event->y - hd;
56cb1807 9532
90611609
RN
9533 // Create a background block to make the text easier to read over the background map
9534 GdkGC *background_block_gc = vik_viewport_new_gc ( vvp, "#cccccc", 1);
9535 gdk_draw_rectangle (pixmap, background_block_gc, TRUE, xd-2, yd-2, wd+4, hd+2);
9536 gdk_draw_layout (pixmap, vtl->current_track_newpoint_gc, xd, yd, pl);
745fda83 9537
90611609
RN
9538 g_object_unref ( G_OBJECT ( pl ) );
9539 g_object_unref ( G_OBJECT ( background_block_gc ) );
9540 g_free (str);
9541 }
6ba8e356 9542
745fda83 9543 passalong = g_new(draw_sync_t,1); // freed by draw_sync()
7b203521 9544 passalong->vtl = vtl;
745fda83 9545 passalong->pixmap = pixmap;
9b082b39 9546 passalong->drawable = gtk_widget_get_window(GTK_WIDGET(vvp));
745fda83 9547 passalong->gc = vtl->current_track_newpoint_gc;
8da84040 9548
9a3538f5
GB
9549 gdouble angle;
9550 gdouble baseangle;
9551 vik_viewport_compute_bearing ( vvp, x1, y1, event->x, event->y, &angle, &baseangle );
9552
8da84040 9553 // Update statusbar with full gain/loss information
9a3538f5 9554 statusbar_write (distance, elev_gain, elev_loss, last_step, angle, vtl);
7b203521 9555
745fda83
RN
9556 // draw pixmap when we have time to
9557 g_idle_add_full (G_PRIORITY_HIGH_IDLE + 10, draw_sync, passalong, NULL);
9558 vtl->draw_sync_done = FALSE;
165d30aa 9559 return VIK_LAYER_TOOL_ACK_GRAB_FOCUS;
7b203521 9560 }
165d30aa 9561 return VIK_LAYER_TOOL_ACK;
7b203521
EB
9562}
9563
215ebe59
RN
9564// NB vtl->current_track must be valid
9565static void undo_trackpoint_add ( VikTrwLayer *vtl )
9566{
9567 // 'undo'
9568 if ( vtl->current_track->trackpoints ) {
9569 // TODO rework this...
9570 //vik_trackpoint_free ( vik_track_get_tp_last (vtl->current_track) );
9571 GList *last = g_list_last(vtl->current_track->trackpoints);
9572 g_free ( last->data );
9573 vtl->current_track->trackpoints = g_list_remove_link ( vtl->current_track->trackpoints, last );
9574
9575 vik_track_calculate_bounds ( vtl->current_track );
9576 }
9577}
9578
777e2d4d
EB
9579static gboolean tool_new_track_key_press ( VikTrwLayer *vtl, GdkEventKey *event, VikViewport *vvp )
9580{
9581 if ( vtl->current_track && event->keyval == GDK_Escape ) {
9582 vtl->current_track = NULL;
da121f9b 9583 vik_layer_emit_update ( VIK_LAYER(vtl) );
777e2d4d
EB
9584 return TRUE;
9585 } else if ( vtl->current_track && event->keyval == GDK_BackSpace ) {
215ebe59 9586 undo_trackpoint_add ( vtl );
8da84040 9587 update_statusbar ( vtl );
da121f9b 9588 vik_layer_emit_update ( VIK_LAYER(vtl) );
777e2d4d
EB
9589 return TRUE;
9590 }
9591 return FALSE;
9592}
9593
e37b2a6d
RN
9594/*
9595 * Common function to handle trackpoint button requests on either a route or a track
9596 * . enables adding a point via normal click
9597 * . enables removal of last point via right click
9598 * . finishing of the track or route via double clicking
9599 */
9600static gboolean tool_new_track_or_route_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
50a14534
EB
9601{
9602 VikTrackpoint *tp;
9603
941aa6e9
AF
9604 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
9605 return FALSE;
9606
ef5e8132
RN
9607 if ( event->button == 2 ) {
9608 // As the display is panning, the new track pixmap is now invalid so don't draw it
9609 // otherwise this drawing done results in flickering back to an old image
9610 vtl->draw_sync_do = FALSE;
9611 return FALSE;
9612 }
9613
e37b2a6d 9614 if ( event->button == 3 )
50a14534 9615 {
e37b2a6d
RN
9616 if ( !vtl->current_track )
9617 return FALSE;
215ebe59 9618 undo_trackpoint_add ( vtl );
8da84040 9619 update_statusbar ( vtl );
da121f9b 9620 vik_layer_emit_update ( VIK_LAYER(vtl) );
50a14534
EB
9621 return TRUE;
9622 }
9623
9624 if ( event->type == GDK_2BUTTON_PRESS )
9625 {
9626 /* subtract last (duplicate from double click) tp then end */
9627 if ( vtl->current_track && vtl->current_track->trackpoints && vtl->ct_x1 == vtl->ct_x2 && vtl->ct_y1 == vtl->ct_y2 )
9628 {
50a14534 9629 /* undo last, then end */
215ebe59 9630 undo_trackpoint_add ( vtl );
50a14534
EB
9631 vtl->current_track = NULL;
9632 }
da121f9b 9633 vik_layer_emit_update ( VIK_LAYER(vtl) );
50a14534
EB
9634 return TRUE;
9635 }
9636
50a14534
EB
9637 tp = vik_trackpoint_new();
9638 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &(tp->coord) );
9639
9640 /* snap to other TP */
9641 if ( event->state & GDK_CONTROL_MASK )
9642 {
9643 VikTrackpoint *other_tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
9644 if ( other_tp )
9645 tp->coord = other_tp->coord;
9646 }
9647
9648 tp->newsegment = FALSE;
9649 tp->has_timestamp = FALSE;
9650 tp->timestamp = 0;
e37b2a6d
RN
9651
9652 if ( vtl->current_track ) {
9bc95d58 9653 vik_track_add_trackpoint ( vtl->current_track, tp, TRUE ); // Ensure bounds is updated
e37b2a6d
RN
9654 /* Auto attempt to get elevation from DEM data (if it's available) */
9655 vik_track_apply_dem_data_last_trackpoint ( vtl->current_track );
9656 }
50a14534
EB
9657
9658 vtl->ct_x1 = vtl->ct_x2;
9659 vtl->ct_y1 = vtl->ct_y2;
9660 vtl->ct_x2 = event->x;
9661 vtl->ct_y2 = event->y;
9662
da121f9b 9663 vik_layer_emit_update ( VIK_LAYER(vtl) );
50a14534
EB
9664 return TRUE;
9665}
9666
e37b2a6d
RN
9667static gboolean tool_new_track_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
9668{
ee43b101
MH
9669 // if we were running the route finder, cancel it
9670 vtl->route_finder_started = FALSE;
9671
e37b2a6d
RN
9672 // ----------------------------------------------------- if current is a route - switch to new track
9673 if ( event->button == 1 && ( ! vtl->current_track || (vtl->current_track && vtl->current_track->is_route ) ))
9674 {
9675 gchar *name = trw_layer_new_unique_sublayer_name(vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, _("Track"));
fb40bae0 9676 if ( ( name = a_dialog_new_track ( VIK_GTK_WINDOW_FROM_LAYER(vtl), name, FALSE ) ) )
e37b2a6d 9677 {
2f154c90 9678 new_track_create_common ( vtl, name );
1613e468 9679 g_free ( name );
e37b2a6d
RN
9680 }
9681 else
9682 return TRUE;
9683 }
9684 return tool_new_track_or_route_click ( vtl, event, vvp );
9685}
9686
ef5e8132
RN
9687static void tool_new_track_release ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
9688{
9689 if ( event->button == 2 ) {
9690 // Pan moving ended - enable potential point drawing again
9691 vtl->draw_sync_do = TRUE;
9692 vtl->draw_sync_done = TRUE;
9693 }
9694}
9695
e37b2a6d
RN
9696/*** New route ****/
9697
9698static gpointer tool_new_route_create ( VikWindow *vw, VikViewport *vvp)
9699{
9700 return vvp;
9701}
9702
9703static gboolean tool_new_route_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
9704{
ee43b101
MH
9705 // if we were running the route finder, cancel it
9706 vtl->route_finder_started = FALSE;
9707
9708 // -------------------------- if current is a track - switch to new route,
9709 if ( event->button == 1 && ( ! vtl->current_track ||
9710 (vtl->current_track && !vtl->current_track->is_route ) ) )
e37b2a6d
RN
9711 {
9712 gchar *name = trw_layer_new_unique_sublayer_name(vtl, VIK_TRW_LAYER_SUBLAYER_ROUTE, _("Route"));
fb40bae0 9713 if ( ( name = a_dialog_new_track ( VIK_GTK_WINDOW_FROM_LAYER(vtl), name, TRUE ) ) ) {
b1453c16 9714 new_route_create_common ( vtl, name );
1613e468
RN
9715 g_free ( name );
9716 }
e37b2a6d
RN
9717 else
9718 return TRUE;
9719 }
9720 return tool_new_track_or_route_click ( vtl, event, vvp );
9721}
9722
941aa6e9
AF
9723/*** New waypoint ****/
9724
9725static gpointer tool_new_waypoint_create ( VikWindow *vw, VikViewport *vvp)
9726{
9727 return vvp;
9728}
9729
9730static gboolean tool_new_waypoint_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
9731{
9732 VikCoord coord;
9733 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
9734 return FALSE;
9735 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &coord );
aa0665e5
RN
9736 if (vik_trw_layer_new_waypoint ( vtl, VIK_GTK_WINDOW_FROM_LAYER(vtl), &coord ) && VIK_LAYER(vtl)->visible) {
9737 trw_layer_calculate_bounds_waypoints ( vtl );
da121f9b 9738 vik_layer_emit_update ( VIK_LAYER(vtl) );
aa0665e5 9739 }
941aa6e9
AF
9740 return TRUE;
9741}
9742
9743
9744/*** Edit trackpoint ****/
9745
9746static gpointer tool_edit_trackpoint_create ( VikWindow *vw, VikViewport *vvp)
9747{
7432fddf 9748 tool_ed_t *t = g_new(tool_ed_t, 1);
33534cd8
AF
9749 t->vvp = vvp;
9750 t->holding = FALSE;
9751 return t;
941aa6e9
AF
9752}
9753
919ed63e
RN
9754static void tool_edit_trackpoint_destroy ( tool_ed_t *t )
9755{
9756 g_free ( t );
9757}
9758
33534cd8 9759static gboolean tool_edit_trackpoint_click ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data )
941aa6e9 9760{
7432fddf 9761 tool_ed_t *t = data;
33534cd8
AF
9762 VikViewport *vvp = t->vvp;
9763 TPSearchParams params;
941aa6e9
AF
9764 /* OUTDATED DOCUMENTATION:
9765 find 5 pixel range on each side. then put these UTM, and a pointer
9766 to the winning track name (and maybe the winning track itself), and a
9767 pointer to the winning trackpoint, inside an array or struct. pass
9768 this along, do a foreach on the tracks which will do a foreach on the
9769 trackpoints. */
9770 params.vvp = vvp;
9771 params.x = event->x;
9772 params.y = event->y;
ce4bd1cf 9773 params.closest_track_id = NULL;
941aa6e9
AF
9774 /* TODO: should get track listitem so we can break it up, make a new track, mess it up, all that. */
9775 params.closest_tp = NULL;
9414408e 9776 params.closest_tpl = NULL;
acf5b0d4 9777 vik_viewport_get_min_max_lat_lon ( vvp, &(params.bbox.south), &(params.bbox.north), &(params.bbox.west), &(params.bbox.east) );
941aa6e9 9778
7432fddf
AF
9779 if ( event->button != 1 )
9780 return FALSE;
9781
941aa6e9
AF
9782 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
9783 return FALSE;
9784
0d2b891f 9785 if ( !vtl->vl.visible || !vtl->tracks_visible || !vtl->routes_visible )
87741170
RN
9786 return FALSE;
9787
941aa6e9
AF
9788 if ( vtl->current_tpl )
9789 {
9790 /* 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.) */
9791 VikTrackpoint *tp = VIK_TRACKPOINT(vtl->current_tpl->data);
ce4bd1cf
RN
9792 VikTrack *current_tr = VIK_TRACK(g_hash_table_lookup(vtl->tracks, vtl->current_tp_id));
9793 if ( !current_tr )
9794 return FALSE;
941aa6e9 9795
ce4bd1cf 9796 gint x, y;
941aa6e9
AF
9797 vik_viewport_coord_to_screen ( vvp, &(tp->coord), &x, &y );
9798
9799 if ( current_tr->visible &&
9800 abs(x - event->x) < TRACKPOINT_SIZE_APPROX &&
7432fddf
AF
9801 abs(y - event->y) < TRACKPOINT_SIZE_APPROX ) {
9802 marker_begin_move ( t, event->x, event->y );
941aa6e9
AF
9803 return TRUE;
9804 }
9805
941aa6e9
AF
9806 }
9807
0d2b891f
RN
9808 if ( vtl->tracks_visible )
9809 g_hash_table_foreach ( vtl->tracks, (GHFunc) track_search_closest_tp, &params);
941aa6e9
AF
9810
9811 if ( params.closest_tp )
9812 {
ce4bd1cf 9813 vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, g_hash_table_lookup ( vtl->tracks_iters, params.closest_track_id ), TRUE );
941aa6e9 9814 vtl->current_tpl = params.closest_tpl;
ce4bd1cf 9815 vtl->current_tp_id = params.closest_track_id;
da14cc69 9816 vtl->current_tp_track = g_hash_table_lookup ( vtl->tracks, params.closest_track_id );
941aa6e9 9817 trw_layer_tpwin_init ( vtl );
95d1b757 9818 set_statusbar_msg_info_trkpt ( vtl, params.closest_tp );
da121f9b 9819 vik_layer_emit_update ( VIK_LAYER(vtl) );
941aa6e9
AF
9820 return TRUE;
9821 }
9822
0d2b891f
RN
9823 if ( vtl->routes_visible )
9824 g_hash_table_foreach ( vtl->routes, (GHFunc) track_search_closest_tp, &params);
9825
9826 if ( params.closest_tp )
9827 {
9828 vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, g_hash_table_lookup ( vtl->routes_iters, params.closest_track_id ), TRUE );
9829 vtl->current_tpl = params.closest_tpl;
9830 vtl->current_tp_id = params.closest_track_id;
9831 vtl->current_tp_track = g_hash_table_lookup ( vtl->routes, params.closest_track_id );
9832 trw_layer_tpwin_init ( vtl );
9833 set_statusbar_msg_info_trkpt ( vtl, params.closest_tp );
da121f9b 9834 vik_layer_emit_update ( VIK_LAYER(vtl) );
0d2b891f
RN
9835 return TRUE;
9836 }
9837
941aa6e9
AF
9838 /* these aren't the droids you're looking for */
9839 return FALSE;
9840}
9841
dc2c040e 9842static gboolean tool_edit_trackpoint_move ( VikTrwLayer *vtl, GdkEventMotion *event, gpointer data )
33534cd8 9843{
7432fddf 9844 tool_ed_t *t = data;
33534cd8
AF
9845 VikViewport *vvp = t->vvp;
9846
9847 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
9848 return FALSE;
9849
9850 if ( t->holding )
9851 {
9852 VikCoord new_coord;
33534cd8
AF
9853 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
9854
9855 /* snap to TP */
9856 if ( event->state & GDK_CONTROL_MASK )
9857 {
9858 VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
9859 if ( tp && tp != vtl->current_tpl->data )
9860 new_coord = tp->coord;
9861 }
9862 // VIK_TRACKPOINT(vtl->current_tpl->data)->coord = new_coord;
7432fddf
AF
9863 {
9864 gint x, y;
9865 vik_viewport_coord_to_screen ( vvp, &new_coord, &x, &y );
9866 marker_moveto ( t, x, y );
9867 }
33534cd8
AF
9868
9869 return TRUE;
9870 }
9871 return FALSE;
9872}
9873
33534cd8 9874static gboolean tool_edit_trackpoint_release ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data )
941aa6e9 9875{
7432fddf 9876 tool_ed_t *t = data;
33534cd8
AF
9877 VikViewport *vvp = t->vvp;
9878
941aa6e9
AF
9879 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
9880 return FALSE;
7432fddf
AF
9881 if ( event->button != 1)
9882 return FALSE;
33534cd8 9883
7432fddf 9884 if ( t->holding ) {
941aa6e9 9885 VikCoord new_coord;
941aa6e9
AF
9886 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
9887
9888 /* snap to TP */
9889 if ( event->state & GDK_CONTROL_MASK )
9890 {
9891 VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
9892 if ( tp && tp != vtl->current_tpl->data )
9893 new_coord = tp->coord;
9894 }
9895
9896 VIK_TRACKPOINT(vtl->current_tpl->data)->coord = new_coord;
20981fd6
RN
9897 if ( vtl->current_tp_track )
9898 vik_track_calculate_bounds ( vtl->current_tp_track );
941aa6e9 9899
7432fddf 9900 marker_end_move ( t );
33534cd8 9901
941aa6e9 9902 /* diff dist is diff from orig */
46b6631a 9903 if ( vtl->tpwin )
ce4bd1cf 9904 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, vtl->current_tp_track->name );
941aa6e9 9905
da121f9b 9906 vik_layer_emit_update ( VIK_LAYER(vtl) );
941aa6e9
AF
9907 return TRUE;
9908 }
9909 return FALSE;
9910}
9911
9912
ee43b101
MH
9913/*** Extended Route Finder ***/
9914
9915static gpointer tool_extended_route_finder_create ( VikWindow *vw, VikViewport *vvp)
1eef1bde
QT
9916{
9917 return vvp;
9918}
9919
ee43b101
MH
9920static void tool_extended_route_finder_undo ( VikTrwLayer *vtl )
9921{
9922 VikCoord *new_end;
9923 new_end = vik_track_cut_back_to_double_point ( vtl->current_track );
9924 if ( new_end ) {
9925 g_free ( new_end );
9926 vik_layer_emit_update ( VIK_LAYER(vtl) );
9927
9928 /* remove last ' to:...' */
9929 if ( vtl->current_track->comment ) {
9930 gchar *last_to = strrchr ( vtl->current_track->comment, 't' );
9931 if ( last_to && (last_to - vtl->current_track->comment > 1) ) {
9932 gchar *new_comment = g_strndup ( vtl->current_track->comment,
9933 last_to - vtl->current_track->comment - 1);
9934 vik_track_set_comment_no_copy ( vtl->current_track, new_comment );
9935 }
9936 }
9937 }
9938}
9939
9940
9941static gboolean tool_extended_route_finder_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
1eef1bde
QT
9942{
9943 VikCoord tmp;
0c1044e9 9944 if ( !vtl ) return FALSE;
1eef1bde 9945 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &tmp );
ee43b101
MH
9946 if ( event->button == 3 && vtl->current_track ) {
9947 tool_extended_route_finder_undo ( vtl );
9948 }
9949 else if ( event->button == 2 ) {
9950 vtl->draw_sync_do = FALSE;
9951 return FALSE;
c3deba01 9952 }
ee43b101
MH
9953 // if we started the track but via undo deleted all the track points, begin again
9954 else if ( vtl->current_track && vtl->current_track->is_route && ! vik_track_get_tp_first ( vtl->current_track ) ) {
9955 return tool_new_track_or_route_click ( vtl, event, vvp );
9956 }
9957 else if ( ( vtl->current_track && vtl->current_track->is_route ) ||
9958 ( event->state & GDK_CONTROL_MASK && vtl->current_track ) ) {
1eef1bde 9959 struct LatLon start, end;
bddd2056 9960
ee43b101
MH
9961 VikTrackpoint *tp_start = vik_track_get_tp_last ( vtl->current_track );
9962 vik_coord_to_latlon ( &(tp_start->coord), &start );
1eef1bde 9963 vik_coord_to_latlon ( &(tmp), &end );
bddd2056 9964
ee43b101
MH
9965 vtl->route_finder_started = TRUE;
9966 vtl->route_finder_append = TRUE; // merge tracks. keep started true.
9967
9968 // update UI to let user know what's going on
9969 VikStatusbar *sb = vik_window_get_statusbar (VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)));
9970 VikRoutingEngine *engine = vik_routing_default_engine ( );
9971 if ( ! engine ) {
9972 vik_statusbar_set_message ( sb, VIK_STATUSBAR_INFO, "Cannot plan route without a default routing engine." );
9973 return TRUE;
bddd2056 9974 }
ee43b101
MH
9975 gchar *msg = g_strdup_printf ( _("Querying %s for route between (%.3f, %.3f) and (%.3f, %.3f)."),
9976 vik_routing_engine_get_label ( engine ),
9977 start.lat, start.lon, end.lat, end.lon );
9978 vik_statusbar_set_message ( sb, VIK_STATUSBAR_INFO, msg );
9979 g_free ( msg );
9980 vik_window_set_busy_cursor ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)) );
bddd2056 9981
bddd2056 9982
ee43b101
MH
9983 /* Give GTK a change to display the new status bar before querying the web */
9984 while ( gtk_events_pending ( ) )
9985 gtk_main_iteration ( );
20981fd6 9986
ee43b101 9987 gboolean find_status = vik_routing_default_find ( vtl, start, end );
20981fd6 9988
ee43b101
MH
9989 /* Update UI to say we're done */
9990 vik_window_clear_busy_cursor ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)) );
9991 msg = ( find_status ) ? g_strdup_printf ( _("%s returned route between (%.3f, %.3f) and (%.3f, %.3f)."),
9992 vik_routing_engine_get_label ( engine ),
9993 start.lat, start.lon, end.lat, end.lon )
9994 : g_strdup_printf ( _("Error getting route from %s."),
9995 vik_routing_engine_get_label ( engine ) );
9996 vik_statusbar_set_message ( sb, VIK_STATUSBAR_INFO, msg );
9997 g_free ( msg );
bddd2056 9998
da121f9b 9999 vik_layer_emit_update ( VIK_LAYER(vtl) );
1eef1bde 10000 } else {
ee43b101
MH
10001 vtl->current_track = NULL;
10002
10003 // create a new route where we will add the planned route to
10004 gboolean ret = tool_new_route_click( vtl, event, vvp );
10005
7ff7d728 10006 vtl->route_finder_started = TRUE;
ee43b101
MH
10007
10008 return ret;
1eef1bde 10009 }
1eef1bde
QT
10010 return TRUE;
10011}
10012
ee43b101
MH
10013static gboolean tool_extended_route_finder_key_press ( VikTrwLayer *vtl, GdkEventKey *event, VikViewport *vvp )
10014{
10015 if ( vtl->current_track && event->keyval == GDK_Escape ) {
10016 vtl->route_finder_started = FALSE;
10017 vtl->current_track = NULL;
10018 vik_layer_emit_update ( VIK_LAYER(vtl) );
10019 return TRUE;
10020 } else if ( vtl->current_track && event->keyval == GDK_BackSpace ) {
10021 tool_extended_route_finder_undo ( vtl );
10022 }
10023 return FALSE;
10024}
10025
10026
10027
941aa6e9
AF
10028/*** Show picture ****/
10029
10030static gpointer tool_show_picture_create ( VikWindow *vw, VikViewport *vvp)
10031{
10032 return vvp;
10033}
10034
10035/* Params are: vvp, event, last match found or NULL */
9e212bfc 10036static void tool_show_picture_wp ( const gpointer id, VikWaypoint *wp, gpointer params[3] )
941aa6e9
AF
10037{
10038 if ( wp->image && wp->visible )
10039 {
10040 gint x, y, slackx, slacky;
10041 GdkEventButton *event = (GdkEventButton *) params[1];
10042
10043 vik_viewport_coord_to_screen ( VIK_VIEWPORT(params[0]), &(wp->coord), &x, &y );
10044 slackx = wp->image_width / 2;
10045 slacky = wp->image_height / 2;
10046 if ( x <= event->x + slackx && x >= event->x - slackx
10047 && y <= event->y + slacky && y >= event->y - slacky )
10048 {
10049 params[2] = wp->image; /* we've found a match. however continue searching
10050 * since we want to find the last match -- that
10051 * is, the match that was drawn last. */
10052 }
10053 }
10054}
10055
19782ffd 10056static void trw_layer_show_picture ( menu_array_sublayer values )
a412f3f5
RN
10057{
10058 /* thanks to the Gaim people for showing me ShellExecute and g_spawn_command_line_async */
10059#ifdef WINDOWS
19782ffd 10060 ShellExecute(NULL, "open", (char *) values[MA_MISC], NULL, NULL, SW_SHOWNORMAL);
a412f3f5
RN
10061#else /* WINDOWS */
10062 GError *err = NULL;
19782ffd 10063 gchar *quoted_file = g_shell_quote ( (gchar *) values[MA_MISC] );
3317dc4e 10064 gchar *cmd = g_strdup_printf ( "%s %s", a_vik_get_image_viewer(), quoted_file );
a412f3f5
RN
10065 g_free ( quoted_file );
10066 if ( ! g_spawn_command_line_async ( cmd, &err ) )
10067 {
19782ffd 10068 a_dialog_error_msg_extra ( VIK_GTK_WINDOW_FROM_LAYER(values[MA_VTL]), _("Could not launch %s to open file."), a_vik_get_image_viewer() );
a412f3f5
RN
10069 g_error_free ( err );
10070 }
10071 g_free ( cmd );
10072#endif /* WINDOWS */
10073}
10074
941aa6e9
AF
10075static gboolean tool_show_picture_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
10076{
10077 gpointer params[3] = { vvp, event, NULL };
10078 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
10079 return FALSE;
10080 g_hash_table_foreach ( vtl->waypoints, (GHFunc) tool_show_picture_wp, params );
10081 if ( params[2] )
10082 {
19782ffd
RN
10083 static menu_array_sublayer values;
10084 values[MA_VTL] = vtl;
10085 values[MA_MISC] = params[2];
10086 trw_layer_show_picture ( values );
941aa6e9
AF
10087 return TRUE; /* found a match */
10088 }
10089 else
10090 return FALSE; /* go through other layers, searching for a match */
10091}
10092
10093/***************************************************************************
10094 ** End tool code
10095 ***************************************************************************/
10096
10097
9e212bfc 10098static void image_wp_make_list ( const gpointer id, VikWaypoint *wp, GSList **pics )
50a14534
EB
10099{
10100 if ( wp->image && ( ! a_thumbnails_exists ( wp->image ) ) )
10101 *pics = g_slist_append ( *pics, (gpointer) g_strdup ( wp->image ) );
10102}
10103
91822ddd
RN
10104/* Structure for thumbnail creating data used in the background thread */
10105typedef struct {
10106 VikTrwLayer *vtl; // Layer needed for redrawing
10107 GSList *pics; // Image list
10108} thumbnail_create_thread_data;
10109
10110static int create_thumbnails_thread ( thumbnail_create_thread_data *tctd, gpointer threaddata )
50a14534 10111{
91822ddd
RN
10112 guint total = g_slist_length(tctd->pics), done = 0;
10113 while ( tctd->pics )
50a14534 10114 {
91822ddd 10115 a_thumbnails_create ( (gchar *) tctd->pics->data );
54861848
GB
10116 int result = a_background_thread_progress ( threaddata, ((gdouble) ++done) / total );
10117 if ( result != 0 )
10118 return -1; /* Abort thread */
10119
91822ddd 10120 tctd->pics = tctd->pics->next;
50a14534 10121 }
91822ddd
RN
10122
10123 // Redraw to show the thumbnails as they are now created
91822ddd 10124 if ( IS_VIK_LAYER(tctd->vtl) )
da121f9b 10125 vik_layer_emit_update ( VIK_LAYER(tctd->vtl) ); // NB update from background thread
91822ddd 10126
17c8aefa 10127 return 0;
50a14534
EB
10128}
10129
91822ddd 10130static void thumbnail_create_thread_free ( thumbnail_create_thread_data *tctd )
50a14534 10131{
91822ddd 10132 while ( tctd->pics )
50a14534 10133 {
91822ddd
RN
10134 g_free ( tctd->pics->data );
10135 tctd->pics = g_slist_delete_link ( tctd->pics, tctd->pics );
50a14534 10136 }
91822ddd 10137 g_free ( tctd );
50a14534
EB
10138}
10139
b3eb3b98 10140void trw_layer_verify_thumbnails ( VikTrwLayer *vtl, GtkWidget *vp )
50a14534
EB
10141{
10142 if ( ! vtl->has_verified_thumbnails )
10143 {
10144 GSList *pics = NULL;
10145 g_hash_table_foreach ( vtl->waypoints, (GHFunc) image_wp_make_list, &pics );
10146 if ( pics )
10147 {
10148 gint len = g_slist_length ( pics );
4c77d5e0 10149 gchar *tmp = g_strdup_printf ( _("Creating %d Image Thumbnails..."), len );
91822ddd
RN
10150 thumbnail_create_thread_data *tctd = g_malloc ( sizeof(thumbnail_create_thread_data) );
10151 tctd->vtl = vtl;
10152 tctd->pics = pics;
10153 a_background_thread ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
10154 tmp,
10155 (vik_thr_func) create_thumbnails_thread,
10156 tctd,
10157 (vik_thr_free_func) thumbnail_create_thread_free,
10158 NULL,
10159 len );
50a14534
EB
10160 g_free ( tmp );
10161 }
10162 }
10163}
10164
b1453c16
RN
10165static const gchar* my_track_colors ( gint ii )
10166{
10167 static const gchar* colors[VIK_TRW_LAYER_TRACK_GCS] = {
10168 "#2d870a",
ca738b73 10169 "#135D34",
b1453c16
RN
10170 "#0a8783",
10171 "#0e4d87",
10172 "#05469f",
ca738b73 10173 "#695CBB",
b1453c16
RN
10174 "#2d059f",
10175 "#4a059f",
ca738b73 10176 "#5A171A",
b1453c16
RN
10177 "#96059f"
10178 };
10179 // Fast and reliable way of returning a colour
10180 return colors[(ii % VIK_TRW_LAYER_TRACK_GCS)];
10181}
10182
10183static void trw_layer_track_alloc_colors ( VikTrwLayer *vtl )
10184{
10185 GHashTableIter iter;
10186 gpointer key, value;
10187
10188 gint ii = 0;
10189 // Tracks
10190 g_hash_table_iter_init ( &iter, vtl->tracks );
10191
10192 while ( g_hash_table_iter_next (&iter, &key, &value) ) {
10193
10194 // Tracks get a random spread of colours if not already assigned
10195 if ( ! VIK_TRACK(value)->has_color ) {
e2bc000f
RN
10196 if ( vtl->drawmode == DRAWMODE_ALL_SAME_COLOR )
10197 VIK_TRACK(value)->color = vtl->track_color;
10198 else {
10199 gdk_color_parse ( my_track_colors (ii), &(VIK_TRACK(value)->color) );
10200 }
b1453c16
RN
10201 VIK_TRACK(value)->has_color = TRUE;
10202 }
10203
93ee73b3 10204 trw_layer_update_treeview ( vtl, VIK_TRACK(value) );
b1453c16
RN
10205
10206 ii++;
10207 if (ii > VIK_TRW_LAYER_TRACK_GCS)
10208 ii = 0;
10209 }
10210
10211 // Routes
10212 ii = 0;
10213 g_hash_table_iter_init ( &iter, vtl->routes );
10214
10215 while ( g_hash_table_iter_next (&iter, &key, &value) ) {
10216
10217 // Routes get an intermix of reds
10218 if ( ! VIK_TRACK(value)->has_color ) {
10219 if ( ii )
10220 gdk_color_parse ( "#FF0000" , &(VIK_TRACK(value)->color) ); // Red
10221 else
10222 gdk_color_parse ( "#B40916" , &(VIK_TRACK(value)->color) ); // Dark Red
10223 VIK_TRACK(value)->has_color = TRUE;
10224 }
10225
93ee73b3 10226 trw_layer_update_treeview ( vtl, VIK_TRACK(value) );
b1453c16
RN
10227
10228 ii = !ii;
10229 }
10230}
10231
aa0665e5
RN
10232/*
10233 * (Re)Calculate the bounds of the waypoints in this layer,
10234 * This should be called whenever waypoints are changed
10235 */
2cec1c4e 10236void trw_layer_calculate_bounds_waypoints ( VikTrwLayer *vtl )
aa0665e5
RN
10237{
10238 struct LatLon topleft = { 0.0, 0.0 };
10239 struct LatLon bottomright = { 0.0, 0.0 };
10240 struct LatLon ll;
10241
10242 GHashTableIter iter;
10243 gpointer key, value;
10244
10245 g_hash_table_iter_init ( &iter, vtl->waypoints );
10246
10247 // Set bounds to first point
10248 if ( g_hash_table_iter_next (&iter, &key, &value) ) {
10249 vik_coord_to_latlon ( &(VIK_WAYPOINT(value)->coord), &topleft );
10250 vik_coord_to_latlon ( &(VIK_WAYPOINT(value)->coord), &bottomright );
10251 }
10252
10253 // Ensure there is another point...
10254 if ( g_hash_table_size ( vtl->waypoints ) > 1 ) {
10255
10256 while ( g_hash_table_iter_next (&iter, &key, &value) ) {
10257
10258 // See if this point increases the bounds.
10259 vik_coord_to_latlon ( &(VIK_WAYPOINT(value)->coord), &ll );
10260
10261 if ( ll.lat > topleft.lat) topleft.lat = ll.lat;
10262 if ( ll.lon < topleft.lon) topleft.lon = ll.lon;
10263 if ( ll.lat < bottomright.lat) bottomright.lat = ll.lat;
10264 if ( ll.lon > bottomright.lon) bottomright.lon = ll.lon;
10265 }
10266 }
10267
10268 vtl->waypoints_bbox.north = topleft.lat;
10269 vtl->waypoints_bbox.east = bottomright.lon;
10270 vtl->waypoints_bbox.south = bottomright.lat;
10271 vtl->waypoints_bbox.west = topleft.lon;
10272}
10273
20981fd6 10274static void trw_layer_calculate_bounds_track ( gpointer id, VikTrack *trk )
b1453c16 10275{
20981fd6
RN
10276 vik_track_calculate_bounds ( trk );
10277}
10278
10279static void trw_layer_calculate_bounds_tracks ( VikTrwLayer *vtl )
10280{
10281 g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_calculate_bounds_track, NULL );
10282 g_hash_table_foreach ( vtl->routes, (GHFunc) trw_layer_calculate_bounds_track, NULL );
10283}
10284
c9cac058
RN
10285static void trw_layer_sort_all ( VikTrwLayer *vtl )
10286{
7a52aac6
RN
10287 if ( ! VIK_LAYER(vtl)->vt )
10288 return;
10289
c9cac058
RN
10290 // Obviously need 2 to tango - sorting with only 1 (or less) is a lonely activity!
10291 if ( g_hash_table_size (vtl->tracks) > 1 )
10292 vik_treeview_sort_children ( VIK_LAYER(vtl)->vt, &(vtl->tracks_iter), vtl->track_sort_order );
10293
10294 if ( g_hash_table_size (vtl->routes) > 1 )
10295 vik_treeview_sort_children ( VIK_LAYER(vtl)->vt, &(vtl->routes_iter), vtl->track_sort_order );
10296
10297 if ( g_hash_table_size (vtl->waypoints) > 1 )
10298 vik_treeview_sort_children ( VIK_LAYER(vtl)->vt, &(vtl->waypoints_iter), vtl->wp_sort_order );
10299}
10300
20981fd6
RN
10301static void trw_layer_post_read ( VikTrwLayer *vtl, GtkWidget *vvp, gboolean from_file )
10302{
626de648
RN
10303 if ( VIK_LAYER(vtl)->realized )
10304 trw_layer_verify_thumbnails ( vtl, vvp );
b1453c16 10305 trw_layer_track_alloc_colors ( vtl );
aa0665e5
RN
10306
10307 trw_layer_calculate_bounds_waypoints ( vtl );
20981fd6 10308 trw_layer_calculate_bounds_tracks ( vtl );
c9cac058
RN
10309
10310 // Apply treeview sort after loading all the tracks for this layer
10311 // (rather than sorted insert on each individual track additional)
10312 // and after subsequent changes to the properties as the specified order may have changed.
10313 // since the sorting of a treeview section is now very quick
10314 // NB sorting is also performed after every name change as well to maintain the list order
10315 trw_layer_sort_all ( vtl );
6ba42f1e
RN
10316
10317 // Setting metadata time if not otherwise set
10318 if ( vtl->metadata ) {
10319
10320 gboolean need_to_set_time = TRUE;
10321 if ( vtl->metadata->timestamp ) {
10322 need_to_set_time = FALSE;
10323 if ( !g_strcmp0(vtl->metadata->timestamp, "" ) )
10324 need_to_set_time = TRUE;
10325 }
10326
10327 if ( need_to_set_time ) {
10328 // Could rewrite this as a general get first time of a TRW Layer function
10329 GTimeVal timestamp;
10330 timestamp.tv_usec = 0;
10331 gboolean has_timestamp = FALSE;
10332
10333 GList *gl = NULL;
10334 gl = g_hash_table_get_values ( vtl->tracks );
10335 gl = g_list_sort ( gl, vik_track_compare_timestamp );
10336 gl = g_list_first ( gl );
10337
10338 // Check times of tracks
10339 if ( gl ) {
10340 // Only need to check the first track as they have been sorted by time
10341 VikTrack *trk = (VikTrack*)gl->data;
10342 // Assume trackpoints already sorted by time
10343 VikTrackpoint *tpt = vik_track_get_tp_first(trk);
10344 if ( tpt && tpt->has_timestamp ) {
10345 timestamp.tv_sec = tpt->timestamp;
10346 has_timestamp = TRUE;
10347 }
10348 g_list_free ( gl );
10349 }
10350
10351 if ( !has_timestamp ) {
10352 // 'Last' resort - current time
10353 // Get before waypoint tests - so that if a waypoint time value (in the past) is found it should be used
10354 g_get_current_time ( &timestamp );
10355
10356 // Check times of waypoints
10357 gl = g_hash_table_get_values ( vtl->waypoints );
10358 GList *iter;
10359 for (iter = g_list_first (gl); iter != NULL; iter = g_list_next (iter)) {
10360 VikWaypoint *wpt = (VikWaypoint*)iter->data;
10361 if ( wpt->has_timestamp ) {
10362 if ( timestamp.tv_sec > wpt->timestamp ) {
10363 timestamp.tv_sec = wpt->timestamp;
10364 has_timestamp = TRUE;
10365 }
10366 }
10367 }
10368 g_list_free ( gl );
10369 }
10370
10371 vtl->metadata->timestamp = g_time_val_to_iso8601 ( &timestamp );
10372 }
10373 }
b1453c16
RN
10374}
10375
50a14534
EB
10376VikCoordMode vik_trw_layer_get_coord_mode ( VikTrwLayer *vtl )
10377{
10378 return vtl->coord_mode;
10379}
10380
073ede8c
RN
10381/**
10382 * Uniquify the whole layer
10383 * Also requires the layers panel as the names shown there need updating too
10384 * Returns whether the operation was successful or not
10385 */
10386gboolean vik_trw_layer_uniquify ( VikTrwLayer *vtl, VikLayersPanel *vlp )
10387{
10388 if ( vtl && vlp ) {
0d2b891f
RN
10389 vik_trw_layer_uniquify_tracks ( vtl, vlp, vtl->tracks, TRUE );
10390 vik_trw_layer_uniquify_tracks ( vtl, vlp, vtl->routes, FALSE );
073ede8c
RN
10391 vik_trw_layer_uniquify_waypoints ( vtl, vlp );
10392 return TRUE;
10393 }
10394 return FALSE;
10395}
50a14534 10396
c9570f86 10397static void waypoint_convert ( const gpointer id, VikWaypoint *wp, VikCoordMode *dest_mode )
50a14534
EB
10398{
10399 vik_coord_convert ( &(wp->coord), *dest_mode );
10400}
10401
9e212bfc 10402static void track_convert ( const gpointer id, VikTrack *tr, VikCoordMode *dest_mode )
50a14534
EB
10403{
10404 vik_track_convert ( tr, *dest_mode );
10405}
10406
10407static void trw_layer_change_coord_mode ( VikTrwLayer *vtl, VikCoordMode dest_mode )
10408{
10409 if ( vtl->coord_mode != dest_mode )
10410 {
10411 vtl->coord_mode = dest_mode;
10412 g_hash_table_foreach ( vtl->waypoints, (GHFunc) waypoint_convert, &dest_mode );
10413 g_hash_table_foreach ( vtl->tracks, (GHFunc) track_convert, &dest_mode );
6440ca9b 10414 g_hash_table_foreach ( vtl->routes, (GHFunc) track_convert, &dest_mode );
50a14534
EB
10415 }
10416}
e4afc73a 10417
a7cd93ac 10418static void trw_layer_set_menu_selection ( VikTrwLayer *vtl, guint16 selection )
20c7a3a0
QT
10419{
10420 vtl->menu_selection = selection;
10421}
10422
a7cd93ac 10423static guint16 trw_layer_get_menu_selection ( VikTrwLayer *vtl )
20c7a3a0 10424{
a7cd93ac 10425 return (vtl->menu_selection);
20c7a3a0
QT
10426}
10427
7114e879
QT
10428/* ----------- Downloading maps along tracks --------------- */
10429
10430static int get_download_area_width(VikViewport *vvp, gdouble zoom_level, struct LatLon *wh)
10431{
10432 /* TODO: calculating based on current size of viewport */
10433 const gdouble w_at_zoom_0_125 = 0.0013;
10434 const gdouble h_at_zoom_0_125 = 0.0011;
10435 gdouble zoom_factor = zoom_level/0.125;
10436
10437 wh->lat = h_at_zoom_0_125 * zoom_factor;
10438 wh->lon = w_at_zoom_0_125 * zoom_factor;
10439
10440 return 0; /* all OK */
10441}
10442
35e22ed8
QT
10443static VikCoord *get_next_coord(VikCoord *from, VikCoord *to, struct LatLon *dist, gdouble gradient)
10444{
10445 if ((dist->lon >= ABS(to->east_west - from->east_west)) &&
10446 (dist->lat >= ABS(to->north_south - from->north_south)))
10447 return NULL;
10448
10449 VikCoord *coord = g_malloc(sizeof(VikCoord));
10450 coord->mode = VIK_COORD_LATLON;
10451
10452 if (ABS(gradient) < 1) {
10453 if (from->east_west > to->east_west)
10454 coord->east_west = from->east_west - dist->lon;
10455 else
10456 coord->east_west = from->east_west + dist->lon;
10457 coord->north_south = gradient * (coord->east_west - from->east_west) + from->north_south;
10458 } else {
10459 if (from->north_south > to->north_south)
10460 coord->north_south = from->north_south - dist->lat;
10461 else
10462 coord->north_south = from->north_south + dist->lat;
10463 coord->east_west = (1/gradient) * (coord->north_south - from->north_south) + from->north_south;
10464 }
10465
10466 return coord;
10467}
10468
10469static GList *add_fillins(GList *list, VikCoord *from, VikCoord *to, struct LatLon *dist)
10470{
10471 /* TODO: handle virtical track (to->east_west - from->east_west == 0) */
10472 gdouble gradient = (to->north_south - from->north_south)/(to->east_west - from->east_west);
10473
10474 VikCoord *next = from;
10475 while (TRUE) {
10476 if ((next = get_next_coord(next, to, dist, gradient)) == NULL)
10477 break;
10478 list = g_list_prepend(list, next);
10479 }
10480
10481 return list;
10482}
10483
7114e879
QT
10484void vik_track_download_map(VikTrack *tr, VikMapsLayer *vml, VikViewport *vvp, gdouble zoom_level)
10485{
10486 typedef struct _Rect {
10487 VikCoord tl;
10488 VikCoord br;
35e22ed8 10489 VikCoord center;
7114e879 10490 } Rect;
35e22ed8 10491#define GLRECT(iter) ((Rect *)((iter)->data))
7114e879
QT
10492
10493 struct LatLon wh;
35e22ed8 10494 GList *rects_to_download = NULL;
7114e879
QT
10495 GList *rect_iter;
10496
10497 if (get_download_area_width(vvp, zoom_level, &wh))
10498 return;
10499
10500 GList *iter = tr->trackpoints;
35e22ed8
QT
10501 if (!iter)
10502 return;
7114e879
QT
10503
10504 gboolean new_map = TRUE;
10505 VikCoord *cur_coord, tl, br;
10506 Rect *rect;
7114e879
QT
10507 while (iter) {
10508 cur_coord = &(VIK_TRACKPOINT(iter->data))->coord;
10509 if (new_map) {
10510 vik_coord_set_area(cur_coord, &wh, &tl, &br);
10511 rect = g_malloc(sizeof(Rect));
10512 rect->tl = tl;
10513 rect->br = br;
35e22ed8
QT
10514 rect->center = *cur_coord;
10515 rects_to_download = g_list_prepend(rects_to_download, rect);
7114e879
QT
10516 new_map = FALSE;
10517 iter = iter->next;
10518 continue;
10519 }
10520 gboolean found = FALSE;
35e22ed8
QT
10521 for (rect_iter = rects_to_download; rect_iter; rect_iter = rect_iter->next) {
10522 if (vik_coord_inside(cur_coord, &GLRECT(rect_iter)->tl, &GLRECT(rect_iter)->br)) {
7114e879
QT
10523 found = TRUE;
10524 break;
10525 }
10526 }
10527 if (found)
10528 iter = iter->next;
10529 else
10530 new_map = TRUE;
10531 }
35e22ed8 10532
35e22ed8 10533 GList *fillins = NULL;
b1e57d16
RN
10534 /* 'fillin' doesn't work in UTM mode - potentially ending up in massive loop continually allocating memory - hence don't do it */
10535 /* seems that ATM the function get_next_coord works only for LATLON */
10536 if ( cur_coord->mode == VIK_COORD_LATLON ) {
10537 /* fill-ins for far apart points */
10538 GList *cur_rect, *next_rect;
10539 for (cur_rect = rects_to_download;
10540 (next_rect = cur_rect->next) != NULL;
10541 cur_rect = cur_rect->next) {
10542 if ((wh.lon < ABS(GLRECT(cur_rect)->center.east_west - GLRECT(next_rect)->center.east_west)) ||
10543 (wh.lat < ABS(GLRECT(cur_rect)->center.north_south - GLRECT(next_rect)->center.north_south))) {
10544 fillins = add_fillins(fillins, &GLRECT(cur_rect)->center, &GLRECT(next_rect)->center, &wh);
10545 }
35e22ed8 10546 }
3cbbb49e
GB
10547 } else
10548 g_message("%s: this feature works only in Mercator mode", __FUNCTION__);
35e22ed8
QT
10549
10550 if (fillins) {
c60f82df
RN
10551 GList *fiter = fillins;
10552 while (fiter) {
10553 cur_coord = (VikCoord *)(fiter->data);
35e22ed8
QT
10554 vik_coord_set_area(cur_coord, &wh, &tl, &br);
10555 rect = g_malloc(sizeof(Rect));
10556 rect->tl = tl;
10557 rect->br = br;
10558 rect->center = *cur_coord;
10559 rects_to_download = g_list_prepend(rects_to_download, rect);
c60f82df 10560 fiter = fiter->next;
35e22ed8
QT
10561 }
10562 }
10563
10564 for (rect_iter = rects_to_download; rect_iter; rect_iter = rect_iter->next) {
3ac548fa 10565 vik_maps_layer_download_section (vml, vvp, &(((Rect *)(rect_iter->data))->tl), &(((Rect *)(rect_iter->data))->br), zoom_level);
7114e879
QT
10566 }
10567
35e22ed8
QT
10568 if (fillins) {
10569 for (iter = fillins; iter; iter = iter->next)
10570 g_free(iter->data);
10571 g_list_free(fillins);
10572 }
10573 if (rects_to_download) {
10574 for (rect_iter = rects_to_download; rect_iter; rect_iter = rect_iter->next)
10575 g_free(rect_iter->data);
10576 g_list_free(rects_to_download);
10577 }
7114e879
QT
10578}
10579
19782ffd 10580static void trw_layer_download_map_along_track_cb ( menu_array_sublayer values )
7114e879
QT
10581{
10582 VikMapsLayer *vml;
fe7e4e45 10583 gint selected_map;
7114e879
QT
10584 gchar *zoomlist[] = {"0.125", "0.25", "0.5", "1", "2", "4", "8", "16", "32", "64", "128", "256", "512", "1024", NULL };
10585 gdouble zoom_vals[] = {0.125, 0.25, 0.5, 1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024};
10586 gint selected_zoom, default_zoom;
7114e879 10587
19782ffd
RN
10588 VikTrwLayer *vtl = values[MA_VTL];
10589 VikLayersPanel *vlp = values[MA_VLP];
0d2b891f 10590 VikTrack *trk;
19782ffd
RN
10591 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
10592 trk = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
0d2b891f 10593 else
19782ffd 10594 trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
0d2b891f
RN
10595 if ( !trk )
10596 return;
10597
7114e879
QT
10598 VikViewport *vvp = vik_window_viewport((VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl)));
10599
aa7ed888 10600 GList *vmls = vik_layers_panel_get_all_layers_of_type(vlp, VIK_LAYER_MAPS, TRUE); // Includes hidden map layer types
7114e879
QT
10601 int num_maps = g_list_length(vmls);
10602
10603 if (!num_maps) {
4d333042 10604 a_dialog_error_msg(VIK_GTK_WINDOW_FROM_LAYER(vtl), _("No map layer in use. Create one first") );
7114e879
QT
10605 return;
10606 }
10607
fe7e4e45 10608 // Convert from list of vmls to list of names. Allowing the user to select one of them
7114e879
QT
10609 gchar **map_names = g_malloc(1 + num_maps * sizeof(gpointer));
10610 VikMapsLayer **map_layers = g_malloc(1 + num_maps * sizeof(gpointer));
10611
10612 gchar **np = map_names;
10613 VikMapsLayer **lp = map_layers;
fe7e4e45 10614 int i;
7114e879 10615 for (i = 0; i < num_maps; i++) {
7114e879 10616 vml = (VikMapsLayer *)(vmls->data);
fe7e4e45
RN
10617 *lp++ = vml;
10618 *np++ = vik_maps_layer_get_map_label(vml);
7114e879
QT
10619 vmls = vmls->next;
10620 }
fe7e4e45 10621 // Mark end of the array lists
7114e879
QT
10622 *lp = NULL;
10623 *np = NULL;
7114e879
QT
10624
10625 gdouble cur_zoom = vik_viewport_get_zoom(vvp);
fe7e4e45 10626 for (default_zoom = 0; default_zoom < G_N_ELEMENTS(zoom_vals); default_zoom++) {
7114e879
QT
10627 if (cur_zoom == zoom_vals[default_zoom])
10628 break;
10629 }
fe7e4e45 10630 default_zoom = (default_zoom == G_N_ELEMENTS(zoom_vals)) ? G_N_ELEMENTS(zoom_vals) - 1 : default_zoom;
7114e879 10631
fe7e4e45 10632 if (!a_dialog_map_n_zoom(VIK_GTK_WINDOW_FROM_LAYER(vtl), map_names, 0, zoomlist, default_zoom, &selected_map, &selected_zoom))
7114e879
QT
10633 goto done;
10634
0d2b891f 10635 vik_track_download_map(trk, map_layers[selected_map], vvp, zoom_vals[selected_zoom]);
7114e879
QT
10636
10637done:
10638 for (i = 0; i < num_maps; i++)
10639 g_free(map_names[i]);
10640 g_free(map_names);
10641 g_free(map_layers);
10642
10643 g_list_free(vmls);
10644
10645}
0c1044e9 10646
a8fe53f8
EB
10647/**** lowest waypoint number calculation ***/
10648static gint highest_wp_number_name_to_number(const gchar *name) {
10649 if ( strlen(name) == 3 ) {
10650 int n = atoi(name);
10651 if ( n < 100 && name[0] != '0' )
10652 return -1;
10653 if ( n < 10 && name[0] != '0' )
10654 return -1;
10655 return n;
10656 }
10657 return -1;
10658}
10659
10660
10661static void highest_wp_number_reset(VikTrwLayer *vtl)
10662{
10663 vtl->highest_wp_number = -1;
10664}
10665
10666static void highest_wp_number_add_wp(VikTrwLayer *vtl, const gchar *new_wp_name)
10667{
10668 /* if is bigger that top, add it */
10669 gint new_wp_num = highest_wp_number_name_to_number(new_wp_name);
10670 if ( new_wp_num > vtl->highest_wp_number )
10671 vtl->highest_wp_number = new_wp_num;
10672}
10673
10674static void highest_wp_number_remove_wp(VikTrwLayer *vtl, const gchar *old_wp_name)
10675{
10676 /* if wasn't top, do nothing. if was top, count backwards until we find one used */
10677 gint old_wp_num = highest_wp_number_name_to_number(old_wp_name);
10678 if ( vtl->highest_wp_number == old_wp_num ) {
10679 gchar buf[4];
c9570f86 10680 vtl->highest_wp_number--;
a8fe53f8
EB
10681
10682 g_snprintf(buf,4,"%03d", vtl->highest_wp_number );
10683 /* search down until we find something that *does* exist */
10684
c9570f86
RN
10685 while ( vtl->highest_wp_number > 0 && ! vik_trw_layer_get_waypoint ( vtl, buf )) {
10686 vtl->highest_wp_number--;
a8fe53f8
EB
10687 g_snprintf(buf,4,"%03d", vtl->highest_wp_number );
10688 }
10689 }
10690}
10691
10692/* get lowest unused number */
10693static gchar *highest_wp_number_get(VikTrwLayer *vtl)
10694{
10695 gchar buf[4];
10696 if ( vtl->highest_wp_number < 0 || vtl->highest_wp_number >= 999 )
3ce17b61 10697 return NULL;
a8fe53f8
EB
10698 g_snprintf(buf,4,"%03d", vtl->highest_wp_number+1 );
10699 return g_strdup(buf);
10700}
07c9d42b
RN
10701
10702/**
10703 * trw_layer_create_track_list_both:
10704 *
10705 * Create the latest list of tracks and routes
10706 */
10707static GList* trw_layer_create_track_list_both ( VikLayer *vl, gpointer user_data )
10708{
10709 VikTrwLayer *vtl = VIK_TRW_LAYER(vl);
10710 GList *tracks = NULL;
10711 tracks = g_list_concat ( tracks, g_hash_table_get_values ( vik_trw_layer_get_tracks ( vtl ) ) );
10712 tracks = g_list_concat ( tracks, g_hash_table_get_values ( vik_trw_layer_get_routes ( vtl ) ) );
10713
10714 return vik_trw_layer_build_track_list_t ( vtl, tracks );
10715}
10716
19782ffd 10717static void trw_layer_track_list_dialog_single ( menu_array_sublayer values )
07c9d42b 10718{
19782ffd 10719 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
07c9d42b
RN
10720
10721 gchar *title = NULL;
19782ffd 10722 if ( GPOINTER_TO_INT(values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_TRACKS )
07c9d42b
RN
10723 title = g_strdup_printf ( _("%s: Track List"), VIK_LAYER(vtl)->name );
10724 else
10725 title = g_strdup_printf ( _("%s: Route List"), VIK_LAYER(vtl)->name );
10726
19782ffd 10727 vik_trw_layer_track_list_show_dialog ( title, VIK_LAYER(vtl), values[MA_SUBTYPE], trw_layer_create_track_list, FALSE );
07c9d42b
RN
10728 g_free ( title );
10729}
10730
1c2083ba 10731static void trw_layer_track_list_dialog ( menu_array_layer values )
07c9d42b 10732{
1c2083ba 10733 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
07c9d42b
RN
10734
10735 gchar *title = g_strdup_printf ( _("%s: Track and Route List"), VIK_LAYER(vtl)->name );
10736 vik_trw_layer_track_list_show_dialog ( title, VIK_LAYER(vtl), NULL, trw_layer_create_track_list_both, FALSE );
10737 g_free ( title );
10738}
00176e85 10739
1c2083ba 10740static void trw_layer_waypoint_list_dialog ( menu_array_layer values )
00176e85 10741{
1c2083ba 10742 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
00176e85
RN
10743
10744 gchar *title = g_strdup_printf ( _("%s: Waypoint List"), VIK_LAYER(vtl)->name );
10745 vik_trw_layer_waypoint_list_show_dialog ( title, VIK_LAYER(vtl), NULL, trw_layer_create_waypoint_list, FALSE );
10746 g_free ( title );
10747}