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