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