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