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