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