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