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