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