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