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