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