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