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