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