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