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