]> git.street.me.uk Git - andy/viking.git/blame - src/viktrwlayer.c
Use a common way to create temporary GPX files.
[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)));
07378116
RN
2919 time_t dur = vik_track_get_duration ( tr );
2920 if ( dur > 0 )
2921 g_snprintf ( time_buf2, sizeof(time_buf2), _("- %d:%02d hrs:mins"), (int)round(dur/3600), (int)round((dur/60)%60) );
c7060c4e
RN
2922 }
2923 // Get length and consider the appropriate distance units
2924 gdouble tr_len = vik_track_get_length(tr);
2925 vik_units_distance_t dist_units = a_vik_get_units_distance ();
2926 switch (dist_units) {
2927 case VIK_UNITS_DISTANCE_KILOMETRES:
2928 g_snprintf (tmp_buf, sizeof(tmp_buf), _("%s%.1f km %s"), time_buf1, tr_len/1000.0, time_buf2);
2929 break;
2930 case VIK_UNITS_DISTANCE_MILES:
a492ff13 2931 g_snprintf (tmp_buf, sizeof(tmp_buf), _("%s%.1f miles %s"), time_buf1, VIK_METERS_TO_MILES(tr_len), time_buf2);
c7060c4e
RN
2932 break;
2933 default:
2934 break;
2935 }
2936 return tmp_buf;
2937 }
2938 }
2939 break;
cc837b7a
RN
2940 case VIK_TRW_LAYER_SUBLAYER_WAYPOINTS:
2941 {
2942 // Very simple tooltip - may expand detail in the future...
2943 static gchar tmp_buf[32];
2944 g_snprintf (tmp_buf, sizeof(tmp_buf),
2945 _("Waypoints: %d"),
2946 g_hash_table_size (l->waypoints));
2947 return tmp_buf;
2948 }
2949 break;
c7060c4e
RN
2950 case VIK_TRW_LAYER_SUBLAYER_WAYPOINT:
2951 {
2952 VikWaypoint *w = g_hash_table_lookup ( l->waypoints, sublayer );
2953 // NB It's OK to return NULL
382b8734
RN
2954 if ( w ) {
2955 if ( w->comment )
2956 return w->comment;
2957 else
2958 return w->description;
2959 }
c7060c4e
RN
2960 }
2961 break;
2962 default: break;
2963 }
2964 return NULL;
2965}
2966
60626a3e
RN
2967#define VIK_SETTINGS_TRKPT_SELECTED_STATUSBAR_FORMAT "trkpt_selected_statusbar_format"
2968
2969/**
2970 * set_statusbar_msg_info_trkpt:
2971 *
2972 * Function to show track point information on the statusbar
2973 * Items displayed is controlled by the settings format code
95d1b757
RN
2974 */
2975static void set_statusbar_msg_info_trkpt ( VikTrwLayer *vtl, VikTrackpoint *trkpt )
2976{
60626a3e
RN
2977 gchar *statusbar_format_code = NULL;
2978 gboolean need2free = FALSE;
2979 if ( !a_settings_get_string ( VIK_SETTINGS_TRKPT_SELECTED_STATUSBAR_FORMAT, &statusbar_format_code ) ) {
2980 // Otherwise use default
b45865b4 2981 statusbar_format_code = g_strdup ( "KEATDN" );
60626a3e 2982 need2free = TRUE;
95d1b757
RN
2983 }
2984
60626a3e 2985 gchar *msg = vu_trackpoint_formatted_message ( statusbar_format_code, trkpt, NULL, vtl->current_tp_track );
95d1b757 2986 vik_statusbar_set_message ( vik_window_get_statusbar (VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl))), VIK_STATUSBAR_INFO, msg );
95d1b757 2987 g_free ( msg );
60626a3e
RN
2988
2989 if ( need2free )
2990 g_free ( statusbar_format_code );
95d1b757
RN
2991}
2992
2993/*
2994 * Function to show basic waypoint information on the statusbar
2995 */
2996static void set_statusbar_msg_info_wpt ( VikTrwLayer *vtl, VikWaypoint *wpt )
2997{
2998 gchar tmp_buf1[64];
2999 switch (a_vik_get_units_height ()) {
3000 case VIK_UNITS_HEIGHT_FEET:
3001 g_snprintf(tmp_buf1, sizeof(tmp_buf1), _("Wpt: Alt %dft"), (int)round(VIK_METERS_TO_FEET(wpt->altitude)));
3002 break;
3003 default:
3004 //VIK_UNITS_HEIGHT_METRES:
3005 g_snprintf(tmp_buf1, sizeof(tmp_buf1), _("Wpt: Alt %dm"), (int)round(wpt->altitude));
3006 }
3007
3008 // Position part
3009 // Position is put last, as this bit is most likely not to be seen if the display is not big enough,
3010 // one can easily use the current pointer position to see this if needed
3011 gchar *lat = NULL, *lon = NULL;
3012 static struct LatLon ll;
3013 vik_coord_to_latlon (&(wpt->coord), &ll);
3014 a_coords_latlon_to_string ( &ll, &lat, &lon );
3015
3016 // Combine parts to make overall message
3017 gchar *msg;
3018 if ( wpt->comment )
3019 // Add comment if available
3020 msg = g_strdup_printf ( _("%s | %s %s | Comment: %s"), tmp_buf1, lat, lon, wpt->comment );
3021 else
3022 msg = g_strdup_printf ( _("%s | %s %s"), tmp_buf1, lat, lon );
3023 vik_statusbar_set_message ( vik_window_get_statusbar (VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl))), VIK_STATUSBAR_INFO, msg );
3024 g_free ( lat );
3025 g_free ( lon );
3026 g_free ( msg );
3027}
3028
a5dcfdb7
RN
3029/**
3030 * General layer selection function, find out which bit is selected and take appropriate action
3031 */
a7cd93ac 3032static gboolean trw_layer_selected ( VikTrwLayer *l, gint subtype, gpointer sublayer, gint type, gpointer vlp )
a5dcfdb7 3033{
c95d6b00 3034 // Reset
c9570f86
RN
3035 l->current_wp = NULL;
3036 l->current_wp_id = NULL;
c95d6b00
RN
3037 trw_layer_cancel_current_tp ( l, FALSE );
3038
95d1b757
RN
3039 // Clear statusbar
3040 vik_statusbar_set_message ( vik_window_get_statusbar (VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(l))), VIK_STATUSBAR_INFO, "" );
3041
a5dcfdb7
RN
3042 switch ( type )
3043 {
3044 case VIK_TREEVIEW_TYPE_LAYER:
3045 {
3046 vik_window_set_selected_trw_layer ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l), l );
3047 /* Mark for redraw */
3048 return TRUE;
3049 }
3050 break;
3051
3052 case VIK_TREEVIEW_TYPE_SUBLAYER:
3053 {
3054 switch ( subtype )
3055 {
3056 case VIK_TRW_LAYER_SUBLAYER_TRACKS:
3057 {
113c74f6 3058 vik_window_set_selected_tracks ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l), l->tracks, l );
a5dcfdb7
RN
3059 /* Mark for redraw */
3060 return TRUE;
3061 }
3062 break;
3063 case VIK_TRW_LAYER_SUBLAYER_TRACK:
3064 {
95d1b757 3065 VikTrack *track = g_hash_table_lookup ( l->tracks, sublayer );
b16effab 3066 vik_window_set_selected_track ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l), (gpointer)track, l );
a5dcfdb7
RN
3067 /* Mark for redraw */
3068 return TRUE;
3069 }
3070 break;
0d2b891f
RN
3071 case VIK_TRW_LAYER_SUBLAYER_ROUTES:
3072 {
3073 vik_window_set_selected_tracks ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l), l->routes, l );
3074 /* Mark for redraw */
3075 return TRUE;
3076 }
3077 break;
3078 case VIK_TRW_LAYER_SUBLAYER_ROUTE:
3079 {
3080 VikTrack *track = g_hash_table_lookup ( l->routes, sublayer );
3081 vik_window_set_selected_track ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l), (gpointer)track, l );
3082 /* Mark for redraw */
3083 return TRUE;
3084 }
3085 break;
a5dcfdb7
RN
3086 case VIK_TRW_LAYER_SUBLAYER_WAYPOINTS:
3087 {
113c74f6 3088 vik_window_set_selected_waypoints ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l), l->waypoints, l );
a5dcfdb7
RN
3089 /* Mark for redraw */
3090 return TRUE;
3091 }
3092 break;
3093 case VIK_TRW_LAYER_SUBLAYER_WAYPOINT:
3094 {
b16effab
RN
3095 VikWaypoint *wpt = g_hash_table_lookup ( l->waypoints, sublayer );
3096 if ( wpt ) {
3097 vik_window_set_selected_waypoint ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l), (gpointer)wpt, l );
3098 // Show some waypoint info
3099 set_statusbar_msg_info_wpt ( l, wpt );
3100 /* Mark for redraw */
3101 return TRUE;
3102 }
a5dcfdb7
RN
3103 }
3104 break;
3105 default:
3106 {
3107 return vik_window_clear_highlight ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l) );
3108 }
3109 break;
3110 }
3111 return FALSE;
3112 }
3113 break;
3114
3115 default:
3116 return vik_window_clear_highlight ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(l) );
3117 break;
3118 }
3119}
c7060c4e 3120
50a14534
EB
3121GHashTable *vik_trw_layer_get_tracks ( VikTrwLayer *l )
3122{
3123 return l->tracks;
3124}
3125
0d2b891f
RN
3126GHashTable *vik_trw_layer_get_routes ( VikTrwLayer *l )
3127{
3128 return l->routes;
3129}
3130
50a14534
EB
3131GHashTable *vik_trw_layer_get_waypoints ( VikTrwLayer *l )
3132{
3133 return l->waypoints;
3134}
3135
c98e2f73
RN
3136GHashTable *vik_trw_layer_get_tracks_iters ( VikTrwLayer *vtl )
3137{
3138 return vtl->tracks_iters;
3139}
3140
3141GHashTable *vik_trw_layer_get_routes_iters ( VikTrwLayer *vtl )
3142{
3143 return vtl->routes_iters;
3144}
3145
3146GHashTable *vik_trw_layer_get_waypoints_iters ( VikTrwLayer *vtl )
3147{
3148 return vtl->waypoints;
3149}
3150
bec82ff5
RN
3151gboolean vik_trw_layer_is_empty ( VikTrwLayer *vtl )
3152{
3153 return ! ( g_hash_table_size ( vtl->tracks ) ||
3154 g_hash_table_size ( vtl->routes ) ||
3155 g_hash_table_size ( vtl->waypoints ) );
3156}
3157
0082c2f6
RN
3158gboolean vik_trw_layer_get_tracks_visibility ( VikTrwLayer *vtl )
3159{
3160 return vtl->tracks_visible;
3161}
3162
3163gboolean vik_trw_layer_get_routes_visibility ( VikTrwLayer *vtl )
3164{
3165 return vtl->routes_visible;
3166}
3167
3168gboolean vik_trw_layer_get_waypoints_visibility ( VikTrwLayer *vtl )
3169{
3170 return vtl->waypoints_visible;
3171}
3172
c9570f86
RN
3173/*
3174 * ATM use a case sensitive find
3175 * Finds the first one
3176 */
3177static gboolean trw_layer_waypoint_find ( const gpointer id, const VikWaypoint *wp, const gchar *name )
3178{
3179 if ( wp && wp->name )
3180 if ( ! strcmp ( wp->name, name ) )
3181 return TRUE;
3182 return FALSE;
3183}
3184
3185/*
3186 * Get waypoint by name - not guaranteed to be unique
3187 * Finds the first one
3188 */
3189VikWaypoint *vik_trw_layer_get_waypoint ( VikTrwLayer *vtl, const gchar *name )
3190{
3191 return g_hash_table_find ( vtl->waypoints, (GHRFunc) trw_layer_waypoint_find, (gpointer) name );
3192}
3193
ce4bd1cf
RN
3194/*
3195 * ATM use a case sensitive find
3196 * Finds the first one
3197 */
3198static gboolean trw_layer_track_find ( const gpointer id, const VikTrack *trk, const gchar *name )
3199{
3200 if ( trk && trk->name )
3201 if ( ! strcmp ( trk->name, name ) )
3202 return TRUE;
3203 return FALSE;
3204}
3205
3206/*
3207 * Get track by name - not guaranteed to be unique
3208 * Finds the first one
3209 */
c9570f86
RN
3210VikTrack *vik_trw_layer_get_track ( VikTrwLayer *vtl, const gchar *name )
3211{
ce4bd1cf 3212 return g_hash_table_find ( vtl->tracks, (GHRFunc) trw_layer_track_find, (gpointer) name );
c9570f86
RN
3213}
3214
0d2b891f
RN
3215/*
3216 * Get route by name - not guaranteed to be unique
3217 * Finds the first one
3218 */
3219VikTrack *vik_trw_layer_get_route ( VikTrwLayer *vtl, const gchar *name )
3220{
3221 return g_hash_table_find ( vtl->routes, (GHRFunc) trw_layer_track_find, (gpointer) name );
3222}
3223
9e212bfc 3224static void trw_layer_find_maxmin_tracks ( const gpointer id, const VikTrack *trk, struct LatLon maxmin[2] )
50a14534 3225{
a8e8ffe1
RN
3226 if ( trk->bbox.north > maxmin[0].lat || maxmin[0].lat == 0.0 )
3227 maxmin[0].lat = trk->bbox.north;
3228 if ( trk->bbox.south < maxmin[1].lat || maxmin[1].lat == 0.0 )
3229 maxmin[1].lat = trk->bbox.south;
3230 if ( trk->bbox.east > maxmin[0].lon || maxmin[0].lon == 0.0 )
3231 maxmin[0].lon = trk->bbox.east;
3232 if ( trk->bbox.west < maxmin[1].lon || maxmin[1].lon == 0.0 )
3233 maxmin[1].lon = trk->bbox.west;
50a14534
EB
3234}
3235
165a4fa9
HR
3236static void trw_layer_find_maxmin (VikTrwLayer *vtl, struct LatLon maxmin[2])
3237{
0d2b891f 3238 // Continually reuse maxmin to find the latest maximum and minimum values
a8e8ffe1
RN
3239 // First set to waypoints bounds
3240 maxmin[0].lat = vtl->waypoints_bbox.north;
3241 maxmin[1].lat = vtl->waypoints_bbox.south;
3242 maxmin[0].lon = vtl->waypoints_bbox.east;
3243 maxmin[1].lon = vtl->waypoints_bbox.west;
0d2b891f
RN
3244 g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_find_maxmin_tracks, maxmin );
3245 g_hash_table_foreach ( vtl->routes, (GHFunc) trw_layer_find_maxmin_tracks, maxmin );
165a4fa9 3246}
50a14534
EB
3247
3248gboolean vik_trw_layer_find_center ( VikTrwLayer *vtl, VikCoord *dest )
3249{
3250 /* 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... */
3251 struct LatLon maxmin[2] = { {0.0,0.0}, {0.0,0.0} };
165a4fa9 3252 trw_layer_find_maxmin (vtl, maxmin);
50a14534
EB
3253 if (maxmin[0].lat == 0.0 && maxmin[0].lon == 0.0 && maxmin[1].lat == 0.0 && maxmin[1].lon == 0.0)
3254 return FALSE;
3255 else
3256 {
3257 struct LatLon average = { (maxmin[0].lat+maxmin[1].lat)/2, (maxmin[0].lon+maxmin[1].lon)/2 };
3258 vik_coord_load_from_latlon ( dest, vtl->coord_mode, &average );
3259 return TRUE;
3260 }
3261}
3262
1c2083ba 3263static void trw_layer_centerize ( menu_array_layer values )
50a14534 3264{
1c2083ba 3265 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
50a14534 3266 VikCoord coord;
1c2083ba
RN
3267 if ( vik_trw_layer_find_center ( vtl, &coord ) )
3268 goto_coord ( values[MA_VLP], NULL, NULL, &coord );
50a14534 3269 else
1c2083ba 3270 a_dialog_info_msg ( VIK_GTK_WINDOW_FROM_LAYER(vtl), _("This layer has no waypoints or trackpoints.") );
50a14534
EB
3271}
3272
c98e2f73 3273void trw_layer_zoom_to_show_latlons ( VikTrwLayer *vtl, VikViewport *vvp, struct LatLon maxmin[2] )
c5638216
RN
3274{
3275 /* First set the center [in case previously viewing from elsewhere] */
3276 /* Then loop through zoom levels until provided positions are in view */
3277 /* This method is not particularly fast - but should work well enough */
3278 struct LatLon average = { (maxmin[0].lat+maxmin[1].lat)/2, (maxmin[0].lon+maxmin[1].lon)/2 };
3279 VikCoord coord;
3280 vik_coord_load_from_latlon ( &coord, vtl->coord_mode, &average );
be5554c5 3281 vik_viewport_set_center_coord ( vvp, &coord, TRUE );
c5638216
RN
3282
3283 /* Convert into definite 'smallest' and 'largest' positions */
3284 struct LatLon minmin;
3285 if ( maxmin[0].lat < maxmin[1].lat )
3286 minmin.lat = maxmin[0].lat;
3287 else
3288 minmin.lat = maxmin[1].lat;
3289
3290 struct LatLon maxmax;
3291 if ( maxmin[0].lon > maxmin[1].lon )
3292 maxmax.lon = maxmin[0].lon;
3293 else
3294 maxmax.lon = maxmin[1].lon;
3295
3296 /* Never zoom in too far - generally not that useful, as too close ! */
3297 /* Always recalculate the 'best' zoom level */
3298 gdouble zoom = 1.0;
3299 vik_viewport_set_zoom ( vvp, zoom );
3300
3301 gdouble min_lat, max_lat, min_lon, max_lon;
3302 /* Should only be a maximum of about 18 iterations from min to max zoom levels */
3303 while ( zoom <= VIK_VIEWPORT_MAX_ZOOM ) {
3304 vik_viewport_get_min_max_lat_lon ( vvp, &min_lat, &max_lat, &min_lon, &max_lon );
3305 /* NB I think the logic used in this test to determine if the bounds is within view
3306 fails if track goes across 180 degrees longitude.
3307 Hopefully that situation is not too common...
3308 Mind you viking doesn't really do edge locations to well anyway */
3309 if ( min_lat < minmin.lat &&
3310 max_lat > minmin.lat &&
3311 min_lon < maxmax.lon &&
3312 max_lon > maxmax.lon )
3313 /* Found within zoom level */
3314 break;
3315
3316 /* Try next */
3317 zoom = zoom * 2;
3318 vik_viewport_set_zoom ( vvp, zoom );
3319 }
3320}
3321
3322gboolean vik_trw_layer_auto_set_view ( VikTrwLayer *vtl, VikViewport *vvp )
3323{
3324 /* TODO: what if there's only one waypoint @ 0,0, it will think nothing found. */
3325 struct LatLon maxmin[2] = { {0.0,0.0}, {0.0,0.0} };
3326 trw_layer_find_maxmin (vtl, maxmin);
3327 if (maxmin[0].lat == 0.0 && maxmin[0].lon == 0.0 && maxmin[1].lat == 0.0 && maxmin[1].lon == 0.0)
3328 return FALSE;
3329 else {
3330 trw_layer_zoom_to_show_latlons ( vtl, vvp, maxmin );
3331 return TRUE;
3332 }
3333}
3334
1c2083ba 3335static void trw_layer_auto_view ( menu_array_layer values )
5a10c240 3336{
1c2083ba
RN
3337 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
3338 VikLayersPanel *vlp = VIK_LAYERS_PANEL(values[MA_VLP]);
3339 if ( vik_trw_layer_auto_set_view ( vtl, vik_layers_panel_get_viewport (vlp) ) ) {
3340 vik_layers_panel_emit_update ( vlp );
5a10c240
RN
3341 }
3342 else
1c2083ba 3343 a_dialog_info_msg ( VIK_GTK_WINDOW_FROM_LAYER(vtl), _("This layer has no waypoints or trackpoints.") );
5a10c240
RN
3344}
3345
e4a11fbe 3346static void trw_layer_export_gpspoint ( menu_array_layer values )
50a14534 3347{
e4a11fbe 3348 gchar *auto_save_name = append_file_ext ( vik_layer_get_name(VIK_LAYER(values[MA_VTL])), FILE_TYPE_GPSPOINT );
6e4a49aa 3349
e4a11fbe 3350 vik_trw_layer_export ( VIK_TRW_LAYER (values[MA_VTL]), _("Export Layer"), auto_save_name, NULL, FILE_TYPE_GPSPOINT );
50a14534 3351
e4a11fbe 3352 g_free ( auto_save_name );
50a14534
EB
3353}
3354
1c2083ba 3355static void trw_layer_export_gpsmapper ( menu_array_layer values )
50a14534 3356{
e4a11fbe
GB
3357 gchar *auto_save_name = append_file_ext ( vik_layer_get_name(VIK_LAYER(values[MA_VTL])), FILE_TYPE_GPSMAPPER );
3358
3359 vik_trw_layer_export ( VIK_TRW_LAYER (values[MA_VTL]), _("Export Layer"), auto_save_name, NULL, FILE_TYPE_GPSMAPPER );
3360
3361 g_free ( auto_save_name );
50a14534
EB
3362}
3363
1c2083ba 3364static void trw_layer_export_gpx ( menu_array_layer values )
561e6ad0 3365{
e4a11fbe 3366 gchar *auto_save_name = append_file_ext ( vik_layer_get_name(VIK_LAYER(values[MA_VTL])), FILE_TYPE_GPX );
18d0a1ed 3367
e4a11fbe 3368 vik_trw_layer_export ( VIK_TRW_LAYER (values[MA_VTL]), _("Export Layer"), auto_save_name, NULL, FILE_TYPE_GPX );
18d0a1ed
RN
3369
3370 g_free ( auto_save_name );
7f6757c4
RN
3371}
3372
1c2083ba 3373static void trw_layer_export_kml ( menu_array_layer values )
ba9d0a00 3374{
e4a11fbe 3375 gchar *auto_save_name = append_file_ext ( vik_layer_get_name(VIK_LAYER(values[MA_VTL])), FILE_TYPE_KML );
ba9d0a00 3376
e4a11fbe 3377 vik_trw_layer_export ( VIK_TRW_LAYER (values[MA_VTL]), _("Export Layer"), auto_save_name, NULL, FILE_TYPE_KML );
ba9d0a00
RN
3378
3379 g_free ( auto_save_name );
3380}
3381
17720e63
GB
3382static void trw_layer_export_babel ( gpointer layer_and_vlp[2] )
3383{
c3c4b41c 3384 const gchar *auto_save_name = vik_layer_get_name(VIK_LAYER(layer_and_vlp[0]));
17720e63
GB
3385 vik_trw_layer_export_gpsbabel ( VIK_TRW_LAYER (layer_and_vlp[0]), _("Export Layer"), auto_save_name );
3386}
ccccf356 3387
1c2083ba 3388static void trw_layer_export_external_gpx_1 ( menu_array_layer values )
ccccf356 3389{
e4a11fbe 3390 vik_trw_layer_export_external_gpx ( VIK_TRW_LAYER (values[MA_VTL]), a_vik_get_external_gpx_program_1() );
ccccf356
RN
3391}
3392
1c2083ba 3393static void trw_layer_export_external_gpx_2 ( menu_array_layer values )
ff02058b 3394{
e4a11fbe 3395 vik_trw_layer_export_external_gpx ( VIK_TRW_LAYER (values[MA_VTL]), a_vik_get_external_gpx_program_2() );
ff02058b
RN
3396}
3397
19782ffd 3398static void trw_layer_export_gpx_track ( menu_array_sublayer values )
7f6757c4 3399{
19782ffd 3400 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
0d2b891f 3401 VikTrack *trk;
19782ffd
RN
3402 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
3403 trk = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
0d2b891f 3404 else
19782ffd 3405 trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
ce4bd1cf
RN
3406
3407 if ( !trk || !trk->name )
3408 return;
7f6757c4 3409
e4a11fbe 3410 gchar *auto_save_name = append_file_ext ( trk->name, FILE_TYPE_GPX );
7f6757c4 3411
0f704e14 3412 gchar *label = NULL;
19782ffd 3413 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
0f704e14
GB
3414 label = _("Export Route as GPX");
3415 else
3416 label = _("Export Track as GPX");
e4a11fbe 3417 vik_trw_layer_export ( VIK_TRW_LAYER (values[MA_VTL]), label, auto_save_name, trk, FILE_TYPE_GPX );
7f6757c4
RN
3418
3419 g_free ( auto_save_name );
561e6ad0
EB
3420}
3421
d18f2933 3422gboolean trw_layer_waypoint_find_uuid ( const gpointer id, const VikWaypoint *wp, gpointer udata )
c9570f86
RN
3423{
3424 wpu_udata *user_data = udata;
3425 if ( wp == user_data->wp ) {
3426 user_data->uuid = id;
3427 return TRUE;
3428 }
3429 return FALSE;
3430}
3431
1c2083ba 3432static void trw_layer_goto_wp ( menu_array_layer values )
50a14534 3433{
1c2083ba
RN
3434 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
3435 VikLayersPanel *vlp = VIK_LAYERS_PANEL(values[MA_VLP]);
a77d62d8 3436 GtkWidget *dia = gtk_dialog_new_with_buttons (_("Find"),
1c2083ba 3437 VIK_GTK_WINDOW_FROM_LAYER(vtl),
50a14534
EB
3438 GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
3439 GTK_STOCK_CANCEL,
3440 GTK_RESPONSE_REJECT,
3441 GTK_STOCK_OK,
3442 GTK_RESPONSE_ACCEPT,
3443 NULL);
3444
3445 GtkWidget *label, *entry;
4c77d5e0 3446 label = gtk_label_new(_("Waypoint Name:"));
50a14534
EB
3447 entry = gtk_entry_new();
3448
9b082b39
RN
3449 gtk_box_pack_start(GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dia))), label, FALSE, FALSE, 0);
3450 gtk_box_pack_start(GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dia))), entry, FALSE, FALSE, 0);
50a14534
EB
3451 gtk_widget_show_all ( label );
3452 gtk_widget_show_all ( entry );
3453
7ea3cb11
RN
3454 gtk_dialog_set_default_response ( GTK_DIALOG(dia), GTK_RESPONSE_ACCEPT );
3455
50a14534
EB
3456 while ( gtk_dialog_run ( GTK_DIALOG(dia) ) == GTK_RESPONSE_ACCEPT )
3457 {
c9570f86
RN
3458 gchar *name = g_strdup(gtk_entry_get_text(GTK_ENTRY(entry)));
3459 // Find *first* wp with the given name
1c2083ba 3460 VikWaypoint *wp = vik_trw_layer_get_waypoint ( vtl, name );
50a14534 3461
c9570f86 3462 if ( !wp )
1c2083ba 3463 a_dialog_error_msg ( VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Waypoint not found in this layer.") );
50a14534
EB
3464 else
3465 {
be5554c5 3466 vik_viewport_set_center_coord ( vik_layers_panel_get_viewport(vlp), &(wp->coord), TRUE );
1c2083ba 3467 vik_layers_panel_emit_update ( vlp );
c9570f86
RN
3468
3469 // Find and select on the side panel
3470 wpu_udata udata;
3471 udata.wp = wp;
3472 udata.uuid = NULL;
3473
3474 // Hmmm, want key of it
1c2083ba 3475 gpointer *wpf = g_hash_table_find ( vtl->waypoints, (GHRFunc) trw_layer_waypoint_find_uuid, (gpointer) &udata );
c9570f86
RN
3476
3477 if ( wpf && udata.uuid ) {
1c2083ba
RN
3478 GtkTreeIter *it = g_hash_table_lookup ( vtl->waypoints_iters, udata.uuid );
3479 vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, it, TRUE );
c9570f86
RN
3480 }
3481
50a14534
EB
3482 break;
3483 }
3484
c9570f86 3485 g_free ( name );
50a14534
EB
3486
3487 }
3488 gtk_widget_destroy ( dia );
3489}
3490
3491gboolean vik_trw_layer_new_waypoint ( VikTrwLayer *vtl, GtkWindow *w, const VikCoord *def_coord )
3492{
ac1bde8b 3493 gchar *default_name = highest_wp_number_get(vtl);
acaf7113 3494 VikWaypoint *wp = vik_waypoint_new();
ac1bde8b
RN
3495 gchar *returned_name;
3496 gboolean updated;
acaf7113 3497 wp->coord = *def_coord;
d60a672e
RN
3498
3499 // Attempt to auto set height if DEM data is available
a90338e5 3500 vik_waypoint_apply_dem_data ( wp, TRUE );
50a14534 3501
d6175f49 3502 returned_name = a_dialog_waypoint ( w, default_name, vtl, wp, vtl->coord_mode, TRUE, &updated );
c9570f86
RN
3503
3504 if ( returned_name )
50a14534 3505 {
805d282e 3506 wp->visible = TRUE;
ac1bde8b
RN
3507 vik_trw_layer_add_waypoint ( vtl, returned_name, wp );
3508 g_free (default_name);
c9570f86 3509 g_free (returned_name);
50a14534
EB
3510 return TRUE;
3511 }
ac1bde8b 3512 g_free (default_name);
acaf7113 3513 vik_waypoint_free(wp);
50a14534
EB
3514 return FALSE;
3515}
3516
1c2083ba 3517static void trw_layer_new_wikipedia_wp_viewport ( menu_array_layer values )
165a4fa9 3518{
165a4fa9 3519 struct LatLon maxmin[2] = { {0.0,0.0}, {0.0,0.0} };
1c2083ba
RN
3520 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
3521 VikLayersPanel *vlp = VIK_LAYERS_PANEL(values[MA_VLP]);
165a4fa9
HR
3522 VikWindow *vw = (VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl));
3523 VikViewport *vvp = vik_window_viewport(vw);
42d6236f
RN
3524
3525 // Note the order is max part first then min part - thus reverse order of use in min_max function:
3526 vik_viewport_get_min_max_lat_lon ( vvp, &maxmin[1].lat, &maxmin[0].lat, &maxmin[1].lon, &maxmin[0].lon );
120ab662 3527 a_geonames_wikipedia_box((VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl)), vtl, maxmin);
aa0665e5 3528 trw_layer_calculate_bounds_waypoints ( vtl );
120ab662 3529 vik_layers_panel_emit_update ( vlp );
165a4fa9
HR
3530}
3531
1c2083ba 3532static void trw_layer_new_wikipedia_wp_layer ( menu_array_layer values )
165a4fa9 3533{
1c2083ba
RN
3534 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
3535 VikLayersPanel *vlp = VIK_LAYERS_PANEL(values[MA_VLP]);
165a4fa9
HR
3536 struct LatLon maxmin[2] = { {0.0,0.0}, {0.0,0.0} };
3537
3538 trw_layer_find_maxmin (vtl, maxmin);
120ab662 3539 a_geonames_wikipedia_box((VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl)), vtl, maxmin);
aa0665e5 3540 trw_layer_calculate_bounds_waypoints ( vtl );
120ab662 3541 vik_layers_panel_emit_update ( vlp );
165a4fa9
HR
3542}
3543
b3eb3b98 3544#ifdef VIK_CONFIG_GEOTAG
19782ffd 3545static void trw_layer_geotagging_waypoint_mtime_keep ( menu_array_sublayer values )
b3eb3b98 3546{
19782ffd 3547 VikWaypoint *wp = g_hash_table_lookup ( VIK_TRW_LAYER(values[MA_VTL])->waypoints, values[MA_SUBLAYER_ID] );
b3eb3b98
RN
3548 if ( wp )
3549 // Update directly - not changing the mtime
3550 a_geotag_write_exif_gps ( wp->image, wp->coord, wp->altitude, TRUE );
3551}
3552
19782ffd 3553static void trw_layer_geotagging_waypoint_mtime_update ( menu_array_sublayer values )
b3eb3b98 3554{
19782ffd 3555 VikWaypoint *wp = g_hash_table_lookup ( VIK_TRW_LAYER(values[MA_VTL])->waypoints, values[MA_SUBLAYER_ID] );
b3eb3b98
RN
3556 if ( wp )
3557 // Update directly
3558 a_geotag_write_exif_gps ( wp->image, wp->coord, wp->altitude, FALSE );
3559}
3560
3561/*
3562 * Use code in separate file for this feature as reasonably complex
3563 */
19782ffd 3564static void trw_layer_geotagging_track ( menu_array_sublayer values )
b3eb3b98 3565{
19782ffd
RN
3566 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
3567 VikTrack *track = g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
b3eb3b98
RN
3568 // Unset so can be reverified later if necessary
3569 vtl->has_verified_thumbnails = FALSE;
3570
3571 trw_layer_geotag_dialog ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
41f4abac
RN
3572 vtl,
3573 NULL,
3574 track );
3575}
3576
19782ffd 3577static void trw_layer_geotagging_waypoint ( menu_array_sublayer values )
41f4abac 3578{
19782ffd
RN
3579 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
3580 VikWaypoint *wpt = g_hash_table_lookup ( vtl->waypoints, values[MA_SUBLAYER_ID] );
41f4abac
RN
3581
3582 trw_layer_geotag_dialog ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
3583 vtl,
3584 wpt,
3585 NULL );
b3eb3b98
RN
3586}
3587
1c2083ba 3588static void trw_layer_geotagging ( menu_array_layer values )
b3eb3b98 3589{
1c2083ba 3590 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
b3eb3b98
RN
3591 // Unset so can be reverified later if necessary
3592 vtl->has_verified_thumbnails = FALSE;
3593
3594 trw_layer_geotag_dialog ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
41f4abac
RN
3595 vtl,
3596 NULL,
3597 NULL );
b3eb3b98
RN
3598}
3599#endif
3600
16fc32f6
RN
3601// 'Acquires' - Same as in File Menu -> Acquire - applies into the selected TRW Layer //
3602
1c2083ba 3603static void trw_layer_acquire ( menu_array_layer values, VikDataSourceInterface *datasource )
16fc32f6 3604{
1c2083ba
RN
3605 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
3606 VikLayersPanel *vlp = VIK_LAYERS_PANEL(values[MA_VLP]);
16fc32f6
RN
3607 VikWindow *vw = (VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl));
3608 VikViewport *vvp = vik_window_viewport(vw);
3609
9cc13848
RN
3610 vik_datasource_mode_t mode = datasource->mode;
3611 if ( mode == VIK_DATASOURCE_AUTO_LAYER_MANAGEMENT )
3612 mode = VIK_DATASOURCE_ADDTOLAYER;
3613 a_acquire ( vw, vlp, vvp, mode, datasource, NULL, NULL );
995e9fd9
GB
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{
1c2083ba 3621 trw_layer_acquire ( values, &vik_datasource_gps_interface );
16fc32f6
RN
3622}
3623
3624/*
7f95fd54 3625 * Acquire into this TRW Layer from Directions
16fc32f6 3626 */
1c2083ba 3627static void trw_layer_acquire_routing_cb ( menu_array_layer values )
16fc32f6 3628{
1c2083ba 3629 trw_layer_acquire ( values, &vik_datasource_routing_interface );
16fc32f6
RN
3630}
3631
c6acf18d
RN
3632/*
3633 * Acquire into this TRW Layer from an entered URL
3634 */
1c2083ba 3635static void trw_layer_acquire_url_cb ( menu_array_layer values )
c6acf18d 3636{
1c2083ba 3637 trw_layer_acquire ( values, &vik_datasource_url_interface );
c6acf18d
RN
3638}
3639
40f5740b
RN
3640#ifdef VIK_CONFIG_OPENSTREETMAP
3641/*
3642 * Acquire into this TRW Layer from OSM
3643 */
1c2083ba 3644static void trw_layer_acquire_osm_cb ( menu_array_layer values )
40f5740b 3645{
1c2083ba 3646 trw_layer_acquire ( values, &vik_datasource_osm_interface );
40f5740b 3647}
3cc57413
RN
3648
3649/**
3650 * Acquire into this TRW Layer from OSM for 'My' Traces
3651 */
1c2083ba 3652static void trw_layer_acquire_osm_my_traces_cb ( menu_array_layer values )
3cc57413 3653{
1c2083ba 3654 trw_layer_acquire ( values, &vik_datasource_osm_my_traces_interface );
3cc57413 3655}
40f5740b
RN
3656#endif
3657
16fc32f6
RN
3658#ifdef VIK_CONFIG_GEOCACHES
3659/*
3660 * Acquire into this TRW Layer from Geocaching.com
3661 */
1c2083ba 3662static void trw_layer_acquire_geocache_cb ( menu_array_layer values )
16fc32f6 3663{
1c2083ba 3664 trw_layer_acquire ( values, &vik_datasource_gc_interface );
16fc32f6
RN
3665}
3666#endif
3667
68bab1bd
RN
3668#ifdef VIK_CONFIG_GEOTAG
3669/*
3670 * Acquire into this TRW Layer from images
3671 */
1c2083ba 3672static void trw_layer_acquire_geotagged_cb ( menu_array_layer values )
68bab1bd 3673{
1c2083ba 3674 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
68bab1bd 3675
1c2083ba 3676 trw_layer_acquire ( values, &vik_datasource_geotag_interface );
68bab1bd
RN
3677
3678 // Reverify thumbnails as they may have changed
3679 vtl->has_verified_thumbnails = FALSE;
3680 trw_layer_verify_thumbnails ( vtl, NULL );
3681}
3682#endif
3683
9cc13848
RN
3684/*
3685 * Acquire into this TRW Layer from any GPS Babel supported file
3686 */
3687static void trw_layer_acquire_file_cb ( menu_array_layer values )
3688{
3689 trw_layer_acquire ( values, &vik_datasource_file_interface );
3690}
3691
1c2083ba 3692static void trw_layer_gps_upload ( menu_array_layer values )
e50758c7 3693{
19782ffd
RN
3694 menu_array_sublayer data;
3695 gint ii;
3696 for ( ii = MA_VTL; ii < MA_LAST; ii++ )
3697 data[ii] = NULL;
3698 data[MA_VTL] = values[MA_VTL];
3699 data[MA_VLP] = values[MA_VLP];
e50758c7 3700
19782ffd 3701 trw_layer_gps_upload_any ( data );
e50758c7
RN
3702}
3703
3704/**
3705 * If pass_along[3] is defined that this will upload just that track
3706 */
19782ffd 3707static void trw_layer_gps_upload_any ( menu_array_sublayer values )
e50758c7 3708{
19782ffd
RN
3709 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
3710 VikLayersPanel *vlp = VIK_LAYERS_PANEL(values[MA_VLP]);
e50758c7 3711
19782ffd 3712 // May not actually get a track here as values[2&3] can be null
0d2b891f
RN
3713 VikTrack *track = NULL;
3714 vik_gps_xfer_type xfer_type = TRK; // VIK_TRW_LAYER_SUBLAYER_TRACKS = 0 so hard to test different from NULL!
3715 gboolean xfer_all = FALSE;
e50758c7 3716
19782ffd 3717 if ( values[MA_SUBTYPE] ) {
0d2b891f 3718 xfer_all = FALSE;
19782ffd
RN
3719 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE ) {
3720 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
0d2b891f
RN
3721 xfer_type = RTE;
3722 }
19782ffd
RN
3723 else if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_TRACK ) {
3724 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
0d2b891f
RN
3725 xfer_type = TRK;
3726 }
19782ffd 3727 else if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_WAYPOINTS ) {
0d2b891f
RN
3728 xfer_type = WPT;
3729 }
19782ffd 3730 else if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTES ) {
0d2b891f
RN
3731 xfer_type = RTE;
3732 }
3733 }
19782ffd 3734 else if ( !values[MA_CONFIRM] )
0d2b891f 3735 xfer_all = TRUE; // i.e. whole layer
e50758c7 3736
0d2b891f 3737 if (track && !track->visible) {
e50758c7
RN
3738 a_dialog_error_msg ( VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Can not upload invisible track.") );
3739 return;
3740 }
3741
3742 GtkWidget *dialog = gtk_dialog_new_with_buttons ( _("GPS Upload"),
19782ffd 3743 VIK_GTK_WINDOW_FROM_LAYER(vtl),
e50758c7
RN
3744 GTK_DIALOG_DESTROY_WITH_PARENT,
3745 GTK_STOCK_OK,
3746 GTK_RESPONSE_ACCEPT,
3747 GTK_STOCK_CANCEL,
3748 GTK_RESPONSE_REJECT,
3749 NULL );
3750
3751 gtk_dialog_set_default_response ( GTK_DIALOG(dialog), GTK_RESPONSE_ACCEPT );
3752 GtkWidget *response_w = NULL;
3753#if GTK_CHECK_VERSION (2, 20, 0)
3754 response_w = gtk_dialog_get_widget_for_response ( GTK_DIALOG(dialog), GTK_RESPONSE_ACCEPT );
3755#endif
3756
3757 if ( response_w )
3758 gtk_widget_grab_focus ( response_w );
3759
0d2b891f 3760 gpointer dgs = datasource_gps_setup ( dialog, xfer_type, xfer_all );
e50758c7
RN
3761
3762 if ( gtk_dialog_run ( GTK_DIALOG(dialog) ) != GTK_RESPONSE_ACCEPT ) {
3763 datasource_gps_clean_up ( dgs );
3764 gtk_widget_destroy ( dialog );
3765 return;
3766 }
3767
3768 // Get info from reused datasource dialog widgets
3769 gchar* protocol = datasource_gps_get_protocol ( dgs );
3770 gchar* port = datasource_gps_get_descriptor ( dgs );
3771 // NB don't free the above strings as they're references to values held elsewhere
3772 gboolean do_tracks = datasource_gps_get_do_tracks ( dgs );
0d2b891f 3773 gboolean do_routes = datasource_gps_get_do_routes ( dgs );
e50758c7
RN
3774 gboolean do_waypoints = datasource_gps_get_do_waypoints ( dgs );
3775 gboolean turn_off = datasource_gps_get_off ( dgs );
3776
3777 gtk_widget_destroy ( dialog );
3778
3779 // When called from the viewport - work the corresponding layerspanel:
3780 if ( !vlp ) {
3781 vlp = vik_window_layers_panel ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)) );
3782 }
3783
3784 // Apply settings to transfer to the GPS device
3785 vik_gps_comm ( vtl,
3786 track,
3787 GPS_UP,
3788 protocol,
3789 port,
3790 FALSE,
3791 vik_layers_panel_get_viewport (vlp),
3792 vlp,
3793 do_tracks,
0d2b891f 3794 do_routes,
e50758c7
RN
3795 do_waypoints,
3796 turn_off );
3797}
3798
1c2083ba 3799static void trw_layer_new_wp ( menu_array_layer values )
50a14534 3800{
1c2083ba
RN
3801 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
3802 VikLayersPanel *vlp = VIK_LAYERS_PANEL(values[MA_VLP]);
50a14534
EB
3803 /* TODO longone: okay, if layer above (aggregate) is invisible but vtl->visible is true, this redraws for no reason.
3804 instead return true if you want to update. */
aa0665e5
RN
3805 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 ) {
3806 trw_layer_calculate_bounds_waypoints ( vtl );
50a14534 3807 vik_layers_panel_emit_update ( vlp );
aa0665e5 3808 }
50a14534
EB
3809}
3810
2f154c90
RN
3811static void new_track_create_common ( VikTrwLayer *vtl, gchar *name )
3812{
3813 vtl->current_track = vik_track_new();
387ff7ac 3814 vik_track_set_defaults ( vtl->current_track );
2f154c90
RN
3815 vtl->current_track->visible = TRUE;
3816 if ( vtl->drawmode == DRAWMODE_ALL_SAME_COLOR )
3817 // Create track with the preferred colour from the layer properties
3818 vtl->current_track->color = vtl->track_color;
3819 else
3820 gdk_color_parse ( "#000000", &(vtl->current_track->color) );
3821 vtl->current_track->has_color = TRUE;
3822 vik_trw_layer_add_track ( vtl, name, vtl->current_track );
3823}
3824
1c2083ba 3825static void trw_layer_new_track ( menu_array_layer values )
37615c52 3826{
1c2083ba 3827 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
37615c52
RN
3828
3829 if ( ! vtl->current_track ) {
3830 gchar *name = trw_layer_new_unique_sublayer_name ( vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, _("Track")) ;
2f154c90 3831 new_track_create_common ( vtl, name );
1613e468 3832 g_free ( name );
37615c52
RN
3833
3834 vik_window_enable_layer_tool ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)), VIK_LAYER_TRW, TOOL_CREATE_TRACK );
3835 }
3836}
3837
b1453c16
RN
3838static void new_route_create_common ( VikTrwLayer *vtl, gchar *name )
3839{
3840 vtl->current_track = vik_track_new();
387ff7ac 3841 vik_track_set_defaults ( vtl->current_track );
b1453c16
RN
3842 vtl->current_track->visible = TRUE;
3843 vtl->current_track->is_route = TRUE;
3844 // By default make all routes red
3845 vtl->current_track->has_color = TRUE;
3846 gdk_color_parse ( "red", &vtl->current_track->color );
3847 vik_trw_layer_add_route ( vtl, name, vtl->current_track );
3848}
3849
1c2083ba 3850static void trw_layer_new_route ( menu_array_layer values )
0d2b891f 3851{
1c2083ba 3852 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
0d2b891f
RN
3853
3854 if ( ! vtl->current_track ) {
3855 gchar *name = trw_layer_new_unique_sublayer_name ( vtl, VIK_TRW_LAYER_SUBLAYER_ROUTE, _("Route")) ;
b1453c16 3856 new_route_create_common ( vtl, name );
1613e468 3857 g_free ( name );
e37b2a6d 3858 vik_window_enable_layer_tool ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)), VIK_LAYER_TRW, TOOL_CREATE_ROUTE );
0d2b891f
RN
3859 }
3860}
3861
1c2083ba 3862static void trw_layer_auto_routes_view ( menu_array_layer values )
0d2b891f 3863{
1c2083ba
RN
3864 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
3865 VikLayersPanel *vlp = VIK_LAYERS_PANEL(values[MA_VLP]);
0d2b891f
RN
3866
3867 if ( g_hash_table_size (vtl->routes) > 0 ) {
3868 struct LatLon maxmin[2] = { {0,0}, {0,0} };
3869 g_hash_table_foreach ( vtl->routes, (GHFunc) trw_layer_find_maxmin_tracks, maxmin );
3870 trw_layer_zoom_to_show_latlons ( vtl, vik_layers_panel_get_viewport (vlp), maxmin );
3871 vik_layers_panel_emit_update ( vlp );
3872 }
3873}
3874
3875
1c2083ba 3876static void trw_layer_finish_track ( menu_array_layer values )
37615c52 3877{
1c2083ba 3878 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
37615c52 3879 vtl->current_track = NULL;
ee43b101 3880 vtl->route_finder_started = FALSE;
da121f9b 3881 vik_layer_emit_update ( VIK_LAYER(vtl) );
37615c52
RN
3882}
3883
1c2083ba 3884static void trw_layer_auto_tracks_view ( menu_array_layer values )
535ed1ae 3885{
1c2083ba
RN
3886 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
3887 VikLayersPanel *vlp = VIK_LAYERS_PANEL(values[MA_VLP]);
535ed1ae
RN
3888
3889 if ( g_hash_table_size (vtl->tracks) > 0 ) {
3890 struct LatLon maxmin[2] = { {0,0}, {0,0} };
3891 g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_find_maxmin_tracks, maxmin );
3892 trw_layer_zoom_to_show_latlons ( vtl, vik_layers_panel_get_viewport (vlp), maxmin );
3893 vik_layers_panel_emit_update ( vlp );
3894 }
3895}
3896
9e212bfc 3897static void trw_layer_single_waypoint_jump ( const gpointer id, const VikWaypoint *wp, gpointer vvp )
fc59e8c7
RN
3898{
3899 /* NB do not care if wp is visible or not */
be5554c5 3900 vik_viewport_set_center_coord ( VIK_VIEWPORT(vvp), &(wp->coord), TRUE );
fc59e8c7
RN
3901}
3902
1c2083ba 3903static void trw_layer_auto_waypoints_view ( menu_array_layer values )
fc59e8c7 3904{
1c2083ba
RN
3905 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
3906 VikLayersPanel *vlp = VIK_LAYERS_PANEL(values[MA_VLP]);
fc59e8c7
RN
3907
3908 /* Only 1 waypoint - jump straight to it */
3909 if ( g_hash_table_size (vtl->waypoints) == 1 ) {
3910 VikViewport *vvp = vik_layers_panel_get_viewport (vlp);
3911 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_single_waypoint_jump, (gpointer) vvp );
3912 }
3913 /* If at least 2 waypoints - find center and then zoom to fit */
3914 else if ( g_hash_table_size (vtl->waypoints) > 1 )
3915 {
3916 struct LatLon maxmin[2] = { {0,0}, {0,0} };
a8e8ffe1
RN
3917 maxmin[0].lat = vtl->waypoints_bbox.north;
3918 maxmin[1].lat = vtl->waypoints_bbox.south;
3919 maxmin[0].lon = vtl->waypoints_bbox.east;
3920 maxmin[1].lon = vtl->waypoints_bbox.west;
fc59e8c7
RN
3921 trw_layer_zoom_to_show_latlons ( vtl, vik_layers_panel_get_viewport (vlp), maxmin );
3922 }
3923
3924 vik_layers_panel_emit_update ( vlp );
3925}
3926
1c2083ba 3927void trw_layer_osm_traces_upload_cb ( menu_array_layer values )
3185b5d3 3928{
1c2083ba 3929 osm_traces_upload_viktrwlayer(VIK_TRW_LAYER(values[MA_VTL]), NULL);
3185b5d3
RN
3930}
3931
d6c110f1 3932void trw_layer_osm_traces_upload_track_cb ( menu_array_sublayer values )
3185b5d3 3933{
d6c110f1
RN
3934 if ( values[MA_MISC] ) {
3935 VikTrack *trk = VIK_TRACK(values[MA_MISC]);
3936 osm_traces_upload_viktrwlayer(VIK_TRW_LAYER(values[MA_VTL]), trk);
3185b5d3
RN
3937 }
3938}
3939
a7cd93ac 3940static void trw_layer_add_menu_items ( VikTrwLayer *vtl, GtkMenu *menu, gpointer vlp )
50a14534 3941{
1c2083ba 3942 static menu_array_layer pass_along;
50a14534 3943 GtkWidget *item;
98fcbbdb 3944 GtkWidget *export_submenu;
1c2083ba
RN
3945 pass_along[MA_VTL] = vtl;
3946 pass_along[MA_VLP] = vlp;
50a14534
EB
3947
3948 item = gtk_menu_item_new();
3949 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
3950 gtk_widget_show ( item );
3951
37615c52 3952 if ( vtl->current_track ) {
0d2b891f
RN
3953 if ( vtl->current_track->is_route )
3954 item = gtk_menu_item_new_with_mnemonic ( _("_Finish Route") );
3955 else
3956 item = gtk_menu_item_new_with_mnemonic ( _("_Finish Track") );
37615c52
RN
3957 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_finish_track), pass_along );
3958 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3959 gtk_widget_show ( item );
3960
3961 // Add separator
3962 item = gtk_menu_item_new ();
3963 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
3964 gtk_widget_show ( item );
3965 }
3966
d6de71f9
RN
3967 /* Now with icons */
3968 item = gtk_image_menu_item_new_with_mnemonic ( _("_View Layer") );
3969 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ZOOM_FIT, GTK_ICON_SIZE_MENU) );
5a10c240
RN
3970 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_view), pass_along );
3971 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3972 gtk_widget_show ( item );
3973
48d28f21
RN
3974 GtkWidget *view_submenu = gtk_menu_new();
3975 item = gtk_image_menu_item_new_with_mnemonic ( _("V_iew") );
3976 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_FIND, GTK_ICON_SIZE_MENU) );
535ed1ae
RN
3977 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3978 gtk_widget_show ( item );
48d28f21 3979 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), view_submenu );
535ed1ae 3980
48d28f21
RN
3981 item = gtk_menu_item_new_with_mnemonic ( _("View All _Tracks") );
3982 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_tracks_view), pass_along );
3983 gtk_menu_shell_append (GTK_MENU_SHELL (view_submenu), item);
3984 gtk_widget_show ( item );
3985
0d2b891f
RN
3986 item = gtk_menu_item_new_with_mnemonic ( _("View All _Routes") );
3987 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_routes_view), pass_along );
3988 gtk_menu_shell_append (GTK_MENU_SHELL (view_submenu), item);
3989 gtk_widget_show ( item );
3990
48d28f21 3991 item = gtk_menu_item_new_with_mnemonic ( _("View All _Waypoints") );
fc59e8c7 3992 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_waypoints_view), pass_along );
48d28f21 3993 gtk_menu_shell_append (GTK_MENU_SHELL (view_submenu), item);
fc59e8c7
RN
3994 gtk_widget_show ( item );
3995
d6de71f9
RN
3996 item = gtk_image_menu_item_new_with_mnemonic ( _("_Goto Center of Layer") );
3997 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_JUMP_TO, GTK_ICON_SIZE_MENU) );
50a14534
EB
3998 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_centerize), pass_along );
3999 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
4000 gtk_widget_show ( item );
4001
7306a492 4002 item = gtk_menu_item_new_with_mnemonic ( _("Goto _Waypoint...") );
50a14534
EB
4003 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_wp), pass_along );
4004 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
4005 gtk_widget_show ( item );
4006
1bd88e66 4007 export_submenu = gtk_menu_new ();
d6de71f9
RN
4008 item = gtk_image_menu_item_new_with_mnemonic ( _("_Export Layer") );
4009 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_HARDDISK, GTK_ICON_SIZE_MENU) );
50a14534
EB
4010 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
4011 gtk_widget_show ( item );
98fcbbdb 4012 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), export_submenu );
1bd88e66 4013
7306a492 4014 item = gtk_menu_item_new_with_mnemonic ( _("Export as GPS_Point...") );
1bd88e66
GB
4015 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_gpspoint), pass_along );
4016 gtk_menu_shell_append (GTK_MENU_SHELL (export_submenu), item);
4017 gtk_widget_show ( item );
50a14534 4018
7306a492 4019 item = gtk_menu_item_new_with_mnemonic ( _("Export as GPS_Mapper...") );
50a14534 4020 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_gpsmapper), pass_along );
1bd88e66 4021 gtk_menu_shell_append (GTK_MENU_SHELL (export_submenu), item);
50a14534
EB
4022 gtk_widget_show ( item );
4023
7306a492 4024 item = gtk_menu_item_new_with_mnemonic ( _("Export as _GPX...") );
561e6ad0 4025 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_gpx), pass_along );
1bd88e66 4026 gtk_menu_shell_append (GTK_MENU_SHELL (export_submenu), item);
561e6ad0
EB
4027 gtk_widget_show ( item );
4028
ba9d0a00
RN
4029 item = gtk_menu_item_new_with_mnemonic ( _("Export as _KML...") );
4030 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_kml), pass_along );
4031 gtk_menu_shell_append (GTK_MENU_SHELL (export_submenu), item);
4032 gtk_widget_show ( item );
4033
17720e63
GB
4034 item = gtk_menu_item_new_with_mnemonic ( _("Export via GPSbabel...") );
4035 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_babel), pass_along );
4036 gtk_menu_shell_append (GTK_MENU_SHELL (export_submenu), item);
4037 gtk_widget_show ( item );
4038
6e4bf640 4039 gchar* external1 = g_strdup_printf ( _("Open with External Program_1: %s"), a_vik_get_external_gpx_program_1() );
ccccf356
RN
4040 item = gtk_menu_item_new_with_mnemonic ( external1 );
4041 g_free ( external1 );
4042 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_external_gpx_1), pass_along );
4043 gtk_menu_shell_append (GTK_MENU_SHELL (export_submenu), item);
4044 gtk_widget_show ( item );
4045
6e4bf640 4046 gchar* external2 = g_strdup_printf ( _("Open with External Program_2: %s"), a_vik_get_external_gpx_program_2() );
ff02058b
RN
4047 item = gtk_menu_item_new_with_mnemonic ( external2 );
4048 g_free ( external2 );
4049 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_external_gpx_2), pass_along );
4050 gtk_menu_shell_append (GTK_MENU_SHELL (export_submenu), item);
4051 gtk_widget_show ( item );
4052
e7a8a2f4
RN
4053 GtkWidget *new_submenu = gtk_menu_new();
4054 item = gtk_image_menu_item_new_with_mnemonic ( _("_New") );
4055 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NEW, GTK_ICON_SIZE_MENU) );
4056 gtk_menu_shell_append(GTK_MENU_SHELL (menu), item);
4057 gtk_widget_show(item);
4058 gtk_menu_item_set_submenu(GTK_MENU_ITEM(item), new_submenu);
4059
4060 item = gtk_image_menu_item_new_with_mnemonic ( _("New _Waypoint...") );
d6de71f9 4061 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NEW, GTK_ICON_SIZE_MENU) );
50a14534 4062 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_wp), pass_along );
e7a8a2f4 4063 gtk_menu_shell_append (GTK_MENU_SHELL (new_submenu), item);
50a14534 4064 gtk_widget_show ( item );
3e7553ae 4065
37615c52
RN
4066 item = gtk_image_menu_item_new_with_mnemonic ( _("New _Track") );
4067 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NEW, GTK_ICON_SIZE_MENU) );
4068 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_track), pass_along );
4069 gtk_menu_shell_append (GTK_MENU_SHELL (new_submenu), item);
4070 gtk_widget_show ( item );
4071 // Make it available only when a new track *not* already in progress
4072 gtk_widget_set_sensitive ( item, ! (gboolean)GPOINTER_TO_INT(vtl->current_track) );
4073
0d2b891f
RN
4074 item = gtk_image_menu_item_new_with_mnemonic ( _("New _Route") );
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_route), 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
b3eb3b98
RN
4082#ifdef VIK_CONFIG_GEOTAG
4083 item = gtk_menu_item_new_with_mnemonic ( _("Geotag _Images...") );
4084 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_geotagging), pass_along );
4085 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
4086 gtk_widget_show ( item );
4087#endif
4088
16fc32f6 4089 GtkWidget *acquire_submenu = gtk_menu_new ();
aaecf368 4090 item = gtk_image_menu_item_new_with_mnemonic ( _("_Acquire") );
d6de71f9 4091 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_DOWN, GTK_ICON_SIZE_MENU) );
16fc32f6
RN
4092 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
4093 gtk_widget_show ( item );
4094 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), acquire_submenu );
4095
4096 item = gtk_menu_item_new_with_mnemonic ( _("From _GPS...") );
4097 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_acquire_gps_cb), pass_along );
4098 gtk_menu_shell_append (GTK_MENU_SHELL (acquire_submenu), item);
4099 gtk_widget_show ( item );
4100
7f95fd54
GB
4101 /* FIXME: only add menu when at least a routing engine has support for Directions */
4102 item = gtk_menu_item_new_with_mnemonic ( _("From _Directions...") );
4103 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_acquire_routing_cb), pass_along );
16fc32f6
RN
4104 gtk_menu_shell_append (GTK_MENU_SHELL (acquire_submenu), item);
4105 gtk_widget_show ( item );
4106
40f5740b
RN
4107#ifdef VIK_CONFIG_OPENSTREETMAP
4108 item = gtk_menu_item_new_with_mnemonic ( _("From _OSM Traces...") );
4109 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_acquire_osm_cb), pass_along );
4110 gtk_menu_shell_append (GTK_MENU_SHELL (acquire_submenu), item);
4111 gtk_widget_show ( item );
3cc57413
RN
4112
4113 item = gtk_menu_item_new_with_mnemonic ( _("From _My OSM Traces...") );
4114 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_acquire_osm_my_traces_cb), pass_along );
4115 gtk_menu_shell_append (GTK_MENU_SHELL (acquire_submenu), item);
4116 gtk_widget_show ( item );
40f5740b
RN
4117#endif
4118
c6acf18d
RN
4119 item = gtk_menu_item_new_with_mnemonic ( _("From _URL...") );
4120 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_acquire_url_cb), pass_along );
4121 gtk_menu_shell_append (GTK_MENU_SHELL (acquire_submenu), item);
4122 gtk_widget_show ( item );
4123
aaecf368
RN
4124#ifdef VIK_CONFIG_GEONAMES
4125 GtkWidget *wikipedia_submenu = gtk_menu_new();
4126 item = gtk_image_menu_item_new_with_mnemonic ( _("From _Wikipedia Waypoints") );
4127 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ADD, GTK_ICON_SIZE_MENU) );
4128 gtk_menu_shell_append(GTK_MENU_SHELL (acquire_submenu), item);
4129 gtk_widget_show(item);
4130 gtk_menu_item_set_submenu(GTK_MENU_ITEM(item), wikipedia_submenu);
4131
4132 item = gtk_image_menu_item_new_with_mnemonic ( _("Within _Layer Bounds") );
4133 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ZOOM_FIT, GTK_ICON_SIZE_MENU) );
4134 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_wikipedia_wp_layer), pass_along );
4135 gtk_menu_shell_append (GTK_MENU_SHELL (wikipedia_submenu), item);
4136 gtk_widget_show ( item );
4137
4138 item = gtk_image_menu_item_new_with_mnemonic ( _("Within _Current View") );
4139 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ZOOM_100, GTK_ICON_SIZE_MENU) );
4140 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_wikipedia_wp_viewport), pass_along );
4141 gtk_menu_shell_append (GTK_MENU_SHELL (wikipedia_submenu), item);
4142 gtk_widget_show ( item );
4143#endif
4144
16fc32f6
RN
4145#ifdef VIK_CONFIG_GEOCACHES
4146 item = gtk_menu_item_new_with_mnemonic ( _("From Geo_caching...") );
4147 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_acquire_geocache_cb), pass_along );
4148 gtk_menu_shell_append (GTK_MENU_SHELL (acquire_submenu), item);
4149 gtk_widget_show ( item );
4150#endif
4151
68bab1bd
RN
4152#ifdef VIK_CONFIG_GEOTAG
4153 item = gtk_menu_item_new_with_mnemonic ( _("From Geotagged _Images...") );
4154 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_acquire_geotagged_cb), pass_along );
4155 gtk_menu_shell_append (GTK_MENU_SHELL (acquire_submenu), item);
4156 gtk_widget_show ( item );
4157#endif
4158
d7ac7564
RN
4159 item = gtk_menu_item_new_with_mnemonic ( _("From _File...") );
4160 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_acquire_file_cb), pass_along );
4161 gtk_menu_shell_append (GTK_MENU_SHELL (acquire_submenu), item);
49f6b9d3 4162 gtk_widget_set_tooltip_text (item, _("Import File With GPS_Babel..."));
d7ac7564
RN
4163 gtk_widget_show ( item );
4164
82993cc7
RN
4165 vik_ext_tool_datasources_add_menu_items_to_menu ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)), GTK_MENU (acquire_submenu) );
4166
e50758c7
RN
4167 GtkWidget *upload_submenu = gtk_menu_new ();
4168 item = gtk_image_menu_item_new_with_mnemonic ( _("_Upload") );
4169 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_UP, GTK_ICON_SIZE_MENU) );
4170 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
4171 gtk_widget_show ( item );
4172 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), upload_submenu );
4173
0d2b891f
RN
4174 item = gtk_image_menu_item_new_with_mnemonic ( _("Upload to _GPS...") );
4175 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_FORWARD, GTK_ICON_SIZE_MENU) );
4176 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_gps_upload), pass_along );
4177 gtk_menu_shell_append (GTK_MENU_SHELL (upload_submenu), item);
4178 gtk_widget_show ( item );
4179
3e7553ae 4180#ifdef VIK_CONFIG_OPENSTREETMAP
d6de71f9
RN
4181 item = gtk_image_menu_item_new_with_mnemonic ( _("Upload to _OSM...") );
4182 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_UP, GTK_ICON_SIZE_MENU) );
3185b5d3 4183 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_osm_traces_upload_cb), pass_along );
e50758c7 4184 gtk_menu_shell_append (GTK_MENU_SHELL (upload_submenu), item);
3e7553ae
GB
4185 gtk_widget_show ( item );
4186#endif
28c82d8b 4187
c9a5cbf9 4188 GtkWidget *delete_submenu = gtk_menu_new ();
d6de71f9
RN
4189 item = gtk_image_menu_item_new_with_mnemonic ( _("De_lete") );
4190 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU) );
c9a5cbf9
RN
4191 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
4192 gtk_widget_show ( item );
4193 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), delete_submenu );
4194
d6de71f9
RN
4195 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete All _Tracks") );
4196 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU) );
c9a5cbf9
RN
4197 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_all_tracks), pass_along );
4198 gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
4199 gtk_widget_show ( item );
4200
d6de71f9
RN
4201 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete Tracks _From Selection...") );
4202 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
20b671c3
RN
4203 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_tracks_from_selection), pass_along );
4204 gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
4205 gtk_widget_show ( item );
58517f22
RN
4206
4207 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete _All Routes") );
4208 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU) );
4209 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_all_routes), pass_along );
4210 gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
4211 gtk_widget_show ( item );
4212
4213 item = gtk_image_menu_item_new_with_mnemonic ( _("_Delete Routes From Selection...") );
4214 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
4215 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_routes_from_selection), pass_along );
4216 gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
4217 gtk_widget_show ( item );
20b671c3 4218
d6de71f9
RN
4219 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete All _Waypoints") );
4220 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU) );
c9a5cbf9
RN
4221 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_all_waypoints), pass_along );
4222 gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
4223 gtk_widget_show ( item );
4224
d6de71f9
RN
4225 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete Waypoints From _Selection...") );
4226 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
20b671c3
RN
4227 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_waypoints_from_selection), pass_along );
4228 gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
4229 gtk_widget_show ( item );
4230
28c82d8b 4231 item = a_acquire_trwlayer_menu ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)), vlp,
d65659e7 4232 vik_layers_panel_get_viewport(VIK_LAYERS_PANEL(vlp)), vtl );
28c82d8b
EB
4233 if ( item ) {
4234 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
4235 gtk_widget_show ( item );
4236 }
4237
4238 item = a_acquire_trwlayer_track_menu ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)), vlp,
d65659e7 4239 vik_layers_panel_get_viewport(VIK_LAYERS_PANEL(vlp)), vtl );
28c82d8b
EB
4240 if ( item ) {
4241 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
4242 gtk_widget_show ( item );
07c9d42b
RN
4243 }
4244
4245 item = gtk_image_menu_item_new_with_mnemonic ( _("Track _List...") );
4246 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
4247 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_track_list_dialog), pass_along );
4248 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
4249 gtk_widget_show ( item );
4250 gtk_widget_set_sensitive ( item, (gboolean)(g_hash_table_size (vtl->tracks)+g_hash_table_size (vtl->routes)) );
00176e85
RN
4251
4252 item = gtk_image_menu_item_new_with_mnemonic ( _("_Waypoint List...") );
4253 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
4254 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_waypoint_list_dialog), pass_along );
4255 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
4256 gtk_widget_show ( item );
4257 gtk_widget_set_sensitive ( item, (gboolean)(g_hash_table_size (vtl->waypoints)) );
50a14534
EB
4258}
4259
c9570f86
RN
4260// Fake Waypoint UUIDs vi simple increasing integer
4261static guint wp_uuid = 0;
4262
50a14534
EB
4263void vik_trw_layer_add_waypoint ( VikTrwLayer *vtl, gchar *name, VikWaypoint *wp )
4264{
c9570f86
RN
4265 wp_uuid++;
4266
4267 vik_waypoint_set_name (wp, name);
4268
50a14534
EB
4269 if ( VIK_LAYER(vtl)->realized )
4270 {
cd3f311e
RN
4271 // Do we need to create the sublayer:
4272 if ( g_hash_table_size (vtl->waypoints) == 0 ) {
4273 trw_layer_add_sublayer_waypoints ( vtl, VIK_LAYER(vtl)->vt, &(VIK_LAYER(vtl)->iter) );
4274 }
4275
c9570f86
RN
4276 GtkTreeIter *iter = g_malloc(sizeof(GtkTreeIter));
4277
4278 // Visibility column always needed for waypoints
c9cac058 4279 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 4280
c9570f86
RN
4281 // Actual setting of visibility dependent on the waypoint
4282 vik_treeview_item_set_visible ( VIK_LAYER(vtl)->vt, iter, wp->visible );
4283
4284 g_hash_table_insert ( vtl->waypoints_iters, GUINT_TO_POINTER(wp_uuid), iter );
7a52aac6
RN
4285
4286 // Sort now as post_read is not called on a realized waypoint
4287 vik_treeview_sort_children ( VIK_LAYER(vtl)->vt, &(vtl->waypoints_iter), vtl->wp_sort_order );
50a14534 4288 }
50a14534 4289
a8fe53f8 4290 highest_wp_number_add_wp(vtl, name);
c9570f86 4291 g_hash_table_insert ( vtl->waypoints, GUINT_TO_POINTER(wp_uuid), wp );
50a14534
EB
4292
4293}
4294
ce4bd1cf
RN
4295// Fake Track UUIDs vi simple increasing integer
4296static guint tr_uuid = 0;
4297
50a14534
EB
4298void vik_trw_layer_add_track ( VikTrwLayer *vtl, gchar *name, VikTrack *t )
4299{
ce4bd1cf
RN
4300 tr_uuid++;
4301
4302 vik_track_set_name (t, name);
4303
50a14534
EB
4304 if ( VIK_LAYER(vtl)->realized )
4305 {
cd3f311e
RN
4306 // Do we need to create the sublayer:
4307 if ( g_hash_table_size (vtl->tracks) == 0 ) {
4308 trw_layer_add_sublayer_tracks ( vtl, VIK_LAYER(vtl)->vt, &(VIK_LAYER(vtl)->iter) );
4309 }
4310
ce4bd1cf
RN
4311 GtkTreeIter *iter = g_malloc(sizeof(GtkTreeIter));
4312 // Visibility column always needed for tracks
c9cac058 4313 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 4314
ce4bd1cf
RN
4315 // Actual setting of visibility dependent on the track
4316 vik_treeview_item_set_visible ( VIK_LAYER(vtl)->vt, iter, t->visible );
4317
4318 g_hash_table_insert ( vtl->tracks_iters, GUINT_TO_POINTER(tr_uuid), iter );
c9cac058
RN
4319
4320 // Sort now as post_read is not called on a realized track
4321 vik_treeview_sort_children ( VIK_LAYER(vtl)->vt, &(vtl->tracks_iter), vtl->track_sort_order );
50a14534 4322 }
50a14534 4323
ce4bd1cf 4324 g_hash_table_insert ( vtl->tracks, GUINT_TO_POINTER(tr_uuid), t );
b1453c16 4325
93ee73b3 4326 trw_layer_update_treeview ( vtl, t );
50a14534
EB
4327}
4328
0d2b891f
RN
4329// Fake Route UUIDs vi simple increasing integer
4330static guint rt_uuid = 0;
4331
4332void vik_trw_layer_add_route ( VikTrwLayer *vtl, gchar *name, VikTrack *t )
4333{
4334 rt_uuid++;
4335
4336 vik_track_set_name (t, name);
4337
4338 if ( VIK_LAYER(vtl)->realized )
4339 {
4340 // Do we need to create the sublayer:
4341 if ( g_hash_table_size (vtl->routes) == 0 ) {
4342 trw_layer_add_sublayer_routes ( vtl, VIK_LAYER(vtl)->vt, &(VIK_LAYER(vtl)->iter) );
4343 }
4344
4345 GtkTreeIter *iter = g_malloc(sizeof(GtkTreeIter));
c9cac058
RN
4346 // Visibility column always needed for routes
4347 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 );
4348 // Actual setting of visibility dependent on the route
0d2b891f
RN
4349 vik_treeview_item_set_visible ( VIK_LAYER(vtl)->vt, iter, t->visible );
4350
4351 g_hash_table_insert ( vtl->routes_iters, GUINT_TO_POINTER(rt_uuid), iter );
c9cac058
RN
4352
4353 // Sort now as post_read is not called on a realized route
4354 vik_treeview_sort_children ( VIK_LAYER(vtl)->vt, &(vtl->routes_iter), vtl->track_sort_order );
0d2b891f
RN
4355 }
4356
4357 g_hash_table_insert ( vtl->routes, GUINT_TO_POINTER(rt_uuid), t );
4358
93ee73b3 4359 trw_layer_update_treeview ( vtl, t );
0d2b891f
RN
4360}
4361
50a14534 4362/* to be called whenever a track has been deleted or may have been changed. */
ce4bd1cf 4363void trw_layer_cancel_tps_of_track ( VikTrwLayer *vtl, VikTrack *trk )
50a14534 4364{
ce4bd1cf 4365 if (vtl->current_tp_track == trk )
50a14534 4366 trw_layer_cancel_current_tp ( vtl, FALSE );
50a14534 4367}
0d2b891f 4368
98acb9a1
RN
4369/**
4370 * Normally this is done to due the waypoint size preference having changed
4371 */
4372void vik_trw_layer_reset_waypoints ( VikTrwLayer *vtl )
4373{
4374 GHashTableIter iter;
4375 gpointer key, value;
4376
4377 // Foreach waypoint
4378 g_hash_table_iter_init ( &iter, vtl->waypoints );
4379 while ( g_hash_table_iter_next ( &iter, &key, &value ) ) {
4380 VikWaypoint *wp = VIK_WAYPOINT(value);
4381 if ( wp->symbol ) {
4382 // Reapply symbol setting to update the pixbuf
4383 gchar *tmp_symbol = g_strdup ( wp->symbol );
4384 vik_waypoint_set_symbol ( wp, tmp_symbol );
4385 g_free ( tmp_symbol );
4386 }
4387 }
4388}
4389
1613e468
RN
4390/**
4391 * trw_layer_new_unique_sublayer_name:
4392 *
4393 * Allocates a unique new name
4394 */
9748531a 4395gchar *trw_layer_new_unique_sublayer_name (VikTrwLayer *vtl, gint sublayer_type, const gchar *name)
e890a6e6 4396{
0d2b891f
RN
4397 gint i = 2;
4398 gchar *newname = g_strdup(name);
4399
4400 gpointer id = NULL;
4401 do {
4402 id = NULL;
4403 switch ( sublayer_type ) {
4404 case VIK_TRW_LAYER_SUBLAYER_TRACK:
4405 id = (gpointer) vik_trw_layer_get_track ( vtl, newname );
4406 break;
4407 case VIK_TRW_LAYER_SUBLAYER_WAYPOINT:
4408 id = (gpointer) vik_trw_layer_get_waypoint ( vtl, newname );
4409 break;
4410 default:
4411 id = (gpointer) vik_trw_layer_get_route ( vtl, newname );
4412 break;
4413 }
4414 // If found a name already in use try adding 1 to it and we try again
4415 if ( id ) {
4416 gchar *new_newname = g_strdup_printf("%s#%d", name, i);
4417 g_free(newname);
4418 newname = new_newname;
4419 i++;
4420 }
4421 } while ( id != NULL);
4422
e890a6e6
EB
4423 return newname;
4424}
50a14534 4425
805d282e
EB
4426void vik_trw_layer_filein_add_waypoint ( VikTrwLayer *vtl, gchar *name, VikWaypoint *wp )
4427{
c9570f86
RN
4428 // No more uniqueness of name forced when loading from a file
4429 // This now makes this function a little redunant as we just flow the parameters through
4430 vik_trw_layer_add_waypoint ( vtl, name, wp );
805d282e 4431}
c9570f86 4432
805d282e
EB
4433void vik_trw_layer_filein_add_track ( VikTrwLayer *vtl, gchar *name, VikTrack *tr )
4434{
ee43b101 4435 if ( vtl->route_finder_append && vtl->current_track ) {
c3deba01 4436 vik_track_remove_dup_points ( tr ); /* make "double point" track work to undo */
ee43b101
MH
4437
4438 // enforce end of current track equal to start of tr
4439 VikTrackpoint *cur_end = vik_track_get_tp_last ( vtl->current_track );
4440 VikTrackpoint *new_start = vik_track_get_tp_first ( tr );
4441 if ( ! vik_coord_equals ( &cur_end->coord, &new_start->coord ) ) {
4442 vik_track_add_trackpoint ( vtl->current_track,
4443 vik_trackpoint_copy ( cur_end ),
4444 FALSE );
4445 }
4446
4447 vik_track_steal_and_append_trackpoints ( vtl->current_track, tr );
bddd2056 4448 vik_track_free ( tr );
7ff7d728 4449 vtl->route_finder_append = FALSE; /* this means we have added it */
bddd2056 4450 } else {
ce4bd1cf
RN
4451
4452 // No more uniqueness of name forced when loading from a file
0d2b891f
RN
4453 if ( tr->is_route )
4454 vik_trw_layer_add_route ( vtl, name, tr );
4455 else
4456 vik_trw_layer_add_track ( vtl, name, tr );
bddd2056 4457
7ff7d728 4458 if ( vtl->route_finder_check_added_track ) {
c3deba01 4459 vik_track_remove_dup_points ( tr ); /* make "double point" track work to undo */
ce4bd1cf 4460 vtl->route_finder_added_track = tr;
bddd2056
EB
4461 }
4462 }
805d282e
EB
4463}
4464
ce4bd1cf 4465static void trw_layer_enum_item ( gpointer id, GList **tr, GList **l )
c48517ad 4466{
ce4bd1cf 4467 *l = g_list_append(*l, id);
c48517ad
AF
4468}
4469
ce4bd1cf
RN
4470/*
4471 * Move an item from one TRW layer to another TRW layer
4472 */
4473static void trw_layer_move_item ( VikTrwLayer *vtl_src, VikTrwLayer *vtl_dest, gpointer id, gint type )
c48517ad 4474{
c9cac058 4475 // TODO reconsider strategy when moving within layer (if anything...)
762cb457 4476 gboolean rename = ( vtl_src != vtl_dest );
c9cac058
RN
4477 if ( ! rename )
4478 return;
762cb457 4479
c48517ad 4480 if (type == VIK_TRW_LAYER_SUBLAYER_TRACK) {
ce4bd1cf
RN
4481 VikTrack *trk = g_hash_table_lookup ( vtl_src->tracks, id );
4482
762cb457
RN
4483 gchar *newname;
4484 if ( rename )
4485 newname = trw_layer_new_unique_sublayer_name ( vtl_dest, type, trk->name );
4486 else
4487 newname = g_strdup ( trk->name );
ce4bd1cf 4488
03817fbf 4489 VikTrack *trk2 = vik_track_copy ( trk, TRUE );
ce4bd1cf 4490 vik_trw_layer_add_track ( vtl_dest, newname, trk2 );
1613e468 4491 g_free ( newname );
ce4bd1cf 4492 vik_trw_layer_delete_track ( vtl_src, trk );
c48517ad 4493 }
c9570f86 4494
0d2b891f
RN
4495 if (type == VIK_TRW_LAYER_SUBLAYER_ROUTE) {
4496 VikTrack *trk = g_hash_table_lookup ( vtl_src->routes, id );
4497
762cb457
RN
4498 gchar *newname;
4499 if ( rename )
4500 newname = trw_layer_new_unique_sublayer_name ( vtl_dest, type, trk->name );
4501 else
4502 newname = g_strdup ( trk->name );
0d2b891f 4503
03817fbf 4504 VikTrack *trk2 = vik_track_copy ( trk, TRUE );
0d2b891f 4505 vik_trw_layer_add_route ( vtl_dest, newname, trk2 );
1613e468 4506 g_free ( newname );
0d2b891f
RN
4507 vik_trw_layer_delete_route ( vtl_src, trk );
4508 }
4509
ce4bd1cf
RN
4510 if (type == VIK_TRW_LAYER_SUBLAYER_WAYPOINT) {
4511 VikWaypoint *wp = g_hash_table_lookup ( vtl_src->waypoints, id );
c9570f86 4512
762cb457
RN
4513 gchar *newname;
4514 if ( rename )
4515 newname = trw_layer_new_unique_sublayer_name ( vtl_dest, type, wp->name );
4516 else
4517 newname = g_strdup ( wp->name );
c9570f86 4518
ce4bd1cf
RN
4519 VikWaypoint *wp2 = vik_waypoint_copy ( wp );
4520 vik_trw_layer_add_waypoint ( vtl_dest, newname, wp2 );
1613e468 4521 g_free ( newname );
c9570f86 4522 trw_layer_delete_waypoint ( vtl_src, wp );
aa0665e5 4523
26e66078
RN
4524 // Recalculate bounds even if not renamed as maybe dragged between layers
4525 trw_layer_calculate_bounds_waypoints ( vtl_dest );
4526 trw_layer_calculate_bounds_waypoints ( vtl_src );
c48517ad
AF
4527 }
4528}
4529
70a23263 4530static void trw_layer_drag_drop_request ( VikTrwLayer *vtl_src, VikTrwLayer *vtl_dest, GtkTreeIter *src_item_iter, GtkTreePath *dest_path )
e4afc73a
EB
4531{
4532 VikTreeview *vt = VIK_LAYER(vtl_src)->vt;
c48517ad
AF
4533 gint type = vik_treeview_item_get_data(vt, src_item_iter);
4534
e4afc73a 4535 if (!vik_treeview_item_get_pointer(vt, src_item_iter)) {
b637058e
EB
4536 GList *items = NULL;
4537 GList *iter;
e4afc73a 4538
c48517ad
AF
4539 if (type==VIK_TRW_LAYER_SUBLAYER_TRACKS) {
4540 g_hash_table_foreach ( vtl_src->tracks, (GHFunc)trw_layer_enum_item, &items);
4541 }
4542 if (type==VIK_TRW_LAYER_SUBLAYER_WAYPOINTS) {
4543 g_hash_table_foreach ( vtl_src->waypoints, (GHFunc)trw_layer_enum_item, &items);
4544 }
0d2b891f
RN
4545 if (type==VIK_TRW_LAYER_SUBLAYER_ROUTES) {
4546 g_hash_table_foreach ( vtl_src->routes, (GHFunc)trw_layer_enum_item, &items);
4547 }
4548
c48517ad
AF
4549 iter = items;
4550 while (iter) {
4551 if (type==VIK_TRW_LAYER_SUBLAYER_TRACKS) {
0d2b891f 4552 trw_layer_move_item ( vtl_src, vtl_dest, iter->data, VIK_TRW_LAYER_SUBLAYER_TRACK);
c9cac058 4553 } else if (type==VIK_TRW_LAYER_SUBLAYER_ROUTES) {
0d2b891f 4554 trw_layer_move_item ( vtl_src, vtl_dest, iter->data, VIK_TRW_LAYER_SUBLAYER_ROUTE);
c48517ad 4555 } else {
0d2b891f 4556 trw_layer_move_item ( vtl_src, vtl_dest, iter->data, VIK_TRW_LAYER_SUBLAYER_WAYPOINT);
c48517ad
AF
4557 }
4558 iter = iter->next;
e4afc73a 4559 }
c48517ad 4560 if (items)
b637058e 4561 g_list_free(items);
c48517ad
AF
4562 } else {
4563 gchar *name = vik_treeview_item_get_pointer(vt, src_item_iter);
4564 trw_layer_move_item(vtl_src, vtl_dest, name, type);
e4afc73a
EB
4565 }
4566}
4567
c98e2f73 4568gboolean trw_layer_track_find_uuid ( const gpointer id, const VikTrack *trk, gpointer udata )
ce4bd1cf
RN
4569{
4570 trku_udata *user_data = udata;
4571 if ( trk == user_data->trk ) {
4572 user_data->uuid = id;
4573 return TRUE;
4574 }
4575 return FALSE;
4576}
4577
4578gboolean vik_trw_layer_delete_track ( VikTrwLayer *vtl, VikTrack *trk )
50a14534 4579{
50a14534 4580 gboolean was_visible = FALSE;
ce4bd1cf
RN
4581 if ( trk && trk->name ) {
4582
4583 if ( trk == vtl->current_track ) {
50a14534 4584 vtl->current_track = NULL;
ce4bd1cf
RN
4585 vtl->current_tp_track = NULL;
4586 vtl->current_tp_id = NULL;
4587 vtl->moving_tp = FALSE;
ee43b101 4588 vtl->route_finder_started = FALSE;
77ad64fa 4589 }
ce4bd1cf
RN
4590
4591 was_visible = trk->visible;
4592
ce4bd1cf
RN
4593 if ( trk == vtl->route_finder_added_track )
4594 vtl->route_finder_added_track = NULL;
4595
4596 trku_udata udata;
4597 udata.trk = trk;
4598 udata.uuid = NULL;
50a14534 4599
ce4bd1cf
RN
4600 // Hmmm, want key of it
4601 gpointer *trkf = g_hash_table_find ( vtl->tracks, (GHRFunc) trw_layer_track_find_uuid, &udata );
50a14534 4602
ce4bd1cf
RN
4603 if ( trkf && udata.uuid ) {
4604 /* could be current_tp, so we have to check */
4605 trw_layer_cancel_tps_of_track ( vtl, trk );
4606
4607 GtkTreeIter *it = g_hash_table_lookup ( vtl->tracks_iters, udata.uuid );
4608
4609 if ( it ) {
4610 vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, it );
4611 g_hash_table_remove ( vtl->tracks_iters, udata.uuid );
4612 g_hash_table_remove ( vtl->tracks, udata.uuid );
cd3f311e
RN
4613
4614 // If last sublayer, then remove sublayer container
4615 if ( g_hash_table_size (vtl->tracks) == 0 ) {
4616 vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, &(vtl->tracks_iter) );
4617 }
ce4bd1cf 4618 }
bc07590a
RN
4619 // Incase it was selected (no item delete signal ATM)
4620 vik_window_clear_highlight ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)) );
ce4bd1cf 4621 }
50a14534
EB
4622 }
4623 return was_visible;
4624}
4625
0d2b891f
RN
4626gboolean vik_trw_layer_delete_route ( VikTrwLayer *vtl, VikTrack *trk )
4627{
4628 gboolean was_visible = FALSE;
4629
4630 if ( trk && trk->name ) {
4631
4632 if ( trk == vtl->current_track ) {
4633 vtl->current_track = NULL;
4634 vtl->current_tp_track = NULL;
4635 vtl->current_tp_id = NULL;
4636 vtl->moving_tp = FALSE;
4637 }
4638
4639 was_visible = trk->visible;
4640
0d2b891f
RN
4641 if ( trk == vtl->route_finder_added_track )
4642 vtl->route_finder_added_track = NULL;
4643
4644 trku_udata udata;
4645 udata.trk = trk;
4646 udata.uuid = NULL;
4647
4648 // Hmmm, want key of it
4649 gpointer *trkf = g_hash_table_find ( vtl->routes, (GHRFunc) trw_layer_track_find_uuid, &udata );
4650
4651 if ( trkf && udata.uuid ) {
4652 /* could be current_tp, so we have to check */
4653 trw_layer_cancel_tps_of_track ( vtl, trk );
4654
4655 GtkTreeIter *it = g_hash_table_lookup ( vtl->routes_iters, udata.uuid );
4656
4657 if ( it ) {
4658 vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, it );
4659 g_hash_table_remove ( vtl->routes_iters, udata.uuid );
4660 g_hash_table_remove ( vtl->routes, udata.uuid );
4661
4662 // If last sublayer, then remove sublayer container
4663 if ( g_hash_table_size (vtl->routes) == 0 ) {
4664 vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, &(vtl->routes_iter) );
4665 }
4666 }
bc07590a
RN
4667 // Incase it was selected (no item delete signal ATM)
4668 vik_window_clear_highlight ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)) );
0d2b891f
RN
4669 }
4670 }
4671 return was_visible;
4672}
4673
c9570f86 4674static gboolean trw_layer_delete_waypoint ( VikTrwLayer *vtl, VikWaypoint *wp )
e4afc73a
EB
4675{
4676 gboolean was_visible = FALSE;
e4afc73a 4677
c9570f86 4678 if ( wp && wp->name ) {
e4afc73a
EB
4679
4680 if ( wp == vtl->current_wp ) {
4681 vtl->current_wp = NULL;
c9570f86 4682 vtl->current_wp_id = NULL;
e4afc73a
EB
4683 vtl->moving_wp = FALSE;
4684 }
4685
4686 was_visible = wp->visible;
c9570f86
RN
4687
4688 wpu_udata udata;
4689 udata.wp = wp;
4690 udata.uuid = NULL;
4691
4692 // Hmmm, want key of it
4693 gpointer *wpf = g_hash_table_find ( vtl->waypoints, (GHRFunc) trw_layer_waypoint_find_uuid, (gpointer) &udata );
4694
4695 if ( wpf && udata.uuid ) {
4696 GtkTreeIter *it = g_hash_table_lookup ( vtl->waypoints_iters, udata.uuid );
4697
4698 if ( it ) {
4699 vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, it );
4700 g_hash_table_remove ( vtl->waypoints_iters, udata.uuid );
4701
4702 highest_wp_number_remove_wp(vtl, wp->name);
4703 g_hash_table_remove ( vtl->waypoints, udata.uuid ); // last because this frees the name
cd3f311e
RN
4704
4705 // If last sublayer, then remove sublayer container
4706 if ( g_hash_table_size (vtl->waypoints) == 0 ) {
4707 vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, &(vtl->waypoints_iter) );
4708 }
c9570f86 4709 }
bc07590a
RN
4710 // Incase it was selected (no item delete signal ATM)
4711 vik_window_clear_highlight ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)) );
c9570f86 4712 }
a8fe53f8 4713
e4afc73a
EB
4714 }
4715
4716 return was_visible;
4717}
4718
c9570f86
RN
4719// Only for temporary use by trw_layer_delete_waypoint_by_name
4720static gboolean trw_layer_waypoint_find_uuid_by_name ( const gpointer id, const VikWaypoint *wp, gpointer udata )
4721{
4722 wpu_udata *user_data = udata;
4723 if ( ! strcmp ( wp->name, user_data->wp->name ) ) {
4724 user_data->uuid = id;
4725 return TRUE;
4726 }
4727 return FALSE;
4728}
4729
4730/*
4731 * Delete a waypoint by the given name
4732 * NOTE: ATM this will delete the first encountered Waypoint with the specified name
4733 * as there be multiple waypoints with the same name
4734 */
4735static gboolean trw_layer_delete_waypoint_by_name ( VikTrwLayer *vtl, const gchar *name )
4736{
4737 wpu_udata udata;
4738 // Fake a waypoint with the given name
4739 udata.wp = vik_waypoint_new ();
4740 vik_waypoint_set_name (udata.wp, name);
4741 // Currently only the name is used in this waypoint find function
4742 udata.uuid = NULL;
4743
4744 // Hmmm, want key of it
4745 gpointer *wpf = g_hash_table_find ( vtl->waypoints, (GHRFunc) trw_layer_waypoint_find_uuid_by_name, (gpointer) &udata );
4746
4747 vik_waypoint_free (udata.wp);
4748
4749 if ( wpf && udata.uuid )
4750 return trw_layer_delete_waypoint (vtl, g_hash_table_lookup ( vtl->waypoints, udata.uuid ));
4751 else
4752 return FALSE;
4753}
4754
ce4bd1cf
RN
4755typedef struct {
4756 VikTrack *trk; // input
4757 gpointer uuid; // output
4758} tpu_udata;
4759
4760// Only for temporary use by trw_layer_delete_track_by_name
4761static gboolean trw_layer_track_find_uuid_by_name ( const gpointer id, const VikTrack *trk, gpointer udata )
4762{
4763 tpu_udata *user_data = udata;
4764 if ( ! strcmp ( trk->name, user_data->trk->name ) ) {
4765 user_data->uuid = id;
4766 return TRUE;
4767 }
4768 return FALSE;
4769}
4770
4771/*
4772 * Delete a track by the given name
4773 * NOTE: ATM this will delete the first encountered Track with the specified name
0d2b891f 4774 * as there may be multiple tracks with the same name within the specified hash table
ce4bd1cf 4775 */
0d2b891f 4776static gboolean trw_layer_delete_track_by_name ( VikTrwLayer *vtl, const gchar *name, GHashTable *ht_tracks )
ce4bd1cf
RN
4777{
4778 tpu_udata udata;
4779 // Fake a track with the given name
4780 udata.trk = vik_track_new ();
4781 vik_track_set_name (udata.trk, name);
4782 // Currently only the name is used in this waypoint find function
4783 udata.uuid = NULL;
4784
4785 // Hmmm, want key of it
0d2b891f 4786 gpointer *trkf = g_hash_table_find ( ht_tracks, (GHRFunc) trw_layer_track_find_uuid_by_name, &udata );
ce4bd1cf
RN
4787
4788 vik_track_free (udata.trk);
4789
0d2b891f
RN
4790 if ( trkf && udata.uuid ) {
4791 // This could be a little better written...
4792 if ( vtl->tracks == ht_tracks )
4793 return vik_trw_layer_delete_track (vtl, g_hash_table_lookup ( ht_tracks, udata.uuid ));
4794 if ( vtl->routes == ht_tracks )
4795 return vik_trw_layer_delete_route (vtl, g_hash_table_lookup ( ht_tracks, udata.uuid ));
4796 return FALSE;
4797 }
ce4bd1cf
RN
4798 else
4799 return FALSE;
4800}
4801
9e212bfc 4802static void remove_item_from_treeview ( const gpointer id, GtkTreeIter *it, VikTreeview * vt )
700b0908
QT
4803{
4804 vik_treeview_item_delete (vt, it );
4805}
4806
0d2b891f
RN
4807void vik_trw_layer_delete_all_routes ( VikTrwLayer *vtl )
4808{
4809
4810 vtl->current_track = NULL;
0d2b891f
RN
4811 vtl->route_finder_added_track = NULL;
4812 if (vtl->current_tp_track)
4813 trw_layer_cancel_current_tp(vtl, FALSE);
4814
4815 g_hash_table_foreach(vtl->routes_iters, (GHFunc) remove_item_from_treeview, VIK_LAYER(vtl)->vt);
4816 g_hash_table_remove_all(vtl->routes_iters);
4817 g_hash_table_remove_all(vtl->routes);
4818
4819 vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, &(vtl->routes_iter) );
4820
da121f9b 4821 vik_layer_emit_update ( VIK_LAYER(vtl) );
0d2b891f
RN
4822}
4823
700b0908
QT
4824void vik_trw_layer_delete_all_tracks ( VikTrwLayer *vtl )
4825{
4826
4827 vtl->current_track = NULL;
ce4bd1cf
RN
4828 vtl->route_finder_added_track = NULL;
4829 if (vtl->current_tp_track)
700b0908 4830 trw_layer_cancel_current_tp(vtl, FALSE);
700b0908
QT
4831
4832 g_hash_table_foreach(vtl->tracks_iters, (GHFunc) remove_item_from_treeview, VIK_LAYER(vtl)->vt);
4833 g_hash_table_remove_all(vtl->tracks_iters);
4834 g_hash_table_remove_all(vtl->tracks);
4835
cd3f311e
RN
4836 vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, &(vtl->tracks_iter) );
4837
da121f9b 4838 vik_layer_emit_update ( VIK_LAYER(vtl) );
700b0908
QT
4839}
4840
4841void vik_trw_layer_delete_all_waypoints ( VikTrwLayer *vtl )
4842{
4843 vtl->current_wp = NULL;
c9570f86 4844 vtl->current_wp_id = NULL;
700b0908
QT
4845 vtl->moving_wp = FALSE;
4846
a8fe53f8
EB
4847 highest_wp_number_reset(vtl);
4848
700b0908
QT
4849 g_hash_table_foreach(vtl->waypoints_iters, (GHFunc) remove_item_from_treeview, VIK_LAYER(vtl)->vt);
4850 g_hash_table_remove_all(vtl->waypoints_iters);
4851 g_hash_table_remove_all(vtl->waypoints);
4852
cd3f311e
RN
4853 vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, &(vtl->waypoints_iter) );
4854
da121f9b 4855 vik_layer_emit_update ( VIK_LAYER(vtl) );
700b0908
QT
4856}
4857
1c2083ba 4858static void trw_layer_delete_all_tracks ( menu_array_layer values )
c9a5cbf9 4859{
1c2083ba 4860 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
c9a5cbf9
RN
4861 // Get confirmation from the user
4862 if ( a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
4863 _("Are you sure you want to delete all tracks in %s?"),
4864 vik_layer_get_name ( VIK_LAYER(vtl) ) ) )
4865 vik_trw_layer_delete_all_tracks (vtl);
4866}
4867
1c2083ba 4868static void trw_layer_delete_all_routes ( menu_array_layer values )
c9a5cbf9 4869{
1c2083ba 4870 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
c9a5cbf9
RN
4871 // Get confirmation from the user
4872 if ( a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
0d2b891f
RN
4873 _("Are you sure you want to delete all routes in %s?"),
4874 vik_layer_get_name ( VIK_LAYER(vtl) ) ) )
4875 vik_trw_layer_delete_all_routes (vtl);
c9a5cbf9
RN
4876}
4877
1c2083ba 4878static void trw_layer_delete_all_waypoints ( menu_array_layer values )
0d2b891f 4879{
1c2083ba 4880 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
0d2b891f
RN
4881 // Get confirmation from the user
4882 if ( a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
4883 _("Are you sure you want to delete all waypoints in %s?"),
4884 vik_layer_get_name ( VIK_LAYER(vtl) ) ) )
4885 vik_trw_layer_delete_all_waypoints (vtl);
4886}
4887
19782ffd 4888static void trw_layer_delete_item ( menu_array_sublayer values )
50a14534 4889{
19782ffd 4890 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
50a14534 4891 gboolean was_visible = FALSE;
19782ffd 4892 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_WAYPOINT )
50a14534 4893 {
19782ffd 4894 VikWaypoint *wp = g_hash_table_lookup ( vtl->waypoints, values[MA_SUBLAYER_ID] );
c9570f86 4895 if ( wp && wp->name ) {
19782ffd 4896 if ( GPOINTER_TO_INT (values[MA_CONFIRM]) )
c9570f86
RN
4897 // Get confirmation from the user
4898 // Maybe this Waypoint Delete should be optional as is it could get annoying...
4899 if ( ! a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
ea3a6072 4900 _("Are you sure you want to delete the waypoint \"%s\"?"),
c9570f86
RN
4901 wp->name ) )
4902 return;
4903 was_visible = trw_layer_delete_waypoint ( vtl, wp );
4904 }
50a14534 4905 }
19782ffd 4906 else if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_TRACK )
50a14534 4907 {
19782ffd 4908 VikTrack *trk = g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
ce4bd1cf 4909 if ( trk && trk->name ) {
19782ffd 4910 if ( GPOINTER_TO_INT (values[MA_CONFIRM]) )
ce4bd1cf
RN
4911 // Get confirmation from the user
4912 if ( ! a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
ea3a6072 4913 _("Are you sure you want to delete the track \"%s\"?"),
ce4bd1cf
RN
4914 trk->name ) )
4915 return;
4916 was_visible = vik_trw_layer_delete_track ( vtl, trk );
4917 }
50a14534 4918 }
0d2b891f
RN
4919 else
4920 {
19782ffd 4921 VikTrack *trk = g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
0d2b891f 4922 if ( trk && trk->name ) {
19782ffd 4923 if ( GPOINTER_TO_INT (values[MA_CONFIRM]) )
0d2b891f
RN
4924 // Get confirmation from the user
4925 if ( ! a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
ea3a6072 4926 _("Are you sure you want to delete the route \"%s\"?"),
0d2b891f
RN
4927 trk->name ) )
4928 return;
4929 was_visible = vik_trw_layer_delete_route ( vtl, trk );
4930 }
4931 }
50a14534 4932 if ( was_visible )
da121f9b 4933 vik_layer_emit_update ( VIK_LAYER(vtl) );
50a14534
EB
4934}
4935
6761d8a4
RN
4936/**
4937 * Rename waypoint and maintain corresponding name of waypoint in the treeview
4938 */
4f556508 4939void trw_layer_waypoint_rename ( VikTrwLayer *vtl, VikWaypoint *wp, const gchar *new_name )
6761d8a4
RN
4940{
4941 vik_waypoint_set_name ( wp, new_name );
4942
4943 // Now update the treeview as well
4944 wpu_udata udataU;
4945 udataU.wp = wp;
4946 udataU.uuid = NULL;
4947
4948 // Need key of it for treeview update
4949 gpointer *wpf = g_hash_table_find ( vtl->waypoints, (GHRFunc) trw_layer_waypoint_find_uuid, &udataU );
4950
4951 if ( wpf && udataU.uuid ) {
4952 GtkTreeIter *it = g_hash_table_lookup ( vtl->waypoints_iters, udataU.uuid );
4953
4954 if ( it ) {
4955 vik_treeview_item_set_name ( VIK_LAYER(vtl)->vt, it, new_name );
c9cac058 4956 vik_treeview_sort_children ( VIK_LAYER(vtl)->vt, &(vtl->waypoints_iter), vtl->wp_sort_order );
6761d8a4
RN
4957 }
4958 }
4959}
50a14534 4960
4f556508
RN
4961/**
4962 * Maintain icon of waypoint in the treeview
4963 */
4964void trw_layer_waypoint_reset_icon ( VikTrwLayer *vtl, VikWaypoint *wp )
4965{
4966 // update the treeview
4967 wpu_udata udataU;
4968 udataU.wp = wp;
4969 udataU.uuid = NULL;
4970
4971 // Need key of it for treeview update
4972 gpointer *wpf = g_hash_table_find ( vtl->waypoints, (GHRFunc) trw_layer_waypoint_find_uuid, &udataU );
4973
4974 if ( wpf && udataU.uuid ) {
4975 GtkTreeIter *it = g_hash_table_lookup ( vtl->waypoints_iters, udataU.uuid );
4976
4977 if ( it ) {
4978 vik_treeview_item_set_icon ( VIK_LAYER(vtl)->vt, it, get_wp_sym_small (wp->symbol) );
4979 }
4980 }
4981}
4982
19782ffd 4983static void trw_layer_properties_item ( menu_array_sublayer values )
50a14534 4984{
19782ffd
RN
4985 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
4986 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_WAYPOINT )
50a14534 4987 {
19782ffd 4988 VikWaypoint *wp = g_hash_table_lookup ( vtl->waypoints, values[MA_SUBLAYER_ID] );
c9570f86
RN
4989
4990 if ( wp && wp->name )
50a14534 4991 {
ac1bde8b 4992 gboolean updated = FALSE;
6761d8a4
RN
4993 gchar *new_name = a_dialog_waypoint ( VIK_GTK_WINDOW_FROM_LAYER(vtl), wp->name, vtl, wp, vtl->coord_mode, FALSE, &updated );
4994 if ( new_name )
4995 trw_layer_waypoint_rename ( vtl, wp, new_name );
50a14534 4996
19782ffd
RN
4997 if ( updated && values[MA_TV_ITER] )
4998 vik_treeview_item_set_icon ( VIK_LAYER(vtl)->vt, values[MA_TV_ITER], get_wp_sym_small (wp->symbol) );
70cefedc 4999
ac1bde8b 5000 if ( updated && VIK_LAYER(vtl)->visible )
da121f9b 5001 vik_layer_emit_update ( VIK_LAYER(vtl) );
50a14534
EB
5002 }
5003 }
5004 else
5005 {
0d2b891f 5006 VikTrack *tr;
19782ffd
RN
5007 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_TRACK )
5008 tr = g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
0d2b891f 5009 else
19782ffd 5010 tr = g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
0d2b891f 5011
ce4bd1cf 5012 if ( tr && tr->name )
50a14534 5013 {
21700912 5014 vik_trw_layer_propwin_run ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
19782ffd 5015 vtl,
b1453c16 5016 tr,
19782ffd
RN
5017 values[MA_VLP],
5018 values[MA_VVP],
626de648 5019 FALSE );
50a14534
EB
5020 }
5021 }
5022}
5023
626de648
RN
5024/**
5025 * trw_layer_track_statistics:
5026 *
5027 * Show track statistics.
5028 * ATM jump to the stats page in the properties
5029 * TODO: consider separating the stats into an individual dialog?
5030 */
19782ffd 5031static void trw_layer_track_statistics ( menu_array_sublayer values )
626de648 5032{
19782ffd 5033 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
626de648 5034 VikTrack *trk;
19782ffd
RN
5035 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_TRACK )
5036 trk = g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
626de648 5037 else
19782ffd 5038 trk = g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
626de648
RN
5039
5040 if ( trk && trk->name ) {
5041 vik_trw_layer_propwin_run ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
5042 vtl,
5043 trk,
19782ffd
RN
5044 values[MA_VLP],
5045 values[MA_VVP],
626de648
RN
5046 TRUE );
5047 }
5048}
5049
b1453c16
RN
5050/*
5051 * Update the treeview of the track id - primarily to update the icon
5052 */
93ee73b3 5053void trw_layer_update_treeview ( VikTrwLayer *vtl, VikTrack *trk )
b1453c16
RN
5054{
5055 trku_udata udata;
5056 udata.trk = trk;
5057 udata.uuid = NULL;
5058
5059 gpointer *trkf = NULL;
5060 if ( trk->is_route )
5061 trkf = g_hash_table_find ( vtl->routes, (GHRFunc) trw_layer_track_find_uuid, &udata );
5062 else
5063 trkf = g_hash_table_find ( vtl->tracks, (GHRFunc) trw_layer_track_find_uuid, &udata );
5064
5065 if ( trkf && udata.uuid ) {
5066
5067 GtkTreeIter *iter = NULL;
5068 if ( trk->is_route )
5069 iter = g_hash_table_lookup ( vtl->routes_iters, udata.uuid );
5070 else
5071 iter = g_hash_table_lookup ( vtl->tracks_iters, udata.uuid );
5072
5073 if ( iter ) {
5074 // TODO: Make this a function
5075 GdkPixbuf *pixbuf = gdk_pixbuf_new ( GDK_COLORSPACE_RGB, FALSE, 8, 18, 18);
5076 guint32 pixel = ((trk->color.red & 0xff00) << 16) |
5077 ((trk->color.green & 0xff00) << 8) |
5078 (trk->color.blue & 0xff00);
5079 gdk_pixbuf_fill ( pixbuf, pixel );
5080 vik_treeview_item_set_icon ( VIK_LAYER(vtl)->vt, iter, pixbuf );
5081 g_object_unref (pixbuf);
5082 }
5083
5084 }
5085}
5086
6bb72350
RN
5087/*
5088 Parameter 1 -> VikLayersPanel
5089 Parameter 2 -> VikLayer
5090 Parameter 3 -> VikViewport
5091*/
5092static void goto_coord ( gpointer *vlp, gpointer vl, gpointer vvp, const VikCoord *coord )
5093{
5094 if ( vlp ) {
be5554c5 5095 vik_viewport_set_center_coord ( vik_layers_panel_get_viewport (VIK_LAYERS_PANEL(vlp)), coord, TRUE );
6bb72350
RN
5096 vik_layers_panel_emit_update ( VIK_LAYERS_PANEL(vlp) );
5097 }
5098 else {
5099 /* since vlp not set, vl & vvp should be valid instead! */
5100 if ( vl && vvp ) {
be5554c5 5101 vik_viewport_set_center_coord ( VIK_VIEWPORT(vvp), coord, TRUE );
da121f9b 5102 vik_layer_emit_update ( VIK_LAYER(vl) );
6bb72350
RN
5103 }
5104 }
50a14534
EB
5105}
5106
19782ffd 5107static void trw_layer_goto_track_startpoint ( menu_array_sublayer values )
50a14534 5108{
19782ffd 5109 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
0d2b891f 5110 VikTrack *track;
19782ffd
RN
5111 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5112 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
0d2b891f 5113 else
19782ffd 5114 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
0d2b891f
RN
5115
5116 if ( track && track->trackpoints )
215ebe59 5117 goto_coord ( values[MA_VLP], vtl, values[MA_VVP], &(vik_track_get_tp_first(track)->coord) );
50a14534
EB
5118}
5119
19782ffd 5120static void trw_layer_goto_track_center ( menu_array_sublayer values )
50a14534 5121{
19782ffd 5122 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
0d2b891f 5123 VikTrack *track;
19782ffd
RN
5124 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5125 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
0d2b891f 5126 else
19782ffd 5127 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
0d2b891f 5128
867f4673 5129 if ( track && track->trackpoints )
50a14534
EB
5130 {
5131 struct LatLon average, maxmin[2] = { {0,0}, {0,0} };
5132 VikCoord coord;
867f4673 5133 trw_layer_find_maxmin_tracks ( NULL, track, maxmin );
50a14534
EB
5134 average.lat = (maxmin[0].lat+maxmin[1].lat)/2;
5135 average.lon = (maxmin[0].lon+maxmin[1].lon)/2;
0d2b891f 5136 vik_coord_load_from_latlon ( &coord, vtl->coord_mode, &average );
19782ffd 5137 goto_coord ( values[MA_VLP], vtl, values[MA_VVP], &coord);
50a14534
EB
5138 }
5139}
5140
19782ffd 5141static void trw_layer_convert_track_route ( menu_array_sublayer values )
2f5d7ea1 5142{
19782ffd 5143 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
2f5d7ea1 5144 VikTrack *trk;
19782ffd
RN
5145 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5146 trk = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
2f5d7ea1 5147 else
19782ffd 5148 trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
2f5d7ea1
RN
5149
5150 if ( !trk )
5151 return;
5152
5153 // Converting a track to a route can be a bit more complicated,
5154 // so give a chance to change our minds:
5155 if ( !trk->is_route &&
5156 ( ( vik_track_get_segment_count ( trk ) > 1 ) ||
5157 ( vik_track_get_average_speed ( trk ) > 0.0 ) ) ) {
5158
5159 if ( ! a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
5160 _("Converting a track to a route removes extra track data such as segments, timestamps, etc...\nDo you want to continue?"), NULL ) )
5161 return;
5162}
5163
5164 // Copy it
03817fbf 5165 VikTrack *trk_copy = vik_track_copy ( trk, TRUE );
2f5d7ea1
RN
5166
5167 // Convert
5168 trk_copy->is_route = !trk_copy->is_route;
5169
5170 // ATM can't set name to self - so must create temporary copy
5171 gchar *name = g_strdup ( trk_copy->name );
5172
5173 // Delete old one and then add new one
5174 if ( trk->is_route ) {
5175 vik_trw_layer_delete_route ( vtl, trk );
5176 vik_trw_layer_add_track ( vtl, name, trk_copy );
5177 }
5178 else {
5179 // Extra route conversion bits...
5180 vik_track_merge_segments ( trk_copy );
5181 vik_track_to_routepoints ( trk_copy );
5182
5183 vik_trw_layer_delete_track ( vtl, trk );
5184 vik_trw_layer_add_route ( vtl, name, trk_copy );
5185 }
5186 g_free ( name );
5187
5188 // Update in case color of track / route changes when moving between sublayers
19782ffd 5189 vik_layer_emit_update ( VIK_LAYER(vtl) );
2f5d7ea1
RN
5190}
5191
19782ffd 5192static void trw_layer_anonymize_times ( menu_array_sublayer values )
76b14439 5193{
19782ffd 5194 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
76b14439 5195 VikTrack *track;
19782ffd
RN
5196 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5197 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
76b14439 5198 else
19782ffd 5199 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
76b14439
RN
5200
5201 if ( track )
5202 vik_track_anonymize_times ( track );
5203}
2f5d7ea1 5204
19782ffd 5205static void trw_layer_extend_track_end ( menu_array_sublayer values )
8fb71d6c 5206{
19782ffd 5207 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
0d2b891f 5208 VikTrack *track;
19782ffd
RN
5209 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5210 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
0d2b891f 5211 else
19782ffd 5212 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
0d2b891f
RN
5213
5214 if ( !track )
5215 return;
8fb71d6c
EB
5216
5217 vtl->current_track = track;
e37b2a6d 5218 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
5219
5220 if ( track->trackpoints )
215ebe59 5221 goto_coord ( values[MA_VLP], vtl, values[MA_VVP], &(vik_track_get_tp_last(track)->coord) );
8fb71d6c
EB
5222}
5223
e3154bef 5224/**
7ff7d728 5225 * extend a track using route finder
e3154bef 5226 */
19782ffd 5227static void trw_layer_extend_track_end_route_finder ( menu_array_sublayer values )
a7955c1d 5228{
19782ffd
RN
5229 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
5230 VikTrack *track = g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
008e972c
RN
5231 if ( !track )
5232 return;
a7955c1d 5233
37baade6 5234 vik_window_enable_layer_tool ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)), VIK_LAYER_TRW, TOOL_ROUTE_FINDER );
ee43b101 5235 vtl->current_track = track;
7ff7d728 5236 vtl->route_finder_started = TRUE;
a7955c1d 5237
ee43b101
MH
5238 if ( track->trackpoints )
5239 goto_coord ( values[MA_VLP], vtl, values[MA_VVP], &vik_track_get_tp_last(track)->coord );
a7955c1d 5240}
a7955c1d 5241
4d333042 5242/**
4d333042 5243 *
4d333042 5244 */
667fda15 5245static gboolean trw_layer_dem_test ( VikTrwLayer *vtl, VikLayersPanel *vlp )
4d333042 5246{
73747c42
RN
5247 // If have a vlp then perform a basic test to see if any DEM info available...
5248 if ( vlp ) {
5249 GList *dems = vik_layers_panel_get_all_layers_of_type (vlp, VIK_LAYER_DEM, TRUE); // Includes hidden DEM layer types
5250
5251 if ( !g_list_length(dems) ) {
5252 a_dialog_error_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), _("No DEM layers available, thus no DEM values can be applied.") );
667fda15 5253 return FALSE;
73747c42
RN
5254 }
5255 }
667fda15
RN
5256 return TRUE;
5257}
5258
5259/**
5260 * apply_dem_data_common:
5261 *
5262 * A common function for applying the DEM values and reporting the results.
5263 */
5264static void apply_dem_data_common ( VikTrwLayer *vtl, VikLayersPanel *vlp, VikTrack *track, gboolean skip_existing_elevations )
5265{
5266 if ( !trw_layer_dem_test ( vtl, vlp ) )
5267 return;
73747c42 5268
4d333042
RN
5269 gulong changed = vik_track_apply_dem_data ( track, skip_existing_elevations );
5270 // Inform user how much was changed
5271 gchar str[64];
5272 const gchar *tmp_str = ngettext("%ld point adjusted", "%ld points adjusted", changed);
5273 g_snprintf(str, 64, tmp_str, changed);
5274 a_dialog_info_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), str);
5275}
5276
19782ffd 5277static void trw_layer_apply_dem_data_all ( menu_array_sublayer values )
4d333042 5278{
19782ffd 5279 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
4d333042 5280 VikTrack *track;
19782ffd
RN
5281 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5282 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
4d333042 5283 else
19782ffd 5284 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
4d333042
RN
5285
5286 if ( track )
19782ffd 5287 apply_dem_data_common ( vtl, values[MA_VLP], track, FALSE );
4d333042
RN
5288}
5289
19782ffd 5290static void trw_layer_apply_dem_data_only_missing ( menu_array_sublayer values )
ad0a8c2d 5291{
19782ffd 5292 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
0d2b891f 5293 VikTrack *track;
19782ffd
RN
5294 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5295 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
0d2b891f 5296 else
19782ffd 5297 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
ad0a8c2d 5298
0d2b891f 5299 if ( track )
19782ffd 5300 apply_dem_data_common ( vtl, values[MA_VLP], track, TRUE );
ad0a8c2d
EB
5301}
5302
81ac2835
RN
5303/**
5304 * smooth_it:
5305 *
5306 * A common function for applying the elevation smoothing and reporting the results.
5307 */
5308static void smooth_it ( VikTrwLayer *vtl, VikTrack *track, gboolean flat )
5309{
5310 gulong changed = vik_track_smooth_missing_elevation_data ( track, flat );
5311 // Inform user how much was changed
5312 gchar str[64];
5313 const gchar *tmp_str = ngettext("%ld point adjusted", "%ld points adjusted", changed);
5314 g_snprintf(str, 64, tmp_str, changed);
5315 a_dialog_info_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), str);
5316}
5317
5318/**
5319 *
5320 */
19782ffd 5321static void trw_layer_missing_elevation_data_interp ( menu_array_sublayer values )
81ac2835 5322{
19782ffd 5323 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
81ac2835 5324 VikTrack *track;
19782ffd
RN
5325 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5326 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
81ac2835 5327 else
19782ffd 5328 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
81ac2835
RN
5329
5330 if ( !track )
5331 return;
5332
5333 smooth_it ( vtl, track, FALSE );
5334}
5335
19782ffd 5336static void trw_layer_missing_elevation_data_flat ( menu_array_sublayer values )
81ac2835 5337{
19782ffd 5338 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
81ac2835 5339 VikTrack *track;
19782ffd
RN
5340 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5341 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
81ac2835 5342 else
19782ffd 5343 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
81ac2835
RN
5344
5345 if ( !track )
5346 return;
5347
5348 smooth_it ( vtl, track, TRUE );
5349}
5350
667fda15
RN
5351/**
5352 * Commonal helper function
5353 */
5354static void wp_changed_message ( VikTrwLayer *vtl, gint changed )
5355{
5356 gchar str[64];
5357 const gchar *tmp_str = ngettext("%ld waypoint changed", "%ld waypoints changed", changed);
5358 g_snprintf(str, 64, tmp_str, changed);
5359 a_dialog_info_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), str);
5360}
5361
19782ffd 5362static void trw_layer_apply_dem_data_wpt_all ( menu_array_sublayer values )
667fda15 5363{
19782ffd
RN
5364 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
5365 VikLayersPanel *vlp = (VikLayersPanel *)values[MA_VLP];
667fda15
RN
5366
5367 if ( !trw_layer_dem_test ( vtl, vlp ) )
5368 return;
5369
5370 gint changed = 0;
19782ffd 5371 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_WAYPOINT ) {
667fda15 5372 // Single Waypoint
19782ffd 5373 VikWaypoint *wp = (VikWaypoint *) g_hash_table_lookup ( vtl->waypoints, values[MA_SUBLAYER_ID] );
667fda15
RN
5374 if ( wp )
5375 changed = (gint)vik_waypoint_apply_dem_data ( wp, FALSE );
5376 }
5377 else {
5378 // All waypoints
5379 GHashTableIter iter;
5380 gpointer key, value;
5381
5382 g_hash_table_iter_init ( &iter, vtl->waypoints );
5383 while ( g_hash_table_iter_next (&iter, &key, &value) ) {
5384 VikWaypoint *wp = VIK_WAYPOINT(value);
5385 changed = changed + (gint)vik_waypoint_apply_dem_data ( wp, FALSE );
5386 }
5387 }
5388 wp_changed_message ( vtl, changed );
5389}
5390
19782ffd 5391static void trw_layer_apply_dem_data_wpt_only_missing ( menu_array_sublayer values )
667fda15 5392{
19782ffd
RN
5393 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
5394 VikLayersPanel *vlp = (VikLayersPanel *)values[MA_VLP];
667fda15
RN
5395
5396 if ( !trw_layer_dem_test ( vtl, vlp ) )
5397 return;
5398
5399 gint changed = 0;
19782ffd 5400 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_WAYPOINT ) {
667fda15 5401 // Single Waypoint
19782ffd 5402 VikWaypoint *wp = (VikWaypoint *) g_hash_table_lookup ( vtl->waypoints, values[MA_SUBLAYER_ID] );
667fda15
RN
5403 if ( wp )
5404 changed = (gint)vik_waypoint_apply_dem_data ( wp, TRUE );
5405 }
5406 else {
5407 // All waypoints
5408 GHashTableIter iter;
5409 gpointer key, value;
5410
5411 g_hash_table_iter_init ( &iter, vtl->waypoints );
5412 while ( g_hash_table_iter_next (&iter, &key, &value) ) {
5413 VikWaypoint *wp = VIK_WAYPOINT(value);
5414 changed = changed + (gint)vik_waypoint_apply_dem_data ( wp, TRUE );
5415 }
5416 }
5417 wp_changed_message ( vtl, changed );
5418}
5419
19782ffd 5420static void trw_layer_goto_track_endpoint ( menu_array_sublayer values )
50a14534 5421{
19782ffd 5422 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
0d2b891f 5423 VikTrack *track;
19782ffd
RN
5424 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5425 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
0d2b891f 5426 else
19782ffd 5427 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
0d2b891f
RN
5428
5429 if ( !track )
5430 return;
215ebe59 5431 if ( !track->trackpoints )
50a14534 5432 return;
215ebe59 5433 goto_coord ( values[MA_VLP], vtl, values[MA_VVP], &(vik_track_get_tp_last(track)->coord));
50a14534
EB
5434}
5435
19782ffd 5436static void trw_layer_goto_track_max_speed ( menu_array_sublayer values )
03e7da75 5437{
19782ffd 5438 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
0d2b891f 5439 VikTrack *track;
19782ffd
RN
5440 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5441 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
0d2b891f 5442 else
19782ffd 5443 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
0d2b891f
RN
5444
5445 if ( !track )
5446 return;
5447
5448 VikTrackpoint* vtp = vik_track_get_tp_by_max_speed ( track );
03e7da75
RN
5449 if ( !vtp )
5450 return;
19782ffd 5451 goto_coord ( values[MA_VLP], vtl, values[MA_VVP], &(vtp->coord));
03e7da75
RN
5452}
5453
19782ffd 5454static void trw_layer_goto_track_max_alt ( menu_array_sublayer values )
c28faca8 5455{
19782ffd 5456 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
0d2b891f 5457 VikTrack *track;
19782ffd
RN
5458 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5459 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
0d2b891f 5460 else
19782ffd 5461 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
0d2b891f
RN
5462
5463 if ( !track )
5464 return;
5465
5466 VikTrackpoint* vtp = vik_track_get_tp_by_max_alt ( track );
c28faca8
RN
5467 if ( !vtp )
5468 return;
19782ffd 5469 goto_coord ( values[MA_VLP], vtl, values[MA_VVP], &(vtp->coord));
c28faca8
RN
5470}
5471
19782ffd 5472static void trw_layer_goto_track_min_alt ( menu_array_sublayer values )
c28faca8 5473{
19782ffd 5474 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
0d2b891f 5475 VikTrack *track;
19782ffd
RN
5476 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5477 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
0d2b891f 5478 else
19782ffd 5479 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
0d2b891f
RN
5480
5481 if ( !track )
5482 return;
5483
5484 VikTrackpoint* vtp = vik_track_get_tp_by_min_alt ( track );
c28faca8
RN
5485 if ( !vtp )
5486 return;
19782ffd 5487 goto_coord ( values[MA_VLP], vtl, values[MA_VVP], &(vtp->coord));
c28faca8 5488}
111fa174 5489
116ea336 5490/*
469113fb 5491 * Automatically change the viewport to center on the track and zoom to see the extent of the track
116ea336 5492 */
19782ffd 5493static void trw_layer_auto_track_view ( menu_array_sublayer values )
469113fb 5494{
19782ffd 5495 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
0d2b891f 5496 VikTrack *trk;
19782ffd
RN
5497 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5498 trk = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
0d2b891f 5499 else
19782ffd 5500 trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
0d2b891f 5501
867f4673 5502 if ( trk && trk->trackpoints )
469113fb
RN
5503 {
5504 struct LatLon maxmin[2] = { {0,0}, {0,0} };
867f4673 5505 trw_layer_find_maxmin_tracks ( NULL, trk, maxmin );
19782ffd
RN
5506 trw_layer_zoom_to_show_latlons ( vtl, values[MA_VVP], maxmin );
5507 if ( values[MA_VLP] )
5508 vik_layers_panel_emit_update ( VIK_LAYERS_PANEL(values[MA_VLP]) );
6bb72350 5509 else
11651e51 5510 vik_layer_emit_update ( VIK_LAYER(vtl) );
469113fb
RN
5511 }
5512}
5513
1a3be6a8
GB
5514/*
5515 * Refine the selected track/route with a routing engine.
5516 * The routing engine is selected by the user, when requestiong the job.
5517 */
19782ffd 5518static void trw_layer_route_refine ( menu_array_sublayer values )
1a3be6a8
GB
5519{
5520 static gint last_engine = 0;
19782ffd 5521 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
1a3be6a8
GB
5522 VikTrack *trk;
5523
19782ffd
RN
5524 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5525 trk = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
1a3be6a8 5526 else
19782ffd 5527 trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
1a3be6a8
GB
5528
5529 if ( trk && trk->trackpoints )
5530 {
80846d50
GB
5531 /* Check size of the route */
5532 int nb = vik_track_get_tp_count(trk);
5533 if (nb > 100) {
5534 GtkWidget *dialog = gtk_message_dialog_new (VIK_GTK_WINDOW_FROM_LAYER (vtl),
5535 GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
5536 GTK_MESSAGE_WARNING,
5537 GTK_BUTTONS_OK_CANCEL,
5538 _("Refining a track with many points (%d) is unlikely to yield sensible results. Do you want to Continue?"),
5539 nb);
5540 gint response = gtk_dialog_run ( GTK_DIALOG(dialog) );
5541 gtk_widget_destroy ( dialog );
5542 if (response != GTK_RESPONSE_OK )
5543 return;
5544 }
1a3be6a8
GB
5545 /* Select engine from dialog */
5546 GtkWidget *dialog = gtk_dialog_new_with_buttons (_("Refine Route with Routing Engine..."),
5547 VIK_GTK_WINDOW_FROM_LAYER (vtl),
5548 GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
5549 GTK_STOCK_CANCEL,
5550 GTK_RESPONSE_REJECT,
5551 GTK_STOCK_OK,
5552 GTK_RESPONSE_ACCEPT,
5553 NULL);
5554 GtkWidget *label = gtk_label_new ( _("Select routing engine") );
5555 gtk_widget_show_all(label);
5556
5557 gtk_box_pack_start ( GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), label, TRUE, TRUE, 0 );
5558
5559 GtkWidget * combo = vik_routing_ui_selector_new ( (Predicate)vik_routing_engine_supports_refine, NULL );
5560 gtk_combo_box_set_active (GTK_COMBO_BOX (combo), last_engine);
5561 gtk_widget_show_all(combo);
5562
5563 gtk_box_pack_start ( GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), combo, TRUE, TRUE, 0 );
5564
5565 gtk_dialog_set_default_response ( GTK_DIALOG(dialog), GTK_RESPONSE_ACCEPT );
5566
5567 if ( gtk_dialog_run ( GTK_DIALOG(dialog) ) == GTK_RESPONSE_ACCEPT )
5568 {
5569 /* Dialog validated: retrieve selected engine and do the job */
5570 last_engine = gtk_combo_box_get_active ( GTK_COMBO_BOX(combo) );
5571 VikRoutingEngine *routing = vik_routing_ui_selector_get_nth (combo, last_engine);
5572
5573 /* Change cursor */
19782ffd 5574 vik_window_set_busy_cursor ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)) );
1a3be6a8
GB
5575
5576 /* Force saving track */
5577 /* FIXME: remove or rename this hack */
5578 vtl->route_finder_check_added_track = TRUE;
5579
5580 /* the job */
5581 vik_routing_engine_refine (routing, vtl, trk);
5582
5583 /* FIXME: remove or rename this hack */
5584 if ( vtl->route_finder_added_track )
5585 vik_track_calculate_bounds ( vtl->route_finder_added_track );
5586
5587 vtl->route_finder_added_track = NULL;
5588 vtl->route_finder_check_added_track = FALSE;
5589
5590 vik_layer_emit_update ( VIK_LAYER(vtl) );
5591
5592 /* Restore cursor */
19782ffd 5593 vik_window_clear_busy_cursor ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)) );
1a3be6a8
GB
5594 }
5595 gtk_widget_destroy ( dialog );
5596 }
5597}
5598
19782ffd 5599static void trw_layer_edit_trackpoint ( menu_array_sublayer values )
c95d6b00 5600{
19782ffd 5601 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
c95d6b00
RN
5602 trw_layer_tpwin_init ( vtl );
5603}
5604
111fa174
AF
5605/*************************************
5606 * merge/split by time routines
5607 *************************************/
5608
15f45edc 5609/* called for each key in track hash table.
5780603d 5610 * If the current track has the same time stamp type, add it to the result,
15f45edc
QT
5611 * except the one pointed by "exclude".
5612 * set exclude to NULL if there is no exclude to check.
5780603d 5613 * Note that the result is in reverse (for performance reasons).
15f45edc
QT
5614 */
5615typedef struct {
5616 GList **result;
9cdc601f 5617 VikTrack *exclude;
5780603d 5618 gboolean with_timestamps;
15f45edc 5619} twt_udata;
5780603d 5620static void find_tracks_with_timestamp_type(gpointer key, gpointer value, gpointer udata)
15f45edc
QT
5621{
5622 twt_udata *user_data = udata;
5623 VikTrackpoint *p1, *p2;
215ebe59 5624 VikTrack *trk = VIK_TRACK(value);
9cdc601f 5625 if (trk == user_data->exclude) {
15f45edc
QT
5626 return;
5627 }
5628
215ebe59
RN
5629 if (trk->trackpoints) {
5630 p1 = vik_track_get_tp_first(trk);
5631 p2 = vik_track_get_tp_last(trk);
15f45edc 5632
5780603d
RN
5633 if ( user_data->with_timestamps ) {
5634 if (!p1->has_timestamp || !p2->has_timestamp) {
5635 return;
5636 }
5637 }
5638 else {
5639 // Don't add tracks with timestamps when getting non timestamp tracks
5640 if (p1->has_timestamp || p2->has_timestamp) {
5641 return;
5642 }
15f45edc 5643 }
15f45edc
QT
5644 }
5645
5646 *(user_data->result) = g_list_prepend(*(user_data->result), key);
5647}
5648
6252ee1d
RN
5649/**
5650 * find_nearby_tracks_by_time:
5651 *
5652 * Called for each track in track hash table.
5653 * If the original track (in user_data[1]) is close enough (threshold period in user_data[2])
5654 * to the current track, then the current track is added to the list in user_data[0]
111fa174 5655 */
fc92c977 5656static void find_nearby_tracks_by_time (gpointer key, gpointer value, gpointer user_data)
111fa174 5657{
fc92c977 5658 VikTrack *trk = VIK_TRACK(value);
111fa174
AF
5659
5660 GList **nearby_tracks = ((gpointer *)user_data)[0];
6252ee1d
RN
5661 VikTrack *orig_trk = VIK_TRACK(((gpointer *)user_data)[1]);
5662
5663 if ( !orig_trk || !orig_trk->trackpoints )
5664 return;
111fa174 5665
a61b2619
AF
5666 /* outline:
5667 * detect reasons for not merging, and return
5668 * if no reason is found not to merge, then do it.
5669 */
111fa174 5670
9cdc601f 5671 twt_udata *udata = user_data;
fc92c977 5672 // Exclude the original track from the compiled list
9cdc601f 5673 if (trk == udata->exclude) {
111fa174
AF
5674 return;
5675 }
5676
6252ee1d
RN
5677 time_t t1 = vik_track_get_tp_first(orig_trk)->timestamp;
5678 time_t t2 = vik_track_get_tp_last(orig_trk)->timestamp;
111fa174 5679
fc92c977 5680 if (trk->trackpoints) {
6252ee1d
RN
5681
5682 VikTrackpoint *p1 = vik_track_get_tp_first(trk);
5683 VikTrackpoint *p2 = vik_track_get_tp_last(trk);
a61b2619
AF
5684
5685 if (!p1->has_timestamp || !p2->has_timestamp) {
fc92c977 5686 //g_print("no timestamp\n");
a61b2619
AF
5687 return;
5688 }
111fa174 5689
fc92c977
RN
5690 guint threshold = GPOINTER_TO_UINT (((gpointer *)user_data)[2]);
5691 //g_print("Got track named %s, times %d, %d\n", trk->name, p1->timestamp, p2->timestamp);
5692 if (! (abs(t1 - p2->timestamp) < threshold ||
a61b2619 5693 /* p1 p2 t1 t2 */
fc92c977 5694 abs(p1->timestamp - t2) < threshold)
a61b2619
AF
5695 /* t1 t2 p1 p2 */
5696 ) {
5697 return;
5698 }
111fa174 5699 }
a61b2619 5700
fc92c977 5701 *nearby_tracks = g_list_prepend(*nearby_tracks, value);
111fa174
AF
5702}
5703
5704/* comparison function used to sort tracks; a and b are hash table keys */
02b5d347 5705/* Not actively used - can be restored if needed
111fa174
AF
5706static gint track_compare(gconstpointer a, gconstpointer b, gpointer user_data)
5707{
5708 GHashTable *tracks = user_data;
5709 time_t t1, t2;
5710
5711 t1 = VIK_TRACKPOINT(VIK_TRACK(g_hash_table_lookup(tracks, a))->trackpoints->data)->timestamp;
5712 t2 = VIK_TRACKPOINT(VIK_TRACK(g_hash_table_lookup(tracks, b))->trackpoints->data)->timestamp;
5713
5714 if (t1 < t2) return -1;
5715 if (t1 > t2) return 1;
5716 return 0;
5717}
02b5d347 5718*/
111fa174
AF
5719
5720/* comparison function used to sort trackpoints */
5721static gint trackpoint_compare(gconstpointer a, gconstpointer b)
5722{
5723 time_t t1 = VIK_TRACKPOINT(a)->timestamp, t2 = VIK_TRACKPOINT(b)->timestamp;
5724
5725 if (t1 < t2) return -1;
5726 if (t1 > t2) return 1;
5727 return 0;
5728}
5729
fb2306f7
RN
5730/**
5731 * comparison function which can be used to sort tracks or waypoints by name
5732 */
5733static gint sort_alphabetically (gconstpointer a, gconstpointer b, gpointer user_data)
5734{
5735 const gchar* namea = (const gchar*) a;
5736 const gchar* nameb = (const gchar*) b;
5737 if ( namea == NULL || nameb == NULL)
5738 return 0;
5739 else
5740 // Same sort method as used in the vik_treeview_*_alphabetize functions
5741 return strcmp ( namea, nameb );
5742}
fb2306f7 5743
5780603d
RN
5744/**
5745 * Attempt to merge selected track with other tracks specified by the user
5746 * Tracks to merge with must be of the same 'type' as the selected track -
5747 * either all with timestamps, or all without timestamps
5748 */
19782ffd 5749static void trw_layer_merge_with_other ( menu_array_sublayer values )
291edcab 5750{
19782ffd 5751 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
5780603d 5752 GList *other_tracks = NULL;
0d2b891f 5753 GHashTable *ght_tracks;
19782ffd 5754 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
0d2b891f
RN
5755 ght_tracks = vtl->routes;
5756 else
5757 ght_tracks = vtl->tracks;
5758
19782ffd 5759 VikTrack *track = (VikTrack *) g_hash_table_lookup ( ght_tracks, values[MA_SUBLAYER_ID] );
0d2b891f
RN
5760
5761 if ( !track )
5762 return;
15f45edc 5763
5780603d 5764 if ( !track->trackpoints )
15f45edc 5765 return;
15f45edc 5766
c1564279 5767 twt_udata udata;
5780603d 5768 udata.result = &other_tracks;
9cdc601f 5769 udata.exclude = track;
5780603d
RN
5770 // Allow merging with 'similar' time type time tracks
5771 // i.e. either those times, or those without
215ebe59 5772 udata.with_timestamps = vik_track_get_tp_first(track)->has_timestamp;
291edcab 5773
0d2b891f 5774 g_hash_table_foreach(ght_tracks, find_tracks_with_timestamp_type, (gpointer)&udata);
5780603d
RN
5775 other_tracks = g_list_reverse(other_tracks);
5776
5777 if ( !other_tracks ) {
5778 if ( udata.with_timestamps )
5779 a_dialog_error_msg(VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Failed. No other tracks with timestamps in this layer found"));
5780 else
5781 a_dialog_error_msg(VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Failed. No other tracks without timestamps in this layer found"));
15f45edc
QT
5782 return;
5783 }
5784
8352326e 5785 // Sort alphabetically for user presentation
ce4bd1cf
RN
5786 // Convert into list of names for usage with dialog function
5787 // TODO: Need to consider how to work best when we can have multiple tracks the same name...
5788 GList *other_tracks_names = NULL;
5789 GList *iter = g_list_first ( other_tracks );
5790 while ( iter ) {
0d2b891f 5791 other_tracks_names = g_list_append ( other_tracks_names, VIK_TRACK(g_hash_table_lookup (ght_tracks, iter->data))->name );
ce4bd1cf
RN
5792 iter = g_list_next ( iter );
5793 }
5794
5795 other_tracks_names = g_list_sort_with_data (other_tracks_names, sort_alphabetically, NULL);
8352326e 5796
7767aa02 5797 GList *merge_list = a_dialog_select_from_list(VIK_GTK_WINDOW_FROM_LAYER(vtl),
0d2b891f
RN
5798 other_tracks_names,
5799 TRUE,
5800 _("Merge with..."),
5801 track->is_route ? _("Select route to merge with") : _("Select track to merge with"));
5780603d 5802 g_list_free(other_tracks);
ce4bd1cf 5803 g_list_free(other_tracks_names);
291edcab 5804
7767aa02 5805 if (merge_list)
291edcab 5806 {
7767aa02
QT
5807 GList *l;
5808 for (l = merge_list; l != NULL; l = g_list_next(l)) {
0d2b891f
RN
5809 VikTrack *merge_track;
5810 if ( track->is_route )
5811 merge_track = vik_trw_layer_get_route ( vtl, l->data );
5812 else
5813 merge_track = vik_trw_layer_get_track ( vtl, l->data );
5814
7767aa02 5815 if (merge_track) {
0a9ed7dc 5816 vik_track_steal_and_append_trackpoints ( track, merge_track );
0d2b891f
RN
5817 if ( track->is_route )
5818 vik_trw_layer_delete_route (vtl, merge_track);
5819 else
5820 vik_trw_layer_delete_track (vtl, merge_track);
7767aa02
QT
5821 track->trackpoints = g_list_sort(track->trackpoints, trackpoint_compare);
5822 }
291edcab 5823 }
7767aa02
QT
5824 for (l = merge_list; l != NULL; l = g_list_next(l))
5825 g_free(l->data);
5826 g_list_free(merge_list);
20981fd6 5827
da121f9b 5828 vik_layer_emit_update( VIK_LAYER(vtl) );
291edcab 5829 }
291edcab
HR
5830}
5831
9c34f614
RN
5832// c.f. trw_layer_sorted_track_id_by_name_list
5833// but don't add the specified track to the list (normally current track)
5834static void trw_layer_sorted_track_id_by_name_list_exclude_self (const gpointer id, const VikTrack *trk, gpointer udata)
5835{
5836 twt_udata *user_data = udata;
5837
5838 // Skip self
9cdc601f 5839 if (trk == user_data->exclude) {
9c34f614
RN
5840 return;
5841 }
5842
5843 // Sort named list alphabetically
5844 *(user_data->result) = g_list_insert_sorted_with_data (*(user_data->result), trk->name, sort_alphabetically, NULL);
5845}
5846
5847/**
0d2b891f 5848 * Join - this allows combining 'tracks' and 'track routes'
9c34f614
RN
5849 * i.e. doesn't care about whether tracks have consistent timestamps
5850 * ATM can only append one track at a time to the currently selected track
5851 */
19782ffd 5852static void trw_layer_append_track ( menu_array_sublayer values )
9c34f614
RN
5853{
5854
19782ffd 5855 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
0d2b891f
RN
5856 VikTrack *trk;
5857 GHashTable *ght_tracks;
19782ffd 5858 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
0d2b891f
RN
5859 ght_tracks = vtl->routes;
5860 else
5861 ght_tracks = vtl->tracks;
5862
19782ffd 5863 trk = (VikTrack *) g_hash_table_lookup ( ght_tracks, values[MA_SUBLAYER_ID] );
0d2b891f
RN
5864
5865 if ( !trk )
5866 return;
9c34f614
RN
5867
5868 GList *other_tracks_names = NULL;
5869
5870 // Sort alphabetically for user presentation
5871 // Convert into list of names for usage with dialog function
5872 // TODO: Need to consider how to work best when we can have multiple tracks the same name...
5873 twt_udata udata;
5874 udata.result = &other_tracks_names;
9cdc601f 5875 udata.exclude = trk;
9c34f614 5876
0d2b891f 5877 g_hash_table_foreach(ght_tracks, (GHFunc) trw_layer_sorted_track_id_by_name_list_exclude_self, (gpointer)&udata);
9c34f614
RN
5878
5879 // Note the limit to selecting one track only
5880 // this is to control the ordering of appending tracks, i.e. the selected track always goes after the current track
5881 // (otherwise with multiple select the ordering would not be controllable by the user - automatically being alphabetically)
5882 GList *append_list = a_dialog_select_from_list(VIK_GTK_WINDOW_FROM_LAYER(vtl),
0d2b891f
RN
5883 other_tracks_names,
5884 FALSE,
5885 trk->is_route ? _("Append Route"): _("Append Track"),
5886 trk->is_route ? _("Select the route to append after the current route") :
5887 _("Select the track to append after the current track") );
9c34f614
RN
5888
5889 g_list_free(other_tracks_names);
5890
5891 // It's a list, but shouldn't contain more than one other track!
5892 if ( append_list ) {
5893 GList *l;
5894 for (l = append_list; l != NULL; l = g_list_next(l)) {
5895 // TODO: at present this uses the first track found by name,
5896 // which with potential multiple same named tracks may not be the one selected...
0d2b891f
RN
5897 VikTrack *append_track;
5898 if ( trk->is_route )
5899 append_track = vik_trw_layer_get_route ( vtl, l->data );
5900 else
5901 append_track = vik_trw_layer_get_track ( vtl, l->data );
5902
9c34f614 5903 if ( append_track ) {
0a9ed7dc 5904 vik_track_steal_and_append_trackpoints ( trk, append_track );
0d2b891f
RN
5905 if ( trk->is_route )
5906 vik_trw_layer_delete_route (vtl, append_track);
5907 else
5908 vik_trw_layer_delete_track (vtl, append_track);
9c34f614
RN
5909 }
5910 }
5911 for (l = append_list; l != NULL; l = g_list_next(l))
5912 g_free(l->data);
5913 g_list_free(append_list);
20981fd6 5914
da121f9b 5915 vik_layer_emit_update( VIK_LAYER(vtl) );
9c34f614
RN
5916 }
5917}
5918
6b5b6c47
RN
5919/**
5920 * Very similar to trw_layer_append_track for joining
5921 * but this allows selection from the 'other' list
5922 * If a track is selected, then is shows routes and joins the selected one
5923 * If a route is selected, then is shows tracks and joins the selected one
5924 */
19782ffd 5925static void trw_layer_append_other ( menu_array_sublayer values )
6b5b6c47
RN
5926{
5927
19782ffd 5928 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
6b5b6c47
RN
5929 VikTrack *trk;
5930 GHashTable *ght_mykind, *ght_others;
19782ffd 5931 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE ) {
6b5b6c47
RN
5932 ght_mykind = vtl->routes;
5933 ght_others = vtl->tracks;
5934 }
5935 else {
5936 ght_mykind = vtl->tracks;
5937 ght_others = vtl->routes;
5938 }
5939
19782ffd 5940 trk = (VikTrack *) g_hash_table_lookup ( ght_mykind, values[MA_SUBLAYER_ID] );
6b5b6c47
RN
5941
5942 if ( !trk )
5943 return;
5944
5945 GList *other_tracks_names = NULL;
5946
5947 // Sort alphabetically for user presentation
5948 // Convert into list of names for usage with dialog function
5949 // TODO: Need to consider how to work best when we can have multiple tracks the same name...
5950 twt_udata udata;
5951 udata.result = &other_tracks_names;
9cdc601f 5952 udata.exclude = trk;
6b5b6c47
RN
5953
5954 g_hash_table_foreach(ght_others, (GHFunc) trw_layer_sorted_track_id_by_name_list_exclude_self, (gpointer)&udata);
5955
5956 // Note the limit to selecting one track only
5957 // this is to control the ordering of appending tracks, i.e. the selected track always goes after the current track
5958 // (otherwise with multiple select the ordering would not be controllable by the user - automatically being alphabetically)
5959 GList *append_list = a_dialog_select_from_list(VIK_GTK_WINDOW_FROM_LAYER(vtl),
5960 other_tracks_names,
5961 FALSE,
5962 trk->is_route ? _("Append Track"): _("Append Route"),
5963 trk->is_route ? _("Select the track to append after the current route") :
5964 _("Select the route to append after the current track") );
5965
5966 g_list_free(other_tracks_names);
5967
5968 // It's a list, but shouldn't contain more than one other track!
5969 if ( append_list ) {
5970 GList *l;
5971 for (l = append_list; l != NULL; l = g_list_next(l)) {
5972 // TODO: at present this uses the first track found by name,
5973 // which with potential multiple same named tracks may not be the one selected...
5974
5975 // Get FROM THE OTHER TYPE list
5976 VikTrack *append_track;
5977 if ( trk->is_route )
5978 append_track = vik_trw_layer_get_track ( vtl, l->data );
5979 else
5980 append_track = vik_trw_layer_get_route ( vtl, l->data );
5981
5982 if ( append_track ) {
5983
5984 if ( !append_track->is_route &&
5985 ( ( vik_track_get_segment_count ( append_track ) > 1 ) ||
5986 ( vik_track_get_average_speed ( append_track ) > 0.0 ) ) ) {
5987
5988 if ( a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
5989 _("Converting a track to a route removes extra track data such as segments, timestamps, etc...\nDo you want to continue?"), NULL ) ) {
5990 vik_track_merge_segments ( append_track );
5991 vik_track_to_routepoints ( append_track );
5992 }
5993 else {
5994 break;
5995 }
5996 }
5997
0a9ed7dc 5998 vik_track_steal_and_append_trackpoints ( trk, append_track );
6b5b6c47
RN
5999
6000 // Delete copied which is FROM THE OTHER TYPE list
6001 if ( trk->is_route )
6002 vik_trw_layer_delete_track (vtl, append_track);
6003 else
6004 vik_trw_layer_delete_route (vtl, append_track);
6005 }
6006 }
6007 for (l = append_list; l != NULL; l = g_list_next(l))
6008 g_free(l->data);
6009 g_list_free(append_list);
da121f9b 6010 vik_layer_emit_update( VIK_LAYER(vtl) );
6b5b6c47
RN
6011 }
6012}
6013
24774c43 6014/* merge by segments */
19782ffd 6015static void trw_layer_merge_by_segment ( menu_array_sublayer values )
24774c43 6016{
19782ffd
RN
6017 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
6018 VikTrack *trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
24774c43
RN
6019 guint segments = vik_track_merge_segments ( trk );
6020 // NB currently no need to redraw as segments not actually shown on the display
6021 // However inform the user of what happened:
6022 gchar str[64];
6023 const gchar *tmp_str = ngettext("%d segment merged", "%d segments merged", segments);
6024 g_snprintf(str, 64, tmp_str, segments);
6025 a_dialog_info_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), str );
6026}
6027
111fa174 6028/* merge by time routine */
19782ffd 6029static void trw_layer_merge_by_timestamp ( menu_array_sublayer values )
111fa174 6030{
19782ffd 6031 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
a61b2619 6032
02b5d347 6033 //time_t t1, t2;
111fa174 6034
c1564279 6035 GList *tracks_with_timestamp = NULL;
19782ffd 6036 VikTrack *orig_trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
ce4bd1cf 6037 if (orig_trk->trackpoints &&
215ebe59 6038 !vik_track_get_tp_first(orig_trk)->has_timestamp) {
c1564279 6039 a_dialog_error_msg(VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Failed. This track does not have timestamp"));
c1564279
RN
6040 return;
6041 }
6042
6043 twt_udata udata;
6044 udata.result = &tracks_with_timestamp;
9cdc601f 6045 udata.exclude = orig_trk;
5780603d
RN
6046 udata.with_timestamps = TRUE;
6047 g_hash_table_foreach(vtl->tracks, find_tracks_with_timestamp_type, (gpointer)&udata);
c1564279
RN
6048 tracks_with_timestamp = g_list_reverse(tracks_with_timestamp);
6049
6050 if (!tracks_with_timestamp) {
6051 a_dialog_error_msg(VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Failed. No other track in this layer has timestamp"));
c1564279
RN
6052 return;
6053 }
6054 g_list_free(tracks_with_timestamp);
6055
fc92c977
RN
6056 static guint threshold_in_minutes = 1;
6057 if (!a_dialog_time_threshold(VIK_GTK_WINDOW_FROM_LAYER(vtl),
6058 _("Merge Threshold..."),
6059 _("Merge when time between tracks less than:"),
6060 &threshold_in_minutes)) {
111fa174
AF
6061 return;
6062 }
6063
fc92c977
RN
6064 // keep attempting to merge all tracks until no merges within the time specified is possible
6065 gboolean attempt_merge = TRUE;
6066 GList *nearby_tracks = NULL;
6067 GList *trps;
6068 static gpointer params[3];
6069
6070 while ( attempt_merge ) {
6071
6072 // Don't try again unless tracks have changed
6073 attempt_merge = FALSE;
111fa174 6074
ce4bd1cf 6075 trps = orig_trk->trackpoints;
111fa174
AF
6076 if ( !trps )
6077 return;
6078
111fa174
AF
6079 if (nearby_tracks) {
6080 g_list_free(nearby_tracks);
6081 nearby_tracks = NULL;
6082 }
6083
111fa174 6084 params[0] = &nearby_tracks;
6252ee1d 6085 params[1] = orig_trk;
fc92c977 6086 params[2] = GUINT_TO_POINTER (threshold_in_minutes*60); // In seconds
111fa174
AF
6087
6088 /* get a list of adjacent-in-time tracks */
fc92c977 6089 g_hash_table_foreach(vtl->tracks, find_nearby_tracks_by_time, params);
111fa174 6090
111fa174 6091 /* merge them */
fc92c977
RN
6092 GList *l = nearby_tracks;
6093 while ( l ) {
fc92c977 6094 /* remove trackpoints from merged track, delete track */
0a9ed7dc 6095 vik_track_steal_and_append_trackpoints ( orig_trk, VIK_TRACK(l->data) );
fc92c977 6096 vik_trw_layer_delete_track (vtl, VIK_TRACK(l->data));
111fa174 6097
fc92c977
RN
6098 // Tracks have changed, therefore retry again against all the remaining tracks
6099 attempt_merge = TRUE;
ce4bd1cf 6100
fc92c977 6101 l = g_list_next(l);
111fa174 6102 }
fc92c977
RN
6103
6104 orig_trk->trackpoints = g_list_sort(orig_trk->trackpoints, trackpoint_compare);
6105 }
6106
111fa174 6107 g_list_free(nearby_tracks);
20981fd6 6108
da121f9b 6109 vik_layer_emit_update( VIK_LAYER(vtl) );
111fa174
AF
6110}
6111
84d3d9f9 6112/**
9914238e 6113 * Split a track at the currently selected trackpoint
84d3d9f9 6114 */
0d2b891f 6115static void trw_layer_split_at_selected_trackpoint ( VikTrwLayer *vtl, gint subtype )
84d3d9f9
RN
6116{
6117 if ( !vtl->current_tpl )
6118 return;
6119
6120 if ( vtl->current_tpl->next && vtl->current_tpl->prev ) {
0d2b891f 6121 gchar *name = trw_layer_new_unique_sublayer_name(vtl, subtype, vtl->current_tp_track->name);
9914238e 6122 if ( name ) {
03817fbf 6123 VikTrack *tr = vik_track_copy ( vtl->current_tp_track, FALSE );
84d3d9f9
RN
6124 GList *newglist = g_list_alloc ();
6125 newglist->prev = NULL;
6126 newglist->next = vtl->current_tpl->next;
6127 newglist->data = vik_trackpoint_copy(VIK_TRACKPOINT(vtl->current_tpl->data));
6128 tr->trackpoints = newglist;
6129
6130 vtl->current_tpl->next->prev = newglist; /* end old track here */
6131 vtl->current_tpl->next = NULL;
6132
20981fd6
RN
6133 // Bounds of the selected track changed due to the split
6134 vik_track_calculate_bounds ( vtl->current_tp_track );
6135
84d3d9f9
RN
6136 vtl->current_tpl = newglist; /* change tp to first of new track. */
6137 vtl->current_tp_track = tr;
6138
0d2b891f
RN
6139 if ( tr->is_route )
6140 vik_trw_layer_add_route ( vtl, name, tr );
6141 else
6142 vik_trw_layer_add_track ( vtl, name, tr );
84d3d9f9 6143
20981fd6
RN
6144 // Bounds of the new track created by the split
6145 vik_track_calculate_bounds ( tr );
6146
84d3d9f9
RN
6147 trku_udata udata;
6148 udata.trk = tr;
6149 udata.uuid = NULL;
6150
6151 // Also need id of newly created track
0d2b891f
RN
6152 gpointer *trkf;
6153 if ( tr->is_route )
6154 trkf = g_hash_table_find ( vtl->routes, (GHRFunc) trw_layer_track_find_uuid, &udata );
6155 else
6156 trkf = g_hash_table_find ( vtl->tracks, (GHRFunc) trw_layer_track_find_uuid, &udata );
6157
84d3d9f9
RN
6158 if ( trkf && udata.uuid )
6159 vtl->current_tp_id = udata.uuid;
6160 else
6161 vtl->current_tp_id = NULL;
6162
da121f9b 6163 vik_layer_emit_update(VIK_LAYER(vtl));
84d3d9f9 6164 }
1613e468 6165 g_free ( name );
84d3d9f9
RN
6166 }
6167}
6168
111fa174 6169/* split by time routine */
19782ffd 6170static void trw_layer_split_by_timestamp ( menu_array_sublayer values )
111fa174 6171{
19782ffd
RN
6172 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
6173 VikTrack *track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
111fa174
AF
6174 GList *trps = track->trackpoints;
6175 GList *iter;
6176 GList *newlists = NULL;
6177 GList *newtps = NULL;
111fa174
AF
6178 static guint thr = 1;
6179
6180 time_t ts, prev_ts;
6181
6182 if ( !trps )
6183 return;
6184
19782ffd 6185 if (!a_dialog_time_threshold(VIK_GTK_WINDOW_FROM_LAYER(vtl),
4c77d5e0
GB
6186 _("Split Threshold..."),
6187 _("Split when time between trackpoints exceeds:"),
111fa174
AF
6188 &thr)) {
6189 return;
6190 }
6191
6192 /* iterate through trackpoints, and copy them into new lists without touching original list */
6193 prev_ts = VIK_TRACKPOINT(trps->data)->timestamp;
6194 iter = trps;
6195
6196 while (iter) {
6197 ts = VIK_TRACKPOINT(iter->data)->timestamp;
1e3b44c4
RN
6198
6199 // Check for unordered time points - this is quite a rare occurence - unless one has reversed a track.
111fa174 6200 if (ts < prev_ts) {
1e3b44c4
RN
6201 gchar tmp_str[64];
6202 strftime ( tmp_str, sizeof(tmp_str), "%c", localtime(&ts) );
6203 if ( a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
6204 _("Can not split track due to trackpoints not ordered in time - such as at %s.\n\nGoto this trackpoint?"),
6205 tmp_str ) ) {
19782ffd 6206 goto_coord ( values[MA_VLP], vtl, values[MA_VVP], &(VIK_TRACKPOINT(iter->data)->coord) );
1e3b44c4 6207 }
111fa174
AF
6208 return;
6209 }
1e3b44c4 6210
111fa174
AF
6211 if (ts - prev_ts > thr*60) {
6212 /* flush accumulated trackpoints into new list */
aa9887a1 6213 newlists = g_list_append(newlists, g_list_reverse(newtps));
111fa174
AF
6214 newtps = NULL;
6215 }
6216
6217 /* accumulate trackpoint copies in newtps, in reverse order */
6218 newtps = g_list_prepend(newtps, vik_trackpoint_copy(VIK_TRACKPOINT(iter->data)));
6219 prev_ts = ts;
6220 iter = g_list_next(iter);
6221 }
6222 if (newtps) {
aa9887a1 6223 newlists = g_list_append(newlists, g_list_reverse(newtps));
111fa174
AF
6224 }
6225
6226 /* put lists of trackpoints into tracks */
6227 iter = newlists;
c9d8f273
RN
6228 // Only bother updating if the split results in new tracks
6229 if (g_list_length (newlists) > 1) {
6230 while (iter) {
6231 gchar *new_tr_name;
6232 VikTrack *tr;
111fa174 6233
03817fbf 6234 tr = vik_track_copy ( track, FALSE );
c9d8f273 6235 tr->trackpoints = (GList *)(iter->data);
111fa174 6236
784b6042
RN
6237 new_tr_name = trw_layer_new_unique_sublayer_name ( vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, track->name);
6238 vik_trw_layer_add_track(vtl, new_tr_name, tr);
1613e468 6239 g_free ( new_tr_name );
20981fd6 6240 vik_track_calculate_bounds ( tr );
c9d8f273
RN
6241 iter = g_list_next(iter);
6242 }
ce4bd1cf 6243 // Remove original track and then update the display
784b6042 6244 vik_trw_layer_delete_track (vtl, track);
19782ffd 6245 vik_layer_emit_update(VIK_LAYER(vtl));
111fa174
AF
6246 }
6247 g_list_free(newlists);
111fa174
AF
6248}
6249
af2341f3
RN
6250/**
6251 * Split a track by the number of points as specified by the user
6252 */
19782ffd 6253static void trw_layer_split_by_n_points ( menu_array_sublayer values )
af2341f3 6254{
19782ffd 6255 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
0d2b891f 6256 VikTrack *track;
19782ffd
RN
6257 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
6258 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
0d2b891f 6259 else
19782ffd 6260 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
0d2b891f
RN
6261
6262 if ( !track )
6263 return;
af2341f3
RN
6264
6265 // Check valid track
6266 GList *trps = track->trackpoints;
6267 if ( !trps )
6268 return;
6269
19782ffd 6270 gint points = a_dialog_get_positive_number(VIK_GTK_WINDOW_FROM_LAYER(vtl),
af2341f3
RN
6271 _("Split Every Nth Point"),
6272 _("Split on every Nth point:"),
6273 250, // Default value as per typical limited track capacity of various GPS devices
6274 2, // Min
6275 65536, // Max
6276 5); // Step
6277 // Was a valid number returned?
6278 if (!points)
6279 return;
6280
6281 // Now split...
6282 GList *iter;
6283 GList *newlists = NULL;
6284 GList *newtps = NULL;
6285 gint count = 0;
6286 iter = trps;
6287
6288 while (iter) {
6289 /* accumulate trackpoint copies in newtps, in reverse order */
6290 newtps = g_list_prepend(newtps, vik_trackpoint_copy(VIK_TRACKPOINT(iter->data)));
6291 count++;
6292 if (count >= points) {
6293 /* flush accumulated trackpoints into new list */
6294 newlists = g_list_append(newlists, g_list_reverse(newtps));
6295 newtps = NULL;
6296 count = 0;
6297 }
6298 iter = g_list_next(iter);
6299 }
6300
6301 // If there is a remaining chunk put that into the new split list
6302 // This may well be the whole track if no split points were encountered
6303 if (newtps) {
6304 newlists = g_list_append(newlists, g_list_reverse(newtps));
6305 }
6306
6307 /* put lists of trackpoints into tracks */
6308 iter = newlists;
af2341f3
RN
6309 // Only bother updating if the split results in new tracks
6310 if (g_list_length (newlists) > 1) {
6311 while (iter) {
6312 gchar *new_tr_name;
6313 VikTrack *tr;
6314
03817fbf 6315 tr = vik_track_copy ( track, FALSE );
af2341f3
RN
6316 tr->trackpoints = (GList *)(iter->data);
6317
0d2b891f
RN
6318 if ( track->is_route ) {
6319 new_tr_name = trw_layer_new_unique_sublayer_name ( vtl, VIK_TRW_LAYER_SUBLAYER_ROUTE, track->name);
6320 vik_trw_layer_add_route(vtl, new_tr_name, tr);
6321 }
6322 else {
6323 new_tr_name = trw_layer_new_unique_sublayer_name ( vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, track->name);
6324 vik_trw_layer_add_track(vtl, new_tr_name, tr);
6325 }
1613e468 6326 g_free ( new_tr_name );
20981fd6
RN
6327 vik_track_calculate_bounds ( tr );
6328
af2341f3
RN
6329 iter = g_list_next(iter);
6330 }
6331 // Remove original track and then update the display
0d2b891f
RN
6332 if ( track->is_route )
6333 vik_trw_layer_delete_route (vtl, track);
6334 else
6335 vik_trw_layer_delete_track (vtl, track);
19782ffd 6336 vik_layer_emit_update(VIK_LAYER(vtl));
af2341f3
RN
6337 }
6338 g_list_free(newlists);
6339}
6340
a4aefe69
RN
6341/**
6342 * Split a track at the currently selected trackpoint
6343 */
19782ffd 6344static void trw_layer_split_at_trackpoint ( menu_array_sublayer values )
a4aefe69 6345{
19782ffd
RN
6346 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
6347 gint subtype = GPOINTER_TO_INT (values[MA_SUBTYPE]);
0d2b891f 6348 trw_layer_split_at_selected_trackpoint ( vtl, subtype );
a4aefe69 6349}
98e87078
RN
6350
6351/**
6352 * Split a track by its segments
0d2b891f 6353 * Routes do not have segments so don't call this for routes
98e87078 6354 */
19782ffd 6355static void trw_layer_split_segments ( menu_array_sublayer values )
98e87078 6356{
19782ffd
RN
6357 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
6358 VikTrack *trk = g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
0d2b891f
RN
6359
6360 if ( !trk )
6361 return;
6362
98e87078
RN
6363 guint ntracks;
6364
6365 VikTrack **tracks = vik_track_split_into_segments (trk, &ntracks);
6366 gchar *new_tr_name;
6367 guint i;
6368 for ( i = 0; i < ntracks; i++ ) {
6369 if ( tracks[i] ) {
6370 new_tr_name = trw_layer_new_unique_sublayer_name ( vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, trk->name);
6371 vik_trw_layer_add_track ( vtl, new_tr_name, tracks[i] );
1613e468 6372 g_free ( new_tr_name );
98e87078
RN
6373 }
6374 }
6375 if ( tracks ) {
6376 g_free ( tracks );
6377 // Remove original track
6378 vik_trw_layer_delete_track ( vtl, trk );
da121f9b 6379 vik_layer_emit_update ( VIK_LAYER(vtl) );
98e87078
RN
6380 }
6381 else {
6382 a_dialog_error_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Can not split track as it has no segments"));
6383 }
6384}
111fa174
AF
6385/* end of split/merge routines */
6386
a56baa08
RN
6387static void trw_layer_trackpoint_selected_delete ( VikTrwLayer *vtl, VikTrack *trk )
6388{
6389 GList *new_tpl;
6390
6391 // Find available adjacent trackpoint
6392 if ( (new_tpl = vtl->current_tpl->next) || (new_tpl = vtl->current_tpl->prev) ) {
6393 if ( VIK_TRACKPOINT(vtl->current_tpl->data)->newsegment && vtl->current_tpl->next )
6394 VIK_TRACKPOINT(vtl->current_tpl->next->data)->newsegment = TRUE; /* don't concat segments on del */
6395
6396 // Delete current trackpoint
6397 vik_trackpoint_free ( vtl->current_tpl->data );
6398 trk->trackpoints = g_list_delete_link ( trk->trackpoints, vtl->current_tpl );
6399
6400 // Set to current to the available adjacent trackpoint
6401 vtl->current_tpl = new_tpl;
6402
6403 if ( vtl->current_tp_track ) {
6404 vik_track_calculate_bounds ( vtl->current_tp_track );
6405 }
6406 }
6407 else {
6408 // Delete current trackpoint
6409 vik_trackpoint_free ( vtl->current_tpl->data );
6410 trk->trackpoints = g_list_delete_link ( trk->trackpoints, vtl->current_tpl );
6411 trw_layer_cancel_current_tp ( vtl, FALSE );
6412 }
6413}
6414
6415/**
6416 * Delete the selected point
6417 */
19782ffd 6418static void trw_layer_delete_point_selected ( menu_array_sublayer values )
a56baa08 6419{
19782ffd 6420 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
a56baa08 6421 VikTrack *trk;
19782ffd
RN
6422 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
6423 trk = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
a56baa08 6424 else
19782ffd 6425 trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
a56baa08
RN
6426
6427 if ( !trk )
6428 return;
6429
6430 if ( !vtl->current_tpl )
6431 return;
6432
6433 trw_layer_trackpoint_selected_delete ( vtl, trk );
6434
6435 // Track has been updated so update tps:
6436 trw_layer_cancel_tps_of_track ( vtl, trk );
6437
6438 vik_layer_emit_update ( VIK_LAYER(vtl) );
6439}
6440
b6eda120
RN
6441/**
6442 * Delete adjacent track points at the same position
6443 * AKA Delete Dulplicates on the Properties Window
6444 */
19782ffd 6445static void trw_layer_delete_points_same_position ( menu_array_sublayer values )
b6eda120 6446{
19782ffd 6447 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
0d2b891f 6448 VikTrack *trk;
19782ffd
RN
6449 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
6450 trk = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
0d2b891f 6451 else
19782ffd 6452 trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
0d2b891f
RN
6453
6454 if ( !trk )
6455 return;
b6eda120
RN
6456
6457 gulong removed = vik_track_remove_dup_points ( trk );
6458
6459 // Track has been updated so update tps:
6460 trw_layer_cancel_tps_of_track ( vtl, trk );
6461
6462 // Inform user how much was deleted as it's not obvious from the normal view
6463 gchar str[64];
6464 const gchar *tmp_str = ngettext("Deleted %ld point", "Deleted %ld points", removed);
6465 g_snprintf(str, 64, tmp_str, removed);
6466 a_dialog_info_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), str);
6467
da121f9b 6468 vik_layer_emit_update ( VIK_LAYER(vtl) );
b6eda120
RN
6469}
6470
6579ca1f
RN
6471/**
6472 * Delete adjacent track points with the same timestamp
6473 * Normally new tracks that are 'routes' won't have any timestamps so should be OK to clean up the track
6474 */
19782ffd 6475static void trw_layer_delete_points_same_time ( menu_array_sublayer values )
6579ca1f 6476{
19782ffd 6477 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
0d2b891f 6478 VikTrack *trk;
19782ffd
RN
6479 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
6480 trk = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
0d2b891f 6481 else
19782ffd 6482 trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
0d2b891f
RN
6483
6484 if ( !trk )
6485 return;
6579ca1f
RN
6486
6487 gulong removed = vik_track_remove_same_time_points ( trk );
6488
6489 // Track has been updated so update tps:
6490 trw_layer_cancel_tps_of_track ( vtl, trk );
6491
6492 // Inform user how much was deleted as it's not obvious from the normal view
6493 gchar str[64];
6494 const gchar *tmp_str = ngettext("Deleted %ld point", "Deleted %ld points", removed);
6495 g_snprintf(str, 64, tmp_str, removed);
6496 a_dialog_info_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), str);
6497
da121f9b 6498 vik_layer_emit_update ( VIK_LAYER(vtl) );
6579ca1f
RN
6499}
6500
491794be
RN
6501/**
6502 * Insert a point
6503 */
19782ffd 6504static void trw_layer_insert_point_after ( menu_array_sublayer values )
491794be 6505{
19782ffd 6506 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
491794be 6507 VikTrack *track;
19782ffd
RN
6508 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
6509 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
491794be 6510 else
19782ffd 6511 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
491794be
RN
6512
6513 if ( ! track )
6514 return;
6515
6516 trw_layer_insert_tp_beside_current_tp ( vtl, FALSE );
6517
6518 vik_layer_emit_update ( VIK_LAYER(vtl) );
6519}
6520
19782ffd 6521static void trw_layer_insert_point_before ( menu_array_sublayer values )
491794be 6522{
19782ffd 6523 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
491794be 6524 VikTrack *track;
19782ffd
RN
6525 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
6526 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
491794be 6527 else
19782ffd 6528 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
491794be
RN
6529
6530 if ( ! track )
6531 return;
6532
6533 trw_layer_insert_tp_beside_current_tp ( vtl, TRUE );
6534
6535 vik_layer_emit_update ( VIK_LAYER(vtl) );
6536}
6537
eb9fd106
RN
6538/**
6539 * Reverse a track
6540 */
19782ffd 6541static void trw_layer_reverse ( menu_array_sublayer values )
eb9fd106 6542{
19782ffd 6543 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
0d2b891f 6544 VikTrack *track;
19782ffd
RN
6545 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
6546 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
0d2b891f 6547 else
19782ffd 6548 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
0d2b891f
RN
6549
6550 if ( ! track )
6551 return;
eb9fd106 6552
eb9fd106
RN
6553 vik_track_reverse ( track );
6554
19782ffd 6555 vik_layer_emit_update ( VIK_LAYER(vtl) );
eb9fd106
RN
6556}
6557
be3b5aa8
RN
6558/**
6559 * Open a diary at the specified date
6560 */
6561static void trw_layer_diary_open ( VikTrwLayer *vtl, const gchar *date_str )
6562{
6563 GError *err = NULL;
6564 gchar *cmd = g_strdup_printf ( "%s%s", "rednotebook --date=", date_str );
6565 if ( ! g_spawn_command_line_async ( cmd, &err ) ) {
6566 a_dialog_error_msg_extra ( VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Could not launch %s to open file."), "rednotebook" );
6567 g_error_free ( err );
6568 }
6569 g_free ( cmd );
6570}
6571
6572/**
6573 * Open a diary at the date of the track or waypoint
6574 */
6575static void trw_layer_diary ( menu_array_sublayer values )
6576{
6577 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
6578
6579 if ( GPOINTER_TO_INT(values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_TRACK ) {
6580 VikTrack *trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
6581 if ( ! trk )
6582 return;
6583
6584 gchar date_buf[20];
6585 date_buf[0] = '\0';
6586 if ( trk->trackpoints && VIK_TRACKPOINT(trk->trackpoints->data)->has_timestamp ) {
6587 strftime (date_buf, sizeof(date_buf), "%Y-%m-%d", gmtime(&(VIK_TRACKPOINT(trk->trackpoints->data)->timestamp)));
6588 trw_layer_diary_open ( vtl, date_buf );
6589 }
6590 else
6591 a_dialog_info_msg ( VIK_GTK_WINDOW_FROM_LAYER(vtl), _("This track has no date information.") );
6592 }
6593 else if ( GPOINTER_TO_INT(values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_WAYPOINT ) {
6594 VikWaypoint *wpt = (VikWaypoint *) g_hash_table_lookup ( vtl->waypoints, values[MA_SUBLAYER_ID] );
6595 if ( ! wpt )
6596 return;
6597
6598 gchar date_buf[20];
6599 date_buf[0] = '\0';
6600 if ( wpt->has_timestamp ) {
6601 strftime (date_buf, sizeof(date_buf), "%Y-%m-%d", gmtime(&(wpt->timestamp)));
6602 trw_layer_diary_open ( vtl, date_buf );
6603 }
6604 else
6605 a_dialog_info_msg ( VIK_GTK_WINDOW_FROM_LAYER(vtl), _("This waypoint has no date information.") );
6606 }
6607}
6608
20b671c3
RN
6609/**
6610 * Similar to trw_layer_enum_item, but this uses a sorted method
6611 */
ce4bd1cf 6612/* Currently unused
20b671c3
RN
6613static void trw_layer_sorted_name_list(gpointer key, gpointer value, gpointer udata)
6614{
6615 GList **list = (GList**)udata;
ce4bd1cf 6616 // *list = g_list_prepend(*all, key); //unsorted method
20b671c3
RN
6617 // Sort named list alphabetically
6618 *list = g_list_insert_sorted_with_data (*list, key, sort_alphabetically, NULL);
6619}
ce4bd1cf 6620*/
20b671c3 6621
c9570f86
RN
6622/**
6623 * Now Waypoint specific sort
6624 */
6625static void trw_layer_sorted_wp_id_by_name_list (const gpointer id, const VikWaypoint *wp, gpointer udata)
6626{
6627 GList **list = (GList**)udata;
6628 // Sort named list alphabetically
6629 *list = g_list_insert_sorted_with_data (*list, wp->name, sort_alphabetically, NULL);
6630}
6631
ce4bd1cf
RN
6632/**
6633 * Track specific sort
6634 */
6635static void trw_layer_sorted_track_id_by_name_list (const gpointer id, const VikTrack *trk, gpointer udata)
6636{
6637 GList **list = (GList**)udata;
6638 // Sort named list alphabetically
6639 *list = g_list_insert_sorted_with_data (*list, trk->name, sort_alphabetically, NULL);
6640}
6641
3d2ad4f8
RN
6642
6643typedef struct {
6644 gboolean has_same_track_name;
6645 const gchar *same_track_name;
6646} same_track_name_udata;
6647
6648static gint check_tracks_for_same_name ( gconstpointer aa, gconstpointer bb, gpointer udata )
6649{
6650 const gchar* namea = (const gchar*) aa;
6651 const gchar* nameb = (const gchar*) bb;
6652
6653 // the test
6654 gint result = strcmp ( namea, nameb );
6655
6656 if ( result == 0 ) {
6657 // Found two names the same
6658 same_track_name_udata *user_data = udata;
6659 user_data->has_same_track_name = TRUE;
6660 user_data->same_track_name = namea;
6661 }
6662
6663 // Leave ordering the same
6664 return 0;
6665}
6666
6667/**
0d2b891f 6668 * Find out if any tracks have the same name in this hash table
3d2ad4f8 6669 */
0d2b891f 6670static gboolean trw_layer_has_same_track_names ( GHashTable *ht_tracks )
3d2ad4f8
RN
6671{
6672 // Sort items by name, then compare if any next to each other are the same
6673
6674 GList *track_names = NULL;
0d2b891f 6675 g_hash_table_foreach ( ht_tracks, (GHFunc) trw_layer_sorted_track_id_by_name_list, &track_names );
3d2ad4f8
RN
6676
6677 // No tracks
6678 if ( ! track_names )
6679 return FALSE;
6680
6681 same_track_name_udata udata;
6682 udata.has_same_track_name = FALSE;
6683
6684 // Use sort routine to traverse list comparing items
6685 // Don't care how this list ends up ordered ( doesn't actually change ) - care about the returned status
6686 GList *dummy_list = g_list_sort_with_data ( track_names, check_tracks_for_same_name, &udata );
6687 // Still no tracks...
6688 if ( ! dummy_list )
6689 return FALSE;
6690
6691 return udata.has_same_track_name;
6692}
6693
6694/**
0d2b891f 6695 * Force unqiue track names for the track table specified
3d2ad4f8 6696 * Note the panel is a required parameter to enable the update of the names displayed
0d2b891f 6697 * Specify if on tracks or else on routes
3d2ad4f8 6698 */
0d2b891f 6699static void vik_trw_layer_uniquify_tracks ( VikTrwLayer *vtl, VikLayersPanel *vlp, GHashTable *track_table, gboolean ontrack )
3d2ad4f8
RN
6700{
6701 // . Search list for an instance of repeated name
6702 // . get track of this name
6703 // . create new name
6704 // . rename track & update equiv. treeview iter
6705 // . repeat until all different
6706
6707 same_track_name_udata udata;
6708
6709 GList *track_names = NULL;
6710 udata.has_same_track_name = FALSE;
6711 udata.same_track_name = NULL;
6712
0d2b891f 6713 g_hash_table_foreach ( track_table, (GHFunc) trw_layer_sorted_track_id_by_name_list, &track_names );
3d2ad4f8
RN
6714
6715 // No tracks
6716 if ( ! track_names )
6717 return;
6718
6719 GList *dummy_list1 = g_list_sort_with_data ( track_names, check_tracks_for_same_name, &udata );
6720
6721 // Still no tracks...
6722 if ( ! dummy_list1 )
6723 return;
6724
6725 while ( udata.has_same_track_name ) {
6726
6727 // Find a track with the same name
0d2b891f
RN
6728 VikTrack *trk;
6729 if ( ontrack )
6730 trk = vik_trw_layer_get_track ( vtl, (gpointer) udata.same_track_name );
6731 else
6732 trk = vik_trw_layer_get_route ( vtl, (gpointer) udata.same_track_name );
3d2ad4f8
RN
6733
6734 if ( ! trk ) {
6735 // Broken :(
6736 g_critical("Houston, we've had a problem.");
6737 vik_statusbar_set_message ( vik_window_get_statusbar (VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl))), VIK_STATUSBAR_INFO,
6738 _("Internal Error in vik_trw_layer_uniquify_tracks") );
6739 return;
6740 }
6741
6742 // Rename it
9748531a 6743 gchar *newname = trw_layer_new_unique_sublayer_name ( vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, udata.same_track_name );
3d2ad4f8
RN
6744 vik_track_set_name ( trk, newname );
6745
6746 trku_udata udataU;
6747 udataU.trk = trk;
6748 udataU.uuid = NULL;
6749
6750 // Need want key of it for treeview update
0d2b891f 6751 gpointer *trkf = g_hash_table_find ( track_table, (GHRFunc) trw_layer_track_find_uuid, &udataU );
3d2ad4f8
RN
6752
6753 if ( trkf && udataU.uuid ) {
6754
0d2b891f
RN
6755 GtkTreeIter *it;
6756 if ( ontrack )
6757 it = g_hash_table_lookup ( vtl->tracks_iters, udataU.uuid );
6758 else
6759 it = g_hash_table_lookup ( vtl->routes_iters, udataU.uuid );
3d2ad4f8
RN
6760
6761 if ( it ) {
6762 vik_treeview_item_set_name ( VIK_LAYER(vtl)->vt, it, newname );
c9cac058
RN
6763 if ( ontrack )
6764 vik_treeview_sort_children ( VIK_LAYER(vtl)->vt, &(vtl->tracks_iter), vtl->wp_sort_order );
6765 else
6766 vik_treeview_sort_children ( VIK_LAYER(vtl)->vt, &(vtl->routes_iter), vtl->wp_sort_order );
3d2ad4f8
RN
6767 }
6768 }
6769
6770 // Start trying to find same names again...
6771 track_names = NULL;
0d2b891f 6772 g_hash_table_foreach ( track_table, (GHFunc) trw_layer_sorted_track_id_by_name_list, &track_names );
3d2ad4f8
RN
6773 udata.has_same_track_name = FALSE;
6774 GList *dummy_list2 = g_list_sort_with_data ( track_names, check_tracks_for_same_name, &udata );
6775
6776 // No tracks any more - give up searching
6777 if ( ! dummy_list2 )
6778 udata.has_same_track_name = FALSE;
6779 }
6780
6781 // Update
6782 vik_layers_panel_emit_update ( vlp );
6783}
6784
19782ffd 6785static void trw_layer_sort_order_a2z ( menu_array_sublayer values )
c9cac058 6786{
19782ffd 6787 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
c9cac058
RN
6788 GtkTreeIter *iter;
6789
19782ffd 6790 switch (GPOINTER_TO_INT (values[MA_SUBTYPE])) {
c9cac058
RN
6791 case VIK_TRW_LAYER_SUBLAYER_TRACKS:
6792 iter = &(vtl->tracks_iter);
6793 vtl->track_sort_order = VL_SO_ALPHABETICAL_ASCENDING;
6794 break;
6795 case VIK_TRW_LAYER_SUBLAYER_ROUTES:
6796 iter = &(vtl->routes_iter);
6797 vtl->track_sort_order = VL_SO_ALPHABETICAL_ASCENDING;
6798 break;
6799 default: // VIK_TRW_LAYER_SUBLAYER_WAYPOINTS:
6800 iter = &(vtl->waypoints_iter);
6801 vtl->wp_sort_order = VL_SO_ALPHABETICAL_ASCENDING;
6802 break;
6803 }
6804
6805 vik_treeview_sort_children ( VIK_LAYER(vtl)->vt, iter, VL_SO_ALPHABETICAL_ASCENDING );
6806}
6807
19782ffd 6808static void trw_layer_sort_order_z2a ( menu_array_sublayer values )
c9cac058 6809{
19782ffd 6810 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
c9cac058
RN
6811 GtkTreeIter *iter;
6812
19782ffd 6813 switch (GPOINTER_TO_INT (values[MA_SUBTYPE])) {
c9cac058
RN
6814 case VIK_TRW_LAYER_SUBLAYER_TRACKS:
6815 iter = &(vtl->tracks_iter);
6816 vtl->track_sort_order = VL_SO_ALPHABETICAL_DESCENDING;
6817 break;
6818 case VIK_TRW_LAYER_SUBLAYER_ROUTES:
6819 iter = &(vtl->routes_iter);
6820 vtl->track_sort_order = VL_SO_ALPHABETICAL_DESCENDING;
6821 break;
6822 default: // VIK_TRW_LAYER_SUBLAYER_WAYPOINTS:
6823 iter = &(vtl->waypoints_iter);
6824 vtl->wp_sort_order = VL_SO_ALPHABETICAL_DESCENDING;
6825 break;
6826 }
6827
6828 vik_treeview_sort_children ( VIK_LAYER(vtl)->vt, iter, VL_SO_ALPHABETICAL_DESCENDING );
6829}
6830
20b671c3
RN
6831/**
6832 *
6833 */
1c2083ba 6834static void trw_layer_delete_tracks_from_selection ( menu_array_layer values )
20b671c3 6835{
1c2083ba 6836 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
20b671c3 6837 GList *all = NULL;
ce4bd1cf 6838
3d2ad4f8 6839 // Ensure list of track names offered is unique
0d2b891f 6840 if ( trw_layer_has_same_track_names ( vtl->tracks ) ) {
3d2ad4f8
RN
6841 if ( a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
6842 _("Multiple entries with the same name exist. This method only works with unique names. Force unique names now?"), NULL ) ) {
1c2083ba 6843 vik_trw_layer_uniquify_tracks ( vtl, VIK_LAYERS_PANEL(values[MA_VLP]), vtl->tracks, TRUE );
3d2ad4f8
RN
6844 }
6845 else
6846 return;
6847 }
ce4bd1cf 6848
20b671c3 6849 // Sort list alphabetically for better presentation
ce4bd1cf 6850 g_hash_table_foreach(vtl->tracks, (GHFunc) trw_layer_sorted_track_id_by_name_list, &all);
20b671c3
RN
6851
6852 if ( ! all ) {
6853 a_dialog_error_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), _("No tracks found"));
6854 return;
6855 }
6856
6857 // Get list of items to delete from the user
6858 GList *delete_list = a_dialog_select_from_list(VIK_GTK_WINDOW_FROM_LAYER(vtl),
6859 all,
6860 TRUE,
6861 _("Delete Selection"),
6862 _("Select tracks to delete"));
6863 g_list_free(all);
6864
6865 // Delete requested tracks
6866 // since specificly requested, IMHO no need for extra confirmation
6867 if ( delete_list ) {
6868 GList *l;
6869 for (l = delete_list; l != NULL; l = g_list_next(l)) {
3d2ad4f8 6870 // This deletes first trk it finds of that name (but uniqueness is enforced above)
0d2b891f
RN
6871 trw_layer_delete_track_by_name (vtl, l->data, vtl->tracks);
6872 }
6873 g_list_free(delete_list);
da121f9b 6874 vik_layer_emit_update( VIK_LAYER(vtl) );
0d2b891f
RN
6875 }
6876}
6877
6878/**
6879 *
6880 */
1c2083ba 6881static void trw_layer_delete_routes_from_selection ( menu_array_layer values )
0d2b891f 6882{
1c2083ba 6883 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
0d2b891f
RN
6884 GList *all = NULL;
6885
6886 // Ensure list of track names offered is unique
6887 if ( trw_layer_has_same_track_names ( vtl->routes ) ) {
6888 if ( a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
6889 _("Multiple entries with the same name exist. This method only works with unique names. Force unique names now?"), NULL ) ) {
1c2083ba 6890 vik_trw_layer_uniquify_tracks ( vtl, VIK_LAYERS_PANEL(values[MA_VLP]), vtl->routes, FALSE );
0d2b891f
RN
6891 }
6892 else
6893 return;
6894 }
6895
6896 // Sort list alphabetically for better presentation
6897 g_hash_table_foreach(vtl->routes, (GHFunc) trw_layer_sorted_track_id_by_name_list, &all);
6898
6899 if ( ! all ) {
6900 a_dialog_error_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), _("No routes found"));
6901 return;
6902 }
6903
6904 // Get list of items to delete from the user
6905 GList *delete_list = a_dialog_select_from_list ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
6906 all,
6907 TRUE,
6908 _("Delete Selection"),
6909 _("Select routes to delete") );
6910 g_list_free(all);
6911
6912 // Delete requested routes
6913 // since specificly requested, IMHO no need for extra confirmation
6914 if ( delete_list ) {
6915 GList *l;
6916 for (l = delete_list; l != NULL; l = g_list_next(l)) {
6917 // This deletes first route it finds of that name (but uniqueness is enforced above)
6918 trw_layer_delete_track_by_name (vtl, l->data, vtl->routes);
20b671c3
RN
6919 }
6920 g_list_free(delete_list);
da121f9b 6921 vik_layer_emit_update( VIK_LAYER(vtl) );
20b671c3
RN
6922 }
6923}
6924
33a89785
RN
6925typedef struct {
6926 gboolean has_same_waypoint_name;
6927 const gchar *same_waypoint_name;
6928} same_waypoint_name_udata;
6929
6930static gint check_waypoints_for_same_name ( gconstpointer aa, gconstpointer bb, gpointer udata )
6931{
6932 const gchar* namea = (const gchar*) aa;
6933 const gchar* nameb = (const gchar*) bb;
6934
6935 // the test
6936 gint result = strcmp ( namea, nameb );
6937
6938 if ( result == 0 ) {
6939 // Found two names the same
6940 same_waypoint_name_udata *user_data = udata;
6941 user_data->has_same_waypoint_name = TRUE;
6942 user_data->same_waypoint_name = namea;
6943 }
6944
6945 // Leave ordering the same
6946 return 0;
6947}
6948
6949/**
6950 * Find out if any waypoints have the same name in this layer
6951 */
6952gboolean trw_layer_has_same_waypoint_names ( VikTrwLayer *vtl )
6953{
6954 // Sort items by name, then compare if any next to each other are the same
6955
6956 GList *waypoint_names = NULL;
6957 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_sorted_wp_id_by_name_list, &waypoint_names );
6958
6959 // No waypoints
6960 if ( ! waypoint_names )
6961 return FALSE;
6962
6963 same_waypoint_name_udata udata;
6964 udata.has_same_waypoint_name = FALSE;
6965
6966 // Use sort routine to traverse list comparing items
6967 // Don't care how this list ends up ordered ( doesn't actually change ) - care about the returned status
6968 GList *dummy_list = g_list_sort_with_data ( waypoint_names, check_waypoints_for_same_name, &udata );
6969 // Still no waypoints...
6970 if ( ! dummy_list )
6971 return FALSE;
6972
6973 return udata.has_same_waypoint_name;
6974}
6975
6976/**
6977 * Force unqiue waypoint names for this layer
6978 * Note the panel is a required parameter to enable the update of the names displayed
6979 */
6980static void vik_trw_layer_uniquify_waypoints ( VikTrwLayer *vtl, VikLayersPanel *vlp )
6981{
6982 // . Search list for an instance of repeated name
6983 // . get waypoint of this name
6984 // . create new name
6985 // . rename waypoint & update equiv. treeview iter
6986 // . repeat until all different
6987
6988 same_waypoint_name_udata udata;
6989
6990 GList *waypoint_names = NULL;
6991 udata.has_same_waypoint_name = FALSE;
6992 udata.same_waypoint_name = NULL;
6993
6994 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_sorted_wp_id_by_name_list, &waypoint_names );
6995
6996 // No waypoints
6997 if ( ! waypoint_names )
6998 return;
6999
7000 GList *dummy_list1 = g_list_sort_with_data ( waypoint_names, check_waypoints_for_same_name, &udata );
7001
7002 // Still no waypoints...
7003 if ( ! dummy_list1 )
7004 return;
7005
7006 while ( udata.has_same_waypoint_name ) {
7007
7008 // Find a waypoint with the same name
7009 VikWaypoint *waypoint = vik_trw_layer_get_waypoint ( vtl, (gpointer) udata.same_waypoint_name );
7010
7011 if ( ! waypoint ) {
7012 // Broken :(
7013 g_critical("Houston, we've had a problem.");
7014 vik_statusbar_set_message ( vik_window_get_statusbar (VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl))), VIK_STATUSBAR_INFO,
7015 _("Internal Error in vik_trw_layer_uniquify_waypoints") );
7016 return;
7017 }
7018
7019 // Rename it
9748531a 7020 gchar *newname = trw_layer_new_unique_sublayer_name ( vtl, VIK_TRW_LAYER_SUBLAYER_WAYPOINT, udata.same_waypoint_name );
33a89785 7021
6761d8a4 7022 trw_layer_waypoint_rename ( vtl, waypoint, newname );
33a89785
RN
7023
7024 // Start trying to find same names again...
7025 waypoint_names = NULL;
7026 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_sorted_wp_id_by_name_list, &waypoint_names );
7027 udata.has_same_waypoint_name = FALSE;
7028 GList *dummy_list2 = g_list_sort_with_data ( waypoint_names, check_waypoints_for_same_name, &udata );
7029
7030 // No waypoints any more - give up searching
7031 if ( ! dummy_list2 )
7032 udata.has_same_waypoint_name = FALSE;
7033 }
7034
7035 // Update
7036 vik_layers_panel_emit_update ( vlp );
7037}
7038
20b671c3
RN
7039/**
7040 *
7041 */
1c2083ba 7042static void trw_layer_delete_waypoints_from_selection ( menu_array_layer values )
20b671c3 7043{
1c2083ba 7044 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
20b671c3
RN
7045 GList *all = NULL;
7046
33a89785
RN
7047 // Ensure list of waypoint names offered is unique
7048 if ( trw_layer_has_same_waypoint_names ( vtl ) ) {
7049 if ( a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
7050 _("Multiple entries with the same name exist. This method only works with unique names. Force unique names now?"), NULL ) ) {
1c2083ba 7051 vik_trw_layer_uniquify_waypoints ( vtl, VIK_LAYERS_PANEL(values[MA_VLP]) );
33a89785
RN
7052 }
7053 else
7054 return;
7055 }
c9570f86 7056
20b671c3 7057 // Sort list alphabetically for better presentation
c9570f86 7058 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_sorted_wp_id_by_name_list, &all);
20b671c3
RN
7059 if ( ! all ) {
7060 a_dialog_error_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), _("No waypoints found"));
7061 return;
7062 }
7063
7064 all = g_list_sort_with_data(all, sort_alphabetically, NULL);
7065
7066 // Get list of items to delete from the user
7067 GList *delete_list = a_dialog_select_from_list(VIK_GTK_WINDOW_FROM_LAYER(vtl),
7068 all,
7069 TRUE,
7070 _("Delete Selection"),
7071 _("Select waypoints to delete"));
7072 g_list_free(all);
7073
7074 // Delete requested waypoints
7075 // since specificly requested, IMHO no need for extra confirmation
7076 if ( delete_list ) {
7077 GList *l;
7078 for (l = delete_list; l != NULL; l = g_list_next(l)) {
33a89785 7079 // This deletes first waypoint it finds of that name (but uniqueness is enforced above)
c9570f86 7080 trw_layer_delete_waypoint_by_name (vtl, l->data);
20b671c3
RN
7081 }
7082 g_list_free(delete_list);
aa0665e5
RN
7083
7084 trw_layer_calculate_bounds_waypoints ( vtl );
da121f9b 7085 vik_layer_emit_update( VIK_LAYER(vtl) );
20b671c3
RN
7086 }
7087
7088}
111fa174 7089
89fdc417
RN
7090/**
7091 *
7092 */
7093static void trw_layer_iter_visibility_toggle ( gpointer id, GtkTreeIter *it, VikTreeview *vt )
7094{
7095 vik_treeview_item_toggle_visible ( vt, it );
7096}
7097
7098/**
7099 *
7100 */
7101static void trw_layer_iter_visibility ( gpointer id, GtkTreeIter *it, gpointer vis_data[2] )
7102{
7103 vik_treeview_item_set_visible ( (VikTreeview*)vis_data[0], it, GPOINTER_TO_INT (vis_data[1]) );
7104}
7105
7106/**
7107 *
7108 */
7109static void trw_layer_waypoints_visibility ( gpointer id, VikWaypoint *wp, gpointer on_off )
7110{
7111 wp->visible = GPOINTER_TO_INT (on_off);
7112}
7113
7114/**
7115 *
7116 */
7117static void trw_layer_waypoints_toggle_visibility ( gpointer id, VikWaypoint *wp )
7118{
7119 wp->visible = !wp->visible;
7120}
7121
7122/**
7123 *
7124 */
1c2083ba 7125static void trw_layer_waypoints_visibility_off ( menu_array_layer values )
89fdc417 7126{
1c2083ba 7127 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
89fdc417
RN
7128 gpointer vis_data[2] = { VIK_LAYER(vtl)->vt, GINT_TO_POINTER(FALSE) };
7129 g_hash_table_foreach ( vtl->waypoints_iters, (GHFunc) trw_layer_iter_visibility, vis_data );
7130 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_waypoints_visibility, vis_data[1] );
7131 // Redraw
7132 vik_layer_emit_update ( VIK_LAYER(vtl) );
7133}
7134
7135/**
7136 *
7137 */
1c2083ba 7138static void trw_layer_waypoints_visibility_on ( menu_array_layer values )
89fdc417 7139{
1c2083ba 7140 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
89fdc417
RN
7141 gpointer vis_data[2] = { VIK_LAYER(vtl)->vt, GINT_TO_POINTER(TRUE) };
7142 g_hash_table_foreach ( vtl->waypoints_iters, (GHFunc) trw_layer_iter_visibility, vis_data );
7143 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_waypoints_visibility, vis_data[1] );
7144 // Redraw
7145 vik_layer_emit_update ( VIK_LAYER(vtl) );
7146}
7147
7148/**
7149 *
7150 */
1c2083ba 7151static void trw_layer_waypoints_visibility_toggle ( menu_array_layer values )
89fdc417 7152{
1c2083ba 7153 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
89fdc417
RN
7154 g_hash_table_foreach ( vtl->waypoints_iters, (GHFunc) trw_layer_iter_visibility_toggle, VIK_LAYER(vtl)->vt );
7155 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_waypoints_toggle_visibility, NULL );
7156 // Redraw
7157 vik_layer_emit_update ( VIK_LAYER(vtl) );
7158}
7159
7160/**
7161 *
7162 */
7163static void trw_layer_tracks_visibility ( gpointer id, VikTrack *trk, gpointer on_off )
7164{
7165 trk->visible = GPOINTER_TO_INT (on_off);
7166}
7167
7168/**
7169 *
7170 */
7171static void trw_layer_tracks_toggle_visibility ( gpointer id, VikTrack *trk )
7172{
7173 trk->visible = !trk->visible;
7174}
7175
7176/**
7177 *
7178 */
1c2083ba 7179static void trw_layer_tracks_visibility_off ( menu_array_layer values )
89fdc417 7180{
1c2083ba 7181 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
89fdc417
RN
7182 gpointer vis_data[2] = { VIK_LAYER(vtl)->vt, GINT_TO_POINTER(FALSE) };
7183 g_hash_table_foreach ( vtl->tracks_iters, (GHFunc) trw_layer_iter_visibility, vis_data );
7184 g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_tracks_visibility, vis_data[1] );
7185 // Redraw
7186 vik_layer_emit_update ( VIK_LAYER(vtl) );
7187}
7188
7189/**
7190 *
7191 */
1c2083ba 7192static void trw_layer_tracks_visibility_on ( menu_array_layer values )
89fdc417 7193{
1c2083ba 7194 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
89fdc417
RN
7195 gpointer vis_data[2] = { VIK_LAYER(vtl)->vt, GINT_TO_POINTER(TRUE) };
7196 g_hash_table_foreach ( vtl->tracks_iters, (GHFunc) trw_layer_iter_visibility, vis_data );
7197 g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_tracks_visibility, vis_data[1] );
7198 // Redraw
7199 vik_layer_emit_update ( VIK_LAYER(vtl) );
7200}
7201
7202/**
7203 *
7204 */
1c2083ba 7205static void trw_layer_tracks_visibility_toggle ( menu_array_layer values )
89fdc417 7206{
1c2083ba 7207 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
89fdc417
RN
7208 g_hash_table_foreach ( vtl->tracks_iters, (GHFunc) trw_layer_iter_visibility_toggle, VIK_LAYER(vtl)->vt );
7209 g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_tracks_toggle_visibility, NULL );
7210 // Redraw
7211 vik_layer_emit_update ( VIK_LAYER(vtl) );
7212}
7213
7214/**
7215 *
7216 */
1c2083ba 7217static void trw_layer_routes_visibility_off ( menu_array_layer values )
89fdc417 7218{
1c2083ba 7219 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
89fdc417
RN
7220 gpointer vis_data[2] = { VIK_LAYER(vtl)->vt, GINT_TO_POINTER(FALSE) };
7221 g_hash_table_foreach ( vtl->routes_iters, (GHFunc) trw_layer_iter_visibility, vis_data );
7222 g_hash_table_foreach ( vtl->routes, (GHFunc) trw_layer_tracks_visibility, vis_data[1] );
7223 // Redraw
7224 vik_layer_emit_update ( VIK_LAYER(vtl) );
7225}
7226
7227/**
7228 *
7229 */
1c2083ba 7230static void trw_layer_routes_visibility_on ( menu_array_layer values )
89fdc417 7231{
1c2083ba 7232 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
89fdc417
RN
7233 gpointer vis_data[2] = { VIK_LAYER(vtl)->vt, GINT_TO_POINTER(TRUE) };
7234 g_hash_table_foreach ( vtl->routes_iters, (GHFunc) trw_layer_iter_visibility, vis_data );
7235 g_hash_table_foreach ( vtl->routes, (GHFunc) trw_layer_tracks_visibility, vis_data[1] );
7236 // Redraw
7237 vik_layer_emit_update ( VIK_LAYER(vtl) );
7238}
7239
7240/**
7241 *
7242 */
1c2083ba 7243static void trw_layer_routes_visibility_toggle ( menu_array_layer values )
89fdc417 7244{
1c2083ba 7245 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
89fdc417
RN
7246 g_hash_table_foreach ( vtl->routes_iters, (GHFunc) trw_layer_iter_visibility_toggle, VIK_LAYER(vtl)->vt );
7247 g_hash_table_foreach ( vtl->routes, (GHFunc) trw_layer_tracks_toggle_visibility, NULL );
7248 // Redraw
7249 vik_layer_emit_update ( VIK_LAYER(vtl) );
7250}
7251
00176e85
RN
7252/**
7253 * vik_trw_layer_build_waypoint_list_t:
7254 *
7255 * Helper function to construct a list of #vik_trw_waypoint_list_t
7256 */
7257GList *vik_trw_layer_build_waypoint_list_t ( VikTrwLayer *vtl, GList *waypoints )
7258{
7259 GList *waypoints_and_layers = NULL;
7260 // build waypoints_and_layers list
7261 while ( waypoints ) {
7262 vik_trw_waypoint_list_t *vtdl = g_malloc (sizeof(vik_trw_waypoint_list_t));
7263 vtdl->wpt = VIK_WAYPOINT(waypoints->data);
7264 vtdl->vtl = vtl;
7265 waypoints_and_layers = g_list_prepend ( waypoints_and_layers, vtdl );
7266 waypoints = g_list_next ( waypoints );
7267 }
7268 return waypoints_and_layers;
7269}
7270
7271/**
7272 * trw_layer_create_waypoint_list:
7273 *
7274 * Create the latest list of waypoints with the associated layer(s)
7275 * Although this will always be from a single layer here
7276 */
7277static GList* trw_layer_create_waypoint_list ( VikLayer *vl, gpointer user_data )
7278{
7279 VikTrwLayer *vtl = VIK_TRW_LAYER(vl);
7280 GList *waypoints = g_hash_table_get_values ( vik_trw_layer_get_waypoints(vtl) );
7281
7282 return vik_trw_layer_build_waypoint_list_t ( vtl, waypoints );
7283}
7284
43984ba1
RN
7285/**
7286 * trw_layer_analyse_close:
7287 *
7288 * Stuff to do on dialog closure
7289 */
7290static void trw_layer_analyse_close ( GtkWidget *dialog, gint resp, VikLayer* vl )
7291{
7292 VikTrwLayer *vtl = VIK_TRW_LAYER(vl);
7293 gtk_widget_destroy ( dialog );
7294 vtl->tracks_analysis_dialog = NULL;
7295}
7296
7297/**
184ec0fa 7298 * vik_trw_layer_build_track_list_t:
43984ba1 7299 *
184ec0fa 7300 * Helper function to construct a list of #vik_trw_track_list_t
43984ba1 7301 */
184ec0fa 7302GList *vik_trw_layer_build_track_list_t ( VikTrwLayer *vtl, GList *tracks )
43984ba1 7303{
43984ba1
RN
7304 GList *tracks_and_layers = NULL;
7305 // build tracks_and_layers list
43984ba1
RN
7306 while ( tracks ) {
7307 vik_trw_track_list_t *vtdl = g_malloc (sizeof(vik_trw_track_list_t));
7308 vtdl->trk = VIK_TRACK(tracks->data);
7309 vtdl->vtl = vtl;
7310 tracks_and_layers = g_list_prepend ( tracks_and_layers, vtdl );
7311 tracks = g_list_next ( tracks );
7312 }
43984ba1
RN
7313 return tracks_and_layers;
7314}
7315
184ec0fa
RN
7316/**
7317 * trw_layer_create_track_list:
7318 *
7319 * Create the latest list of tracks with the associated layer(s)
7320 * Although this will always be from a single layer here
7321 */
7322static GList* trw_layer_create_track_list ( VikLayer *vl, gpointer user_data )
7323{
7324 VikTrwLayer *vtl = VIK_TRW_LAYER(vl);
7325 GList *tracks = NULL;
7326 if ( GPOINTER_TO_INT(user_data) == VIK_TRW_LAYER_SUBLAYER_TRACKS )
7327 tracks = g_hash_table_get_values ( vik_trw_layer_get_tracks(vtl) );
7328 else
7329 tracks = g_hash_table_get_values ( vik_trw_layer_get_routes(vtl) );
7330
7331 return vik_trw_layer_build_track_list_t ( vtl, tracks );
7332}
7333
1c2083ba 7334static void trw_layer_tracks_stats ( menu_array_layer values )
43984ba1 7335{
1c2083ba 7336 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
43984ba1
RN
7337 // There can only be one!
7338 if ( vtl->tracks_analysis_dialog )
7339 return;
7340
7341 vtl->tracks_analysis_dialog = vik_trw_layer_analyse_this ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
7342 VIK_LAYER(vtl)->name,
7343 VIK_LAYER(vtl),
7344 GINT_TO_POINTER(VIK_TRW_LAYER_SUBLAYER_TRACKS),
184ec0fa 7345 trw_layer_create_track_list,
43984ba1
RN
7346 trw_layer_analyse_close );
7347}
7348
7349/**
7350 *
7351 */
1c2083ba 7352static void trw_layer_routes_stats ( menu_array_layer values )
43984ba1 7353{
1c2083ba 7354 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
43984ba1
RN
7355 // There can only be one!
7356 if ( vtl->tracks_analysis_dialog )
7357 return;
7358
7359 vtl->tracks_analysis_dialog = vik_trw_layer_analyse_this ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
7360 VIK_LAYER(vtl)->name,
7361 VIK_LAYER(vtl),
7362 GINT_TO_POINTER(VIK_TRW_LAYER_SUBLAYER_ROUTES),
184ec0fa 7363 trw_layer_create_track_list,
43984ba1
RN
7364 trw_layer_analyse_close );
7365}
7366
19782ffd 7367static void trw_layer_goto_waypoint ( menu_array_sublayer values )
50a14534 7368{
19782ffd
RN
7369 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
7370 VikWaypoint *wp = g_hash_table_lookup ( vtl->waypoints, values[MA_SUBLAYER_ID] );
50a14534 7371 if ( wp )
19782ffd 7372 goto_coord ( values[MA_VLP], vtl, values[MA_VVP], &(wp->coord) );
50a14534
EB
7373}
7374
19782ffd 7375static void trw_layer_waypoint_gc_webpage ( 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] );
d64e2724
RN
7379 if ( !wp )
7380 return;
7381 gchar *webpage = g_strdup_printf("http://www.geocaching.com/seek/cache_details.aspx?wp=%s", wp->name );
19782ffd 7382 open_url(VIK_GTK_WINDOW_FROM_LAYER(VIK_LAYER(vtl)), webpage);
50a14534
EB
7383 g_free ( webpage );
7384}
7385
19782ffd 7386static void trw_layer_waypoint_webpage ( menu_array_sublayer values )
3c13fff8 7387{
19782ffd
RN
7388 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
7389 VikWaypoint *wp = g_hash_table_lookup ( vtl->waypoints, values[MA_SUBLAYER_ID] );
3c13fff8
RN
7390 if ( !wp )
7391 return;
415b0e21
RN
7392 if ( wp->url ) {
7393 open_url(VIK_GTK_WINDOW_FROM_LAYER(VIK_LAYER(vtl)), wp->url);
7394 } else if ( !strncmp(wp->comment, "http", 4) ) {
19782ffd 7395 open_url(VIK_GTK_WINDOW_FROM_LAYER(VIK_LAYER(vtl)), wp->comment);
3c13fff8 7396 } else if ( !strncmp(wp->description, "http", 4) ) {
19782ffd 7397 open_url(VIK_GTK_WINDOW_FROM_LAYER(VIK_LAYER(vtl)), wp->description);
3c13fff8
RN
7398 }
7399}
7400
a7cd93ac 7401static const gchar* trw_layer_sublayer_rename_request ( VikTrwLayer *l, const gchar *newname, gpointer vlp, gint subtype, gpointer sublayer, GtkTreeIter *iter )
50a14534
EB
7402{
7403 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT )
7404 {
c9570f86 7405 VikWaypoint *wp = g_hash_table_lookup ( l->waypoints, sublayer );
50a14534 7406
c9570f86 7407 // No actual change to the name supplied
27d267f4
RN
7408 if ( wp->name )
7409 if (strcmp(newname, wp->name) == 0 )
7410 return NULL;
50a14534 7411
c9570f86
RN
7412 VikWaypoint *wpf = vik_trw_layer_get_waypoint ( l, newname );
7413
7414 if ( wpf ) {
7415 // An existing waypoint has been found with the requested name
7416 if ( ! a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(l),
77bca466 7417 _("A waypoint with the name \"%s\" already exists. Really rename to the same name?"),
c9570f86 7418 newname ) )
8499a412 7419 return NULL;
50a14534
EB
7420 }
7421
c9570f86
RN
7422 // Update WP name and refresh the treeview
7423 vik_waypoint_set_name (wp, newname);
50a14534 7424
c9cac058
RN
7425 vik_treeview_item_set_name ( VIK_LAYER(l)->vt, iter, newname );
7426 vik_treeview_sort_children ( VIK_LAYER(l)->vt, &(l->waypoints_iter), l->wp_sort_order );
50a14534
EB
7427
7428 vik_layers_panel_emit_update ( VIK_LAYERS_PANEL(vlp) );
c9570f86
RN
7429
7430 return newname;
50a14534 7431 }
c9570f86 7432
50a14534
EB
7433 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
7434 {
ce4bd1cf 7435 VikTrack *trk = g_hash_table_lookup ( l->tracks, sublayer );
50a14534 7436
ce4bd1cf 7437 // No actual change to the name supplied
27d267f4
RN
7438 if ( trk->name )
7439 if (strcmp(newname, trk->name) == 0)
7440 return NULL;
50a14534 7441
ce4bd1cf
RN
7442 VikTrack *trkf = vik_trw_layer_get_track ( l, (gpointer) newname );
7443
7444 if ( trkf ) {
7445 // An existing track has been found with the requested name
7446 if ( ! a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(l),
77bca466 7447 _("A track with the name \"%s\" already exists. Really rename to the same name?"),
ce4bd1cf 7448 newname ) )
8499a412 7449 return NULL;
50a14534 7450 }
ce4bd1cf
RN
7451 // Update track name and refresh GUI parts
7452 vik_track_set_name (trk, newname);
50a14534 7453
ce4bd1cf
RN
7454 // Update any subwindows that could be displaying this track which has changed name
7455 // Only one Track Edit Window
7456 if ( l->current_tp_track == trk && l->tpwin ) {
7457 vik_trw_layer_tpwin_set_track_name ( l->tpwin, newname );
50a14534 7458 }
ce4bd1cf
RN
7459 // Property Dialog of the track
7460 vik_trw_layer_propwin_update ( trk );
50a14534 7461
c9cac058
RN
7462 vik_treeview_item_set_name ( VIK_LAYER(l)->vt, iter, newname );
7463 vik_treeview_sort_children ( VIK_LAYER(l)->vt, &(l->tracks_iter), l->track_sort_order );
0d2b891f
RN
7464
7465 vik_layers_panel_emit_update ( VIK_LAYERS_PANEL(vlp) );
7466
7467 return newname;
7468 }
7469
7470 if ( subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE )
7471 {
7472 VikTrack *trk = g_hash_table_lookup ( l->routes, sublayer );
7473
7474 // No actual change to the name supplied
27d267f4
RN
7475 if ( trk->name )
7476 if (strcmp(newname, trk->name) == 0)
7477 return NULL;
0d2b891f
RN
7478
7479 VikTrack *trkf = vik_trw_layer_get_route ( l, (gpointer) newname );
7480
7481 if ( trkf ) {
7482 // An existing track has been found with the requested name
7483 if ( ! a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(l),
7484 _("A route with the name \"%s\" already exists. Really rename to the same name?"),
7485 newname ) )
7486 return NULL;
7487 }
7488 // Update track name and refresh GUI parts
7489 vik_track_set_name (trk, newname);
7490
7491 // Update any subwindows that could be displaying this track which has changed name
7492 // Only one Track Edit Window
7493 if ( l->current_tp_track == trk && l->tpwin ) {
7494 vik_trw_layer_tpwin_set_track_name ( l->tpwin, newname );
7495 }
7496 // Property Dialog of the track
7497 vik_trw_layer_propwin_update ( trk );
7498
c9cac058
RN
7499 vik_treeview_item_set_name ( VIK_LAYER(l)->vt, iter, newname );
7500 vik_treeview_sort_children ( VIK_LAYER(l)->vt, &(l->tracks_iter), l->track_sort_order );
50a14534
EB
7501
7502 vik_layers_panel_emit_update ( VIK_LAYERS_PANEL(vlp) );
ce4bd1cf
RN
7503
7504 return newname;
50a14534
EB
7505 }
7506 return NULL;
7507}
7508
7509static gboolean is_valid_geocache_name ( gchar *str )
7510{
7511 gint len = strlen ( str );
0c1044e9 7512 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
7513}
7514
19782ffd 7515static void trw_layer_track_use_with_filter ( menu_array_sublayer values )
28c82d8b 7516{
19782ffd 7517 VikTrack *trk = g_hash_table_lookup ( VIK_TRW_LAYER(values[MA_VTL])->tracks, values[MA_SUBLAYER_ID] );
ce4bd1cf 7518 a_acquire_set_filter_track ( trk );
28c82d8b
EB
7519}
7520
55340efa 7521#ifdef VIK_CONFIG_GOOGLE
ce4bd1cf 7522static gboolean is_valid_google_route ( VikTrwLayer *vtl, const gpointer track_id )
bddd2056 7523{
008e972c 7524 VikTrack *tr = g_hash_table_lookup ( vtl->routes, track_id );
bddd2056
EB
7525 return ( tr && tr->comment && strlen(tr->comment) > 7 && !strncmp(tr->comment, "from:", 5) );
7526}
7527
19782ffd 7528static void trw_layer_google_route_webpage ( menu_array_sublayer values )
bddd2056 7529{
19782ffd 7530 VikTrack *tr = g_hash_table_lookup ( VIK_TRW_LAYER(values[MA_VTL])->routes, values[MA_SUBLAYER_ID] );
bddd2056
EB
7531 if ( tr ) {
7532 gchar *escaped = uri_escape ( tr->comment );
7533 gchar *webpage = g_strdup_printf("http://maps.google.com/maps?f=q&hl=en&q=%s", escaped );
19782ffd 7534 open_url(VIK_GTK_WINDOW_FROM_LAYER(VIK_LAYER(values[MA_VTL])), webpage);
bddd2056 7535 g_free ( escaped );
bddd2056
EB
7536 g_free ( webpage );
7537 }
7538}
ff1f2b3e 7539#endif
bddd2056 7540
50eadc64
RN
7541/* vlp can be NULL if necessary - i.e. right-click from a tool */
7542/* viewpoint is now available instead */
a7cd93ac 7543static gboolean trw_layer_sublayer_add_menu_items ( VikTrwLayer *l, GtkMenu *menu, gpointer vlp, gint subtype, gpointer sublayer, GtkTreeIter *iter, VikViewport *vvp )
50a14534 7544{
19782ffd 7545 static menu_array_sublayer pass_along;
50a14534
EB
7546 GtkWidget *item;
7547 gboolean rv = FALSE;
7548
19782ffd
RN
7549 pass_along[MA_VTL] = l;
7550 pass_along[MA_VLP] = vlp;
7551 pass_along[MA_SUBTYPE] = GINT_TO_POINTER (subtype);
7552 pass_along[MA_SUBLAYER_ID] = sublayer;
7553 pass_along[MA_CONFIRM] = GINT_TO_POINTER (1); // Confirm delete request
7554 pass_along[MA_VVP] = vvp;
7555 pass_along[MA_TV_ITER] = iter;
7556 pass_along[MA_MISC] = NULL; // For misc purposes - maybe track or waypoint
50a14534 7557
0d2b891f 7558 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT || subtype == VIK_TRW_LAYER_SUBLAYER_TRACK || subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE )
50a14534
EB
7559 {
7560 rv = TRUE;
7561
7562 item = gtk_image_menu_item_new_from_stock ( GTK_STOCK_PROPERTIES, NULL );
7563 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_properties_item), pass_along );
7564 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7565 gtk_widget_show ( item );
7566
21700912 7567 if (subtype == VIK_TRW_LAYER_SUBLAYER_TRACK) {
0d2b891f
RN
7568 VikTrack *tr = g_hash_table_lookup ( l->tracks, sublayer );
7569 if (tr && tr->property_dialog)
7570 gtk_widget_set_sensitive(GTK_WIDGET(item), FALSE );
7571 }
7572 if (subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE) {
7573 VikTrack *tr = g_hash_table_lookup ( l->routes, sublayer );
21700912
QT
7574 if (tr && tr->property_dialog)
7575 gtk_widget_set_sensitive(GTK_WIDGET(item), FALSE );
7576 }
7577
2cebc318
QT
7578 item = gtk_image_menu_item_new_from_stock ( GTK_STOCK_CUT, NULL );
7579 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_cut_item_cb), pass_along );
7580 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7581 gtk_widget_show ( item );
7582
7583 item = gtk_image_menu_item_new_from_stock ( GTK_STOCK_COPY, NULL );
7584 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_copy_item_cb), pass_along );
7585 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7586 gtk_widget_show ( item );
7587
50a14534
EB
7588 item = gtk_image_menu_item_new_from_stock ( GTK_STOCK_DELETE, NULL );
7589 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_item), pass_along );
7590 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7591 gtk_widget_show ( item );
7592
7593 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT )
7594 {
a8952eb1
RN
7595 // Always create separator as now there is always at least the transform menu option
7596 item = gtk_menu_item_new ();
7597 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7598 gtk_widget_show ( item );
5ede6aa6 7599
50a14534
EB
7600 /* could be a right-click using the tool */
7601 if ( vlp != NULL ) {
d6de71f9 7602 item = gtk_image_menu_item_new_with_mnemonic ( _("_Goto") );
a8952eb1 7603 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_JUMP_TO, GTK_ICON_SIZE_MENU) );
50a14534
EB
7604 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_waypoint), pass_along );
7605 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7606 gtk_widget_show ( item );
7607 }
7608
c9570f86 7609 VikWaypoint *wp = g_hash_table_lookup ( VIK_TRW_LAYER(l)->waypoints, sublayer );
5ede6aa6 7610
c9570f86
RN
7611 if ( wp && wp->name ) {
7612 if ( is_valid_geocache_name ( wp->name ) ) {
c9570f86
RN
7613 item = gtk_menu_item_new_with_mnemonic ( _("_Visit Geocache Webpage") );
7614 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_waypoint_gc_webpage), pass_along );
7615 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7616 gtk_widget_show ( item );
7617 }
41f4abac
RN
7618#ifdef VIK_CONFIG_GEOTAG
7619 item = gtk_menu_item_new_with_mnemonic ( _("Geotag _Images...") );
7620 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_geotagging_waypoint), pass_along );
7621 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7622 gtk_widget_set_tooltip_text (item, _("Geotag multiple images against this waypoint"));
7623 gtk_widget_show ( item );
7624#endif
c9570f86 7625 }
a412f3f5
RN
7626
7627 if ( wp && wp->image )
7628 {
19782ffd
RN
7629 // Set up image paramater
7630 pass_along[MA_MISC] = wp->image;
a412f3f5 7631
d6de71f9 7632 item = gtk_image_menu_item_new_with_mnemonic ( _("_Show Picture...") );
43b5e335 7633 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
7634 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_show_picture), pass_along );
7635 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7636 gtk_widget_show ( item );
b3eb3b98
RN
7637
7638#ifdef VIK_CONFIG_GEOTAG
7639 GtkWidget *geotag_submenu = gtk_menu_new ();
7640 item = gtk_image_menu_item_new_with_mnemonic ( _("Update Geotag on _Image") );
7641 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REFRESH, GTK_ICON_SIZE_MENU) );
7642 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7643 gtk_widget_show ( item );
7644 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), geotag_submenu );
7645
7646 item = gtk_menu_item_new_with_mnemonic ( _("_Update") );
7647 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_geotagging_waypoint_mtime_update), pass_along );
7648 gtk_menu_shell_append (GTK_MENU_SHELL (geotag_submenu), item);
7649 gtk_widget_show ( item );
7650
7651 item = gtk_menu_item_new_with_mnemonic ( _("Update and _Keep File Timestamp") );
7652 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_geotagging_waypoint_mtime_keep), pass_along );
7653 gtk_menu_shell_append (GTK_MENU_SHELL (geotag_submenu), item);
7654 gtk_widget_show ( item );
7655#endif
a412f3f5
RN
7656 }
7657
3c13fff8
RN
7658 if ( wp )
7659 {
415b0e21
RN
7660 if ( wp->url ||
7661 ( wp->comment && !strncmp(wp->comment, "http", 4) ) ||
3c13fff8
RN
7662 ( wp->description && !strncmp(wp->description, "http", 4) )) {
7663 item = gtk_image_menu_item_new_with_mnemonic ( _("Visit _Webpage") );
7664 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NETWORK, GTK_ICON_SIZE_MENU) );
7665 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_waypoint_webpage), pass_along );
7666 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7667 gtk_widget_show ( item );
7668 }
7669 }
50a14534
EB
7670 }
7671 }
7672
0d2b891f 7673 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINTS || subtype == VIK_TRW_LAYER_SUBLAYER_TRACKS || subtype == VIK_TRW_LAYER_SUBLAYER_ROUTES ) {
0d80642e
RN
7674 item = gtk_image_menu_item_new_from_stock ( GTK_STOCK_PASTE, NULL );
7675 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_paste_item_cb), pass_along );
7676 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7677 gtk_widget_show ( item );
7678 // TODO: only enable if suitable item is in clipboard - want to determine *which* sublayer type
7679 if ( a_clipboard_type ( ) == VIK_CLIPBOARD_DATA_SUBLAYER )
7680 gtk_widget_set_sensitive ( item, TRUE );
7681 else
7682 gtk_widget_set_sensitive ( item, FALSE );
7683
7684 // Add separator
7685 item = gtk_menu_item_new ();
7686 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7687 gtk_widget_show ( item );
7688 }
7689
5ede6aa6
RN
7690 if ( vlp && (subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINTS || subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT) )
7691 {
b66bb4ab 7692 rv = TRUE;
d6de71f9
RN
7693 item = gtk_image_menu_item_new_with_mnemonic ( _("_New Waypoint...") );
7694 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NEW, GTK_ICON_SIZE_MENU) );
5ede6aa6
RN
7695 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_wp), pass_along );
7696 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7697 gtk_widget_show ( item );
7698 }
7699
539ba038
RN
7700 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINTS )
7701 {
d6de71f9
RN
7702 item = gtk_image_menu_item_new_with_mnemonic ( _("_View All Waypoints") );
7703 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ZOOM_FIT, GTK_ICON_SIZE_MENU) );
539ba038
RN
7704 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_waypoints_view), pass_along );
7705 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7706 gtk_widget_show ( item );
7707
d6de71f9
RN
7708 item = gtk_image_menu_item_new_with_mnemonic ( _("Goto _Waypoint...") );
7709 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_JUMP_TO, GTK_ICON_SIZE_MENU) );
539ba038
RN
7710 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_wp), pass_along );
7711 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7712 gtk_widget_show ( item );
c9a5cbf9 7713
d6de71f9
RN
7714 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete _All Waypoints") );
7715 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU) );
c9a5cbf9
RN
7716 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_all_waypoints), pass_along );
7717 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7718 gtk_widget_show ( item );
20b671c3 7719
d6de71f9
RN
7720 item = gtk_image_menu_item_new_with_mnemonic ( _("_Delete Waypoints From Selection...") );
7721 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
20b671c3
RN
7722 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_waypoints_from_selection), pass_along );
7723 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7724 gtk_widget_show ( item );
89fdc417
RN
7725
7726 GtkWidget *vis_submenu = gtk_menu_new ();
7727 item = gtk_menu_item_new_with_mnemonic ( _("_Visibility") );
7728 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7729 gtk_widget_show ( item );
7730 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), vis_submenu );
7731
7732 item = gtk_image_menu_item_new_with_mnemonic ( _("_Show All Waypoints") );
7733 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_APPLY, GTK_ICON_SIZE_MENU) );
7734 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_waypoints_visibility_on), pass_along );
7735 gtk_menu_shell_append ( GTK_MENU_SHELL(vis_submenu), item );
7736 gtk_widget_show ( item );
7737
7738 item = gtk_image_menu_item_new_with_mnemonic ( _("_Hide All Waypoints") );
7739 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_CLEAR, GTK_ICON_SIZE_MENU) );
7740 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_waypoints_visibility_off), pass_along );
7741 gtk_menu_shell_append ( GTK_MENU_SHELL(vis_submenu), item );
7742 gtk_widget_show ( item );
7743
7744 item = gtk_image_menu_item_new_with_mnemonic ( _("_Toggle") );
7745 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REFRESH, GTK_ICON_SIZE_MENU) );
7746 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_waypoints_visibility_toggle), pass_along );
7747 gtk_menu_shell_append ( GTK_MENU_SHELL(vis_submenu), item );
7748 gtk_widget_show ( item );
00176e85
RN
7749
7750 item = gtk_image_menu_item_new_with_mnemonic ( _("_List Waypoints...") );
7751 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
7752 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_waypoint_list_dialog), pass_along );
7753 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
539ba038
RN
7754 }
7755
f1e68516
RN
7756 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACKS )
7757 {
7758 rv = TRUE;
7759
0d2b891f 7760 if ( l->current_track && !l->current_track->is_route ) {
37615c52
RN
7761 item = gtk_menu_item_new_with_mnemonic ( _("_Finish Track") );
7762 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_finish_track), pass_along );
7763 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7764 gtk_widget_show ( item );
7765 // Add separator
7766 item = gtk_menu_item_new ();
7767 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7768 gtk_widget_show ( item );
7769 }
7770
d6de71f9
RN
7771 item = gtk_image_menu_item_new_with_mnemonic ( _("_View All Tracks") );
7772 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ZOOM_FIT, GTK_ICON_SIZE_MENU) );
f1e68516
RN
7773 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_tracks_view), pass_along );
7774 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7775 gtk_widget_show ( item );
c9a5cbf9 7776
37615c52
RN
7777 item = gtk_image_menu_item_new_with_mnemonic ( _("_New Track") );
7778 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NEW, GTK_ICON_SIZE_MENU) );
7779 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_track), pass_along );
7780 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7781 gtk_widget_show ( item );
7782 // Make it available only when a new track *not* already in progress
7783 gtk_widget_set_sensitive ( item, ! (gboolean)GPOINTER_TO_INT(l->current_track) );
7784
d6de71f9
RN
7785 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete _All Tracks") );
7786 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU) );
c9a5cbf9
RN
7787 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_all_tracks), pass_along );
7788 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7789 gtk_widget_show ( item );
20b671c3 7790
d6de71f9
RN
7791 item = gtk_image_menu_item_new_with_mnemonic ( _("_Delete Tracks From Selection...") );
7792 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
20b671c3
RN
7793 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_tracks_from_selection), pass_along );
7794 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7795 gtk_widget_show ( item );
89fdc417
RN
7796
7797 GtkWidget *vis_submenu = gtk_menu_new ();
7798 item = gtk_menu_item_new_with_mnemonic ( _("_Visibility") );
7799 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7800 gtk_widget_show ( item );
7801 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), vis_submenu );
7802
7803 item = gtk_image_menu_item_new_with_mnemonic ( _("_Show All Tracks") );
7804 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_APPLY, GTK_ICON_SIZE_MENU) );
7805 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_tracks_visibility_on), pass_along );
7806 gtk_menu_shell_append ( GTK_MENU_SHELL(vis_submenu), item );
7807 gtk_widget_show ( item );
7808
7809 item = gtk_image_menu_item_new_with_mnemonic ( _("_Hide All Tracks") );
7810 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_CLEAR, GTK_ICON_SIZE_MENU) );
7811 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_tracks_visibility_off), pass_along );
7812 gtk_menu_shell_append ( GTK_MENU_SHELL(vis_submenu), item );
7813 gtk_widget_show ( item );
7814
7815 item = gtk_image_menu_item_new_with_mnemonic ( _("_Toggle") );
7816 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REFRESH, GTK_ICON_SIZE_MENU) );
7817 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_tracks_visibility_toggle), pass_along );
7818 gtk_menu_shell_append ( GTK_MENU_SHELL(vis_submenu), item );
07c9d42b
RN
7819
7820 item = gtk_image_menu_item_new_with_mnemonic ( _("_List Tracks...") );
7821 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
7822 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_track_list_dialog_single), pass_along );
7823 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
89fdc417 7824 gtk_widget_show ( item );
43984ba1
RN
7825
7826 item = gtk_menu_item_new_with_mnemonic ( _("_Statistics") );
7827 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_tracks_stats), pass_along );
7828 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7829 gtk_widget_show ( item );
f1e68516
RN
7830 }
7831
0d2b891f
RN
7832 if ( subtype == VIK_TRW_LAYER_SUBLAYER_ROUTES )
7833 {
7834 rv = TRUE;
7835
7836 if ( l->current_track && l->current_track->is_route ) {
7837 item = gtk_menu_item_new_with_mnemonic ( _("_Finish Route") );
7838 // Reuse finish track method
7839 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_finish_track), pass_along );
7840 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7841 gtk_widget_show ( item );
7842 // Add separator
7843 item = gtk_menu_item_new ();
7844 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7845 gtk_widget_show ( item );
7846 }
7847
7848 item = gtk_image_menu_item_new_with_mnemonic ( _("_View All Routes") );
7849 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ZOOM_FIT, GTK_ICON_SIZE_MENU) );
7850 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_routes_view), pass_along );
7851 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7852 gtk_widget_show ( item );
7853
7854 item = gtk_image_menu_item_new_with_mnemonic ( _("_New Route") );
7855 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NEW, GTK_ICON_SIZE_MENU) );
7856 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_route), pass_along );
7857 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7858 gtk_widget_show ( item );
7859 // Make it available only when a new track *not* already in progress
7860 gtk_widget_set_sensitive ( item, ! (gboolean)GPOINTER_TO_INT(l->current_track) );
7861
7862 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete _All Routes") );
7863 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU) );
7864 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_all_routes), pass_along );
7865 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7866 gtk_widget_show ( item );
7867
7868 item = gtk_image_menu_item_new_with_mnemonic ( _("_Delete Routes From Selection...") );
7869 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
7870 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_routes_from_selection), pass_along );
7871 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7872 gtk_widget_show ( item );
89fdc417
RN
7873
7874 GtkWidget *vis_submenu = gtk_menu_new ();
7875 item = gtk_menu_item_new_with_mnemonic ( _("_Visibility") );
7876 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7877 gtk_widget_show ( item );
7878 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), vis_submenu );
7879
7880 item = gtk_image_menu_item_new_with_mnemonic ( _("_Show All Routes") );
7881 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_APPLY, GTK_ICON_SIZE_MENU) );
7882 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_routes_visibility_on), pass_along );
7883 gtk_menu_shell_append ( GTK_MENU_SHELL(vis_submenu), item );
7884 gtk_widget_show ( item );
7885
7886 item = gtk_image_menu_item_new_with_mnemonic ( _("_Hide All Routes") );
7887 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_CLEAR, GTK_ICON_SIZE_MENU) );
7888 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_routes_visibility_off), pass_along );
7889 gtk_menu_shell_append ( GTK_MENU_SHELL(vis_submenu), item );
7890 gtk_widget_show ( item );
7891
7892 item = gtk_image_menu_item_new_with_mnemonic ( _("_Toggle") );
7893 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REFRESH, GTK_ICON_SIZE_MENU) );
7894 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_routes_visibility_toggle), pass_along );
7895 gtk_menu_shell_append ( GTK_MENU_SHELL(vis_submenu), item );
07c9d42b
RN
7896
7897 item = gtk_image_menu_item_new_with_mnemonic ( _("_List Routes...") );
7898 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
7899 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_track_list_dialog_single), pass_along );
7900 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7901
89fdc417 7902 gtk_widget_show ( item );
43984ba1
RN
7903
7904 item = gtk_menu_item_new_with_mnemonic ( _("_Statistics") );
7905 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_routes_stats), pass_along );
7906 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7907 gtk_widget_show ( item );
0d2b891f
RN
7908 }
7909
c9cac058
RN
7910
7911 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINTS || subtype == VIK_TRW_LAYER_SUBLAYER_TRACKS || subtype == VIK_TRW_LAYER_SUBLAYER_ROUTES ) {
7912 GtkWidget *submenu_sort = gtk_menu_new ();
7913 item = gtk_image_menu_item_new_with_mnemonic ( _("_Sort") );
7914 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REFRESH, GTK_ICON_SIZE_MENU) );
7915 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7916 gtk_widget_show ( item );
7917 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), submenu_sort );
7918
7919 item = gtk_image_menu_item_new_with_mnemonic ( _("Name _Ascending") );
7920 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_SORT_ASCENDING, GTK_ICON_SIZE_MENU) );
7921 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_sort_order_a2z), pass_along );
7922 gtk_menu_shell_append ( GTK_MENU_SHELL(submenu_sort), item );
7923 gtk_widget_show ( item );
7924
7925 item = gtk_image_menu_item_new_with_mnemonic ( _("Name _Descending") );
7926 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_SORT_DESCENDING, GTK_ICON_SIZE_MENU) );
7927 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_sort_order_z2a), pass_along );
7928 gtk_menu_shell_append ( GTK_MENU_SHELL(submenu_sort), item );
7929 gtk_widget_show ( item );
7930 }
7931
0d2b891f
RN
7932 GtkWidget *upload_submenu = gtk_menu_new ();
7933
7934 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK || subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE )
50a14534
EB
7935 {
7936 item = gtk_menu_item_new ();
7937 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7938 gtk_widget_show ( item );
7939
0d2b891f 7940 if ( l->current_track && subtype == VIK_TRW_LAYER_SUBLAYER_TRACK && !l->current_track->is_route )
37615c52 7941 item = gtk_menu_item_new_with_mnemonic ( _("_Finish Track") );
0d2b891f
RN
7942 if ( l->current_track && subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE && l->current_track->is_route )
7943 item = gtk_menu_item_new_with_mnemonic ( _("_Finish Route") );
7944 if ( l->current_track ) {
37615c52
RN
7945 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_finish_track), pass_along );
7946 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7947 gtk_widget_show ( item );
7948
7949 // Add separator
7950 item = gtk_menu_item_new ();
7951 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7952 gtk_widget_show ( item );
7953 }
7954
0d2b891f
RN
7955 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
7956 item = gtk_image_menu_item_new_with_mnemonic ( _("_View Track") );
7957 else
7958 item = gtk_image_menu_item_new_with_mnemonic ( _("_View Route") );
88a49424
RN
7959 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ZOOM_FIT, GTK_ICON_SIZE_MENU) );
7960 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_track_view), pass_along );
7961 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7962 gtk_widget_show ( item );
7963
626de648
RN
7964 item = gtk_menu_item_new_with_mnemonic ( _("_Statistics") );
7965 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_track_statistics), pass_along );
7966 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7967 gtk_widget_show ( item );
7968
88a49424 7969 GtkWidget *goto_submenu;
937b36ed 7970 goto_submenu = gtk_menu_new ();
d6de71f9
RN
7971 item = gtk_image_menu_item_new_with_mnemonic ( _("_Goto") );
7972 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_JUMP_TO, GTK_ICON_SIZE_MENU) );
937b36ed
RN
7973 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7974 gtk_widget_show ( item );
7975 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), goto_submenu );
7976
d6de71f9
RN
7977 item = gtk_image_menu_item_new_with_mnemonic ( _("_Startpoint") );
7978 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GOTO_FIRST, GTK_ICON_SIZE_MENU) );
50a14534 7979 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_startpoint), pass_along );
937b36ed 7980 gtk_menu_shell_append ( GTK_MENU_SHELL(goto_submenu), item );
50a14534
EB
7981 gtk_widget_show ( item );
7982
d6de71f9
RN
7983 item = gtk_image_menu_item_new_with_mnemonic ( _("\"_Center\"") );
7984 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_JUMP_TO, GTK_ICON_SIZE_MENU) );
50a14534 7985 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_center), pass_along );
937b36ed 7986 gtk_menu_shell_append ( GTK_MENU_SHELL(goto_submenu), item );
50a14534
EB
7987 gtk_widget_show ( item );
7988
d6de71f9
RN
7989 item = gtk_image_menu_item_new_with_mnemonic ( _("_Endpoint") );
7990 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GOTO_LAST, GTK_ICON_SIZE_MENU) );
50a14534 7991 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_endpoint), pass_along );
937b36ed 7992 gtk_menu_shell_append ( GTK_MENU_SHELL(goto_submenu), item );
50a14534 7993 gtk_widget_show ( item );
111fa174 7994
d6de71f9
RN
7995 item = gtk_image_menu_item_new_with_mnemonic ( _("_Highest Altitude") );
7996 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GOTO_TOP, GTK_ICON_SIZE_MENU) );
c28faca8
RN
7997 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_max_alt), pass_along );
7998 gtk_menu_shell_append ( GTK_MENU_SHELL(goto_submenu), item );
7999 gtk_widget_show ( item );
8000
d6de71f9
RN
8001 item = gtk_image_menu_item_new_with_mnemonic ( _("_Lowest Altitude") );
8002 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GOTO_BOTTOM, GTK_ICON_SIZE_MENU) );
c28faca8
RN
8003 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_min_alt), pass_along );
8004 gtk_menu_shell_append ( GTK_MENU_SHELL(goto_submenu), item );
8005 gtk_widget_show ( item );
8006
0d2b891f
RN
8007 // Routes don't have speeds
8008 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK ) {
8009 item = gtk_image_menu_item_new_with_mnemonic ( _("_Maximum Speed") );
8010 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_MEDIA_FORWARD, GTK_ICON_SIZE_MENU) );
8011 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_max_speed), pass_along );
8012 gtk_menu_shell_append ( GTK_MENU_SHELL(goto_submenu), item );
8013 gtk_widget_show ( item );
8014 }
03e7da75 8015
59f9414d
RN
8016 GtkWidget *combine_submenu;
8017 combine_submenu = gtk_menu_new ();
8018 item = gtk_image_menu_item_new_with_mnemonic ( _("Co_mbine") );
8019 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_CONNECT, GTK_ICON_SIZE_MENU) );
8020 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
469113fb 8021 gtk_widget_show ( item );
59f9414d 8022 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), combine_submenu );
469113fb 8023
0d2b891f
RN
8024 // Routes don't have times or segments...
8025 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK ) {
8026 item = gtk_menu_item_new_with_mnemonic ( _("_Merge By Time...") );
8027 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_merge_by_timestamp), pass_along );
8028 gtk_menu_shell_append ( GTK_MENU_SHELL(combine_submenu), item );
8029 gtk_widget_show ( item );
8030
8031 item = gtk_menu_item_new_with_mnemonic ( _("Merge _Segments") );
8032 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_merge_by_segment), pass_along );
8033 gtk_menu_shell_append ( GTK_MENU_SHELL(combine_submenu), item );
8034 gtk_widget_show ( item );
8035 }
111fa174 8036
40a68e7c 8037 item = gtk_menu_item_new_with_mnemonic ( _("Merge _With Other Tracks...") );
291edcab 8038 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_merge_with_other), pass_along );
59f9414d 8039 gtk_menu_shell_append ( GTK_MENU_SHELL(combine_submenu), item );
291edcab
HR
8040 gtk_widget_show ( item );
8041
0d2b891f
RN
8042 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
8043 item = gtk_menu_item_new_with_mnemonic ( _("_Append Track...") );
8044 else
8045 item = gtk_menu_item_new_with_mnemonic ( _("_Append Route...") );
9c34f614
RN
8046 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_append_track), pass_along );
8047 gtk_menu_shell_append ( GTK_MENU_SHELL(combine_submenu), item );
8048 gtk_widget_show ( item );
8049
6b5b6c47
RN
8050 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
8051 item = gtk_menu_item_new_with_mnemonic ( _("Append _Route...") );
8052 else
8053 item = gtk_menu_item_new_with_mnemonic ( _("Append _Track...") );
8054 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_append_other), pass_along );
8055 gtk_menu_shell_append ( GTK_MENU_SHELL(combine_submenu), item );
8056 gtk_widget_show ( item );
8057
78ac928c
RN
8058 GtkWidget *split_submenu;
8059 split_submenu = gtk_menu_new ();
8060 item = gtk_image_menu_item_new_with_mnemonic ( _("_Split") );
8061 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_DISCONNECT, GTK_ICON_SIZE_MENU) );
8062 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
291edcab 8063 gtk_widget_show ( item );
78ac928c 8064 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), split_submenu );
291edcab 8065
0d2b891f
RN
8066 // Routes don't have times or segments...
8067 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK ) {
8068 item = gtk_menu_item_new_with_mnemonic ( _("_Split By Time...") );
8069 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_split_by_timestamp), pass_along );
8070 gtk_menu_shell_append ( GTK_MENU_SHELL(split_submenu), item );
8071 gtk_widget_show ( item );
8072
8073 // ATM always enable this entry - don't want to have to analyse the track before displaying the menu - to keep the menu speedy
8074 item = gtk_menu_item_new_with_mnemonic ( _("Split Se_gments") );
8075 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_split_segments), pass_along );
8076 gtk_menu_shell_append ( GTK_MENU_SHELL(split_submenu), item );
8077 gtk_widget_show ( item );
8078 }
7114e879 8079
7306a492 8080 item = gtk_menu_item_new_with_mnemonic ( _("Split By _Number of Points...") );
af2341f3 8081 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_split_by_n_points), pass_along );
78ac928c 8082 gtk_menu_shell_append ( GTK_MENU_SHELL(split_submenu), item );
af2341f3
RN
8083 gtk_widget_show ( item );
8084
a4aefe69
RN
8085 item = gtk_menu_item_new_with_mnemonic ( _("Split at _Trackpoint") );
8086 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_split_at_trackpoint), pass_along );
8087 gtk_menu_shell_append ( GTK_MENU_SHELL(split_submenu), item );
8088 gtk_widget_show ( item );
8089 // Make it available only when a trackpoint is selected.
8090 gtk_widget_set_sensitive ( item, (gboolean)GPOINTER_TO_INT(l->current_tpl) );
8091
491794be
RN
8092 GtkWidget *insert_submenu = gtk_menu_new ();
8093 item = gtk_image_menu_item_new_with_mnemonic ( _("_Insert Points") );
8094 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ADD, GTK_ICON_SIZE_MENU) );
8095 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
8096 gtk_widget_show ( item );
8097 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), insert_submenu );
8098
8099 item = gtk_menu_item_new_with_mnemonic ( _("Insert Point _Before Selected Point") );
8100 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_insert_point_before), pass_along );
8101 gtk_menu_shell_append ( GTK_MENU_SHELL(insert_submenu), item );
8102 gtk_widget_show ( item );
8103 // Make it available only when a point is selected
8104 gtk_widget_set_sensitive ( item, (gboolean)GPOINTER_TO_INT(l->current_tpl) );
8105
8106 item = gtk_menu_item_new_with_mnemonic ( _("Insert Point _After Selected Point") );
8107 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_insert_point_after), pass_along );
8108 gtk_menu_shell_append ( GTK_MENU_SHELL(insert_submenu), item );
8109 gtk_widget_show ( item );
8110 // Make it available only when a point is selected
8111 gtk_widget_set_sensitive ( item, (gboolean)GPOINTER_TO_INT(l->current_tpl) );
8112
b6eda120
RN
8113 GtkWidget *delete_submenu;
8114 delete_submenu = gtk_menu_new ();
8115 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete Poi_nts") );
8116 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_DELETE, GTK_ICON_SIZE_MENU) );
8117 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
8118 gtk_widget_show ( item );
8119 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), delete_submenu );
8120
a56baa08
RN
8121 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete _Selected Point") );
8122 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_DELETE, GTK_ICON_SIZE_MENU) );
8123 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_point_selected), pass_along );
8124 gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
8125 gtk_widget_show ( item );
8126 // Make it available only when a point is selected
8127 gtk_widget_set_sensitive ( item, (gboolean)GPOINTER_TO_INT(l->current_tpl) );
8128
b6eda120
RN
8129 item = gtk_menu_item_new_with_mnemonic ( _("Delete Points With The Same _Position") );
8130 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_points_same_position), pass_along );
8131 gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
8132 gtk_widget_show ( item );
8133
6579ca1f
RN
8134 item = gtk_menu_item_new_with_mnemonic ( _("Delete Points With The Same _Time") );
8135 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_points_same_time), pass_along );
8136 gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
af2341f3
RN
8137 gtk_widget_show ( item );
8138
89d310d1
RN
8139 GtkWidget *transform_submenu;
8140 transform_submenu = gtk_menu_new ();
8141 item = gtk_image_menu_item_new_with_mnemonic ( _("_Transform") );
8142 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_CONVERT, GTK_ICON_SIZE_MENU) );
8143 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
8144 gtk_widget_show ( item );
8145 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), transform_submenu );
8146
4d333042
RN
8147 GtkWidget *dem_submenu;
8148 dem_submenu = gtk_menu_new ();
89d310d1
RN
8149 item = gtk_image_menu_item_new_with_mnemonic ( _("_Apply DEM Data") );
8150 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 8151 gtk_menu_shell_append ( GTK_MENU_SHELL(transform_submenu), item );
4d333042
RN
8152 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), dem_submenu );
8153
8154 item = gtk_image_menu_item_new_with_mnemonic ( _("_Overwrite") );
8155 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_apply_dem_data_all), pass_along );
8156 gtk_menu_shell_append ( GTK_MENU_SHELL(dem_submenu), item );
8157 gtk_widget_set_tooltip_text (item, _("Overwrite any existing elevation values with DEM values"));
8158 gtk_widget_show ( item );
8159
8160 item = gtk_image_menu_item_new_with_mnemonic ( _("_Keep Existing") );
8161 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_apply_dem_data_only_missing), pass_along );
8162 gtk_menu_shell_append ( GTK_MENU_SHELL(dem_submenu), item );
8163 gtk_widget_set_tooltip_text (item, _("Keep existing elevation values, only attempt for missing values"));
89d310d1
RN
8164 gtk_widget_show ( item );
8165
81ac2835
RN
8166 GtkWidget *smooth_submenu;
8167 smooth_submenu = gtk_menu_new ();
8168 item = gtk_menu_item_new_with_mnemonic ( _("_Smooth Missing Elevation Data") );
8169 gtk_menu_shell_append ( GTK_MENU_SHELL(transform_submenu), item );
8170 gtk_widget_show ( item );
8171 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), smooth_submenu );
8172
8173 item = gtk_image_menu_item_new_with_mnemonic ( _("_Interpolated") );
8174 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_missing_elevation_data_interp), pass_along );
8175 gtk_menu_shell_append ( GTK_MENU_SHELL(smooth_submenu), item );
8176 gtk_widget_set_tooltip_text (item, _("Interpolate between known elevation values to derive values for the missing elevations"));
8177 gtk_widget_show ( item );
8178
8179 item = gtk_image_menu_item_new_with_mnemonic ( _("_Flat") );
8180 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_missing_elevation_data_flat), pass_along );
8181 gtk_menu_shell_append ( GTK_MENU_SHELL(smooth_submenu), item );
8182 gtk_widget_set_tooltip_text (item, _("Set unknown elevation values to the last known value"));
8183 gtk_widget_show ( item );
8184
89d310d1
RN
8185 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
8186 item = gtk_image_menu_item_new_with_mnemonic ( _("C_onvert to a Route") );
8187 else
8188 item = gtk_image_menu_item_new_with_mnemonic ( _("C_onvert to a Track") );
8189 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_CONVERT, GTK_ICON_SIZE_MENU) );
8190 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_convert_track_route), pass_along );
8191 gtk_menu_shell_append ( GTK_MENU_SHELL(transform_submenu), item );
8192 gtk_widget_show ( item );
8193
76b14439
RN
8194 // Routes don't have timestamps - so this is only available for tracks
8195 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK ) {
8196 item = gtk_image_menu_item_new_with_mnemonic ( _("_Anonymize Times") );
8197 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_anonymize_times), pass_along );
8198 gtk_menu_shell_append ( GTK_MENU_SHELL(transform_submenu), item );
8199 gtk_widget_set_tooltip_text (item, _("Shift timestamps to a relative offset from 1901-01-01"));
8200 gtk_widget_show ( item );
8201 }
8202
0d2b891f
RN
8203 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
8204 item = gtk_image_menu_item_new_with_mnemonic ( _("_Reverse Track") );
8205 else
8206 item = gtk_image_menu_item_new_with_mnemonic ( _("_Reverse Route") );
eb9fd106
RN
8207 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_BACK, GTK_ICON_SIZE_MENU) );
8208 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_reverse), pass_along );
8209 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
8210 gtk_widget_show ( item );
8211
1a3be6a8
GB
8212 if ( subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE ) {
8213 item = gtk_image_menu_item_new_with_mnemonic ( _("Refine Route...") );
8214 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_FIND, GTK_ICON_SIZE_MENU) );
8215 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_route_refine), pass_along );
8216 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
8217 gtk_widget_show ( item );
8218 }
8219
6bb72350
RN
8220 /* ATM This function is only available via the layers panel, due to the method in finding out the maps in use */
8221 if ( vlp ) {
0d2b891f
RN
8222 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
8223 item = gtk_image_menu_item_new_with_mnemonic ( _("Down_load Maps Along Track...") );
8224 else
8225 item = gtk_image_menu_item_new_with_mnemonic ( _("Down_load Maps Along Route...") );
43b5e335 8226 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
8227 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_download_map_along_track_cb), pass_along );
8228 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
8229 gtk_widget_show ( item );
8230 }
ad0a8c2d 8231
0d2b891f
RN
8232 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
8233 item = gtk_image_menu_item_new_with_mnemonic ( _("_Export Track as GPX...") );
8234 else
8235 item = gtk_image_menu_item_new_with_mnemonic ( _("_Export Route as GPX...") );
d6de71f9 8236 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_HARDDISK, GTK_ICON_SIZE_MENU) );
7f6757c4
RN
8237 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_gpx_track), pass_along );
8238 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
8239 gtk_widget_show ( item );
8240
0d2b891f
RN
8241 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
8242 item = gtk_image_menu_item_new_with_mnemonic ( _("E_xtend Track End") );
8243 else
8244 item = gtk_image_menu_item_new_with_mnemonic ( _("E_xtend Route End") );
d6de71f9 8245 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ADD, GTK_ICON_SIZE_MENU) );
8fb71d6c
EB
8246 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_extend_track_end), pass_along );
8247 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
ad0a8c2d 8248 gtk_widget_show ( item );
5092de80 8249
008e972c
RN
8250 if ( subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE ) {
8251 item = gtk_image_menu_item_new_with_mnemonic ( _("Extend _Using Route Finder") );
43b5e335 8252 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
8253 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_extend_track_end_route_finder), pass_along );
8254 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
8255 gtk_widget_show ( item );
8256 }
a7955c1d 8257
0d2b891f
RN
8258 // ATM can't upload a single waypoint but can do waypoints to a GPS
8259 if ( subtype != VIK_TRW_LAYER_SUBLAYER_WAYPOINT ) {
8260 item = gtk_image_menu_item_new_with_mnemonic ( _("_Upload") );
8261 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_UP, GTK_ICON_SIZE_MENU) );
8262 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
8263 gtk_widget_show ( item );
8264 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), upload_submenu );
8265
8266 item = gtk_image_menu_item_new_with_mnemonic ( _("_Upload to GPS...") );
8267 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_FORWARD, GTK_ICON_SIZE_MENU) );
8268 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_gps_upload_any), pass_along );
8269 gtk_menu_shell_append ( GTK_MENU_SHELL(upload_submenu), item );
8270 gtk_widget_show ( item );
8271 }
8272 }
e50758c7 8273
be3b5aa8
RN
8274 // Only made available if a suitable program is installed
8275 if ( have_diary_program ) {
8276 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK || subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT ) {
8277 item = gtk_image_menu_item_new_with_mnemonic ( _("Diar_y") );
8278 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_SPELL_CHECK, GTK_ICON_SIZE_MENU) );
8279 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_diary), pass_along );
8280 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
8281 gtk_widget_show ( item );
8282 }
8283 }
8284
008e972c
RN
8285#ifdef VIK_CONFIG_GOOGLE
8286 if ( subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE && is_valid_google_route ( l, sublayer ) )
8287 {
8288 item = gtk_image_menu_item_new_with_mnemonic ( _("_View Google Directions") );
8289 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NETWORK, GTK_ICON_SIZE_MENU) );
8290 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_google_route_webpage), pass_along );
8291 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
8292 gtk_widget_show ( item );
8293 }
8294#endif
8295
0d2b891f
RN
8296 // Some things aren't usable with routes
8297 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK ) {
5092de80 8298#ifdef VIK_CONFIG_OPENSTREETMAP
d6de71f9 8299 item = gtk_image_menu_item_new_with_mnemonic ( _("Upload to _OSM...") );
d6c110f1
RN
8300 // Convert internal pointer into track
8301 pass_along[MA_MISC] = g_hash_table_lookup ( l->tracks, sublayer);
d6de71f9 8302 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_UP, GTK_ICON_SIZE_MENU) );
3185b5d3 8303 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_osm_traces_upload_track_cb), pass_along );
e50758c7 8304 gtk_menu_shell_append ( GTK_MENU_SHELL(upload_submenu), item );
5092de80
GB
8305 gtk_widget_show ( item );
8306#endif
bddd2056 8307
d6de71f9
RN
8308 item = gtk_image_menu_item_new_with_mnemonic ( _("Use with _Filter") );
8309 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
28c82d8b
EB
8310 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_track_use_with_filter), pass_along );
8311 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
8312 gtk_widget_show ( item );
8313
6bb72350
RN
8314 /* ATM This function is only available via the layers panel, due to needing a vlp */
8315 if ( vlp ) {
8316 item = a_acquire_track_menu ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(l)), vlp,
0d2b891f
RN
8317 vik_layers_panel_get_viewport(VIK_LAYERS_PANEL(vlp)),
8318 g_hash_table_lookup ( l->tracks, (gchar *) sublayer ) );
6bb72350 8319 if ( item ) {
0d2b891f
RN
8320 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
8321 gtk_widget_show ( item );
6bb72350
RN
8322 }
8323 }
c95d6b00 8324
b3eb3b98 8325#ifdef VIK_CONFIG_GEOTAG
0d2b891f
RN
8326 item = gtk_menu_item_new_with_mnemonic ( _("Geotag _Images...") );
8327 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_geotagging_track), pass_along );
8328 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
8329 gtk_widget_show ( item );
b3eb3b98 8330#endif
0d2b891f 8331 }
b3eb3b98 8332
0d2b891f 8333 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK || subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE ) {
c95d6b00
RN
8334 // Only show on viewport popmenu when a trackpoint is selected
8335 if ( ! vlp && l->current_tpl ) {
8336 // Add separator
8337 item = gtk_menu_item_new ();
8338 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
8339 gtk_widget_show ( item );
8340
8341 item = gtk_image_menu_item_new_with_mnemonic ( _("_Edit Trackpoint") );
8342 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_PROPERTIES, GTK_ICON_SIZE_MENU) );
8343 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_edit_trackpoint), pass_along );
8344 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
8345 gtk_widget_show ( item );
8346 }
50a14534
EB
8347 }
8348
667fda15
RN
8349 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINTS || subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT ) {
8350 GtkWidget *transform_submenu;
8351 transform_submenu = gtk_menu_new ();
8352 item = gtk_image_menu_item_new_with_mnemonic ( _("_Transform") );
8353 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_CONVERT, GTK_ICON_SIZE_MENU) );
8354 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
8355 gtk_widget_show ( item );
8356 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), transform_submenu );
8357
8358 GtkWidget *dem_submenu;
8359 dem_submenu = gtk_menu_new ();
8360 item = gtk_image_menu_item_new_with_mnemonic ( _("_Apply DEM Data") );
8361 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
8362 gtk_menu_shell_append ( GTK_MENU_SHELL(transform_submenu), item );
8363 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), dem_submenu );
8364
8365 item = gtk_image_menu_item_new_with_mnemonic ( _("_Overwrite") );
8366 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_apply_dem_data_wpt_all), pass_along );
8367 gtk_menu_shell_append ( GTK_MENU_SHELL(dem_submenu), item );
8368 gtk_widget_set_tooltip_text (item, _("Overwrite any existing elevation values with DEM values"));
8369 gtk_widget_show ( item );
8370
8371 item = gtk_image_menu_item_new_with_mnemonic ( _("_Keep Existing") );
8372 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_apply_dem_data_wpt_only_missing), pass_along );
8373 gtk_menu_shell_append ( GTK_MENU_SHELL(dem_submenu), item );
8374 gtk_widget_set_tooltip_text (item, _("Keep existing elevation values, only attempt for missing values"));
8375 gtk_widget_show ( item );
8376 }
8377
4d333042
RN
8378 gtk_widget_show_all ( GTK_WIDGET(menu) );
8379
50a14534
EB
8380 return rv;
8381}
8382
491794be
RN
8383// TODO: Probably better to rework this track manipulation in viktrack.c
8384static void trw_layer_insert_tp_beside_current_tp ( VikTrwLayer *vtl, gboolean before )
db79f75f 8385{
491794be 8386 // sanity check
db79f75f
RN
8387 if (!vtl->current_tpl)
8388 return;
db79f75f
RN
8389
8390 VikTrackpoint *tp_current = VIK_TRACKPOINT(vtl->current_tpl->data);
491794be
RN
8391 VikTrackpoint *tp_other = NULL;
8392
8393 if ( before ) {
8394 if (!vtl->current_tpl->prev)
8395 return;
8396 tp_other = VIK_TRACKPOINT(vtl->current_tpl->prev->data);
8397 } else {
8398 if (!vtl->current_tpl->next)
8399 return;
8400 tp_other = VIK_TRACKPOINT(vtl->current_tpl->next->data);
8401 }
db79f75f 8402
491794be
RN
8403 // Use current and other trackpoints to form a new track point which is inserted into the tracklist
8404 if ( tp_other ) {
db79f75f
RN
8405
8406 VikTrackpoint *tp_new = vik_trackpoint_new();
491794be 8407 struct LatLon ll_current, ll_other;
db79f75f 8408 vik_coord_to_latlon ( &tp_current->coord, &ll_current );
491794be 8409 vik_coord_to_latlon ( &tp_other->coord, &ll_other );
db79f75f
RN
8410
8411 /* main positional interpolation */
491794be 8412 struct LatLon ll_new = { (ll_current.lat+ll_other.lat)/2, (ll_current.lon+ll_other.lon)/2 };
db79f75f
RN
8413 vik_coord_load_from_latlon ( &(tp_new->coord), vtl->coord_mode, &ll_new );
8414
8415 /* Now other properties that can be interpolated */
491794be 8416 tp_new->altitude = (tp_current->altitude + tp_other->altitude) / 2;
db79f75f 8417
491794be 8418 if (tp_current->has_timestamp && tp_other->has_timestamp) {
db79f75f
RN
8419 /* Note here the division is applied to each part, then added
8420 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 8421 tp_new->timestamp = (tp_current->timestamp/2) + (tp_other->timestamp/2);
db79f75f
RN
8422 tp_new->has_timestamp = TRUE;
8423 }
8424
491794be
RN
8425 if (tp_current->speed != NAN && tp_other->speed != NAN)
8426 tp_new->speed = (tp_current->speed + tp_other->speed)/2;
db79f75f
RN
8427
8428 /* TODO - improve interpolation of course, as it may not be correct.
8429 if courses in degrees are 350 + 020, the mid course more likely to be 005 (not 185)
8430 [similar applies if value is in radians] */
491794be
RN
8431 if (tp_current->course != NAN && tp_other->course != NAN)
8432 tp_new->course = (tp_current->course + tp_other->course)/2;
db79f75f
RN
8433
8434 /* DOP / sat values remain at defaults as not they do not seem applicable to a dreamt up point */
8435
491794be 8436 // Insert new point into the appropriate trackpoint list, either before or after the current trackpoint as directed
0d2b891f
RN
8437 VikTrack *trk = g_hash_table_lookup ( vtl->tracks, vtl->current_tp_id );
8438 if ( !trk )
8439 // Otherwise try routes
8440 trk = g_hash_table_lookup ( vtl->routes, vtl->current_tp_id );
8441 if ( !trk )
8442 return;
8443
8444 gint index = g_list_index ( trk->trackpoints, tp_current );
db79f75f 8445 if ( index > -1 ) {
491794be
RN
8446 if ( !before )
8447 index = index + 1;
20981fd6 8448 // NB no recalculation of bounds since it is inserted between points
491794be 8449 trk->trackpoints = g_list_insert ( trk->trackpoints, tp_new, index );
db79f75f
RN
8450 }
8451 }
8452}
50a14534 8453
50a14534
EB
8454static void trw_layer_cancel_current_tp ( VikTrwLayer *vtl, gboolean destroy )
8455{
8456 if ( vtl->tpwin )
8457 {
8458 if ( destroy)
8459 {
8460 gtk_widget_destroy ( GTK_WIDGET(vtl->tpwin) );
8461 vtl->tpwin = NULL;
8462 }
8463 else
8464 vik_trw_layer_tpwin_set_empty ( vtl->tpwin );
8465 }
8466 if ( vtl->current_tpl )
8467 {
8468 vtl->current_tpl = NULL;
ce4bd1cf
RN
8469 vtl->current_tp_track = NULL;
8470 vtl->current_tp_id = NULL;
da121f9b 8471 vik_layer_emit_update(VIK_LAYER(vtl));
50a14534
EB
8472 }
8473}
8474
8475static void trw_layer_tpwin_response ( VikTrwLayer *vtl, gint response )
8476{
8477 g_assert ( vtl->tpwin != NULL );
8478 if ( response == VIK_TRW_LAYER_TPWIN_CLOSE )
8479 trw_layer_cancel_current_tp ( vtl, TRUE );
0d601fd4
RN
8480
8481 if ( vtl->current_tpl == NULL )
8482 return;
8483
8484 if ( response == VIK_TRW_LAYER_TPWIN_SPLIT && vtl->current_tpl->next && vtl->current_tpl->prev )
50a14534 8485 {
0d2b891f 8486 trw_layer_split_at_selected_trackpoint ( vtl, vtl->current_tp_track->is_route ? VIK_TRW_LAYER_SUBLAYER_ROUTE : VIK_TRW_LAYER_SUBLAYER_TRACK );
84d3d9f9 8487 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, vtl->current_tp_track->name );
50a14534
EB
8488 }
8489 else if ( response == VIK_TRW_LAYER_TPWIN_DELETE )
8490 {
ce4bd1cf 8491 VikTrack *tr = g_hash_table_lookup ( vtl->tracks, vtl->current_tp_id );
0d2b891f
RN
8492 if ( tr == NULL )
8493 tr = g_hash_table_lookup ( vtl->routes, vtl->current_tp_id );
ce4bd1cf
RN
8494 if ( tr == NULL )
8495 return;
50a14534 8496
a56baa08 8497 trw_layer_trackpoint_selected_delete ( vtl, tr );
ab4553c3 8498
a56baa08 8499 if ( vtl->current_tpl )
ab4553c3 8500 // Reset dialog with the available adjacent trackpoint
a56baa08 8501 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, vtl->current_tp_track->name );
50a14534 8502
a56baa08 8503 vik_layer_emit_update(VIK_LAYER(vtl));
50a14534
EB
8504 }
8505 else if ( response == VIK_TRW_LAYER_TPWIN_FORWARD && vtl->current_tpl->next )
8506 {
ce4bd1cf
RN
8507 if ( vtl->current_tp_track )
8508 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl = vtl->current_tpl->next, vtl->current_tp_track->name );
da121f9b 8509 vik_layer_emit_update(VIK_LAYER(vtl)); /* TODO longone: either move or only update if tp is inside drawing window */
50a14534
EB
8510 }
8511 else if ( response == VIK_TRW_LAYER_TPWIN_BACK && vtl->current_tpl->prev )
8512 {
ce4bd1cf
RN
8513 if ( vtl->current_tp_track )
8514 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl = vtl->current_tpl->prev, vtl->current_tp_track->name );
da121f9b 8515 vik_layer_emit_update(VIK_LAYER(vtl));
50a14534 8516 }
2880a1de
RN
8517 else if ( response == VIK_TRW_LAYER_TPWIN_INSERT && vtl->current_tpl->next )
8518 {
491794be 8519 trw_layer_insert_tp_beside_current_tp ( vtl, FALSE );
da121f9b 8520 vik_layer_emit_update(VIK_LAYER(vtl));
2880a1de 8521 }
50a14534 8522 else if ( response == VIK_TRW_LAYER_TPWIN_DATA_CHANGED )
da121f9b 8523 vik_layer_emit_update(VIK_LAYER(vtl));
50a14534
EB
8524}
8525
d6175f49
RN
8526/**
8527 * trw_layer_dialog_shift:
8528 * @vertical: The reposition strategy. If Vertical moves dialog vertically, otherwise moves it horizontally
8529 *
8530 * Try to reposition a dialog if it's over the specified coord
8531 * so to not obscure the item of interest
8532 */
8533void trw_layer_dialog_shift ( VikTrwLayer *vtl, GtkWindow *dialog, VikCoord *coord, gboolean vertical )
8534{
8535 GtkWindow *parent = VIK_GTK_WINDOW_FROM_LAYER(vtl); //i.e. the main window
8536
06ce5b5a
RN
8537 // Attempt force dialog to be shown so we can find out where it is more reliably...
8538 while ( gtk_events_pending() )
8539 gtk_main_iteration ();
8540
d6175f49
RN
8541 // get parent window position & size
8542 gint win_pos_x, win_pos_y;
8543 gtk_window_get_position ( parent, &win_pos_x, &win_pos_y );
8544
8545 gint win_size_x, win_size_y;
8546 gtk_window_get_size ( parent, &win_size_x, &win_size_y );
8547
8548 // get own dialog size
8549 gint dia_size_x, dia_size_y;
8550 gtk_window_get_size ( dialog, &dia_size_x, &dia_size_y );
8551
8552 // get own dialog position
8553 gint dia_pos_x, dia_pos_y;
8554 gtk_window_get_position ( dialog, &dia_pos_x, &dia_pos_y );
8555
8556 // Dialog not 'realized'/positioned - so can't really do any repositioning logic
8557 if ( dia_pos_x > 2 && dia_pos_y > 2 ) {
8558
8559 VikViewport *vvp = vik_window_viewport ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)) );
8560
8561 gint vp_xx, vp_yy; // In viewport pixels
8562 vik_viewport_coord_to_screen ( vvp, coord, &vp_xx, &vp_yy );
8563
8564 // Work out the 'bounding box' in pixel terms of the dialog and only move it when over the position
8565
8566 gint dest_x = 0;
8567 gint dest_y = 0;
8568 if ( gtk_widget_translate_coordinates ( GTK_WIDGET(vvp), GTK_WIDGET(parent), 0, 0, &dest_x, &dest_y ) ) {
8569
8570 // Transform Viewport pixels into absolute pixels
8571 gint tmp_xx = vp_xx + dest_x + win_pos_x - 10;
8572 gint tmp_yy = vp_yy + dest_y + win_pos_y - 10;
8573
8574 // Is dialog over the point (to within an ^^ edge value)
8575 if ( (tmp_xx > dia_pos_x) && (tmp_xx < (dia_pos_x + dia_size_x)) &&
8576 (tmp_yy > dia_pos_y) && (tmp_yy < (dia_pos_y + dia_size_y)) ) {
8577
8578 if ( vertical ) {
8579 // Shift up<->down
8580 gint hh = vik_viewport_get_height ( vvp );
8581
8582 // Consider the difference in viewport to the full window
8583 gint offset_y = dest_y;
8584 // Add difference between dialog and window sizes
8585 offset_y += win_pos_y + (hh/2 - dia_size_y)/2;
8586
8587 if ( vp_yy > hh/2 ) {
8588 // Point in bottom half, move window to top half
8589 gtk_window_move ( dialog, dia_pos_x, offset_y );
8590 }
8591 else {
8592 // Point in top half, move dialog down
8593 gtk_window_move ( dialog, dia_pos_x, hh/2 + offset_y );
8594 }
8595 }
8596 else {
8597 // Shift left<->right
8598 gint ww = vik_viewport_get_width ( vvp );
8599
8600 // Consider the difference in viewport to the full window
8601 gint offset_x = dest_x;
8602 // Add difference between dialog and window sizes
8603 offset_x += win_pos_x + (ww/2 - dia_size_x)/2;
8604
8605 if ( vp_xx > ww/2 ) {
8606 // Point on right, move window to left
8607 gtk_window_move ( dialog, offset_x, dia_pos_y );
8608 }
8609 else {
8610 // Point on left, move right
8611 gtk_window_move ( dialog, ww/2 + offset_x, dia_pos_y );
8612 }
8613 }
8614 }
8615 }
8616 }
8617}
8618
50a14534
EB
8619static void trw_layer_tpwin_init ( VikTrwLayer *vtl )
8620{
8621 if ( ! vtl->tpwin )
8622 {
8623 vtl->tpwin = vik_trw_layer_tpwin_new ( VIK_GTK_WINDOW_FROM_LAYER(vtl) );
8624 g_signal_connect_swapped ( GTK_DIALOG(vtl->tpwin), "response", G_CALLBACK(trw_layer_tpwin_response), vtl );
8625 /* connect signals -- DELETE SIGNAL VERY IMPORTANT TO SET TO NULL */
8626 g_signal_connect_swapped ( vtl->tpwin, "delete-event", G_CALLBACK(trw_layer_cancel_current_tp), vtl );
d6175f49 8627
50a14534 8628 gtk_widget_show_all ( GTK_WIDGET(vtl->tpwin) );
d6175f49
RN
8629
8630 if ( vtl->current_tpl ) {
8631 // get tp pixel position
8632 VikTrackpoint *tp = VIK_TRACKPOINT(vtl->current_tpl->data);
8633
8634 // Shift up<->down to try not to obscure the trackpoint.
8635 trw_layer_dialog_shift ( vtl, GTK_WINDOW(vtl->tpwin), &(tp->coord), TRUE );
8636 }
50a14534 8637 }
d6175f49 8638
50a14534 8639 if ( vtl->current_tpl )
ce4bd1cf
RN
8640 if ( vtl->current_tp_track )
8641 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, vtl->current_tp_track->name );
50a14534
EB
8642 /* set layer name and TP data */
8643}
8644
941aa6e9
AF
8645/***************************************************************************
8646 ** Tool code
8647 ***************************************************************************/
50a14534 8648
941aa6e9 8649/*** Utility data structures and functions ****/
50a14534
EB
8650
8651typedef struct {
8652 gint x, y;
8653 gint closest_x, closest_y;
7d961c6c 8654 gboolean draw_images;
c9570f86 8655 gpointer *closest_wp_id;
50a14534
EB
8656 VikWaypoint *closest_wp;
8657 VikViewport *vvp;
8658} WPSearchParams;
8659
941aa6e9
AF
8660typedef struct {
8661 gint x, y;
8662 gint closest_x, closest_y;
ce4bd1cf 8663 gpointer closest_track_id;
941aa6e9
AF
8664 VikTrackpoint *closest_tp;
8665 VikViewport *vvp;
8666 GList *closest_tpl;
79773236 8667 LatLonBBox bbox;
941aa6e9
AF
8668} TPSearchParams;
8669
c9570f86 8670static void waypoint_search_closest_tp ( gpointer id, VikWaypoint *wp, WPSearchParams *params )
50a14534
EB
8671{
8672 gint x, y;
8673 if ( !wp->visible )
8674 return;
8675
8676 vik_viewport_coord_to_screen ( params->vvp, &(wp->coord), &x, &y );
59b0a97a
RN
8677
8678 // If waypoint has an image then use the image size to select
7d961c6c 8679 if ( params->draw_images && wp->image ) {
59b0a97a
RN
8680 gint slackx, slacky;
8681 slackx = wp->image_width / 2;
8682 slacky = wp->image_height / 2;
8683
8684 if ( x <= params->x + slackx && x >= params->x - slackx
8685 && y <= params->y + slacky && y >= params->y - slacky ) {
c9570f86 8686 params->closest_wp_id = id;
59b0a97a
RN
8687 params->closest_wp = wp;
8688 params->closest_x = x;
8689 params->closest_y = y;
8690 }
50a14534 8691 }
59b0a97a
RN
8692 else if ( abs (x - params->x) <= WAYPOINT_SIZE_APPROX && abs (y - params->y) <= WAYPOINT_SIZE_APPROX &&
8693 ((!params->closest_wp) || /* was the old waypoint we already found closer than this one? */
8694 abs(x - params->x)+abs(y - params->y) < abs(x - params->closest_x)+abs(y - params->closest_y)))
8695 {
c9570f86 8696 params->closest_wp_id = id;
59b0a97a
RN
8697 params->closest_wp = wp;
8698 params->closest_x = x;
8699 params->closest_y = y;
8700 }
50a14534
EB
8701}
8702
ce4bd1cf 8703static void track_search_closest_tp ( gpointer id, VikTrack *t, TPSearchParams *params )
50a14534 8704{
941aa6e9
AF
8705 GList *tpl = t->trackpoints;
8706 VikTrackpoint *tp;
50a14534 8707
941aa6e9
AF
8708 if ( !t->visible )
8709 return;
50a14534 8710
79773236
RN
8711 if ( ! BBOX_INTERSECT ( t->bbox, params->bbox ) )
8712 return;
8713
941aa6e9
AF
8714 while (tpl)
8715 {
8716 gint x, y;
8717 tp = VIK_TRACKPOINT(tpl->data);
8718
8719 vik_viewport_coord_to_screen ( params->vvp, &(tp->coord), &x, &y );
8720
8721 if ( abs (x - params->x) <= TRACKPOINT_SIZE_APPROX && abs (y - params->y) <= TRACKPOINT_SIZE_APPROX &&
8722 ((!params->closest_tp) || /* was the old trackpoint we already found closer than this one? */
8723 abs(x - params->x)+abs(y - params->y) < abs(x - params->closest_x)+abs(y - params->closest_y)))
50a14534 8724 {
ce4bd1cf 8725 params->closest_track_id = id;
941aa6e9
AF
8726 params->closest_tp = tp;
8727 params->closest_tpl = tpl;
8728 params->closest_x = x;
8729 params->closest_y = y;
50a14534 8730 }
941aa6e9 8731 tpl = tpl->next;
50a14534 8732 }
941aa6e9
AF
8733}
8734
0d2b891f
RN
8735// ATM: Leave this as 'Track' only.
8736// Not overly bothered about having a snap to route trackpoint capability
941aa6e9
AF
8737static VikTrackpoint *closest_tp_in_five_pixel_interval ( VikTrwLayer *vtl, VikViewport *vvp, gint x, gint y )
8738{
8739 TPSearchParams params;
8740 params.x = x;
8741 params.y = y;
8742 params.vvp = vvp;
ce4bd1cf 8743 params.closest_track_id = NULL;
941aa6e9 8744 params.closest_tp = NULL;
79773236 8745 vik_viewport_get_min_max_lat_lon ( params.vvp, &(params.bbox.south), &(params.bbox.north), &(params.bbox.west), &(params.bbox.east) );
941aa6e9
AF
8746 g_hash_table_foreach ( vtl->tracks, (GHFunc) track_search_closest_tp, &params);
8747 return params.closest_tp;
50a14534
EB
8748}
8749
8750static VikWaypoint *closest_wp_in_five_pixel_interval ( VikTrwLayer *vtl, VikViewport *vvp, gint x, gint y )
8751{
8752 WPSearchParams params;
8753 params.x = x;
8754 params.y = y;
8755 params.vvp = vvp;
7d961c6c 8756 params.draw_images = vtl->drawimages;
50a14534 8757 params.closest_wp = NULL;
c9570f86 8758 params.closest_wp_id = NULL;
50a14534
EB
8759 g_hash_table_foreach ( vtl->waypoints, (GHFunc) waypoint_search_closest_tp, &params);
8760 return params.closest_wp;
8761}
8762
95d1b757 8763
08f14055
RN
8764// Some forward declarations
8765static void marker_begin_move ( tool_ed_t *t, gint x, gint y );
8766static void marker_moveto ( tool_ed_t *t, gint x, gint y );
8767static void marker_end_move ( tool_ed_t *t );
8768//
8769
8770static gboolean trw_layer_select_move ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp, tool_ed_t* t )
8771{
8772 if ( t->holding ) {
8773 VikCoord new_coord;
8774 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
8775
8776 // Here always allow snapping back to the original location
8777 // this is useful when one decides not to move the thing afterall
8778 // If one wants to move the item only a little bit then don't hold down the 'snap' key!
8779
8780 // snap to TP
8781 if ( event->state & GDK_CONTROL_MASK )
8782 {
8783 VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
8784 if ( tp )
8785 new_coord = tp->coord;
8786 }
8787
8788 // snap to WP
8789 if ( event->state & GDK_SHIFT_MASK )
8790 {
8791 VikWaypoint *wp = closest_wp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
8792 if ( wp )
8793 new_coord = wp->coord;
8794 }
8795
8796 gint x, y;
8797 vik_viewport_coord_to_screen ( vvp, &new_coord, &x, &y );
8798
8799 marker_moveto ( t, x, y );
8800
8801 return TRUE;
8802 }
8803 return FALSE;
8804}
8805
8806static gboolean trw_layer_select_release ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp, tool_ed_t* t )
8807{
8808 if ( t->holding && event->button == 1 )
8809 {
8810 VikCoord new_coord;
8811 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
8812
8813 // snap to TP
8814 if ( event->state & GDK_CONTROL_MASK )
8815 {
8816 VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
8817 if ( tp )
8818 new_coord = tp->coord;
8819 }
8820
8821 // snap to WP
8822 if ( event->state & GDK_SHIFT_MASK )
8823 {
8824 VikWaypoint *wp = closest_wp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
8825 if ( wp )
8826 new_coord = wp->coord;
8827 }
8828
8829 marker_end_move ( t );
8830
8831 // Determine if working on a waypoint or a trackpoint
aa0665e5 8832 if ( t->is_waypoint ) {
bd78ae7d 8833 // Update waypoint position
08f14055 8834 vtl->current_wp->coord = new_coord;
aa0665e5 8835 trw_layer_calculate_bounds_waypoints ( vtl );
bd78ae7d
RN
8836 // Reset waypoint pointer
8837 vtl->current_wp = NULL;
8838 vtl->current_wp_id = NULL;
aa0665e5 8839 }
08f14055
RN
8840 else {
8841 if ( vtl->current_tpl ) {
ce4bd1cf 8842 VIK_TRACKPOINT(vtl->current_tpl->data)->coord = new_coord;
20981fd6
RN
8843
8844 if ( vtl->current_tp_track )
8845 vik_track_calculate_bounds ( vtl->current_tp_track );
8846
bd78ae7d 8847 if ( vtl->tpwin )
ce4bd1cf
RN
8848 if ( vtl->current_tp_track )
8849 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, vtl->current_tp_track->name );
bd78ae7d 8850 // NB don't reset the selected trackpoint, thus ensuring it's still in the tpwin
08f14055
RN
8851 }
8852 }
8853
da121f9b 8854 vik_layer_emit_update ( VIK_LAYER(vtl) );
08f14055
RN
8855 return TRUE;
8856 }
8857 return FALSE;
8858}
8859
77ad64fa
RN
8860/*
8861 Returns true if a waypoint or track is found near the requested event position for this particular layer
8862 The item found is automatically selected
8863 This is a tool like feature but routed via the layer interface, since it's instigated by a 'global' layer tool in vikwindow.c
8864 */
08f14055 8865static gboolean trw_layer_select_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp, tool_ed_t* tet )
77ad64fa
RN
8866{
8867 if ( event->button != 1 )
8868 return FALSE;
8869
8870 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
8871 return FALSE;
8872
0d2b891f 8873 if ( !vtl->tracks_visible && !vtl->waypoints_visible && !vtl->routes_visible )
77ad64fa
RN
8874 return FALSE;
8875
79773236
RN
8876 LatLonBBox bbox;
8877 vik_viewport_get_min_max_lat_lon ( vvp, &(bbox.south), &(bbox.north), &(bbox.west), &(bbox.east) );
8878
08f14055 8879 // 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 8880
79773236 8881 if ( vtl->waypoints_visible && BBOX_INTERSECT (vtl->waypoints_bbox, bbox ) ) {
77ad64fa
RN
8882 WPSearchParams wp_params;
8883 wp_params.vvp = vvp;
8884 wp_params.x = event->x;
8885 wp_params.y = event->y;
7d961c6c 8886 wp_params.draw_images = vtl->drawimages;
c9570f86 8887 wp_params.closest_wp_id = NULL;
77ad64fa
RN
8888 wp_params.closest_wp = NULL;
8889
8890 g_hash_table_foreach ( vtl->waypoints, (GHFunc) waypoint_search_closest_tp, &wp_params);
8891
8892 if ( wp_params.closest_wp ) {
08f14055
RN
8893
8894 // Select
c9570f86 8895 vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, g_hash_table_lookup ( vtl->waypoints_iters, wp_params.closest_wp_id ), TRUE );
08f14055
RN
8896
8897 // Too easy to move it so must be holding shift to start immediately moving it
3c73e6c4 8898 // or otherwise be previously selected but not have an image (otherwise clicking within image bounds (again) moves it)
08f14055 8899 if ( event->state & GDK_SHIFT_MASK ||
3c73e6c4 8900 ( vtl->current_wp == wp_params.closest_wp && !vtl->current_wp->image ) ) {
08f14055
RN
8901 // Put into 'move buffer'
8902 // NB vvp & vw already set in tet
8903 tet->vtl = (gpointer)vtl;
8904 tet->is_waypoint = TRUE;
8905
8906 marker_begin_move (tet, event->x, event->y);
8907 }
8908
c9570f86
RN
8909 vtl->current_wp = wp_params.closest_wp;
8910 vtl->current_wp_id = wp_params.closest_wp_id;
08f14055 8911
89af1ff6
RN
8912 if ( event->type == GDK_2BUTTON_PRESS ) {
8913 if ( vtl->current_wp->image ) {
8914 menu_array_sublayer values;
8915 values[MA_VTL] = vtl;
8916 values[MA_MISC] = vtl->current_wp->image;
8917 trw_layer_show_picture ( values );
8918 }
8919 }
8920
da121f9b 8921 vik_layer_emit_update ( VIK_LAYER(vtl) );
08f14055 8922
77ad64fa
RN
8923 return TRUE;
8924 }
8925 }
8926
0d2b891f
RN
8927 // Used for both track and route lists
8928 TPSearchParams tp_params;
8929 tp_params.vvp = vvp;
8930 tp_params.x = event->x;
8931 tp_params.y = event->y;
8932 tp_params.closest_track_id = NULL;
8933 tp_params.closest_tp = NULL;
9414408e 8934 tp_params.closest_tpl = NULL;
79773236 8935 tp_params.bbox = bbox;
77ad64fa 8936
0d2b891f 8937 if (vtl->tracks_visible) {
77ad64fa
RN
8938 g_hash_table_foreach ( vtl->tracks, (GHFunc) track_search_closest_tp, &tp_params);
8939
8940 if ( tp_params.closest_tp ) {
08f14055
RN
8941
8942 // Always select + highlight the track
ce4bd1cf 8943 vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, g_hash_table_lookup ( vtl->tracks_iters, tp_params.closest_track_id ), TRUE );
08f14055
RN
8944
8945 tet->is_waypoint = FALSE;
8946
8947 // Select the Trackpoint
8948 // Can move it immediately when control held or it's the previously selected tp
8949 if ( event->state & GDK_CONTROL_MASK ||
8950 vtl->current_tpl == tp_params.closest_tpl ) {
8951 // Put into 'move buffer'
8952 // NB vvp & vw already set in tet
8953 tet->vtl = (gpointer)vtl;
8954 marker_begin_move (tet, event->x, event->y);
8955 }
8956
8957 vtl->current_tpl = tp_params.closest_tpl;
ce4bd1cf
RN
8958 vtl->current_tp_id = tp_params.closest_track_id;
8959 vtl->current_tp_track = g_hash_table_lookup ( vtl->tracks, tp_params.closest_track_id );
08f14055 8960
95d1b757
RN
8961 set_statusbar_msg_info_trkpt ( vtl, tp_params.closest_tp );
8962
08f14055 8963 if ( vtl->tpwin )
ce4bd1cf 8964 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, vtl->current_tp_track->name );
08f14055 8965
da121f9b 8966 vik_layer_emit_update ( VIK_LAYER(vtl) );
77ad64fa
RN
8967 return TRUE;
8968 }
8969 }
8970
0d2b891f
RN
8971 // Try again for routes
8972 if (vtl->routes_visible) {
8973 g_hash_table_foreach ( vtl->routes, (GHFunc) track_search_closest_tp, &tp_params);
8974
8975 if ( tp_params.closest_tp ) {
8976
8977 // Always select + highlight the track
8978 vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, g_hash_table_lookup ( vtl->routes_iters, tp_params.closest_track_id ), TRUE );
8979
8980 tet->is_waypoint = FALSE;
8981
8982 // Select the Trackpoint
8983 // Can move it immediately when control held or it's the previously selected tp
8984 if ( event->state & GDK_CONTROL_MASK ||
8985 vtl->current_tpl == tp_params.closest_tpl ) {
8986 // Put into 'move buffer'
8987 // NB vvp & vw already set in tet
8988 tet->vtl = (gpointer)vtl;
8989 marker_begin_move (tet, event->x, event->y);
8990 }
8991
8992 vtl->current_tpl = tp_params.closest_tpl;
8993 vtl->current_tp_id = tp_params.closest_track_id;
8994 vtl->current_tp_track = g_hash_table_lookup ( vtl->routes, tp_params.closest_track_id );
8995
8996 set_statusbar_msg_info_trkpt ( vtl, tp_params.closest_tp );
8997
8998 if ( vtl->tpwin )
8999 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, vtl->current_tp_track->name );
9000
da121f9b 9001 vik_layer_emit_update ( VIK_LAYER(vtl) );
0d2b891f
RN
9002 return TRUE;
9003 }
9004 }
9005
77ad64fa 9006 /* these aren't the droids you're looking for */
c9570f86
RN
9007 vtl->current_wp = NULL;
9008 vtl->current_wp_id = NULL;
08f14055
RN
9009 trw_layer_cancel_current_tp ( vtl, FALSE );
9010
95d1b757
RN
9011 // Blank info
9012 vik_statusbar_set_message ( vik_window_get_statusbar (VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl))), VIK_STATUSBAR_INFO, "" );
9013
77ad64fa
RN
9014 return FALSE;
9015}
9016
e46f259a
RN
9017static gboolean trw_layer_show_selected_viewport_menu ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
9018{
9019 if ( event->button != 3 )
9020 return FALSE;
9021
9022 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
9023 return FALSE;
9024
0d2b891f 9025 if ( !vtl->tracks_visible && !vtl->waypoints_visible && !vtl->routes_visible )
e46f259a
RN
9026 return FALSE;
9027
9028 /* Post menu for the currently selected item */
9029
9030 /* See if a track is selected */
9031 VikTrack *track = (VikTrack*)vik_window_get_selected_track ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(vtl) );
9032 if ( track && track->visible ) {
9033
60a69560 9034 if ( track->name ) {
e46f259a
RN
9035
9036 if ( vtl->track_right_click_menu )
1a7a0378 9037 g_object_ref_sink ( G_OBJECT(vtl->track_right_click_menu) );
e46f259a
RN
9038
9039 vtl->track_right_click_menu = GTK_MENU ( gtk_menu_new () );
9040
60a69560
RN
9041 trku_udata udataU;
9042 udataU.trk = track;
9043 udataU.uuid = NULL;
9044
0d2b891f
RN
9045 gpointer *trkf;
9046 if ( track->is_route )
9047 trkf = g_hash_table_find ( vtl->routes, (GHRFunc) trw_layer_track_find_uuid, &udataU );
9048 else
9049 trkf = g_hash_table_find ( vtl->tracks, (GHRFunc) trw_layer_track_find_uuid, &udataU );
60a69560
RN
9050
9051 if ( trkf && udataU.uuid ) {
9052
0d2b891f
RN
9053 GtkTreeIter *iter;
9054 if ( track->is_route )
9055 iter = g_hash_table_lookup ( vtl->routes_iters, udataU.uuid );
9056 else
9057 iter = g_hash_table_lookup ( vtl->tracks_iters, udataU.uuid );
60a69560
RN
9058
9059 trw_layer_sublayer_add_menu_items ( vtl,
9060 vtl->track_right_click_menu,
9061 NULL,
0d2b891f 9062 track->is_route ? VIK_TRW_LAYER_SUBLAYER_ROUTE : VIK_TRW_LAYER_SUBLAYER_TRACK,
60a69560
RN
9063 udataU.uuid,
9064 iter,
9065 vvp );
9066 }
e46f259a
RN
9067
9068 gtk_menu_popup ( vtl->track_right_click_menu, NULL, NULL, NULL, NULL, event->button, gtk_get_current_event_time() );
9069
9070 return TRUE;
9071 }
9072 }
9073
9074 /* See if a waypoint is selected */
9075 VikWaypoint *waypoint = (VikWaypoint*)vik_window_get_selected_waypoint ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(vtl) );
9076 if ( waypoint && waypoint->visible ) {
60a69560 9077 if ( waypoint->name ) {
e46f259a
RN
9078
9079 if ( vtl->wp_right_click_menu )
1a7a0378 9080 g_object_ref_sink ( G_OBJECT(vtl->wp_right_click_menu) );
e46f259a
RN
9081
9082 vtl->wp_right_click_menu = GTK_MENU ( gtk_menu_new () );
60a69560
RN
9083
9084 wpu_udata udata;
9085 udata.wp = waypoint;
9086 udata.uuid = NULL;
9087
9088 gpointer *wpf = g_hash_table_find ( vtl->waypoints, (GHRFunc) trw_layer_waypoint_find_uuid, (gpointer) &udata );
9089
9090 if ( wpf && udata.uuid ) {
9091 GtkTreeIter *iter = g_hash_table_lookup ( vtl->waypoints_iters, udata.uuid );
9092
9093 trw_layer_sublayer_add_menu_items ( vtl,
9094 vtl->wp_right_click_menu,
9095 NULL,
9096 VIK_TRW_LAYER_SUBLAYER_WAYPOINT,
9097 udata.uuid,
9098 iter,
9099 vvp );
9100 }
e46f259a
RN
9101 gtk_menu_popup ( vtl->wp_right_click_menu, NULL, NULL, NULL, NULL, event->button, gtk_get_current_event_time() );
9102
9103 return TRUE;
9104 }
9105 }
9106
9107 return FALSE;
9108}
9109
7432fddf
AF
9110/* background drawing hook, to be passed the viewport */
9111static gboolean tool_sync_done = TRUE;
9112
9113static gboolean tool_sync(gpointer data)
9114{
9115 VikViewport *vvp = data;
9116 gdk_threads_enter();
9117 vik_viewport_sync(vvp);
9118 tool_sync_done = TRUE;
9119 gdk_threads_leave();
9120 return FALSE;
9121}
9122
7432fddf
AF
9123static void marker_begin_move ( tool_ed_t *t, gint x, gint y )
9124{
9125 t->holding = TRUE;
9126 t->gc = vik_viewport_new_gc (t->vvp, "black", 2);
9127 gdk_gc_set_function ( t->gc, GDK_INVERT );
9128 vik_viewport_draw_rectangle ( t->vvp, t->gc, FALSE, x-3, y-3, 6, 6 );
9129 vik_viewport_sync(t->vvp);
9130 t->oldx = x;
9131 t->oldy = y;
9132}
9133
9134static void marker_moveto ( tool_ed_t *t, gint x, gint y )
9135{
9136 VikViewport *vvp = t->vvp;
9137 vik_viewport_draw_rectangle ( vvp, t->gc, FALSE, t->oldx-3, t->oldy-3, 6, 6 );
9138 vik_viewport_draw_rectangle ( vvp, t->gc, FALSE, x-3, y-3, 6, 6 );
9139 t->oldx = x;
9140 t->oldy = y;
7b203521 9141
7432fddf
AF
9142 if (tool_sync_done) {
9143 g_idle_add_full (G_PRIORITY_HIGH_IDLE + 10, tool_sync, vvp, NULL);
9144 tool_sync_done = FALSE;
9145 }
9146}
9147
9148static void marker_end_move ( tool_ed_t *t )
9149{
9150 vik_viewport_draw_rectangle ( t->vvp, t->gc, FALSE, t->oldx-3, t->oldy-3, 6, 6 );
9151 g_object_unref ( t->gc );
9152 t->holding = FALSE;
9153}
9154
941aa6e9
AF
9155/*** Edit waypoint ****/
9156
9157static gpointer tool_edit_waypoint_create ( VikWindow *vw, VikViewport *vvp)
9158{
7432fddf
AF
9159 tool_ed_t *t = g_new(tool_ed_t, 1);
9160 t->vvp = vvp;
9161 t->holding = FALSE;
9162 return t;
941aa6e9
AF
9163}
9164
919ed63e
RN
9165static void tool_edit_waypoint_destroy ( tool_ed_t *t )
9166{
9167 g_free ( t );
9168}
9169
7432fddf 9170static gboolean tool_edit_waypoint_click ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data )
50a14534
EB
9171{
9172 WPSearchParams params;
7432fddf
AF
9173 tool_ed_t *t = data;
9174 VikViewport *vvp = t->vvp;
50a14534 9175
941aa6e9
AF
9176 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
9177 return FALSE;
7432fddf
AF
9178
9179 if ( t->holding ) {
9180 return TRUE;
9181 }
9182
87741170
RN
9183 if ( !vtl->vl.visible || !vtl->waypoints_visible )
9184 return FALSE;
9185
50a14534
EB
9186 if ( vtl->current_wp && vtl->current_wp->visible )
9187 {
9188 /* first check if current WP is within area (other may be 'closer', but we want to move the current) */
9189 gint x, y;
9190 vik_viewport_coord_to_screen ( vvp, &(vtl->current_wp->coord), &x, &y );
9191
c75d78d7
AF
9192 if ( abs(x - event->x) <= WAYPOINT_SIZE_APPROX &&
9193 abs(y - event->y) <= WAYPOINT_SIZE_APPROX )
50a14534
EB
9194 {
9195 if ( event->button == 3 )
9196 vtl->waypoint_rightclick = TRUE; /* remember that we're clicking; other layers will ignore release signal */
7432fddf
AF
9197 else {
9198 marker_begin_move(t, event->x, event->y);
9199 }
50a14534
EB
9200 return TRUE;
9201 }
9202 }
9203
9204 params.vvp = vvp;
9205 params.x = event->x;
9206 params.y = event->y;
7d961c6c 9207 params.draw_images = vtl->drawimages;
c9570f86 9208 params.closest_wp_id = NULL;
50a14534
EB
9209 params.closest_wp = NULL;
9210 g_hash_table_foreach ( vtl->waypoints, (GHFunc) waypoint_search_closest_tp, &params);
98356138 9211 if ( vtl->current_wp && (vtl->current_wp == params.closest_wp) )
50a14534 9212 {
a129aa51
RN
9213 if ( event->button == 3 )
9214 vtl->waypoint_rightclick = TRUE; /* remember that we're clicking; other layers will ignore release signal */
9215 else
9216 marker_begin_move(t, event->x, event->y);
d84ade77 9217 return FALSE;
50a14534
EB
9218 }
9219 else if ( params.closest_wp )
9220 {
9221 if ( event->button == 3 )
9222 vtl->waypoint_rightclick = TRUE; /* remember that we're clicking; other layers will ignore release signal */
9223 else
9224 vtl->waypoint_rightclick = FALSE;
9225
c9570f86 9226 vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, g_hash_table_lookup ( vtl->waypoints_iters, params.closest_wp_id ), TRUE );
8623d370 9227
50a14534 9228 vtl->current_wp = params.closest_wp;
c9570f86 9229 vtl->current_wp_id = params.closest_wp_id;
50a14534 9230
50a14534 9231 /* could make it so don't update if old WP is off screen and new is null but oh well */
da121f9b 9232 vik_layer_emit_update ( VIK_LAYER(vtl) );
50a14534
EB
9233 return TRUE;
9234 }
9235
9236 vtl->current_wp = NULL;
c9570f86 9237 vtl->current_wp_id = NULL;
50a14534 9238 vtl->waypoint_rightclick = FALSE;
da121f9b 9239 vik_layer_emit_update ( VIK_LAYER(vtl) );
7432fddf
AF
9240 return FALSE;
9241}
9242
dc2c040e 9243static gboolean tool_edit_waypoint_move ( VikTrwLayer *vtl, GdkEventMotion *event, gpointer data )
7432fddf
AF
9244{
9245 tool_ed_t *t = data;
9246 VikViewport *vvp = t->vvp;
9247
9248 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
9249 return FALSE;
9250
9251 if ( t->holding ) {
9252 VikCoord new_coord;
9253 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
9254
9255 /* snap to TP */
9256 if ( event->state & GDK_CONTROL_MASK )
9257 {
9258 VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
9259 if ( tp )
9260 new_coord = tp->coord;
9261 }
9262
9263 /* snap to WP */
9264 if ( event->state & GDK_SHIFT_MASK )
9265 {
9266 VikWaypoint *wp = closest_wp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
9267 if ( wp && wp != vtl->current_wp )
9268 new_coord = wp->coord;
9269 }
9270
9271 {
9272 gint x, y;
9273 vik_viewport_coord_to_screen ( vvp, &new_coord, &x, &y );
7b203521 9274
7432fddf
AF
9275 marker_moveto ( t, x, y );
9276 }
9277 return TRUE;
9278 }
50a14534
EB
9279 return FALSE;
9280}
9281
7432fddf 9282static gboolean tool_edit_waypoint_release ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data )
941aa6e9 9283{
7432fddf
AF
9284 tool_ed_t *t = data;
9285 VikViewport *vvp = t->vvp;
9286
941aa6e9
AF
9287 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
9288 return FALSE;
7432fddf
AF
9289
9290 if ( t->holding && event->button == 1 )
941aa6e9
AF
9291 {
9292 VikCoord new_coord;
941aa6e9
AF
9293 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
9294
9295 /* snap to TP */
9296 if ( event->state & GDK_CONTROL_MASK )
9297 {
9298 VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
9299 if ( tp )
9300 new_coord = tp->coord;
9301 }
9302
9303 /* snap to WP */
9304 if ( event->state & GDK_SHIFT_MASK )
9305 {
9306 VikWaypoint *wp = closest_wp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
9307 if ( wp && wp != vtl->current_wp )
9308 new_coord = wp->coord;
9309 }
9310
7432fddf
AF
9311 marker_end_move ( t );
9312
941aa6e9 9313 vtl->current_wp->coord = new_coord;
aa0665e5
RN
9314
9315 trw_layer_calculate_bounds_waypoints ( vtl );
da121f9b 9316 vik_layer_emit_update ( VIK_LAYER(vtl) );
941aa6e9
AF
9317 return TRUE;
9318 }
9319 /* PUT IN RIGHT PLACE!!! */
7432fddf 9320 if ( event->button == 3 && vtl->waypoint_rightclick )
941aa6e9
AF
9321 {
9322 if ( vtl->wp_right_click_menu )
4f14a010 9323 g_object_ref_sink ( G_OBJECT(vtl->wp_right_click_menu) );
0b951ebe
RN
9324 if ( vtl->current_wp ) {
9325 vtl->wp_right_click_menu = GTK_MENU ( gtk_menu_new () );
c9570f86 9326 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
9327 gtk_menu_popup ( vtl->wp_right_click_menu, NULL, NULL, NULL, NULL, event->button, gtk_get_current_event_time() );
9328 }
941aa6e9
AF
9329 vtl->waypoint_rightclick = FALSE;
9330 }
9331 return FALSE;
9332}
9333
9334/*** New track ****/
9335
9336static gpointer tool_new_track_create ( VikWindow *vw, VikViewport *vvp)
9337{
9338 return vvp;
9339}
9340
7b203521
EB
9341typedef struct {
9342 VikTrwLayer *vtl;
745fda83
RN
9343 GdkDrawable *drawable;
9344 GdkGC *gc;
9345 GdkPixmap *pixmap;
9346} draw_sync_t;
7b203521 9347
745fda83
RN
9348/*
9349 * Draw specified pixmap
9350 */
9351static gboolean draw_sync ( gpointer data )
7b203521 9352{
745fda83 9353 draw_sync_t *ds = (draw_sync_t*) data;
ef5e8132
RN
9354 // Sometimes don't want to draw
9355 // normally because another update has taken precedent such as panning the display
9356 // which means this pixmap is no longer valid
9357 if ( ds->vtl->draw_sync_do ) {
9358 gdk_threads_enter();
9359 gdk_draw_drawable (ds->drawable,
9360 ds->gc,
9361 ds->pixmap,
9362 0, 0, 0, 0, -1, -1);
9363 ds->vtl->draw_sync_done = TRUE;
9364 gdk_threads_leave();
9365 }
351eb253 9366 g_free ( ds );
7b203521
EB
9367 return FALSE;
9368}
9369
19a0d58f 9370static gchar* distance_string (gdouble distance)
8da84040
RN
9371{
9372 gchar str[128];
9373
9374 /* draw label with distance */
9375 vik_units_distance_t dist_units = a_vik_get_units_distance ();
9376 switch (dist_units) {
9377 case VIK_UNITS_DISTANCE_MILES:
9378 if (distance >= VIK_MILES_TO_METERS(1) && distance < VIK_MILES_TO_METERS(100)) {
9379 g_sprintf(str, "%3.2f miles", VIK_METERS_TO_MILES(distance));
9380 } else if (distance < 1609.4) {
9381 g_sprintf(str, "%d yards", (int)(distance*1.0936133));
9382 } else {
9383 g_sprintf(str, "%d miles", (int)VIK_METERS_TO_MILES(distance));
9384 }
9385 break;
9386 default:
9387 // VIK_UNITS_DISTANCE_KILOMETRES
9388 if (distance >= 1000 && distance < 100000) {
9389 g_sprintf(str, "%3.2f km", distance/1000.0);
9390 } else if (distance < 1000) {
9391 g_sprintf(str, "%d m", (int)distance);
9392 } else {
9393 g_sprintf(str, "%d km", (int)distance/1000);
9394 }
9395 break;
9396 }
9397 return g_strdup (str);
9398}
9399
9400/*
9401 * Actually set the message in statusbar
9402 */
9a3538f5 9403static void statusbar_write (gdouble distance, gdouble elev_gain, gdouble elev_loss, gdouble last_step, gdouble angle, VikTrwLayer *vtl )
8da84040
RN
9404{
9405 // Only show elevation data when track has some elevation properties
9406 gchar str_gain_loss[64];
9407 str_gain_loss[0] = '\0';
9a3538f5
GB
9408 gchar str_last_step[64];
9409 str_last_step[0] = '\0';
9410 gchar *str_total = distance_string (distance);
8da84040
RN
9411
9412 if ( (elev_gain > 0.1) || (elev_loss > 0.1) ) {
9413 if ( a_vik_get_units_height () == VIK_UNITS_HEIGHT_METRES )
9414 g_sprintf(str_gain_loss, _(" - Gain %dm:Loss %dm"), (int)elev_gain, (int)elev_loss);
9415 else
9416 g_sprintf(str_gain_loss, _(" - Gain %dft:Loss %dft"), (int)VIK_METERS_TO_FEET(elev_gain), (int)VIK_METERS_TO_FEET(elev_loss));
9417 }
9a3538f5
GB
9418
9419 if ( last_step > 0 ) {
9420 gchar *tmp = distance_string (last_step);
0da53bd9 9421 g_sprintf(str_last_step, _(" - Bearing %3.1f° - Step %s"), RAD2DEG(angle), tmp);
9a3538f5
GB
9422 g_free ( tmp );
9423 }
9424
9425 VikWindow *vw = VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl));
8da84040
RN
9426
9427 // Write with full gain/loss information
9a3538f5
GB
9428 gchar *msg = g_strdup_printf ( "Total %s%s%s", str_total, str_last_step, str_gain_loss);
9429 vik_statusbar_set_message ( vik_window_get_statusbar (vw), VIK_STATUSBAR_INFO, msg );
8da84040 9430 g_free ( msg );
9a3538f5 9431 g_free ( str_total );
8da84040
RN
9432}
9433
9434/*
9435 * Figure out what information should be set in the statusbar and then write it
9436 */
9437static void update_statusbar ( VikTrwLayer *vtl )
9438{
9439 // Get elevation data
9440 gdouble elev_gain, elev_loss;
9441 vik_track_get_total_elevation_gain ( vtl->current_track, &elev_gain, &elev_loss);
9442
9443 /* Find out actual distance of current track */
9444 gdouble distance = vik_track_get_length (vtl->current_track);
8da84040 9445
9a3538f5 9446 statusbar_write (distance, elev_gain, elev_loss, 0, 0, vtl);
8da84040
RN
9447}
9448
9449
dc2c040e 9450static VikLayerToolFuncStatus tool_new_track_move ( VikTrwLayer *vtl, GdkEventMotion *event, VikViewport *vvp )
7b203521
EB
9451{
9452 /* if we haven't sync'ed yet, we don't have time to do more. */
745fda83 9453 if ( vtl->draw_sync_done && vtl->current_track && vtl->current_track->trackpoints ) {
215ebe59 9454 VikTrackpoint *last_tpt = vik_track_get_tp_last(vtl->current_track);
745fda83
RN
9455
9456 static GdkPixmap *pixmap = NULL;
9457 int w1, h1, w2, h2;
9458 // Need to check in case window has been resized
9459 w1 = vik_viewport_get_width(vvp);
9460 h1 = vik_viewport_get_height(vvp);
9461 if (!pixmap) {
9b082b39 9462 pixmap = gdk_pixmap_new ( gtk_widget_get_window(GTK_WIDGET(vvp)), w1, h1, -1 );
745fda83
RN
9463 }
9464 gdk_drawable_get_size (pixmap, &w2, &h2);
9465 if (w1 != w2 || h1 != h2) {
9466 g_object_unref ( G_OBJECT ( pixmap ) );
9b082b39 9467 pixmap = gdk_pixmap_new ( gtk_widget_get_window(GTK_WIDGET(vvp)), w1, h1, -1 );
745fda83
RN
9468 }
9469
9470 // Reset to background
9471 gdk_draw_drawable (pixmap,
9472 vtl->current_track_newpoint_gc,
9473 vik_viewport_get_pixmap(vvp),
9474 0, 0, 0, 0, -1, -1);
9475
9476 draw_sync_t *passalong;
7b203521
EB
9477 gint x1, y1;
9478
c31b3fbb 9479 vik_viewport_coord_to_screen ( vvp, &(last_tpt->coord), &x1, &y1 );
745fda83
RN
9480
9481 // FOR SCREEN OVERLAYS WE MUST DRAW INTO THIS PIXMAP (when using the reset method)
9482 // otherwise using vik_viewport_draw_* functions puts the data into the base pixmap,
9483 // thus when we come to reset to the background it would include what we have already drawn!!
9484 gdk_draw_line ( pixmap,
9485 vtl->current_track_newpoint_gc,
9486 x1, y1, event->x, event->y );
9487 // Using this reset method is more reliable than trying to undraw previous efforts via the GDK_INVERT method
23ea1329 9488
23ea1329
RN
9489 /* Find out actual distance of current track */
9490 gdouble distance = vik_track_get_length (vtl->current_track);
9491
9492 // Now add distance to where the pointer is //
9493 VikCoord coord;
9494 struct LatLon ll;
9495 vik_viewport_screen_to_coord ( vvp, (gint) event->x, (gint) event->y, &coord );
9496 vik_coord_to_latlon ( &coord, &ll );
9a3538f5
GB
9497 gdouble last_step = vik_coord_diff( &coord, &(last_tpt->coord));
9498 distance = distance + last_step;
23ea1329 9499
8da84040
RN
9500 // Get elevation data
9501 gdouble elev_gain, elev_loss;
9502 vik_track_get_total_elevation_gain ( vtl->current_track, &elev_gain, &elev_loss);
9503
9504 // Adjust elevation data (if available) for the current pointer position
9505 gdouble elev_new;
9506 elev_new = (gdouble) a_dems_get_elev_by_coord ( &coord, VIK_DEM_INTERPOL_BEST );
9507 if ( elev_new != VIK_DEM_INVALID_ELEVATION ) {
c31b3fbb 9508 if ( last_tpt->altitude != VIK_DEFAULT_ALTITUDE ) {
8da84040 9509 // Adjust elevation of last track point
c31b3fbb 9510 if ( elev_new > last_tpt->altitude )
8da84040 9511 // Going up
c31b3fbb 9512 elev_gain += elev_new - last_tpt->altitude;
8da84040
RN
9513 else
9514 // Going down
c31b3fbb 9515 elev_loss += last_tpt->altitude - elev_new;
23ea1329 9516 }
23ea1329 9517 }
6ba8e356 9518
90611609
RN
9519 //
9520 // Display of the distance 'tooltip' during track creation is controlled by a preference
9521 //
9522 if ( a_vik_get_create_track_tooltip() ) {
9523
9524 gchar *str = distance_string (distance);
061ccfdc 9525
90611609
RN
9526 PangoLayout *pl = gtk_widget_create_pango_layout (GTK_WIDGET(vvp), NULL);
9527 pango_layout_set_font_description (pl, gtk_widget_get_style(GTK_WIDGET(vvp))->font_desc);
9528 pango_layout_set_text (pl, str, -1);
9529 gint wd, hd;
9530 pango_layout_get_pixel_size ( pl, &wd, &hd );
6ba8e356 9531
90611609
RN
9532 gint xd,yd;
9533 // offset from cursor a bit depending on font size
9534 xd = event->x + 10;
9535 yd = event->y - hd;
56cb1807 9536
90611609
RN
9537 // Create a background block to make the text easier to read over the background map
9538 GdkGC *background_block_gc = vik_viewport_new_gc ( vvp, "#cccccc", 1);
9539 gdk_draw_rectangle (pixmap, background_block_gc, TRUE, xd-2, yd-2, wd+4, hd+2);
9540 gdk_draw_layout (pixmap, vtl->current_track_newpoint_gc, xd, yd, pl);
745fda83 9541
90611609
RN
9542 g_object_unref ( G_OBJECT ( pl ) );
9543 g_object_unref ( G_OBJECT ( background_block_gc ) );
9544 g_free (str);
9545 }
6ba8e356 9546
745fda83 9547 passalong = g_new(draw_sync_t,1); // freed by draw_sync()
7b203521 9548 passalong->vtl = vtl;
745fda83 9549 passalong->pixmap = pixmap;
9b082b39 9550 passalong->drawable = gtk_widget_get_window(GTK_WIDGET(vvp));
745fda83 9551 passalong->gc = vtl->current_track_newpoint_gc;
8da84040 9552
9a3538f5
GB
9553 gdouble angle;
9554 gdouble baseangle;
9555 vik_viewport_compute_bearing ( vvp, x1, y1, event->x, event->y, &angle, &baseangle );
9556
8da84040 9557 // Update statusbar with full gain/loss information
9a3538f5 9558 statusbar_write (distance, elev_gain, elev_loss, last_step, angle, vtl);
7b203521 9559
745fda83
RN
9560 // draw pixmap when we have time to
9561 g_idle_add_full (G_PRIORITY_HIGH_IDLE + 10, draw_sync, passalong, NULL);
9562 vtl->draw_sync_done = FALSE;
165d30aa 9563 return VIK_LAYER_TOOL_ACK_GRAB_FOCUS;
7b203521 9564 }
165d30aa 9565 return VIK_LAYER_TOOL_ACK;
7b203521
EB
9566}
9567
215ebe59
RN
9568// NB vtl->current_track must be valid
9569static void undo_trackpoint_add ( VikTrwLayer *vtl )
9570{
9571 // 'undo'
9572 if ( vtl->current_track->trackpoints ) {
9573 // TODO rework this...
9574 //vik_trackpoint_free ( vik_track_get_tp_last (vtl->current_track) );
9575 GList *last = g_list_last(vtl->current_track->trackpoints);
9576 g_free ( last->data );
9577 vtl->current_track->trackpoints = g_list_remove_link ( vtl->current_track->trackpoints, last );
9578
9579 vik_track_calculate_bounds ( vtl->current_track );
9580 }
9581}
9582
777e2d4d
EB
9583static gboolean tool_new_track_key_press ( VikTrwLayer *vtl, GdkEventKey *event, VikViewport *vvp )
9584{
9585 if ( vtl->current_track && event->keyval == GDK_Escape ) {
9586 vtl->current_track = NULL;
da121f9b 9587 vik_layer_emit_update ( VIK_LAYER(vtl) );
777e2d4d
EB
9588 return TRUE;
9589 } else if ( vtl->current_track && event->keyval == GDK_BackSpace ) {
215ebe59 9590 undo_trackpoint_add ( vtl );
8da84040 9591 update_statusbar ( vtl );
da121f9b 9592 vik_layer_emit_update ( VIK_LAYER(vtl) );
777e2d4d
EB
9593 return TRUE;
9594 }
9595 return FALSE;
9596}
9597
e37b2a6d
RN
9598/*
9599 * Common function to handle trackpoint button requests on either a route or a track
9600 * . enables adding a point via normal click
9601 * . enables removal of last point via right click
9602 * . finishing of the track or route via double clicking
9603 */
9604static gboolean tool_new_track_or_route_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
50a14534
EB
9605{
9606 VikTrackpoint *tp;
9607
941aa6e9
AF
9608 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
9609 return FALSE;
9610
ef5e8132
RN
9611 if ( event->button == 2 ) {
9612 // As the display is panning, the new track pixmap is now invalid so don't draw it
9613 // otherwise this drawing done results in flickering back to an old image
9614 vtl->draw_sync_do = FALSE;
9615 return FALSE;
9616 }
9617
e37b2a6d 9618 if ( event->button == 3 )
50a14534 9619 {
e37b2a6d
RN
9620 if ( !vtl->current_track )
9621 return FALSE;
215ebe59 9622 undo_trackpoint_add ( vtl );
8da84040 9623 update_statusbar ( vtl );
da121f9b 9624 vik_layer_emit_update ( VIK_LAYER(vtl) );
50a14534
EB
9625 return TRUE;
9626 }
9627
9628 if ( event->type == GDK_2BUTTON_PRESS )
9629 {
9630 /* subtract last (duplicate from double click) tp then end */
9631 if ( vtl->current_track && vtl->current_track->trackpoints && vtl->ct_x1 == vtl->ct_x2 && vtl->ct_y1 == vtl->ct_y2 )
9632 {
50a14534 9633 /* undo last, then end */
215ebe59 9634 undo_trackpoint_add ( vtl );
50a14534
EB
9635 vtl->current_track = NULL;
9636 }
da121f9b 9637 vik_layer_emit_update ( VIK_LAYER(vtl) );
50a14534
EB
9638 return TRUE;
9639 }
9640
50a14534
EB
9641 tp = vik_trackpoint_new();
9642 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &(tp->coord) );
9643
9644 /* snap to other TP */
9645 if ( event->state & GDK_CONTROL_MASK )
9646 {
9647 VikTrackpoint *other_tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
9648 if ( other_tp )
9649 tp->coord = other_tp->coord;
9650 }
9651
9652 tp->newsegment = FALSE;
9653 tp->has_timestamp = FALSE;
9654 tp->timestamp = 0;
e37b2a6d
RN
9655
9656 if ( vtl->current_track ) {
9bc95d58 9657 vik_track_add_trackpoint ( vtl->current_track, tp, TRUE ); // Ensure bounds is updated
e37b2a6d
RN
9658 /* Auto attempt to get elevation from DEM data (if it's available) */
9659 vik_track_apply_dem_data_last_trackpoint ( vtl->current_track );
9660 }
50a14534
EB
9661
9662 vtl->ct_x1 = vtl->ct_x2;
9663 vtl->ct_y1 = vtl->ct_y2;
9664 vtl->ct_x2 = event->x;
9665 vtl->ct_y2 = event->y;
9666
da121f9b 9667 vik_layer_emit_update ( VIK_LAYER(vtl) );
50a14534
EB
9668 return TRUE;
9669}
9670
e37b2a6d
RN
9671static gboolean tool_new_track_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
9672{
ee43b101
MH
9673 // if we were running the route finder, cancel it
9674 vtl->route_finder_started = FALSE;
9675
e37b2a6d
RN
9676 // ----------------------------------------------------- if current is a route - switch to new track
9677 if ( event->button == 1 && ( ! vtl->current_track || (vtl->current_track && vtl->current_track->is_route ) ))
9678 {
9679 gchar *name = trw_layer_new_unique_sublayer_name(vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, _("Track"));
fb40bae0 9680 if ( ( name = a_dialog_new_track ( VIK_GTK_WINDOW_FROM_LAYER(vtl), name, FALSE ) ) )
e37b2a6d 9681 {
2f154c90 9682 new_track_create_common ( vtl, name );
1613e468 9683 g_free ( name );
e37b2a6d
RN
9684 }
9685 else
9686 return TRUE;
9687 }
9688 return tool_new_track_or_route_click ( vtl, event, vvp );
9689}
9690
ef5e8132
RN
9691static void tool_new_track_release ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
9692{
9693 if ( event->button == 2 ) {
9694 // Pan moving ended - enable potential point drawing again
9695 vtl->draw_sync_do = TRUE;
9696 vtl->draw_sync_done = TRUE;
9697 }
9698}
9699
e37b2a6d
RN
9700/*** New route ****/
9701
9702static gpointer tool_new_route_create ( VikWindow *vw, VikViewport *vvp)
9703{
9704 return vvp;
9705}
9706
9707static gboolean tool_new_route_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
9708{
ee43b101
MH
9709 // if we were running the route finder, cancel it
9710 vtl->route_finder_started = FALSE;
9711
9712 // -------------------------- if current is a track - switch to new route,
9713 if ( event->button == 1 && ( ! vtl->current_track ||
9714 (vtl->current_track && !vtl->current_track->is_route ) ) )
e37b2a6d
RN
9715 {
9716 gchar *name = trw_layer_new_unique_sublayer_name(vtl, VIK_TRW_LAYER_SUBLAYER_ROUTE, _("Route"));
fb40bae0 9717 if ( ( name = a_dialog_new_track ( VIK_GTK_WINDOW_FROM_LAYER(vtl), name, TRUE ) ) ) {
b1453c16 9718 new_route_create_common ( vtl, name );
1613e468
RN
9719 g_free ( name );
9720 }
e37b2a6d
RN
9721 else
9722 return TRUE;
9723 }
9724 return tool_new_track_or_route_click ( vtl, event, vvp );
9725}
9726
941aa6e9
AF
9727/*** New waypoint ****/
9728
9729static gpointer tool_new_waypoint_create ( VikWindow *vw, VikViewport *vvp)
9730{
9731 return vvp;
9732}
9733
9734static gboolean tool_new_waypoint_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
9735{
9736 VikCoord coord;
9737 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
9738 return FALSE;
9739 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &coord );
aa0665e5
RN
9740 if (vik_trw_layer_new_waypoint ( vtl, VIK_GTK_WINDOW_FROM_LAYER(vtl), &coord ) && VIK_LAYER(vtl)->visible) {
9741 trw_layer_calculate_bounds_waypoints ( vtl );
da121f9b 9742 vik_layer_emit_update ( VIK_LAYER(vtl) );
aa0665e5 9743 }
941aa6e9
AF
9744 return TRUE;
9745}
9746
9747
9748/*** Edit trackpoint ****/
9749
9750static gpointer tool_edit_trackpoint_create ( VikWindow *vw, VikViewport *vvp)
9751{
7432fddf 9752 tool_ed_t *t = g_new(tool_ed_t, 1);
33534cd8
AF
9753 t->vvp = vvp;
9754 t->holding = FALSE;
9755 return t;
941aa6e9
AF
9756}
9757
919ed63e
RN
9758static void tool_edit_trackpoint_destroy ( tool_ed_t *t )
9759{
9760 g_free ( t );
9761}
9762
33534cd8 9763static gboolean tool_edit_trackpoint_click ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data )
941aa6e9 9764{
7432fddf 9765 tool_ed_t *t = data;
33534cd8
AF
9766 VikViewport *vvp = t->vvp;
9767 TPSearchParams params;
941aa6e9
AF
9768 /* OUTDATED DOCUMENTATION:
9769 find 5 pixel range on each side. then put these UTM, and a pointer
9770 to the winning track name (and maybe the winning track itself), and a
9771 pointer to the winning trackpoint, inside an array or struct. pass
9772 this along, do a foreach on the tracks which will do a foreach on the
9773 trackpoints. */
9774 params.vvp = vvp;
9775 params.x = event->x;
9776 params.y = event->y;
ce4bd1cf 9777 params.closest_track_id = NULL;
941aa6e9
AF
9778 /* TODO: should get track listitem so we can break it up, make a new track, mess it up, all that. */
9779 params.closest_tp = NULL;
9414408e 9780 params.closest_tpl = NULL;
acf5b0d4 9781 vik_viewport_get_min_max_lat_lon ( vvp, &(params.bbox.south), &(params.bbox.north), &(params.bbox.west), &(params.bbox.east) );
941aa6e9 9782
7432fddf
AF
9783 if ( event->button != 1 )
9784 return FALSE;
9785
941aa6e9
AF
9786 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
9787 return FALSE;
9788
0d2b891f 9789 if ( !vtl->vl.visible || !vtl->tracks_visible || !vtl->routes_visible )
87741170
RN
9790 return FALSE;
9791
941aa6e9
AF
9792 if ( vtl->current_tpl )
9793 {
9794 /* 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.) */
9795 VikTrackpoint *tp = VIK_TRACKPOINT(vtl->current_tpl->data);
ce4bd1cf
RN
9796 VikTrack *current_tr = VIK_TRACK(g_hash_table_lookup(vtl->tracks, vtl->current_tp_id));
9797 if ( !current_tr )
9798 return FALSE;
941aa6e9 9799
ce4bd1cf 9800 gint x, y;
941aa6e9
AF
9801 vik_viewport_coord_to_screen ( vvp, &(tp->coord), &x, &y );
9802
9803 if ( current_tr->visible &&
9804 abs(x - event->x) < TRACKPOINT_SIZE_APPROX &&
7432fddf
AF
9805 abs(y - event->y) < TRACKPOINT_SIZE_APPROX ) {
9806 marker_begin_move ( t, event->x, event->y );
941aa6e9
AF
9807 return TRUE;
9808 }
9809
941aa6e9
AF
9810 }
9811
0d2b891f
RN
9812 if ( vtl->tracks_visible )
9813 g_hash_table_foreach ( vtl->tracks, (GHFunc) track_search_closest_tp, &params);
941aa6e9
AF
9814
9815 if ( params.closest_tp )
9816 {
ce4bd1cf 9817 vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, g_hash_table_lookup ( vtl->tracks_iters, params.closest_track_id ), TRUE );
941aa6e9 9818 vtl->current_tpl = params.closest_tpl;
ce4bd1cf 9819 vtl->current_tp_id = params.closest_track_id;
da14cc69 9820 vtl->current_tp_track = g_hash_table_lookup ( vtl->tracks, params.closest_track_id );
941aa6e9 9821 trw_layer_tpwin_init ( vtl );
95d1b757 9822 set_statusbar_msg_info_trkpt ( vtl, params.closest_tp );
da121f9b 9823 vik_layer_emit_update ( VIK_LAYER(vtl) );
941aa6e9
AF
9824 return TRUE;
9825 }
9826
0d2b891f
RN
9827 if ( vtl->routes_visible )
9828 g_hash_table_foreach ( vtl->routes, (GHFunc) track_search_closest_tp, &params);
9829
9830 if ( params.closest_tp )
9831 {
9832 vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, g_hash_table_lookup ( vtl->routes_iters, params.closest_track_id ), TRUE );
9833 vtl->current_tpl = params.closest_tpl;
9834 vtl->current_tp_id = params.closest_track_id;
9835 vtl->current_tp_track = g_hash_table_lookup ( vtl->routes, params.closest_track_id );
9836 trw_layer_tpwin_init ( vtl );
9837 set_statusbar_msg_info_trkpt ( vtl, params.closest_tp );
da121f9b 9838 vik_layer_emit_update ( VIK_LAYER(vtl) );
0d2b891f
RN
9839 return TRUE;
9840 }
9841
941aa6e9
AF
9842 /* these aren't the droids you're looking for */
9843 return FALSE;
9844}
9845
dc2c040e 9846static gboolean tool_edit_trackpoint_move ( VikTrwLayer *vtl, GdkEventMotion *event, gpointer data )
33534cd8 9847{
7432fddf 9848 tool_ed_t *t = data;
33534cd8
AF
9849 VikViewport *vvp = t->vvp;
9850
9851 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
9852 return FALSE;
9853
9854 if ( t->holding )
9855 {
9856 VikCoord new_coord;
33534cd8
AF
9857 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
9858
9859 /* snap to TP */
9860 if ( event->state & GDK_CONTROL_MASK )
9861 {
9862 VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
9863 if ( tp && tp != vtl->current_tpl->data )
9864 new_coord = tp->coord;
9865 }
9866 // VIK_TRACKPOINT(vtl->current_tpl->data)->coord = new_coord;
7432fddf
AF
9867 {
9868 gint x, y;
9869 vik_viewport_coord_to_screen ( vvp, &new_coord, &x, &y );
9870 marker_moveto ( t, x, y );
9871 }
33534cd8
AF
9872
9873 return TRUE;
9874 }
9875 return FALSE;
9876}
9877
33534cd8 9878static gboolean tool_edit_trackpoint_release ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data )
941aa6e9 9879{
7432fddf 9880 tool_ed_t *t = data;
33534cd8
AF
9881 VikViewport *vvp = t->vvp;
9882
941aa6e9
AF
9883 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
9884 return FALSE;
7432fddf
AF
9885 if ( event->button != 1)
9886 return FALSE;
33534cd8 9887
7432fddf 9888 if ( t->holding ) {
941aa6e9 9889 VikCoord new_coord;
941aa6e9
AF
9890 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
9891
9892 /* snap to TP */
9893 if ( event->state & GDK_CONTROL_MASK )
9894 {
9895 VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
9896 if ( tp && tp != vtl->current_tpl->data )
9897 new_coord = tp->coord;
9898 }
9899
9900 VIK_TRACKPOINT(vtl->current_tpl->data)->coord = new_coord;
20981fd6
RN
9901 if ( vtl->current_tp_track )
9902 vik_track_calculate_bounds ( vtl->current_tp_track );
941aa6e9 9903
7432fddf 9904 marker_end_move ( t );
33534cd8 9905
941aa6e9 9906 /* diff dist is diff from orig */
46b6631a 9907 if ( vtl->tpwin )
ce4bd1cf 9908 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, vtl->current_tp_track->name );
941aa6e9 9909
da121f9b 9910 vik_layer_emit_update ( VIK_LAYER(vtl) );
941aa6e9
AF
9911 return TRUE;
9912 }
9913 return FALSE;
9914}
9915
9916
ee43b101
MH
9917/*** Extended Route Finder ***/
9918
9919static gpointer tool_extended_route_finder_create ( VikWindow *vw, VikViewport *vvp)
1eef1bde
QT
9920{
9921 return vvp;
9922}
9923
ee43b101
MH
9924static void tool_extended_route_finder_undo ( VikTrwLayer *vtl )
9925{
9926 VikCoord *new_end;
9927 new_end = vik_track_cut_back_to_double_point ( vtl->current_track );
9928 if ( new_end ) {
9929 g_free ( new_end );
9930 vik_layer_emit_update ( VIK_LAYER(vtl) );
9931
9932 /* remove last ' to:...' */
9933 if ( vtl->current_track->comment ) {
9934 gchar *last_to = strrchr ( vtl->current_track->comment, 't' );
9935 if ( last_to && (last_to - vtl->current_track->comment > 1) ) {
9936 gchar *new_comment = g_strndup ( vtl->current_track->comment,
9937 last_to - vtl->current_track->comment - 1);
9938 vik_track_set_comment_no_copy ( vtl->current_track, new_comment );
9939 }
9940 }
9941 }
9942}
9943
9944
9945static gboolean tool_extended_route_finder_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
1eef1bde
QT
9946{
9947 VikCoord tmp;
0c1044e9 9948 if ( !vtl ) return FALSE;
1eef1bde 9949 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &tmp );
ee43b101
MH
9950 if ( event->button == 3 && vtl->current_track ) {
9951 tool_extended_route_finder_undo ( vtl );
9952 }
9953 else if ( event->button == 2 ) {
9954 vtl->draw_sync_do = FALSE;
9955 return FALSE;
c3deba01 9956 }
ee43b101
MH
9957 // if we started the track but via undo deleted all the track points, begin again
9958 else if ( vtl->current_track && vtl->current_track->is_route && ! vik_track_get_tp_first ( vtl->current_track ) ) {
9959 return tool_new_track_or_route_click ( vtl, event, vvp );
9960 }
9961 else if ( ( vtl->current_track && vtl->current_track->is_route ) ||
9962 ( event->state & GDK_CONTROL_MASK && vtl->current_track ) ) {
1eef1bde 9963 struct LatLon start, end;
bddd2056 9964
ee43b101
MH
9965 VikTrackpoint *tp_start = vik_track_get_tp_last ( vtl->current_track );
9966 vik_coord_to_latlon ( &(tp_start->coord), &start );
1eef1bde 9967 vik_coord_to_latlon ( &(tmp), &end );
bddd2056 9968
ee43b101
MH
9969 vtl->route_finder_started = TRUE;
9970 vtl->route_finder_append = TRUE; // merge tracks. keep started true.
9971
9972 // update UI to let user know what's going on
9973 VikStatusbar *sb = vik_window_get_statusbar (VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)));
9974 VikRoutingEngine *engine = vik_routing_default_engine ( );
9975 if ( ! engine ) {
9976 vik_statusbar_set_message ( sb, VIK_STATUSBAR_INFO, "Cannot plan route without a default routing engine." );
9977 return TRUE;
bddd2056 9978 }
ee43b101
MH
9979 gchar *msg = g_strdup_printf ( _("Querying %s for route between (%.3f, %.3f) and (%.3f, %.3f)."),
9980 vik_routing_engine_get_label ( engine ),
9981 start.lat, start.lon, end.lat, end.lon );
9982 vik_statusbar_set_message ( sb, VIK_STATUSBAR_INFO, msg );
9983 g_free ( msg );
9984 vik_window_set_busy_cursor ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)) );
bddd2056 9985
bddd2056 9986
ee43b101
MH
9987 /* Give GTK a change to display the new status bar before querying the web */
9988 while ( gtk_events_pending ( ) )
9989 gtk_main_iteration ( );
20981fd6 9990
ee43b101 9991 gboolean find_status = vik_routing_default_find ( vtl, start, end );
20981fd6 9992
ee43b101
MH
9993 /* Update UI to say we're done */
9994 vik_window_clear_busy_cursor ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)) );
9995 msg = ( find_status ) ? g_strdup_printf ( _("%s returned route between (%.3f, %.3f) and (%.3f, %.3f)."),
9996 vik_routing_engine_get_label ( engine ),
9997 start.lat, start.lon, end.lat, end.lon )
9998 : g_strdup_printf ( _("Error getting route from %s."),
9999 vik_routing_engine_get_label ( engine ) );
10000 vik_statusbar_set_message ( sb, VIK_STATUSBAR_INFO, msg );
10001 g_free ( msg );
bddd2056 10002
da121f9b 10003 vik_layer_emit_update ( VIK_LAYER(vtl) );
1eef1bde 10004 } else {
ee43b101
MH
10005 vtl->current_track = NULL;
10006
10007 // create a new route where we will add the planned route to
10008 gboolean ret = tool_new_route_click( vtl, event, vvp );
10009
7ff7d728 10010 vtl->route_finder_started = TRUE;
ee43b101
MH
10011
10012 return ret;
1eef1bde 10013 }
1eef1bde
QT
10014 return TRUE;
10015}
10016
ee43b101
MH
10017static gboolean tool_extended_route_finder_key_press ( VikTrwLayer *vtl, GdkEventKey *event, VikViewport *vvp )
10018{
10019 if ( vtl->current_track && event->keyval == GDK_Escape ) {
10020 vtl->route_finder_started = FALSE;
10021 vtl->current_track = NULL;
10022 vik_layer_emit_update ( VIK_LAYER(vtl) );
10023 return TRUE;
10024 } else if ( vtl->current_track && event->keyval == GDK_BackSpace ) {
10025 tool_extended_route_finder_undo ( vtl );
10026 }
10027 return FALSE;
10028}
10029
10030
10031
941aa6e9
AF
10032/*** Show picture ****/
10033
10034static gpointer tool_show_picture_create ( VikWindow *vw, VikViewport *vvp)
10035{
10036 return vvp;
10037}
10038
10039/* Params are: vvp, event, last match found or NULL */
9e212bfc 10040static void tool_show_picture_wp ( const gpointer id, VikWaypoint *wp, gpointer params[3] )
941aa6e9
AF
10041{
10042 if ( wp->image && wp->visible )
10043 {
10044 gint x, y, slackx, slacky;
10045 GdkEventButton *event = (GdkEventButton *) params[1];
10046
10047 vik_viewport_coord_to_screen ( VIK_VIEWPORT(params[0]), &(wp->coord), &x, &y );
10048 slackx = wp->image_width / 2;
10049 slacky = wp->image_height / 2;
10050 if ( x <= event->x + slackx && x >= event->x - slackx
10051 && y <= event->y + slacky && y >= event->y - slacky )
10052 {
10053 params[2] = wp->image; /* we've found a match. however continue searching
10054 * since we want to find the last match -- that
10055 * is, the match that was drawn last. */
10056 }
10057 }
10058}
10059
19782ffd 10060static void trw_layer_show_picture ( menu_array_sublayer values )
a412f3f5
RN
10061{
10062 /* thanks to the Gaim people for showing me ShellExecute and g_spawn_command_line_async */
10063#ifdef WINDOWS
19782ffd 10064 ShellExecute(NULL, "open", (char *) values[MA_MISC], NULL, NULL, SW_SHOWNORMAL);
a412f3f5
RN
10065#else /* WINDOWS */
10066 GError *err = NULL;
19782ffd 10067 gchar *quoted_file = g_shell_quote ( (gchar *) values[MA_MISC] );
3317dc4e 10068 gchar *cmd = g_strdup_printf ( "%s %s", a_vik_get_image_viewer(), quoted_file );
a412f3f5
RN
10069 g_free ( quoted_file );
10070 if ( ! g_spawn_command_line_async ( cmd, &err ) )
10071 {
19782ffd 10072 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
10073 g_error_free ( err );
10074 }
10075 g_free ( cmd );
10076#endif /* WINDOWS */
10077}
10078
941aa6e9
AF
10079static gboolean tool_show_picture_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
10080{
10081 gpointer params[3] = { vvp, event, NULL };
10082 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
10083 return FALSE;
10084 g_hash_table_foreach ( vtl->waypoints, (GHFunc) tool_show_picture_wp, params );
10085 if ( params[2] )
10086 {
19782ffd
RN
10087 static menu_array_sublayer values;
10088 values[MA_VTL] = vtl;
10089 values[MA_MISC] = params[2];
10090 trw_layer_show_picture ( values );
941aa6e9
AF
10091 return TRUE; /* found a match */
10092 }
10093 else
10094 return FALSE; /* go through other layers, searching for a match */
10095}
10096
10097/***************************************************************************
10098 ** End tool code
10099 ***************************************************************************/
10100
10101
9e212bfc 10102static void image_wp_make_list ( const gpointer id, VikWaypoint *wp, GSList **pics )
50a14534
EB
10103{
10104 if ( wp->image && ( ! a_thumbnails_exists ( wp->image ) ) )
10105 *pics = g_slist_append ( *pics, (gpointer) g_strdup ( wp->image ) );
10106}
10107
91822ddd
RN
10108/* Structure for thumbnail creating data used in the background thread */
10109typedef struct {
10110 VikTrwLayer *vtl; // Layer needed for redrawing
10111 GSList *pics; // Image list
10112} thumbnail_create_thread_data;
10113
10114static int create_thumbnails_thread ( thumbnail_create_thread_data *tctd, gpointer threaddata )
50a14534 10115{
91822ddd
RN
10116 guint total = g_slist_length(tctd->pics), done = 0;
10117 while ( tctd->pics )
50a14534 10118 {
91822ddd 10119 a_thumbnails_create ( (gchar *) tctd->pics->data );
54861848
GB
10120 int result = a_background_thread_progress ( threaddata, ((gdouble) ++done) / total );
10121 if ( result != 0 )
10122 return -1; /* Abort thread */
10123
91822ddd 10124 tctd->pics = tctd->pics->next;
50a14534 10125 }
91822ddd
RN
10126
10127 // Redraw to show the thumbnails as they are now created
91822ddd 10128 if ( IS_VIK_LAYER(tctd->vtl) )
da121f9b 10129 vik_layer_emit_update ( VIK_LAYER(tctd->vtl) ); // NB update from background thread
91822ddd 10130
17c8aefa 10131 return 0;
50a14534
EB
10132}
10133
91822ddd 10134static void thumbnail_create_thread_free ( thumbnail_create_thread_data *tctd )
50a14534 10135{
91822ddd 10136 while ( tctd->pics )
50a14534 10137 {
91822ddd
RN
10138 g_free ( tctd->pics->data );
10139 tctd->pics = g_slist_delete_link ( tctd->pics, tctd->pics );
50a14534 10140 }
91822ddd 10141 g_free ( tctd );
50a14534
EB
10142}
10143
b3eb3b98 10144void trw_layer_verify_thumbnails ( VikTrwLayer *vtl, GtkWidget *vp )
50a14534
EB
10145{
10146 if ( ! vtl->has_verified_thumbnails )
10147 {
10148 GSList *pics = NULL;
10149 g_hash_table_foreach ( vtl->waypoints, (GHFunc) image_wp_make_list, &pics );
10150 if ( pics )
10151 {
10152 gint len = g_slist_length ( pics );
4c77d5e0 10153 gchar *tmp = g_strdup_printf ( _("Creating %d Image Thumbnails..."), len );
91822ddd
RN
10154 thumbnail_create_thread_data *tctd = g_malloc ( sizeof(thumbnail_create_thread_data) );
10155 tctd->vtl = vtl;
10156 tctd->pics = pics;
10157 a_background_thread ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
10158 tmp,
10159 (vik_thr_func) create_thumbnails_thread,
10160 tctd,
10161 (vik_thr_free_func) thumbnail_create_thread_free,
10162 NULL,
10163 len );
50a14534
EB
10164 g_free ( tmp );
10165 }
10166 }
10167}
10168
b1453c16
RN
10169static const gchar* my_track_colors ( gint ii )
10170{
10171 static const gchar* colors[VIK_TRW_LAYER_TRACK_GCS] = {
10172 "#2d870a",
ca738b73 10173 "#135D34",
b1453c16
RN
10174 "#0a8783",
10175 "#0e4d87",
10176 "#05469f",
ca738b73 10177 "#695CBB",
b1453c16
RN
10178 "#2d059f",
10179 "#4a059f",
ca738b73 10180 "#5A171A",
b1453c16
RN
10181 "#96059f"
10182 };
10183 // Fast and reliable way of returning a colour
10184 return colors[(ii % VIK_TRW_LAYER_TRACK_GCS)];
10185}
10186
10187static void trw_layer_track_alloc_colors ( VikTrwLayer *vtl )
10188{
10189 GHashTableIter iter;
10190 gpointer key, value;
10191
10192 gint ii = 0;
10193 // Tracks
10194 g_hash_table_iter_init ( &iter, vtl->tracks );
10195
10196 while ( g_hash_table_iter_next (&iter, &key, &value) ) {
10197
10198 // Tracks get a random spread of colours if not already assigned
10199 if ( ! VIK_TRACK(value)->has_color ) {
e2bc000f
RN
10200 if ( vtl->drawmode == DRAWMODE_ALL_SAME_COLOR )
10201 VIK_TRACK(value)->color = vtl->track_color;
10202 else {
10203 gdk_color_parse ( my_track_colors (ii), &(VIK_TRACK(value)->color) );
10204 }
b1453c16
RN
10205 VIK_TRACK(value)->has_color = TRUE;
10206 }
10207
93ee73b3 10208 trw_layer_update_treeview ( vtl, VIK_TRACK(value) );
b1453c16
RN
10209
10210 ii++;
10211 if (ii > VIK_TRW_LAYER_TRACK_GCS)
10212 ii = 0;
10213 }
10214
10215 // Routes
10216 ii = 0;
10217 g_hash_table_iter_init ( &iter, vtl->routes );
10218
10219 while ( g_hash_table_iter_next (&iter, &key, &value) ) {
10220
10221 // Routes get an intermix of reds
10222 if ( ! VIK_TRACK(value)->has_color ) {
10223 if ( ii )
10224 gdk_color_parse ( "#FF0000" , &(VIK_TRACK(value)->color) ); // Red
10225 else
10226 gdk_color_parse ( "#B40916" , &(VIK_TRACK(value)->color) ); // Dark Red
10227 VIK_TRACK(value)->has_color = TRUE;
10228 }
10229
93ee73b3 10230 trw_layer_update_treeview ( vtl, VIK_TRACK(value) );
b1453c16
RN
10231
10232 ii = !ii;
10233 }
10234}
10235
aa0665e5
RN
10236/*
10237 * (Re)Calculate the bounds of the waypoints in this layer,
10238 * This should be called whenever waypoints are changed
10239 */
2cec1c4e 10240void trw_layer_calculate_bounds_waypoints ( VikTrwLayer *vtl )
aa0665e5
RN
10241{
10242 struct LatLon topleft = { 0.0, 0.0 };
10243 struct LatLon bottomright = { 0.0, 0.0 };
10244 struct LatLon ll;
10245
10246 GHashTableIter iter;
10247 gpointer key, value;
10248
10249 g_hash_table_iter_init ( &iter, vtl->waypoints );
10250
10251 // Set bounds to first point
10252 if ( g_hash_table_iter_next (&iter, &key, &value) ) {
10253 vik_coord_to_latlon ( &(VIK_WAYPOINT(value)->coord), &topleft );
10254 vik_coord_to_latlon ( &(VIK_WAYPOINT(value)->coord), &bottomright );
10255 }
10256
10257 // Ensure there is another point...
10258 if ( g_hash_table_size ( vtl->waypoints ) > 1 ) {
10259
10260 while ( g_hash_table_iter_next (&iter, &key, &value) ) {
10261
10262 // See if this point increases the bounds.
10263 vik_coord_to_latlon ( &(VIK_WAYPOINT(value)->coord), &ll );
10264
10265 if ( ll.lat > topleft.lat) topleft.lat = ll.lat;
10266 if ( ll.lon < topleft.lon) topleft.lon = ll.lon;
10267 if ( ll.lat < bottomright.lat) bottomright.lat = ll.lat;
10268 if ( ll.lon > bottomright.lon) bottomright.lon = ll.lon;
10269 }
10270 }
10271
10272 vtl->waypoints_bbox.north = topleft.lat;
10273 vtl->waypoints_bbox.east = bottomright.lon;
10274 vtl->waypoints_bbox.south = bottomright.lat;
10275 vtl->waypoints_bbox.west = topleft.lon;
10276}
10277
20981fd6 10278static void trw_layer_calculate_bounds_track ( gpointer id, VikTrack *trk )
b1453c16 10279{
20981fd6
RN
10280 vik_track_calculate_bounds ( trk );
10281}
10282
10283static void trw_layer_calculate_bounds_tracks ( VikTrwLayer *vtl )
10284{
10285 g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_calculate_bounds_track, NULL );
10286 g_hash_table_foreach ( vtl->routes, (GHFunc) trw_layer_calculate_bounds_track, NULL );
10287}
10288
c9cac058
RN
10289static void trw_layer_sort_all ( VikTrwLayer *vtl )
10290{
7a52aac6
RN
10291 if ( ! VIK_LAYER(vtl)->vt )
10292 return;
10293
c9cac058
RN
10294 // Obviously need 2 to tango - sorting with only 1 (or less) is a lonely activity!
10295 if ( g_hash_table_size (vtl->tracks) > 1 )
10296 vik_treeview_sort_children ( VIK_LAYER(vtl)->vt, &(vtl->tracks_iter), vtl->track_sort_order );
10297
10298 if ( g_hash_table_size (vtl->routes) > 1 )
10299 vik_treeview_sort_children ( VIK_LAYER(vtl)->vt, &(vtl->routes_iter), vtl->track_sort_order );
10300
10301 if ( g_hash_table_size (vtl->waypoints) > 1 )
10302 vik_treeview_sort_children ( VIK_LAYER(vtl)->vt, &(vtl->waypoints_iter), vtl->wp_sort_order );
10303}
10304
20981fd6
RN
10305static void trw_layer_post_read ( VikTrwLayer *vtl, GtkWidget *vvp, gboolean from_file )
10306{
626de648
RN
10307 if ( VIK_LAYER(vtl)->realized )
10308 trw_layer_verify_thumbnails ( vtl, vvp );
b1453c16 10309 trw_layer_track_alloc_colors ( vtl );
aa0665e5
RN
10310
10311 trw_layer_calculate_bounds_waypoints ( vtl );
20981fd6 10312 trw_layer_calculate_bounds_tracks ( vtl );
c9cac058
RN
10313
10314 // Apply treeview sort after loading all the tracks for this layer
10315 // (rather than sorted insert on each individual track additional)
10316 // and after subsequent changes to the properties as the specified order may have changed.
10317 // since the sorting of a treeview section is now very quick
10318 // NB sorting is also performed after every name change as well to maintain the list order
10319 trw_layer_sort_all ( vtl );
6ba42f1e
RN
10320
10321 // Setting metadata time if not otherwise set
10322 if ( vtl->metadata ) {
10323
10324 gboolean need_to_set_time = TRUE;
10325 if ( vtl->metadata->timestamp ) {
10326 need_to_set_time = FALSE;
10327 if ( !g_strcmp0(vtl->metadata->timestamp, "" ) )
10328 need_to_set_time = TRUE;
10329 }
10330
10331 if ( need_to_set_time ) {
10332 // Could rewrite this as a general get first time of a TRW Layer function
10333 GTimeVal timestamp;
10334 timestamp.tv_usec = 0;
10335 gboolean has_timestamp = FALSE;
10336
10337 GList *gl = NULL;
10338 gl = g_hash_table_get_values ( vtl->tracks );
10339 gl = g_list_sort ( gl, vik_track_compare_timestamp );
10340 gl = g_list_first ( gl );
10341
10342 // Check times of tracks
10343 if ( gl ) {
10344 // Only need to check the first track as they have been sorted by time
10345 VikTrack *trk = (VikTrack*)gl->data;
10346 // Assume trackpoints already sorted by time
10347 VikTrackpoint *tpt = vik_track_get_tp_first(trk);
10348 if ( tpt && tpt->has_timestamp ) {
10349 timestamp.tv_sec = tpt->timestamp;
10350 has_timestamp = TRUE;
10351 }
10352 g_list_free ( gl );
10353 }
10354
10355 if ( !has_timestamp ) {
10356 // 'Last' resort - current time
10357 // Get before waypoint tests - so that if a waypoint time value (in the past) is found it should be used
10358 g_get_current_time ( &timestamp );
10359
10360 // Check times of waypoints
10361 gl = g_hash_table_get_values ( vtl->waypoints );
10362 GList *iter;
10363 for (iter = g_list_first (gl); iter != NULL; iter = g_list_next (iter)) {
10364 VikWaypoint *wpt = (VikWaypoint*)iter->data;
10365 if ( wpt->has_timestamp ) {
10366 if ( timestamp.tv_sec > wpt->timestamp ) {
10367 timestamp.tv_sec = wpt->timestamp;
10368 has_timestamp = TRUE;
10369 }
10370 }
10371 }
10372 g_list_free ( gl );
10373 }
10374
10375 vtl->metadata->timestamp = g_time_val_to_iso8601 ( &timestamp );
10376 }
10377 }
b1453c16
RN
10378}
10379
50a14534
EB
10380VikCoordMode vik_trw_layer_get_coord_mode ( VikTrwLayer *vtl )
10381{
10382 return vtl->coord_mode;
10383}
10384
073ede8c
RN
10385/**
10386 * Uniquify the whole layer
10387 * Also requires the layers panel as the names shown there need updating too
10388 * Returns whether the operation was successful or not
10389 */
10390gboolean vik_trw_layer_uniquify ( VikTrwLayer *vtl, VikLayersPanel *vlp )
10391{
10392 if ( vtl && vlp ) {
0d2b891f
RN
10393 vik_trw_layer_uniquify_tracks ( vtl, vlp, vtl->tracks, TRUE );
10394 vik_trw_layer_uniquify_tracks ( vtl, vlp, vtl->routes, FALSE );
073ede8c
RN
10395 vik_trw_layer_uniquify_waypoints ( vtl, vlp );
10396 return TRUE;
10397 }
10398 return FALSE;
10399}
50a14534 10400
c9570f86 10401static void waypoint_convert ( const gpointer id, VikWaypoint *wp, VikCoordMode *dest_mode )
50a14534
EB
10402{
10403 vik_coord_convert ( &(wp->coord), *dest_mode );
10404}
10405
9e212bfc 10406static void track_convert ( const gpointer id, VikTrack *tr, VikCoordMode *dest_mode )
50a14534
EB
10407{
10408 vik_track_convert ( tr, *dest_mode );
10409}
10410
10411static void trw_layer_change_coord_mode ( VikTrwLayer *vtl, VikCoordMode dest_mode )
10412{
10413 if ( vtl->coord_mode != dest_mode )
10414 {
10415 vtl->coord_mode = dest_mode;
10416 g_hash_table_foreach ( vtl->waypoints, (GHFunc) waypoint_convert, &dest_mode );
10417 g_hash_table_foreach ( vtl->tracks, (GHFunc) track_convert, &dest_mode );
6440ca9b 10418 g_hash_table_foreach ( vtl->routes, (GHFunc) track_convert, &dest_mode );
50a14534
EB
10419 }
10420}
e4afc73a 10421
a7cd93ac 10422static void trw_layer_set_menu_selection ( VikTrwLayer *vtl, guint16 selection )
20c7a3a0
QT
10423{
10424 vtl->menu_selection = selection;
10425}
10426
a7cd93ac 10427static guint16 trw_layer_get_menu_selection ( VikTrwLayer *vtl )
20c7a3a0 10428{
a7cd93ac 10429 return (vtl->menu_selection);
20c7a3a0
QT
10430}
10431
7114e879
QT
10432/* ----------- Downloading maps along tracks --------------- */
10433
10434static int get_download_area_width(VikViewport *vvp, gdouble zoom_level, struct LatLon *wh)
10435{
10436 /* TODO: calculating based on current size of viewport */
10437 const gdouble w_at_zoom_0_125 = 0.0013;
10438 const gdouble h_at_zoom_0_125 = 0.0011;
10439 gdouble zoom_factor = zoom_level/0.125;
10440
10441 wh->lat = h_at_zoom_0_125 * zoom_factor;
10442 wh->lon = w_at_zoom_0_125 * zoom_factor;
10443
10444 return 0; /* all OK */
10445}
10446
35e22ed8
QT
10447static VikCoord *get_next_coord(VikCoord *from, VikCoord *to, struct LatLon *dist, gdouble gradient)
10448{
10449 if ((dist->lon >= ABS(to->east_west - from->east_west)) &&
10450 (dist->lat >= ABS(to->north_south - from->north_south)))
10451 return NULL;
10452
10453 VikCoord *coord = g_malloc(sizeof(VikCoord));
10454 coord->mode = VIK_COORD_LATLON;
10455
10456 if (ABS(gradient) < 1) {
10457 if (from->east_west > to->east_west)
10458 coord->east_west = from->east_west - dist->lon;
10459 else
10460 coord->east_west = from->east_west + dist->lon;
10461 coord->north_south = gradient * (coord->east_west - from->east_west) + from->north_south;
10462 } else {
10463 if (from->north_south > to->north_south)
10464 coord->north_south = from->north_south - dist->lat;
10465 else
10466 coord->north_south = from->north_south + dist->lat;
10467 coord->east_west = (1/gradient) * (coord->north_south - from->north_south) + from->north_south;
10468 }
10469
10470 return coord;
10471}
10472
10473static GList *add_fillins(GList *list, VikCoord *from, VikCoord *to, struct LatLon *dist)
10474{
10475 /* TODO: handle virtical track (to->east_west - from->east_west == 0) */
10476 gdouble gradient = (to->north_south - from->north_south)/(to->east_west - from->east_west);
10477
10478 VikCoord *next = from;
10479 while (TRUE) {
10480 if ((next = get_next_coord(next, to, dist, gradient)) == NULL)
10481 break;
10482 list = g_list_prepend(list, next);
10483 }
10484
10485 return list;
10486}
10487
7114e879
QT
10488void vik_track_download_map(VikTrack *tr, VikMapsLayer *vml, VikViewport *vvp, gdouble zoom_level)
10489{
10490 typedef struct _Rect {
10491 VikCoord tl;
10492 VikCoord br;
35e22ed8 10493 VikCoord center;
7114e879 10494 } Rect;
35e22ed8 10495#define GLRECT(iter) ((Rect *)((iter)->data))
7114e879
QT
10496
10497 struct LatLon wh;
35e22ed8 10498 GList *rects_to_download = NULL;
7114e879
QT
10499 GList *rect_iter;
10500
10501 if (get_download_area_width(vvp, zoom_level, &wh))
10502 return;
10503
10504 GList *iter = tr->trackpoints;
35e22ed8
QT
10505 if (!iter)
10506 return;
7114e879
QT
10507
10508 gboolean new_map = TRUE;
10509 VikCoord *cur_coord, tl, br;
10510 Rect *rect;
7114e879
QT
10511 while (iter) {
10512 cur_coord = &(VIK_TRACKPOINT(iter->data))->coord;
10513 if (new_map) {
10514 vik_coord_set_area(cur_coord, &wh, &tl, &br);
10515 rect = g_malloc(sizeof(Rect));
10516 rect->tl = tl;
10517 rect->br = br;
35e22ed8
QT
10518 rect->center = *cur_coord;
10519 rects_to_download = g_list_prepend(rects_to_download, rect);
7114e879
QT
10520 new_map = FALSE;
10521 iter = iter->next;
10522 continue;
10523 }
10524 gboolean found = FALSE;
35e22ed8
QT
10525 for (rect_iter = rects_to_download; rect_iter; rect_iter = rect_iter->next) {
10526 if (vik_coord_inside(cur_coord, &GLRECT(rect_iter)->tl, &GLRECT(rect_iter)->br)) {
7114e879
QT
10527 found = TRUE;
10528 break;
10529 }
10530 }
10531 if (found)
10532 iter = iter->next;
10533 else
10534 new_map = TRUE;
10535 }
35e22ed8 10536
35e22ed8 10537 GList *fillins = NULL;
b1e57d16
RN
10538 /* 'fillin' doesn't work in UTM mode - potentially ending up in massive loop continually allocating memory - hence don't do it */
10539 /* seems that ATM the function get_next_coord works only for LATLON */
10540 if ( cur_coord->mode == VIK_COORD_LATLON ) {
10541 /* fill-ins for far apart points */
10542 GList *cur_rect, *next_rect;
10543 for (cur_rect = rects_to_download;
10544 (next_rect = cur_rect->next) != NULL;
10545 cur_rect = cur_rect->next) {
10546 if ((wh.lon < ABS(GLRECT(cur_rect)->center.east_west - GLRECT(next_rect)->center.east_west)) ||
10547 (wh.lat < ABS(GLRECT(cur_rect)->center.north_south - GLRECT(next_rect)->center.north_south))) {
10548 fillins = add_fillins(fillins, &GLRECT(cur_rect)->center, &GLRECT(next_rect)->center, &wh);
10549 }
35e22ed8 10550 }
3cbbb49e
GB
10551 } else
10552 g_message("%s: this feature works only in Mercator mode", __FUNCTION__);
35e22ed8
QT
10553
10554 if (fillins) {
c60f82df
RN
10555 GList *fiter = fillins;
10556 while (fiter) {
10557 cur_coord = (VikCoord *)(fiter->data);
35e22ed8
QT
10558 vik_coord_set_area(cur_coord, &wh, &tl, &br);
10559 rect = g_malloc(sizeof(Rect));
10560 rect->tl = tl;
10561 rect->br = br;
10562 rect->center = *cur_coord;
10563 rects_to_download = g_list_prepend(rects_to_download, rect);
c60f82df 10564 fiter = fiter->next;
35e22ed8
QT
10565 }
10566 }
10567
10568 for (rect_iter = rects_to_download; rect_iter; rect_iter = rect_iter->next) {
3ac548fa 10569 vik_maps_layer_download_section (vml, vvp, &(((Rect *)(rect_iter->data))->tl), &(((Rect *)(rect_iter->data))->br), zoom_level);
7114e879
QT
10570 }
10571
35e22ed8
QT
10572 if (fillins) {
10573 for (iter = fillins; iter; iter = iter->next)
10574 g_free(iter->data);
10575 g_list_free(fillins);
10576 }
10577 if (rects_to_download) {
10578 for (rect_iter = rects_to_download; rect_iter; rect_iter = rect_iter->next)
10579 g_free(rect_iter->data);
10580 g_list_free(rects_to_download);
10581 }
7114e879
QT
10582}
10583
19782ffd 10584static void trw_layer_download_map_along_track_cb ( menu_array_sublayer values )
7114e879
QT
10585{
10586 VikMapsLayer *vml;
fe7e4e45 10587 gint selected_map;
7114e879
QT
10588 gchar *zoomlist[] = {"0.125", "0.25", "0.5", "1", "2", "4", "8", "16", "32", "64", "128", "256", "512", "1024", NULL };
10589 gdouble zoom_vals[] = {0.125, 0.25, 0.5, 1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024};
10590 gint selected_zoom, default_zoom;
7114e879 10591
19782ffd
RN
10592 VikTrwLayer *vtl = values[MA_VTL];
10593 VikLayersPanel *vlp = values[MA_VLP];
0d2b891f 10594 VikTrack *trk;
19782ffd
RN
10595 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
10596 trk = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
0d2b891f 10597 else
19782ffd 10598 trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
0d2b891f
RN
10599 if ( !trk )
10600 return;
10601
7114e879
QT
10602 VikViewport *vvp = vik_window_viewport((VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl)));
10603
aa7ed888 10604 GList *vmls = vik_layers_panel_get_all_layers_of_type(vlp, VIK_LAYER_MAPS, TRUE); // Includes hidden map layer types
7114e879
QT
10605 int num_maps = g_list_length(vmls);
10606
10607 if (!num_maps) {
4d333042 10608 a_dialog_error_msg(VIK_GTK_WINDOW_FROM_LAYER(vtl), _("No map layer in use. Create one first") );
7114e879
QT
10609 return;
10610 }
10611
fe7e4e45 10612 // Convert from list of vmls to list of names. Allowing the user to select one of them
7114e879
QT
10613 gchar **map_names = g_malloc(1 + num_maps * sizeof(gpointer));
10614 VikMapsLayer **map_layers = g_malloc(1 + num_maps * sizeof(gpointer));
10615
10616 gchar **np = map_names;
10617 VikMapsLayer **lp = map_layers;
fe7e4e45 10618 int i;
7114e879 10619 for (i = 0; i < num_maps; i++) {
7114e879 10620 vml = (VikMapsLayer *)(vmls->data);
fe7e4e45
RN
10621 *lp++ = vml;
10622 *np++ = vik_maps_layer_get_map_label(vml);
7114e879
QT
10623 vmls = vmls->next;
10624 }
fe7e4e45 10625 // Mark end of the array lists
7114e879
QT
10626 *lp = NULL;
10627 *np = NULL;
7114e879
QT
10628
10629 gdouble cur_zoom = vik_viewport_get_zoom(vvp);
fe7e4e45 10630 for (default_zoom = 0; default_zoom < G_N_ELEMENTS(zoom_vals); default_zoom++) {
7114e879
QT
10631 if (cur_zoom == zoom_vals[default_zoom])
10632 break;
10633 }
fe7e4e45 10634 default_zoom = (default_zoom == G_N_ELEMENTS(zoom_vals)) ? G_N_ELEMENTS(zoom_vals) - 1 : default_zoom;
7114e879 10635
fe7e4e45 10636 if (!a_dialog_map_n_zoom(VIK_GTK_WINDOW_FROM_LAYER(vtl), map_names, 0, zoomlist, default_zoom, &selected_map, &selected_zoom))
7114e879
QT
10637 goto done;
10638
0d2b891f 10639 vik_track_download_map(trk, map_layers[selected_map], vvp, zoom_vals[selected_zoom]);
7114e879
QT
10640
10641done:
10642 for (i = 0; i < num_maps; i++)
10643 g_free(map_names[i]);
10644 g_free(map_names);
10645 g_free(map_layers);
10646
10647 g_list_free(vmls);
10648
10649}
0c1044e9 10650
a8fe53f8
EB
10651/**** lowest waypoint number calculation ***/
10652static gint highest_wp_number_name_to_number(const gchar *name) {
10653 if ( strlen(name) == 3 ) {
10654 int n = atoi(name);
10655 if ( n < 100 && name[0] != '0' )
10656 return -1;
10657 if ( n < 10 && name[0] != '0' )
10658 return -1;
10659 return n;
10660 }
10661 return -1;
10662}
10663
10664
10665static void highest_wp_number_reset(VikTrwLayer *vtl)
10666{
10667 vtl->highest_wp_number = -1;
10668}
10669
10670static void highest_wp_number_add_wp(VikTrwLayer *vtl, const gchar *new_wp_name)
10671{
10672 /* if is bigger that top, add it */
10673 gint new_wp_num = highest_wp_number_name_to_number(new_wp_name);
10674 if ( new_wp_num > vtl->highest_wp_number )
10675 vtl->highest_wp_number = new_wp_num;
10676}
10677
10678static void highest_wp_number_remove_wp(VikTrwLayer *vtl, const gchar *old_wp_name)
10679{
10680 /* if wasn't top, do nothing. if was top, count backwards until we find one used */
10681 gint old_wp_num = highest_wp_number_name_to_number(old_wp_name);
10682 if ( vtl->highest_wp_number == old_wp_num ) {
10683 gchar buf[4];
c9570f86 10684 vtl->highest_wp_number--;
a8fe53f8
EB
10685
10686 g_snprintf(buf,4,"%03d", vtl->highest_wp_number );
10687 /* search down until we find something that *does* exist */
10688
c9570f86
RN
10689 while ( vtl->highest_wp_number > 0 && ! vik_trw_layer_get_waypoint ( vtl, buf )) {
10690 vtl->highest_wp_number--;
a8fe53f8
EB
10691 g_snprintf(buf,4,"%03d", vtl->highest_wp_number );
10692 }
10693 }
10694}
10695
10696/* get lowest unused number */
10697static gchar *highest_wp_number_get(VikTrwLayer *vtl)
10698{
10699 gchar buf[4];
10700 if ( vtl->highest_wp_number < 0 || vtl->highest_wp_number >= 999 )
3ce17b61 10701 return NULL;
a8fe53f8
EB
10702 g_snprintf(buf,4,"%03d", vtl->highest_wp_number+1 );
10703 return g_strdup(buf);
10704}
07c9d42b
RN
10705
10706/**
10707 * trw_layer_create_track_list_both:
10708 *
10709 * Create the latest list of tracks and routes
10710 */
10711static GList* trw_layer_create_track_list_both ( VikLayer *vl, gpointer user_data )
10712{
10713 VikTrwLayer *vtl = VIK_TRW_LAYER(vl);
10714 GList *tracks = NULL;
10715 tracks = g_list_concat ( tracks, g_hash_table_get_values ( vik_trw_layer_get_tracks ( vtl ) ) );
10716 tracks = g_list_concat ( tracks, g_hash_table_get_values ( vik_trw_layer_get_routes ( vtl ) ) );
10717
10718 return vik_trw_layer_build_track_list_t ( vtl, tracks );
10719}
10720
19782ffd 10721static void trw_layer_track_list_dialog_single ( menu_array_sublayer values )
07c9d42b 10722{
19782ffd 10723 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
07c9d42b
RN
10724
10725 gchar *title = NULL;
19782ffd 10726 if ( GPOINTER_TO_INT(values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_TRACKS )
07c9d42b
RN
10727 title = g_strdup_printf ( _("%s: Track List"), VIK_LAYER(vtl)->name );
10728 else
10729 title = g_strdup_printf ( _("%s: Route List"), VIK_LAYER(vtl)->name );
10730
19782ffd 10731 vik_trw_layer_track_list_show_dialog ( title, VIK_LAYER(vtl), values[MA_SUBTYPE], trw_layer_create_track_list, FALSE );
07c9d42b
RN
10732 g_free ( title );
10733}
10734
1c2083ba 10735static void trw_layer_track_list_dialog ( menu_array_layer values )
07c9d42b 10736{
1c2083ba 10737 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
07c9d42b
RN
10738
10739 gchar *title = g_strdup_printf ( _("%s: Track and Route List"), VIK_LAYER(vtl)->name );
10740 vik_trw_layer_track_list_show_dialog ( title, VIK_LAYER(vtl), NULL, trw_layer_create_track_list_both, FALSE );
10741 g_free ( title );
10742}
00176e85 10743
1c2083ba 10744static void trw_layer_waypoint_list_dialog ( menu_array_layer values )
00176e85 10745{
1c2083ba 10746 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
00176e85
RN
10747
10748 gchar *title = g_strdup_printf ( _("%s: Waypoint List"), VIK_LAYER(vtl)->name );
10749 vik_trw_layer_waypoint_list_show_dialog ( title, VIK_LAYER(vtl), NULL, trw_layer_create_waypoint_list, FALSE );
10750 g_free ( title );
10751}