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