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