]> git.street.me.uk Git - andy/viking.git/blame - src/viktrwlayer.c
Fix compiler warnings from Export via GPSBabel work
[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"
e4a11fbe 36#include "viktrwlayer_export.h"
50a14534
EB
37#include "viktrwlayer_tpwin.h"
38#include "viktrwlayer_propwin.h"
43984ba1 39#include "viktrwlayer_analysis.h"
07c9d42b 40#include "viktrwlayer_tracklist.h"
00176e85 41#include "viktrwlayer_waypointlist.h"
b3eb3b98
RN
42#ifdef VIK_CONFIG_GEOTAG
43#include "viktrwlayer_geotag.h"
44#include "geotag_exif.h"
45#endif
acaf7113 46#include "garminsymbols.h"
50a14534
EB
47#include "thumbnails.h"
48#include "background.h"
911400b5 49#include "gpx.h"
e0b0b9c1 50#include "babel.h"
ad0a8c2d
EB
51#include "dem.h"
52#include "dems.h"
165a4fa9 53#include "geonamessearch.h"
3e7553ae
GB
54#ifdef VIK_CONFIG_OPENSTREETMAP
55#include "osm-traces.h"
56#endif
28c82d8b 57#include "acquire.h"
16fc32f6 58#include "datasources.h"
e50758c7 59#include "datasource_gps.h"
82993cc7 60#include "vikexttool_datasources.h"
7d02a0b0 61#include "util.h"
60626a3e 62#include "vikutils.h"
50a14534 63
9f30939a
GB
64#include "vikrouting.h"
65
bce3a7b0
EB
66#include "icons/icons.h"
67
8c00358d 68#ifdef HAVE_MATH_H
50a14534 69#include <math.h>
8c00358d
GB
70#endif
71#ifdef HAVE_STRING_H
50a14534 72#include <string.h>
8c00358d
GB
73#endif
74#ifdef HAVE_STDLIB_H
50a14534 75#include <stdlib.h>
8c00358d 76#endif
8c060406 77#include <stdio.h>
50a14534
EB
78#include <ctype.h>
79
777e2d4d 80#include <gdk/gdkkeysyms.h>
8c060406
MA
81#include <glib.h>
82#include <glib/gstdio.h>
4c77d5e0 83#include <glib/gi18n.h>
777e2d4d 84
074b1067 85#define VIK_TRW_LAYER_TRACK_GC 6
b1453c16
RN
86#define VIK_TRW_LAYER_TRACK_GCS 10
87#define VIK_TRW_LAYER_TRACK_GC_BLACK 0
88#define VIK_TRW_LAYER_TRACK_GC_SLOW 1
89#define VIK_TRW_LAYER_TRACK_GC_AVER 2
90#define VIK_TRW_LAYER_TRACK_GC_FAST 3
91#define VIK_TRW_LAYER_TRACK_GC_STOP 4
074b1067 92#define VIK_TRW_LAYER_TRACK_GC_SINGLE 5
50a14534
EB
93
94#define DRAWMODE_BY_TRACK 0
18df6d42 95#define DRAWMODE_BY_SPEED 1
074b1067 96#define DRAWMODE_ALL_SAME_COLOR 2
18df6d42
RN
97// Note using DRAWMODE_BY_SPEED may be slow especially for vast numbers of trackpoints
98// as we are (re)calculating the colour for every point
50a14534
EB
99
100#define POINTS 1
101#define LINES 2
102
103/* this is how it knows when you click if you are clicking close to a trackpoint. */
104#define TRACKPOINT_SIZE_APPROX 5
105#define WAYPOINT_SIZE_APPROX 5
106
b42a25ba
EB
107#define MIN_STOP_LENGTH 15
108#define MAX_STOP_LENGTH 86400
109#define DRAW_ELEVATION_FACTOR 30 /* height of elevation plotting, sort of relative to zoom level ("mpp" that isn't mpp necessarily) */
110 /* this is multiplied by user-inputted value from 1-100. */
50a14534
EB
111
112enum { WP_SYMBOL_FILLED_SQUARE, WP_SYMBOL_SQUARE, WP_SYMBOL_CIRCLE, WP_SYMBOL_X, WP_NUM_SYMBOLS };
113
5a77ae71
RN
114// See http://developer.gnome.org/pango/stable/PangoMarkupFormat.html
115typedef enum {
116 FS_XX_SMALL = 0, // 'xx-small'
117 FS_X_SMALL,
118 FS_SMALL,
119 FS_MEDIUM, // DEFAULT
120 FS_LARGE,
121 FS_X_LARGE,
122 FS_XX_LARGE,
123 FS_NUM_SIZES
124} font_size_t;
125
50a14534
EB
126struct _VikTrwLayer {
127 VikLayer vl;
128 GHashTable *tracks;
129 GHashTable *tracks_iters;
0d2b891f
RN
130 GHashTable *routes;
131 GHashTable *routes_iters;
50a14534
EB
132 GHashTable *waypoints_iters;
133 GHashTable *waypoints;
0d2b891f
RN
134 GtkTreeIter tracks_iter, routes_iter, waypoints_iter;
135 gboolean tracks_visible, routes_visible, waypoints_visible;
aa0665e5
RN
136 LatLonBBox waypoints_bbox;
137
387ff7ac 138 gboolean track_draw_labels;
50a14534
EB
139 guint8 drawmode;
140 guint8 drawpoints;
3a1c8e42 141 guint8 drawpoints_size;
b42a25ba
EB
142 guint8 drawelevation;
143 guint8 elevation_factor;
144 guint8 drawstops;
145 guint32 stop_length;
50a14534 146 guint8 drawlines;
08084371 147 guint8 drawdirections;
3a1c8e42 148 guint8 drawdirections_size;
50a14534
EB
149 guint8 line_thickness;
150 guint8 bg_line_thickness;
c9cac058 151 vik_layer_sort_order_t track_sort_order;
50a14534 152
6ba42f1e
RN
153 // Metadata
154 VikTRWMetadata *metadata;
155
387ff7ac
RN
156 PangoLayout *tracklabellayout;
157 font_size_t track_font_size;
158 gchar *track_fsize_str;
159
50a14534
EB
160 guint8 wp_symbol;
161 guint8 wp_size;
ea3933fc 162 gboolean wp_draw_symbols;
5a77ae71 163 font_size_t wp_font_size;
63ffa244 164 gchar *wp_fsize_str;
c9cac058 165 vik_layer_sort_order_t wp_sort_order;
50a14534 166
18df6d42 167 gdouble track_draw_speed_factor;
50a14534 168 GArray *track_gc;
b1453c16 169 GdkGC *track_1color_gc;
074b1067 170 GdkColor track_color;
8e9c992d 171 GdkGC *current_track_gc;
745fda83
RN
172 // Separate GC for a track's potential new point as drawn via separate method
173 // (compared to the actual track points drawn in the main trw_layer_draw_track function)
174 GdkGC *current_track_newpoint_gc;
a7023a1b
RN
175 GdkGC *track_bg_gc; GdkColor track_bg_color;
176 GdkGC *waypoint_gc; GdkColor waypoint_color;
177 GdkGC *waypoint_text_gc; GdkColor waypoint_text_color;
178 GdkGC *waypoint_bg_gc; GdkColor waypoint_bg_color;
179 gboolean wpbgand;
0d2b891f
RN
180 GdkFont *waypoint_font;
181 VikTrack *current_track; // ATM shared between new tracks and new routes
50a14534 182 guint16 ct_x1, ct_y1, ct_x2, ct_y2;
745fda83 183 gboolean draw_sync_done;
ef5e8132 184 gboolean draw_sync_do;
50a14534
EB
185
186 VikCoordMode coord_mode;
187
188 /* wp editing tool */
189 VikWaypoint *current_wp;
c9570f86 190 gpointer current_wp_id;
50a14534
EB
191 gboolean moving_wp;
192 gboolean waypoint_rightclick;
193
194 /* track editing tool */
195 GList *current_tpl;
ce4bd1cf
RN
196 VikTrack *current_tp_track;
197 gpointer current_tp_id;
50a14534
EB
198 VikTrwLayerTpwin *tpwin;
199
50a14534
EB
200 /* track editing tool -- more specifically, moving tps */
201 gboolean moving_tp;
202
7ff7d728
RN
203 /* route finder tool */
204 gboolean route_finder_started;
205 VikCoord route_finder_coord;
206 gboolean route_finder_check_added_track;
ce4bd1cf 207 VikTrack *route_finder_added_track;
7ff7d728
RN
208 VikTrack *route_finder_current_track;
209 gboolean route_finder_append;
1eef1bde 210
50a14534
EB
211 gboolean drawlabels;
212 gboolean drawimages;
213 guint8 image_alpha;
214 GQueue *image_cache;
215 guint8 image_size;
216 guint16 image_cache_size;
217
218 /* for waypoint text */
219 PangoLayout *wplabellayout;
220
221 gboolean has_verified_thumbnails;
222
223 GtkMenu *wp_right_click_menu;
8bd81489 224 GtkMenu *track_right_click_menu;
50a14534 225
20c7a3a0
QT
226 /* menu */
227 VikStdLayerMenuItem menu_selection;
228
a8fe53f8 229 gint highest_wp_number;
43984ba1
RN
230
231 // One per layer
232 GtkWidget *tracks_analysis_dialog;
50a14534
EB
233};
234
235/* A caached waypoint image. */
236typedef struct {
237 GdkPixbuf *pixbuf;
238 gchar *image; /* filename */
239} CachedPixbuf;
240
241struct DrawingParams {
242 VikViewport *vp;
243 VikTrwLayer *vtl;
b2ddff02 244 VikWindow *vw;
50a14534
EB
245 gdouble xmpp, ympp;
246 guint16 width, height;
08084371
RN
247 gdouble cc; // Cosine factor in track directions
248 gdouble ss; // Sine factor in track directions
50a14534 249 const VikCoord *center;
50a14534
EB
250 gboolean one_zone, lat_lon;
251 gdouble ce1, ce2, cn1, cn2;
20981fd6 252 LatLonBBox bbox;
50a14534
EB
253};
254
c9570f86
RN
255static gboolean trw_layer_delete_waypoint ( VikTrwLayer *vtl, VikWaypoint *wp );
256
1c2083ba
RN
257typedef enum {
258 MA_VTL = 0,
259 MA_VLP,
19782ffd
RN
260 MA_SUBTYPE, // OR END for Layer only
261 MA_SUBLAYER_ID,
262 MA_CONFIRM,
263 MA_VVP,
264 MA_TV_ITER,
265 MA_MISC,
266 MA_LAST,
1c2083ba
RN
267} menu_array_index;
268
19782ffd
RN
269typedef gpointer menu_array_layer[2];
270typedef gpointer menu_array_sublayer[MA_LAST];
1c2083ba 271
19782ffd
RN
272static void trw_layer_delete_item ( menu_array_sublayer values );
273static void trw_layer_copy_item_cb ( menu_array_sublayer values );
274static void trw_layer_cut_item_cb ( menu_array_sublayer values );
33534cd8 275
9e212bfc 276static void trw_layer_find_maxmin_tracks ( const gpointer id, const VikTrack *trk, struct LatLon maxmin[2] );
165a4fa9 277static void trw_layer_find_maxmin (VikTrwLayer *vtl, struct LatLon maxmin[2]);
50a14534
EB
278
279static void trw_layer_new_track_gcs ( VikTrwLayer *vtl, VikViewport *vp );
280static void trw_layer_free_track_gcs ( VikTrwLayer *vtl );
281
9e212bfc
RN
282static void trw_layer_draw_track_cb ( const gpointer id, VikTrack *track, struct DrawingParams *dp );
283static void trw_layer_draw_waypoint ( const gpointer id, VikWaypoint *wp, struct DrawingParams *dp );
50a14534 284
6bb72350 285static void goto_coord ( gpointer *vlp, gpointer vvp, gpointer vl, const VikCoord *coord );
19782ffd
RN
286static void trw_layer_goto_track_startpoint ( menu_array_sublayer values );
287static void trw_layer_goto_track_endpoint ( menu_array_sublayer values );
288static void trw_layer_goto_track_max_speed ( menu_array_sublayer values );
289static void trw_layer_goto_track_max_alt ( menu_array_sublayer values );
290static void trw_layer_goto_track_min_alt ( menu_array_sublayer values );
291static void trw_layer_goto_track_center ( menu_array_sublayer values );
292static void trw_layer_merge_by_segment ( menu_array_sublayer values );
293static void trw_layer_merge_by_timestamp ( menu_array_sublayer values );
294static void trw_layer_merge_with_other ( menu_array_sublayer values );
295static void trw_layer_append_track ( menu_array_sublayer values );
296static void trw_layer_split_by_timestamp ( menu_array_sublayer values );
297static void trw_layer_split_by_n_points ( menu_array_sublayer values );
298static void trw_layer_split_at_trackpoint ( menu_array_sublayer values );
299static void trw_layer_split_segments ( menu_array_sublayer values );
300static void trw_layer_delete_point_selected ( menu_array_sublayer values );
301static void trw_layer_delete_points_same_position ( menu_array_sublayer values );
302static void trw_layer_delete_points_same_time ( menu_array_sublayer values );
303static void trw_layer_reverse ( menu_array_sublayer values );
304static void trw_layer_download_map_along_track_cb ( menu_array_sublayer values );
305static void trw_layer_edit_trackpoint ( menu_array_sublayer values );
306static void trw_layer_show_picture ( menu_array_sublayer values );
307static void trw_layer_gps_upload_any ( menu_array_sublayer values );
c95d6b00 308
1c2083ba
RN
309static void trw_layer_centerize ( menu_array_layer values );
310static void trw_layer_auto_view ( menu_array_layer values );
1c2083ba
RN
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
e4a11fbe 3248static void trw_layer_export_gpspoint ( menu_array_layer values )
50a14534 3249{
e4a11fbe 3250 gchar *auto_save_name = append_file_ext ( vik_layer_get_name(VIK_LAYER(values[MA_VTL])), FILE_TYPE_GPSPOINT );
6e4a49aa 3251
e4a11fbe 3252 vik_trw_layer_export ( VIK_TRW_LAYER (values[MA_VTL]), _("Export Layer"), auto_save_name, NULL, FILE_TYPE_GPSPOINT );
50a14534 3253
e4a11fbe 3254 g_free ( auto_save_name );
50a14534
EB
3255}
3256
1c2083ba 3257static void trw_layer_export_gpsmapper ( menu_array_layer values )
50a14534 3258{
e4a11fbe
GB
3259 gchar *auto_save_name = append_file_ext ( vik_layer_get_name(VIK_LAYER(values[MA_VTL])), FILE_TYPE_GPSMAPPER );
3260
3261 vik_trw_layer_export ( VIK_TRW_LAYER (values[MA_VTL]), _("Export Layer"), auto_save_name, NULL, FILE_TYPE_GPSMAPPER );
3262
3263 g_free ( auto_save_name );
50a14534
EB
3264}
3265
1c2083ba 3266static void trw_layer_export_gpx ( menu_array_layer values )
561e6ad0 3267{
e4a11fbe 3268 gchar *auto_save_name = append_file_ext ( vik_layer_get_name(VIK_LAYER(values[MA_VTL])), FILE_TYPE_GPX );
18d0a1ed 3269
e4a11fbe 3270 vik_trw_layer_export ( VIK_TRW_LAYER (values[MA_VTL]), _("Export Layer"), auto_save_name, NULL, FILE_TYPE_GPX );
18d0a1ed
RN
3271
3272 g_free ( auto_save_name );
7f6757c4
RN
3273}
3274
1c2083ba 3275static void trw_layer_export_kml ( menu_array_layer values )
ba9d0a00 3276{
e4a11fbe 3277 gchar *auto_save_name = append_file_ext ( vik_layer_get_name(VIK_LAYER(values[MA_VTL])), FILE_TYPE_KML );
ba9d0a00 3278
e4a11fbe 3279 vik_trw_layer_export ( VIK_TRW_LAYER (values[MA_VTL]), _("Export Layer"), auto_save_name, NULL, FILE_TYPE_KML );
ba9d0a00
RN
3280
3281 g_free ( auto_save_name );
3282}
3283
17720e63
GB
3284static void trw_layer_export_babel ( gpointer layer_and_vlp[2] )
3285{
c3c4b41c 3286 const gchar *auto_save_name = vik_layer_get_name(VIK_LAYER(layer_and_vlp[0]));
17720e63
GB
3287 vik_trw_layer_export_gpsbabel ( VIK_TRW_LAYER (layer_and_vlp[0]), _("Export Layer"), auto_save_name );
3288}
ccccf356 3289
1c2083ba 3290static void trw_layer_export_external_gpx_1 ( menu_array_layer values )
ccccf356 3291{
e4a11fbe 3292 vik_trw_layer_export_external_gpx ( VIK_TRW_LAYER (values[MA_VTL]), a_vik_get_external_gpx_program_1() );
ccccf356
RN
3293}
3294
1c2083ba 3295static void trw_layer_export_external_gpx_2 ( menu_array_layer values )
ff02058b 3296{
e4a11fbe 3297 vik_trw_layer_export_external_gpx ( VIK_TRW_LAYER (values[MA_VTL]), a_vik_get_external_gpx_program_2() );
ff02058b
RN
3298}
3299
19782ffd 3300static void trw_layer_export_gpx_track ( menu_array_sublayer values )
7f6757c4 3301{
19782ffd 3302 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
0d2b891f 3303 VikTrack *trk;
19782ffd
RN
3304 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
3305 trk = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
0d2b891f 3306 else
19782ffd 3307 trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
ce4bd1cf
RN
3308
3309 if ( !trk || !trk->name )
3310 return;
7f6757c4 3311
e4a11fbe 3312 gchar *auto_save_name = append_file_ext ( trk->name, FILE_TYPE_GPX );
7f6757c4 3313
0f704e14 3314 gchar *label = NULL;
19782ffd 3315 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
0f704e14
GB
3316 label = _("Export Route as GPX");
3317 else
3318 label = _("Export Track as GPX");
e4a11fbe 3319 vik_trw_layer_export ( VIK_TRW_LAYER (values[MA_VTL]), label, auto_save_name, trk, FILE_TYPE_GPX );
7f6757c4
RN
3320
3321 g_free ( auto_save_name );
561e6ad0
EB
3322}
3323
d18f2933 3324gboolean trw_layer_waypoint_find_uuid ( const gpointer id, const VikWaypoint *wp, gpointer udata )
c9570f86
RN
3325{
3326 wpu_udata *user_data = udata;
3327 if ( wp == user_data->wp ) {
3328 user_data->uuid = id;
3329 return TRUE;
3330 }
3331 return FALSE;
3332}
3333
1c2083ba 3334static void trw_layer_goto_wp ( menu_array_layer values )
50a14534 3335{
1c2083ba
RN
3336 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
3337 VikLayersPanel *vlp = VIK_LAYERS_PANEL(values[MA_VLP]);
a77d62d8 3338 GtkWidget *dia = gtk_dialog_new_with_buttons (_("Find"),
1c2083ba 3339 VIK_GTK_WINDOW_FROM_LAYER(vtl),
50a14534
EB
3340 GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
3341 GTK_STOCK_CANCEL,
3342 GTK_RESPONSE_REJECT,
3343 GTK_STOCK_OK,
3344 GTK_RESPONSE_ACCEPT,
3345 NULL);
3346
3347 GtkWidget *label, *entry;
4c77d5e0 3348 label = gtk_label_new(_("Waypoint Name:"));
50a14534
EB
3349 entry = gtk_entry_new();
3350
9b082b39
RN
3351 gtk_box_pack_start(GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dia))), label, FALSE, FALSE, 0);
3352 gtk_box_pack_start(GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dia))), entry, FALSE, FALSE, 0);
50a14534
EB
3353 gtk_widget_show_all ( label );
3354 gtk_widget_show_all ( entry );
3355
7ea3cb11
RN
3356 gtk_dialog_set_default_response ( GTK_DIALOG(dia), GTK_RESPONSE_ACCEPT );
3357
50a14534
EB
3358 while ( gtk_dialog_run ( GTK_DIALOG(dia) ) == GTK_RESPONSE_ACCEPT )
3359 {
c9570f86
RN
3360 gchar *name = g_strdup(gtk_entry_get_text(GTK_ENTRY(entry)));
3361 // Find *first* wp with the given name
1c2083ba 3362 VikWaypoint *wp = vik_trw_layer_get_waypoint ( vtl, name );
50a14534 3363
c9570f86 3364 if ( !wp )
1c2083ba 3365 a_dialog_error_msg ( VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Waypoint not found in this layer.") );
50a14534
EB
3366 else
3367 {
1c2083ba
RN
3368 vik_viewport_set_center_coord ( vik_layers_panel_get_viewport(vlp), &(wp->coord) );
3369 vik_layers_panel_emit_update ( vlp );
c9570f86
RN
3370
3371 // Find and select on the side panel
3372 wpu_udata udata;
3373 udata.wp = wp;
3374 udata.uuid = NULL;
3375
3376 // Hmmm, want key of it
1c2083ba 3377 gpointer *wpf = g_hash_table_find ( vtl->waypoints, (GHRFunc) trw_layer_waypoint_find_uuid, (gpointer) &udata );
c9570f86
RN
3378
3379 if ( wpf && udata.uuid ) {
1c2083ba
RN
3380 GtkTreeIter *it = g_hash_table_lookup ( vtl->waypoints_iters, udata.uuid );
3381 vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, it, TRUE );
c9570f86
RN
3382 }
3383
50a14534
EB
3384 break;
3385 }
3386
c9570f86 3387 g_free ( name );
50a14534
EB
3388
3389 }
3390 gtk_widget_destroy ( dia );
3391}
3392
3393gboolean vik_trw_layer_new_waypoint ( VikTrwLayer *vtl, GtkWindow *w, const VikCoord *def_coord )
3394{
ac1bde8b 3395 gchar *default_name = highest_wp_number_get(vtl);
acaf7113 3396 VikWaypoint *wp = vik_waypoint_new();
ac1bde8b
RN
3397 gchar *returned_name;
3398 gboolean updated;
acaf7113 3399 wp->coord = *def_coord;
d60a672e
RN
3400
3401 // Attempt to auto set height if DEM data is available
a90338e5 3402 vik_waypoint_apply_dem_data ( wp, TRUE );
50a14534 3403
d6175f49 3404 returned_name = a_dialog_waypoint ( w, default_name, vtl, wp, vtl->coord_mode, TRUE, &updated );
c9570f86
RN
3405
3406 if ( returned_name )
50a14534 3407 {
805d282e 3408 wp->visible = TRUE;
ac1bde8b
RN
3409 vik_trw_layer_add_waypoint ( vtl, returned_name, wp );
3410 g_free (default_name);
c9570f86 3411 g_free (returned_name);
50a14534
EB
3412 return TRUE;
3413 }
ac1bde8b 3414 g_free (default_name);
acaf7113 3415 vik_waypoint_free(wp);
50a14534
EB
3416 return FALSE;
3417}
3418
1c2083ba 3419static void trw_layer_new_wikipedia_wp_viewport ( menu_array_layer values )
165a4fa9 3420{
165a4fa9 3421 struct LatLon maxmin[2] = { {0.0,0.0}, {0.0,0.0} };
1c2083ba
RN
3422 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
3423 VikLayersPanel *vlp = VIK_LAYERS_PANEL(values[MA_VLP]);
165a4fa9
HR
3424 VikWindow *vw = (VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl));
3425 VikViewport *vvp = vik_window_viewport(vw);
42d6236f
RN
3426
3427 // Note the order is max part first then min part - thus reverse order of use in min_max function:
3428 vik_viewport_get_min_max_lat_lon ( vvp, &maxmin[1].lat, &maxmin[0].lat, &maxmin[1].lon, &maxmin[0].lon );
120ab662 3429 a_geonames_wikipedia_box((VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl)), vtl, maxmin);
aa0665e5 3430 trw_layer_calculate_bounds_waypoints ( vtl );
120ab662 3431 vik_layers_panel_emit_update ( vlp );
165a4fa9
HR
3432}
3433
1c2083ba 3434static void trw_layer_new_wikipedia_wp_layer ( menu_array_layer values )
165a4fa9 3435{
1c2083ba
RN
3436 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
3437 VikLayersPanel *vlp = VIK_LAYERS_PANEL(values[MA_VLP]);
165a4fa9
HR
3438 struct LatLon maxmin[2] = { {0.0,0.0}, {0.0,0.0} };
3439
3440 trw_layer_find_maxmin (vtl, maxmin);
120ab662 3441 a_geonames_wikipedia_box((VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl)), vtl, maxmin);
aa0665e5 3442 trw_layer_calculate_bounds_waypoints ( vtl );
120ab662 3443 vik_layers_panel_emit_update ( vlp );
165a4fa9
HR
3444}
3445
b3eb3b98 3446#ifdef VIK_CONFIG_GEOTAG
19782ffd 3447static void trw_layer_geotagging_waypoint_mtime_keep ( menu_array_sublayer values )
b3eb3b98 3448{
19782ffd 3449 VikWaypoint *wp = g_hash_table_lookup ( VIK_TRW_LAYER(values[MA_VTL])->waypoints, values[MA_SUBLAYER_ID] );
b3eb3b98
RN
3450 if ( wp )
3451 // Update directly - not changing the mtime
3452 a_geotag_write_exif_gps ( wp->image, wp->coord, wp->altitude, TRUE );
3453}
3454
19782ffd 3455static void trw_layer_geotagging_waypoint_mtime_update ( menu_array_sublayer values )
b3eb3b98 3456{
19782ffd 3457 VikWaypoint *wp = g_hash_table_lookup ( VIK_TRW_LAYER(values[MA_VTL])->waypoints, values[MA_SUBLAYER_ID] );
b3eb3b98
RN
3458 if ( wp )
3459 // Update directly
3460 a_geotag_write_exif_gps ( wp->image, wp->coord, wp->altitude, FALSE );
3461}
3462
3463/*
3464 * Use code in separate file for this feature as reasonably complex
3465 */
19782ffd 3466static void trw_layer_geotagging_track ( menu_array_sublayer values )
b3eb3b98 3467{
19782ffd
RN
3468 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
3469 VikTrack *track = g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
b3eb3b98
RN
3470 // Unset so can be reverified later if necessary
3471 vtl->has_verified_thumbnails = FALSE;
3472
3473 trw_layer_geotag_dialog ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
41f4abac
RN
3474 vtl,
3475 NULL,
3476 track );
3477}
3478
19782ffd 3479static void trw_layer_geotagging_waypoint ( menu_array_sublayer values )
41f4abac 3480{
19782ffd
RN
3481 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
3482 VikWaypoint *wpt = g_hash_table_lookup ( vtl->waypoints, values[MA_SUBLAYER_ID] );
41f4abac
RN
3483
3484 trw_layer_geotag_dialog ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
3485 vtl,
3486 wpt,
3487 NULL );
b3eb3b98
RN
3488}
3489
1c2083ba 3490static void trw_layer_geotagging ( menu_array_layer values )
b3eb3b98 3491{
1c2083ba 3492 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
b3eb3b98
RN
3493 // Unset so can be reverified later if necessary
3494 vtl->has_verified_thumbnails = FALSE;
3495
3496 trw_layer_geotag_dialog ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
41f4abac
RN
3497 vtl,
3498 NULL,
3499 NULL );
b3eb3b98
RN
3500}
3501#endif
3502
16fc32f6
RN
3503// 'Acquires' - Same as in File Menu -> Acquire - applies into the selected TRW Layer //
3504
1c2083ba 3505static void trw_layer_acquire ( menu_array_layer values, VikDataSourceInterface *datasource )
16fc32f6 3506{
1c2083ba
RN
3507 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
3508 VikLayersPanel *vlp = VIK_LAYERS_PANEL(values[MA_VLP]);
16fc32f6
RN
3509 VikWindow *vw = (VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl));
3510 VikViewport *vvp = vik_window_viewport(vw);
3511
995e9fd9
GB
3512 a_acquire ( vw, vlp, vvp, datasource, NULL, NULL );
3513}
3514
3515/*
3516 * Acquire into this TRW Layer straight from GPS Device
3517 */
1c2083ba 3518static void trw_layer_acquire_gps_cb ( menu_array_layer values )
995e9fd9 3519{
16fc32f6 3520 vik_datasource_gps_interface.mode = VIK_DATASOURCE_ADDTOLAYER;
1c2083ba 3521 trw_layer_acquire ( values, &vik_datasource_gps_interface );
16fc32f6
RN
3522}
3523
3524/*
7f95fd54 3525 * Acquire into this TRW Layer from Directions
16fc32f6 3526 */
1c2083ba 3527static void trw_layer_acquire_routing_cb ( menu_array_layer values )
16fc32f6 3528{
1c2083ba 3529 trw_layer_acquire ( values, &vik_datasource_routing_interface );
16fc32f6
RN
3530}
3531
c6acf18d
RN
3532/*
3533 * Acquire into this TRW Layer from an entered URL
3534 */
1c2083ba 3535static void trw_layer_acquire_url_cb ( menu_array_layer values )
c6acf18d 3536{
c6acf18d 3537 vik_datasource_url_interface.mode = VIK_DATASOURCE_ADDTOLAYER;
1c2083ba 3538 trw_layer_acquire ( values, &vik_datasource_url_interface );
c6acf18d
RN
3539}
3540
40f5740b
RN
3541#ifdef VIK_CONFIG_OPENSTREETMAP
3542/*
3543 * Acquire into this TRW Layer from OSM
3544 */
1c2083ba 3545static void trw_layer_acquire_osm_cb ( menu_array_layer values )
40f5740b 3546{
1c2083ba 3547 trw_layer_acquire ( values, &vik_datasource_osm_interface );
40f5740b 3548}
3cc57413
RN
3549
3550/**
3551 * Acquire into this TRW Layer from OSM for 'My' Traces
3552 */
1c2083ba 3553static void trw_layer_acquire_osm_my_traces_cb ( menu_array_layer values )
3cc57413 3554{
1c2083ba 3555 trw_layer_acquire ( values, &vik_datasource_osm_my_traces_interface );
3cc57413 3556}
40f5740b
RN
3557#endif
3558
16fc32f6
RN
3559#ifdef VIK_CONFIG_GEOCACHES
3560/*
3561 * Acquire into this TRW Layer from Geocaching.com
3562 */
1c2083ba 3563static void trw_layer_acquire_geocache_cb ( menu_array_layer values )
16fc32f6 3564{
1c2083ba 3565 trw_layer_acquire ( values, &vik_datasource_gc_interface );
16fc32f6
RN
3566}
3567#endif
3568
68bab1bd
RN
3569#ifdef VIK_CONFIG_GEOTAG
3570/*
3571 * Acquire into this TRW Layer from images
3572 */
1c2083ba 3573static void trw_layer_acquire_geotagged_cb ( menu_array_layer values )
68bab1bd 3574{
1c2083ba 3575 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
68bab1bd
RN
3576
3577 vik_datasource_geotag_interface.mode = VIK_DATASOURCE_ADDTOLAYER;
1c2083ba 3578 trw_layer_acquire ( values, &vik_datasource_geotag_interface );
68bab1bd
RN
3579
3580 // Reverify thumbnails as they may have changed
3581 vtl->has_verified_thumbnails = FALSE;
3582 trw_layer_verify_thumbnails ( vtl, NULL );
3583}
3584#endif
3585
1c2083ba 3586static void trw_layer_gps_upload ( menu_array_layer values )
e50758c7 3587{
19782ffd
RN
3588 menu_array_sublayer data;
3589 gint ii;
3590 for ( ii = MA_VTL; ii < MA_LAST; ii++ )
3591 data[ii] = NULL;
3592 data[MA_VTL] = values[MA_VTL];
3593 data[MA_VLP] = values[MA_VLP];
e50758c7 3594
19782ffd 3595 trw_layer_gps_upload_any ( data );
e50758c7
RN
3596}
3597
3598/**
3599 * If pass_along[3] is defined that this will upload just that track
3600 */
19782ffd 3601static void trw_layer_gps_upload_any ( menu_array_sublayer values )
e50758c7 3602{
19782ffd
RN
3603 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
3604 VikLayersPanel *vlp = VIK_LAYERS_PANEL(values[MA_VLP]);
e50758c7 3605
19782ffd 3606 // May not actually get a track here as values[2&3] can be null
0d2b891f
RN
3607 VikTrack *track = NULL;
3608 vik_gps_xfer_type xfer_type = TRK; // VIK_TRW_LAYER_SUBLAYER_TRACKS = 0 so hard to test different from NULL!
3609 gboolean xfer_all = FALSE;
e50758c7 3610
19782ffd 3611 if ( values[MA_SUBTYPE] ) {
0d2b891f 3612 xfer_all = FALSE;
19782ffd
RN
3613 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE ) {
3614 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
0d2b891f
RN
3615 xfer_type = RTE;
3616 }
19782ffd
RN
3617 else if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_TRACK ) {
3618 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
0d2b891f
RN
3619 xfer_type = TRK;
3620 }
19782ffd 3621 else if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_WAYPOINTS ) {
0d2b891f
RN
3622 xfer_type = WPT;
3623 }
19782ffd 3624 else if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTES ) {
0d2b891f
RN
3625 xfer_type = RTE;
3626 }
3627 }
19782ffd 3628 else if ( !values[MA_CONFIRM] )
0d2b891f 3629 xfer_all = TRUE; // i.e. whole layer
e50758c7 3630
0d2b891f 3631 if (track && !track->visible) {
e50758c7
RN
3632 a_dialog_error_msg ( VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Can not upload invisible track.") );
3633 return;
3634 }
3635
3636 GtkWidget *dialog = gtk_dialog_new_with_buttons ( _("GPS Upload"),
19782ffd 3637 VIK_GTK_WINDOW_FROM_LAYER(vtl),
e50758c7
RN
3638 GTK_DIALOG_DESTROY_WITH_PARENT,
3639 GTK_STOCK_OK,
3640 GTK_RESPONSE_ACCEPT,
3641 GTK_STOCK_CANCEL,
3642 GTK_RESPONSE_REJECT,
3643 NULL );
3644
3645 gtk_dialog_set_default_response ( GTK_DIALOG(dialog), GTK_RESPONSE_ACCEPT );
3646 GtkWidget *response_w = NULL;
3647#if GTK_CHECK_VERSION (2, 20, 0)
3648 response_w = gtk_dialog_get_widget_for_response ( GTK_DIALOG(dialog), GTK_RESPONSE_ACCEPT );
3649#endif
3650
3651 if ( response_w )
3652 gtk_widget_grab_focus ( response_w );
3653
0d2b891f 3654 gpointer dgs = datasource_gps_setup ( dialog, xfer_type, xfer_all );
e50758c7
RN
3655
3656 if ( gtk_dialog_run ( GTK_DIALOG(dialog) ) != GTK_RESPONSE_ACCEPT ) {
3657 datasource_gps_clean_up ( dgs );
3658 gtk_widget_destroy ( dialog );
3659 return;
3660 }
3661
3662 // Get info from reused datasource dialog widgets
3663 gchar* protocol = datasource_gps_get_protocol ( dgs );
3664 gchar* port = datasource_gps_get_descriptor ( dgs );
3665 // NB don't free the above strings as they're references to values held elsewhere
3666 gboolean do_tracks = datasource_gps_get_do_tracks ( dgs );
0d2b891f 3667 gboolean do_routes = datasource_gps_get_do_routes ( dgs );
e50758c7
RN
3668 gboolean do_waypoints = datasource_gps_get_do_waypoints ( dgs );
3669 gboolean turn_off = datasource_gps_get_off ( dgs );
3670
3671 gtk_widget_destroy ( dialog );
3672
3673 // When called from the viewport - work the corresponding layerspanel:
3674 if ( !vlp ) {
3675 vlp = vik_window_layers_panel ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)) );
3676 }
3677
3678 // Apply settings to transfer to the GPS device
3679 vik_gps_comm ( vtl,
3680 track,
3681 GPS_UP,
3682 protocol,
3683 port,
3684 FALSE,
3685 vik_layers_panel_get_viewport (vlp),
3686 vlp,
3687 do_tracks,
0d2b891f 3688 do_routes,
e50758c7
RN
3689 do_waypoints,
3690 turn_off );
3691}
3692
d7ac7564
RN
3693/*
3694 * Acquire into this TRW Layer from any GPS Babel supported file
3695 */
1c2083ba 3696static void trw_layer_acquire_file_cb ( menu_array_layer values )
d7ac7564 3697{
1c2083ba
RN
3698 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
3699 VikLayersPanel *vlp = VIK_LAYERS_PANEL(values[MA_VLP]);
d7ac7564
RN
3700 VikWindow *vw = (VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl));
3701 VikViewport *vvp = vik_window_viewport(vw);
3702
2801a19b 3703 a_acquire ( vw, vlp, vvp, &vik_datasource_file_interface, NULL, NULL );
d7ac7564
RN
3704}
3705
1c2083ba 3706static void trw_layer_new_wp ( menu_array_layer values )
50a14534 3707{
1c2083ba
RN
3708 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
3709 VikLayersPanel *vlp = VIK_LAYERS_PANEL(values[MA_VLP]);
50a14534
EB
3710 /* TODO longone: okay, if layer above (aggregate) is invisible but vtl->visible is true, this redraws for no reason.
3711 instead return true if you want to update. */
aa0665e5
RN
3712 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 ) {
3713 trw_layer_calculate_bounds_waypoints ( vtl );
50a14534 3714 vik_layers_panel_emit_update ( vlp );
aa0665e5 3715 }
50a14534
EB
3716}
3717
2f154c90
RN
3718static void new_track_create_common ( VikTrwLayer *vtl, gchar *name )
3719{
3720 vtl->current_track = vik_track_new();
387ff7ac 3721 vik_track_set_defaults ( vtl->current_track );
2f154c90
RN
3722 vtl->current_track->visible = TRUE;
3723 if ( vtl->drawmode == DRAWMODE_ALL_SAME_COLOR )
3724 // Create track with the preferred colour from the layer properties
3725 vtl->current_track->color = vtl->track_color;
3726 else
3727 gdk_color_parse ( "#000000", &(vtl->current_track->color) );
3728 vtl->current_track->has_color = TRUE;
3729 vik_trw_layer_add_track ( vtl, name, vtl->current_track );
3730}
3731
1c2083ba 3732static void trw_layer_new_track ( menu_array_layer values )
37615c52 3733{
1c2083ba 3734 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
37615c52
RN
3735
3736 if ( ! vtl->current_track ) {
3737 gchar *name = trw_layer_new_unique_sublayer_name ( vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, _("Track")) ;
2f154c90 3738 new_track_create_common ( vtl, name );
1613e468 3739 g_free ( name );
37615c52
RN
3740
3741 vik_window_enable_layer_tool ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)), VIK_LAYER_TRW, TOOL_CREATE_TRACK );
3742 }
3743}
3744
b1453c16
RN
3745static void new_route_create_common ( VikTrwLayer *vtl, gchar *name )
3746{
3747 vtl->current_track = vik_track_new();
387ff7ac 3748 vik_track_set_defaults ( vtl->current_track );
b1453c16
RN
3749 vtl->current_track->visible = TRUE;
3750 vtl->current_track->is_route = TRUE;
3751 // By default make all routes red
3752 vtl->current_track->has_color = TRUE;
3753 gdk_color_parse ( "red", &vtl->current_track->color );
3754 vik_trw_layer_add_route ( vtl, name, vtl->current_track );
3755}
3756
1c2083ba 3757static void trw_layer_new_route ( menu_array_layer values )
0d2b891f 3758{
1c2083ba 3759 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
0d2b891f
RN
3760
3761 if ( ! vtl->current_track ) {
3762 gchar *name = trw_layer_new_unique_sublayer_name ( vtl, VIK_TRW_LAYER_SUBLAYER_ROUTE, _("Route")) ;
b1453c16 3763 new_route_create_common ( vtl, name );
1613e468 3764 g_free ( name );
e37b2a6d 3765 vik_window_enable_layer_tool ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)), VIK_LAYER_TRW, TOOL_CREATE_ROUTE );
0d2b891f
RN
3766 }
3767}
3768
1c2083ba 3769static void trw_layer_auto_routes_view ( menu_array_layer values )
0d2b891f 3770{
1c2083ba
RN
3771 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
3772 VikLayersPanel *vlp = VIK_LAYERS_PANEL(values[MA_VLP]);
0d2b891f
RN
3773
3774 if ( g_hash_table_size (vtl->routes) > 0 ) {
3775 struct LatLon maxmin[2] = { {0,0}, {0,0} };
3776 g_hash_table_foreach ( vtl->routes, (GHFunc) trw_layer_find_maxmin_tracks, maxmin );
3777 trw_layer_zoom_to_show_latlons ( vtl, vik_layers_panel_get_viewport (vlp), maxmin );
3778 vik_layers_panel_emit_update ( vlp );
3779 }
3780}
3781
3782
1c2083ba 3783static void trw_layer_finish_track ( menu_array_layer values )
37615c52 3784{
1c2083ba 3785 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
37615c52 3786 vtl->current_track = NULL;
da121f9b 3787 vik_layer_emit_update ( VIK_LAYER(vtl) );
37615c52
RN
3788}
3789
1c2083ba 3790static void trw_layer_auto_tracks_view ( menu_array_layer values )
535ed1ae 3791{
1c2083ba
RN
3792 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
3793 VikLayersPanel *vlp = VIK_LAYERS_PANEL(values[MA_VLP]);
535ed1ae
RN
3794
3795 if ( g_hash_table_size (vtl->tracks) > 0 ) {
3796 struct LatLon maxmin[2] = { {0,0}, {0,0} };
3797 g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_find_maxmin_tracks, maxmin );
3798 trw_layer_zoom_to_show_latlons ( vtl, vik_layers_panel_get_viewport (vlp), maxmin );
3799 vik_layers_panel_emit_update ( vlp );
3800 }
3801}
3802
9e212bfc 3803static void trw_layer_single_waypoint_jump ( const gpointer id, const VikWaypoint *wp, gpointer vvp )
fc59e8c7
RN
3804{
3805 /* NB do not care if wp is visible or not */
3806 vik_viewport_set_center_coord ( VIK_VIEWPORT(vvp), &(wp->coord) );
3807}
3808
1c2083ba 3809static void trw_layer_auto_waypoints_view ( menu_array_layer values )
fc59e8c7 3810{
1c2083ba
RN
3811 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
3812 VikLayersPanel *vlp = VIK_LAYERS_PANEL(values[MA_VLP]);
fc59e8c7
RN
3813
3814 /* Only 1 waypoint - jump straight to it */
3815 if ( g_hash_table_size (vtl->waypoints) == 1 ) {
3816 VikViewport *vvp = vik_layers_panel_get_viewport (vlp);
3817 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_single_waypoint_jump, (gpointer) vvp );
3818 }
3819 /* If at least 2 waypoints - find center and then zoom to fit */
3820 else if ( g_hash_table_size (vtl->waypoints) > 1 )
3821 {
3822 struct LatLon maxmin[2] = { {0,0}, {0,0} };
a8e8ffe1
RN
3823 maxmin[0].lat = vtl->waypoints_bbox.north;
3824 maxmin[1].lat = vtl->waypoints_bbox.south;
3825 maxmin[0].lon = vtl->waypoints_bbox.east;
3826 maxmin[1].lon = vtl->waypoints_bbox.west;
fc59e8c7
RN
3827 trw_layer_zoom_to_show_latlons ( vtl, vik_layers_panel_get_viewport (vlp), maxmin );
3828 }
3829
3830 vik_layers_panel_emit_update ( vlp );
3831}
3832
1c2083ba 3833void trw_layer_osm_traces_upload_cb ( menu_array_layer values )
3185b5d3 3834{
1c2083ba 3835 osm_traces_upload_viktrwlayer(VIK_TRW_LAYER(values[MA_VTL]), NULL);
3185b5d3
RN
3836}
3837
d6c110f1 3838void trw_layer_osm_traces_upload_track_cb ( menu_array_sublayer values )
3185b5d3 3839{
d6c110f1
RN
3840 if ( values[MA_MISC] ) {
3841 VikTrack *trk = VIK_TRACK(values[MA_MISC]);
3842 osm_traces_upload_viktrwlayer(VIK_TRW_LAYER(values[MA_VTL]), trk);
3185b5d3
RN
3843 }
3844}
3845
a7cd93ac 3846static void trw_layer_add_menu_items ( VikTrwLayer *vtl, GtkMenu *menu, gpointer vlp )
50a14534 3847{
1c2083ba 3848 static menu_array_layer pass_along;
50a14534 3849 GtkWidget *item;
98fcbbdb 3850 GtkWidget *export_submenu;
1c2083ba
RN
3851 pass_along[MA_VTL] = vtl;
3852 pass_along[MA_VLP] = vlp;
50a14534
EB
3853
3854 item = gtk_menu_item_new();
3855 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
3856 gtk_widget_show ( item );
3857
37615c52 3858 if ( vtl->current_track ) {
0d2b891f
RN
3859 if ( vtl->current_track->is_route )
3860 item = gtk_menu_item_new_with_mnemonic ( _("_Finish Route") );
3861 else
3862 item = gtk_menu_item_new_with_mnemonic ( _("_Finish Track") );
37615c52
RN
3863 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_finish_track), pass_along );
3864 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3865 gtk_widget_show ( item );
3866
3867 // Add separator
3868 item = gtk_menu_item_new ();
3869 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
3870 gtk_widget_show ( item );
3871 }
3872
d6de71f9
RN
3873 /* Now with icons */
3874 item = gtk_image_menu_item_new_with_mnemonic ( _("_View Layer") );
3875 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ZOOM_FIT, GTK_ICON_SIZE_MENU) );
5a10c240
RN
3876 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_view), pass_along );
3877 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3878 gtk_widget_show ( item );
3879
48d28f21
RN
3880 GtkWidget *view_submenu = gtk_menu_new();
3881 item = gtk_image_menu_item_new_with_mnemonic ( _("V_iew") );
3882 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_FIND, GTK_ICON_SIZE_MENU) );
535ed1ae
RN
3883 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3884 gtk_widget_show ( item );
48d28f21 3885 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), view_submenu );
535ed1ae 3886
48d28f21
RN
3887 item = gtk_menu_item_new_with_mnemonic ( _("View All _Tracks") );
3888 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_tracks_view), pass_along );
3889 gtk_menu_shell_append (GTK_MENU_SHELL (view_submenu), item);
3890 gtk_widget_show ( item );
3891
0d2b891f
RN
3892 item = gtk_menu_item_new_with_mnemonic ( _("View All _Routes") );
3893 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_routes_view), pass_along );
3894 gtk_menu_shell_append (GTK_MENU_SHELL (view_submenu), item);
3895 gtk_widget_show ( item );
3896
48d28f21 3897 item = gtk_menu_item_new_with_mnemonic ( _("View All _Waypoints") );
fc59e8c7 3898 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_waypoints_view), pass_along );
48d28f21 3899 gtk_menu_shell_append (GTK_MENU_SHELL (view_submenu), item);
fc59e8c7
RN
3900 gtk_widget_show ( item );
3901
d6de71f9
RN
3902 item = gtk_image_menu_item_new_with_mnemonic ( _("_Goto Center of Layer") );
3903 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_JUMP_TO, GTK_ICON_SIZE_MENU) );
50a14534
EB
3904 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_centerize), pass_along );
3905 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3906 gtk_widget_show ( item );
3907
7306a492 3908 item = gtk_menu_item_new_with_mnemonic ( _("Goto _Waypoint...") );
50a14534
EB
3909 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_wp), pass_along );
3910 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3911 gtk_widget_show ( item );
3912
1bd88e66 3913 export_submenu = gtk_menu_new ();
d6de71f9
RN
3914 item = gtk_image_menu_item_new_with_mnemonic ( _("_Export Layer") );
3915 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_HARDDISK, GTK_ICON_SIZE_MENU) );
50a14534
EB
3916 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3917 gtk_widget_show ( item );
98fcbbdb 3918 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), export_submenu );
1bd88e66 3919
7306a492 3920 item = gtk_menu_item_new_with_mnemonic ( _("Export as GPS_Point...") );
1bd88e66
GB
3921 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_gpspoint), pass_along );
3922 gtk_menu_shell_append (GTK_MENU_SHELL (export_submenu), item);
3923 gtk_widget_show ( item );
50a14534 3924
7306a492 3925 item = gtk_menu_item_new_with_mnemonic ( _("Export as GPS_Mapper...") );
50a14534 3926 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_gpsmapper), pass_along );
1bd88e66 3927 gtk_menu_shell_append (GTK_MENU_SHELL (export_submenu), item);
50a14534
EB
3928 gtk_widget_show ( item );
3929
7306a492 3930 item = gtk_menu_item_new_with_mnemonic ( _("Export as _GPX...") );
561e6ad0 3931 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_gpx), pass_along );
1bd88e66 3932 gtk_menu_shell_append (GTK_MENU_SHELL (export_submenu), item);
561e6ad0
EB
3933 gtk_widget_show ( item );
3934
ba9d0a00
RN
3935 item = gtk_menu_item_new_with_mnemonic ( _("Export as _KML...") );
3936 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_kml), pass_along );
3937 gtk_menu_shell_append (GTK_MENU_SHELL (export_submenu), item);
3938 gtk_widget_show ( item );
3939
17720e63
GB
3940 item = gtk_menu_item_new_with_mnemonic ( _("Export via GPSbabel...") );
3941 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_babel), pass_along );
3942 gtk_menu_shell_append (GTK_MENU_SHELL (export_submenu), item);
3943 gtk_widget_show ( item );
3944
6e4bf640 3945 gchar* external1 = g_strdup_printf ( _("Open with External Program_1: %s"), a_vik_get_external_gpx_program_1() );
ccccf356
RN
3946 item = gtk_menu_item_new_with_mnemonic ( external1 );
3947 g_free ( external1 );
3948 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_external_gpx_1), pass_along );
3949 gtk_menu_shell_append (GTK_MENU_SHELL (export_submenu), item);
3950 gtk_widget_show ( item );
3951
6e4bf640 3952 gchar* external2 = g_strdup_printf ( _("Open with External Program_2: %s"), a_vik_get_external_gpx_program_2() );
ff02058b
RN
3953 item = gtk_menu_item_new_with_mnemonic ( external2 );
3954 g_free ( external2 );
3955 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_external_gpx_2), pass_along );
3956 gtk_menu_shell_append (GTK_MENU_SHELL (export_submenu), item);
3957 gtk_widget_show ( item );
3958
e7a8a2f4
RN
3959 GtkWidget *new_submenu = gtk_menu_new();
3960 item = gtk_image_menu_item_new_with_mnemonic ( _("_New") );
3961 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NEW, GTK_ICON_SIZE_MENU) );
3962 gtk_menu_shell_append(GTK_MENU_SHELL (menu), item);
3963 gtk_widget_show(item);
3964 gtk_menu_item_set_submenu(GTK_MENU_ITEM(item), new_submenu);
3965
3966 item = gtk_image_menu_item_new_with_mnemonic ( _("New _Waypoint...") );
d6de71f9 3967 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NEW, GTK_ICON_SIZE_MENU) );
50a14534 3968 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_wp), pass_along );
e7a8a2f4 3969 gtk_menu_shell_append (GTK_MENU_SHELL (new_submenu), item);
50a14534 3970 gtk_widget_show ( item );
3e7553ae 3971
37615c52
RN
3972 item = gtk_image_menu_item_new_with_mnemonic ( _("New _Track") );
3973 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NEW, GTK_ICON_SIZE_MENU) );
3974 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_track), pass_along );
3975 gtk_menu_shell_append (GTK_MENU_SHELL (new_submenu), item);
3976 gtk_widget_show ( item );
3977 // Make it available only when a new track *not* already in progress
3978 gtk_widget_set_sensitive ( item, ! (gboolean)GPOINTER_TO_INT(vtl->current_track) );
3979
0d2b891f
RN
3980 item = gtk_image_menu_item_new_with_mnemonic ( _("New _Route") );
3981 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NEW, GTK_ICON_SIZE_MENU) );
3982 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_route), pass_along );
3983 gtk_menu_shell_append (GTK_MENU_SHELL (new_submenu), item);
3984 gtk_widget_show ( item );
3985 // Make it available only when a new track *not* already in progress
3986 gtk_widget_set_sensitive ( item, ! (gboolean)GPOINTER_TO_INT(vtl->current_track) );
3987
b3eb3b98
RN
3988#ifdef VIK_CONFIG_GEOTAG
3989 item = gtk_menu_item_new_with_mnemonic ( _("Geotag _Images...") );
3990 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_geotagging), pass_along );
3991 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3992 gtk_widget_show ( item );
3993#endif
3994
16fc32f6 3995 GtkWidget *acquire_submenu = gtk_menu_new ();
aaecf368 3996 item = gtk_image_menu_item_new_with_mnemonic ( _("_Acquire") );
d6de71f9 3997 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_DOWN, GTK_ICON_SIZE_MENU) );
16fc32f6
RN
3998 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3999 gtk_widget_show ( item );
4000 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), acquire_submenu );
4001
4002 item = gtk_menu_item_new_with_mnemonic ( _("From _GPS...") );
4003 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_acquire_gps_cb), pass_along );
4004 gtk_menu_shell_append (GTK_MENU_SHELL (acquire_submenu), item);
4005 gtk_widget_show ( item );
4006
7f95fd54
GB
4007 /* FIXME: only add menu when at least a routing engine has support for Directions */
4008 item = gtk_menu_item_new_with_mnemonic ( _("From _Directions...") );
4009 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_acquire_routing_cb), pass_along );
16fc32f6
RN
4010 gtk_menu_shell_append (GTK_MENU_SHELL (acquire_submenu), item);
4011 gtk_widget_show ( item );
4012
40f5740b
RN
4013#ifdef VIK_CONFIG_OPENSTREETMAP
4014 item = gtk_menu_item_new_with_mnemonic ( _("From _OSM Traces...") );
4015 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_acquire_osm_cb), pass_along );
4016 gtk_menu_shell_append (GTK_MENU_SHELL (acquire_submenu), item);
4017 gtk_widget_show ( item );
3cc57413
RN
4018
4019 item = gtk_menu_item_new_with_mnemonic ( _("From _My OSM Traces...") );
4020 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_acquire_osm_my_traces_cb), pass_along );
4021 gtk_menu_shell_append (GTK_MENU_SHELL (acquire_submenu), item);
4022 gtk_widget_show ( item );
40f5740b
RN
4023#endif
4024
c6acf18d
RN
4025 item = gtk_menu_item_new_with_mnemonic ( _("From _URL...") );
4026 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_acquire_url_cb), pass_along );
4027 gtk_menu_shell_append (GTK_MENU_SHELL (acquire_submenu), item);
4028 gtk_widget_show ( item );
4029
aaecf368
RN
4030#ifdef VIK_CONFIG_GEONAMES
4031 GtkWidget *wikipedia_submenu = gtk_menu_new();
4032 item = gtk_image_menu_item_new_with_mnemonic ( _("From _Wikipedia Waypoints") );
4033 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ADD, GTK_ICON_SIZE_MENU) );
4034 gtk_menu_shell_append(GTK_MENU_SHELL (acquire_submenu), item);
4035 gtk_widget_show(item);
4036 gtk_menu_item_set_submenu(GTK_MENU_ITEM(item), wikipedia_submenu);
4037
4038 item = gtk_image_menu_item_new_with_mnemonic ( _("Within _Layer Bounds") );
4039 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ZOOM_FIT, GTK_ICON_SIZE_MENU) );
4040 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_wikipedia_wp_layer), pass_along );
4041 gtk_menu_shell_append (GTK_MENU_SHELL (wikipedia_submenu), item);
4042 gtk_widget_show ( item );
4043
4044 item = gtk_image_menu_item_new_with_mnemonic ( _("Within _Current View") );
4045 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ZOOM_100, GTK_ICON_SIZE_MENU) );
4046 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_wikipedia_wp_viewport), pass_along );
4047 gtk_menu_shell_append (GTK_MENU_SHELL (wikipedia_submenu), item);
4048 gtk_widget_show ( item );
4049#endif
4050
16fc32f6
RN
4051#ifdef VIK_CONFIG_GEOCACHES
4052 item = gtk_menu_item_new_with_mnemonic ( _("From Geo_caching...") );
4053 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_acquire_geocache_cb), pass_along );
4054 gtk_menu_shell_append (GTK_MENU_SHELL (acquire_submenu), item);
4055 gtk_widget_show ( item );
4056#endif
4057
68bab1bd
RN
4058#ifdef VIK_CONFIG_GEOTAG
4059 item = gtk_menu_item_new_with_mnemonic ( _("From Geotagged _Images...") );
4060 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_acquire_geotagged_cb), pass_along );
4061 gtk_menu_shell_append (GTK_MENU_SHELL (acquire_submenu), item);
4062 gtk_widget_show ( item );
4063#endif
4064
d7ac7564
RN
4065 item = gtk_menu_item_new_with_mnemonic ( _("From _File...") );
4066 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_acquire_file_cb), pass_along );
4067 gtk_menu_shell_append (GTK_MENU_SHELL (acquire_submenu), item);
49f6b9d3 4068 gtk_widget_set_tooltip_text (item, _("Import File With GPS_Babel..."));
d7ac7564
RN
4069 gtk_widget_show ( item );
4070
82993cc7
RN
4071 vik_ext_tool_datasources_add_menu_items_to_menu ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)), GTK_MENU (acquire_submenu) );
4072
e50758c7
RN
4073 GtkWidget *upload_submenu = gtk_menu_new ();
4074 item = gtk_image_menu_item_new_with_mnemonic ( _("_Upload") );
4075 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_UP, GTK_ICON_SIZE_MENU) );
4076 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
4077 gtk_widget_show ( item );
4078 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), upload_submenu );
4079
0d2b891f
RN
4080 item = gtk_image_menu_item_new_with_mnemonic ( _("Upload to _GPS...") );
4081 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_FORWARD, GTK_ICON_SIZE_MENU) );
4082 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_gps_upload), pass_along );
4083 gtk_menu_shell_append (GTK_MENU_SHELL (upload_submenu), item);
4084 gtk_widget_show ( item );
4085
3e7553ae 4086#ifdef VIK_CONFIG_OPENSTREETMAP
d6de71f9
RN
4087 item = gtk_image_menu_item_new_with_mnemonic ( _("Upload to _OSM...") );
4088 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_UP, GTK_ICON_SIZE_MENU) );
3185b5d3 4089 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_osm_traces_upload_cb), pass_along );
e50758c7 4090 gtk_menu_shell_append (GTK_MENU_SHELL (upload_submenu), item);
3e7553ae
GB
4091 gtk_widget_show ( item );
4092#endif
28c82d8b 4093
c9a5cbf9 4094 GtkWidget *delete_submenu = gtk_menu_new ();
d6de71f9
RN
4095 item = gtk_image_menu_item_new_with_mnemonic ( _("De_lete") );
4096 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU) );
c9a5cbf9
RN
4097 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
4098 gtk_widget_show ( item );
4099 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), delete_submenu );
4100
d6de71f9
RN
4101 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete All _Tracks") );
4102 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU) );
c9a5cbf9
RN
4103 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_all_tracks), pass_along );
4104 gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
4105 gtk_widget_show ( item );
4106
d6de71f9
RN
4107 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete Tracks _From Selection...") );
4108 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
20b671c3
RN
4109 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_tracks_from_selection), pass_along );
4110 gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
4111 gtk_widget_show ( item );
58517f22
RN
4112
4113 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete _All Routes") );
4114 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU) );
4115 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_all_routes), pass_along );
4116 gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
4117 gtk_widget_show ( item );
4118
4119 item = gtk_image_menu_item_new_with_mnemonic ( _("_Delete Routes From Selection...") );
4120 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
4121 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_routes_from_selection), pass_along );
4122 gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
4123 gtk_widget_show ( item );
20b671c3 4124
d6de71f9
RN
4125 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete All _Waypoints") );
4126 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU) );
c9a5cbf9
RN
4127 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_all_waypoints), pass_along );
4128 gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
4129 gtk_widget_show ( item );
4130
d6de71f9
RN
4131 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete Waypoints From _Selection...") );
4132 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
20b671c3
RN
4133 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_waypoints_from_selection), pass_along );
4134 gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
4135 gtk_widget_show ( item );
4136
28c82d8b 4137 item = a_acquire_trwlayer_menu ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)), vlp,
d65659e7 4138 vik_layers_panel_get_viewport(VIK_LAYERS_PANEL(vlp)), vtl );
28c82d8b
EB
4139 if ( item ) {
4140 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
4141 gtk_widget_show ( item );
4142 }
4143
4144 item = a_acquire_trwlayer_track_menu ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)), vlp,
d65659e7 4145 vik_layers_panel_get_viewport(VIK_LAYERS_PANEL(vlp)), vtl );
28c82d8b
EB
4146 if ( item ) {
4147 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
4148 gtk_widget_show ( item );
07c9d42b
RN
4149 }
4150
4151 item = gtk_image_menu_item_new_with_mnemonic ( _("Track _List...") );
4152 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
4153 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_track_list_dialog), pass_along );
4154 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
4155 gtk_widget_show ( item );
4156 gtk_widget_set_sensitive ( item, (gboolean)(g_hash_table_size (vtl->tracks)+g_hash_table_size (vtl->routes)) );
00176e85
RN
4157
4158 item = gtk_image_menu_item_new_with_mnemonic ( _("_Waypoint List...") );
4159 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
4160 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_waypoint_list_dialog), pass_along );
4161 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
4162 gtk_widget_show ( item );
4163 gtk_widget_set_sensitive ( item, (gboolean)(g_hash_table_size (vtl->waypoints)) );
50a14534
EB
4164}
4165
c9570f86
RN
4166// Fake Waypoint UUIDs vi simple increasing integer
4167static guint wp_uuid = 0;
4168
50a14534
EB
4169void vik_trw_layer_add_waypoint ( VikTrwLayer *vtl, gchar *name, VikWaypoint *wp )
4170{
c9570f86
RN
4171 wp_uuid++;
4172
4173 vik_waypoint_set_name (wp, name);
4174
50a14534
EB
4175 if ( VIK_LAYER(vtl)->realized )
4176 {
cd3f311e
RN
4177 // Do we need to create the sublayer:
4178 if ( g_hash_table_size (vtl->waypoints) == 0 ) {
4179 trw_layer_add_sublayer_waypoints ( vtl, VIK_LAYER(vtl)->vt, &(VIK_LAYER(vtl)->iter) );
4180 }
4181
c9570f86
RN
4182 GtkTreeIter *iter = g_malloc(sizeof(GtkTreeIter));
4183
4184 // Visibility column always needed for waypoints
c9cac058 4185 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 4186
c9570f86
RN
4187 // Actual setting of visibility dependent on the waypoint
4188 vik_treeview_item_set_visible ( VIK_LAYER(vtl)->vt, iter, wp->visible );
4189
4190 g_hash_table_insert ( vtl->waypoints_iters, GUINT_TO_POINTER(wp_uuid), iter );
7a52aac6
RN
4191
4192 // Sort now as post_read is not called on a realized waypoint
4193 vik_treeview_sort_children ( VIK_LAYER(vtl)->vt, &(vtl->waypoints_iter), vtl->wp_sort_order );
50a14534 4194 }
50a14534 4195
a8fe53f8 4196 highest_wp_number_add_wp(vtl, name);
c9570f86 4197 g_hash_table_insert ( vtl->waypoints, GUINT_TO_POINTER(wp_uuid), wp );
50a14534
EB
4198
4199}
4200
ce4bd1cf
RN
4201// Fake Track UUIDs vi simple increasing integer
4202static guint tr_uuid = 0;
4203
50a14534
EB
4204void vik_trw_layer_add_track ( VikTrwLayer *vtl, gchar *name, VikTrack *t )
4205{
ce4bd1cf
RN
4206 tr_uuid++;
4207
4208 vik_track_set_name (t, name);
4209
50a14534
EB
4210 if ( VIK_LAYER(vtl)->realized )
4211 {
cd3f311e
RN
4212 // Do we need to create the sublayer:
4213 if ( g_hash_table_size (vtl->tracks) == 0 ) {
4214 trw_layer_add_sublayer_tracks ( vtl, VIK_LAYER(vtl)->vt, &(VIK_LAYER(vtl)->iter) );
4215 }
4216
ce4bd1cf
RN
4217 GtkTreeIter *iter = g_malloc(sizeof(GtkTreeIter));
4218 // Visibility column always needed for tracks
c9cac058 4219 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 4220
ce4bd1cf
RN
4221 // Actual setting of visibility dependent on the track
4222 vik_treeview_item_set_visible ( VIK_LAYER(vtl)->vt, iter, t->visible );
4223
4224 g_hash_table_insert ( vtl->tracks_iters, GUINT_TO_POINTER(tr_uuid), iter );
c9cac058
RN
4225
4226 // Sort now as post_read is not called on a realized track
4227 vik_treeview_sort_children ( VIK_LAYER(vtl)->vt, &(vtl->tracks_iter), vtl->track_sort_order );
50a14534 4228 }
50a14534 4229
ce4bd1cf 4230 g_hash_table_insert ( vtl->tracks, GUINT_TO_POINTER(tr_uuid), t );
b1453c16 4231
93ee73b3 4232 trw_layer_update_treeview ( vtl, t );
50a14534
EB
4233}
4234
0d2b891f
RN
4235// Fake Route UUIDs vi simple increasing integer
4236static guint rt_uuid = 0;
4237
4238void vik_trw_layer_add_route ( VikTrwLayer *vtl, gchar *name, VikTrack *t )
4239{
4240 rt_uuid++;
4241
4242 vik_track_set_name (t, name);
4243
4244 if ( VIK_LAYER(vtl)->realized )
4245 {
4246 // Do we need to create the sublayer:
4247 if ( g_hash_table_size (vtl->routes) == 0 ) {
4248 trw_layer_add_sublayer_routes ( vtl, VIK_LAYER(vtl)->vt, &(VIK_LAYER(vtl)->iter) );
4249 }
4250
4251 GtkTreeIter *iter = g_malloc(sizeof(GtkTreeIter));
c9cac058
RN
4252 // Visibility column always needed for routes
4253 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 );
4254 // Actual setting of visibility dependent on the route
0d2b891f
RN
4255 vik_treeview_item_set_visible ( VIK_LAYER(vtl)->vt, iter, t->visible );
4256
4257 g_hash_table_insert ( vtl->routes_iters, GUINT_TO_POINTER(rt_uuid), iter );
c9cac058
RN
4258
4259 // Sort now as post_read is not called on a realized route
4260 vik_treeview_sort_children ( VIK_LAYER(vtl)->vt, &(vtl->routes_iter), vtl->track_sort_order );
0d2b891f
RN
4261 }
4262
4263 g_hash_table_insert ( vtl->routes, GUINT_TO_POINTER(rt_uuid), t );
4264
93ee73b3 4265 trw_layer_update_treeview ( vtl, t );
0d2b891f
RN
4266}
4267
50a14534 4268/* to be called whenever a track has been deleted or may have been changed. */
ce4bd1cf 4269void trw_layer_cancel_tps_of_track ( VikTrwLayer *vtl, VikTrack *trk )
50a14534 4270{
ce4bd1cf 4271 if (vtl->current_tp_track == trk )
50a14534 4272 trw_layer_cancel_current_tp ( vtl, FALSE );
50a14534 4273}
0d2b891f 4274
98acb9a1
RN
4275/**
4276 * Normally this is done to due the waypoint size preference having changed
4277 */
4278void vik_trw_layer_reset_waypoints ( VikTrwLayer *vtl )
4279{
4280 GHashTableIter iter;
4281 gpointer key, value;
4282
4283 // Foreach waypoint
4284 g_hash_table_iter_init ( &iter, vtl->waypoints );
4285 while ( g_hash_table_iter_next ( &iter, &key, &value ) ) {
4286 VikWaypoint *wp = VIK_WAYPOINT(value);
4287 if ( wp->symbol ) {
4288 // Reapply symbol setting to update the pixbuf
4289 gchar *tmp_symbol = g_strdup ( wp->symbol );
4290 vik_waypoint_set_symbol ( wp, tmp_symbol );
4291 g_free ( tmp_symbol );
4292 }
4293 }
4294}
4295
1613e468
RN
4296/**
4297 * trw_layer_new_unique_sublayer_name:
4298 *
4299 * Allocates a unique new name
4300 */
9748531a 4301gchar *trw_layer_new_unique_sublayer_name (VikTrwLayer *vtl, gint sublayer_type, const gchar *name)
e890a6e6 4302{
0d2b891f
RN
4303 gint i = 2;
4304 gchar *newname = g_strdup(name);
4305
4306 gpointer id = NULL;
4307 do {
4308 id = NULL;
4309 switch ( sublayer_type ) {
4310 case VIK_TRW_LAYER_SUBLAYER_TRACK:
4311 id = (gpointer) vik_trw_layer_get_track ( vtl, newname );
4312 break;
4313 case VIK_TRW_LAYER_SUBLAYER_WAYPOINT:
4314 id = (gpointer) vik_trw_layer_get_waypoint ( vtl, newname );
4315 break;
4316 default:
4317 id = (gpointer) vik_trw_layer_get_route ( vtl, newname );
4318 break;
4319 }
4320 // If found a name already in use try adding 1 to it and we try again
4321 if ( id ) {
4322 gchar *new_newname = g_strdup_printf("%s#%d", name, i);
4323 g_free(newname);
4324 newname = new_newname;
4325 i++;
4326 }
4327 } while ( id != NULL);
4328
e890a6e6
EB
4329 return newname;
4330}
50a14534 4331
805d282e
EB
4332void vik_trw_layer_filein_add_waypoint ( VikTrwLayer *vtl, gchar *name, VikWaypoint *wp )
4333{
c9570f86
RN
4334 // No more uniqueness of name forced when loading from a file
4335 // This now makes this function a little redunant as we just flow the parameters through
4336 vik_trw_layer_add_waypoint ( vtl, name, wp );
805d282e 4337}
c9570f86 4338
805d282e
EB
4339void vik_trw_layer_filein_add_track ( VikTrwLayer *vtl, gchar *name, VikTrack *tr )
4340{
7ff7d728 4341 if ( vtl->route_finder_append && vtl->route_finder_current_track ) {
c3deba01 4342 vik_track_remove_dup_points ( tr ); /* make "double point" track work to undo */
7ff7d728 4343 vik_track_steal_and_append_trackpoints ( vtl->route_finder_current_track, tr );
bddd2056 4344 vik_track_free ( tr );
7ff7d728 4345 vtl->route_finder_append = FALSE; /* this means we have added it */
bddd2056 4346 } else {
ce4bd1cf
RN
4347
4348 // No more uniqueness of name forced when loading from a file
0d2b891f
RN
4349 if ( tr->is_route )
4350 vik_trw_layer_add_route ( vtl, name, tr );
4351 else
4352 vik_trw_layer_add_track ( vtl, name, tr );
bddd2056 4353
7ff7d728 4354 if ( vtl->route_finder_check_added_track ) {
c3deba01 4355 vik_track_remove_dup_points ( tr ); /* make "double point" track work to undo */
ce4bd1cf 4356 vtl->route_finder_added_track = tr;
bddd2056
EB
4357 }
4358 }
805d282e
EB
4359}
4360
ce4bd1cf 4361static void trw_layer_enum_item ( gpointer id, GList **tr, GList **l )
c48517ad 4362{
ce4bd1cf 4363 *l = g_list_append(*l, id);
c48517ad
AF
4364}
4365
ce4bd1cf
RN
4366/*
4367 * Move an item from one TRW layer to another TRW layer
4368 */
4369static void trw_layer_move_item ( VikTrwLayer *vtl_src, VikTrwLayer *vtl_dest, gpointer id, gint type )
c48517ad 4370{
c9cac058 4371 // TODO reconsider strategy when moving within layer (if anything...)
762cb457 4372 gboolean rename = ( vtl_src != vtl_dest );
c9cac058
RN
4373 if ( ! rename )
4374 return;
762cb457 4375
c48517ad 4376 if (type == VIK_TRW_LAYER_SUBLAYER_TRACK) {
ce4bd1cf
RN
4377 VikTrack *trk = g_hash_table_lookup ( vtl_src->tracks, id );
4378
762cb457
RN
4379 gchar *newname;
4380 if ( rename )
4381 newname = trw_layer_new_unique_sublayer_name ( vtl_dest, type, trk->name );
4382 else
4383 newname = g_strdup ( trk->name );
ce4bd1cf 4384
03817fbf 4385 VikTrack *trk2 = vik_track_copy ( trk, TRUE );
ce4bd1cf 4386 vik_trw_layer_add_track ( vtl_dest, newname, trk2 );
1613e468 4387 g_free ( newname );
ce4bd1cf 4388 vik_trw_layer_delete_track ( vtl_src, trk );
c48517ad 4389 }
c9570f86 4390
0d2b891f
RN
4391 if (type == VIK_TRW_LAYER_SUBLAYER_ROUTE) {
4392 VikTrack *trk = g_hash_table_lookup ( vtl_src->routes, id );
4393
762cb457
RN
4394 gchar *newname;
4395 if ( rename )
4396 newname = trw_layer_new_unique_sublayer_name ( vtl_dest, type, trk->name );
4397 else
4398 newname = g_strdup ( trk->name );
0d2b891f 4399
03817fbf 4400 VikTrack *trk2 = vik_track_copy ( trk, TRUE );
0d2b891f 4401 vik_trw_layer_add_route ( vtl_dest, newname, trk2 );
1613e468 4402 g_free ( newname );
0d2b891f
RN
4403 vik_trw_layer_delete_route ( vtl_src, trk );
4404 }
4405
ce4bd1cf
RN
4406 if (type == VIK_TRW_LAYER_SUBLAYER_WAYPOINT) {
4407 VikWaypoint *wp = g_hash_table_lookup ( vtl_src->waypoints, id );
c9570f86 4408
762cb457
RN
4409 gchar *newname;
4410 if ( rename )
4411 newname = trw_layer_new_unique_sublayer_name ( vtl_dest, type, wp->name );
4412 else
4413 newname = g_strdup ( wp->name );
c9570f86 4414
ce4bd1cf
RN
4415 VikWaypoint *wp2 = vik_waypoint_copy ( wp );
4416 vik_trw_layer_add_waypoint ( vtl_dest, newname, wp2 );
1613e468 4417 g_free ( newname );
c9570f86 4418 trw_layer_delete_waypoint ( vtl_src, wp );
aa0665e5 4419
26e66078
RN
4420 // Recalculate bounds even if not renamed as maybe dragged between layers
4421 trw_layer_calculate_bounds_waypoints ( vtl_dest );
4422 trw_layer_calculate_bounds_waypoints ( vtl_src );
c48517ad
AF
4423 }
4424}
4425
70a23263 4426static void trw_layer_drag_drop_request ( VikTrwLayer *vtl_src, VikTrwLayer *vtl_dest, GtkTreeIter *src_item_iter, GtkTreePath *dest_path )
e4afc73a
EB
4427{
4428 VikTreeview *vt = VIK_LAYER(vtl_src)->vt;
c48517ad
AF
4429 gint type = vik_treeview_item_get_data(vt, src_item_iter);
4430
e4afc73a 4431 if (!vik_treeview_item_get_pointer(vt, src_item_iter)) {
b637058e
EB
4432 GList *items = NULL;
4433 GList *iter;
e4afc73a 4434
c48517ad
AF
4435 if (type==VIK_TRW_LAYER_SUBLAYER_TRACKS) {
4436 g_hash_table_foreach ( vtl_src->tracks, (GHFunc)trw_layer_enum_item, &items);
4437 }
4438 if (type==VIK_TRW_LAYER_SUBLAYER_WAYPOINTS) {
4439 g_hash_table_foreach ( vtl_src->waypoints, (GHFunc)trw_layer_enum_item, &items);
4440 }
0d2b891f
RN
4441 if (type==VIK_TRW_LAYER_SUBLAYER_ROUTES) {
4442 g_hash_table_foreach ( vtl_src->routes, (GHFunc)trw_layer_enum_item, &items);
4443 }
4444
c48517ad
AF
4445 iter = items;
4446 while (iter) {
4447 if (type==VIK_TRW_LAYER_SUBLAYER_TRACKS) {
0d2b891f 4448 trw_layer_move_item ( vtl_src, vtl_dest, iter->data, VIK_TRW_LAYER_SUBLAYER_TRACK);
c9cac058 4449 } else if (type==VIK_TRW_LAYER_SUBLAYER_ROUTES) {
0d2b891f 4450 trw_layer_move_item ( vtl_src, vtl_dest, iter->data, VIK_TRW_LAYER_SUBLAYER_ROUTE);
c48517ad 4451 } else {
0d2b891f 4452 trw_layer_move_item ( vtl_src, vtl_dest, iter->data, VIK_TRW_LAYER_SUBLAYER_WAYPOINT);
c48517ad
AF
4453 }
4454 iter = iter->next;
e4afc73a 4455 }
c48517ad 4456 if (items)
b637058e 4457 g_list_free(items);
c48517ad
AF
4458 } else {
4459 gchar *name = vik_treeview_item_get_pointer(vt, src_item_iter);
4460 trw_layer_move_item(vtl_src, vtl_dest, name, type);
e4afc73a
EB
4461 }
4462}
4463
c98e2f73 4464gboolean trw_layer_track_find_uuid ( const gpointer id, const VikTrack *trk, gpointer udata )
ce4bd1cf
RN
4465{
4466 trku_udata *user_data = udata;
4467 if ( trk == user_data->trk ) {
4468 user_data->uuid = id;
4469 return TRUE;
4470 }
4471 return FALSE;
4472}
4473
4474gboolean vik_trw_layer_delete_track ( VikTrwLayer *vtl, VikTrack *trk )
50a14534 4475{
50a14534 4476 gboolean was_visible = FALSE;
ce4bd1cf
RN
4477
4478 if ( trk && trk->name ) {
4479
4480 if ( trk == vtl->current_track ) {
50a14534 4481 vtl->current_track = NULL;
ce4bd1cf
RN
4482 vtl->current_tp_track = NULL;
4483 vtl->current_tp_id = NULL;
4484 vtl->moving_tp = FALSE;
77ad64fa 4485 }
ce4bd1cf
RN
4486
4487 was_visible = trk->visible;
4488
4489 if ( trk == vtl->route_finder_current_track )
7ff7d728
RN
4490 vtl->route_finder_current_track = NULL;
4491
ce4bd1cf
RN
4492 if ( trk == vtl->route_finder_added_track )
4493 vtl->route_finder_added_track = NULL;
4494
4495 trku_udata udata;
4496 udata.trk = trk;
4497 udata.uuid = NULL;
50a14534 4498
ce4bd1cf
RN
4499 // Hmmm, want key of it
4500 gpointer *trkf = g_hash_table_find ( vtl->tracks, (GHRFunc) trw_layer_track_find_uuid, &udata );
50a14534 4501
ce4bd1cf
RN
4502 if ( trkf && udata.uuid ) {
4503 /* could be current_tp, so we have to check */
4504 trw_layer_cancel_tps_of_track ( vtl, trk );
4505
4506 GtkTreeIter *it = g_hash_table_lookup ( vtl->tracks_iters, udata.uuid );
4507
4508 if ( it ) {
4509 vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, it );
4510 g_hash_table_remove ( vtl->tracks_iters, udata.uuid );
4511 g_hash_table_remove ( vtl->tracks, udata.uuid );
cd3f311e
RN
4512
4513 // If last sublayer, then remove sublayer container
4514 if ( g_hash_table_size (vtl->tracks) == 0 ) {
4515 vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, &(vtl->tracks_iter) );
4516 }
ce4bd1cf
RN
4517 }
4518 }
50a14534
EB
4519 }
4520 return was_visible;
4521}
4522
0d2b891f
RN
4523gboolean vik_trw_layer_delete_route ( VikTrwLayer *vtl, VikTrack *trk )
4524{
4525 gboolean was_visible = FALSE;
4526
4527 if ( trk && trk->name ) {
4528
4529 if ( trk == vtl->current_track ) {
4530 vtl->current_track = NULL;
4531 vtl->current_tp_track = NULL;
4532 vtl->current_tp_id = NULL;
4533 vtl->moving_tp = FALSE;
4534 }
4535
4536 was_visible = trk->visible;
4537
4538 if ( trk == vtl->route_finder_current_track )
4539 vtl->route_finder_current_track = NULL;
4540
4541 if ( trk == vtl->route_finder_added_track )
4542 vtl->route_finder_added_track = NULL;
4543
4544 trku_udata udata;
4545 udata.trk = trk;
4546 udata.uuid = NULL;
4547
4548 // Hmmm, want key of it
4549 gpointer *trkf = g_hash_table_find ( vtl->routes, (GHRFunc) trw_layer_track_find_uuid, &udata );
4550
4551 if ( trkf && udata.uuid ) {
4552 /* could be current_tp, so we have to check */
4553 trw_layer_cancel_tps_of_track ( vtl, trk );
4554
4555 GtkTreeIter *it = g_hash_table_lookup ( vtl->routes_iters, udata.uuid );
4556
4557 if ( it ) {
4558 vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, it );
4559 g_hash_table_remove ( vtl->routes_iters, udata.uuid );
4560 g_hash_table_remove ( vtl->routes, udata.uuid );
4561
4562 // If last sublayer, then remove sublayer container
4563 if ( g_hash_table_size (vtl->routes) == 0 ) {
4564 vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, &(vtl->routes_iter) );
4565 }
4566 }
4567 }
4568 }
4569 return was_visible;
4570}
4571
c9570f86 4572static gboolean trw_layer_delete_waypoint ( VikTrwLayer *vtl, VikWaypoint *wp )
e4afc73a
EB
4573{
4574 gboolean was_visible = FALSE;
e4afc73a 4575
c9570f86 4576 if ( wp && wp->name ) {
e4afc73a
EB
4577
4578 if ( wp == vtl->current_wp ) {
4579 vtl->current_wp = NULL;
c9570f86 4580 vtl->current_wp_id = NULL;
e4afc73a
EB
4581 vtl->moving_wp = FALSE;
4582 }
4583
4584 was_visible = wp->visible;
c9570f86
RN
4585
4586 wpu_udata udata;
4587 udata.wp = wp;
4588 udata.uuid = NULL;
4589
4590 // Hmmm, want key of it
4591 gpointer *wpf = g_hash_table_find ( vtl->waypoints, (GHRFunc) trw_layer_waypoint_find_uuid, (gpointer) &udata );
4592
4593 if ( wpf && udata.uuid ) {
4594 GtkTreeIter *it = g_hash_table_lookup ( vtl->waypoints_iters, udata.uuid );
4595
4596 if ( it ) {
4597 vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, it );
4598 g_hash_table_remove ( vtl->waypoints_iters, udata.uuid );
4599
4600 highest_wp_number_remove_wp(vtl, wp->name);
4601 g_hash_table_remove ( vtl->waypoints, udata.uuid ); // last because this frees the name
cd3f311e
RN
4602
4603 // If last sublayer, then remove sublayer container
4604 if ( g_hash_table_size (vtl->waypoints) == 0 ) {
4605 vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, &(vtl->waypoints_iter) );
4606 }
c9570f86
RN
4607 }
4608 }
a8fe53f8 4609
e4afc73a
EB
4610 }
4611
4612 return was_visible;
4613}
4614
c9570f86
RN
4615// Only for temporary use by trw_layer_delete_waypoint_by_name
4616static gboolean trw_layer_waypoint_find_uuid_by_name ( const gpointer id, const VikWaypoint *wp, gpointer udata )
4617{
4618 wpu_udata *user_data = udata;
4619 if ( ! strcmp ( wp->name, user_data->wp->name ) ) {
4620 user_data->uuid = id;
4621 return TRUE;
4622 }
4623 return FALSE;
4624}
4625
4626/*
4627 * Delete a waypoint by the given name
4628 * NOTE: ATM this will delete the first encountered Waypoint with the specified name
4629 * as there be multiple waypoints with the same name
4630 */
4631static gboolean trw_layer_delete_waypoint_by_name ( VikTrwLayer *vtl, const gchar *name )
4632{
4633 wpu_udata udata;
4634 // Fake a waypoint with the given name
4635 udata.wp = vik_waypoint_new ();
4636 vik_waypoint_set_name (udata.wp, name);
4637 // Currently only the name is used in this waypoint find function
4638 udata.uuid = NULL;
4639
4640 // Hmmm, want key of it
4641 gpointer *wpf = g_hash_table_find ( vtl->waypoints, (GHRFunc) trw_layer_waypoint_find_uuid_by_name, (gpointer) &udata );
4642
4643 vik_waypoint_free (udata.wp);
4644
4645 if ( wpf && udata.uuid )
4646 return trw_layer_delete_waypoint (vtl, g_hash_table_lookup ( vtl->waypoints, udata.uuid ));
4647 else
4648 return FALSE;
4649}
4650
ce4bd1cf
RN
4651typedef struct {
4652 VikTrack *trk; // input
4653 gpointer uuid; // output
4654} tpu_udata;
4655
4656// Only for temporary use by trw_layer_delete_track_by_name
4657static gboolean trw_layer_track_find_uuid_by_name ( const gpointer id, const VikTrack *trk, gpointer udata )
4658{
4659 tpu_udata *user_data = udata;
4660 if ( ! strcmp ( trk->name, user_data->trk->name ) ) {
4661 user_data->uuid = id;
4662 return TRUE;
4663 }
4664 return FALSE;
4665}
4666
4667/*
4668 * Delete a track by the given name
4669 * NOTE: ATM this will delete the first encountered Track with the specified name
0d2b891f 4670 * as there may be multiple tracks with the same name within the specified hash table
ce4bd1cf 4671 */
0d2b891f 4672static gboolean trw_layer_delete_track_by_name ( VikTrwLayer *vtl, const gchar *name, GHashTable *ht_tracks )
ce4bd1cf
RN
4673{
4674 tpu_udata udata;
4675 // Fake a track with the given name
4676 udata.trk = vik_track_new ();
4677 vik_track_set_name (udata.trk, name);
4678 // Currently only the name is used in this waypoint find function
4679 udata.uuid = NULL;
4680
4681 // Hmmm, want key of it
0d2b891f 4682 gpointer *trkf = g_hash_table_find ( ht_tracks, (GHRFunc) trw_layer_track_find_uuid_by_name, &udata );
ce4bd1cf
RN
4683
4684 vik_track_free (udata.trk);
4685
0d2b891f
RN
4686 if ( trkf && udata.uuid ) {
4687 // This could be a little better written...
4688 if ( vtl->tracks == ht_tracks )
4689 return vik_trw_layer_delete_track (vtl, g_hash_table_lookup ( ht_tracks, udata.uuid ));
4690 if ( vtl->routes == ht_tracks )
4691 return vik_trw_layer_delete_route (vtl, g_hash_table_lookup ( ht_tracks, udata.uuid ));
4692 return FALSE;
4693 }
ce4bd1cf
RN
4694 else
4695 return FALSE;
4696}
4697
9e212bfc 4698static void remove_item_from_treeview ( const gpointer id, GtkTreeIter *it, VikTreeview * vt )
700b0908
QT
4699{
4700 vik_treeview_item_delete (vt, it );
4701}
4702
0d2b891f
RN
4703void vik_trw_layer_delete_all_routes ( VikTrwLayer *vtl )
4704{
4705
4706 vtl->current_track = NULL;
4707 vtl->route_finder_current_track = NULL;
4708 vtl->route_finder_added_track = NULL;
4709 if (vtl->current_tp_track)
4710 trw_layer_cancel_current_tp(vtl, FALSE);
4711
4712 g_hash_table_foreach(vtl->routes_iters, (GHFunc) remove_item_from_treeview, VIK_LAYER(vtl)->vt);
4713 g_hash_table_remove_all(vtl->routes_iters);
4714 g_hash_table_remove_all(vtl->routes);
4715
4716 vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, &(vtl->routes_iter) );
4717
da121f9b 4718 vik_layer_emit_update ( VIK_LAYER(vtl) );
0d2b891f
RN
4719}
4720
700b0908
QT
4721void vik_trw_layer_delete_all_tracks ( VikTrwLayer *vtl )
4722{
4723
4724 vtl->current_track = NULL;
7ff7d728 4725 vtl->route_finder_current_track = NULL;
ce4bd1cf
RN
4726 vtl->route_finder_added_track = NULL;
4727 if (vtl->current_tp_track)
700b0908 4728 trw_layer_cancel_current_tp(vtl, FALSE);
700b0908
QT
4729
4730 g_hash_table_foreach(vtl->tracks_iters, (GHFunc) remove_item_from_treeview, VIK_LAYER(vtl)->vt);
4731 g_hash_table_remove_all(vtl->tracks_iters);
4732 g_hash_table_remove_all(vtl->tracks);
4733
cd3f311e
RN
4734 vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, &(vtl->tracks_iter) );
4735
da121f9b 4736 vik_layer_emit_update ( VIK_LAYER(vtl) );
700b0908
QT
4737}
4738
4739void vik_trw_layer_delete_all_waypoints ( VikTrwLayer *vtl )
4740{
4741 vtl->current_wp = NULL;
c9570f86 4742 vtl->current_wp_id = NULL;
700b0908
QT
4743 vtl->moving_wp = FALSE;
4744
a8fe53f8
EB
4745 highest_wp_number_reset(vtl);
4746
700b0908
QT
4747 g_hash_table_foreach(vtl->waypoints_iters, (GHFunc) remove_item_from_treeview, VIK_LAYER(vtl)->vt);
4748 g_hash_table_remove_all(vtl->waypoints_iters);
4749 g_hash_table_remove_all(vtl->waypoints);
4750
cd3f311e
RN
4751 vik_treeview_item_delete ( VIK_LAYER(vtl)->vt, &(vtl->waypoints_iter) );
4752
da121f9b 4753 vik_layer_emit_update ( VIK_LAYER(vtl) );
700b0908
QT
4754}
4755
1c2083ba 4756static void trw_layer_delete_all_tracks ( menu_array_layer values )
c9a5cbf9 4757{
1c2083ba 4758 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
c9a5cbf9
RN
4759 // Get confirmation from the user
4760 if ( a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
4761 _("Are you sure you want to delete all tracks in %s?"),
4762 vik_layer_get_name ( VIK_LAYER(vtl) ) ) )
4763 vik_trw_layer_delete_all_tracks (vtl);
4764}
4765
1c2083ba 4766static void trw_layer_delete_all_routes ( menu_array_layer values )
c9a5cbf9 4767{
1c2083ba 4768 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
c9a5cbf9
RN
4769 // Get confirmation from the user
4770 if ( a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
0d2b891f
RN
4771 _("Are you sure you want to delete all routes in %s?"),
4772 vik_layer_get_name ( VIK_LAYER(vtl) ) ) )
4773 vik_trw_layer_delete_all_routes (vtl);
c9a5cbf9
RN
4774}
4775
1c2083ba 4776static void trw_layer_delete_all_waypoints ( menu_array_layer values )
0d2b891f 4777{
1c2083ba 4778 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
0d2b891f
RN
4779 // Get confirmation from the user
4780 if ( a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
4781 _("Are you sure you want to delete all waypoints in %s?"),
4782 vik_layer_get_name ( VIK_LAYER(vtl) ) ) )
4783 vik_trw_layer_delete_all_waypoints (vtl);
4784}
4785
19782ffd 4786static void trw_layer_delete_item ( menu_array_sublayer values )
50a14534 4787{
19782ffd 4788 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
50a14534 4789 gboolean was_visible = FALSE;
19782ffd 4790 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_WAYPOINT )
50a14534 4791 {
19782ffd 4792 VikWaypoint *wp = g_hash_table_lookup ( vtl->waypoints, values[MA_SUBLAYER_ID] );
c9570f86 4793 if ( wp && wp->name ) {
19782ffd 4794 if ( GPOINTER_TO_INT (values[MA_CONFIRM]) )
c9570f86
RN
4795 // Get confirmation from the user
4796 // Maybe this Waypoint Delete should be optional as is it could get annoying...
4797 if ( ! a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
ea3a6072 4798 _("Are you sure you want to delete the waypoint \"%s\"?"),
c9570f86
RN
4799 wp->name ) )
4800 return;
4801 was_visible = trw_layer_delete_waypoint ( vtl, wp );
4802 }
50a14534 4803 }
19782ffd 4804 else if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_TRACK )
50a14534 4805 {
19782ffd 4806 VikTrack *trk = g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
ce4bd1cf 4807 if ( trk && trk->name ) {
19782ffd 4808 if ( GPOINTER_TO_INT (values[MA_CONFIRM]) )
ce4bd1cf
RN
4809 // Get confirmation from the user
4810 if ( ! a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
ea3a6072 4811 _("Are you sure you want to delete the track \"%s\"?"),
ce4bd1cf
RN
4812 trk->name ) )
4813 return;
4814 was_visible = vik_trw_layer_delete_track ( vtl, trk );
4815 }
50a14534 4816 }
0d2b891f
RN
4817 else
4818 {
19782ffd 4819 VikTrack *trk = g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
0d2b891f 4820 if ( trk && trk->name ) {
19782ffd 4821 if ( GPOINTER_TO_INT (values[MA_CONFIRM]) )
0d2b891f
RN
4822 // Get confirmation from the user
4823 if ( ! a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
ea3a6072 4824 _("Are you sure you want to delete the route \"%s\"?"),
0d2b891f
RN
4825 trk->name ) )
4826 return;
4827 was_visible = vik_trw_layer_delete_route ( vtl, trk );
4828 }
4829 }
50a14534 4830 if ( was_visible )
da121f9b 4831 vik_layer_emit_update ( VIK_LAYER(vtl) );
50a14534
EB
4832}
4833
6761d8a4
RN
4834/**
4835 * Rename waypoint and maintain corresponding name of waypoint in the treeview
4836 */
4f556508 4837void trw_layer_waypoint_rename ( VikTrwLayer *vtl, VikWaypoint *wp, const gchar *new_name )
6761d8a4
RN
4838{
4839 vik_waypoint_set_name ( wp, new_name );
4840
4841 // Now update the treeview as well
4842 wpu_udata udataU;
4843 udataU.wp = wp;
4844 udataU.uuid = NULL;
4845
4846 // Need key of it for treeview update
4847 gpointer *wpf = g_hash_table_find ( vtl->waypoints, (GHRFunc) trw_layer_waypoint_find_uuid, &udataU );
4848
4849 if ( wpf && udataU.uuid ) {
4850 GtkTreeIter *it = g_hash_table_lookup ( vtl->waypoints_iters, udataU.uuid );
4851
4852 if ( it ) {
4853 vik_treeview_item_set_name ( VIK_LAYER(vtl)->vt, it, new_name );
c9cac058 4854 vik_treeview_sort_children ( VIK_LAYER(vtl)->vt, &(vtl->waypoints_iter), vtl->wp_sort_order );
6761d8a4
RN
4855 }
4856 }
4857}
50a14534 4858
4f556508
RN
4859/**
4860 * Maintain icon of waypoint in the treeview
4861 */
4862void trw_layer_waypoint_reset_icon ( VikTrwLayer *vtl, VikWaypoint *wp )
4863{
4864 // update the treeview
4865 wpu_udata udataU;
4866 udataU.wp = wp;
4867 udataU.uuid = NULL;
4868
4869 // Need key of it for treeview update
4870 gpointer *wpf = g_hash_table_find ( vtl->waypoints, (GHRFunc) trw_layer_waypoint_find_uuid, &udataU );
4871
4872 if ( wpf && udataU.uuid ) {
4873 GtkTreeIter *it = g_hash_table_lookup ( vtl->waypoints_iters, udataU.uuid );
4874
4875 if ( it ) {
4876 vik_treeview_item_set_icon ( VIK_LAYER(vtl)->vt, it, get_wp_sym_small (wp->symbol) );
4877 }
4878 }
4879}
4880
19782ffd 4881static void trw_layer_properties_item ( menu_array_sublayer values )
50a14534 4882{
19782ffd
RN
4883 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
4884 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_WAYPOINT )
50a14534 4885 {
19782ffd 4886 VikWaypoint *wp = g_hash_table_lookup ( vtl->waypoints, values[MA_SUBLAYER_ID] );
c9570f86
RN
4887
4888 if ( wp && wp->name )
50a14534 4889 {
ac1bde8b 4890 gboolean updated = FALSE;
6761d8a4
RN
4891 gchar *new_name = a_dialog_waypoint ( VIK_GTK_WINDOW_FROM_LAYER(vtl), wp->name, vtl, wp, vtl->coord_mode, FALSE, &updated );
4892 if ( new_name )
4893 trw_layer_waypoint_rename ( vtl, wp, new_name );
50a14534 4894
19782ffd
RN
4895 if ( updated && values[MA_TV_ITER] )
4896 vik_treeview_item_set_icon ( VIK_LAYER(vtl)->vt, values[MA_TV_ITER], get_wp_sym_small (wp->symbol) );
70cefedc 4897
ac1bde8b 4898 if ( updated && VIK_LAYER(vtl)->visible )
da121f9b 4899 vik_layer_emit_update ( VIK_LAYER(vtl) );
50a14534
EB
4900 }
4901 }
4902 else
4903 {
0d2b891f 4904 VikTrack *tr;
19782ffd
RN
4905 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_TRACK )
4906 tr = g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
0d2b891f 4907 else
19782ffd 4908 tr = g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
0d2b891f 4909
ce4bd1cf 4910 if ( tr && tr->name )
50a14534 4911 {
21700912 4912 vik_trw_layer_propwin_run ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
19782ffd 4913 vtl,
b1453c16 4914 tr,
19782ffd
RN
4915 values[MA_VLP],
4916 values[MA_VVP],
626de648 4917 FALSE );
50a14534
EB
4918 }
4919 }
4920}
4921
626de648
RN
4922/**
4923 * trw_layer_track_statistics:
4924 *
4925 * Show track statistics.
4926 * ATM jump to the stats page in the properties
4927 * TODO: consider separating the stats into an individual dialog?
4928 */
19782ffd 4929static void trw_layer_track_statistics ( menu_array_sublayer values )
626de648 4930{
19782ffd 4931 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
626de648 4932 VikTrack *trk;
19782ffd
RN
4933 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_TRACK )
4934 trk = g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
626de648 4935 else
19782ffd 4936 trk = g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
626de648
RN
4937
4938 if ( trk && trk->name ) {
4939 vik_trw_layer_propwin_run ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
4940 vtl,
4941 trk,
19782ffd
RN
4942 values[MA_VLP],
4943 values[MA_VVP],
626de648
RN
4944 TRUE );
4945 }
4946}
4947
b1453c16
RN
4948/*
4949 * Update the treeview of the track id - primarily to update the icon
4950 */
93ee73b3 4951void trw_layer_update_treeview ( VikTrwLayer *vtl, VikTrack *trk )
b1453c16
RN
4952{
4953 trku_udata udata;
4954 udata.trk = trk;
4955 udata.uuid = NULL;
4956
4957 gpointer *trkf = NULL;
4958 if ( trk->is_route )
4959 trkf = g_hash_table_find ( vtl->routes, (GHRFunc) trw_layer_track_find_uuid, &udata );
4960 else
4961 trkf = g_hash_table_find ( vtl->tracks, (GHRFunc) trw_layer_track_find_uuid, &udata );
4962
4963 if ( trkf && udata.uuid ) {
4964
4965 GtkTreeIter *iter = NULL;
4966 if ( trk->is_route )
4967 iter = g_hash_table_lookup ( vtl->routes_iters, udata.uuid );
4968 else
4969 iter = g_hash_table_lookup ( vtl->tracks_iters, udata.uuid );
4970
4971 if ( iter ) {
4972 // TODO: Make this a function
4973 GdkPixbuf *pixbuf = gdk_pixbuf_new ( GDK_COLORSPACE_RGB, FALSE, 8, 18, 18);
4974 guint32 pixel = ((trk->color.red & 0xff00) << 16) |
4975 ((trk->color.green & 0xff00) << 8) |
4976 (trk->color.blue & 0xff00);
4977 gdk_pixbuf_fill ( pixbuf, pixel );
4978 vik_treeview_item_set_icon ( VIK_LAYER(vtl)->vt, iter, pixbuf );
4979 g_object_unref (pixbuf);
4980 }
4981
4982 }
4983}
4984
6bb72350
RN
4985/*
4986 Parameter 1 -> VikLayersPanel
4987 Parameter 2 -> VikLayer
4988 Parameter 3 -> VikViewport
4989*/
4990static void goto_coord ( gpointer *vlp, gpointer vl, gpointer vvp, const VikCoord *coord )
4991{
4992 if ( vlp ) {
4993 vik_viewport_set_center_coord ( vik_layers_panel_get_viewport (VIK_LAYERS_PANEL(vlp)), coord );
4994 vik_layers_panel_emit_update ( VIK_LAYERS_PANEL(vlp) );
4995 }
4996 else {
4997 /* since vlp not set, vl & vvp should be valid instead! */
4998 if ( vl && vvp ) {
4999 vik_viewport_set_center_coord ( VIK_VIEWPORT(vvp), coord );
da121f9b 5000 vik_layer_emit_update ( VIK_LAYER(vl) );
6bb72350
RN
5001 }
5002 }
50a14534
EB
5003}
5004
19782ffd 5005static void trw_layer_goto_track_startpoint ( menu_array_sublayer values )
50a14534 5006{
19782ffd 5007 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
0d2b891f 5008 VikTrack *track;
19782ffd
RN
5009 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5010 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
0d2b891f 5011 else
19782ffd 5012 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
0d2b891f
RN
5013
5014 if ( track && track->trackpoints )
215ebe59 5015 goto_coord ( values[MA_VLP], vtl, values[MA_VVP], &(vik_track_get_tp_first(track)->coord) );
50a14534
EB
5016}
5017
19782ffd 5018static void trw_layer_goto_track_center ( menu_array_sublayer values )
50a14534 5019{
19782ffd 5020 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
0d2b891f 5021 VikTrack *track;
19782ffd
RN
5022 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5023 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
0d2b891f 5024 else
19782ffd 5025 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
0d2b891f 5026
867f4673 5027 if ( track && track->trackpoints )
50a14534
EB
5028 {
5029 struct LatLon average, maxmin[2] = { {0,0}, {0,0} };
5030 VikCoord coord;
867f4673 5031 trw_layer_find_maxmin_tracks ( NULL, track, maxmin );
50a14534
EB
5032 average.lat = (maxmin[0].lat+maxmin[1].lat)/2;
5033 average.lon = (maxmin[0].lon+maxmin[1].lon)/2;
0d2b891f 5034 vik_coord_load_from_latlon ( &coord, vtl->coord_mode, &average );
19782ffd 5035 goto_coord ( values[MA_VLP], vtl, values[MA_VVP], &coord);
50a14534
EB
5036 }
5037}
5038
19782ffd 5039static void trw_layer_convert_track_route ( menu_array_sublayer values )
2f5d7ea1 5040{
19782ffd 5041 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
2f5d7ea1 5042 VikTrack *trk;
19782ffd
RN
5043 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5044 trk = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
2f5d7ea1 5045 else
19782ffd 5046 trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
2f5d7ea1
RN
5047
5048 if ( !trk )
5049 return;
5050
5051 // Converting a track to a route can be a bit more complicated,
5052 // so give a chance to change our minds:
5053 if ( !trk->is_route &&
5054 ( ( vik_track_get_segment_count ( trk ) > 1 ) ||
5055 ( vik_track_get_average_speed ( trk ) > 0.0 ) ) ) {
5056
5057 if ( ! a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
5058 _("Converting a track to a route removes extra track data such as segments, timestamps, etc...\nDo you want to continue?"), NULL ) )
5059 return;
5060}
5061
5062 // Copy it
03817fbf 5063 VikTrack *trk_copy = vik_track_copy ( trk, TRUE );
2f5d7ea1
RN
5064
5065 // Convert
5066 trk_copy->is_route = !trk_copy->is_route;
5067
5068 // ATM can't set name to self - so must create temporary copy
5069 gchar *name = g_strdup ( trk_copy->name );
5070
5071 // Delete old one and then add new one
5072 if ( trk->is_route ) {
5073 vik_trw_layer_delete_route ( vtl, trk );
5074 vik_trw_layer_add_track ( vtl, name, trk_copy );
5075 }
5076 else {
5077 // Extra route conversion bits...
5078 vik_track_merge_segments ( trk_copy );
5079 vik_track_to_routepoints ( trk_copy );
5080
5081 vik_trw_layer_delete_track ( vtl, trk );
5082 vik_trw_layer_add_route ( vtl, name, trk_copy );
5083 }
5084 g_free ( name );
5085
5086 // Update in case color of track / route changes when moving between sublayers
19782ffd 5087 vik_layer_emit_update ( VIK_LAYER(vtl) );
2f5d7ea1
RN
5088}
5089
19782ffd 5090static void trw_layer_anonymize_times ( menu_array_sublayer values )
76b14439 5091{
19782ffd 5092 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
76b14439 5093 VikTrack *track;
19782ffd
RN
5094 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5095 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
76b14439 5096 else
19782ffd 5097 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
76b14439
RN
5098
5099 if ( track )
5100 vik_track_anonymize_times ( track );
5101}
2f5d7ea1 5102
19782ffd 5103static void trw_layer_extend_track_end ( menu_array_sublayer values )
8fb71d6c 5104{
19782ffd 5105 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
0d2b891f 5106 VikTrack *track;
19782ffd
RN
5107 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5108 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
0d2b891f 5109 else
19782ffd 5110 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
0d2b891f
RN
5111
5112 if ( !track )
5113 return;
8fb71d6c
EB
5114
5115 vtl->current_track = track;
e37b2a6d 5116 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
5117
5118 if ( track->trackpoints )
215ebe59 5119 goto_coord ( values[MA_VLP], vtl, values[MA_VVP], &(vik_track_get_tp_last(track)->coord) );
8fb71d6c
EB
5120}
5121
e3154bef 5122/**
7ff7d728 5123 * extend a track using route finder
e3154bef 5124 */
19782ffd 5125static void trw_layer_extend_track_end_route_finder ( menu_array_sublayer values )
a7955c1d 5126{
19782ffd
RN
5127 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
5128 VikTrack *track = g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
008e972c
RN
5129 if ( !track )
5130 return;
af8abe8b
RN
5131 if ( !track->trackpoints )
5132 return;
a7955c1d 5133
37baade6 5134 vik_window_enable_layer_tool ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)), VIK_LAYER_TRW, TOOL_ROUTE_FINDER );
215ebe59 5135 vtl->route_finder_coord = vik_track_get_tp_last(track)->coord;
7ff7d728
RN
5136 vtl->route_finder_current_track = track;
5137 vtl->route_finder_started = TRUE;
a7955c1d 5138
215ebe59 5139 goto_coord ( values[MA_VLP], vtl, values[MA_VVP], &vtl->route_finder_coord );
a7955c1d 5140}
a7955c1d 5141
4d333042 5142/**
4d333042 5143 *
4d333042 5144 */
667fda15 5145static gboolean trw_layer_dem_test ( VikTrwLayer *vtl, VikLayersPanel *vlp )
4d333042 5146{
73747c42
RN
5147 // If have a vlp then perform a basic test to see if any DEM info available...
5148 if ( vlp ) {
5149 GList *dems = vik_layers_panel_get_all_layers_of_type (vlp, VIK_LAYER_DEM, TRUE); // Includes hidden DEM layer types
5150
5151 if ( !g_list_length(dems) ) {
5152 a_dialog_error_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), _("No DEM layers available, thus no DEM values can be applied.") );
667fda15 5153 return FALSE;
73747c42
RN
5154 }
5155 }
667fda15
RN
5156 return TRUE;
5157}
5158
5159/**
5160 * apply_dem_data_common:
5161 *
5162 * A common function for applying the DEM values and reporting the results.
5163 */
5164static void apply_dem_data_common ( VikTrwLayer *vtl, VikLayersPanel *vlp, VikTrack *track, gboolean skip_existing_elevations )
5165{
5166 if ( !trw_layer_dem_test ( vtl, vlp ) )
5167 return;
73747c42 5168
4d333042
RN
5169 gulong changed = vik_track_apply_dem_data ( track, skip_existing_elevations );
5170 // Inform user how much was changed
5171 gchar str[64];
5172 const gchar *tmp_str = ngettext("%ld point adjusted", "%ld points adjusted", changed);
5173 g_snprintf(str, 64, tmp_str, changed);
5174 a_dialog_info_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), str);
5175}
5176
19782ffd 5177static void trw_layer_apply_dem_data_all ( menu_array_sublayer values )
4d333042 5178{
19782ffd 5179 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
4d333042 5180 VikTrack *track;
19782ffd
RN
5181 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5182 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
4d333042 5183 else
19782ffd 5184 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
4d333042
RN
5185
5186 if ( track )
19782ffd 5187 apply_dem_data_common ( vtl, values[MA_VLP], track, FALSE );
4d333042
RN
5188}
5189
19782ffd 5190static void trw_layer_apply_dem_data_only_missing ( menu_array_sublayer values )
ad0a8c2d 5191{
19782ffd 5192 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
0d2b891f 5193 VikTrack *track;
19782ffd
RN
5194 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5195 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
0d2b891f 5196 else
19782ffd 5197 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
ad0a8c2d 5198
0d2b891f 5199 if ( track )
19782ffd 5200 apply_dem_data_common ( vtl, values[MA_VLP], track, TRUE );
ad0a8c2d
EB
5201}
5202
81ac2835
RN
5203/**
5204 * smooth_it:
5205 *
5206 * A common function for applying the elevation smoothing and reporting the results.
5207 */
5208static void smooth_it ( VikTrwLayer *vtl, VikTrack *track, gboolean flat )
5209{
5210 gulong changed = vik_track_smooth_missing_elevation_data ( track, flat );
5211 // Inform user how much was changed
5212 gchar str[64];
5213 const gchar *tmp_str = ngettext("%ld point adjusted", "%ld points adjusted", changed);
5214 g_snprintf(str, 64, tmp_str, changed);
5215 a_dialog_info_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), str);
5216}
5217
5218/**
5219 *
5220 */
19782ffd 5221static void trw_layer_missing_elevation_data_interp ( menu_array_sublayer values )
81ac2835 5222{
19782ffd 5223 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
81ac2835 5224 VikTrack *track;
19782ffd
RN
5225 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5226 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
81ac2835 5227 else
19782ffd 5228 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
81ac2835
RN
5229
5230 if ( !track )
5231 return;
5232
5233 smooth_it ( vtl, track, FALSE );
5234}
5235
19782ffd 5236static void trw_layer_missing_elevation_data_flat ( menu_array_sublayer values )
81ac2835 5237{
19782ffd 5238 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
81ac2835 5239 VikTrack *track;
19782ffd
RN
5240 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5241 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
81ac2835 5242 else
19782ffd 5243 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
81ac2835
RN
5244
5245 if ( !track )
5246 return;
5247
5248 smooth_it ( vtl, track, TRUE );
5249}
5250
667fda15
RN
5251/**
5252 * Commonal helper function
5253 */
5254static void wp_changed_message ( VikTrwLayer *vtl, gint changed )
5255{
5256 gchar str[64];
5257 const gchar *tmp_str = ngettext("%ld waypoint changed", "%ld waypoints changed", changed);
5258 g_snprintf(str, 64, tmp_str, changed);
5259 a_dialog_info_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), str);
5260}
5261
19782ffd 5262static void trw_layer_apply_dem_data_wpt_all ( menu_array_sublayer values )
667fda15 5263{
19782ffd
RN
5264 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
5265 VikLayersPanel *vlp = (VikLayersPanel *)values[MA_VLP];
667fda15
RN
5266
5267 if ( !trw_layer_dem_test ( vtl, vlp ) )
5268 return;
5269
5270 gint changed = 0;
19782ffd 5271 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_WAYPOINT ) {
667fda15 5272 // Single Waypoint
19782ffd 5273 VikWaypoint *wp = (VikWaypoint *) g_hash_table_lookup ( vtl->waypoints, values[MA_SUBLAYER_ID] );
667fda15
RN
5274 if ( wp )
5275 changed = (gint)vik_waypoint_apply_dem_data ( wp, FALSE );
5276 }
5277 else {
5278 // All waypoints
5279 GHashTableIter iter;
5280 gpointer key, value;
5281
5282 g_hash_table_iter_init ( &iter, vtl->waypoints );
5283 while ( g_hash_table_iter_next (&iter, &key, &value) ) {
5284 VikWaypoint *wp = VIK_WAYPOINT(value);
5285 changed = changed + (gint)vik_waypoint_apply_dem_data ( wp, FALSE );
5286 }
5287 }
5288 wp_changed_message ( vtl, changed );
5289}
5290
19782ffd 5291static void trw_layer_apply_dem_data_wpt_only_missing ( menu_array_sublayer values )
667fda15 5292{
19782ffd
RN
5293 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
5294 VikLayersPanel *vlp = (VikLayersPanel *)values[MA_VLP];
667fda15
RN
5295
5296 if ( !trw_layer_dem_test ( vtl, vlp ) )
5297 return;
5298
5299 gint changed = 0;
19782ffd 5300 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_WAYPOINT ) {
667fda15 5301 // Single Waypoint
19782ffd 5302 VikWaypoint *wp = (VikWaypoint *) g_hash_table_lookup ( vtl->waypoints, values[MA_SUBLAYER_ID] );
667fda15
RN
5303 if ( wp )
5304 changed = (gint)vik_waypoint_apply_dem_data ( wp, TRUE );
5305 }
5306 else {
5307 // All waypoints
5308 GHashTableIter iter;
5309 gpointer key, value;
5310
5311 g_hash_table_iter_init ( &iter, vtl->waypoints );
5312 while ( g_hash_table_iter_next (&iter, &key, &value) ) {
5313 VikWaypoint *wp = VIK_WAYPOINT(value);
5314 changed = changed + (gint)vik_waypoint_apply_dem_data ( wp, TRUE );
5315 }
5316 }
5317 wp_changed_message ( vtl, changed );
5318}
5319
19782ffd 5320static void trw_layer_goto_track_endpoint ( menu_array_sublayer values )
50a14534 5321{
19782ffd 5322 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
0d2b891f 5323 VikTrack *track;
19782ffd
RN
5324 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5325 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
0d2b891f 5326 else
19782ffd 5327 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
0d2b891f
RN
5328
5329 if ( !track )
5330 return;
215ebe59 5331 if ( !track->trackpoints )
50a14534 5332 return;
215ebe59 5333 goto_coord ( values[MA_VLP], vtl, values[MA_VVP], &(vik_track_get_tp_last(track)->coord));
50a14534
EB
5334}
5335
19782ffd 5336static void trw_layer_goto_track_max_speed ( menu_array_sublayer values )
03e7da75 5337{
19782ffd 5338 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
0d2b891f 5339 VikTrack *track;
19782ffd
RN
5340 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5341 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
0d2b891f 5342 else
19782ffd 5343 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
0d2b891f
RN
5344
5345 if ( !track )
5346 return;
5347
5348 VikTrackpoint* vtp = vik_track_get_tp_by_max_speed ( track );
03e7da75
RN
5349 if ( !vtp )
5350 return;
19782ffd 5351 goto_coord ( values[MA_VLP], vtl, values[MA_VVP], &(vtp->coord));
03e7da75
RN
5352}
5353
19782ffd 5354static void trw_layer_goto_track_max_alt ( menu_array_sublayer values )
c28faca8 5355{
19782ffd 5356 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
0d2b891f 5357 VikTrack *track;
19782ffd
RN
5358 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5359 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
0d2b891f 5360 else
19782ffd 5361 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
0d2b891f
RN
5362
5363 if ( !track )
5364 return;
5365
5366 VikTrackpoint* vtp = vik_track_get_tp_by_max_alt ( track );
c28faca8
RN
5367 if ( !vtp )
5368 return;
19782ffd 5369 goto_coord ( values[MA_VLP], vtl, values[MA_VVP], &(vtp->coord));
c28faca8
RN
5370}
5371
19782ffd 5372static void trw_layer_goto_track_min_alt ( menu_array_sublayer values )
c28faca8 5373{
19782ffd 5374 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
0d2b891f 5375 VikTrack *track;
19782ffd
RN
5376 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5377 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
0d2b891f 5378 else
19782ffd 5379 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
0d2b891f
RN
5380
5381 if ( !track )
5382 return;
5383
5384 VikTrackpoint* vtp = vik_track_get_tp_by_min_alt ( track );
c28faca8
RN
5385 if ( !vtp )
5386 return;
19782ffd 5387 goto_coord ( values[MA_VLP], vtl, values[MA_VVP], &(vtp->coord));
c28faca8 5388}
111fa174 5389
116ea336 5390/*
469113fb 5391 * Automatically change the viewport to center on the track and zoom to see the extent of the track
116ea336 5392 */
19782ffd 5393static void trw_layer_auto_track_view ( menu_array_sublayer values )
469113fb 5394{
19782ffd 5395 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
0d2b891f 5396 VikTrack *trk;
19782ffd
RN
5397 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5398 trk = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
0d2b891f 5399 else
19782ffd 5400 trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
0d2b891f 5401
867f4673 5402 if ( trk && trk->trackpoints )
469113fb
RN
5403 {
5404 struct LatLon maxmin[2] = { {0,0}, {0,0} };
867f4673 5405 trw_layer_find_maxmin_tracks ( NULL, trk, maxmin );
19782ffd
RN
5406 trw_layer_zoom_to_show_latlons ( vtl, values[MA_VVP], maxmin );
5407 if ( values[MA_VLP] )
5408 vik_layers_panel_emit_update ( VIK_LAYERS_PANEL(values[MA_VLP]) );
6bb72350 5409 else
11651e51 5410 vik_layer_emit_update ( VIK_LAYER(vtl) );
469113fb
RN
5411 }
5412}
5413
1a3be6a8
GB
5414/*
5415 * Refine the selected track/route with a routing engine.
5416 * The routing engine is selected by the user, when requestiong the job.
5417 */
19782ffd 5418static void trw_layer_route_refine ( menu_array_sublayer values )
1a3be6a8
GB
5419{
5420 static gint last_engine = 0;
19782ffd 5421 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
1a3be6a8
GB
5422 VikTrack *trk;
5423
19782ffd
RN
5424 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
5425 trk = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
1a3be6a8 5426 else
19782ffd 5427 trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
1a3be6a8
GB
5428
5429 if ( trk && trk->trackpoints )
5430 {
80846d50
GB
5431 /* Check size of the route */
5432 int nb = vik_track_get_tp_count(trk);
5433 if (nb > 100) {
5434 GtkWidget *dialog = gtk_message_dialog_new (VIK_GTK_WINDOW_FROM_LAYER (vtl),
5435 GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
5436 GTK_MESSAGE_WARNING,
5437 GTK_BUTTONS_OK_CANCEL,
5438 _("Refining a track with many points (%d) is unlikely to yield sensible results. Do you want to Continue?"),
5439 nb);
5440 gint response = gtk_dialog_run ( GTK_DIALOG(dialog) );
5441 gtk_widget_destroy ( dialog );
5442 if (response != GTK_RESPONSE_OK )
5443 return;
5444 }
1a3be6a8
GB
5445 /* Select engine from dialog */
5446 GtkWidget *dialog = gtk_dialog_new_with_buttons (_("Refine Route with Routing Engine..."),
5447 VIK_GTK_WINDOW_FROM_LAYER (vtl),
5448 GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
5449 GTK_STOCK_CANCEL,
5450 GTK_RESPONSE_REJECT,
5451 GTK_STOCK_OK,
5452 GTK_RESPONSE_ACCEPT,
5453 NULL);
5454 GtkWidget *label = gtk_label_new ( _("Select routing engine") );
5455 gtk_widget_show_all(label);
5456
5457 gtk_box_pack_start ( GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), label, TRUE, TRUE, 0 );
5458
5459 GtkWidget * combo = vik_routing_ui_selector_new ( (Predicate)vik_routing_engine_supports_refine, NULL );
5460 gtk_combo_box_set_active (GTK_COMBO_BOX (combo), last_engine);
5461 gtk_widget_show_all(combo);
5462
5463 gtk_box_pack_start ( GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), combo, TRUE, TRUE, 0 );
5464
5465 gtk_dialog_set_default_response ( GTK_DIALOG(dialog), GTK_RESPONSE_ACCEPT );
5466
5467 if ( gtk_dialog_run ( GTK_DIALOG(dialog) ) == GTK_RESPONSE_ACCEPT )
5468 {
5469 /* Dialog validated: retrieve selected engine and do the job */
5470 last_engine = gtk_combo_box_get_active ( GTK_COMBO_BOX(combo) );
5471 VikRoutingEngine *routing = vik_routing_ui_selector_get_nth (combo, last_engine);
5472
5473 /* Change cursor */
19782ffd 5474 vik_window_set_busy_cursor ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)) );
1a3be6a8
GB
5475
5476 /* Force saving track */
5477 /* FIXME: remove or rename this hack */
5478 vtl->route_finder_check_added_track = TRUE;
5479
5480 /* the job */
5481 vik_routing_engine_refine (routing, vtl, trk);
5482
5483 /* FIXME: remove or rename this hack */
5484 if ( vtl->route_finder_added_track )
5485 vik_track_calculate_bounds ( vtl->route_finder_added_track );
5486
5487 vtl->route_finder_added_track = NULL;
5488 vtl->route_finder_check_added_track = FALSE;
5489
5490 vik_layer_emit_update ( VIK_LAYER(vtl) );
5491
5492 /* Restore cursor */
19782ffd 5493 vik_window_clear_busy_cursor ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)) );
1a3be6a8
GB
5494 }
5495 gtk_widget_destroy ( dialog );
5496 }
5497}
5498
19782ffd 5499static void trw_layer_edit_trackpoint ( menu_array_sublayer values )
c95d6b00 5500{
19782ffd 5501 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
c95d6b00
RN
5502 trw_layer_tpwin_init ( vtl );
5503}
5504
111fa174
AF
5505/*************************************
5506 * merge/split by time routines
5507 *************************************/
5508
15f45edc 5509/* called for each key in track hash table.
5780603d 5510 * If the current track has the same time stamp type, add it to the result,
15f45edc
QT
5511 * except the one pointed by "exclude".
5512 * set exclude to NULL if there is no exclude to check.
5780603d 5513 * Note that the result is in reverse (for performance reasons).
15f45edc
QT
5514 */
5515typedef struct {
5516 GList **result;
5517 GList *exclude;
5780603d 5518 gboolean with_timestamps;
15f45edc 5519} twt_udata;
5780603d 5520static void find_tracks_with_timestamp_type(gpointer key, gpointer value, gpointer udata)
15f45edc
QT
5521{
5522 twt_udata *user_data = udata;
5523 VikTrackpoint *p1, *p2;
215ebe59
RN
5524 VikTrack *trk = VIK_TRACK(value);
5525 if (trk == (VikTrack *)user_data->exclude) {
15f45edc
QT
5526 return;
5527 }
5528
215ebe59
RN
5529 if (trk->trackpoints) {
5530 p1 = vik_track_get_tp_first(trk);
5531 p2 = vik_track_get_tp_last(trk);
15f45edc 5532
5780603d
RN
5533 if ( user_data->with_timestamps ) {
5534 if (!p1->has_timestamp || !p2->has_timestamp) {
5535 return;
5536 }
5537 }
5538 else {
5539 // Don't add tracks with timestamps when getting non timestamp tracks
5540 if (p1->has_timestamp || p2->has_timestamp) {
5541 return;
5542 }
15f45edc 5543 }
15f45edc
QT
5544 }
5545
5546 *(user_data->result) = g_list_prepend(*(user_data->result), key);
5547}
5548
111fa174
AF
5549/* called for each key in track hash table. if original track user_data[1] is close enough
5550 * to the passed one, add it to list in user_data[0]
5551 */
fc92c977 5552static void find_nearby_tracks_by_time (gpointer key, gpointer value, gpointer user_data)
111fa174
AF
5553{
5554 time_t t1, t2;
5555 VikTrackpoint *p1, *p2;
fc92c977 5556 VikTrack *trk = VIK_TRACK(value);
111fa174
AF
5557
5558 GList **nearby_tracks = ((gpointer *)user_data)[0];
fc92c977 5559 GList *tpoints = ((gpointer *)user_data)[1];
111fa174 5560
a61b2619
AF
5561 /* outline:
5562 * detect reasons for not merging, and return
5563 * if no reason is found not to merge, then do it.
5564 */
111fa174 5565
fc92c977
RN
5566 // Exclude the original track from the compiled list
5567 if (trk->trackpoints == tpoints) {
111fa174
AF
5568 return;
5569 }
5570
215ebe59
RN
5571 t1 = vik_track_get_tp_first(trk)->timestamp;
5572 t2 = vik_track_get_tp_last(trk)->timestamp;
111fa174 5573
fc92c977 5574 if (trk->trackpoints) {
215ebe59
RN
5575 p1 = vik_track_get_tp_first(trk);
5576 p2 = vik_track_get_tp_last(trk);
a61b2619
AF
5577
5578 if (!p1->has_timestamp || !p2->has_timestamp) {
fc92c977 5579 //g_print("no timestamp\n");
a61b2619
AF
5580 return;
5581 }
111fa174 5582
fc92c977
RN
5583 guint threshold = GPOINTER_TO_UINT (((gpointer *)user_data)[2]);
5584 //g_print("Got track named %s, times %d, %d\n", trk->name, p1->timestamp, p2->timestamp);
5585 if (! (abs(t1 - p2->timestamp) < threshold ||
a61b2619 5586 /* p1 p2 t1 t2 */
fc92c977 5587 abs(p1->timestamp - t2) < threshold)
a61b2619
AF
5588 /* t1 t2 p1 p2 */
5589 ) {
5590 return;
5591 }
111fa174 5592 }
a61b2619 5593
fc92c977 5594 *nearby_tracks = g_list_prepend(*nearby_tracks, value);
111fa174
AF
5595}
5596
5597/* comparison function used to sort tracks; a and b are hash table keys */
02b5d347 5598/* Not actively used - can be restored if needed
111fa174
AF
5599static gint track_compare(gconstpointer a, gconstpointer b, gpointer user_data)
5600{
5601 GHashTable *tracks = user_data;
5602 time_t t1, t2;
5603
5604 t1 = VIK_TRACKPOINT(VIK_TRACK(g_hash_table_lookup(tracks, a))->trackpoints->data)->timestamp;
5605 t2 = VIK_TRACKPOINT(VIK_TRACK(g_hash_table_lookup(tracks, b))->trackpoints->data)->timestamp;
5606
5607 if (t1 < t2) return -1;
5608 if (t1 > t2) return 1;
5609 return 0;
5610}
02b5d347 5611*/
111fa174
AF
5612
5613/* comparison function used to sort trackpoints */
5614static gint trackpoint_compare(gconstpointer a, gconstpointer b)
5615{
5616 time_t t1 = VIK_TRACKPOINT(a)->timestamp, t2 = VIK_TRACKPOINT(b)->timestamp;
5617
5618 if (t1 < t2) return -1;
5619 if (t1 > t2) return 1;
5620 return 0;
5621}
5622
fb2306f7
RN
5623/**
5624 * comparison function which can be used to sort tracks or waypoints by name
5625 */
5626static gint sort_alphabetically (gconstpointer a, gconstpointer b, gpointer user_data)
5627{
5628 const gchar* namea = (const gchar*) a;
5629 const gchar* nameb = (const gchar*) b;
5630 if ( namea == NULL || nameb == NULL)
5631 return 0;
5632 else
5633 // Same sort method as used in the vik_treeview_*_alphabetize functions
5634 return strcmp ( namea, nameb );
5635}
fb2306f7 5636
5780603d
RN
5637/**
5638 * Attempt to merge selected track with other tracks specified by the user
5639 * Tracks to merge with must be of the same 'type' as the selected track -
5640 * either all with timestamps, or all without timestamps
5641 */
19782ffd 5642static void trw_layer_merge_with_other ( menu_array_sublayer values )
291edcab 5643{
19782ffd 5644 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
5780603d 5645 GList *other_tracks = NULL;
0d2b891f 5646 GHashTable *ght_tracks;
19782ffd 5647 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
0d2b891f
RN
5648 ght_tracks = vtl->routes;
5649 else
5650 ght_tracks = vtl->tracks;
5651
19782ffd 5652 VikTrack *track = (VikTrack *) g_hash_table_lookup ( ght_tracks, values[MA_SUBLAYER_ID] );
0d2b891f
RN
5653
5654 if ( !track )
5655 return;
15f45edc 5656
5780603d 5657 if ( !track->trackpoints )
15f45edc 5658 return;
15f45edc 5659
c1564279 5660 twt_udata udata;
5780603d 5661 udata.result = &other_tracks;
c1564279 5662 udata.exclude = track->trackpoints;
5780603d
RN
5663 // Allow merging with 'similar' time type time tracks
5664 // i.e. either those times, or those without
215ebe59 5665 udata.with_timestamps = vik_track_get_tp_first(track)->has_timestamp;
291edcab 5666
0d2b891f 5667 g_hash_table_foreach(ght_tracks, find_tracks_with_timestamp_type, (gpointer)&udata);
5780603d
RN
5668 other_tracks = g_list_reverse(other_tracks);
5669
5670 if ( !other_tracks ) {
5671 if ( udata.with_timestamps )
5672 a_dialog_error_msg(VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Failed. No other tracks with timestamps in this layer found"));
5673 else
5674 a_dialog_error_msg(VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Failed. No other tracks without timestamps in this layer found"));
15f45edc
QT
5675 return;
5676 }
5677
8352326e 5678 // Sort alphabetically for user presentation
ce4bd1cf
RN
5679 // Convert into list of names for usage with dialog function
5680 // TODO: Need to consider how to work best when we can have multiple tracks the same name...
5681 GList *other_tracks_names = NULL;
5682 GList *iter = g_list_first ( other_tracks );
5683 while ( iter ) {
0d2b891f 5684 other_tracks_names = g_list_append ( other_tracks_names, VIK_TRACK(g_hash_table_lookup (ght_tracks, iter->data))->name );
ce4bd1cf
RN
5685 iter = g_list_next ( iter );
5686 }
5687
5688 other_tracks_names = g_list_sort_with_data (other_tracks_names, sort_alphabetically, NULL);
8352326e 5689
7767aa02 5690 GList *merge_list = a_dialog_select_from_list(VIK_GTK_WINDOW_FROM_LAYER(vtl),
0d2b891f
RN
5691 other_tracks_names,
5692 TRUE,
5693 _("Merge with..."),
5694 track->is_route ? _("Select route to merge with") : _("Select track to merge with"));
5780603d 5695 g_list_free(other_tracks);
ce4bd1cf 5696 g_list_free(other_tracks_names);
291edcab 5697
7767aa02 5698 if (merge_list)
291edcab 5699 {
7767aa02
QT
5700 GList *l;
5701 for (l = merge_list; l != NULL; l = g_list_next(l)) {
0d2b891f
RN
5702 VikTrack *merge_track;
5703 if ( track->is_route )
5704 merge_track = vik_trw_layer_get_route ( vtl, l->data );
5705 else
5706 merge_track = vik_trw_layer_get_track ( vtl, l->data );
5707
7767aa02 5708 if (merge_track) {
0a9ed7dc 5709 vik_track_steal_and_append_trackpoints ( track, merge_track );
0d2b891f
RN
5710 if ( track->is_route )
5711 vik_trw_layer_delete_route (vtl, merge_track);
5712 else
5713 vik_trw_layer_delete_track (vtl, merge_track);
7767aa02
QT
5714 track->trackpoints = g_list_sort(track->trackpoints, trackpoint_compare);
5715 }
291edcab 5716 }
7767aa02
QT
5717 for (l = merge_list; l != NULL; l = g_list_next(l))
5718 g_free(l->data);
5719 g_list_free(merge_list);
20981fd6 5720
da121f9b 5721 vik_layer_emit_update( VIK_LAYER(vtl) );
291edcab 5722 }
291edcab
HR
5723}
5724
9c34f614
RN
5725// c.f. trw_layer_sorted_track_id_by_name_list
5726// but don't add the specified track to the list (normally current track)
5727static void trw_layer_sorted_track_id_by_name_list_exclude_self (const gpointer id, const VikTrack *trk, gpointer udata)
5728{
5729 twt_udata *user_data = udata;
5730
5731 // Skip self
5732 if (trk->trackpoints == user_data->exclude) {
5733 return;
5734 }
5735
5736 // Sort named list alphabetically
5737 *(user_data->result) = g_list_insert_sorted_with_data (*(user_data->result), trk->name, sort_alphabetically, NULL);
5738}
5739
5740/**
0d2b891f 5741 * Join - this allows combining 'tracks' and 'track routes'
9c34f614
RN
5742 * i.e. doesn't care about whether tracks have consistent timestamps
5743 * ATM can only append one track at a time to the currently selected track
5744 */
19782ffd 5745static void trw_layer_append_track ( menu_array_sublayer values )
9c34f614
RN
5746{
5747
19782ffd 5748 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
0d2b891f
RN
5749 VikTrack *trk;
5750 GHashTable *ght_tracks;
19782ffd 5751 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
0d2b891f
RN
5752 ght_tracks = vtl->routes;
5753 else
5754 ght_tracks = vtl->tracks;
5755
19782ffd 5756 trk = (VikTrack *) g_hash_table_lookup ( ght_tracks, values[MA_SUBLAYER_ID] );
0d2b891f
RN
5757
5758 if ( !trk )
5759 return;
9c34f614
RN
5760
5761 GList *other_tracks_names = NULL;
5762
5763 // Sort alphabetically for user presentation
5764 // Convert into list of names for usage with dialog function
5765 // TODO: Need to consider how to work best when we can have multiple tracks the same name...
5766 twt_udata udata;
5767 udata.result = &other_tracks_names;
5768 udata.exclude = trk->trackpoints;
5769
0d2b891f 5770 g_hash_table_foreach(ght_tracks, (GHFunc) trw_layer_sorted_track_id_by_name_list_exclude_self, (gpointer)&udata);
9c34f614
RN
5771
5772 // Note the limit to selecting one track only
5773 // this is to control the ordering of appending tracks, i.e. the selected track always goes after the current track
5774 // (otherwise with multiple select the ordering would not be controllable by the user - automatically being alphabetically)
5775 GList *append_list = a_dialog_select_from_list(VIK_GTK_WINDOW_FROM_LAYER(vtl),
0d2b891f
RN
5776 other_tracks_names,
5777 FALSE,
5778 trk->is_route ? _("Append Route"): _("Append Track"),
5779 trk->is_route ? _("Select the route to append after the current route") :
5780 _("Select the track to append after the current track") );
9c34f614
RN
5781
5782 g_list_free(other_tracks_names);
5783
5784 // It's a list, but shouldn't contain more than one other track!
5785 if ( append_list ) {
5786 GList *l;
5787 for (l = append_list; l != NULL; l = g_list_next(l)) {
5788 // TODO: at present this uses the first track found by name,
5789 // which with potential multiple same named tracks may not be the one selected...
0d2b891f
RN
5790 VikTrack *append_track;
5791 if ( trk->is_route )
5792 append_track = vik_trw_layer_get_route ( vtl, l->data );
5793 else
5794 append_track = vik_trw_layer_get_track ( vtl, l->data );
5795
9c34f614 5796 if ( append_track ) {
0a9ed7dc 5797 vik_track_steal_and_append_trackpoints ( trk, append_track );
0d2b891f
RN
5798 if ( trk->is_route )
5799 vik_trw_layer_delete_route (vtl, append_track);
5800 else
5801 vik_trw_layer_delete_track (vtl, append_track);
9c34f614
RN
5802 }
5803 }
5804 for (l = append_list; l != NULL; l = g_list_next(l))
5805 g_free(l->data);
5806 g_list_free(append_list);
20981fd6 5807
da121f9b 5808 vik_layer_emit_update( VIK_LAYER(vtl) );
9c34f614
RN
5809 }
5810}
5811
6b5b6c47
RN
5812/**
5813 * Very similar to trw_layer_append_track for joining
5814 * but this allows selection from the 'other' list
5815 * If a track is selected, then is shows routes and joins the selected one
5816 * If a route is selected, then is shows tracks and joins the selected one
5817 */
19782ffd 5818static void trw_layer_append_other ( menu_array_sublayer values )
6b5b6c47
RN
5819{
5820
19782ffd 5821 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
6b5b6c47
RN
5822 VikTrack *trk;
5823 GHashTable *ght_mykind, *ght_others;
19782ffd 5824 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE ) {
6b5b6c47
RN
5825 ght_mykind = vtl->routes;
5826 ght_others = vtl->tracks;
5827 }
5828 else {
5829 ght_mykind = vtl->tracks;
5830 ght_others = vtl->routes;
5831 }
5832
19782ffd 5833 trk = (VikTrack *) g_hash_table_lookup ( ght_mykind, values[MA_SUBLAYER_ID] );
6b5b6c47
RN
5834
5835 if ( !trk )
5836 return;
5837
5838 GList *other_tracks_names = NULL;
5839
5840 // Sort alphabetically for user presentation
5841 // Convert into list of names for usage with dialog function
5842 // TODO: Need to consider how to work best when we can have multiple tracks the same name...
5843 twt_udata udata;
5844 udata.result = &other_tracks_names;
5845 udata.exclude = trk->trackpoints;
5846
5847 g_hash_table_foreach(ght_others, (GHFunc) trw_layer_sorted_track_id_by_name_list_exclude_self, (gpointer)&udata);
5848
5849 // Note the limit to selecting one track only
5850 // this is to control the ordering of appending tracks, i.e. the selected track always goes after the current track
5851 // (otherwise with multiple select the ordering would not be controllable by the user - automatically being alphabetically)
5852 GList *append_list = a_dialog_select_from_list(VIK_GTK_WINDOW_FROM_LAYER(vtl),
5853 other_tracks_names,
5854 FALSE,
5855 trk->is_route ? _("Append Track"): _("Append Route"),
5856 trk->is_route ? _("Select the track to append after the current route") :
5857 _("Select the route to append after the current track") );
5858
5859 g_list_free(other_tracks_names);
5860
5861 // It's a list, but shouldn't contain more than one other track!
5862 if ( append_list ) {
5863 GList *l;
5864 for (l = append_list; l != NULL; l = g_list_next(l)) {
5865 // TODO: at present this uses the first track found by name,
5866 // which with potential multiple same named tracks may not be the one selected...
5867
5868 // Get FROM THE OTHER TYPE list
5869 VikTrack *append_track;
5870 if ( trk->is_route )
5871 append_track = vik_trw_layer_get_track ( vtl, l->data );
5872 else
5873 append_track = vik_trw_layer_get_route ( vtl, l->data );
5874
5875 if ( append_track ) {
5876
5877 if ( !append_track->is_route &&
5878 ( ( vik_track_get_segment_count ( append_track ) > 1 ) ||
5879 ( vik_track_get_average_speed ( append_track ) > 0.0 ) ) ) {
5880
5881 if ( a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
5882 _("Converting a track to a route removes extra track data such as segments, timestamps, etc...\nDo you want to continue?"), NULL ) ) {
5883 vik_track_merge_segments ( append_track );
5884 vik_track_to_routepoints ( append_track );
5885 }
5886 else {
5887 break;
5888 }
5889 }
5890
0a9ed7dc 5891 vik_track_steal_and_append_trackpoints ( trk, append_track );
6b5b6c47
RN
5892
5893 // Delete copied which is FROM THE OTHER TYPE list
5894 if ( trk->is_route )
5895 vik_trw_layer_delete_track (vtl, append_track);
5896 else
5897 vik_trw_layer_delete_route (vtl, append_track);
5898 }
5899 }
5900 for (l = append_list; l != NULL; l = g_list_next(l))
5901 g_free(l->data);
5902 g_list_free(append_list);
da121f9b 5903 vik_layer_emit_update( VIK_LAYER(vtl) );
6b5b6c47
RN
5904 }
5905}
5906
24774c43 5907/* merge by segments */
19782ffd 5908static void trw_layer_merge_by_segment ( menu_array_sublayer values )
24774c43 5909{
19782ffd
RN
5910 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
5911 VikTrack *trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
24774c43
RN
5912 guint segments = vik_track_merge_segments ( trk );
5913 // NB currently no need to redraw as segments not actually shown on the display
5914 // However inform the user of what happened:
5915 gchar str[64];
5916 const gchar *tmp_str = ngettext("%d segment merged", "%d segments merged", segments);
5917 g_snprintf(str, 64, tmp_str, segments);
5918 a_dialog_info_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), str );
5919}
5920
111fa174 5921/* merge by time routine */
19782ffd 5922static void trw_layer_merge_by_timestamp ( menu_array_sublayer values )
111fa174 5923{
19782ffd 5924 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
a61b2619 5925
02b5d347 5926 //time_t t1, t2;
111fa174 5927
c1564279 5928 GList *tracks_with_timestamp = NULL;
19782ffd 5929 VikTrack *orig_trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
ce4bd1cf 5930 if (orig_trk->trackpoints &&
215ebe59 5931 !vik_track_get_tp_first(orig_trk)->has_timestamp) {
c1564279 5932 a_dialog_error_msg(VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Failed. This track does not have timestamp"));
c1564279
RN
5933 return;
5934 }
5935
5936 twt_udata udata;
5937 udata.result = &tracks_with_timestamp;
ce4bd1cf 5938 udata.exclude = orig_trk->trackpoints;
5780603d
RN
5939 udata.with_timestamps = TRUE;
5940 g_hash_table_foreach(vtl->tracks, find_tracks_with_timestamp_type, (gpointer)&udata);
c1564279
RN
5941 tracks_with_timestamp = g_list_reverse(tracks_with_timestamp);
5942
5943 if (!tracks_with_timestamp) {
5944 a_dialog_error_msg(VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Failed. No other track in this layer has timestamp"));
c1564279
RN
5945 return;
5946 }
5947 g_list_free(tracks_with_timestamp);
5948
fc92c977
RN
5949 static guint threshold_in_minutes = 1;
5950 if (!a_dialog_time_threshold(VIK_GTK_WINDOW_FROM_LAYER(vtl),
5951 _("Merge Threshold..."),
5952 _("Merge when time between tracks less than:"),
5953 &threshold_in_minutes)) {
111fa174
AF
5954 return;
5955 }
5956
fc92c977
RN
5957 // keep attempting to merge all tracks until no merges within the time specified is possible
5958 gboolean attempt_merge = TRUE;
5959 GList *nearby_tracks = NULL;
5960 GList *trps;
5961 static gpointer params[3];
5962
5963 while ( attempt_merge ) {
5964
5965 // Don't try again unless tracks have changed
5966 attempt_merge = FALSE;
111fa174 5967
ce4bd1cf 5968 trps = orig_trk->trackpoints;
111fa174
AF
5969 if ( !trps )
5970 return;
5971
111fa174
AF
5972 if (nearby_tracks) {
5973 g_list_free(nearby_tracks);
5974 nearby_tracks = NULL;
5975 }
5976
111fa174 5977 params[0] = &nearby_tracks;
fc92c977
RN
5978 params[1] = (gpointer)trps;
5979 params[2] = GUINT_TO_POINTER (threshold_in_minutes*60); // In seconds
111fa174
AF
5980
5981 /* get a list of adjacent-in-time tracks */
fc92c977 5982 g_hash_table_foreach(vtl->tracks, find_nearby_tracks_by_time, params);
111fa174 5983
111fa174 5984 /* merge them */
fc92c977
RN
5985 GList *l = nearby_tracks;
5986 while ( l ) {
fc92c977 5987 /* remove trackpoints from merged track, delete track */
0a9ed7dc 5988 vik_track_steal_and_append_trackpoints ( orig_trk, VIK_TRACK(l->data) );
fc92c977 5989 vik_trw_layer_delete_track (vtl, VIK_TRACK(l->data));
111fa174 5990
fc92c977
RN
5991 // Tracks have changed, therefore retry again against all the remaining tracks
5992 attempt_merge = TRUE;
ce4bd1cf 5993
fc92c977 5994 l = g_list_next(l);
111fa174 5995 }
fc92c977
RN
5996
5997 orig_trk->trackpoints = g_list_sort(orig_trk->trackpoints, trackpoint_compare);
5998 }
5999
111fa174 6000 g_list_free(nearby_tracks);
20981fd6 6001
da121f9b 6002 vik_layer_emit_update( VIK_LAYER(vtl) );
111fa174
AF
6003}
6004
84d3d9f9 6005/**
9914238e 6006 * Split a track at the currently selected trackpoint
84d3d9f9 6007 */
0d2b891f 6008static void trw_layer_split_at_selected_trackpoint ( VikTrwLayer *vtl, gint subtype )
84d3d9f9
RN
6009{
6010 if ( !vtl->current_tpl )
6011 return;
6012
6013 if ( vtl->current_tpl->next && vtl->current_tpl->prev ) {
0d2b891f 6014 gchar *name = trw_layer_new_unique_sublayer_name(vtl, subtype, vtl->current_tp_track->name);
9914238e 6015 if ( name ) {
03817fbf 6016 VikTrack *tr = vik_track_copy ( vtl->current_tp_track, FALSE );
84d3d9f9
RN
6017 GList *newglist = g_list_alloc ();
6018 newglist->prev = NULL;
6019 newglist->next = vtl->current_tpl->next;
6020 newglist->data = vik_trackpoint_copy(VIK_TRACKPOINT(vtl->current_tpl->data));
6021 tr->trackpoints = newglist;
6022
6023 vtl->current_tpl->next->prev = newglist; /* end old track here */
6024 vtl->current_tpl->next = NULL;
6025
20981fd6
RN
6026 // Bounds of the selected track changed due to the split
6027 vik_track_calculate_bounds ( vtl->current_tp_track );
6028
84d3d9f9
RN
6029 vtl->current_tpl = newglist; /* change tp to first of new track. */
6030 vtl->current_tp_track = tr;
6031
0d2b891f
RN
6032 if ( tr->is_route )
6033 vik_trw_layer_add_route ( vtl, name, tr );
6034 else
6035 vik_trw_layer_add_track ( vtl, name, tr );
84d3d9f9 6036
20981fd6
RN
6037 // Bounds of the new track created by the split
6038 vik_track_calculate_bounds ( tr );
6039
84d3d9f9
RN
6040 trku_udata udata;
6041 udata.trk = tr;
6042 udata.uuid = NULL;
6043
6044 // Also need id of newly created track
0d2b891f
RN
6045 gpointer *trkf;
6046 if ( tr->is_route )
6047 trkf = g_hash_table_find ( vtl->routes, (GHRFunc) trw_layer_track_find_uuid, &udata );
6048 else
6049 trkf = g_hash_table_find ( vtl->tracks, (GHRFunc) trw_layer_track_find_uuid, &udata );
6050
84d3d9f9
RN
6051 if ( trkf && udata.uuid )
6052 vtl->current_tp_id = udata.uuid;
6053 else
6054 vtl->current_tp_id = NULL;
6055
da121f9b 6056 vik_layer_emit_update(VIK_LAYER(vtl));
84d3d9f9 6057 }
1613e468 6058 g_free ( name );
84d3d9f9
RN
6059 }
6060}
6061
111fa174 6062/* split by time routine */
19782ffd 6063static void trw_layer_split_by_timestamp ( menu_array_sublayer values )
111fa174 6064{
19782ffd
RN
6065 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
6066 VikTrack *track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
111fa174
AF
6067 GList *trps = track->trackpoints;
6068 GList *iter;
6069 GList *newlists = NULL;
6070 GList *newtps = NULL;
111fa174
AF
6071 static guint thr = 1;
6072
6073 time_t ts, prev_ts;
6074
6075 if ( !trps )
6076 return;
6077
19782ffd 6078 if (!a_dialog_time_threshold(VIK_GTK_WINDOW_FROM_LAYER(vtl),
4c77d5e0
GB
6079 _("Split Threshold..."),
6080 _("Split when time between trackpoints exceeds:"),
111fa174
AF
6081 &thr)) {
6082 return;
6083 }
6084
6085 /* iterate through trackpoints, and copy them into new lists without touching original list */
6086 prev_ts = VIK_TRACKPOINT(trps->data)->timestamp;
6087 iter = trps;
6088
6089 while (iter) {
6090 ts = VIK_TRACKPOINT(iter->data)->timestamp;
1e3b44c4
RN
6091
6092 // Check for unordered time points - this is quite a rare occurence - unless one has reversed a track.
111fa174 6093 if (ts < prev_ts) {
1e3b44c4
RN
6094 gchar tmp_str[64];
6095 strftime ( tmp_str, sizeof(tmp_str), "%c", localtime(&ts) );
6096 if ( a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
6097 _("Can not split track due to trackpoints not ordered in time - such as at %s.\n\nGoto this trackpoint?"),
6098 tmp_str ) ) {
19782ffd 6099 goto_coord ( values[MA_VLP], vtl, values[MA_VVP], &(VIK_TRACKPOINT(iter->data)->coord) );
1e3b44c4 6100 }
111fa174
AF
6101 return;
6102 }
1e3b44c4 6103
111fa174
AF
6104 if (ts - prev_ts > thr*60) {
6105 /* flush accumulated trackpoints into new list */
aa9887a1 6106 newlists = g_list_append(newlists, g_list_reverse(newtps));
111fa174
AF
6107 newtps = NULL;
6108 }
6109
6110 /* accumulate trackpoint copies in newtps, in reverse order */
6111 newtps = g_list_prepend(newtps, vik_trackpoint_copy(VIK_TRACKPOINT(iter->data)));
6112 prev_ts = ts;
6113 iter = g_list_next(iter);
6114 }
6115 if (newtps) {
aa9887a1 6116 newlists = g_list_append(newlists, g_list_reverse(newtps));
111fa174
AF
6117 }
6118
6119 /* put lists of trackpoints into tracks */
6120 iter = newlists;
c9d8f273
RN
6121 // Only bother updating if the split results in new tracks
6122 if (g_list_length (newlists) > 1) {
6123 while (iter) {
6124 gchar *new_tr_name;
6125 VikTrack *tr;
111fa174 6126
03817fbf 6127 tr = vik_track_copy ( track, FALSE );
c9d8f273 6128 tr->trackpoints = (GList *)(iter->data);
111fa174 6129
784b6042
RN
6130 new_tr_name = trw_layer_new_unique_sublayer_name ( vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, track->name);
6131 vik_trw_layer_add_track(vtl, new_tr_name, tr);
1613e468 6132 g_free ( new_tr_name );
20981fd6 6133 vik_track_calculate_bounds ( tr );
c9d8f273
RN
6134 iter = g_list_next(iter);
6135 }
ce4bd1cf 6136 // Remove original track and then update the display
784b6042 6137 vik_trw_layer_delete_track (vtl, track);
19782ffd 6138 vik_layer_emit_update(VIK_LAYER(vtl));
111fa174
AF
6139 }
6140 g_list_free(newlists);
111fa174
AF
6141}
6142
af2341f3
RN
6143/**
6144 * Split a track by the number of points as specified by the user
6145 */
19782ffd 6146static void trw_layer_split_by_n_points ( menu_array_sublayer values )
af2341f3 6147{
19782ffd 6148 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
0d2b891f 6149 VikTrack *track;
19782ffd
RN
6150 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
6151 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
0d2b891f 6152 else
19782ffd 6153 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
0d2b891f
RN
6154
6155 if ( !track )
6156 return;
af2341f3
RN
6157
6158 // Check valid track
6159 GList *trps = track->trackpoints;
6160 if ( !trps )
6161 return;
6162
19782ffd 6163 gint points = a_dialog_get_positive_number(VIK_GTK_WINDOW_FROM_LAYER(vtl),
af2341f3
RN
6164 _("Split Every Nth Point"),
6165 _("Split on every Nth point:"),
6166 250, // Default value as per typical limited track capacity of various GPS devices
6167 2, // Min
6168 65536, // Max
6169 5); // Step
6170 // Was a valid number returned?
6171 if (!points)
6172 return;
6173
6174 // Now split...
6175 GList *iter;
6176 GList *newlists = NULL;
6177 GList *newtps = NULL;
6178 gint count = 0;
6179 iter = trps;
6180
6181 while (iter) {
6182 /* accumulate trackpoint copies in newtps, in reverse order */
6183 newtps = g_list_prepend(newtps, vik_trackpoint_copy(VIK_TRACKPOINT(iter->data)));
6184 count++;
6185 if (count >= points) {
6186 /* flush accumulated trackpoints into new list */
6187 newlists = g_list_append(newlists, g_list_reverse(newtps));
6188 newtps = NULL;
6189 count = 0;
6190 }
6191 iter = g_list_next(iter);
6192 }
6193
6194 // If there is a remaining chunk put that into the new split list
6195 // This may well be the whole track if no split points were encountered
6196 if (newtps) {
6197 newlists = g_list_append(newlists, g_list_reverse(newtps));
6198 }
6199
6200 /* put lists of trackpoints into tracks */
6201 iter = newlists;
af2341f3
RN
6202 // Only bother updating if the split results in new tracks
6203 if (g_list_length (newlists) > 1) {
6204 while (iter) {
6205 gchar *new_tr_name;
6206 VikTrack *tr;
6207
03817fbf 6208 tr = vik_track_copy ( track, FALSE );
af2341f3
RN
6209 tr->trackpoints = (GList *)(iter->data);
6210
0d2b891f
RN
6211 if ( track->is_route ) {
6212 new_tr_name = trw_layer_new_unique_sublayer_name ( vtl, VIK_TRW_LAYER_SUBLAYER_ROUTE, track->name);
6213 vik_trw_layer_add_route(vtl, new_tr_name, tr);
6214 }
6215 else {
6216 new_tr_name = trw_layer_new_unique_sublayer_name ( vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, track->name);
6217 vik_trw_layer_add_track(vtl, new_tr_name, tr);
6218 }
1613e468 6219 g_free ( new_tr_name );
20981fd6
RN
6220 vik_track_calculate_bounds ( tr );
6221
af2341f3
RN
6222 iter = g_list_next(iter);
6223 }
6224 // Remove original track and then update the display
0d2b891f
RN
6225 if ( track->is_route )
6226 vik_trw_layer_delete_route (vtl, track);
6227 else
6228 vik_trw_layer_delete_track (vtl, track);
19782ffd 6229 vik_layer_emit_update(VIK_LAYER(vtl));
af2341f3
RN
6230 }
6231 g_list_free(newlists);
6232}
6233
a4aefe69
RN
6234/**
6235 * Split a track at the currently selected trackpoint
6236 */
19782ffd 6237static void trw_layer_split_at_trackpoint ( menu_array_sublayer values )
a4aefe69 6238{
19782ffd
RN
6239 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
6240 gint subtype = GPOINTER_TO_INT (values[MA_SUBTYPE]);
0d2b891f 6241 trw_layer_split_at_selected_trackpoint ( vtl, subtype );
a4aefe69 6242}
98e87078
RN
6243
6244/**
6245 * Split a track by its segments
0d2b891f 6246 * Routes do not have segments so don't call this for routes
98e87078 6247 */
19782ffd 6248static void trw_layer_split_segments ( menu_array_sublayer values )
98e87078 6249{
19782ffd
RN
6250 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
6251 VikTrack *trk = g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
0d2b891f
RN
6252
6253 if ( !trk )
6254 return;
6255
98e87078
RN
6256 guint ntracks;
6257
6258 VikTrack **tracks = vik_track_split_into_segments (trk, &ntracks);
6259 gchar *new_tr_name;
6260 guint i;
6261 for ( i = 0; i < ntracks; i++ ) {
6262 if ( tracks[i] ) {
6263 new_tr_name = trw_layer_new_unique_sublayer_name ( vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, trk->name);
6264 vik_trw_layer_add_track ( vtl, new_tr_name, tracks[i] );
1613e468 6265 g_free ( new_tr_name );
98e87078
RN
6266 }
6267 }
6268 if ( tracks ) {
6269 g_free ( tracks );
6270 // Remove original track
6271 vik_trw_layer_delete_track ( vtl, trk );
da121f9b 6272 vik_layer_emit_update ( VIK_LAYER(vtl) );
98e87078
RN
6273 }
6274 else {
6275 a_dialog_error_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), _("Can not split track as it has no segments"));
6276 }
6277}
111fa174
AF
6278/* end of split/merge routines */
6279
a56baa08
RN
6280static void trw_layer_trackpoint_selected_delete ( VikTrwLayer *vtl, VikTrack *trk )
6281{
6282 GList *new_tpl;
6283
6284 // Find available adjacent trackpoint
6285 if ( (new_tpl = vtl->current_tpl->next) || (new_tpl = vtl->current_tpl->prev) ) {
6286 if ( VIK_TRACKPOINT(vtl->current_tpl->data)->newsegment && vtl->current_tpl->next )
6287 VIK_TRACKPOINT(vtl->current_tpl->next->data)->newsegment = TRUE; /* don't concat segments on del */
6288
6289 // Delete current trackpoint
6290 vik_trackpoint_free ( vtl->current_tpl->data );
6291 trk->trackpoints = g_list_delete_link ( trk->trackpoints, vtl->current_tpl );
6292
6293 // Set to current to the available adjacent trackpoint
6294 vtl->current_tpl = new_tpl;
6295
6296 if ( vtl->current_tp_track ) {
6297 vik_track_calculate_bounds ( vtl->current_tp_track );
6298 }
6299 }
6300 else {
6301 // Delete current trackpoint
6302 vik_trackpoint_free ( vtl->current_tpl->data );
6303 trk->trackpoints = g_list_delete_link ( trk->trackpoints, vtl->current_tpl );
6304 trw_layer_cancel_current_tp ( vtl, FALSE );
6305 }
6306}
6307
6308/**
6309 * Delete the selected point
6310 */
19782ffd 6311static void trw_layer_delete_point_selected ( menu_array_sublayer values )
a56baa08 6312{
19782ffd 6313 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
a56baa08 6314 VikTrack *trk;
19782ffd
RN
6315 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
6316 trk = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
a56baa08 6317 else
19782ffd 6318 trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
a56baa08
RN
6319
6320 if ( !trk )
6321 return;
6322
6323 if ( !vtl->current_tpl )
6324 return;
6325
6326 trw_layer_trackpoint_selected_delete ( vtl, trk );
6327
6328 // Track has been updated so update tps:
6329 trw_layer_cancel_tps_of_track ( vtl, trk );
6330
6331 vik_layer_emit_update ( VIK_LAYER(vtl) );
6332}
6333
b6eda120
RN
6334/**
6335 * Delete adjacent track points at the same position
6336 * AKA Delete Dulplicates on the Properties Window
6337 */
19782ffd 6338static void trw_layer_delete_points_same_position ( menu_array_sublayer values )
b6eda120 6339{
19782ffd 6340 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
0d2b891f 6341 VikTrack *trk;
19782ffd
RN
6342 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
6343 trk = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
0d2b891f 6344 else
19782ffd 6345 trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
0d2b891f
RN
6346
6347 if ( !trk )
6348 return;
b6eda120
RN
6349
6350 gulong removed = vik_track_remove_dup_points ( trk );
6351
6352 // Track has been updated so update tps:
6353 trw_layer_cancel_tps_of_track ( vtl, trk );
6354
6355 // Inform user how much was deleted as it's not obvious from the normal view
6356 gchar str[64];
6357 const gchar *tmp_str = ngettext("Deleted %ld point", "Deleted %ld points", removed);
6358 g_snprintf(str, 64, tmp_str, removed);
6359 a_dialog_info_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), str);
6360
da121f9b 6361 vik_layer_emit_update ( VIK_LAYER(vtl) );
b6eda120
RN
6362}
6363
6579ca1f
RN
6364/**
6365 * Delete adjacent track points with the same timestamp
6366 * Normally new tracks that are 'routes' won't have any timestamps so should be OK to clean up the track
6367 */
19782ffd 6368static void trw_layer_delete_points_same_time ( menu_array_sublayer values )
6579ca1f 6369{
19782ffd 6370 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
0d2b891f 6371 VikTrack *trk;
19782ffd
RN
6372 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
6373 trk = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
0d2b891f 6374 else
19782ffd 6375 trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
0d2b891f
RN
6376
6377 if ( !trk )
6378 return;
6579ca1f
RN
6379
6380 gulong removed = vik_track_remove_same_time_points ( trk );
6381
6382 // Track has been updated so update tps:
6383 trw_layer_cancel_tps_of_track ( vtl, trk );
6384
6385 // Inform user how much was deleted as it's not obvious from the normal view
6386 gchar str[64];
6387 const gchar *tmp_str = ngettext("Deleted %ld point", "Deleted %ld points", removed);
6388 g_snprintf(str, 64, tmp_str, removed);
6389 a_dialog_info_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), str);
6390
da121f9b 6391 vik_layer_emit_update ( VIK_LAYER(vtl) );
6579ca1f
RN
6392}
6393
491794be
RN
6394/**
6395 * Insert a point
6396 */
19782ffd 6397static void trw_layer_insert_point_after ( menu_array_sublayer values )
491794be 6398{
19782ffd 6399 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
491794be 6400 VikTrack *track;
19782ffd
RN
6401 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
6402 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
491794be 6403 else
19782ffd 6404 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
491794be
RN
6405
6406 if ( ! track )
6407 return;
6408
6409 trw_layer_insert_tp_beside_current_tp ( vtl, FALSE );
6410
6411 vik_layer_emit_update ( VIK_LAYER(vtl) );
6412}
6413
19782ffd 6414static void trw_layer_insert_point_before ( menu_array_sublayer values )
491794be 6415{
19782ffd 6416 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
491794be 6417 VikTrack *track;
19782ffd
RN
6418 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
6419 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
491794be 6420 else
19782ffd 6421 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
491794be
RN
6422
6423 if ( ! track )
6424 return;
6425
6426 trw_layer_insert_tp_beside_current_tp ( vtl, TRUE );
6427
6428 vik_layer_emit_update ( VIK_LAYER(vtl) );
6429}
6430
eb9fd106
RN
6431/**
6432 * Reverse a track
6433 */
19782ffd 6434static void trw_layer_reverse ( menu_array_sublayer values )
eb9fd106 6435{
19782ffd 6436 VikTrwLayer *vtl = (VikTrwLayer *)values[MA_VTL];
0d2b891f 6437 VikTrack *track;
19782ffd
RN
6438 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
6439 track = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
0d2b891f 6440 else
19782ffd 6441 track = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
0d2b891f
RN
6442
6443 if ( ! track )
6444 return;
eb9fd106 6445
eb9fd106
RN
6446 vik_track_reverse ( track );
6447
19782ffd 6448 vik_layer_emit_update ( VIK_LAYER(vtl) );
eb9fd106
RN
6449}
6450
20b671c3
RN
6451/**
6452 * Similar to trw_layer_enum_item, but this uses a sorted method
6453 */
ce4bd1cf 6454/* Currently unused
20b671c3
RN
6455static void trw_layer_sorted_name_list(gpointer key, gpointer value, gpointer udata)
6456{
6457 GList **list = (GList**)udata;
ce4bd1cf 6458 // *list = g_list_prepend(*all, key); //unsorted method
20b671c3
RN
6459 // Sort named list alphabetically
6460 *list = g_list_insert_sorted_with_data (*list, key, sort_alphabetically, NULL);
6461}
ce4bd1cf 6462*/
20b671c3 6463
c9570f86
RN
6464/**
6465 * Now Waypoint specific sort
6466 */
6467static void trw_layer_sorted_wp_id_by_name_list (const gpointer id, const VikWaypoint *wp, gpointer udata)
6468{
6469 GList **list = (GList**)udata;
6470 // Sort named list alphabetically
6471 *list = g_list_insert_sorted_with_data (*list, wp->name, sort_alphabetically, NULL);
6472}
6473
ce4bd1cf
RN
6474/**
6475 * Track specific sort
6476 */
6477static void trw_layer_sorted_track_id_by_name_list (const gpointer id, const VikTrack *trk, gpointer udata)
6478{
6479 GList **list = (GList**)udata;
6480 // Sort named list alphabetically
6481 *list = g_list_insert_sorted_with_data (*list, trk->name, sort_alphabetically, NULL);
6482}
6483
3d2ad4f8
RN
6484
6485typedef struct {
6486 gboolean has_same_track_name;
6487 const gchar *same_track_name;
6488} same_track_name_udata;
6489
6490static gint check_tracks_for_same_name ( gconstpointer aa, gconstpointer bb, gpointer udata )
6491{
6492 const gchar* namea = (const gchar*) aa;
6493 const gchar* nameb = (const gchar*) bb;
6494
6495 // the test
6496 gint result = strcmp ( namea, nameb );
6497
6498 if ( result == 0 ) {
6499 // Found two names the same
6500 same_track_name_udata *user_data = udata;
6501 user_data->has_same_track_name = TRUE;
6502 user_data->same_track_name = namea;
6503 }
6504
6505 // Leave ordering the same
6506 return 0;
6507}
6508
6509/**
0d2b891f 6510 * Find out if any tracks have the same name in this hash table
3d2ad4f8 6511 */
0d2b891f 6512static gboolean trw_layer_has_same_track_names ( GHashTable *ht_tracks )
3d2ad4f8
RN
6513{
6514 // Sort items by name, then compare if any next to each other are the same
6515
6516 GList *track_names = NULL;
0d2b891f 6517 g_hash_table_foreach ( ht_tracks, (GHFunc) trw_layer_sorted_track_id_by_name_list, &track_names );
3d2ad4f8
RN
6518
6519 // No tracks
6520 if ( ! track_names )
6521 return FALSE;
6522
6523 same_track_name_udata udata;
6524 udata.has_same_track_name = FALSE;
6525
6526 // Use sort routine to traverse list comparing items
6527 // Don't care how this list ends up ordered ( doesn't actually change ) - care about the returned status
6528 GList *dummy_list = g_list_sort_with_data ( track_names, check_tracks_for_same_name, &udata );
6529 // Still no tracks...
6530 if ( ! dummy_list )
6531 return FALSE;
6532
6533 return udata.has_same_track_name;
6534}
6535
6536/**
0d2b891f 6537 * Force unqiue track names for the track table specified
3d2ad4f8 6538 * Note the panel is a required parameter to enable the update of the names displayed
0d2b891f 6539 * Specify if on tracks or else on routes
3d2ad4f8 6540 */
0d2b891f 6541static void vik_trw_layer_uniquify_tracks ( VikTrwLayer *vtl, VikLayersPanel *vlp, GHashTable *track_table, gboolean ontrack )
3d2ad4f8
RN
6542{
6543 // . Search list for an instance of repeated name
6544 // . get track of this name
6545 // . create new name
6546 // . rename track & update equiv. treeview iter
6547 // . repeat until all different
6548
6549 same_track_name_udata udata;
6550
6551 GList *track_names = NULL;
6552 udata.has_same_track_name = FALSE;
6553 udata.same_track_name = NULL;
6554
0d2b891f 6555 g_hash_table_foreach ( track_table, (GHFunc) trw_layer_sorted_track_id_by_name_list, &track_names );
3d2ad4f8
RN
6556
6557 // No tracks
6558 if ( ! track_names )
6559 return;
6560
6561 GList *dummy_list1 = g_list_sort_with_data ( track_names, check_tracks_for_same_name, &udata );
6562
6563 // Still no tracks...
6564 if ( ! dummy_list1 )
6565 return;
6566
6567 while ( udata.has_same_track_name ) {
6568
6569 // Find a track with the same name
0d2b891f
RN
6570 VikTrack *trk;
6571 if ( ontrack )
6572 trk = vik_trw_layer_get_track ( vtl, (gpointer) udata.same_track_name );
6573 else
6574 trk = vik_trw_layer_get_route ( vtl, (gpointer) udata.same_track_name );
3d2ad4f8
RN
6575
6576 if ( ! trk ) {
6577 // Broken :(
6578 g_critical("Houston, we've had a problem.");
6579 vik_statusbar_set_message ( vik_window_get_statusbar (VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl))), VIK_STATUSBAR_INFO,
6580 _("Internal Error in vik_trw_layer_uniquify_tracks") );
6581 return;
6582 }
6583
6584 // Rename it
9748531a 6585 gchar *newname = trw_layer_new_unique_sublayer_name ( vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, udata.same_track_name );
3d2ad4f8
RN
6586 vik_track_set_name ( trk, newname );
6587
6588 trku_udata udataU;
6589 udataU.trk = trk;
6590 udataU.uuid = NULL;
6591
6592 // Need want key of it for treeview update
0d2b891f 6593 gpointer *trkf = g_hash_table_find ( track_table, (GHRFunc) trw_layer_track_find_uuid, &udataU );
3d2ad4f8
RN
6594
6595 if ( trkf && udataU.uuid ) {
6596
0d2b891f
RN
6597 GtkTreeIter *it;
6598 if ( ontrack )
6599 it = g_hash_table_lookup ( vtl->tracks_iters, udataU.uuid );
6600 else
6601 it = g_hash_table_lookup ( vtl->routes_iters, udataU.uuid );
3d2ad4f8
RN
6602
6603 if ( it ) {
6604 vik_treeview_item_set_name ( VIK_LAYER(vtl)->vt, it, newname );
c9cac058
RN
6605 if ( ontrack )
6606 vik_treeview_sort_children ( VIK_LAYER(vtl)->vt, &(vtl->tracks_iter), vtl->wp_sort_order );
6607 else
6608 vik_treeview_sort_children ( VIK_LAYER(vtl)->vt, &(vtl->routes_iter), vtl->wp_sort_order );
3d2ad4f8
RN
6609 }
6610 }
6611
6612 // Start trying to find same names again...
6613 track_names = NULL;
0d2b891f 6614 g_hash_table_foreach ( track_table, (GHFunc) trw_layer_sorted_track_id_by_name_list, &track_names );
3d2ad4f8
RN
6615 udata.has_same_track_name = FALSE;
6616 GList *dummy_list2 = g_list_sort_with_data ( track_names, check_tracks_for_same_name, &udata );
6617
6618 // No tracks any more - give up searching
6619 if ( ! dummy_list2 )
6620 udata.has_same_track_name = FALSE;
6621 }
6622
6623 // Update
6624 vik_layers_panel_emit_update ( vlp );
6625}
6626
19782ffd 6627static void trw_layer_sort_order_a2z ( menu_array_sublayer values )
c9cac058 6628{
19782ffd 6629 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
c9cac058
RN
6630 GtkTreeIter *iter;
6631
19782ffd 6632 switch (GPOINTER_TO_INT (values[MA_SUBTYPE])) {
c9cac058
RN
6633 case VIK_TRW_LAYER_SUBLAYER_TRACKS:
6634 iter = &(vtl->tracks_iter);
6635 vtl->track_sort_order = VL_SO_ALPHABETICAL_ASCENDING;
6636 break;
6637 case VIK_TRW_LAYER_SUBLAYER_ROUTES:
6638 iter = &(vtl->routes_iter);
6639 vtl->track_sort_order = VL_SO_ALPHABETICAL_ASCENDING;
6640 break;
6641 default: // VIK_TRW_LAYER_SUBLAYER_WAYPOINTS:
6642 iter = &(vtl->waypoints_iter);
6643 vtl->wp_sort_order = VL_SO_ALPHABETICAL_ASCENDING;
6644 break;
6645 }
6646
6647 vik_treeview_sort_children ( VIK_LAYER(vtl)->vt, iter, VL_SO_ALPHABETICAL_ASCENDING );
6648}
6649
19782ffd 6650static void trw_layer_sort_order_z2a ( menu_array_sublayer values )
c9cac058 6651{
19782ffd 6652 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
c9cac058
RN
6653 GtkTreeIter *iter;
6654
19782ffd 6655 switch (GPOINTER_TO_INT (values[MA_SUBTYPE])) {
c9cac058
RN
6656 case VIK_TRW_LAYER_SUBLAYER_TRACKS:
6657 iter = &(vtl->tracks_iter);
6658 vtl->track_sort_order = VL_SO_ALPHABETICAL_DESCENDING;
6659 break;
6660 case VIK_TRW_LAYER_SUBLAYER_ROUTES:
6661 iter = &(vtl->routes_iter);
6662 vtl->track_sort_order = VL_SO_ALPHABETICAL_DESCENDING;
6663 break;
6664 default: // VIK_TRW_LAYER_SUBLAYER_WAYPOINTS:
6665 iter = &(vtl->waypoints_iter);
6666 vtl->wp_sort_order = VL_SO_ALPHABETICAL_DESCENDING;
6667 break;
6668 }
6669
6670 vik_treeview_sort_children ( VIK_LAYER(vtl)->vt, iter, VL_SO_ALPHABETICAL_DESCENDING );
6671}
6672
20b671c3
RN
6673/**
6674 *
6675 */
1c2083ba 6676static void trw_layer_delete_tracks_from_selection ( menu_array_layer values )
20b671c3 6677{
1c2083ba 6678 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
20b671c3 6679 GList *all = NULL;
ce4bd1cf 6680
3d2ad4f8 6681 // Ensure list of track names offered is unique
0d2b891f 6682 if ( trw_layer_has_same_track_names ( vtl->tracks ) ) {
3d2ad4f8
RN
6683 if ( a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
6684 _("Multiple entries with the same name exist. This method only works with unique names. Force unique names now?"), NULL ) ) {
1c2083ba 6685 vik_trw_layer_uniquify_tracks ( vtl, VIK_LAYERS_PANEL(values[MA_VLP]), vtl->tracks, TRUE );
3d2ad4f8
RN
6686 }
6687 else
6688 return;
6689 }
ce4bd1cf 6690
20b671c3 6691 // Sort list alphabetically for better presentation
ce4bd1cf 6692 g_hash_table_foreach(vtl->tracks, (GHFunc) trw_layer_sorted_track_id_by_name_list, &all);
20b671c3
RN
6693
6694 if ( ! all ) {
6695 a_dialog_error_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), _("No tracks found"));
6696 return;
6697 }
6698
6699 // Get list of items to delete from the user
6700 GList *delete_list = a_dialog_select_from_list(VIK_GTK_WINDOW_FROM_LAYER(vtl),
6701 all,
6702 TRUE,
6703 _("Delete Selection"),
6704 _("Select tracks to delete"));
6705 g_list_free(all);
6706
6707 // Delete requested tracks
6708 // since specificly requested, IMHO no need for extra confirmation
6709 if ( delete_list ) {
6710 GList *l;
6711 for (l = delete_list; l != NULL; l = g_list_next(l)) {
3d2ad4f8 6712 // This deletes first trk it finds of that name (but uniqueness is enforced above)
0d2b891f
RN
6713 trw_layer_delete_track_by_name (vtl, l->data, vtl->tracks);
6714 }
6715 g_list_free(delete_list);
da121f9b 6716 vik_layer_emit_update( VIK_LAYER(vtl) );
0d2b891f
RN
6717 }
6718}
6719
6720/**
6721 *
6722 */
1c2083ba 6723static void trw_layer_delete_routes_from_selection ( menu_array_layer values )
0d2b891f 6724{
1c2083ba 6725 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
0d2b891f
RN
6726 GList *all = NULL;
6727
6728 // Ensure list of track names offered is unique
6729 if ( trw_layer_has_same_track_names ( vtl->routes ) ) {
6730 if ( a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
6731 _("Multiple entries with the same name exist. This method only works with unique names. Force unique names now?"), NULL ) ) {
1c2083ba 6732 vik_trw_layer_uniquify_tracks ( vtl, VIK_LAYERS_PANEL(values[MA_VLP]), vtl->routes, FALSE );
0d2b891f
RN
6733 }
6734 else
6735 return;
6736 }
6737
6738 // Sort list alphabetically for better presentation
6739 g_hash_table_foreach(vtl->routes, (GHFunc) trw_layer_sorted_track_id_by_name_list, &all);
6740
6741 if ( ! all ) {
6742 a_dialog_error_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), _("No routes found"));
6743 return;
6744 }
6745
6746 // Get list of items to delete from the user
6747 GList *delete_list = a_dialog_select_from_list ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
6748 all,
6749 TRUE,
6750 _("Delete Selection"),
6751 _("Select routes to delete") );
6752 g_list_free(all);
6753
6754 // Delete requested routes
6755 // since specificly requested, IMHO no need for extra confirmation
6756 if ( delete_list ) {
6757 GList *l;
6758 for (l = delete_list; l != NULL; l = g_list_next(l)) {
6759 // This deletes first route it finds of that name (but uniqueness is enforced above)
6760 trw_layer_delete_track_by_name (vtl, l->data, vtl->routes);
20b671c3
RN
6761 }
6762 g_list_free(delete_list);
da121f9b 6763 vik_layer_emit_update( VIK_LAYER(vtl) );
20b671c3
RN
6764 }
6765}
6766
33a89785
RN
6767typedef struct {
6768 gboolean has_same_waypoint_name;
6769 const gchar *same_waypoint_name;
6770} same_waypoint_name_udata;
6771
6772static gint check_waypoints_for_same_name ( gconstpointer aa, gconstpointer bb, gpointer udata )
6773{
6774 const gchar* namea = (const gchar*) aa;
6775 const gchar* nameb = (const gchar*) bb;
6776
6777 // the test
6778 gint result = strcmp ( namea, nameb );
6779
6780 if ( result == 0 ) {
6781 // Found two names the same
6782 same_waypoint_name_udata *user_data = udata;
6783 user_data->has_same_waypoint_name = TRUE;
6784 user_data->same_waypoint_name = namea;
6785 }
6786
6787 // Leave ordering the same
6788 return 0;
6789}
6790
6791/**
6792 * Find out if any waypoints have the same name in this layer
6793 */
6794gboolean trw_layer_has_same_waypoint_names ( VikTrwLayer *vtl )
6795{
6796 // Sort items by name, then compare if any next to each other are the same
6797
6798 GList *waypoint_names = NULL;
6799 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_sorted_wp_id_by_name_list, &waypoint_names );
6800
6801 // No waypoints
6802 if ( ! waypoint_names )
6803 return FALSE;
6804
6805 same_waypoint_name_udata udata;
6806 udata.has_same_waypoint_name = FALSE;
6807
6808 // Use sort routine to traverse list comparing items
6809 // Don't care how this list ends up ordered ( doesn't actually change ) - care about the returned status
6810 GList *dummy_list = g_list_sort_with_data ( waypoint_names, check_waypoints_for_same_name, &udata );
6811 // Still no waypoints...
6812 if ( ! dummy_list )
6813 return FALSE;
6814
6815 return udata.has_same_waypoint_name;
6816}
6817
6818/**
6819 * Force unqiue waypoint names for this layer
6820 * Note the panel is a required parameter to enable the update of the names displayed
6821 */
6822static void vik_trw_layer_uniquify_waypoints ( VikTrwLayer *vtl, VikLayersPanel *vlp )
6823{
6824 // . Search list for an instance of repeated name
6825 // . get waypoint of this name
6826 // . create new name
6827 // . rename waypoint & update equiv. treeview iter
6828 // . repeat until all different
6829
6830 same_waypoint_name_udata udata;
6831
6832 GList *waypoint_names = NULL;
6833 udata.has_same_waypoint_name = FALSE;
6834 udata.same_waypoint_name = NULL;
6835
6836 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_sorted_wp_id_by_name_list, &waypoint_names );
6837
6838 // No waypoints
6839 if ( ! waypoint_names )
6840 return;
6841
6842 GList *dummy_list1 = g_list_sort_with_data ( waypoint_names, check_waypoints_for_same_name, &udata );
6843
6844 // Still no waypoints...
6845 if ( ! dummy_list1 )
6846 return;
6847
6848 while ( udata.has_same_waypoint_name ) {
6849
6850 // Find a waypoint with the same name
6851 VikWaypoint *waypoint = vik_trw_layer_get_waypoint ( vtl, (gpointer) udata.same_waypoint_name );
6852
6853 if ( ! waypoint ) {
6854 // Broken :(
6855 g_critical("Houston, we've had a problem.");
6856 vik_statusbar_set_message ( vik_window_get_statusbar (VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl))), VIK_STATUSBAR_INFO,
6857 _("Internal Error in vik_trw_layer_uniquify_waypoints") );
6858 return;
6859 }
6860
6861 // Rename it
9748531a 6862 gchar *newname = trw_layer_new_unique_sublayer_name ( vtl, VIK_TRW_LAYER_SUBLAYER_WAYPOINT, udata.same_waypoint_name );
33a89785 6863
6761d8a4 6864 trw_layer_waypoint_rename ( vtl, waypoint, newname );
33a89785
RN
6865
6866 // Start trying to find same names again...
6867 waypoint_names = NULL;
6868 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_sorted_wp_id_by_name_list, &waypoint_names );
6869 udata.has_same_waypoint_name = FALSE;
6870 GList *dummy_list2 = g_list_sort_with_data ( waypoint_names, check_waypoints_for_same_name, &udata );
6871
6872 // No waypoints any more - give up searching
6873 if ( ! dummy_list2 )
6874 udata.has_same_waypoint_name = FALSE;
6875 }
6876
6877 // Update
6878 vik_layers_panel_emit_update ( vlp );
6879}
6880
20b671c3
RN
6881/**
6882 *
6883 */
1c2083ba 6884static void trw_layer_delete_waypoints_from_selection ( menu_array_layer values )
20b671c3 6885{
1c2083ba 6886 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
20b671c3
RN
6887 GList *all = NULL;
6888
33a89785
RN
6889 // Ensure list of waypoint names offered is unique
6890 if ( trw_layer_has_same_waypoint_names ( vtl ) ) {
6891 if ( a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
6892 _("Multiple entries with the same name exist. This method only works with unique names. Force unique names now?"), NULL ) ) {
1c2083ba 6893 vik_trw_layer_uniquify_waypoints ( vtl, VIK_LAYERS_PANEL(values[MA_VLP]) );
33a89785
RN
6894 }
6895 else
6896 return;
6897 }
c9570f86 6898
20b671c3 6899 // Sort list alphabetically for better presentation
c9570f86 6900 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_sorted_wp_id_by_name_list, &all);
20b671c3
RN
6901 if ( ! all ) {
6902 a_dialog_error_msg (VIK_GTK_WINDOW_FROM_LAYER(vtl), _("No waypoints found"));
6903 return;
6904 }
6905
6906 all = g_list_sort_with_data(all, sort_alphabetically, NULL);
6907
6908 // Get list of items to delete from the user
6909 GList *delete_list = a_dialog_select_from_list(VIK_GTK_WINDOW_FROM_LAYER(vtl),
6910 all,
6911 TRUE,
6912 _("Delete Selection"),
6913 _("Select waypoints to delete"));
6914 g_list_free(all);
6915
6916 // Delete requested waypoints
6917 // since specificly requested, IMHO no need for extra confirmation
6918 if ( delete_list ) {
6919 GList *l;
6920 for (l = delete_list; l != NULL; l = g_list_next(l)) {
33a89785 6921 // This deletes first waypoint it finds of that name (but uniqueness is enforced above)
c9570f86 6922 trw_layer_delete_waypoint_by_name (vtl, l->data);
20b671c3
RN
6923 }
6924 g_list_free(delete_list);
aa0665e5
RN
6925
6926 trw_layer_calculate_bounds_waypoints ( vtl );
da121f9b 6927 vik_layer_emit_update( VIK_LAYER(vtl) );
20b671c3
RN
6928 }
6929
6930}
111fa174 6931
89fdc417
RN
6932/**
6933 *
6934 */
6935static void trw_layer_iter_visibility_toggle ( gpointer id, GtkTreeIter *it, VikTreeview *vt )
6936{
6937 vik_treeview_item_toggle_visible ( vt, it );
6938}
6939
6940/**
6941 *
6942 */
6943static void trw_layer_iter_visibility ( gpointer id, GtkTreeIter *it, gpointer vis_data[2] )
6944{
6945 vik_treeview_item_set_visible ( (VikTreeview*)vis_data[0], it, GPOINTER_TO_INT (vis_data[1]) );
6946}
6947
6948/**
6949 *
6950 */
6951static void trw_layer_waypoints_visibility ( gpointer id, VikWaypoint *wp, gpointer on_off )
6952{
6953 wp->visible = GPOINTER_TO_INT (on_off);
6954}
6955
6956/**
6957 *
6958 */
6959static void trw_layer_waypoints_toggle_visibility ( gpointer id, VikWaypoint *wp )
6960{
6961 wp->visible = !wp->visible;
6962}
6963
6964/**
6965 *
6966 */
1c2083ba 6967static void trw_layer_waypoints_visibility_off ( menu_array_layer values )
89fdc417 6968{
1c2083ba 6969 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
89fdc417
RN
6970 gpointer vis_data[2] = { VIK_LAYER(vtl)->vt, GINT_TO_POINTER(FALSE) };
6971 g_hash_table_foreach ( vtl->waypoints_iters, (GHFunc) trw_layer_iter_visibility, vis_data );
6972 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_waypoints_visibility, vis_data[1] );
6973 // Redraw
6974 vik_layer_emit_update ( VIK_LAYER(vtl) );
6975}
6976
6977/**
6978 *
6979 */
1c2083ba 6980static void trw_layer_waypoints_visibility_on ( menu_array_layer values )
89fdc417 6981{
1c2083ba 6982 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
89fdc417
RN
6983 gpointer vis_data[2] = { VIK_LAYER(vtl)->vt, GINT_TO_POINTER(TRUE) };
6984 g_hash_table_foreach ( vtl->waypoints_iters, (GHFunc) trw_layer_iter_visibility, vis_data );
6985 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_waypoints_visibility, vis_data[1] );
6986 // Redraw
6987 vik_layer_emit_update ( VIK_LAYER(vtl) );
6988}
6989
6990/**
6991 *
6992 */
1c2083ba 6993static void trw_layer_waypoints_visibility_toggle ( menu_array_layer values )
89fdc417 6994{
1c2083ba 6995 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
89fdc417
RN
6996 g_hash_table_foreach ( vtl->waypoints_iters, (GHFunc) trw_layer_iter_visibility_toggle, VIK_LAYER(vtl)->vt );
6997 g_hash_table_foreach ( vtl->waypoints, (GHFunc) trw_layer_waypoints_toggle_visibility, NULL );
6998 // Redraw
6999 vik_layer_emit_update ( VIK_LAYER(vtl) );
7000}
7001
7002/**
7003 *
7004 */
7005static void trw_layer_tracks_visibility ( gpointer id, VikTrack *trk, gpointer on_off )
7006{
7007 trk->visible = GPOINTER_TO_INT (on_off);
7008}
7009
7010/**
7011 *
7012 */
7013static void trw_layer_tracks_toggle_visibility ( gpointer id, VikTrack *trk )
7014{
7015 trk->visible = !trk->visible;
7016}
7017
7018/**
7019 *
7020 */
1c2083ba 7021static void trw_layer_tracks_visibility_off ( menu_array_layer values )
89fdc417 7022{
1c2083ba 7023 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
89fdc417
RN
7024 gpointer vis_data[2] = { VIK_LAYER(vtl)->vt, GINT_TO_POINTER(FALSE) };
7025 g_hash_table_foreach ( vtl->tracks_iters, (GHFunc) trw_layer_iter_visibility, vis_data );
7026 g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_tracks_visibility, vis_data[1] );
7027 // Redraw
7028 vik_layer_emit_update ( VIK_LAYER(vtl) );
7029}
7030
7031/**
7032 *
7033 */
1c2083ba 7034static void trw_layer_tracks_visibility_on ( menu_array_layer values )
89fdc417 7035{
1c2083ba 7036 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
89fdc417
RN
7037 gpointer vis_data[2] = { VIK_LAYER(vtl)->vt, GINT_TO_POINTER(TRUE) };
7038 g_hash_table_foreach ( vtl->tracks_iters, (GHFunc) trw_layer_iter_visibility, vis_data );
7039 g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_tracks_visibility, vis_data[1] );
7040 // Redraw
7041 vik_layer_emit_update ( VIK_LAYER(vtl) );
7042}
7043
7044/**
7045 *
7046 */
1c2083ba 7047static void trw_layer_tracks_visibility_toggle ( menu_array_layer values )
89fdc417 7048{
1c2083ba 7049 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
89fdc417
RN
7050 g_hash_table_foreach ( vtl->tracks_iters, (GHFunc) trw_layer_iter_visibility_toggle, VIK_LAYER(vtl)->vt );
7051 g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_tracks_toggle_visibility, NULL );
7052 // Redraw
7053 vik_layer_emit_update ( VIK_LAYER(vtl) );
7054}
7055
7056/**
7057 *
7058 */
1c2083ba 7059static void trw_layer_routes_visibility_off ( menu_array_layer values )
89fdc417 7060{
1c2083ba 7061 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
89fdc417
RN
7062 gpointer vis_data[2] = { VIK_LAYER(vtl)->vt, GINT_TO_POINTER(FALSE) };
7063 g_hash_table_foreach ( vtl->routes_iters, (GHFunc) trw_layer_iter_visibility, vis_data );
7064 g_hash_table_foreach ( vtl->routes, (GHFunc) trw_layer_tracks_visibility, vis_data[1] );
7065 // Redraw
7066 vik_layer_emit_update ( VIK_LAYER(vtl) );
7067}
7068
7069/**
7070 *
7071 */
1c2083ba 7072static void trw_layer_routes_visibility_on ( menu_array_layer values )
89fdc417 7073{
1c2083ba 7074 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
89fdc417
RN
7075 gpointer vis_data[2] = { VIK_LAYER(vtl)->vt, GINT_TO_POINTER(TRUE) };
7076 g_hash_table_foreach ( vtl->routes_iters, (GHFunc) trw_layer_iter_visibility, vis_data );
7077 g_hash_table_foreach ( vtl->routes, (GHFunc) trw_layer_tracks_visibility, vis_data[1] );
7078 // Redraw
7079 vik_layer_emit_update ( VIK_LAYER(vtl) );
7080}
7081
7082/**
7083 *
7084 */
1c2083ba 7085static void trw_layer_routes_visibility_toggle ( menu_array_layer values )
89fdc417 7086{
1c2083ba 7087 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
89fdc417
RN
7088 g_hash_table_foreach ( vtl->routes_iters, (GHFunc) trw_layer_iter_visibility_toggle, VIK_LAYER(vtl)->vt );
7089 g_hash_table_foreach ( vtl->routes, (GHFunc) trw_layer_tracks_toggle_visibility, NULL );
7090 // Redraw
7091 vik_layer_emit_update ( VIK_LAYER(vtl) );
7092}
7093
00176e85
RN
7094/**
7095 * vik_trw_layer_build_waypoint_list_t:
7096 *
7097 * Helper function to construct a list of #vik_trw_waypoint_list_t
7098 */
7099GList *vik_trw_layer_build_waypoint_list_t ( VikTrwLayer *vtl, GList *waypoints )
7100{
7101 GList *waypoints_and_layers = NULL;
7102 // build waypoints_and_layers list
7103 while ( waypoints ) {
7104 vik_trw_waypoint_list_t *vtdl = g_malloc (sizeof(vik_trw_waypoint_list_t));
7105 vtdl->wpt = VIK_WAYPOINT(waypoints->data);
7106 vtdl->vtl = vtl;
7107 waypoints_and_layers = g_list_prepend ( waypoints_and_layers, vtdl );
7108 waypoints = g_list_next ( waypoints );
7109 }
7110 return waypoints_and_layers;
7111}
7112
7113/**
7114 * trw_layer_create_waypoint_list:
7115 *
7116 * Create the latest list of waypoints with the associated layer(s)
7117 * Although this will always be from a single layer here
7118 */
7119static GList* trw_layer_create_waypoint_list ( VikLayer *vl, gpointer user_data )
7120{
7121 VikTrwLayer *vtl = VIK_TRW_LAYER(vl);
7122 GList *waypoints = g_hash_table_get_values ( vik_trw_layer_get_waypoints(vtl) );
7123
7124 return vik_trw_layer_build_waypoint_list_t ( vtl, waypoints );
7125}
7126
43984ba1
RN
7127/**
7128 * trw_layer_analyse_close:
7129 *
7130 * Stuff to do on dialog closure
7131 */
7132static void trw_layer_analyse_close ( GtkWidget *dialog, gint resp, VikLayer* vl )
7133{
7134 VikTrwLayer *vtl = VIK_TRW_LAYER(vl);
7135 gtk_widget_destroy ( dialog );
7136 vtl->tracks_analysis_dialog = NULL;
7137}
7138
7139/**
184ec0fa 7140 * vik_trw_layer_build_track_list_t:
43984ba1 7141 *
184ec0fa 7142 * Helper function to construct a list of #vik_trw_track_list_t
43984ba1 7143 */
184ec0fa 7144GList *vik_trw_layer_build_track_list_t ( VikTrwLayer *vtl, GList *tracks )
43984ba1 7145{
43984ba1
RN
7146 GList *tracks_and_layers = NULL;
7147 // build tracks_and_layers list
43984ba1
RN
7148 while ( tracks ) {
7149 vik_trw_track_list_t *vtdl = g_malloc (sizeof(vik_trw_track_list_t));
7150 vtdl->trk = VIK_TRACK(tracks->data);
7151 vtdl->vtl = vtl;
7152 tracks_and_layers = g_list_prepend ( tracks_and_layers, vtdl );
7153 tracks = g_list_next ( tracks );
7154 }
43984ba1
RN
7155 return tracks_and_layers;
7156}
7157
184ec0fa
RN
7158/**
7159 * trw_layer_create_track_list:
7160 *
7161 * Create the latest list of tracks with the associated layer(s)
7162 * Although this will always be from a single layer here
7163 */
7164static GList* trw_layer_create_track_list ( VikLayer *vl, gpointer user_data )
7165{
7166 VikTrwLayer *vtl = VIK_TRW_LAYER(vl);
7167 GList *tracks = NULL;
7168 if ( GPOINTER_TO_INT(user_data) == VIK_TRW_LAYER_SUBLAYER_TRACKS )
7169 tracks = g_hash_table_get_values ( vik_trw_layer_get_tracks(vtl) );
7170 else
7171 tracks = g_hash_table_get_values ( vik_trw_layer_get_routes(vtl) );
7172
7173 return vik_trw_layer_build_track_list_t ( vtl, tracks );
7174}
7175
1c2083ba 7176static void trw_layer_tracks_stats ( menu_array_layer values )
43984ba1 7177{
1c2083ba 7178 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
43984ba1
RN
7179 // There can only be one!
7180 if ( vtl->tracks_analysis_dialog )
7181 return;
7182
7183 vtl->tracks_analysis_dialog = vik_trw_layer_analyse_this ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
7184 VIK_LAYER(vtl)->name,
7185 VIK_LAYER(vtl),
7186 GINT_TO_POINTER(VIK_TRW_LAYER_SUBLAYER_TRACKS),
184ec0fa 7187 trw_layer_create_track_list,
43984ba1
RN
7188 trw_layer_analyse_close );
7189}
7190
7191/**
7192 *
7193 */
1c2083ba 7194static void trw_layer_routes_stats ( menu_array_layer values )
43984ba1 7195{
1c2083ba 7196 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
43984ba1
RN
7197 // There can only be one!
7198 if ( vtl->tracks_analysis_dialog )
7199 return;
7200
7201 vtl->tracks_analysis_dialog = vik_trw_layer_analyse_this ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
7202 VIK_LAYER(vtl)->name,
7203 VIK_LAYER(vtl),
7204 GINT_TO_POINTER(VIK_TRW_LAYER_SUBLAYER_ROUTES),
184ec0fa 7205 trw_layer_create_track_list,
43984ba1
RN
7206 trw_layer_analyse_close );
7207}
7208
19782ffd 7209static void trw_layer_goto_waypoint ( menu_array_sublayer values )
50a14534 7210{
19782ffd
RN
7211 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
7212 VikWaypoint *wp = g_hash_table_lookup ( vtl->waypoints, values[MA_SUBLAYER_ID] );
50a14534 7213 if ( wp )
19782ffd 7214 goto_coord ( values[MA_VLP], vtl, values[MA_VVP], &(wp->coord) );
50a14534
EB
7215}
7216
19782ffd 7217static void trw_layer_waypoint_gc_webpage ( menu_array_sublayer values )
50a14534 7218{
19782ffd
RN
7219 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
7220 VikWaypoint *wp = g_hash_table_lookup ( vtl->waypoints, values[MA_SUBLAYER_ID] );
d64e2724
RN
7221 if ( !wp )
7222 return;
7223 gchar *webpage = g_strdup_printf("http://www.geocaching.com/seek/cache_details.aspx?wp=%s", wp->name );
19782ffd 7224 open_url(VIK_GTK_WINDOW_FROM_LAYER(VIK_LAYER(vtl)), webpage);
50a14534
EB
7225 g_free ( webpage );
7226}
7227
19782ffd 7228static void trw_layer_waypoint_webpage ( menu_array_sublayer values )
3c13fff8 7229{
19782ffd
RN
7230 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
7231 VikWaypoint *wp = g_hash_table_lookup ( vtl->waypoints, values[MA_SUBLAYER_ID] );
3c13fff8
RN
7232 if ( !wp )
7233 return;
7234 if ( !strncmp(wp->comment, "http", 4) ) {
19782ffd 7235 open_url(VIK_GTK_WINDOW_FROM_LAYER(VIK_LAYER(vtl)), wp->comment);
3c13fff8 7236 } else if ( !strncmp(wp->description, "http", 4) ) {
19782ffd 7237 open_url(VIK_GTK_WINDOW_FROM_LAYER(VIK_LAYER(vtl)), wp->description);
3c13fff8
RN
7238 }
7239}
7240
a7cd93ac 7241static const gchar* trw_layer_sublayer_rename_request ( VikTrwLayer *l, const gchar *newname, gpointer vlp, gint subtype, gpointer sublayer, GtkTreeIter *iter )
50a14534
EB
7242{
7243 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT )
7244 {
c9570f86 7245 VikWaypoint *wp = g_hash_table_lookup ( l->waypoints, sublayer );
50a14534 7246
c9570f86 7247 // No actual change to the name supplied
27d267f4
RN
7248 if ( wp->name )
7249 if (strcmp(newname, wp->name) == 0 )
7250 return NULL;
50a14534 7251
c9570f86
RN
7252 VikWaypoint *wpf = vik_trw_layer_get_waypoint ( l, newname );
7253
7254 if ( wpf ) {
7255 // An existing waypoint has been found with the requested name
7256 if ( ! a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(l),
77bca466 7257 _("A waypoint with the name \"%s\" already exists. Really rename to the same name?"),
c9570f86 7258 newname ) )
8499a412 7259 return NULL;
50a14534
EB
7260 }
7261
c9570f86
RN
7262 // Update WP name and refresh the treeview
7263 vik_waypoint_set_name (wp, newname);
50a14534 7264
c9cac058
RN
7265 vik_treeview_item_set_name ( VIK_LAYER(l)->vt, iter, newname );
7266 vik_treeview_sort_children ( VIK_LAYER(l)->vt, &(l->waypoints_iter), l->wp_sort_order );
50a14534
EB
7267
7268 vik_layers_panel_emit_update ( VIK_LAYERS_PANEL(vlp) );
c9570f86
RN
7269
7270 return newname;
50a14534 7271 }
c9570f86 7272
50a14534
EB
7273 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
7274 {
ce4bd1cf 7275 VikTrack *trk = g_hash_table_lookup ( l->tracks, sublayer );
50a14534 7276
ce4bd1cf 7277 // No actual change to the name supplied
27d267f4
RN
7278 if ( trk->name )
7279 if (strcmp(newname, trk->name) == 0)
7280 return NULL;
50a14534 7281
ce4bd1cf
RN
7282 VikTrack *trkf = vik_trw_layer_get_track ( l, (gpointer) newname );
7283
7284 if ( trkf ) {
7285 // An existing track has been found with the requested name
7286 if ( ! a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(l),
77bca466 7287 _("A track with the name \"%s\" already exists. Really rename to the same name?"),
ce4bd1cf 7288 newname ) )
8499a412 7289 return NULL;
50a14534 7290 }
ce4bd1cf
RN
7291 // Update track name and refresh GUI parts
7292 vik_track_set_name (trk, newname);
50a14534 7293
ce4bd1cf
RN
7294 // Update any subwindows that could be displaying this track which has changed name
7295 // Only one Track Edit Window
7296 if ( l->current_tp_track == trk && l->tpwin ) {
7297 vik_trw_layer_tpwin_set_track_name ( l->tpwin, newname );
50a14534 7298 }
ce4bd1cf
RN
7299 // Property Dialog of the track
7300 vik_trw_layer_propwin_update ( trk );
50a14534 7301
c9cac058
RN
7302 vik_treeview_item_set_name ( VIK_LAYER(l)->vt, iter, newname );
7303 vik_treeview_sort_children ( VIK_LAYER(l)->vt, &(l->tracks_iter), l->track_sort_order );
0d2b891f
RN
7304
7305 vik_layers_panel_emit_update ( VIK_LAYERS_PANEL(vlp) );
7306
7307 return newname;
7308 }
7309
7310 if ( subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE )
7311 {
7312 VikTrack *trk = g_hash_table_lookup ( l->routes, sublayer );
7313
7314 // No actual change to the name supplied
27d267f4
RN
7315 if ( trk->name )
7316 if (strcmp(newname, trk->name) == 0)
7317 return NULL;
0d2b891f
RN
7318
7319 VikTrack *trkf = vik_trw_layer_get_route ( l, (gpointer) newname );
7320
7321 if ( trkf ) {
7322 // An existing track has been found with the requested name
7323 if ( ! a_dialog_yes_or_no ( VIK_GTK_WINDOW_FROM_LAYER(l),
7324 _("A route with the name \"%s\" already exists. Really rename to the same name?"),
7325 newname ) )
7326 return NULL;
7327 }
7328 // Update track name and refresh GUI parts
7329 vik_track_set_name (trk, newname);
7330
7331 // Update any subwindows that could be displaying this track which has changed name
7332 // Only one Track Edit Window
7333 if ( l->current_tp_track == trk && l->tpwin ) {
7334 vik_trw_layer_tpwin_set_track_name ( l->tpwin, newname );
7335 }
7336 // Property Dialog of the track
7337 vik_trw_layer_propwin_update ( trk );
7338
c9cac058
RN
7339 vik_treeview_item_set_name ( VIK_LAYER(l)->vt, iter, newname );
7340 vik_treeview_sort_children ( VIK_LAYER(l)->vt, &(l->tracks_iter), l->track_sort_order );
50a14534
EB
7341
7342 vik_layers_panel_emit_update ( VIK_LAYERS_PANEL(vlp) );
ce4bd1cf
RN
7343
7344 return newname;
50a14534
EB
7345 }
7346 return NULL;
7347}
7348
7349static gboolean is_valid_geocache_name ( gchar *str )
7350{
7351 gint len = strlen ( str );
0c1044e9 7352 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
7353}
7354
19782ffd 7355static void trw_layer_track_use_with_filter ( menu_array_sublayer values )
28c82d8b 7356{
19782ffd 7357 VikTrack *trk = g_hash_table_lookup ( VIK_TRW_LAYER(values[MA_VTL])->tracks, values[MA_SUBLAYER_ID] );
ce4bd1cf 7358 a_acquire_set_filter_track ( trk );
28c82d8b
EB
7359}
7360
55340efa 7361#ifdef VIK_CONFIG_GOOGLE
ce4bd1cf 7362static gboolean is_valid_google_route ( VikTrwLayer *vtl, const gpointer track_id )
bddd2056 7363{
008e972c 7364 VikTrack *tr = g_hash_table_lookup ( vtl->routes, track_id );
bddd2056
EB
7365 return ( tr && tr->comment && strlen(tr->comment) > 7 && !strncmp(tr->comment, "from:", 5) );
7366}
7367
19782ffd 7368static void trw_layer_google_route_webpage ( menu_array_sublayer values )
bddd2056 7369{
19782ffd 7370 VikTrack *tr = g_hash_table_lookup ( VIK_TRW_LAYER(values[MA_VTL])->routes, values[MA_SUBLAYER_ID] );
bddd2056
EB
7371 if ( tr ) {
7372 gchar *escaped = uri_escape ( tr->comment );
7373 gchar *webpage = g_strdup_printf("http://maps.google.com/maps?f=q&hl=en&q=%s", escaped );
19782ffd 7374 open_url(VIK_GTK_WINDOW_FROM_LAYER(VIK_LAYER(values[MA_VTL])), webpage);
bddd2056 7375 g_free ( escaped );
bddd2056
EB
7376 g_free ( webpage );
7377 }
7378}
ff1f2b3e 7379#endif
bddd2056 7380
50eadc64
RN
7381/* vlp can be NULL if necessary - i.e. right-click from a tool */
7382/* viewpoint is now available instead */
a7cd93ac 7383static gboolean trw_layer_sublayer_add_menu_items ( VikTrwLayer *l, GtkMenu *menu, gpointer vlp, gint subtype, gpointer sublayer, GtkTreeIter *iter, VikViewport *vvp )
50a14534 7384{
19782ffd 7385 static menu_array_sublayer pass_along;
50a14534
EB
7386 GtkWidget *item;
7387 gboolean rv = FALSE;
7388
19782ffd
RN
7389 pass_along[MA_VTL] = l;
7390 pass_along[MA_VLP] = vlp;
7391 pass_along[MA_SUBTYPE] = GINT_TO_POINTER (subtype);
7392 pass_along[MA_SUBLAYER_ID] = sublayer;
7393 pass_along[MA_CONFIRM] = GINT_TO_POINTER (1); // Confirm delete request
7394 pass_along[MA_VVP] = vvp;
7395 pass_along[MA_TV_ITER] = iter;
7396 pass_along[MA_MISC] = NULL; // For misc purposes - maybe track or waypoint
50a14534 7397
0d2b891f 7398 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT || subtype == VIK_TRW_LAYER_SUBLAYER_TRACK || subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE )
50a14534
EB
7399 {
7400 rv = TRUE;
7401
7402 item = gtk_image_menu_item_new_from_stock ( GTK_STOCK_PROPERTIES, NULL );
7403 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_properties_item), pass_along );
7404 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7405 gtk_widget_show ( item );
7406
21700912 7407 if (subtype == VIK_TRW_LAYER_SUBLAYER_TRACK) {
0d2b891f
RN
7408 VikTrack *tr = g_hash_table_lookup ( l->tracks, sublayer );
7409 if (tr && tr->property_dialog)
7410 gtk_widget_set_sensitive(GTK_WIDGET(item), FALSE );
7411 }
7412 if (subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE) {
7413 VikTrack *tr = g_hash_table_lookup ( l->routes, sublayer );
21700912
QT
7414 if (tr && tr->property_dialog)
7415 gtk_widget_set_sensitive(GTK_WIDGET(item), FALSE );
7416 }
7417
2cebc318
QT
7418 item = gtk_image_menu_item_new_from_stock ( GTK_STOCK_CUT, NULL );
7419 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_cut_item_cb), pass_along );
7420 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7421 gtk_widget_show ( item );
7422
7423 item = gtk_image_menu_item_new_from_stock ( GTK_STOCK_COPY, NULL );
7424 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_copy_item_cb), pass_along );
7425 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7426 gtk_widget_show ( item );
7427
50a14534
EB
7428 item = gtk_image_menu_item_new_from_stock ( GTK_STOCK_DELETE, NULL );
7429 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_item), pass_along );
7430 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7431 gtk_widget_show ( item );
7432
7433 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT )
7434 {
a8952eb1
RN
7435 // Always create separator as now there is always at least the transform menu option
7436 item = gtk_menu_item_new ();
7437 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7438 gtk_widget_show ( item );
5ede6aa6 7439
50a14534
EB
7440 /* could be a right-click using the tool */
7441 if ( vlp != NULL ) {
d6de71f9 7442 item = gtk_image_menu_item_new_with_mnemonic ( _("_Goto") );
a8952eb1 7443 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_JUMP_TO, GTK_ICON_SIZE_MENU) );
50a14534
EB
7444 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_waypoint), pass_along );
7445 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7446 gtk_widget_show ( item );
7447 }
7448
c9570f86 7449 VikWaypoint *wp = g_hash_table_lookup ( VIK_TRW_LAYER(l)->waypoints, sublayer );
5ede6aa6 7450
c9570f86
RN
7451 if ( wp && wp->name ) {
7452 if ( is_valid_geocache_name ( wp->name ) ) {
c9570f86
RN
7453 item = gtk_menu_item_new_with_mnemonic ( _("_Visit Geocache Webpage") );
7454 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_waypoint_gc_webpage), pass_along );
7455 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7456 gtk_widget_show ( item );
7457 }
41f4abac
RN
7458#ifdef VIK_CONFIG_GEOTAG
7459 item = gtk_menu_item_new_with_mnemonic ( _("Geotag _Images...") );
7460 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_geotagging_waypoint), pass_along );
7461 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7462 gtk_widget_set_tooltip_text (item, _("Geotag multiple images against this waypoint"));
7463 gtk_widget_show ( item );
7464#endif
c9570f86 7465 }
a412f3f5
RN
7466
7467 if ( wp && wp->image )
7468 {
19782ffd
RN
7469 // Set up image paramater
7470 pass_along[MA_MISC] = wp->image;
a412f3f5 7471
d6de71f9 7472 item = gtk_image_menu_item_new_with_mnemonic ( _("_Show Picture...") );
43b5e335 7473 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
7474 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_show_picture), pass_along );
7475 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7476 gtk_widget_show ( item );
b3eb3b98
RN
7477
7478#ifdef VIK_CONFIG_GEOTAG
7479 GtkWidget *geotag_submenu = gtk_menu_new ();
7480 item = gtk_image_menu_item_new_with_mnemonic ( _("Update Geotag on _Image") );
7481 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REFRESH, GTK_ICON_SIZE_MENU) );
7482 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7483 gtk_widget_show ( item );
7484 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), geotag_submenu );
7485
7486 item = gtk_menu_item_new_with_mnemonic ( _("_Update") );
7487 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_geotagging_waypoint_mtime_update), pass_along );
7488 gtk_menu_shell_append (GTK_MENU_SHELL (geotag_submenu), item);
7489 gtk_widget_show ( item );
7490
7491 item = gtk_menu_item_new_with_mnemonic ( _("Update and _Keep File Timestamp") );
7492 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_geotagging_waypoint_mtime_keep), pass_along );
7493 gtk_menu_shell_append (GTK_MENU_SHELL (geotag_submenu), item);
7494 gtk_widget_show ( item );
7495#endif
a412f3f5
RN
7496 }
7497
3c13fff8
RN
7498 if ( wp )
7499 {
7500 if ( ( wp->comment && !strncmp(wp->comment, "http", 4) ) ||
7501 ( wp->description && !strncmp(wp->description, "http", 4) )) {
7502 item = gtk_image_menu_item_new_with_mnemonic ( _("Visit _Webpage") );
7503 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NETWORK, GTK_ICON_SIZE_MENU) );
7504 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_waypoint_webpage), pass_along );
7505 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7506 gtk_widget_show ( item );
7507 }
7508 }
50a14534
EB
7509 }
7510 }
7511
0d2b891f 7512 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINTS || subtype == VIK_TRW_LAYER_SUBLAYER_TRACKS || subtype == VIK_TRW_LAYER_SUBLAYER_ROUTES ) {
0d80642e
RN
7513 item = gtk_image_menu_item_new_from_stock ( GTK_STOCK_PASTE, NULL );
7514 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_paste_item_cb), pass_along );
7515 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7516 gtk_widget_show ( item );
7517 // TODO: only enable if suitable item is in clipboard - want to determine *which* sublayer type
7518 if ( a_clipboard_type ( ) == VIK_CLIPBOARD_DATA_SUBLAYER )
7519 gtk_widget_set_sensitive ( item, TRUE );
7520 else
7521 gtk_widget_set_sensitive ( item, FALSE );
7522
7523 // Add separator
7524 item = gtk_menu_item_new ();
7525 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7526 gtk_widget_show ( item );
7527 }
7528
5ede6aa6
RN
7529 if ( vlp && (subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINTS || subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT) )
7530 {
b66bb4ab 7531 rv = TRUE;
d6de71f9
RN
7532 item = gtk_image_menu_item_new_with_mnemonic ( _("_New Waypoint...") );
7533 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NEW, GTK_ICON_SIZE_MENU) );
5ede6aa6
RN
7534 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_wp), pass_along );
7535 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7536 gtk_widget_show ( item );
7537 }
7538
539ba038
RN
7539 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINTS )
7540 {
d6de71f9
RN
7541 item = gtk_image_menu_item_new_with_mnemonic ( _("_View All Waypoints") );
7542 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ZOOM_FIT, GTK_ICON_SIZE_MENU) );
539ba038
RN
7543 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_waypoints_view), pass_along );
7544 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7545 gtk_widget_show ( item );
7546
d6de71f9
RN
7547 item = gtk_image_menu_item_new_with_mnemonic ( _("Goto _Waypoint...") );
7548 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_JUMP_TO, GTK_ICON_SIZE_MENU) );
539ba038
RN
7549 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_wp), pass_along );
7550 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7551 gtk_widget_show ( item );
c9a5cbf9 7552
d6de71f9
RN
7553 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete _All Waypoints") );
7554 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU) );
c9a5cbf9
RN
7555 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_all_waypoints), pass_along );
7556 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7557 gtk_widget_show ( item );
20b671c3 7558
d6de71f9
RN
7559 item = gtk_image_menu_item_new_with_mnemonic ( _("_Delete Waypoints From Selection...") );
7560 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
20b671c3
RN
7561 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_waypoints_from_selection), pass_along );
7562 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7563 gtk_widget_show ( item );
89fdc417
RN
7564
7565 GtkWidget *vis_submenu = gtk_menu_new ();
7566 item = gtk_menu_item_new_with_mnemonic ( _("_Visibility") );
7567 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7568 gtk_widget_show ( item );
7569 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), vis_submenu );
7570
7571 item = gtk_image_menu_item_new_with_mnemonic ( _("_Show All Waypoints") );
7572 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_APPLY, GTK_ICON_SIZE_MENU) );
7573 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_waypoints_visibility_on), pass_along );
7574 gtk_menu_shell_append ( GTK_MENU_SHELL(vis_submenu), item );
7575 gtk_widget_show ( item );
7576
7577 item = gtk_image_menu_item_new_with_mnemonic ( _("_Hide All Waypoints") );
7578 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_CLEAR, GTK_ICON_SIZE_MENU) );
7579 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_waypoints_visibility_off), pass_along );
7580 gtk_menu_shell_append ( GTK_MENU_SHELL(vis_submenu), item );
7581 gtk_widget_show ( item );
7582
7583 item = gtk_image_menu_item_new_with_mnemonic ( _("_Toggle") );
7584 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REFRESH, GTK_ICON_SIZE_MENU) );
7585 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_waypoints_visibility_toggle), pass_along );
7586 gtk_menu_shell_append ( GTK_MENU_SHELL(vis_submenu), item );
7587 gtk_widget_show ( item );
00176e85
RN
7588
7589 item = gtk_image_menu_item_new_with_mnemonic ( _("_List Waypoints...") );
7590 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
7591 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_waypoint_list_dialog), pass_along );
7592 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
539ba038
RN
7593 }
7594
f1e68516
RN
7595 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACKS )
7596 {
7597 rv = TRUE;
7598
0d2b891f 7599 if ( l->current_track && !l->current_track->is_route ) {
37615c52
RN
7600 item = gtk_menu_item_new_with_mnemonic ( _("_Finish Track") );
7601 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_finish_track), pass_along );
7602 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7603 gtk_widget_show ( item );
7604 // Add separator
7605 item = gtk_menu_item_new ();
7606 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7607 gtk_widget_show ( item );
7608 }
7609
d6de71f9
RN
7610 item = gtk_image_menu_item_new_with_mnemonic ( _("_View All Tracks") );
7611 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ZOOM_FIT, GTK_ICON_SIZE_MENU) );
f1e68516
RN
7612 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_tracks_view), pass_along );
7613 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7614 gtk_widget_show ( item );
c9a5cbf9 7615
37615c52
RN
7616 item = gtk_image_menu_item_new_with_mnemonic ( _("_New Track") );
7617 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NEW, GTK_ICON_SIZE_MENU) );
7618 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_track), pass_along );
7619 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7620 gtk_widget_show ( item );
7621 // Make it available only when a new track *not* already in progress
7622 gtk_widget_set_sensitive ( item, ! (gboolean)GPOINTER_TO_INT(l->current_track) );
7623
d6de71f9
RN
7624 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete _All Tracks") );
7625 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU) );
c9a5cbf9
RN
7626 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_all_tracks), pass_along );
7627 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7628 gtk_widget_show ( item );
20b671c3 7629
d6de71f9
RN
7630 item = gtk_image_menu_item_new_with_mnemonic ( _("_Delete Tracks From Selection...") );
7631 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
20b671c3
RN
7632 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_tracks_from_selection), pass_along );
7633 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7634 gtk_widget_show ( item );
89fdc417
RN
7635
7636 GtkWidget *vis_submenu = gtk_menu_new ();
7637 item = gtk_menu_item_new_with_mnemonic ( _("_Visibility") );
7638 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7639 gtk_widget_show ( item );
7640 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), vis_submenu );
7641
7642 item = gtk_image_menu_item_new_with_mnemonic ( _("_Show All Tracks") );
7643 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_APPLY, GTK_ICON_SIZE_MENU) );
7644 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_tracks_visibility_on), pass_along );
7645 gtk_menu_shell_append ( GTK_MENU_SHELL(vis_submenu), item );
7646 gtk_widget_show ( item );
7647
7648 item = gtk_image_menu_item_new_with_mnemonic ( _("_Hide All Tracks") );
7649 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_CLEAR, GTK_ICON_SIZE_MENU) );
7650 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_tracks_visibility_off), pass_along );
7651 gtk_menu_shell_append ( GTK_MENU_SHELL(vis_submenu), item );
7652 gtk_widget_show ( item );
7653
7654 item = gtk_image_menu_item_new_with_mnemonic ( _("_Toggle") );
7655 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REFRESH, GTK_ICON_SIZE_MENU) );
7656 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_tracks_visibility_toggle), pass_along );
7657 gtk_menu_shell_append ( GTK_MENU_SHELL(vis_submenu), item );
07c9d42b
RN
7658
7659 item = gtk_image_menu_item_new_with_mnemonic ( _("_List Tracks...") );
7660 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
7661 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_track_list_dialog_single), pass_along );
7662 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
89fdc417 7663 gtk_widget_show ( item );
43984ba1
RN
7664
7665 item = gtk_menu_item_new_with_mnemonic ( _("_Statistics") );
7666 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_tracks_stats), pass_along );
7667 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7668 gtk_widget_show ( item );
f1e68516
RN
7669 }
7670
0d2b891f
RN
7671 if ( subtype == VIK_TRW_LAYER_SUBLAYER_ROUTES )
7672 {
7673 rv = TRUE;
7674
7675 if ( l->current_track && l->current_track->is_route ) {
7676 item = gtk_menu_item_new_with_mnemonic ( _("_Finish Route") );
7677 // Reuse finish track method
7678 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_finish_track), pass_along );
7679 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7680 gtk_widget_show ( item );
7681 // Add separator
7682 item = gtk_menu_item_new ();
7683 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7684 gtk_widget_show ( item );
7685 }
7686
7687 item = gtk_image_menu_item_new_with_mnemonic ( _("_View All Routes") );
7688 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ZOOM_FIT, GTK_ICON_SIZE_MENU) );
7689 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_routes_view), pass_along );
7690 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7691 gtk_widget_show ( item );
7692
7693 item = gtk_image_menu_item_new_with_mnemonic ( _("_New Route") );
7694 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NEW, GTK_ICON_SIZE_MENU) );
7695 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_new_route), pass_along );
7696 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7697 gtk_widget_show ( item );
7698 // Make it available only when a new track *not* already in progress
7699 gtk_widget_set_sensitive ( item, ! (gboolean)GPOINTER_TO_INT(l->current_track) );
7700
7701 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete _All Routes") );
7702 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU) );
7703 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_all_routes), pass_along );
7704 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7705 gtk_widget_show ( item );
7706
7707 item = gtk_image_menu_item_new_with_mnemonic ( _("_Delete Routes From Selection...") );
7708 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
7709 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_routes_from_selection), pass_along );
7710 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7711 gtk_widget_show ( item );
89fdc417
RN
7712
7713 GtkWidget *vis_submenu = gtk_menu_new ();
7714 item = gtk_menu_item_new_with_mnemonic ( _("_Visibility") );
7715 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7716 gtk_widget_show ( item );
7717 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), vis_submenu );
7718
7719 item = gtk_image_menu_item_new_with_mnemonic ( _("_Show All Routes") );
7720 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_APPLY, GTK_ICON_SIZE_MENU) );
7721 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_routes_visibility_on), pass_along );
7722 gtk_menu_shell_append ( GTK_MENU_SHELL(vis_submenu), item );
7723 gtk_widget_show ( item );
7724
7725 item = gtk_image_menu_item_new_with_mnemonic ( _("_Hide All Routes") );
7726 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_CLEAR, GTK_ICON_SIZE_MENU) );
7727 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_routes_visibility_off), pass_along );
7728 gtk_menu_shell_append ( GTK_MENU_SHELL(vis_submenu), item );
7729 gtk_widget_show ( item );
7730
7731 item = gtk_image_menu_item_new_with_mnemonic ( _("_Toggle") );
7732 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REFRESH, GTK_ICON_SIZE_MENU) );
7733 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_routes_visibility_toggle), pass_along );
7734 gtk_menu_shell_append ( GTK_MENU_SHELL(vis_submenu), item );
07c9d42b
RN
7735
7736 item = gtk_image_menu_item_new_with_mnemonic ( _("_List Routes...") );
7737 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
7738 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_track_list_dialog_single), pass_along );
7739 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7740
89fdc417 7741 gtk_widget_show ( item );
43984ba1
RN
7742
7743 item = gtk_menu_item_new_with_mnemonic ( _("_Statistics") );
7744 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_routes_stats), pass_along );
7745 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7746 gtk_widget_show ( item );
0d2b891f
RN
7747 }
7748
c9cac058
RN
7749
7750 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINTS || subtype == VIK_TRW_LAYER_SUBLAYER_TRACKS || subtype == VIK_TRW_LAYER_SUBLAYER_ROUTES ) {
7751 GtkWidget *submenu_sort = gtk_menu_new ();
7752 item = gtk_image_menu_item_new_with_mnemonic ( _("_Sort") );
7753 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_REFRESH, GTK_ICON_SIZE_MENU) );
7754 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7755 gtk_widget_show ( item );
7756 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), submenu_sort );
7757
7758 item = gtk_image_menu_item_new_with_mnemonic ( _("Name _Ascending") );
7759 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_SORT_ASCENDING, GTK_ICON_SIZE_MENU) );
7760 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_sort_order_a2z), pass_along );
7761 gtk_menu_shell_append ( GTK_MENU_SHELL(submenu_sort), item );
7762 gtk_widget_show ( item );
7763
7764 item = gtk_image_menu_item_new_with_mnemonic ( _("Name _Descending") );
7765 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_SORT_DESCENDING, GTK_ICON_SIZE_MENU) );
7766 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_sort_order_z2a), pass_along );
7767 gtk_menu_shell_append ( GTK_MENU_SHELL(submenu_sort), item );
7768 gtk_widget_show ( item );
7769 }
7770
0d2b891f
RN
7771 GtkWidget *upload_submenu = gtk_menu_new ();
7772
7773 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK || subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE )
50a14534
EB
7774 {
7775 item = gtk_menu_item_new ();
7776 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7777 gtk_widget_show ( item );
7778
0d2b891f 7779 if ( l->current_track && subtype == VIK_TRW_LAYER_SUBLAYER_TRACK && !l->current_track->is_route )
37615c52 7780 item = gtk_menu_item_new_with_mnemonic ( _("_Finish Track") );
0d2b891f
RN
7781 if ( l->current_track && subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE && l->current_track->is_route )
7782 item = gtk_menu_item_new_with_mnemonic ( _("_Finish Route") );
7783 if ( l->current_track ) {
37615c52
RN
7784 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_finish_track), pass_along );
7785 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7786 gtk_widget_show ( item );
7787
7788 // Add separator
7789 item = gtk_menu_item_new ();
7790 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7791 gtk_widget_show ( item );
7792 }
7793
0d2b891f
RN
7794 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
7795 item = gtk_image_menu_item_new_with_mnemonic ( _("_View Track") );
7796 else
7797 item = gtk_image_menu_item_new_with_mnemonic ( _("_View Route") );
88a49424
RN
7798 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ZOOM_FIT, GTK_ICON_SIZE_MENU) );
7799 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_auto_track_view), pass_along );
7800 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7801 gtk_widget_show ( item );
7802
626de648
RN
7803 item = gtk_menu_item_new_with_mnemonic ( _("_Statistics") );
7804 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_track_statistics), pass_along );
7805 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
7806 gtk_widget_show ( item );
7807
88a49424 7808 GtkWidget *goto_submenu;
937b36ed 7809 goto_submenu = gtk_menu_new ();
d6de71f9
RN
7810 item = gtk_image_menu_item_new_with_mnemonic ( _("_Goto") );
7811 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_JUMP_TO, GTK_ICON_SIZE_MENU) );
937b36ed
RN
7812 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7813 gtk_widget_show ( item );
7814 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), goto_submenu );
7815
d6de71f9
RN
7816 item = gtk_image_menu_item_new_with_mnemonic ( _("_Startpoint") );
7817 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GOTO_FIRST, GTK_ICON_SIZE_MENU) );
50a14534 7818 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_startpoint), pass_along );
937b36ed 7819 gtk_menu_shell_append ( GTK_MENU_SHELL(goto_submenu), item );
50a14534
EB
7820 gtk_widget_show ( item );
7821
d6de71f9
RN
7822 item = gtk_image_menu_item_new_with_mnemonic ( _("\"_Center\"") );
7823 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_JUMP_TO, GTK_ICON_SIZE_MENU) );
50a14534 7824 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_center), pass_along );
937b36ed 7825 gtk_menu_shell_append ( GTK_MENU_SHELL(goto_submenu), item );
50a14534
EB
7826 gtk_widget_show ( item );
7827
d6de71f9
RN
7828 item = gtk_image_menu_item_new_with_mnemonic ( _("_Endpoint") );
7829 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GOTO_LAST, GTK_ICON_SIZE_MENU) );
50a14534 7830 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_endpoint), pass_along );
937b36ed 7831 gtk_menu_shell_append ( GTK_MENU_SHELL(goto_submenu), item );
50a14534 7832 gtk_widget_show ( item );
111fa174 7833
d6de71f9
RN
7834 item = gtk_image_menu_item_new_with_mnemonic ( _("_Highest Altitude") );
7835 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GOTO_TOP, GTK_ICON_SIZE_MENU) );
c28faca8
RN
7836 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_max_alt), pass_along );
7837 gtk_menu_shell_append ( GTK_MENU_SHELL(goto_submenu), item );
7838 gtk_widget_show ( item );
7839
d6de71f9
RN
7840 item = gtk_image_menu_item_new_with_mnemonic ( _("_Lowest Altitude") );
7841 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GOTO_BOTTOM, GTK_ICON_SIZE_MENU) );
c28faca8
RN
7842 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_min_alt), pass_along );
7843 gtk_menu_shell_append ( GTK_MENU_SHELL(goto_submenu), item );
7844 gtk_widget_show ( item );
7845
0d2b891f
RN
7846 // Routes don't have speeds
7847 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK ) {
7848 item = gtk_image_menu_item_new_with_mnemonic ( _("_Maximum Speed") );
7849 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_MEDIA_FORWARD, GTK_ICON_SIZE_MENU) );
7850 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_goto_track_max_speed), pass_along );
7851 gtk_menu_shell_append ( GTK_MENU_SHELL(goto_submenu), item );
7852 gtk_widget_show ( item );
7853 }
03e7da75 7854
59f9414d
RN
7855 GtkWidget *combine_submenu;
7856 combine_submenu = gtk_menu_new ();
7857 item = gtk_image_menu_item_new_with_mnemonic ( _("Co_mbine") );
7858 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_CONNECT, GTK_ICON_SIZE_MENU) );
7859 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
469113fb 7860 gtk_widget_show ( item );
59f9414d 7861 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), combine_submenu );
469113fb 7862
0d2b891f
RN
7863 // Routes don't have times or segments...
7864 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK ) {
7865 item = gtk_menu_item_new_with_mnemonic ( _("_Merge By Time...") );
7866 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_merge_by_timestamp), pass_along );
7867 gtk_menu_shell_append ( GTK_MENU_SHELL(combine_submenu), item );
7868 gtk_widget_show ( item );
7869
7870 item = gtk_menu_item_new_with_mnemonic ( _("Merge _Segments") );
7871 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_merge_by_segment), pass_along );
7872 gtk_menu_shell_append ( GTK_MENU_SHELL(combine_submenu), item );
7873 gtk_widget_show ( item );
7874 }
111fa174 7875
40a68e7c 7876 item = gtk_menu_item_new_with_mnemonic ( _("Merge _With Other Tracks...") );
291edcab 7877 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_merge_with_other), pass_along );
59f9414d 7878 gtk_menu_shell_append ( GTK_MENU_SHELL(combine_submenu), item );
291edcab
HR
7879 gtk_widget_show ( item );
7880
0d2b891f
RN
7881 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
7882 item = gtk_menu_item_new_with_mnemonic ( _("_Append Track...") );
7883 else
7884 item = gtk_menu_item_new_with_mnemonic ( _("_Append Route...") );
9c34f614
RN
7885 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_append_track), pass_along );
7886 gtk_menu_shell_append ( GTK_MENU_SHELL(combine_submenu), item );
7887 gtk_widget_show ( item );
7888
6b5b6c47
RN
7889 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
7890 item = gtk_menu_item_new_with_mnemonic ( _("Append _Route...") );
7891 else
7892 item = gtk_menu_item_new_with_mnemonic ( _("Append _Track...") );
7893 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_append_other), pass_along );
7894 gtk_menu_shell_append ( GTK_MENU_SHELL(combine_submenu), item );
7895 gtk_widget_show ( item );
7896
78ac928c
RN
7897 GtkWidget *split_submenu;
7898 split_submenu = gtk_menu_new ();
7899 item = gtk_image_menu_item_new_with_mnemonic ( _("_Split") );
7900 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_DISCONNECT, GTK_ICON_SIZE_MENU) );
7901 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
291edcab 7902 gtk_widget_show ( item );
78ac928c 7903 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), split_submenu );
291edcab 7904
0d2b891f
RN
7905 // Routes don't have times or segments...
7906 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK ) {
7907 item = gtk_menu_item_new_with_mnemonic ( _("_Split By Time...") );
7908 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_split_by_timestamp), pass_along );
7909 gtk_menu_shell_append ( GTK_MENU_SHELL(split_submenu), item );
7910 gtk_widget_show ( item );
7911
7912 // ATM always enable this entry - don't want to have to analyse the track before displaying the menu - to keep the menu speedy
7913 item = gtk_menu_item_new_with_mnemonic ( _("Split Se_gments") );
7914 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_split_segments), pass_along );
7915 gtk_menu_shell_append ( GTK_MENU_SHELL(split_submenu), item );
7916 gtk_widget_show ( item );
7917 }
7114e879 7918
7306a492 7919 item = gtk_menu_item_new_with_mnemonic ( _("Split By _Number of Points...") );
af2341f3 7920 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_split_by_n_points), pass_along );
78ac928c 7921 gtk_menu_shell_append ( GTK_MENU_SHELL(split_submenu), item );
af2341f3
RN
7922 gtk_widget_show ( item );
7923
a4aefe69
RN
7924 item = gtk_menu_item_new_with_mnemonic ( _("Split at _Trackpoint") );
7925 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_split_at_trackpoint), pass_along );
7926 gtk_menu_shell_append ( GTK_MENU_SHELL(split_submenu), item );
7927 gtk_widget_show ( item );
7928 // Make it available only when a trackpoint is selected.
7929 gtk_widget_set_sensitive ( item, (gboolean)GPOINTER_TO_INT(l->current_tpl) );
7930
491794be
RN
7931 GtkWidget *insert_submenu = gtk_menu_new ();
7932 item = gtk_image_menu_item_new_with_mnemonic ( _("_Insert Points") );
7933 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ADD, GTK_ICON_SIZE_MENU) );
7934 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7935 gtk_widget_show ( item );
7936 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), insert_submenu );
7937
7938 item = gtk_menu_item_new_with_mnemonic ( _("Insert Point _Before Selected Point") );
7939 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_insert_point_before), pass_along );
7940 gtk_menu_shell_append ( GTK_MENU_SHELL(insert_submenu), item );
7941 gtk_widget_show ( item );
7942 // Make it available only when a point is selected
7943 gtk_widget_set_sensitive ( item, (gboolean)GPOINTER_TO_INT(l->current_tpl) );
7944
7945 item = gtk_menu_item_new_with_mnemonic ( _("Insert Point _After Selected Point") );
7946 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_insert_point_after), pass_along );
7947 gtk_menu_shell_append ( GTK_MENU_SHELL(insert_submenu), item );
7948 gtk_widget_show ( item );
7949 // Make it available only when a point is selected
7950 gtk_widget_set_sensitive ( item, (gboolean)GPOINTER_TO_INT(l->current_tpl) );
7951
b6eda120
RN
7952 GtkWidget *delete_submenu;
7953 delete_submenu = gtk_menu_new ();
7954 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete Poi_nts") );
7955 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_DELETE, GTK_ICON_SIZE_MENU) );
7956 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7957 gtk_widget_show ( item );
7958 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), delete_submenu );
7959
a56baa08
RN
7960 item = gtk_image_menu_item_new_with_mnemonic ( _("Delete _Selected Point") );
7961 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_DELETE, GTK_ICON_SIZE_MENU) );
7962 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_point_selected), pass_along );
7963 gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
7964 gtk_widget_show ( item );
7965 // Make it available only when a point is selected
7966 gtk_widget_set_sensitive ( item, (gboolean)GPOINTER_TO_INT(l->current_tpl) );
7967
b6eda120
RN
7968 item = gtk_menu_item_new_with_mnemonic ( _("Delete Points With The Same _Position") );
7969 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_points_same_position), pass_along );
7970 gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
7971 gtk_widget_show ( item );
7972
6579ca1f
RN
7973 item = gtk_menu_item_new_with_mnemonic ( _("Delete Points With The Same _Time") );
7974 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_delete_points_same_time), pass_along );
7975 gtk_menu_shell_append ( GTK_MENU_SHELL(delete_submenu), item );
af2341f3
RN
7976 gtk_widget_show ( item );
7977
89d310d1
RN
7978 GtkWidget *transform_submenu;
7979 transform_submenu = gtk_menu_new ();
7980 item = gtk_image_menu_item_new_with_mnemonic ( _("_Transform") );
7981 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_CONVERT, GTK_ICON_SIZE_MENU) );
7982 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
7983 gtk_widget_show ( item );
7984 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), transform_submenu );
7985
4d333042
RN
7986 GtkWidget *dem_submenu;
7987 dem_submenu = gtk_menu_new ();
89d310d1
RN
7988 item = gtk_image_menu_item_new_with_mnemonic ( _("_Apply DEM Data") );
7989 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 7990 gtk_menu_shell_append ( GTK_MENU_SHELL(transform_submenu), item );
4d333042
RN
7991 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), dem_submenu );
7992
7993 item = gtk_image_menu_item_new_with_mnemonic ( _("_Overwrite") );
7994 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_apply_dem_data_all), pass_along );
7995 gtk_menu_shell_append ( GTK_MENU_SHELL(dem_submenu), item );
7996 gtk_widget_set_tooltip_text (item, _("Overwrite any existing elevation values with DEM values"));
7997 gtk_widget_show ( item );
7998
7999 item = gtk_image_menu_item_new_with_mnemonic ( _("_Keep Existing") );
8000 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_apply_dem_data_only_missing), pass_along );
8001 gtk_menu_shell_append ( GTK_MENU_SHELL(dem_submenu), item );
8002 gtk_widget_set_tooltip_text (item, _("Keep existing elevation values, only attempt for missing values"));
89d310d1
RN
8003 gtk_widget_show ( item );
8004
81ac2835
RN
8005 GtkWidget *smooth_submenu;
8006 smooth_submenu = gtk_menu_new ();
8007 item = gtk_menu_item_new_with_mnemonic ( _("_Smooth Missing Elevation Data") );
8008 gtk_menu_shell_append ( GTK_MENU_SHELL(transform_submenu), item );
8009 gtk_widget_show ( item );
8010 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), smooth_submenu );
8011
8012 item = gtk_image_menu_item_new_with_mnemonic ( _("_Interpolated") );
8013 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_missing_elevation_data_interp), pass_along );
8014 gtk_menu_shell_append ( GTK_MENU_SHELL(smooth_submenu), item );
8015 gtk_widget_set_tooltip_text (item, _("Interpolate between known elevation values to derive values for the missing elevations"));
8016 gtk_widget_show ( item );
8017
8018 item = gtk_image_menu_item_new_with_mnemonic ( _("_Flat") );
8019 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_missing_elevation_data_flat), pass_along );
8020 gtk_menu_shell_append ( GTK_MENU_SHELL(smooth_submenu), item );
8021 gtk_widget_set_tooltip_text (item, _("Set unknown elevation values to the last known value"));
8022 gtk_widget_show ( item );
8023
89d310d1
RN
8024 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
8025 item = gtk_image_menu_item_new_with_mnemonic ( _("C_onvert to a Route") );
8026 else
8027 item = gtk_image_menu_item_new_with_mnemonic ( _("C_onvert to a Track") );
8028 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_CONVERT, GTK_ICON_SIZE_MENU) );
8029 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_convert_track_route), pass_along );
8030 gtk_menu_shell_append ( GTK_MENU_SHELL(transform_submenu), item );
8031 gtk_widget_show ( item );
8032
76b14439
RN
8033 // Routes don't have timestamps - so this is only available for tracks
8034 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK ) {
8035 item = gtk_image_menu_item_new_with_mnemonic ( _("_Anonymize Times") );
8036 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_anonymize_times), pass_along );
8037 gtk_menu_shell_append ( GTK_MENU_SHELL(transform_submenu), item );
8038 gtk_widget_set_tooltip_text (item, _("Shift timestamps to a relative offset from 1901-01-01"));
8039 gtk_widget_show ( item );
8040 }
8041
0d2b891f
RN
8042 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
8043 item = gtk_image_menu_item_new_with_mnemonic ( _("_Reverse Track") );
8044 else
8045 item = gtk_image_menu_item_new_with_mnemonic ( _("_Reverse Route") );
eb9fd106
RN
8046 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_BACK, GTK_ICON_SIZE_MENU) );
8047 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_reverse), pass_along );
8048 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
8049 gtk_widget_show ( item );
8050
1a3be6a8
GB
8051 if ( subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE ) {
8052 item = gtk_image_menu_item_new_with_mnemonic ( _("Refine Route...") );
8053 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_FIND, GTK_ICON_SIZE_MENU) );
8054 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_route_refine), pass_along );
8055 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
8056 gtk_widget_show ( item );
8057 }
8058
6bb72350
RN
8059 /* ATM This function is only available via the layers panel, due to the method in finding out the maps in use */
8060 if ( vlp ) {
0d2b891f
RN
8061 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
8062 item = gtk_image_menu_item_new_with_mnemonic ( _("Down_load Maps Along Track...") );
8063 else
8064 item = gtk_image_menu_item_new_with_mnemonic ( _("Down_load Maps Along Route...") );
43b5e335 8065 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
8066 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_download_map_along_track_cb), pass_along );
8067 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
8068 gtk_widget_show ( item );
8069 }
ad0a8c2d 8070
0d2b891f
RN
8071 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
8072 item = gtk_image_menu_item_new_with_mnemonic ( _("_Export Track as GPX...") );
8073 else
8074 item = gtk_image_menu_item_new_with_mnemonic ( _("_Export Route as GPX...") );
d6de71f9 8075 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_HARDDISK, GTK_ICON_SIZE_MENU) );
7f6757c4
RN
8076 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_export_gpx_track), pass_along );
8077 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
8078 gtk_widget_show ( item );
8079
0d2b891f
RN
8080 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK )
8081 item = gtk_image_menu_item_new_with_mnemonic ( _("E_xtend Track End") );
8082 else
8083 item = gtk_image_menu_item_new_with_mnemonic ( _("E_xtend Route End") );
d6de71f9 8084 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ADD, GTK_ICON_SIZE_MENU) );
8fb71d6c
EB
8085 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_extend_track_end), pass_along );
8086 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
ad0a8c2d 8087 gtk_widget_show ( item );
5092de80 8088
008e972c
RN
8089 if ( subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE ) {
8090 item = gtk_image_menu_item_new_with_mnemonic ( _("Extend _Using Route Finder") );
43b5e335 8091 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
8092 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_extend_track_end_route_finder), pass_along );
8093 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
8094 gtk_widget_show ( item );
8095 }
a7955c1d 8096
0d2b891f
RN
8097 // ATM can't upload a single waypoint but can do waypoints to a GPS
8098 if ( subtype != VIK_TRW_LAYER_SUBLAYER_WAYPOINT ) {
8099 item = gtk_image_menu_item_new_with_mnemonic ( _("_Upload") );
8100 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_UP, GTK_ICON_SIZE_MENU) );
8101 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
8102 gtk_widget_show ( item );
8103 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), upload_submenu );
8104
8105 item = gtk_image_menu_item_new_with_mnemonic ( _("_Upload to GPS...") );
8106 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_FORWARD, GTK_ICON_SIZE_MENU) );
8107 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_gps_upload_any), pass_along );
8108 gtk_menu_shell_append ( GTK_MENU_SHELL(upload_submenu), item );
8109 gtk_widget_show ( item );
8110 }
8111 }
e50758c7 8112
008e972c
RN
8113#ifdef VIK_CONFIG_GOOGLE
8114 if ( subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE && is_valid_google_route ( l, sublayer ) )
8115 {
8116 item = gtk_image_menu_item_new_with_mnemonic ( _("_View Google Directions") );
8117 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_NETWORK, GTK_ICON_SIZE_MENU) );
8118 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_google_route_webpage), pass_along );
8119 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
8120 gtk_widget_show ( item );
8121 }
8122#endif
8123
0d2b891f
RN
8124 // Some things aren't usable with routes
8125 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK ) {
5092de80 8126#ifdef VIK_CONFIG_OPENSTREETMAP
d6de71f9 8127 item = gtk_image_menu_item_new_with_mnemonic ( _("Upload to _OSM...") );
d6c110f1
RN
8128 // Convert internal pointer into track
8129 pass_along[MA_MISC] = g_hash_table_lookup ( l->tracks, sublayer);
d6de71f9 8130 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_GO_UP, GTK_ICON_SIZE_MENU) );
3185b5d3 8131 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_osm_traces_upload_track_cb), pass_along );
e50758c7 8132 gtk_menu_shell_append ( GTK_MENU_SHELL(upload_submenu), item );
5092de80
GB
8133 gtk_widget_show ( item );
8134#endif
bddd2056 8135
d6de71f9
RN
8136 item = gtk_image_menu_item_new_with_mnemonic ( _("Use with _Filter") );
8137 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INDEX, GTK_ICON_SIZE_MENU) );
28c82d8b
EB
8138 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_track_use_with_filter), pass_along );
8139 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
8140 gtk_widget_show ( item );
8141
6bb72350
RN
8142 /* ATM This function is only available via the layers panel, due to needing a vlp */
8143 if ( vlp ) {
8144 item = a_acquire_track_menu ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(l)), vlp,
0d2b891f
RN
8145 vik_layers_panel_get_viewport(VIK_LAYERS_PANEL(vlp)),
8146 g_hash_table_lookup ( l->tracks, (gchar *) sublayer ) );
6bb72350 8147 if ( item ) {
0d2b891f
RN
8148 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
8149 gtk_widget_show ( item );
6bb72350
RN
8150 }
8151 }
c95d6b00 8152
b3eb3b98 8153#ifdef VIK_CONFIG_GEOTAG
0d2b891f
RN
8154 item = gtk_menu_item_new_with_mnemonic ( _("Geotag _Images...") );
8155 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_geotagging_track), pass_along );
8156 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
8157 gtk_widget_show ( item );
b3eb3b98 8158#endif
0d2b891f 8159 }
b3eb3b98 8160
0d2b891f 8161 if ( subtype == VIK_TRW_LAYER_SUBLAYER_TRACK || subtype == VIK_TRW_LAYER_SUBLAYER_ROUTE ) {
c95d6b00
RN
8162 // Only show on viewport popmenu when a trackpoint is selected
8163 if ( ! vlp && l->current_tpl ) {
8164 // Add separator
8165 item = gtk_menu_item_new ();
8166 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
8167 gtk_widget_show ( item );
8168
8169 item = gtk_image_menu_item_new_with_mnemonic ( _("_Edit Trackpoint") );
8170 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_PROPERTIES, GTK_ICON_SIZE_MENU) );
8171 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_edit_trackpoint), pass_along );
8172 gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
8173 gtk_widget_show ( item );
8174 }
50a14534
EB
8175 }
8176
667fda15
RN
8177 if ( subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINTS || subtype == VIK_TRW_LAYER_SUBLAYER_WAYPOINT ) {
8178 GtkWidget *transform_submenu;
8179 transform_submenu = gtk_menu_new ();
8180 item = gtk_image_menu_item_new_with_mnemonic ( _("_Transform") );
8181 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_CONVERT, GTK_ICON_SIZE_MENU) );
8182 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
8183 gtk_widget_show ( item );
8184 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), transform_submenu );
8185
8186 GtkWidget *dem_submenu;
8187 dem_submenu = gtk_menu_new ();
8188 item = gtk_image_menu_item_new_with_mnemonic ( _("_Apply DEM Data") );
8189 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
8190 gtk_menu_shell_append ( GTK_MENU_SHELL(transform_submenu), item );
8191 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), dem_submenu );
8192
8193 item = gtk_image_menu_item_new_with_mnemonic ( _("_Overwrite") );
8194 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_apply_dem_data_wpt_all), pass_along );
8195 gtk_menu_shell_append ( GTK_MENU_SHELL(dem_submenu), item );
8196 gtk_widget_set_tooltip_text (item, _("Overwrite any existing elevation values with DEM values"));
8197 gtk_widget_show ( item );
8198
8199 item = gtk_image_menu_item_new_with_mnemonic ( _("_Keep Existing") );
8200 g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(trw_layer_apply_dem_data_wpt_only_missing), pass_along );
8201 gtk_menu_shell_append ( GTK_MENU_SHELL(dem_submenu), item );
8202 gtk_widget_set_tooltip_text (item, _("Keep existing elevation values, only attempt for missing values"));
8203 gtk_widget_show ( item );
8204 }
8205
4d333042
RN
8206 gtk_widget_show_all ( GTK_WIDGET(menu) );
8207
50a14534
EB
8208 return rv;
8209}
8210
491794be
RN
8211// TODO: Probably better to rework this track manipulation in viktrack.c
8212static void trw_layer_insert_tp_beside_current_tp ( VikTrwLayer *vtl, gboolean before )
db79f75f 8213{
491794be 8214 // sanity check
db79f75f
RN
8215 if (!vtl->current_tpl)
8216 return;
db79f75f
RN
8217
8218 VikTrackpoint *tp_current = VIK_TRACKPOINT(vtl->current_tpl->data);
491794be
RN
8219 VikTrackpoint *tp_other = NULL;
8220
8221 if ( before ) {
8222 if (!vtl->current_tpl->prev)
8223 return;
8224 tp_other = VIK_TRACKPOINT(vtl->current_tpl->prev->data);
8225 } else {
8226 if (!vtl->current_tpl->next)
8227 return;
8228 tp_other = VIK_TRACKPOINT(vtl->current_tpl->next->data);
8229 }
db79f75f 8230
491794be
RN
8231 // Use current and other trackpoints to form a new track point which is inserted into the tracklist
8232 if ( tp_other ) {
db79f75f
RN
8233
8234 VikTrackpoint *tp_new = vik_trackpoint_new();
491794be 8235 struct LatLon ll_current, ll_other;
db79f75f 8236 vik_coord_to_latlon ( &tp_current->coord, &ll_current );
491794be 8237 vik_coord_to_latlon ( &tp_other->coord, &ll_other );
db79f75f
RN
8238
8239 /* main positional interpolation */
491794be 8240 struct LatLon ll_new = { (ll_current.lat+ll_other.lat)/2, (ll_current.lon+ll_other.lon)/2 };
db79f75f
RN
8241 vik_coord_load_from_latlon ( &(tp_new->coord), vtl->coord_mode, &ll_new );
8242
8243 /* Now other properties that can be interpolated */
491794be 8244 tp_new->altitude = (tp_current->altitude + tp_other->altitude) / 2;
db79f75f 8245
491794be 8246 if (tp_current->has_timestamp && tp_other->has_timestamp) {
db79f75f
RN
8247 /* Note here the division is applied to each part, then added
8248 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 8249 tp_new->timestamp = (tp_current->timestamp/2) + (tp_other->timestamp/2);
db79f75f
RN
8250 tp_new->has_timestamp = TRUE;
8251 }
8252
491794be
RN
8253 if (tp_current->speed != NAN && tp_other->speed != NAN)
8254 tp_new->speed = (tp_current->speed + tp_other->speed)/2;
db79f75f
RN
8255
8256 /* TODO - improve interpolation of course, as it may not be correct.
8257 if courses in degrees are 350 + 020, the mid course more likely to be 005 (not 185)
8258 [similar applies if value is in radians] */
491794be
RN
8259 if (tp_current->course != NAN && tp_other->course != NAN)
8260 tp_new->course = (tp_current->course + tp_other->course)/2;
db79f75f
RN
8261
8262 /* DOP / sat values remain at defaults as not they do not seem applicable to a dreamt up point */
8263
491794be 8264 // Insert new point into the appropriate trackpoint list, either before or after the current trackpoint as directed
0d2b891f
RN
8265 VikTrack *trk = g_hash_table_lookup ( vtl->tracks, vtl->current_tp_id );
8266 if ( !trk )
8267 // Otherwise try routes
8268 trk = g_hash_table_lookup ( vtl->routes, vtl->current_tp_id );
8269 if ( !trk )
8270 return;
8271
8272 gint index = g_list_index ( trk->trackpoints, tp_current );
db79f75f 8273 if ( index > -1 ) {
491794be
RN
8274 if ( !before )
8275 index = index + 1;
20981fd6 8276 // NB no recalculation of bounds since it is inserted between points
491794be 8277 trk->trackpoints = g_list_insert ( trk->trackpoints, tp_new, index );
db79f75f
RN
8278 }
8279 }
8280}
50a14534 8281
50a14534
EB
8282static void trw_layer_cancel_current_tp ( VikTrwLayer *vtl, gboolean destroy )
8283{
8284 if ( vtl->tpwin )
8285 {
8286 if ( destroy)
8287 {
8288 gtk_widget_destroy ( GTK_WIDGET(vtl->tpwin) );
8289 vtl->tpwin = NULL;
8290 }
8291 else
8292 vik_trw_layer_tpwin_set_empty ( vtl->tpwin );
8293 }
8294 if ( vtl->current_tpl )
8295 {
8296 vtl->current_tpl = NULL;
ce4bd1cf
RN
8297 vtl->current_tp_track = NULL;
8298 vtl->current_tp_id = NULL;
da121f9b 8299 vik_layer_emit_update(VIK_LAYER(vtl));
50a14534
EB
8300 }
8301}
8302
8303static void trw_layer_tpwin_response ( VikTrwLayer *vtl, gint response )
8304{
8305 g_assert ( vtl->tpwin != NULL );
8306 if ( response == VIK_TRW_LAYER_TPWIN_CLOSE )
8307 trw_layer_cancel_current_tp ( vtl, TRUE );
0d601fd4
RN
8308
8309 if ( vtl->current_tpl == NULL )
8310 return;
8311
8312 if ( response == VIK_TRW_LAYER_TPWIN_SPLIT && vtl->current_tpl->next && vtl->current_tpl->prev )
50a14534 8313 {
0d2b891f 8314 trw_layer_split_at_selected_trackpoint ( vtl, vtl->current_tp_track->is_route ? VIK_TRW_LAYER_SUBLAYER_ROUTE : VIK_TRW_LAYER_SUBLAYER_TRACK );
84d3d9f9 8315 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, vtl->current_tp_track->name );
50a14534
EB
8316 }
8317 else if ( response == VIK_TRW_LAYER_TPWIN_DELETE )
8318 {
ce4bd1cf 8319 VikTrack *tr = g_hash_table_lookup ( vtl->tracks, vtl->current_tp_id );
0d2b891f
RN
8320 if ( tr == NULL )
8321 tr = g_hash_table_lookup ( vtl->routes, vtl->current_tp_id );
ce4bd1cf
RN
8322 if ( tr == NULL )
8323 return;
50a14534 8324
a56baa08 8325 trw_layer_trackpoint_selected_delete ( vtl, tr );
ab4553c3 8326
a56baa08 8327 if ( vtl->current_tpl )
ab4553c3 8328 // Reset dialog with the available adjacent trackpoint
a56baa08 8329 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, vtl->current_tp_track->name );
50a14534 8330
a56baa08 8331 vik_layer_emit_update(VIK_LAYER(vtl));
50a14534
EB
8332 }
8333 else if ( response == VIK_TRW_LAYER_TPWIN_FORWARD && vtl->current_tpl->next )
8334 {
ce4bd1cf
RN
8335 if ( vtl->current_tp_track )
8336 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl = vtl->current_tpl->next, vtl->current_tp_track->name );
da121f9b 8337 vik_layer_emit_update(VIK_LAYER(vtl)); /* TODO longone: either move or only update if tp is inside drawing window */
50a14534
EB
8338 }
8339 else if ( response == VIK_TRW_LAYER_TPWIN_BACK && vtl->current_tpl->prev )
8340 {
ce4bd1cf
RN
8341 if ( vtl->current_tp_track )
8342 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl = vtl->current_tpl->prev, vtl->current_tp_track->name );
da121f9b 8343 vik_layer_emit_update(VIK_LAYER(vtl));
50a14534 8344 }
2880a1de
RN
8345 else if ( response == VIK_TRW_LAYER_TPWIN_INSERT && vtl->current_tpl->next )
8346 {
491794be 8347 trw_layer_insert_tp_beside_current_tp ( vtl, FALSE );
da121f9b 8348 vik_layer_emit_update(VIK_LAYER(vtl));
2880a1de 8349 }
50a14534 8350 else if ( response == VIK_TRW_LAYER_TPWIN_DATA_CHANGED )
da121f9b 8351 vik_layer_emit_update(VIK_LAYER(vtl));
50a14534
EB
8352}
8353
d6175f49
RN
8354/**
8355 * trw_layer_dialog_shift:
8356 * @vertical: The reposition strategy. If Vertical moves dialog vertically, otherwise moves it horizontally
8357 *
8358 * Try to reposition a dialog if it's over the specified coord
8359 * so to not obscure the item of interest
8360 */
8361void trw_layer_dialog_shift ( VikTrwLayer *vtl, GtkWindow *dialog, VikCoord *coord, gboolean vertical )
8362{
8363 GtkWindow *parent = VIK_GTK_WINDOW_FROM_LAYER(vtl); //i.e. the main window
8364
06ce5b5a
RN
8365 // Attempt force dialog to be shown so we can find out where it is more reliably...
8366 while ( gtk_events_pending() )
8367 gtk_main_iteration ();
8368
d6175f49
RN
8369 // get parent window position & size
8370 gint win_pos_x, win_pos_y;
8371 gtk_window_get_position ( parent, &win_pos_x, &win_pos_y );
8372
8373 gint win_size_x, win_size_y;
8374 gtk_window_get_size ( parent, &win_size_x, &win_size_y );
8375
8376 // get own dialog size
8377 gint dia_size_x, dia_size_y;
8378 gtk_window_get_size ( dialog, &dia_size_x, &dia_size_y );
8379
8380 // get own dialog position
8381 gint dia_pos_x, dia_pos_y;
8382 gtk_window_get_position ( dialog, &dia_pos_x, &dia_pos_y );
8383
8384 // Dialog not 'realized'/positioned - so can't really do any repositioning logic
8385 if ( dia_pos_x > 2 && dia_pos_y > 2 ) {
8386
8387 VikViewport *vvp = vik_window_viewport ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl)) );
8388
8389 gint vp_xx, vp_yy; // In viewport pixels
8390 vik_viewport_coord_to_screen ( vvp, coord, &vp_xx, &vp_yy );
8391
8392 // Work out the 'bounding box' in pixel terms of the dialog and only move it when over the position
8393
8394 gint dest_x = 0;
8395 gint dest_y = 0;
8396 if ( gtk_widget_translate_coordinates ( GTK_WIDGET(vvp), GTK_WIDGET(parent), 0, 0, &dest_x, &dest_y ) ) {
8397
8398 // Transform Viewport pixels into absolute pixels
8399 gint tmp_xx = vp_xx + dest_x + win_pos_x - 10;
8400 gint tmp_yy = vp_yy + dest_y + win_pos_y - 10;
8401
8402 // Is dialog over the point (to within an ^^ edge value)
8403 if ( (tmp_xx > dia_pos_x) && (tmp_xx < (dia_pos_x + dia_size_x)) &&
8404 (tmp_yy > dia_pos_y) && (tmp_yy < (dia_pos_y + dia_size_y)) ) {
8405
8406 if ( vertical ) {
8407 // Shift up<->down
8408 gint hh = vik_viewport_get_height ( vvp );
8409
8410 // Consider the difference in viewport to the full window
8411 gint offset_y = dest_y;
8412 // Add difference between dialog and window sizes
8413 offset_y += win_pos_y + (hh/2 - dia_size_y)/2;
8414
8415 if ( vp_yy > hh/2 ) {
8416 // Point in bottom half, move window to top half
8417 gtk_window_move ( dialog, dia_pos_x, offset_y );
8418 }
8419 else {
8420 // Point in top half, move dialog down
8421 gtk_window_move ( dialog, dia_pos_x, hh/2 + offset_y );
8422 }
8423 }
8424 else {
8425 // Shift left<->right
8426 gint ww = vik_viewport_get_width ( vvp );
8427
8428 // Consider the difference in viewport to the full window
8429 gint offset_x = dest_x;
8430 // Add difference between dialog and window sizes
8431 offset_x += win_pos_x + (ww/2 - dia_size_x)/2;
8432
8433 if ( vp_xx > ww/2 ) {
8434 // Point on right, move window to left
8435 gtk_window_move ( dialog, offset_x, dia_pos_y );
8436 }
8437 else {
8438 // Point on left, move right
8439 gtk_window_move ( dialog, ww/2 + offset_x, dia_pos_y );
8440 }
8441 }
8442 }
8443 }
8444 }
8445}
8446
50a14534
EB
8447static void trw_layer_tpwin_init ( VikTrwLayer *vtl )
8448{
8449 if ( ! vtl->tpwin )
8450 {
8451 vtl->tpwin = vik_trw_layer_tpwin_new ( VIK_GTK_WINDOW_FROM_LAYER(vtl) );
8452 g_signal_connect_swapped ( GTK_DIALOG(vtl->tpwin), "response", G_CALLBACK(trw_layer_tpwin_response), vtl );
8453 /* connect signals -- DELETE SIGNAL VERY IMPORTANT TO SET TO NULL */
8454 g_signal_connect_swapped ( vtl->tpwin, "delete-event", G_CALLBACK(trw_layer_cancel_current_tp), vtl );
d6175f49 8455
50a14534 8456 gtk_widget_show_all ( GTK_WIDGET(vtl->tpwin) );
d6175f49
RN
8457
8458 if ( vtl->current_tpl ) {
8459 // get tp pixel position
8460 VikTrackpoint *tp = VIK_TRACKPOINT(vtl->current_tpl->data);
8461
8462 // Shift up<->down to try not to obscure the trackpoint.
8463 trw_layer_dialog_shift ( vtl, GTK_WINDOW(vtl->tpwin), &(tp->coord), TRUE );
8464 }
50a14534 8465 }
d6175f49 8466
50a14534 8467 if ( vtl->current_tpl )
ce4bd1cf
RN
8468 if ( vtl->current_tp_track )
8469 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, vtl->current_tp_track->name );
50a14534
EB
8470 /* set layer name and TP data */
8471}
8472
941aa6e9
AF
8473/***************************************************************************
8474 ** Tool code
8475 ***************************************************************************/
50a14534 8476
941aa6e9 8477/*** Utility data structures and functions ****/
50a14534
EB
8478
8479typedef struct {
8480 gint x, y;
8481 gint closest_x, closest_y;
7d961c6c 8482 gboolean draw_images;
c9570f86 8483 gpointer *closest_wp_id;
50a14534
EB
8484 VikWaypoint *closest_wp;
8485 VikViewport *vvp;
8486} WPSearchParams;
8487
941aa6e9
AF
8488typedef struct {
8489 gint x, y;
8490 gint closest_x, closest_y;
ce4bd1cf 8491 gpointer closest_track_id;
941aa6e9
AF
8492 VikTrackpoint *closest_tp;
8493 VikViewport *vvp;
8494 GList *closest_tpl;
79773236 8495 LatLonBBox bbox;
941aa6e9
AF
8496} TPSearchParams;
8497
c9570f86 8498static void waypoint_search_closest_tp ( gpointer id, VikWaypoint *wp, WPSearchParams *params )
50a14534
EB
8499{
8500 gint x, y;
8501 if ( !wp->visible )
8502 return;
8503
8504 vik_viewport_coord_to_screen ( params->vvp, &(wp->coord), &x, &y );
59b0a97a
RN
8505
8506 // If waypoint has an image then use the image size to select
7d961c6c 8507 if ( params->draw_images && wp->image ) {
59b0a97a
RN
8508 gint slackx, slacky;
8509 slackx = wp->image_width / 2;
8510 slacky = wp->image_height / 2;
8511
8512 if ( x <= params->x + slackx && x >= params->x - slackx
8513 && y <= params->y + slacky && y >= params->y - slacky ) {
c9570f86 8514 params->closest_wp_id = id;
59b0a97a
RN
8515 params->closest_wp = wp;
8516 params->closest_x = x;
8517 params->closest_y = y;
8518 }
50a14534 8519 }
59b0a97a
RN
8520 else if ( abs (x - params->x) <= WAYPOINT_SIZE_APPROX && abs (y - params->y) <= WAYPOINT_SIZE_APPROX &&
8521 ((!params->closest_wp) || /* was the old waypoint we already found closer than this one? */
8522 abs(x - params->x)+abs(y - params->y) < abs(x - params->closest_x)+abs(y - params->closest_y)))
8523 {
c9570f86 8524 params->closest_wp_id = id;
59b0a97a
RN
8525 params->closest_wp = wp;
8526 params->closest_x = x;
8527 params->closest_y = y;
8528 }
50a14534
EB
8529}
8530
ce4bd1cf 8531static void track_search_closest_tp ( gpointer id, VikTrack *t, TPSearchParams *params )
50a14534 8532{
941aa6e9
AF
8533 GList *tpl = t->trackpoints;
8534 VikTrackpoint *tp;
50a14534 8535
941aa6e9
AF
8536 if ( !t->visible )
8537 return;
50a14534 8538
79773236
RN
8539 if ( ! BBOX_INTERSECT ( t->bbox, params->bbox ) )
8540 return;
8541
941aa6e9
AF
8542 while (tpl)
8543 {
8544 gint x, y;
8545 tp = VIK_TRACKPOINT(tpl->data);
8546
8547 vik_viewport_coord_to_screen ( params->vvp, &(tp->coord), &x, &y );
8548
8549 if ( abs (x - params->x) <= TRACKPOINT_SIZE_APPROX && abs (y - params->y) <= TRACKPOINT_SIZE_APPROX &&
8550 ((!params->closest_tp) || /* was the old trackpoint we already found closer than this one? */
8551 abs(x - params->x)+abs(y - params->y) < abs(x - params->closest_x)+abs(y - params->closest_y)))
50a14534 8552 {
ce4bd1cf 8553 params->closest_track_id = id;
941aa6e9
AF
8554 params->closest_tp = tp;
8555 params->closest_tpl = tpl;
8556 params->closest_x = x;
8557 params->closest_y = y;
50a14534 8558 }
941aa6e9 8559 tpl = tpl->next;
50a14534 8560 }
941aa6e9
AF
8561}
8562
0d2b891f
RN
8563// ATM: Leave this as 'Track' only.
8564// Not overly bothered about having a snap to route trackpoint capability
941aa6e9
AF
8565static VikTrackpoint *closest_tp_in_five_pixel_interval ( VikTrwLayer *vtl, VikViewport *vvp, gint x, gint y )
8566{
8567 TPSearchParams params;
8568 params.x = x;
8569 params.y = y;
8570 params.vvp = vvp;
ce4bd1cf 8571 params.closest_track_id = NULL;
941aa6e9 8572 params.closest_tp = NULL;
79773236 8573 vik_viewport_get_min_max_lat_lon ( params.vvp, &(params.bbox.south), &(params.bbox.north), &(params.bbox.west), &(params.bbox.east) );
941aa6e9
AF
8574 g_hash_table_foreach ( vtl->tracks, (GHFunc) track_search_closest_tp, &params);
8575 return params.closest_tp;
50a14534
EB
8576}
8577
8578static VikWaypoint *closest_wp_in_five_pixel_interval ( VikTrwLayer *vtl, VikViewport *vvp, gint x, gint y )
8579{
8580 WPSearchParams params;
8581 params.x = x;
8582 params.y = y;
8583 params.vvp = vvp;
7d961c6c 8584 params.draw_images = vtl->drawimages;
50a14534 8585 params.closest_wp = NULL;
c9570f86 8586 params.closest_wp_id = NULL;
50a14534
EB
8587 g_hash_table_foreach ( vtl->waypoints, (GHFunc) waypoint_search_closest_tp, &params);
8588 return params.closest_wp;
8589}
8590
95d1b757 8591
08f14055
RN
8592// Some forward declarations
8593static void marker_begin_move ( tool_ed_t *t, gint x, gint y );
8594static void marker_moveto ( tool_ed_t *t, gint x, gint y );
8595static void marker_end_move ( tool_ed_t *t );
8596//
8597
8598static gboolean trw_layer_select_move ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp, tool_ed_t* t )
8599{
8600 if ( t->holding ) {
8601 VikCoord new_coord;
8602 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
8603
8604 // Here always allow snapping back to the original location
8605 // this is useful when one decides not to move the thing afterall
8606 // If one wants to move the item only a little bit then don't hold down the 'snap' key!
8607
8608 // snap to TP
8609 if ( event->state & GDK_CONTROL_MASK )
8610 {
8611 VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
8612 if ( tp )
8613 new_coord = tp->coord;
8614 }
8615
8616 // snap to WP
8617 if ( event->state & GDK_SHIFT_MASK )
8618 {
8619 VikWaypoint *wp = closest_wp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
8620 if ( wp )
8621 new_coord = wp->coord;
8622 }
8623
8624 gint x, y;
8625 vik_viewport_coord_to_screen ( vvp, &new_coord, &x, &y );
8626
8627 marker_moveto ( t, x, y );
8628
8629 return TRUE;
8630 }
8631 return FALSE;
8632}
8633
8634static gboolean trw_layer_select_release ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp, tool_ed_t* t )
8635{
8636 if ( t->holding && event->button == 1 )
8637 {
8638 VikCoord new_coord;
8639 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
8640
8641 // snap to TP
8642 if ( event->state & GDK_CONTROL_MASK )
8643 {
8644 VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
8645 if ( tp )
8646 new_coord = tp->coord;
8647 }
8648
8649 // snap to WP
8650 if ( event->state & GDK_SHIFT_MASK )
8651 {
8652 VikWaypoint *wp = closest_wp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
8653 if ( wp )
8654 new_coord = wp->coord;
8655 }
8656
8657 marker_end_move ( t );
8658
8659 // Determine if working on a waypoint or a trackpoint
aa0665e5 8660 if ( t->is_waypoint ) {
bd78ae7d 8661 // Update waypoint position
08f14055 8662 vtl->current_wp->coord = new_coord;
aa0665e5 8663 trw_layer_calculate_bounds_waypoints ( vtl );
bd78ae7d
RN
8664 // Reset waypoint pointer
8665 vtl->current_wp = NULL;
8666 vtl->current_wp_id = NULL;
aa0665e5 8667 }
08f14055
RN
8668 else {
8669 if ( vtl->current_tpl ) {
ce4bd1cf 8670 VIK_TRACKPOINT(vtl->current_tpl->data)->coord = new_coord;
20981fd6
RN
8671
8672 if ( vtl->current_tp_track )
8673 vik_track_calculate_bounds ( vtl->current_tp_track );
8674
bd78ae7d 8675 if ( vtl->tpwin )
ce4bd1cf
RN
8676 if ( vtl->current_tp_track )
8677 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, vtl->current_tp_track->name );
bd78ae7d 8678 // NB don't reset the selected trackpoint, thus ensuring it's still in the tpwin
08f14055
RN
8679 }
8680 }
8681
da121f9b 8682 vik_layer_emit_update ( VIK_LAYER(vtl) );
08f14055
RN
8683 return TRUE;
8684 }
8685 return FALSE;
8686}
8687
77ad64fa
RN
8688/*
8689 Returns true if a waypoint or track is found near the requested event position for this particular layer
8690 The item found is automatically selected
8691 This is a tool like feature but routed via the layer interface, since it's instigated by a 'global' layer tool in vikwindow.c
8692 */
08f14055 8693static gboolean trw_layer_select_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp, tool_ed_t* tet )
77ad64fa
RN
8694{
8695 if ( event->button != 1 )
8696 return FALSE;
8697
8698 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
8699 return FALSE;
8700
0d2b891f 8701 if ( !vtl->tracks_visible && !vtl->waypoints_visible && !vtl->routes_visible )
77ad64fa
RN
8702 return FALSE;
8703
79773236
RN
8704 LatLonBBox bbox;
8705 vik_viewport_get_min_max_lat_lon ( vvp, &(bbox.south), &(bbox.north), &(bbox.west), &(bbox.east) );
8706
08f14055 8707 // 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 8708
79773236 8709 if ( vtl->waypoints_visible && BBOX_INTERSECT (vtl->waypoints_bbox, bbox ) ) {
77ad64fa
RN
8710 WPSearchParams wp_params;
8711 wp_params.vvp = vvp;
8712 wp_params.x = event->x;
8713 wp_params.y = event->y;
7d961c6c 8714 wp_params.draw_images = vtl->drawimages;
c9570f86 8715 wp_params.closest_wp_id = NULL;
77ad64fa
RN
8716 wp_params.closest_wp = NULL;
8717
8718 g_hash_table_foreach ( vtl->waypoints, (GHFunc) waypoint_search_closest_tp, &wp_params);
8719
8720 if ( wp_params.closest_wp ) {
08f14055
RN
8721
8722 // Select
c9570f86 8723 vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, g_hash_table_lookup ( vtl->waypoints_iters, wp_params.closest_wp_id ), TRUE );
08f14055
RN
8724
8725 // Too easy to move it so must be holding shift to start immediately moving it
3c73e6c4 8726 // or otherwise be previously selected but not have an image (otherwise clicking within image bounds (again) moves it)
08f14055 8727 if ( event->state & GDK_SHIFT_MASK ||
3c73e6c4 8728 ( vtl->current_wp == wp_params.closest_wp && !vtl->current_wp->image ) ) {
08f14055
RN
8729 // Put into 'move buffer'
8730 // NB vvp & vw already set in tet
8731 tet->vtl = (gpointer)vtl;
8732 tet->is_waypoint = TRUE;
8733
8734 marker_begin_move (tet, event->x, event->y);
8735 }
8736
c9570f86
RN
8737 vtl->current_wp = wp_params.closest_wp;
8738 vtl->current_wp_id = wp_params.closest_wp_id;
08f14055 8739
89af1ff6
RN
8740 if ( event->type == GDK_2BUTTON_PRESS ) {
8741 if ( vtl->current_wp->image ) {
8742 menu_array_sublayer values;
8743 values[MA_VTL] = vtl;
8744 values[MA_MISC] = vtl->current_wp->image;
8745 trw_layer_show_picture ( values );
8746 }
8747 }
8748
da121f9b 8749 vik_layer_emit_update ( VIK_LAYER(vtl) );
08f14055 8750
77ad64fa
RN
8751 return TRUE;
8752 }
8753 }
8754
0d2b891f
RN
8755 // Used for both track and route lists
8756 TPSearchParams tp_params;
8757 tp_params.vvp = vvp;
8758 tp_params.x = event->x;
8759 tp_params.y = event->y;
8760 tp_params.closest_track_id = NULL;
8761 tp_params.closest_tp = NULL;
9414408e 8762 tp_params.closest_tpl = NULL;
79773236 8763 tp_params.bbox = bbox;
77ad64fa 8764
0d2b891f 8765 if (vtl->tracks_visible) {
77ad64fa
RN
8766 g_hash_table_foreach ( vtl->tracks, (GHFunc) track_search_closest_tp, &tp_params);
8767
8768 if ( tp_params.closest_tp ) {
08f14055
RN
8769
8770 // Always select + highlight the track
ce4bd1cf 8771 vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, g_hash_table_lookup ( vtl->tracks_iters, tp_params.closest_track_id ), TRUE );
08f14055
RN
8772
8773 tet->is_waypoint = FALSE;
8774
8775 // Select the Trackpoint
8776 // Can move it immediately when control held or it's the previously selected tp
8777 if ( event->state & GDK_CONTROL_MASK ||
8778 vtl->current_tpl == tp_params.closest_tpl ) {
8779 // Put into 'move buffer'
8780 // NB vvp & vw already set in tet
8781 tet->vtl = (gpointer)vtl;
8782 marker_begin_move (tet, event->x, event->y);
8783 }
8784
8785 vtl->current_tpl = tp_params.closest_tpl;
ce4bd1cf
RN
8786 vtl->current_tp_id = tp_params.closest_track_id;
8787 vtl->current_tp_track = g_hash_table_lookup ( vtl->tracks, tp_params.closest_track_id );
08f14055 8788
95d1b757
RN
8789 set_statusbar_msg_info_trkpt ( vtl, tp_params.closest_tp );
8790
08f14055 8791 if ( vtl->tpwin )
ce4bd1cf 8792 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, vtl->current_tp_track->name );
08f14055 8793
da121f9b 8794 vik_layer_emit_update ( VIK_LAYER(vtl) );
77ad64fa
RN
8795 return TRUE;
8796 }
8797 }
8798
0d2b891f
RN
8799 // Try again for routes
8800 if (vtl->routes_visible) {
8801 g_hash_table_foreach ( vtl->routes, (GHFunc) track_search_closest_tp, &tp_params);
8802
8803 if ( tp_params.closest_tp ) {
8804
8805 // Always select + highlight the track
8806 vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, g_hash_table_lookup ( vtl->routes_iters, tp_params.closest_track_id ), TRUE );
8807
8808 tet->is_waypoint = FALSE;
8809
8810 // Select the Trackpoint
8811 // Can move it immediately when control held or it's the previously selected tp
8812 if ( event->state & GDK_CONTROL_MASK ||
8813 vtl->current_tpl == tp_params.closest_tpl ) {
8814 // Put into 'move buffer'
8815 // NB vvp & vw already set in tet
8816 tet->vtl = (gpointer)vtl;
8817 marker_begin_move (tet, event->x, event->y);
8818 }
8819
8820 vtl->current_tpl = tp_params.closest_tpl;
8821 vtl->current_tp_id = tp_params.closest_track_id;
8822 vtl->current_tp_track = g_hash_table_lookup ( vtl->routes, tp_params.closest_track_id );
8823
8824 set_statusbar_msg_info_trkpt ( vtl, tp_params.closest_tp );
8825
8826 if ( vtl->tpwin )
8827 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, vtl->current_tp_track->name );
8828
da121f9b 8829 vik_layer_emit_update ( VIK_LAYER(vtl) );
0d2b891f
RN
8830 return TRUE;
8831 }
8832 }
8833
77ad64fa 8834 /* these aren't the droids you're looking for */
c9570f86
RN
8835 vtl->current_wp = NULL;
8836 vtl->current_wp_id = NULL;
08f14055
RN
8837 trw_layer_cancel_current_tp ( vtl, FALSE );
8838
95d1b757
RN
8839 // Blank info
8840 vik_statusbar_set_message ( vik_window_get_statusbar (VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl))), VIK_STATUSBAR_INFO, "" );
8841
77ad64fa
RN
8842 return FALSE;
8843}
8844
e46f259a
RN
8845static gboolean trw_layer_show_selected_viewport_menu ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
8846{
8847 if ( event->button != 3 )
8848 return FALSE;
8849
8850 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
8851 return FALSE;
8852
0d2b891f 8853 if ( !vtl->tracks_visible && !vtl->waypoints_visible && !vtl->routes_visible )
e46f259a
RN
8854 return FALSE;
8855
8856 /* Post menu for the currently selected item */
8857
8858 /* See if a track is selected */
8859 VikTrack *track = (VikTrack*)vik_window_get_selected_track ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(vtl) );
8860 if ( track && track->visible ) {
8861
60a69560 8862 if ( track->name ) {
e46f259a
RN
8863
8864 if ( vtl->track_right_click_menu )
1a7a0378 8865 g_object_ref_sink ( G_OBJECT(vtl->track_right_click_menu) );
e46f259a
RN
8866
8867 vtl->track_right_click_menu = GTK_MENU ( gtk_menu_new () );
8868
60a69560
RN
8869 trku_udata udataU;
8870 udataU.trk = track;
8871 udataU.uuid = NULL;
8872
0d2b891f
RN
8873 gpointer *trkf;
8874 if ( track->is_route )
8875 trkf = g_hash_table_find ( vtl->routes, (GHRFunc) trw_layer_track_find_uuid, &udataU );
8876 else
8877 trkf = g_hash_table_find ( vtl->tracks, (GHRFunc) trw_layer_track_find_uuid, &udataU );
60a69560
RN
8878
8879 if ( trkf && udataU.uuid ) {
8880
0d2b891f
RN
8881 GtkTreeIter *iter;
8882 if ( track->is_route )
8883 iter = g_hash_table_lookup ( vtl->routes_iters, udataU.uuid );
8884 else
8885 iter = g_hash_table_lookup ( vtl->tracks_iters, udataU.uuid );
60a69560
RN
8886
8887 trw_layer_sublayer_add_menu_items ( vtl,
8888 vtl->track_right_click_menu,
8889 NULL,
0d2b891f 8890 track->is_route ? VIK_TRW_LAYER_SUBLAYER_ROUTE : VIK_TRW_LAYER_SUBLAYER_TRACK,
60a69560
RN
8891 udataU.uuid,
8892 iter,
8893 vvp );
8894 }
e46f259a
RN
8895
8896 gtk_menu_popup ( vtl->track_right_click_menu, NULL, NULL, NULL, NULL, event->button, gtk_get_current_event_time() );
8897
8898 return TRUE;
8899 }
8900 }
8901
8902 /* See if a waypoint is selected */
8903 VikWaypoint *waypoint = (VikWaypoint*)vik_window_get_selected_waypoint ( (VikWindow *)VIK_GTK_WINDOW_FROM_LAYER(vtl) );
8904 if ( waypoint && waypoint->visible ) {
60a69560 8905 if ( waypoint->name ) {
e46f259a
RN
8906
8907 if ( vtl->wp_right_click_menu )
1a7a0378 8908 g_object_ref_sink ( G_OBJECT(vtl->wp_right_click_menu) );
e46f259a
RN
8909
8910 vtl->wp_right_click_menu = GTK_MENU ( gtk_menu_new () );
60a69560
RN
8911
8912 wpu_udata udata;
8913 udata.wp = waypoint;
8914 udata.uuid = NULL;
8915
8916 gpointer *wpf = g_hash_table_find ( vtl->waypoints, (GHRFunc) trw_layer_waypoint_find_uuid, (gpointer) &udata );
8917
8918 if ( wpf && udata.uuid ) {
8919 GtkTreeIter *iter = g_hash_table_lookup ( vtl->waypoints_iters, udata.uuid );
8920
8921 trw_layer_sublayer_add_menu_items ( vtl,
8922 vtl->wp_right_click_menu,
8923 NULL,
8924 VIK_TRW_LAYER_SUBLAYER_WAYPOINT,
8925 udata.uuid,
8926 iter,
8927 vvp );
8928 }
e46f259a
RN
8929 gtk_menu_popup ( vtl->wp_right_click_menu, NULL, NULL, NULL, NULL, event->button, gtk_get_current_event_time() );
8930
8931 return TRUE;
8932 }
8933 }
8934
8935 return FALSE;
8936}
8937
7432fddf
AF
8938/* background drawing hook, to be passed the viewport */
8939static gboolean tool_sync_done = TRUE;
8940
8941static gboolean tool_sync(gpointer data)
8942{
8943 VikViewport *vvp = data;
8944 gdk_threads_enter();
8945 vik_viewport_sync(vvp);
8946 tool_sync_done = TRUE;
8947 gdk_threads_leave();
8948 return FALSE;
8949}
8950
7432fddf
AF
8951static void marker_begin_move ( tool_ed_t *t, gint x, gint y )
8952{
8953 t->holding = TRUE;
8954 t->gc = vik_viewport_new_gc (t->vvp, "black", 2);
8955 gdk_gc_set_function ( t->gc, GDK_INVERT );
8956 vik_viewport_draw_rectangle ( t->vvp, t->gc, FALSE, x-3, y-3, 6, 6 );
8957 vik_viewport_sync(t->vvp);
8958 t->oldx = x;
8959 t->oldy = y;
8960}
8961
8962static void marker_moveto ( tool_ed_t *t, gint x, gint y )
8963{
8964 VikViewport *vvp = t->vvp;
8965 vik_viewport_draw_rectangle ( vvp, t->gc, FALSE, t->oldx-3, t->oldy-3, 6, 6 );
8966 vik_viewport_draw_rectangle ( vvp, t->gc, FALSE, x-3, y-3, 6, 6 );
8967 t->oldx = x;
8968 t->oldy = y;
7b203521 8969
7432fddf
AF
8970 if (tool_sync_done) {
8971 g_idle_add_full (G_PRIORITY_HIGH_IDLE + 10, tool_sync, vvp, NULL);
8972 tool_sync_done = FALSE;
8973 }
8974}
8975
8976static void marker_end_move ( tool_ed_t *t )
8977{
8978 vik_viewport_draw_rectangle ( t->vvp, t->gc, FALSE, t->oldx-3, t->oldy-3, 6, 6 );
8979 g_object_unref ( t->gc );
8980 t->holding = FALSE;
8981}
8982
941aa6e9
AF
8983/*** Edit waypoint ****/
8984
8985static gpointer tool_edit_waypoint_create ( VikWindow *vw, VikViewport *vvp)
8986{
7432fddf
AF
8987 tool_ed_t *t = g_new(tool_ed_t, 1);
8988 t->vvp = vvp;
8989 t->holding = FALSE;
8990 return t;
941aa6e9
AF
8991}
8992
919ed63e
RN
8993static void tool_edit_waypoint_destroy ( tool_ed_t *t )
8994{
8995 g_free ( t );
8996}
8997
7432fddf 8998static gboolean tool_edit_waypoint_click ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data )
50a14534
EB
8999{
9000 WPSearchParams params;
7432fddf
AF
9001 tool_ed_t *t = data;
9002 VikViewport *vvp = t->vvp;
50a14534 9003
941aa6e9
AF
9004 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
9005 return FALSE;
7432fddf
AF
9006
9007 if ( t->holding ) {
9008 return TRUE;
9009 }
9010
87741170
RN
9011 if ( !vtl->vl.visible || !vtl->waypoints_visible )
9012 return FALSE;
9013
50a14534
EB
9014 if ( vtl->current_wp && vtl->current_wp->visible )
9015 {
9016 /* first check if current WP is within area (other may be 'closer', but we want to move the current) */
9017 gint x, y;
9018 vik_viewport_coord_to_screen ( vvp, &(vtl->current_wp->coord), &x, &y );
9019
c75d78d7
AF
9020 if ( abs(x - event->x) <= WAYPOINT_SIZE_APPROX &&
9021 abs(y - event->y) <= WAYPOINT_SIZE_APPROX )
50a14534
EB
9022 {
9023 if ( event->button == 3 )
9024 vtl->waypoint_rightclick = TRUE; /* remember that we're clicking; other layers will ignore release signal */
7432fddf
AF
9025 else {
9026 marker_begin_move(t, event->x, event->y);
9027 }
50a14534
EB
9028 return TRUE;
9029 }
9030 }
9031
9032 params.vvp = vvp;
9033 params.x = event->x;
9034 params.y = event->y;
7d961c6c 9035 params.draw_images = vtl->drawimages;
c9570f86 9036 params.closest_wp_id = NULL;
50a14534
EB
9037 params.closest_wp = NULL;
9038 g_hash_table_foreach ( vtl->waypoints, (GHFunc) waypoint_search_closest_tp, &params);
98356138 9039 if ( vtl->current_wp && (vtl->current_wp == params.closest_wp) )
50a14534 9040 {
7432fddf 9041 marker_begin_move(t, event->x, event->y);
d84ade77 9042 return FALSE;
50a14534
EB
9043 }
9044 else if ( params.closest_wp )
9045 {
9046 if ( event->button == 3 )
9047 vtl->waypoint_rightclick = TRUE; /* remember that we're clicking; other layers will ignore release signal */
9048 else
9049 vtl->waypoint_rightclick = FALSE;
9050
c9570f86 9051 vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, g_hash_table_lookup ( vtl->waypoints_iters, params.closest_wp_id ), TRUE );
8623d370 9052
50a14534 9053 vtl->current_wp = params.closest_wp;
c9570f86 9054 vtl->current_wp_id = params.closest_wp_id;
50a14534 9055
50a14534 9056 /* could make it so don't update if old WP is off screen and new is null but oh well */
da121f9b 9057 vik_layer_emit_update ( VIK_LAYER(vtl) );
50a14534
EB
9058 return TRUE;
9059 }
9060
9061 vtl->current_wp = NULL;
c9570f86 9062 vtl->current_wp_id = NULL;
50a14534 9063 vtl->waypoint_rightclick = FALSE;
da121f9b 9064 vik_layer_emit_update ( VIK_LAYER(vtl) );
7432fddf
AF
9065 return FALSE;
9066}
9067
dc2c040e 9068static gboolean tool_edit_waypoint_move ( VikTrwLayer *vtl, GdkEventMotion *event, gpointer data )
7432fddf
AF
9069{
9070 tool_ed_t *t = data;
9071 VikViewport *vvp = t->vvp;
9072
9073 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
9074 return FALSE;
9075
9076 if ( t->holding ) {
9077 VikCoord new_coord;
9078 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
9079
9080 /* snap to TP */
9081 if ( event->state & GDK_CONTROL_MASK )
9082 {
9083 VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
9084 if ( tp )
9085 new_coord = tp->coord;
9086 }
9087
9088 /* snap to WP */
9089 if ( event->state & GDK_SHIFT_MASK )
9090 {
9091 VikWaypoint *wp = closest_wp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
9092 if ( wp && wp != vtl->current_wp )
9093 new_coord = wp->coord;
9094 }
9095
9096 {
9097 gint x, y;
9098 vik_viewport_coord_to_screen ( vvp, &new_coord, &x, &y );
7b203521 9099
7432fddf
AF
9100 marker_moveto ( t, x, y );
9101 }
9102 return TRUE;
9103 }
50a14534
EB
9104 return FALSE;
9105}
9106
7432fddf 9107static gboolean tool_edit_waypoint_release ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data )
941aa6e9 9108{
7432fddf
AF
9109 tool_ed_t *t = data;
9110 VikViewport *vvp = t->vvp;
9111
941aa6e9
AF
9112 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
9113 return FALSE;
7432fddf
AF
9114
9115 if ( t->holding && event->button == 1 )
941aa6e9
AF
9116 {
9117 VikCoord new_coord;
941aa6e9
AF
9118 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
9119
9120 /* snap to TP */
9121 if ( event->state & GDK_CONTROL_MASK )
9122 {
9123 VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
9124 if ( tp )
9125 new_coord = tp->coord;
9126 }
9127
9128 /* snap to WP */
9129 if ( event->state & GDK_SHIFT_MASK )
9130 {
9131 VikWaypoint *wp = closest_wp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
9132 if ( wp && wp != vtl->current_wp )
9133 new_coord = wp->coord;
9134 }
9135
7432fddf
AF
9136 marker_end_move ( t );
9137
941aa6e9 9138 vtl->current_wp->coord = new_coord;
aa0665e5
RN
9139
9140 trw_layer_calculate_bounds_waypoints ( vtl );
da121f9b 9141 vik_layer_emit_update ( VIK_LAYER(vtl) );
941aa6e9
AF
9142 return TRUE;
9143 }
9144 /* PUT IN RIGHT PLACE!!! */
7432fddf 9145 if ( event->button == 3 && vtl->waypoint_rightclick )
941aa6e9
AF
9146 {
9147 if ( vtl->wp_right_click_menu )
4f14a010 9148 g_object_ref_sink ( G_OBJECT(vtl->wp_right_click_menu) );
0b951ebe
RN
9149 if ( vtl->current_wp ) {
9150 vtl->wp_right_click_menu = GTK_MENU ( gtk_menu_new () );
c9570f86 9151 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
9152 gtk_menu_popup ( vtl->wp_right_click_menu, NULL, NULL, NULL, NULL, event->button, gtk_get_current_event_time() );
9153 }
941aa6e9
AF
9154 vtl->waypoint_rightclick = FALSE;
9155 }
9156 return FALSE;
9157}
9158
9159/*** New track ****/
9160
9161static gpointer tool_new_track_create ( VikWindow *vw, VikViewport *vvp)
9162{
9163 return vvp;
9164}
9165
7b203521
EB
9166typedef struct {
9167 VikTrwLayer *vtl;
745fda83
RN
9168 GdkDrawable *drawable;
9169 GdkGC *gc;
9170 GdkPixmap *pixmap;
9171} draw_sync_t;
7b203521 9172
745fda83
RN
9173/*
9174 * Draw specified pixmap
9175 */
9176static gboolean draw_sync ( gpointer data )
7b203521 9177{
745fda83 9178 draw_sync_t *ds = (draw_sync_t*) data;
ef5e8132
RN
9179 // Sometimes don't want to draw
9180 // normally because another update has taken precedent such as panning the display
9181 // which means this pixmap is no longer valid
9182 if ( ds->vtl->draw_sync_do ) {
9183 gdk_threads_enter();
9184 gdk_draw_drawable (ds->drawable,
9185 ds->gc,
9186 ds->pixmap,
9187 0, 0, 0, 0, -1, -1);
9188 ds->vtl->draw_sync_done = TRUE;
9189 gdk_threads_leave();
9190 }
351eb253 9191 g_free ( ds );
7b203521
EB
9192 return FALSE;
9193}
9194
19a0d58f 9195static gchar* distance_string (gdouble distance)
8da84040
RN
9196{
9197 gchar str[128];
9198
9199 /* draw label with distance */
9200 vik_units_distance_t dist_units = a_vik_get_units_distance ();
9201 switch (dist_units) {
9202 case VIK_UNITS_DISTANCE_MILES:
9203 if (distance >= VIK_MILES_TO_METERS(1) && distance < VIK_MILES_TO_METERS(100)) {
9204 g_sprintf(str, "%3.2f miles", VIK_METERS_TO_MILES(distance));
9205 } else if (distance < 1609.4) {
9206 g_sprintf(str, "%d yards", (int)(distance*1.0936133));
9207 } else {
9208 g_sprintf(str, "%d miles", (int)VIK_METERS_TO_MILES(distance));
9209 }
9210 break;
9211 default:
9212 // VIK_UNITS_DISTANCE_KILOMETRES
9213 if (distance >= 1000 && distance < 100000) {
9214 g_sprintf(str, "%3.2f km", distance/1000.0);
9215 } else if (distance < 1000) {
9216 g_sprintf(str, "%d m", (int)distance);
9217 } else {
9218 g_sprintf(str, "%d km", (int)distance/1000);
9219 }
9220 break;
9221 }
9222 return g_strdup (str);
9223}
9224
9225/*
9226 * Actually set the message in statusbar
9227 */
9a3538f5 9228static void statusbar_write (gdouble distance, gdouble elev_gain, gdouble elev_loss, gdouble last_step, gdouble angle, VikTrwLayer *vtl )
8da84040
RN
9229{
9230 // Only show elevation data when track has some elevation properties
9231 gchar str_gain_loss[64];
9232 str_gain_loss[0] = '\0';
9a3538f5
GB
9233 gchar str_last_step[64];
9234 str_last_step[0] = '\0';
9235 gchar *str_total = distance_string (distance);
8da84040
RN
9236
9237 if ( (elev_gain > 0.1) || (elev_loss > 0.1) ) {
9238 if ( a_vik_get_units_height () == VIK_UNITS_HEIGHT_METRES )
9239 g_sprintf(str_gain_loss, _(" - Gain %dm:Loss %dm"), (int)elev_gain, (int)elev_loss);
9240 else
9241 g_sprintf(str_gain_loss, _(" - Gain %dft:Loss %dft"), (int)VIK_METERS_TO_FEET(elev_gain), (int)VIK_METERS_TO_FEET(elev_loss));
9242 }
9a3538f5
GB
9243
9244 if ( last_step > 0 ) {
9245 gchar *tmp = distance_string (last_step);
0da53bd9 9246 g_sprintf(str_last_step, _(" - Bearing %3.1f° - Step %s"), RAD2DEG(angle), tmp);
9a3538f5
GB
9247 g_free ( tmp );
9248 }
9249
9250 VikWindow *vw = VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(vtl));
8da84040
RN
9251
9252 // Write with full gain/loss information
9a3538f5
GB
9253 gchar *msg = g_strdup_printf ( "Total %s%s%s", str_total, str_last_step, str_gain_loss);
9254 vik_statusbar_set_message ( vik_window_get_statusbar (vw), VIK_STATUSBAR_INFO, msg );
8da84040 9255 g_free ( msg );
9a3538f5 9256 g_free ( str_total );
8da84040
RN
9257}
9258
9259/*
9260 * Figure out what information should be set in the statusbar and then write it
9261 */
9262static void update_statusbar ( VikTrwLayer *vtl )
9263{
9264 // Get elevation data
9265 gdouble elev_gain, elev_loss;
9266 vik_track_get_total_elevation_gain ( vtl->current_track, &elev_gain, &elev_loss);
9267
9268 /* Find out actual distance of current track */
9269 gdouble distance = vik_track_get_length (vtl->current_track);
8da84040 9270
9a3538f5 9271 statusbar_write (distance, elev_gain, elev_loss, 0, 0, vtl);
8da84040
RN
9272}
9273
9274
dc2c040e 9275static VikLayerToolFuncStatus tool_new_track_move ( VikTrwLayer *vtl, GdkEventMotion *event, VikViewport *vvp )
7b203521
EB
9276{
9277 /* if we haven't sync'ed yet, we don't have time to do more. */
745fda83 9278 if ( vtl->draw_sync_done && vtl->current_track && vtl->current_track->trackpoints ) {
215ebe59 9279 VikTrackpoint *last_tpt = vik_track_get_tp_last(vtl->current_track);
745fda83
RN
9280
9281 static GdkPixmap *pixmap = NULL;
9282 int w1, h1, w2, h2;
9283 // Need to check in case window has been resized
9284 w1 = vik_viewport_get_width(vvp);
9285 h1 = vik_viewport_get_height(vvp);
9286 if (!pixmap) {
9b082b39 9287 pixmap = gdk_pixmap_new ( gtk_widget_get_window(GTK_WIDGET(vvp)), w1, h1, -1 );
745fda83
RN
9288 }
9289 gdk_drawable_get_size (pixmap, &w2, &h2);
9290 if (w1 != w2 || h1 != h2) {
9291 g_object_unref ( G_OBJECT ( pixmap ) );
9b082b39 9292 pixmap = gdk_pixmap_new ( gtk_widget_get_window(GTK_WIDGET(vvp)), w1, h1, -1 );
745fda83
RN
9293 }
9294
9295 // Reset to background
9296 gdk_draw_drawable (pixmap,
9297 vtl->current_track_newpoint_gc,
9298 vik_viewport_get_pixmap(vvp),
9299 0, 0, 0, 0, -1, -1);
9300
9301 draw_sync_t *passalong;
7b203521
EB
9302 gint x1, y1;
9303
c31b3fbb 9304 vik_viewport_coord_to_screen ( vvp, &(last_tpt->coord), &x1, &y1 );
745fda83
RN
9305
9306 // FOR SCREEN OVERLAYS WE MUST DRAW INTO THIS PIXMAP (when using the reset method)
9307 // otherwise using vik_viewport_draw_* functions puts the data into the base pixmap,
9308 // thus when we come to reset to the background it would include what we have already drawn!!
9309 gdk_draw_line ( pixmap,
9310 vtl->current_track_newpoint_gc,
9311 x1, y1, event->x, event->y );
9312 // Using this reset method is more reliable than trying to undraw previous efforts via the GDK_INVERT method
23ea1329 9313
23ea1329
RN
9314 /* Find out actual distance of current track */
9315 gdouble distance = vik_track_get_length (vtl->current_track);
9316
9317 // Now add distance to where the pointer is //
9318 VikCoord coord;
9319 struct LatLon ll;
9320 vik_viewport_screen_to_coord ( vvp, (gint) event->x, (gint) event->y, &coord );
9321 vik_coord_to_latlon ( &coord, &ll );
9a3538f5
GB
9322 gdouble last_step = vik_coord_diff( &coord, &(last_tpt->coord));
9323 distance = distance + last_step;
23ea1329 9324
8da84040
RN
9325 // Get elevation data
9326 gdouble elev_gain, elev_loss;
9327 vik_track_get_total_elevation_gain ( vtl->current_track, &elev_gain, &elev_loss);
9328
9329 // Adjust elevation data (if available) for the current pointer position
9330 gdouble elev_new;
9331 elev_new = (gdouble) a_dems_get_elev_by_coord ( &coord, VIK_DEM_INTERPOL_BEST );
9332 if ( elev_new != VIK_DEM_INVALID_ELEVATION ) {
c31b3fbb 9333 if ( last_tpt->altitude != VIK_DEFAULT_ALTITUDE ) {
8da84040 9334 // Adjust elevation of last track point
c31b3fbb 9335 if ( elev_new > last_tpt->altitude )
8da84040 9336 // Going up
c31b3fbb 9337 elev_gain += elev_new - last_tpt->altitude;
8da84040
RN
9338 else
9339 // Going down
c31b3fbb 9340 elev_loss += last_tpt->altitude - elev_new;
23ea1329 9341 }
23ea1329 9342 }
6ba8e356 9343
90611609
RN
9344 //
9345 // Display of the distance 'tooltip' during track creation is controlled by a preference
9346 //
9347 if ( a_vik_get_create_track_tooltip() ) {
9348
9349 gchar *str = distance_string (distance);
061ccfdc 9350
90611609
RN
9351 PangoLayout *pl = gtk_widget_create_pango_layout (GTK_WIDGET(vvp), NULL);
9352 pango_layout_set_font_description (pl, gtk_widget_get_style(GTK_WIDGET(vvp))->font_desc);
9353 pango_layout_set_text (pl, str, -1);
9354 gint wd, hd;
9355 pango_layout_get_pixel_size ( pl, &wd, &hd );
6ba8e356 9356
90611609
RN
9357 gint xd,yd;
9358 // offset from cursor a bit depending on font size
9359 xd = event->x + 10;
9360 yd = event->y - hd;
56cb1807 9361
90611609
RN
9362 // Create a background block to make the text easier to read over the background map
9363 GdkGC *background_block_gc = vik_viewport_new_gc ( vvp, "#cccccc", 1);
9364 gdk_draw_rectangle (pixmap, background_block_gc, TRUE, xd-2, yd-2, wd+4, hd+2);
9365 gdk_draw_layout (pixmap, vtl->current_track_newpoint_gc, xd, yd, pl);
745fda83 9366
90611609
RN
9367 g_object_unref ( G_OBJECT ( pl ) );
9368 g_object_unref ( G_OBJECT ( background_block_gc ) );
9369 g_free (str);
9370 }
6ba8e356 9371
745fda83 9372 passalong = g_new(draw_sync_t,1); // freed by draw_sync()
7b203521 9373 passalong->vtl = vtl;
745fda83 9374 passalong->pixmap = pixmap;
9b082b39 9375 passalong->drawable = gtk_widget_get_window(GTK_WIDGET(vvp));
745fda83 9376 passalong->gc = vtl->current_track_newpoint_gc;
8da84040 9377
9a3538f5
GB
9378 gdouble angle;
9379 gdouble baseangle;
9380 vik_viewport_compute_bearing ( vvp, x1, y1, event->x, event->y, &angle, &baseangle );
9381
8da84040 9382 // Update statusbar with full gain/loss information
9a3538f5 9383 statusbar_write (distance, elev_gain, elev_loss, last_step, angle, vtl);
7b203521 9384
745fda83
RN
9385 // draw pixmap when we have time to
9386 g_idle_add_full (G_PRIORITY_HIGH_IDLE + 10, draw_sync, passalong, NULL);
9387 vtl->draw_sync_done = FALSE;
165d30aa 9388 return VIK_LAYER_TOOL_ACK_GRAB_FOCUS;
7b203521 9389 }
165d30aa 9390 return VIK_LAYER_TOOL_ACK;
7b203521
EB
9391}
9392
215ebe59
RN
9393// NB vtl->current_track must be valid
9394static void undo_trackpoint_add ( VikTrwLayer *vtl )
9395{
9396 // 'undo'
9397 if ( vtl->current_track->trackpoints ) {
9398 // TODO rework this...
9399 //vik_trackpoint_free ( vik_track_get_tp_last (vtl->current_track) );
9400 GList *last = g_list_last(vtl->current_track->trackpoints);
9401 g_free ( last->data );
9402 vtl->current_track->trackpoints = g_list_remove_link ( vtl->current_track->trackpoints, last );
9403
9404 vik_track_calculate_bounds ( vtl->current_track );
9405 }
9406}
9407
777e2d4d
EB
9408static gboolean tool_new_track_key_press ( VikTrwLayer *vtl, GdkEventKey *event, VikViewport *vvp )
9409{
9410 if ( vtl->current_track && event->keyval == GDK_Escape ) {
9411 vtl->current_track = NULL;
da121f9b 9412 vik_layer_emit_update ( VIK_LAYER(vtl) );
777e2d4d
EB
9413 return TRUE;
9414 } else if ( vtl->current_track && event->keyval == GDK_BackSpace ) {
215ebe59 9415 undo_trackpoint_add ( vtl );
8da84040 9416 update_statusbar ( vtl );
da121f9b 9417 vik_layer_emit_update ( VIK_LAYER(vtl) );
777e2d4d
EB
9418 return TRUE;
9419 }
9420 return FALSE;
9421}
9422
e37b2a6d
RN
9423/*
9424 * Common function to handle trackpoint button requests on either a route or a track
9425 * . enables adding a point via normal click
9426 * . enables removal of last point via right click
9427 * . finishing of the track or route via double clicking
9428 */
9429static gboolean tool_new_track_or_route_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
50a14534
EB
9430{
9431 VikTrackpoint *tp;
9432
941aa6e9
AF
9433 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
9434 return FALSE;
9435
ef5e8132
RN
9436 if ( event->button == 2 ) {
9437 // As the display is panning, the new track pixmap is now invalid so don't draw it
9438 // otherwise this drawing done results in flickering back to an old image
9439 vtl->draw_sync_do = FALSE;
9440 return FALSE;
9441 }
9442
e37b2a6d 9443 if ( event->button == 3 )
50a14534 9444 {
e37b2a6d
RN
9445 if ( !vtl->current_track )
9446 return FALSE;
215ebe59 9447 undo_trackpoint_add ( vtl );
8da84040 9448 update_statusbar ( vtl );
da121f9b 9449 vik_layer_emit_update ( VIK_LAYER(vtl) );
50a14534
EB
9450 return TRUE;
9451 }
9452
9453 if ( event->type == GDK_2BUTTON_PRESS )
9454 {
9455 /* subtract last (duplicate from double click) tp then end */
9456 if ( vtl->current_track && vtl->current_track->trackpoints && vtl->ct_x1 == vtl->ct_x2 && vtl->ct_y1 == vtl->ct_y2 )
9457 {
50a14534 9458 /* undo last, then end */
215ebe59 9459 undo_trackpoint_add ( vtl );
50a14534
EB
9460 vtl->current_track = NULL;
9461 }
da121f9b 9462 vik_layer_emit_update ( VIK_LAYER(vtl) );
50a14534
EB
9463 return TRUE;
9464 }
9465
50a14534
EB
9466 tp = vik_trackpoint_new();
9467 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &(tp->coord) );
9468
9469 /* snap to other TP */
9470 if ( event->state & GDK_CONTROL_MASK )
9471 {
9472 VikTrackpoint *other_tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
9473 if ( other_tp )
9474 tp->coord = other_tp->coord;
9475 }
9476
9477 tp->newsegment = FALSE;
9478 tp->has_timestamp = FALSE;
9479 tp->timestamp = 0;
e37b2a6d
RN
9480
9481 if ( vtl->current_track ) {
9bc95d58 9482 vik_track_add_trackpoint ( vtl->current_track, tp, TRUE ); // Ensure bounds is updated
e37b2a6d
RN
9483 /* Auto attempt to get elevation from DEM data (if it's available) */
9484 vik_track_apply_dem_data_last_trackpoint ( vtl->current_track );
9485 }
50a14534
EB
9486
9487 vtl->ct_x1 = vtl->ct_x2;
9488 vtl->ct_y1 = vtl->ct_y2;
9489 vtl->ct_x2 = event->x;
9490 vtl->ct_y2 = event->y;
9491
da121f9b 9492 vik_layer_emit_update ( VIK_LAYER(vtl) );
50a14534
EB
9493 return TRUE;
9494}
9495
e37b2a6d
RN
9496static gboolean tool_new_track_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
9497{
9498 // ----------------------------------------------------- if current is a route - switch to new track
9499 if ( event->button == 1 && ( ! vtl->current_track || (vtl->current_track && vtl->current_track->is_route ) ))
9500 {
9501 gchar *name = trw_layer_new_unique_sublayer_name(vtl, VIK_TRW_LAYER_SUBLAYER_TRACK, _("Track"));
fb40bae0 9502 if ( ( name = a_dialog_new_track ( VIK_GTK_WINDOW_FROM_LAYER(vtl), name, FALSE ) ) )
e37b2a6d 9503 {
2f154c90 9504 new_track_create_common ( vtl, name );
1613e468 9505 g_free ( name );
e37b2a6d
RN
9506 }
9507 else
9508 return TRUE;
9509 }
9510 return tool_new_track_or_route_click ( vtl, event, vvp );
9511}
9512
ef5e8132
RN
9513static void tool_new_track_release ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
9514{
9515 if ( event->button == 2 ) {
9516 // Pan moving ended - enable potential point drawing again
9517 vtl->draw_sync_do = TRUE;
9518 vtl->draw_sync_done = TRUE;
9519 }
9520}
9521
e37b2a6d
RN
9522/*** New route ****/
9523
9524static gpointer tool_new_route_create ( VikWindow *vw, VikViewport *vvp)
9525{
9526 return vvp;
9527}
9528
9529static gboolean tool_new_route_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
9530{
9531 // -------------------------- if current is a track - switch to new route
9532 if ( event->button == 1 && ( ! vtl->current_track || (vtl->current_track && !vtl->current_track->is_route ) ) )
9533 {
9534 gchar *name = trw_layer_new_unique_sublayer_name(vtl, VIK_TRW_LAYER_SUBLAYER_ROUTE, _("Route"));
fb40bae0 9535 if ( ( name = a_dialog_new_track ( VIK_GTK_WINDOW_FROM_LAYER(vtl), name, TRUE ) ) ) {
b1453c16 9536 new_route_create_common ( vtl, name );
1613e468
RN
9537 g_free ( name );
9538 }
e37b2a6d
RN
9539 else
9540 return TRUE;
9541 }
9542 return tool_new_track_or_route_click ( vtl, event, vvp );
9543}
9544
941aa6e9
AF
9545/*** New waypoint ****/
9546
9547static gpointer tool_new_waypoint_create ( VikWindow *vw, VikViewport *vvp)
9548{
9549 return vvp;
9550}
9551
9552static gboolean tool_new_waypoint_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
9553{
9554 VikCoord coord;
9555 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
9556 return FALSE;
9557 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &coord );
aa0665e5
RN
9558 if (vik_trw_layer_new_waypoint ( vtl, VIK_GTK_WINDOW_FROM_LAYER(vtl), &coord ) && VIK_LAYER(vtl)->visible) {
9559 trw_layer_calculate_bounds_waypoints ( vtl );
da121f9b 9560 vik_layer_emit_update ( VIK_LAYER(vtl) );
aa0665e5 9561 }
941aa6e9
AF
9562 return TRUE;
9563}
9564
9565
9566/*** Edit trackpoint ****/
9567
9568static gpointer tool_edit_trackpoint_create ( VikWindow *vw, VikViewport *vvp)
9569{
7432fddf 9570 tool_ed_t *t = g_new(tool_ed_t, 1);
33534cd8
AF
9571 t->vvp = vvp;
9572 t->holding = FALSE;
9573 return t;
941aa6e9
AF
9574}
9575
919ed63e
RN
9576static void tool_edit_trackpoint_destroy ( tool_ed_t *t )
9577{
9578 g_free ( t );
9579}
9580
33534cd8 9581static gboolean tool_edit_trackpoint_click ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data )
941aa6e9 9582{
7432fddf 9583 tool_ed_t *t = data;
33534cd8
AF
9584 VikViewport *vvp = t->vvp;
9585 TPSearchParams params;
941aa6e9
AF
9586 /* OUTDATED DOCUMENTATION:
9587 find 5 pixel range on each side. then put these UTM, and a pointer
9588 to the winning track name (and maybe the winning track itself), and a
9589 pointer to the winning trackpoint, inside an array or struct. pass
9590 this along, do a foreach on the tracks which will do a foreach on the
9591 trackpoints. */
9592 params.vvp = vvp;
9593 params.x = event->x;
9594 params.y = event->y;
ce4bd1cf 9595 params.closest_track_id = NULL;
941aa6e9
AF
9596 /* TODO: should get track listitem so we can break it up, make a new track, mess it up, all that. */
9597 params.closest_tp = NULL;
9414408e 9598 params.closest_tpl = NULL;
acf5b0d4 9599 vik_viewport_get_min_max_lat_lon ( vvp, &(params.bbox.south), &(params.bbox.north), &(params.bbox.west), &(params.bbox.east) );
941aa6e9 9600
7432fddf
AF
9601 if ( event->button != 1 )
9602 return FALSE;
9603
941aa6e9
AF
9604 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
9605 return FALSE;
9606
0d2b891f 9607 if ( !vtl->vl.visible || !vtl->tracks_visible || !vtl->routes_visible )
87741170
RN
9608 return FALSE;
9609
941aa6e9
AF
9610 if ( vtl->current_tpl )
9611 {
9612 /* 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.) */
9613 VikTrackpoint *tp = VIK_TRACKPOINT(vtl->current_tpl->data);
ce4bd1cf
RN
9614 VikTrack *current_tr = VIK_TRACK(g_hash_table_lookup(vtl->tracks, vtl->current_tp_id));
9615 if ( !current_tr )
9616 return FALSE;
941aa6e9 9617
ce4bd1cf 9618 gint x, y;
941aa6e9
AF
9619 vik_viewport_coord_to_screen ( vvp, &(tp->coord), &x, &y );
9620
9621 if ( current_tr->visible &&
9622 abs(x - event->x) < TRACKPOINT_SIZE_APPROX &&
7432fddf
AF
9623 abs(y - event->y) < TRACKPOINT_SIZE_APPROX ) {
9624 marker_begin_move ( t, event->x, event->y );
941aa6e9
AF
9625 return TRUE;
9626 }
9627
941aa6e9
AF
9628 }
9629
0d2b891f
RN
9630 if ( vtl->tracks_visible )
9631 g_hash_table_foreach ( vtl->tracks, (GHFunc) track_search_closest_tp, &params);
941aa6e9
AF
9632
9633 if ( params.closest_tp )
9634 {
ce4bd1cf 9635 vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, g_hash_table_lookup ( vtl->tracks_iters, params.closest_track_id ), TRUE );
941aa6e9 9636 vtl->current_tpl = params.closest_tpl;
ce4bd1cf 9637 vtl->current_tp_id = params.closest_track_id;
da14cc69 9638 vtl->current_tp_track = g_hash_table_lookup ( vtl->tracks, params.closest_track_id );
941aa6e9 9639 trw_layer_tpwin_init ( vtl );
95d1b757 9640 set_statusbar_msg_info_trkpt ( vtl, params.closest_tp );
da121f9b 9641 vik_layer_emit_update ( VIK_LAYER(vtl) );
941aa6e9
AF
9642 return TRUE;
9643 }
9644
0d2b891f
RN
9645 if ( vtl->routes_visible )
9646 g_hash_table_foreach ( vtl->routes, (GHFunc) track_search_closest_tp, &params);
9647
9648 if ( params.closest_tp )
9649 {
9650 vik_treeview_select_iter ( VIK_LAYER(vtl)->vt, g_hash_table_lookup ( vtl->routes_iters, params.closest_track_id ), TRUE );
9651 vtl->current_tpl = params.closest_tpl;
9652 vtl->current_tp_id = params.closest_track_id;
9653 vtl->current_tp_track = g_hash_table_lookup ( vtl->routes, params.closest_track_id );
9654 trw_layer_tpwin_init ( vtl );
9655 set_statusbar_msg_info_trkpt ( vtl, params.closest_tp );
da121f9b 9656 vik_layer_emit_update ( VIK_LAYER(vtl) );
0d2b891f
RN
9657 return TRUE;
9658 }
9659
941aa6e9
AF
9660 /* these aren't the droids you're looking for */
9661 return FALSE;
9662}
9663
dc2c040e 9664static gboolean tool_edit_trackpoint_move ( VikTrwLayer *vtl, GdkEventMotion *event, gpointer data )
33534cd8 9665{
7432fddf 9666 tool_ed_t *t = data;
33534cd8
AF
9667 VikViewport *vvp = t->vvp;
9668
9669 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
9670 return FALSE;
9671
9672 if ( t->holding )
9673 {
9674 VikCoord new_coord;
33534cd8
AF
9675 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
9676
9677 /* snap to TP */
9678 if ( event->state & GDK_CONTROL_MASK )
9679 {
9680 VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
9681 if ( tp && tp != vtl->current_tpl->data )
9682 new_coord = tp->coord;
9683 }
9684 // VIK_TRACKPOINT(vtl->current_tpl->data)->coord = new_coord;
7432fddf
AF
9685 {
9686 gint x, y;
9687 vik_viewport_coord_to_screen ( vvp, &new_coord, &x, &y );
9688 marker_moveto ( t, x, y );
9689 }
33534cd8
AF
9690
9691 return TRUE;
9692 }
9693 return FALSE;
9694}
9695
33534cd8 9696static gboolean tool_edit_trackpoint_release ( VikTrwLayer *vtl, GdkEventButton *event, gpointer data )
941aa6e9 9697{
7432fddf 9698 tool_ed_t *t = data;
33534cd8
AF
9699 VikViewport *vvp = t->vvp;
9700
941aa6e9
AF
9701 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
9702 return FALSE;
7432fddf
AF
9703 if ( event->button != 1)
9704 return FALSE;
33534cd8 9705
7432fddf 9706 if ( t->holding ) {
941aa6e9 9707 VikCoord new_coord;
941aa6e9
AF
9708 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &new_coord );
9709
9710 /* snap to TP */
9711 if ( event->state & GDK_CONTROL_MASK )
9712 {
9713 VikTrackpoint *tp = closest_tp_in_five_pixel_interval ( vtl, vvp, event->x, event->y );
9714 if ( tp && tp != vtl->current_tpl->data )
9715 new_coord = tp->coord;
9716 }
9717
9718 VIK_TRACKPOINT(vtl->current_tpl->data)->coord = new_coord;
20981fd6
RN
9719 if ( vtl->current_tp_track )
9720 vik_track_calculate_bounds ( vtl->current_tp_track );
941aa6e9 9721
7432fddf 9722 marker_end_move ( t );
33534cd8 9723
941aa6e9 9724 /* diff dist is diff from orig */
46b6631a 9725 if ( vtl->tpwin )
ce4bd1cf 9726 vik_trw_layer_tpwin_set_tp ( vtl->tpwin, vtl->current_tpl, vtl->current_tp_track->name );
941aa6e9 9727
da121f9b 9728 vik_layer_emit_update ( VIK_LAYER(vtl) );
941aa6e9
AF
9729 return TRUE;
9730 }
9731 return FALSE;
9732}
9733
9734
7ff7d728
RN
9735/*** Route Finder ***/
9736static gpointer tool_route_finder_create ( VikWindow *vw, VikViewport *vvp)
1eef1bde
QT
9737{
9738 return vvp;
9739}
9740
7ff7d728 9741static gboolean tool_route_finder_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
1eef1bde
QT
9742{
9743 VikCoord tmp;
0c1044e9 9744 if ( !vtl ) return FALSE;
1eef1bde 9745 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &tmp );
7ff7d728 9746 if ( event->button == 3 && vtl->route_finder_current_track ) {
c3deba01 9747 VikCoord *new_end;
7ff7d728 9748 new_end = vik_track_cut_back_to_double_point ( vtl->route_finder_current_track );
c3deba01 9749 if ( new_end ) {
7ff7d728 9750 vtl->route_finder_coord = *new_end;
c3deba01 9751 g_free ( new_end );
da121f9b 9752 vik_layer_emit_update ( VIK_LAYER(vtl) );
c3deba01 9753 /* remove last ' to:...' */
7ff7d728
RN
9754 if ( vtl->route_finder_current_track->comment ) {
9755 gchar *last_to = strrchr ( vtl->route_finder_current_track->comment, 't' );
9756 if ( last_to && (last_to - vtl->route_finder_current_track->comment > 1) ) {
9757 gchar *new_comment = g_strndup ( vtl->route_finder_current_track->comment,
9758 last_to - vtl->route_finder_current_track->comment - 1);
9759 vik_track_set_comment_no_copy ( vtl->route_finder_current_track, new_comment );
c3deba01
EB
9760 }
9761 }
9762 }
9763 }
7ff7d728 9764 else if ( vtl->route_finder_started || (event->state & GDK_CONTROL_MASK && vtl->route_finder_current_track) ) {
1eef1bde 9765 struct LatLon start, end;
bddd2056 9766
7ff7d728 9767 vik_coord_to_latlon ( &(vtl->route_finder_coord), &start );
1eef1bde 9768 vik_coord_to_latlon ( &(tmp), &end );
7ff7d728 9769 vtl->route_finder_coord = tmp; /* for continuations */
bddd2056
EB
9770
9771 /* these are checked when adding a track from a file (vik_trw_layer_filein_add_track) */
7ff7d728
RN
9772 if ( event->state & GDK_CONTROL_MASK && vtl->route_finder_current_track ) {
9773 vtl->route_finder_append = TRUE; // merge tracks. keep started true.
bddd2056 9774 } else {
7ff7d728
RN
9775 vtl->route_finder_check_added_track = TRUE;
9776 vtl->route_finder_started = FALSE;
bddd2056
EB
9777 }
9778
9f30939a 9779 vik_routing_default_find ( vtl, start, end);
bddd2056
EB
9780
9781 /* see if anything was done -- a track was added or appended to */
ce4bd1cf
RN
9782 if ( vtl->route_finder_check_added_track && vtl->route_finder_added_track ) {
9783 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
9784 } else if ( vtl->route_finder_append == FALSE && vtl->route_finder_current_track ) {
9785 /* route_finder_append was originally TRUE but set to FALSE by filein_add_track */
9786 gchar *new_comment = g_strdup_printf("%s to: %f,%f", vtl->route_finder_current_track->comment, end.lat, end.lon );
9787 vik_track_set_comment_no_copy ( vtl->route_finder_current_track, new_comment );
bddd2056 9788 }
20981fd6
RN
9789
9790 if ( vtl->route_finder_added_track )
9791 vik_track_calculate_bounds ( vtl->route_finder_added_track );
9792
ce4bd1cf 9793 vtl->route_finder_added_track = NULL;
7ff7d728
RN
9794 vtl->route_finder_check_added_track = FALSE;
9795 vtl->route_finder_append = FALSE;
bddd2056 9796
da121f9b 9797 vik_layer_emit_update ( VIK_LAYER(vtl) );
1eef1bde 9798 } else {
7ff7d728
RN
9799 vtl->route_finder_started = TRUE;
9800 vtl->route_finder_coord = tmp;
9801 vtl->route_finder_current_track = NULL;
1eef1bde 9802 }
1eef1bde
QT
9803 return TRUE;
9804}
9805
941aa6e9
AF
9806/*** Show picture ****/
9807
9808static gpointer tool_show_picture_create ( VikWindow *vw, VikViewport *vvp)
9809{
9810 return vvp;
9811}
9812
9813/* Params are: vvp, event, last match found or NULL */
9e212bfc 9814static void tool_show_picture_wp ( const gpointer id, VikWaypoint *wp, gpointer params[3] )
941aa6e9
AF
9815{
9816 if ( wp->image && wp->visible )
9817 {
9818 gint x, y, slackx, slacky;
9819 GdkEventButton *event = (GdkEventButton *) params[1];
9820
9821 vik_viewport_coord_to_screen ( VIK_VIEWPORT(params[0]), &(wp->coord), &x, &y );
9822 slackx = wp->image_width / 2;
9823 slacky = wp->image_height / 2;
9824 if ( x <= event->x + slackx && x >= event->x - slackx
9825 && y <= event->y + slacky && y >= event->y - slacky )
9826 {
9827 params[2] = wp->image; /* we've found a match. however continue searching
9828 * since we want to find the last match -- that
9829 * is, the match that was drawn last. */
9830 }
9831 }
9832}
9833
19782ffd 9834static void trw_layer_show_picture ( menu_array_sublayer values )
a412f3f5
RN
9835{
9836 /* thanks to the Gaim people for showing me ShellExecute and g_spawn_command_line_async */
9837#ifdef WINDOWS
19782ffd 9838 ShellExecute(NULL, "open", (char *) values[MA_MISC], NULL, NULL, SW_SHOWNORMAL);
a412f3f5
RN
9839#else /* WINDOWS */
9840 GError *err = NULL;
19782ffd 9841 gchar *quoted_file = g_shell_quote ( (gchar *) values[MA_MISC] );
3317dc4e 9842 gchar *cmd = g_strdup_printf ( "%s %s", a_vik_get_image_viewer(), quoted_file );
a412f3f5
RN
9843 g_free ( quoted_file );
9844 if ( ! g_spawn_command_line_async ( cmd, &err ) )
9845 {
19782ffd 9846 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
9847 g_error_free ( err );
9848 }
9849 g_free ( cmd );
9850#endif /* WINDOWS */
9851}
9852
941aa6e9
AF
9853static gboolean tool_show_picture_click ( VikTrwLayer *vtl, GdkEventButton *event, VikViewport *vvp )
9854{
9855 gpointer params[3] = { vvp, event, NULL };
9856 if (!vtl || vtl->vl.type != VIK_LAYER_TRW)
9857 return FALSE;
9858 g_hash_table_foreach ( vtl->waypoints, (GHFunc) tool_show_picture_wp, params );
9859 if ( params[2] )
9860 {
19782ffd
RN
9861 static menu_array_sublayer values;
9862 values[MA_VTL] = vtl;
9863 values[MA_MISC] = params[2];
9864 trw_layer_show_picture ( values );
941aa6e9
AF
9865 return TRUE; /* found a match */
9866 }
9867 else
9868 return FALSE; /* go through other layers, searching for a match */
9869}
9870
9871/***************************************************************************
9872 ** End tool code
9873 ***************************************************************************/
9874
9875
9e212bfc 9876static void image_wp_make_list ( const gpointer id, VikWaypoint *wp, GSList **pics )
50a14534
EB
9877{
9878 if ( wp->image && ( ! a_thumbnails_exists ( wp->image ) ) )
9879 *pics = g_slist_append ( *pics, (gpointer) g_strdup ( wp->image ) );
9880}
9881
91822ddd
RN
9882/* Structure for thumbnail creating data used in the background thread */
9883typedef struct {
9884 VikTrwLayer *vtl; // Layer needed for redrawing
9885 GSList *pics; // Image list
9886} thumbnail_create_thread_data;
9887
9888static int create_thumbnails_thread ( thumbnail_create_thread_data *tctd, gpointer threaddata )
50a14534 9889{
91822ddd
RN
9890 guint total = g_slist_length(tctd->pics), done = 0;
9891 while ( tctd->pics )
50a14534 9892 {
91822ddd 9893 a_thumbnails_create ( (gchar *) tctd->pics->data );
54861848
GB
9894 int result = a_background_thread_progress ( threaddata, ((gdouble) ++done) / total );
9895 if ( result != 0 )
9896 return -1; /* Abort thread */
9897
91822ddd 9898 tctd->pics = tctd->pics->next;
50a14534 9899 }
91822ddd
RN
9900
9901 // Redraw to show the thumbnails as they are now created
91822ddd 9902 if ( IS_VIK_LAYER(tctd->vtl) )
da121f9b 9903 vik_layer_emit_update ( VIK_LAYER(tctd->vtl) ); // NB update from background thread
91822ddd 9904
17c8aefa 9905 return 0;
50a14534
EB
9906}
9907
91822ddd 9908static void thumbnail_create_thread_free ( thumbnail_create_thread_data *tctd )
50a14534 9909{
91822ddd 9910 while ( tctd->pics )
50a14534 9911 {
91822ddd
RN
9912 g_free ( tctd->pics->data );
9913 tctd->pics = g_slist_delete_link ( tctd->pics, tctd->pics );
50a14534 9914 }
91822ddd 9915 g_free ( tctd );
50a14534
EB
9916}
9917
b3eb3b98 9918void trw_layer_verify_thumbnails ( VikTrwLayer *vtl, GtkWidget *vp )
50a14534
EB
9919{
9920 if ( ! vtl->has_verified_thumbnails )
9921 {
9922 GSList *pics = NULL;
9923 g_hash_table_foreach ( vtl->waypoints, (GHFunc) image_wp_make_list, &pics );
9924 if ( pics )
9925 {
9926 gint len = g_slist_length ( pics );
4c77d5e0 9927 gchar *tmp = g_strdup_printf ( _("Creating %d Image Thumbnails..."), len );
91822ddd
RN
9928 thumbnail_create_thread_data *tctd = g_malloc ( sizeof(thumbnail_create_thread_data) );
9929 tctd->vtl = vtl;
9930 tctd->pics = pics;
9931 a_background_thread ( VIK_GTK_WINDOW_FROM_LAYER(vtl),
9932 tmp,
9933 (vik_thr_func) create_thumbnails_thread,
9934 tctd,
9935 (vik_thr_free_func) thumbnail_create_thread_free,
9936 NULL,
9937 len );
50a14534
EB
9938 g_free ( tmp );
9939 }
9940 }
9941}
9942
b1453c16
RN
9943static const gchar* my_track_colors ( gint ii )
9944{
9945 static const gchar* colors[VIK_TRW_LAYER_TRACK_GCS] = {
9946 "#2d870a",
ca738b73 9947 "#135D34",
b1453c16
RN
9948 "#0a8783",
9949 "#0e4d87",
9950 "#05469f",
ca738b73 9951 "#695CBB",
b1453c16
RN
9952 "#2d059f",
9953 "#4a059f",
ca738b73 9954 "#5A171A",
b1453c16
RN
9955 "#96059f"
9956 };
9957 // Fast and reliable way of returning a colour
9958 return colors[(ii % VIK_TRW_LAYER_TRACK_GCS)];
9959}
9960
9961static void trw_layer_track_alloc_colors ( VikTrwLayer *vtl )
9962{
9963 GHashTableIter iter;
9964 gpointer key, value;
9965
9966 gint ii = 0;
9967 // Tracks
9968 g_hash_table_iter_init ( &iter, vtl->tracks );
9969
9970 while ( g_hash_table_iter_next (&iter, &key, &value) ) {
9971
9972 // Tracks get a random spread of colours if not already assigned
9973 if ( ! VIK_TRACK(value)->has_color ) {
e2bc000f
RN
9974 if ( vtl->drawmode == DRAWMODE_ALL_SAME_COLOR )
9975 VIK_TRACK(value)->color = vtl->track_color;
9976 else {
9977 gdk_color_parse ( my_track_colors (ii), &(VIK_TRACK(value)->color) );
9978 }
b1453c16
RN
9979 VIK_TRACK(value)->has_color = TRUE;
9980 }
9981
93ee73b3 9982 trw_layer_update_treeview ( vtl, VIK_TRACK(value) );
b1453c16
RN
9983
9984 ii++;
9985 if (ii > VIK_TRW_LAYER_TRACK_GCS)
9986 ii = 0;
9987 }
9988
9989 // Routes
9990 ii = 0;
9991 g_hash_table_iter_init ( &iter, vtl->routes );
9992
9993 while ( g_hash_table_iter_next (&iter, &key, &value) ) {
9994
9995 // Routes get an intermix of reds
9996 if ( ! VIK_TRACK(value)->has_color ) {
9997 if ( ii )
9998 gdk_color_parse ( "#FF0000" , &(VIK_TRACK(value)->color) ); // Red
9999 else
10000 gdk_color_parse ( "#B40916" , &(VIK_TRACK(value)->color) ); // Dark Red
10001 VIK_TRACK(value)->has_color = TRUE;
10002 }
10003
93ee73b3 10004 trw_layer_update_treeview ( vtl, VIK_TRACK(value) );
b1453c16
RN
10005
10006 ii = !ii;
10007 }
10008}
10009
aa0665e5
RN
10010/*
10011 * (Re)Calculate the bounds of the waypoints in this layer,
10012 * This should be called whenever waypoints are changed
10013 */
2cec1c4e 10014void trw_layer_calculate_bounds_waypoints ( VikTrwLayer *vtl )
aa0665e5
RN
10015{
10016 struct LatLon topleft = { 0.0, 0.0 };
10017 struct LatLon bottomright = { 0.0, 0.0 };
10018 struct LatLon ll;
10019
10020 GHashTableIter iter;
10021 gpointer key, value;
10022
10023 g_hash_table_iter_init ( &iter, vtl->waypoints );
10024
10025 // Set bounds to first point
10026 if ( g_hash_table_iter_next (&iter, &key, &value) ) {
10027 vik_coord_to_latlon ( &(VIK_WAYPOINT(value)->coord), &topleft );
10028 vik_coord_to_latlon ( &(VIK_WAYPOINT(value)->coord), &bottomright );
10029 }
10030
10031 // Ensure there is another point...
10032 if ( g_hash_table_size ( vtl->waypoints ) > 1 ) {
10033
10034 while ( g_hash_table_iter_next (&iter, &key, &value) ) {
10035
10036 // See if this point increases the bounds.
10037 vik_coord_to_latlon ( &(VIK_WAYPOINT(value)->coord), &ll );
10038
10039 if ( ll.lat > topleft.lat) topleft.lat = ll.lat;
10040 if ( ll.lon < topleft.lon) topleft.lon = ll.lon;
10041 if ( ll.lat < bottomright.lat) bottomright.lat = ll.lat;
10042 if ( ll.lon > bottomright.lon) bottomright.lon = ll.lon;
10043 }
10044 }
10045
10046 vtl->waypoints_bbox.north = topleft.lat;
10047 vtl->waypoints_bbox.east = bottomright.lon;
10048 vtl->waypoints_bbox.south = bottomright.lat;
10049 vtl->waypoints_bbox.west = topleft.lon;
10050}
10051
20981fd6 10052static void trw_layer_calculate_bounds_track ( gpointer id, VikTrack *trk )
b1453c16 10053{
20981fd6
RN
10054 vik_track_calculate_bounds ( trk );
10055}
10056
10057static void trw_layer_calculate_bounds_tracks ( VikTrwLayer *vtl )
10058{
10059 g_hash_table_foreach ( vtl->tracks, (GHFunc) trw_layer_calculate_bounds_track, NULL );
10060 g_hash_table_foreach ( vtl->routes, (GHFunc) trw_layer_calculate_bounds_track, NULL );
10061}
10062
c9cac058
RN
10063static void trw_layer_sort_all ( VikTrwLayer *vtl )
10064{
7a52aac6
RN
10065 if ( ! VIK_LAYER(vtl)->vt )
10066 return;
10067
c9cac058
RN
10068 // Obviously need 2 to tango - sorting with only 1 (or less) is a lonely activity!
10069 if ( g_hash_table_size (vtl->tracks) > 1 )
10070 vik_treeview_sort_children ( VIK_LAYER(vtl)->vt, &(vtl->tracks_iter), vtl->track_sort_order );
10071
10072 if ( g_hash_table_size (vtl->routes) > 1 )
10073 vik_treeview_sort_children ( VIK_LAYER(vtl)->vt, &(vtl->routes_iter), vtl->track_sort_order );
10074
10075 if ( g_hash_table_size (vtl->waypoints) > 1 )
10076 vik_treeview_sort_children ( VIK_LAYER(vtl)->vt, &(vtl->waypoints_iter), vtl->wp_sort_order );
10077}
10078
20981fd6
RN
10079static void trw_layer_post_read ( VikTrwLayer *vtl, GtkWidget *vvp, gboolean from_file )
10080{
626de648
RN
10081 if ( VIK_LAYER(vtl)->realized )
10082 trw_layer_verify_thumbnails ( vtl, vvp );
b1453c16 10083 trw_layer_track_alloc_colors ( vtl );
aa0665e5
RN
10084
10085 trw_layer_calculate_bounds_waypoints ( vtl );
20981fd6 10086 trw_layer_calculate_bounds_tracks ( vtl );
c9cac058
RN
10087
10088 // Apply treeview sort after loading all the tracks for this layer
10089 // (rather than sorted insert on each individual track additional)
10090 // and after subsequent changes to the properties as the specified order may have changed.
10091 // since the sorting of a treeview section is now very quick
10092 // NB sorting is also performed after every name change as well to maintain the list order
10093 trw_layer_sort_all ( vtl );
6ba42f1e
RN
10094
10095 // Setting metadata time if not otherwise set
10096 if ( vtl->metadata ) {
10097
10098 gboolean need_to_set_time = TRUE;
10099 if ( vtl->metadata->timestamp ) {
10100 need_to_set_time = FALSE;
10101 if ( !g_strcmp0(vtl->metadata->timestamp, "" ) )
10102 need_to_set_time = TRUE;
10103 }
10104
10105 if ( need_to_set_time ) {
10106 // Could rewrite this as a general get first time of a TRW Layer function
10107 GTimeVal timestamp;
10108 timestamp.tv_usec = 0;
10109 gboolean has_timestamp = FALSE;
10110
10111 GList *gl = NULL;
10112 gl = g_hash_table_get_values ( vtl->tracks );
10113 gl = g_list_sort ( gl, vik_track_compare_timestamp );
10114 gl = g_list_first ( gl );
10115
10116 // Check times of tracks
10117 if ( gl ) {
10118 // Only need to check the first track as they have been sorted by time
10119 VikTrack *trk = (VikTrack*)gl->data;
10120 // Assume trackpoints already sorted by time
10121 VikTrackpoint *tpt = vik_track_get_tp_first(trk);
10122 if ( tpt && tpt->has_timestamp ) {
10123 timestamp.tv_sec = tpt->timestamp;
10124 has_timestamp = TRUE;
10125 }
10126 g_list_free ( gl );
10127 }
10128
10129 if ( !has_timestamp ) {
10130 // 'Last' resort - current time
10131 // Get before waypoint tests - so that if a waypoint time value (in the past) is found it should be used
10132 g_get_current_time ( &timestamp );
10133
10134 // Check times of waypoints
10135 gl = g_hash_table_get_values ( vtl->waypoints );
10136 GList *iter;
10137 for (iter = g_list_first (gl); iter != NULL; iter = g_list_next (iter)) {
10138 VikWaypoint *wpt = (VikWaypoint*)iter->data;
10139 if ( wpt->has_timestamp ) {
10140 if ( timestamp.tv_sec > wpt->timestamp ) {
10141 timestamp.tv_sec = wpt->timestamp;
10142 has_timestamp = TRUE;
10143 }
10144 }
10145 }
10146 g_list_free ( gl );
10147 }
10148
10149 vtl->metadata->timestamp = g_time_val_to_iso8601 ( &timestamp );
10150 }
10151 }
b1453c16
RN
10152}
10153
50a14534
EB
10154VikCoordMode vik_trw_layer_get_coord_mode ( VikTrwLayer *vtl )
10155{
10156 return vtl->coord_mode;
10157}
10158
073ede8c
RN
10159/**
10160 * Uniquify the whole layer
10161 * Also requires the layers panel as the names shown there need updating too
10162 * Returns whether the operation was successful or not
10163 */
10164gboolean vik_trw_layer_uniquify ( VikTrwLayer *vtl, VikLayersPanel *vlp )
10165{
10166 if ( vtl && vlp ) {
0d2b891f
RN
10167 vik_trw_layer_uniquify_tracks ( vtl, vlp, vtl->tracks, TRUE );
10168 vik_trw_layer_uniquify_tracks ( vtl, vlp, vtl->routes, FALSE );
073ede8c
RN
10169 vik_trw_layer_uniquify_waypoints ( vtl, vlp );
10170 return TRUE;
10171 }
10172 return FALSE;
10173}
50a14534 10174
c9570f86 10175static void waypoint_convert ( const gpointer id, VikWaypoint *wp, VikCoordMode *dest_mode )
50a14534
EB
10176{
10177 vik_coord_convert ( &(wp->coord), *dest_mode );
10178}
10179
9e212bfc 10180static void track_convert ( const gpointer id, VikTrack *tr, VikCoordMode *dest_mode )
50a14534
EB
10181{
10182 vik_track_convert ( tr, *dest_mode );
10183}
10184
10185static void trw_layer_change_coord_mode ( VikTrwLayer *vtl, VikCoordMode dest_mode )
10186{
10187 if ( vtl->coord_mode != dest_mode )
10188 {
10189 vtl->coord_mode = dest_mode;
10190 g_hash_table_foreach ( vtl->waypoints, (GHFunc) waypoint_convert, &dest_mode );
10191 g_hash_table_foreach ( vtl->tracks, (GHFunc) track_convert, &dest_mode );
6440ca9b 10192 g_hash_table_foreach ( vtl->routes, (GHFunc) track_convert, &dest_mode );
50a14534
EB
10193 }
10194}
e4afc73a 10195
a7cd93ac 10196static void trw_layer_set_menu_selection ( VikTrwLayer *vtl, guint16 selection )
20c7a3a0
QT
10197{
10198 vtl->menu_selection = selection;
10199}
10200
a7cd93ac 10201static guint16 trw_layer_get_menu_selection ( VikTrwLayer *vtl )
20c7a3a0 10202{
a7cd93ac 10203 return (vtl->menu_selection);
20c7a3a0
QT
10204}
10205
7114e879
QT
10206/* ----------- Downloading maps along tracks --------------- */
10207
10208static int get_download_area_width(VikViewport *vvp, gdouble zoom_level, struct LatLon *wh)
10209{
10210 /* TODO: calculating based on current size of viewport */
10211 const gdouble w_at_zoom_0_125 = 0.0013;
10212 const gdouble h_at_zoom_0_125 = 0.0011;
10213 gdouble zoom_factor = zoom_level/0.125;
10214
10215 wh->lat = h_at_zoom_0_125 * zoom_factor;
10216 wh->lon = w_at_zoom_0_125 * zoom_factor;
10217
10218 return 0; /* all OK */
10219}
10220
35e22ed8
QT
10221static VikCoord *get_next_coord(VikCoord *from, VikCoord *to, struct LatLon *dist, gdouble gradient)
10222{
10223 if ((dist->lon >= ABS(to->east_west - from->east_west)) &&
10224 (dist->lat >= ABS(to->north_south - from->north_south)))
10225 return NULL;
10226
10227 VikCoord *coord = g_malloc(sizeof(VikCoord));
10228 coord->mode = VIK_COORD_LATLON;
10229
10230 if (ABS(gradient) < 1) {
10231 if (from->east_west > to->east_west)
10232 coord->east_west = from->east_west - dist->lon;
10233 else
10234 coord->east_west = from->east_west + dist->lon;
10235 coord->north_south = gradient * (coord->east_west - from->east_west) + from->north_south;
10236 } else {
10237 if (from->north_south > to->north_south)
10238 coord->north_south = from->north_south - dist->lat;
10239 else
10240 coord->north_south = from->north_south + dist->lat;
10241 coord->east_west = (1/gradient) * (coord->north_south - from->north_south) + from->north_south;
10242 }
10243
10244 return coord;
10245}
10246
10247static GList *add_fillins(GList *list, VikCoord *from, VikCoord *to, struct LatLon *dist)
10248{
10249 /* TODO: handle virtical track (to->east_west - from->east_west == 0) */
10250 gdouble gradient = (to->north_south - from->north_south)/(to->east_west - from->east_west);
10251
10252 VikCoord *next = from;
10253 while (TRUE) {
10254 if ((next = get_next_coord(next, to, dist, gradient)) == NULL)
10255 break;
10256 list = g_list_prepend(list, next);
10257 }
10258
10259 return list;
10260}
10261
7114e879
QT
10262void vik_track_download_map(VikTrack *tr, VikMapsLayer *vml, VikViewport *vvp, gdouble zoom_level)
10263{
10264 typedef struct _Rect {
10265 VikCoord tl;
10266 VikCoord br;
35e22ed8 10267 VikCoord center;
7114e879 10268 } Rect;
35e22ed8 10269#define GLRECT(iter) ((Rect *)((iter)->data))
7114e879
QT
10270
10271 struct LatLon wh;
35e22ed8 10272 GList *rects_to_download = NULL;
7114e879
QT
10273 GList *rect_iter;
10274
10275 if (get_download_area_width(vvp, zoom_level, &wh))
10276 return;
10277
10278 GList *iter = tr->trackpoints;
35e22ed8
QT
10279 if (!iter)
10280 return;
7114e879
QT
10281
10282 gboolean new_map = TRUE;
10283 VikCoord *cur_coord, tl, br;
10284 Rect *rect;
7114e879
QT
10285 while (iter) {
10286 cur_coord = &(VIK_TRACKPOINT(iter->data))->coord;
10287 if (new_map) {
10288 vik_coord_set_area(cur_coord, &wh, &tl, &br);
10289 rect = g_malloc(sizeof(Rect));
10290 rect->tl = tl;
10291 rect->br = br;
35e22ed8
QT
10292 rect->center = *cur_coord;
10293 rects_to_download = g_list_prepend(rects_to_download, rect);
7114e879
QT
10294 new_map = FALSE;
10295 iter = iter->next;
10296 continue;
10297 }
10298 gboolean found = FALSE;
35e22ed8
QT
10299 for (rect_iter = rects_to_download; rect_iter; rect_iter = rect_iter->next) {
10300 if (vik_coord_inside(cur_coord, &GLRECT(rect_iter)->tl, &GLRECT(rect_iter)->br)) {
7114e879
QT
10301 found = TRUE;
10302 break;
10303 }
10304 }
10305 if (found)
10306 iter = iter->next;
10307 else
10308 new_map = TRUE;
10309 }
35e22ed8 10310
35e22ed8 10311 GList *fillins = NULL;
b1e57d16
RN
10312 /* 'fillin' doesn't work in UTM mode - potentially ending up in massive loop continually allocating memory - hence don't do it */
10313 /* seems that ATM the function get_next_coord works only for LATLON */
10314 if ( cur_coord->mode == VIK_COORD_LATLON ) {
10315 /* fill-ins for far apart points */
10316 GList *cur_rect, *next_rect;
10317 for (cur_rect = rects_to_download;
10318 (next_rect = cur_rect->next) != NULL;
10319 cur_rect = cur_rect->next) {
10320 if ((wh.lon < ABS(GLRECT(cur_rect)->center.east_west - GLRECT(next_rect)->center.east_west)) ||
10321 (wh.lat < ABS(GLRECT(cur_rect)->center.north_south - GLRECT(next_rect)->center.north_south))) {
10322 fillins = add_fillins(fillins, &GLRECT(cur_rect)->center, &GLRECT(next_rect)->center, &wh);
10323 }
35e22ed8 10324 }
3cbbb49e
GB
10325 } else
10326 g_message("%s: this feature works only in Mercator mode", __FUNCTION__);
35e22ed8
QT
10327
10328 if (fillins) {
10329 GList *iter = fillins;
10330 while (iter) {
10331 cur_coord = (VikCoord *)(iter->data);
10332 vik_coord_set_area(cur_coord, &wh, &tl, &br);
10333 rect = g_malloc(sizeof(Rect));
10334 rect->tl = tl;
10335 rect->br = br;
10336 rect->center = *cur_coord;
10337 rects_to_download = g_list_prepend(rects_to_download, rect);
10338 iter = iter->next;
10339 }
10340 }
10341
10342 for (rect_iter = rects_to_download; rect_iter; rect_iter = rect_iter->next) {
3ac548fa 10343 vik_maps_layer_download_section (vml, vvp, &(((Rect *)(rect_iter->data))->tl), &(((Rect *)(rect_iter->data))->br), zoom_level);
7114e879
QT
10344 }
10345
35e22ed8
QT
10346 if (fillins) {
10347 for (iter = fillins; iter; iter = iter->next)
10348 g_free(iter->data);
10349 g_list_free(fillins);
10350 }
10351 if (rects_to_download) {
10352 for (rect_iter = rects_to_download; rect_iter; rect_iter = rect_iter->next)
10353 g_free(rect_iter->data);
10354 g_list_free(rects_to_download);
10355 }
7114e879
QT
10356}
10357
19782ffd 10358static void trw_layer_download_map_along_track_cb ( menu_array_sublayer values )
7114e879
QT
10359{
10360 VikMapsLayer *vml;
fe7e4e45 10361 gint selected_map;
7114e879
QT
10362 gchar *zoomlist[] = {"0.125", "0.25", "0.5", "1", "2", "4", "8", "16", "32", "64", "128", "256", "512", "1024", NULL };
10363 gdouble zoom_vals[] = {0.125, 0.25, 0.5, 1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024};
10364 gint selected_zoom, default_zoom;
7114e879 10365
19782ffd
RN
10366 VikTrwLayer *vtl = values[MA_VTL];
10367 VikLayersPanel *vlp = values[MA_VLP];
0d2b891f 10368 VikTrack *trk;
19782ffd
RN
10369 if ( GPOINTER_TO_INT (values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_ROUTE )
10370 trk = (VikTrack *) g_hash_table_lookup ( vtl->routes, values[MA_SUBLAYER_ID] );
0d2b891f 10371 else
19782ffd 10372 trk = (VikTrack *) g_hash_table_lookup ( vtl->tracks, values[MA_SUBLAYER_ID] );
0d2b891f
RN
10373 if ( !trk )
10374 return;
10375
7114e879
QT
10376 VikViewport *vvp = vik_window_viewport((VikWindow *)(VIK_GTK_WINDOW_FROM_LAYER(vtl)));
10377
aa7ed888 10378 GList *vmls = vik_layers_panel_get_all_layers_of_type(vlp, VIK_LAYER_MAPS, TRUE); // Includes hidden map layer types
7114e879
QT
10379 int num_maps = g_list_length(vmls);
10380
10381 if (!num_maps) {
4d333042 10382 a_dialog_error_msg(VIK_GTK_WINDOW_FROM_LAYER(vtl), _("No map layer in use. Create one first") );
7114e879
QT
10383 return;
10384 }
10385
fe7e4e45 10386 // Convert from list of vmls to list of names. Allowing the user to select one of them
7114e879
QT
10387 gchar **map_names = g_malloc(1 + num_maps * sizeof(gpointer));
10388 VikMapsLayer **map_layers = g_malloc(1 + num_maps * sizeof(gpointer));
10389
10390 gchar **np = map_names;
10391 VikMapsLayer **lp = map_layers;
fe7e4e45 10392 int i;
7114e879 10393 for (i = 0; i < num_maps; i++) {
7114e879 10394 vml = (VikMapsLayer *)(vmls->data);
fe7e4e45
RN
10395 *lp++ = vml;
10396 *np++ = vik_maps_layer_get_map_label(vml);
7114e879
QT
10397 vmls = vmls->next;
10398 }
fe7e4e45 10399 // Mark end of the array lists
7114e879
QT
10400 *lp = NULL;
10401 *np = NULL;
7114e879
QT
10402
10403 gdouble cur_zoom = vik_viewport_get_zoom(vvp);
fe7e4e45 10404 for (default_zoom = 0; default_zoom < G_N_ELEMENTS(zoom_vals); default_zoom++) {
7114e879
QT
10405 if (cur_zoom == zoom_vals[default_zoom])
10406 break;
10407 }
fe7e4e45 10408 default_zoom = (default_zoom == G_N_ELEMENTS(zoom_vals)) ? G_N_ELEMENTS(zoom_vals) - 1 : default_zoom;
7114e879 10409
fe7e4e45 10410 if (!a_dialog_map_n_zoom(VIK_GTK_WINDOW_FROM_LAYER(vtl), map_names, 0, zoomlist, default_zoom, &selected_map, &selected_zoom))
7114e879
QT
10411 goto done;
10412
0d2b891f 10413 vik_track_download_map(trk, map_layers[selected_map], vvp, zoom_vals[selected_zoom]);
7114e879
QT
10414
10415done:
10416 for (i = 0; i < num_maps; i++)
10417 g_free(map_names[i]);
10418 g_free(map_names);
10419 g_free(map_layers);
10420
10421 g_list_free(vmls);
10422
10423}
0c1044e9 10424
a8fe53f8
EB
10425/**** lowest waypoint number calculation ***/
10426static gint highest_wp_number_name_to_number(const gchar *name) {
10427 if ( strlen(name) == 3 ) {
10428 int n = atoi(name);
10429 if ( n < 100 && name[0] != '0' )
10430 return -1;
10431 if ( n < 10 && name[0] != '0' )
10432 return -1;
10433 return n;
10434 }
10435 return -1;
10436}
10437
10438
10439static void highest_wp_number_reset(VikTrwLayer *vtl)
10440{
10441 vtl->highest_wp_number = -1;
10442}
10443
10444static void highest_wp_number_add_wp(VikTrwLayer *vtl, const gchar *new_wp_name)
10445{
10446 /* if is bigger that top, add it */
10447 gint new_wp_num = highest_wp_number_name_to_number(new_wp_name);
10448 if ( new_wp_num > vtl->highest_wp_number )
10449 vtl->highest_wp_number = new_wp_num;
10450}
10451
10452static void highest_wp_number_remove_wp(VikTrwLayer *vtl, const gchar *old_wp_name)
10453{
10454 /* if wasn't top, do nothing. if was top, count backwards until we find one used */
10455 gint old_wp_num = highest_wp_number_name_to_number(old_wp_name);
10456 if ( vtl->highest_wp_number == old_wp_num ) {
10457 gchar buf[4];
c9570f86 10458 vtl->highest_wp_number--;
a8fe53f8
EB
10459
10460 g_snprintf(buf,4,"%03d", vtl->highest_wp_number );
10461 /* search down until we find something that *does* exist */
10462
c9570f86
RN
10463 while ( vtl->highest_wp_number > 0 && ! vik_trw_layer_get_waypoint ( vtl, buf )) {
10464 vtl->highest_wp_number--;
a8fe53f8
EB
10465 g_snprintf(buf,4,"%03d", vtl->highest_wp_number );
10466 }
10467 }
10468}
10469
10470/* get lowest unused number */
10471static gchar *highest_wp_number_get(VikTrwLayer *vtl)
10472{
10473 gchar buf[4];
10474 if ( vtl->highest_wp_number < 0 || vtl->highest_wp_number >= 999 )
3ce17b61 10475 return NULL;
a8fe53f8
EB
10476 g_snprintf(buf,4,"%03d", vtl->highest_wp_number+1 );
10477 return g_strdup(buf);
10478}
07c9d42b
RN
10479
10480/**
10481 * trw_layer_create_track_list_both:
10482 *
10483 * Create the latest list of tracks and routes
10484 */
10485static GList* trw_layer_create_track_list_both ( VikLayer *vl, gpointer user_data )
10486{
10487 VikTrwLayer *vtl = VIK_TRW_LAYER(vl);
10488 GList *tracks = NULL;
10489 tracks = g_list_concat ( tracks, g_hash_table_get_values ( vik_trw_layer_get_tracks ( vtl ) ) );
10490 tracks = g_list_concat ( tracks, g_hash_table_get_values ( vik_trw_layer_get_routes ( vtl ) ) );
10491
10492 return vik_trw_layer_build_track_list_t ( vtl, tracks );
10493}
10494
19782ffd 10495static void trw_layer_track_list_dialog_single ( menu_array_sublayer values )
07c9d42b 10496{
19782ffd 10497 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
07c9d42b
RN
10498
10499 gchar *title = NULL;
19782ffd 10500 if ( GPOINTER_TO_INT(values[MA_SUBTYPE]) == VIK_TRW_LAYER_SUBLAYER_TRACKS )
07c9d42b
RN
10501 title = g_strdup_printf ( _("%s: Track List"), VIK_LAYER(vtl)->name );
10502 else
10503 title = g_strdup_printf ( _("%s: Route List"), VIK_LAYER(vtl)->name );
10504
19782ffd 10505 vik_trw_layer_track_list_show_dialog ( title, VIK_LAYER(vtl), values[MA_SUBTYPE], trw_layer_create_track_list, FALSE );
07c9d42b
RN
10506 g_free ( title );
10507}
10508
1c2083ba 10509static void trw_layer_track_list_dialog ( menu_array_layer values )
07c9d42b 10510{
1c2083ba 10511 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
07c9d42b
RN
10512
10513 gchar *title = g_strdup_printf ( _("%s: Track and Route List"), VIK_LAYER(vtl)->name );
10514 vik_trw_layer_track_list_show_dialog ( title, VIK_LAYER(vtl), NULL, trw_layer_create_track_list_both, FALSE );
10515 g_free ( title );
10516}
00176e85 10517
1c2083ba 10518static void trw_layer_waypoint_list_dialog ( menu_array_layer values )
00176e85 10519{
1c2083ba 10520 VikTrwLayer *vtl = VIK_TRW_LAYER(values[MA_VTL]);
00176e85
RN
10521
10522 gchar *title = g_strdup_printf ( _("%s: Waypoint List"), VIK_LAYER(vtl)->name );
10523 vik_trw_layer_waypoint_list_show_dialog ( title, VIK_LAYER(vtl), NULL, trw_layer_create_waypoint_list, FALSE );
10524 g_free ( title );
10525}