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