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